@flare-engine/render 0.2.1 → 0.3.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/draw-order.ts","../src/camera2d.ts","../src/renderer.ts","../src/sprite-batch.ts","../src/sprite-effects.ts","../src/shaders.ts","../src/frame-renderer.ts","../src/sprite-font.ts","../src/render-system.ts"],"sourcesContent":["export {\n type Drawable,\n sortByDrawOrder,\n drawOrderCompare,\n} from './draw-order.js';\n\nexport { Camera2D, type FollowOptions } from './camera2d.js';\n\nexport {\n type CanvasLike,\n type RenderStats,\n Renderer,\n} from './renderer.js';\n\nexport {\n type SpriteDrawCommand,\n SpriteBatch,\n} from './sprite-batch.js';\n\nexport {\n type SpriteColor,\n type SpriteEffect,\n type TintEffect,\n type FlashEffect,\n type GrayscaleEffect,\n type OutlineEffect,\n type CompiledShader,\n type ShaderUniforms,\n type CustomShaderEffect,\n type WaterEffect,\n type FogEffect,\n type FireEffect,\n type DropShadowEffect,\n type RoofsCityEffect,\n type ForestCanopyEffect,\n type RiverWaveEffect,\n type MoonCraterEffect,\n type CaveInteriorEffect,\n type MountainHillsEffect,\n type AnySpriteEffect,\n tint,\n flash,\n grayscale,\n outline,\n custom,\n water,\n fog,\n fire,\n dropShadow,\n roofsCity,\n forestCanopy,\n riverWave,\n moonCrater,\n caveInterior,\n mountainHills,\n updateEffect,\n flashIntensity,\n} from './sprite-effects.js';\n\nexport {\n WATER_SKSL,\n FOG_SKSL,\n FIRE_SKSL,\n ROOFS_CITY_SKSL,\n FOREST_CANOPY_SKSL,\n RIVER_WAVE_SKSL,\n MOON_CRATER_SKSL,\n CAVE_INTERIOR_SKSL,\n MOUNTAIN_HILLS_SKSL,\n} from './shaders.js';\n\nexport {\n type ColorLike,\n type RectLike,\n type RSXformLike,\n type RuntimeEffectLike,\n type ColorFilterFactoryLike,\n type ImageFilterFactoryLike,\n type RuntimeEffectFactoryLike,\n type PaintLike,\n type DrawSurfaceLike,\n type PictureLike,\n type PictureRecorderLike,\n type FrameRendererModule,\n type FrameRendererCompiledPass,\n type FrameRendererPostFxRuntime,\n type FrameRendererPostFxChain,\n type FrameRendererPostFxDescriptor,\n type FrameRenderer,\n createFrameRenderer,\n} from './frame-renderer.js';\n\nexport {\n type Glyph,\n type KerningPair,\n type BitmapFont,\n parseBMFontJSON,\n getKerning,\n measureText,\n drawText,\n} from './sprite-font.js';\n\nexport {\n type PositionData,\n type SpriteData,\n type SpriteAnchor,\n Sprite,\n createRenderSystem,\n} from './render-system.js';\n","/** A drawable item with a z-order for sorting. */\nexport interface Drawable {\n /** Z-index for draw ordering. Higher values draw on top. */\n z: number;\n}\n\n/**\n * Sort an array of drawables by z-index (ascending = back to front).\n * Uses insertion sort which is fast for nearly-sorted arrays\n * (typical in game rendering where order rarely changes).\n *\n * Mutates the input array in place. Returns the same array.\n *\n * @param items The array to sort in place.\n * @param length Optional upper bound — when provided, only sorts `items[0..length)`.\n * Use this when passing a pooled array that may hold stale tail entries.\n */\nexport function sortByDrawOrder<T extends Drawable>(items: T[], length?: number): T[] {\n const n = length ?? items.length;\n // Insertion sort — O(n) for nearly-sorted, no allocations\n for (let i = 1; i < n; i++) {\n const item = items[i]!;\n let j = i - 1;\n while (j >= 0 && items[j]!.z > item.z) {\n items[j + 1] = items[j]!;\n j--;\n }\n items[j + 1] = item;\n }\n return items;\n}\n\n/**\n * Compare function for use with Array.sort when insertion sort isn't desired.\n * Stable relative order for equal z values (returns 0).\n */\nexport function drawOrderCompare(a: Drawable, b: Drawable): number {\n return a.z - b.z;\n}\n","import type { Vec2 } from '@flare-engine/math';\nimport type { Rect } from '@flare-engine/math';\n\n/** Camera follow smoothing options. */\nexport interface FollowOptions {\n /** Smoothing factor (0 = instant, lower = faster, default 0.1). */\n smoothing?: number;\n /** Offset from target (e.g. look-ahead). */\n offset?: Vec2;\n /** Dead zone rect — camera won't move while target is inside. */\n deadZone?: Rect;\n}\n\n/** Camera shake state. */\ninterface ShakeState {\n intensity: number;\n duration: number;\n elapsed: number;\n offsetX: number;\n offsetY: number;\n}\n\n/** Simple pseudo-random for deterministic shake. Avoids Math.random() allocs. */\nfunction pseudoRandom(seed: number): number {\n const x = Math.sin(seed * 12.9898 + seed * 78.233) * 43758.5453;\n return x - Math.floor(x);\n}\n\n// ─── Simplex-like 2D noise for shakePerlin ───────────────────────────────────\n// 256-entry permutation table — written once at module load, never mutated.\nconst PERM = new Uint8Array(256);\n{\n for (let i = 0; i < 256; i++) PERM[i] = i;\n // Deterministic Fisher-Yates with a fixed seed (no Math.random()).\n let s = 0x9e3779b9;\n for (let i = 255; i > 0; i--) {\n s = (s ^ (s >>> 15)) >>> 0;\n s = Math.imul(s, 0x85ebca6b) >>> 0;\n s = (s ^ (s >>> 13)) >>> 0;\n const j = s % (i + 1);\n const t = PERM[i]!;\n PERM[i] = PERM[j]!;\n PERM[j] = t;\n }\n}\n\n// 8 evenly-spaced 2D gradient directions (unit vectors × 1 for integer math).\nconst S = Math.SQRT1_2; // ≈ 0.7071 — diagonal component\nconst GRADS: ReadonlyArray<readonly [number, number]> = [\n [1, 0],\n [S, S],\n [0, 1],\n [-S, S],\n [-1, 0],\n [-S, -S],\n [0, -1],\n [S, -S],\n];\n\nfunction grad2(hash: number, x: number, y: number): number {\n const g = GRADS[hash & 7]!;\n return g[0] * x + g[1] * y;\n}\n\nfunction fade(t: number): number {\n return t * t * t * (t * (t * 6 - 15) + 10);\n}\n\n/** 2D value-gradient noise in [-1, 1]. */\nfunction noise2d(x: number, y: number): number {\n const xi = Math.floor(x) & 255;\n const yi = Math.floor(y) & 255;\n const xf = x - Math.floor(x);\n const yf = y - Math.floor(y);\n const u = fade(xf);\n const v = fade(yf);\n\n const aa = PERM[PERM[xi]! + yi]!;\n const ab = PERM[PERM[xi]! + yi + 1]!;\n const ba = PERM[PERM[(xi + 1) & 255]! + yi]!;\n const bb = PERM[PERM[(xi + 1) & 255]! + yi + 1]!;\n\n const x1 = xf;\n const y1 = yf;\n const x2 = xf - 1;\n const y2 = yf - 1;\n\n const n00 = grad2(aa, x1, y1);\n const n10 = grad2(ba, x2, y1);\n const n01 = grad2(ab, x1, y2);\n const n11 = grad2(bb, x2, y2);\n\n const lerpX0 = n00 + u * (n10 - n00);\n const lerpX1 = n01 + u * (n11 - n01);\n return lerpX0 + v * (lerpX1 - lerpX0);\n}\n\n/** 2-octave FBM for shake displacement — result in [-1, 1]. */\nfunction fbmNoise2d(x: number, y: number): number {\n return 0.6667 * noise2d(x, y) + 0.3333 * noise2d(x * 2, y * 2);\n}\n\n/**\n * Camera2D — 2D camera with position, zoom, rotation, follow, and shake.\n * Pure data + methods — no Skia dependency. The renderer reads camera state\n * to build the view transform.\n */\nexport class Camera2D {\n /** Camera position (world-space center). */\n x = 0;\n y = 0;\n\n /** Zoom level (1 = 100%). */\n zoom = 1;\n\n /** Rotation in radians. */\n rotation = 0;\n\n /** Viewport width in pixels. */\n viewportWidth = 0;\n\n /** Viewport height in pixels. */\n viewportHeight = 0;\n\n /** Optional world bounds to clamp camera position. */\n bounds: Rect | null = null;\n\n private _follow: { target: Vec2; options: FollowOptions } | null = null;\n private _shake: ShakeState | null = null;\n private _shakePerlin: ShakeState | null = null;\n private _shakeSeed = 0;\n private _shakePerlinSeed = 0;\n private _motionScale = 1;\n\n /** Set viewport dimensions. */\n setViewport(width: number, height: number): void {\n this.viewportWidth = width;\n this.viewportHeight = height;\n }\n\n /** Start following a target position. */\n follow(target: Vec2, options?: FollowOptions): void {\n this._follow = { target, options: options ?? {} };\n }\n\n /** Stop following. */\n unfollow(): void {\n this._follow = null;\n }\n\n /**\n * Global motion multiplier applied to subsequent `shake()` calls. Scales\n * both amplitude and duration. Default 1 (no-op). 0 fully suppresses\n * shake. Intended to be driven by `@flare-engine/a11y` reduced-motion so\n * callers don't have to guard every shake site.\n *\n * Only affects future `shake()` calls — does not retroactively modify an\n * in-flight shake.\n *\n * Clamped to `>= 0`.\n */\n setMotionScale(scale: number): void {\n this._motionScale = scale < 0 ? 0 : scale;\n }\n\n /** Current motion scale (default 1). */\n get motionScale(): number {\n return this._motionScale;\n }\n\n /** Start camera shake. */\n shake(intensity: number, duration: number): void {\n const k = this._motionScale;\n if (k <= 0) {\n this._shake = null;\n return;\n }\n this._shake = {\n intensity: intensity * k,\n duration: duration * k,\n elapsed: 0,\n offsetX: 0,\n offsetY: 0,\n };\n this._shakeSeed++;\n }\n\n /**\n * Start a Perlin-noise-based camera shake. Overwrites any in-flight\n * shakePerlin (same semantics as shake()). Respects motionScale.\n */\n shakePerlin(intensity: number, duration: number): void {\n const k = this._motionScale;\n if (k <= 0) {\n this._shakePerlin = null;\n return;\n }\n this._shakePerlin = {\n intensity: intensity * k,\n duration: duration * k,\n elapsed: 0,\n offsetX: 0,\n offsetY: 0,\n };\n this._shakePerlinSeed++;\n }\n\n /**\n * Update camera. Call once per frame.\n * @param dt Delta time in seconds.\n */\n update(dt: number): void {\n // Follow target\n if (this._follow) {\n const { target, options } = this._follow;\n const smoothing = options.smoothing ?? 0.1;\n const offsetX = options.offset?.x ?? 0;\n const offsetY = options.offset?.y ?? 0;\n const goalX = target.x + offsetX;\n const goalY = target.y + offsetY;\n\n if (options.deadZone) {\n const dz = options.deadZone;\n const relX = target.x - this.x;\n const relY = target.y - this.y;\n\n // Only move if target is outside dead zone\n if (relX < dz.x) {\n this.x += (goalX - this.x - (dz.x - relX)) * (1 - smoothing ** dt);\n } else if (relX > dz.x + dz.width) {\n this.x += (goalX - this.x + (relX - dz.x - dz.width)) * (1 - smoothing ** dt);\n }\n if (relY < dz.y) {\n this.y += (goalY - this.y - (dz.y - relY)) * (1 - smoothing ** dt);\n } else if (relY > dz.y + dz.height) {\n this.y += (goalY - this.y + (relY - dz.y - dz.height)) * (1 - smoothing ** dt);\n }\n } else {\n // Exponential smoothing\n const factor = 1 - smoothing ** dt;\n this.x += (goalX - this.x) * factor;\n this.y += (goalY - this.y) * factor;\n }\n }\n\n // Clamp to bounds\n if (this.bounds) {\n this._clampToBounds();\n }\n\n // Shake (pseudo-random)\n if (this._shake) {\n this._shake.elapsed += dt;\n if (this._shake.elapsed >= this._shake.duration) {\n this._shake = null;\n } else {\n const progress = this._shake.elapsed / this._shake.duration;\n const decay = 1 - progress;\n const seed = this._shakeSeed + this._shake.elapsed * 100;\n this._shake.offsetX = (pseudoRandom(seed) * 2 - 1) * this._shake.intensity * decay;\n this._shake.offsetY = (pseudoRandom(seed + 1) * 2 - 1) * this._shake.intensity * decay;\n }\n }\n\n // Shake (Perlin / FBM noise)\n if (this._shakePerlin) {\n this._shakePerlin.elapsed += dt;\n if (this._shakePerlin.elapsed >= this._shakePerlin.duration) {\n this._shakePerlin = null;\n } else {\n const progress = this._shakePerlin.elapsed / this._shakePerlin.duration;\n const decay = 1 - progress;\n const t = this._shakePerlin.elapsed * 4.7 + this._shakePerlinSeed * 17.3;\n this._shakePerlin.offsetX = fbmNoise2d(t, 0) * this._shakePerlin.intensity * decay;\n this._shakePerlin.offsetY = fbmNoise2d(0, t + 5.4) * this._shakePerlin.intensity * decay;\n }\n }\n }\n\n /** Get the effective camera X (including shake + shakePerlin offsets). */\n get effectiveX(): number {\n return this.x + (this._shake?.offsetX ?? 0) + (this._shakePerlin?.offsetX ?? 0);\n }\n\n /** Get the effective camera Y (including shake + shakePerlin offsets). */\n get effectiveY(): number {\n return this.y + (this._shake?.offsetY ?? 0) + (this._shakePerlin?.offsetY ?? 0);\n }\n\n /** Convert world coordinates to screen coordinates. */\n worldToScreen(worldX: number, worldY: number): Vec2 {\n return this.worldToScreenInto(worldX, worldY, { x: 0, y: 0 });\n }\n\n /** Zero-alloc variant — writes screen coordinates into `out`. */\n worldToScreenInto(worldX: number, worldY: number, out: Vec2): Vec2 {\n const cos = Math.cos(-this.rotation);\n const sin = Math.sin(-this.rotation);\n const dx = (worldX - this.effectiveX) * this.zoom;\n const dy = (worldY - this.effectiveY) * this.zoom;\n out.x = cos * dx - sin * dy + this.viewportWidth / 2;\n out.y = sin * dx + cos * dy + this.viewportHeight / 2;\n return out;\n }\n\n /** Convert screen coordinates to world coordinates. */\n screenToWorld(screenX: number, screenY: number): Vec2 {\n return this.screenToWorldInto(screenX, screenY, { x: 0, y: 0 });\n }\n\n /** Zero-alloc variant — writes world coordinates into `out`. */\n screenToWorldInto(screenX: number, screenY: number, out: Vec2): Vec2 {\n const cx = screenX - this.viewportWidth / 2;\n const cy = screenY - this.viewportHeight / 2;\n const cos = Math.cos(this.rotation);\n const sin = Math.sin(this.rotation);\n const rx = cos * cx - sin * cy;\n const ry = sin * cx + cos * cy;\n out.x = rx / this.zoom + this.effectiveX;\n out.y = ry / this.zoom + this.effectiveY;\n return out;\n }\n\n /** Get the visible world-space bounding rect (allocates a fresh Rect). */\n getBounds(): Rect {\n return this.getBoundsInto({ x: 0, y: 0, width: 0, height: 0 });\n }\n\n /** Zero-alloc variant — writes the visible bounds into `out`. */\n getBoundsInto(out: Rect): Rect {\n const halfW = this.viewportWidth / (2 * this.zoom);\n const halfH = this.viewportHeight / (2 * this.zoom);\n out.x = this.effectiveX - halfW;\n out.y = this.effectiveY - halfH;\n out.width = halfW * 2;\n out.height = halfH * 2;\n return out;\n }\n\n private _clampToBounds(): void {\n const b = this.bounds!;\n const halfW = this.viewportWidth / (2 * this.zoom);\n const halfH = this.viewportHeight / (2 * this.zoom);\n\n const minX = b.x + halfW;\n const maxX = b.x + b.width - halfW;\n const minY = b.y + halfH;\n const maxY = b.y + b.height - halfH;\n\n if (minX <= maxX) {\n this.x = Math.max(minX, Math.min(maxX, this.x));\n } else {\n // Viewport larger than bounds — center\n this.x = b.x + b.width / 2;\n }\n\n if (minY <= maxY) {\n this.y = Math.max(minY, Math.min(maxY, this.y));\n } else {\n this.y = b.y + b.height / 2;\n }\n }\n}\n","/** Minimal Skia Canvas-like interface for the Renderer. */\nexport interface CanvasLike {\n save(): number;\n restore(): void;\n translate(x: number, y: number): void;\n scale(sx: number, sy: number): void;\n rotate(radians: number, cx: number, cy: number): void;\n clear(color: number): void;\n}\n\n/** Stats tracked per frame. */\nexport interface RenderStats {\n drawCalls: number;\n batchCount: number;\n}\n\n/**\n * Renderer — manages the Skia Canvas lifecycle for a frame.\n * `begin()` to start, draw your content, `end()` to finish.\n */\nexport class Renderer {\n /** Current frame stats. */\n readonly stats: RenderStats = { drawCalls: 0, batchCount: 0 };\n\n private _canvas: CanvasLike | null = null;\n private _inFrame = false;\n\n /** Set the canvas to render to. Called once when canvas is available. */\n setCanvas(canvas: CanvasLike): void {\n this._canvas = canvas;\n }\n\n /** Get the current canvas. Throws if not in a frame. */\n get canvas(): CanvasLike {\n if (!this._canvas) {\n throw new Error('Renderer: no canvas set');\n }\n return this._canvas;\n }\n\n /** Whether we are currently between begin() and end(). */\n get inFrame(): boolean {\n return this._inFrame;\n }\n\n /**\n * Begin a new frame. Clears the canvas and resets stats.\n * @param clearColor 32-bit ARGB color to clear with (default black = 0xFF000000).\n */\n begin(clearColor = 0xff000000): void {\n if (this._inFrame) {\n throw new Error('Renderer: begin() called while already in frame');\n }\n this._inFrame = true;\n this.stats.drawCalls = 0;\n this.stats.batchCount = 0;\n this.canvas.save();\n this.canvas.clear(clearColor);\n }\n\n /**\n * End the current frame. Restores canvas state.\n */\n end(): void {\n if (!this._inFrame) {\n throw new Error('Renderer: end() called without begin()');\n }\n this.canvas.restore();\n this._inFrame = false;\n }\n\n /**\n * Apply camera transform to the canvas.\n * Call after begin() and before drawing.\n */\n applyCamera(\n cameraX: number,\n cameraY: number,\n zoom: number,\n rotation: number,\n viewportWidth: number,\n viewportHeight: number,\n ): void {\n const c = this.canvas;\n // Move origin to viewport center\n c.translate(viewportWidth / 2, viewportHeight / 2);\n // Apply zoom\n c.scale(zoom, zoom);\n // Apply rotation around center\n if (rotation !== 0) {\n c.rotate(-rotation, 0, 0);\n }\n // Offset by camera position\n c.translate(-cameraX, -cameraY);\n }\n\n /** Increment draw call counter. Called by batching systems. */\n recordDrawCall(): void {\n this.stats.drawCalls++;\n }\n\n /** Increment batch counter. */\n recordBatch(): void {\n this.stats.batchCount++;\n }\n}\n","import type { SkImageLike } from '@flare-engine/assets';\nimport type { SpriteFrame } from '@flare-engine/assets';\nimport type { Renderer } from './renderer.js';\nimport type { AnySpriteEffect } from './sprite-effects.js';\n\n/** A single sprite draw request in the batch. */\nexport interface SpriteDrawCommand {\n /** Texture (Skia SkImage). */\n image: SkImageLike;\n /** Source rectangle in the texture. */\n srcX: number;\n srcY: number;\n srcW: number;\n srcH: number;\n /** Destination position and size. */\n dstX: number;\n dstY: number;\n dstW: number;\n dstH: number;\n /** Optional per-sprite effect. Shared by reference across commands in a\n * batch — the render adapter applies it once per batch. */\n effect: AnySpriteEffect | undefined;\n /** Per-instance alpha multiplier. Range [0, 1]. Default 1 (fully opaque).\n * Participates in the batch key — sprites with different alphas are\n * placed in separate batches so the renderer can apply each alpha once\n * per draw call without per-sprite overhead. */\n alpha: number;\n /** Rotation in radians, clockwise (Skia Y-down convention). Default 0.\n * Rotation is applied about the anchor point. */\n rotation: number;\n /** Anchor X fraction stored for the bridge rotation path. Range [0, 1]. */\n anchorX: number;\n /** Anchor Y fraction stored for the bridge rotation path. Range [0, 1]. */\n anchorY: number;\n /**\n * When true, the sprite is drawn via `drawImageRect` so `dstW` and `dstH`\n * are honored independently (non-uniform scale). A 256×256 source image\n * will fill an arbitrary W×H destination rect.\n *\n * Default (false/absent): drawn via the batched `drawAtlas` path (uniform\n * scale — dstW/srcW drives both axes). Stretched sprites are NOT batched\n * with regular sprites; each is an independent `drawImageRect` call.\n *\n * Rotation is unsupported in stretch mode — the RSXform similarity-transform\n * limit does not apply, but the bridge draws a plain axis-aligned rect.\n * Combine with a zero rotation only.\n */\n stretch: boolean;\n}\n\n/** Internal grouping bucket — one per unique (texture, effect, alpha) triple. */\ninterface Batch {\n image: SkImageLike;\n effect: AnySpriteEffect | undefined;\n alpha: number;\n commands: SpriteDrawCommand[];\n}\n\n/**\n * SpriteBatch — collects sprite draw commands and groups them by texture\n * (and optionally by effect) for efficient batched rendering.\n *\n * Usage:\n * 1. Call `draw()` for each sprite, optionally passing an effect\n * 2. Call `flush()` with an atlas draw callback to execute all batched draws\n * 3. Batching groups by texture identity, then by effect identity — so\n * sharing an effect instance across sprites maximizes batch size\n *\n * The actual Skia Atlas draw call is delegated to a callback so this module\n * remains testable without Skia.\n */\nexport class SpriteBatch {\n private _commands: SpriteDrawCommand[] = [];\n private _pool: SpriteDrawCommand[] = [];\n private _batches: Batch[] = [];\n\n /** Number of queued draw commands. */\n get count(): number {\n return this._commands.length;\n }\n\n /** Queue a sprite draw from a frame.\n *\n * `anchorX` / `anchorY` define the pivot point of the sprite as a\n * fraction of its destination size — `(0, 0)` = top-left (default),\n * `(0.5, 0.5)` = center, `(1, 1)` = bottom-right. `dstX` / `dstY`\n * then refer to the anchor point instead of the top-left corner.\n *\n * `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */\n draw(\n image: SkImageLike,\n frame: SpriteFrame,\n dstX: number,\n dstY: number,\n dstW?: number,\n dstH?: number,\n effect?: AnySpriteEffect,\n anchorX = 0,\n anchorY = 0,\n alpha = 1,\n rotation = 0,\n stretch = false,\n ): void {\n const cmd = this._acquire();\n const w = dstW ?? frame.w;\n const h = dstH ?? frame.h;\n cmd.image = image;\n cmd.srcX = frame.x;\n cmd.srcY = frame.y;\n cmd.srcW = frame.w;\n cmd.srcH = frame.h;\n cmd.dstX = dstX - anchorX * w;\n cmd.dstY = dstY - anchorY * h;\n cmd.dstW = w;\n cmd.dstH = h;\n cmd.effect = effect;\n cmd.alpha = alpha;\n cmd.rotation = rotation;\n cmd.anchorX = anchorX;\n cmd.anchorY = anchorY;\n cmd.stretch = stretch;\n this._commands.push(cmd);\n }\n\n /** Queue a raw sprite draw with explicit src/dst rects. `dstX` / `dstY`\n * refer to the anchor point — offset by `anchorX * dstW` / `anchorY * dstH`.\n *\n * `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */\n drawRaw(\n image: SkImageLike,\n srcX: number,\n srcY: number,\n srcW: number,\n srcH: number,\n dstX: number,\n dstY: number,\n dstW: number,\n dstH: number,\n effect?: AnySpriteEffect,\n anchorX = 0,\n anchorY = 0,\n alpha = 1,\n rotation = 0,\n stretch = false,\n ): void {\n const cmd = this._acquire();\n cmd.image = image;\n cmd.srcX = srcX;\n cmd.srcY = srcY;\n cmd.srcW = srcW;\n cmd.srcH = srcH;\n cmd.dstX = dstX - anchorX * dstW;\n cmd.dstY = dstY - anchorY * dstH;\n cmd.dstW = dstW;\n cmd.dstH = dstH;\n cmd.effect = effect;\n cmd.alpha = alpha;\n cmd.rotation = rotation;\n cmd.anchorX = anchorX;\n cmd.anchorY = anchorY;\n cmd.stretch = stretch;\n this._commands.push(cmd);\n }\n\n /**\n * Flush all queued commands, grouped by (texture, effect, alpha) identity.\n *\n * @param drawAtlas Callback invoked once per batch. Receives\n * `(image, commands)` — all commands in a batch share the same texture,\n * the same effect (readable from `commands[0].effect`), and the same\n * alpha (readable from `commands[0].alpha`).\n * @param renderer Optional renderer to record stats.\n */\n flush(\n drawAtlas: (image: SkImageLike, commands: SpriteDrawCommand[]) => void,\n renderer?: Renderer,\n ): void {\n if (this._commands.length === 0) return;\n\n // Group by (image, effect, alpha) via linear scan. Typical frames have\n // few unique batches, so O(n·k) is effectively O(n).\n const batches = this._batches;\n for (const cmd of this._commands) {\n let batch: Batch | undefined;\n for (let i = 0; i < batches.length; i++) {\n const b = batches[i]!;\n if (b.image === cmd.image && b.effect === cmd.effect && b.alpha === cmd.alpha) {\n batch = b;\n break;\n }\n }\n if (!batch) {\n batch = { image: cmd.image, effect: cmd.effect, alpha: cmd.alpha, commands: [] };\n batches.push(batch);\n }\n batch.commands.push(cmd);\n }\n\n // Draw each batch.\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i]!;\n drawAtlas(batch.image, batch.commands);\n if (renderer) {\n renderer.recordDrawCall();\n renderer.recordBatch();\n }\n }\n\n // Return commands to pool. Field values remain on the command until\n // the next draw() overwrites them — consumers must read inside the\n // drawAtlas callback, not after flush returns.\n for (const cmd of this._commands) {\n this._pool.push(cmd);\n }\n this._commands.length = 0;\n\n // Release batch buckets (reuse the outer array, drop inner arrays).\n for (let i = 0; i < batches.length; i++) {\n batches[i]!.commands.length = 0;\n }\n batches.length = 0;\n }\n\n /** Clear all queued commands without drawing. */\n clear(): void {\n for (const cmd of this._commands) {\n this._pool.push(cmd);\n }\n this._commands.length = 0;\n }\n\n private _acquire(): SpriteDrawCommand {\n if (this._pool.length > 0) {\n return this._pool.pop()!;\n }\n return {\n image: null!,\n srcX: 0,\n srcY: 0,\n srcW: 0,\n srcH: 0,\n dstX: 0,\n dstY: 0,\n dstW: 0,\n dstH: 0,\n effect: undefined,\n alpha: 1,\n rotation: 0,\n anchorX: 0,\n anchorY: 0,\n stretch: false,\n };\n }\n}\n","/** Color as RGBA (0-1 per channel). */\nexport interface SpriteColor {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n readonly a: number;\n}\n\n/** A sprite effect that can be applied during rendering. */\nexport interface SpriteEffect {\n readonly type: string;\n}\n\n/** Tint effect — blend a color onto the sprite. */\nexport interface TintEffect extends SpriteEffect {\n readonly type: 'tint';\n readonly color: SpriteColor;\n readonly amount: number;\n}\n\n/** Flash effect — overlay a solid color that decays. */\nexport interface FlashEffect extends SpriteEffect {\n readonly type: 'flash';\n readonly color: SpriteColor;\n readonly duration: number;\n readonly elapsed: number;\n}\n\n/** Grayscale effect — desaturate the sprite. */\nexport interface GrayscaleEffect extends SpriteEffect {\n readonly type: 'grayscale';\n readonly amount: number;\n}\n\n/** Outline effect — draw an outline around the sprite. */\nexport interface OutlineEffect extends SpriteEffect {\n readonly type: 'outline';\n readonly color: SpriteColor;\n readonly width: number;\n}\n\n/**\n * Opaque pre-compiled shader program. Typically a Skia `RuntimeEffect` built\n * once at asset load time via `Skia.RuntimeEffect.Make(sksl)`. The render\n * package stays Skia-agnostic; the `@flare-engine/react` layer narrows this to the\n * concrete Skia type when applying the effect.\n */\nexport type CompiledShader = object;\n\n/** Uniform values keyed by name. Scalars for floats, arrays for vec/mat. */\nexport type ShaderUniforms = Record<string, number | readonly number[]>;\n\n/** Custom shader effect — apply a pre-compiled runtime shader with uniforms. */\nexport interface CustomShaderEffect extends SpriteEffect {\n readonly type: 'custom';\n readonly shader: CompiledShader;\n readonly uniforms: ShaderUniforms;\n}\n\n/** Water surface effect — animated UV-displaced sprite with foam crests. */\nexport interface WaterEffect extends SpriteEffect {\n readonly type: 'water';\n readonly color: SpriteColor;\n readonly foamColor: SpriteColor;\n readonly waveSpeed: number;\n readonly waveAmplitude: number;\n}\n\n/** Fog / volumetric haze effect — animated FBM noise layered over a sprite. */\nexport interface FogEffect extends SpriteEffect {\n readonly type: 'fog';\n readonly color: SpriteColor;\n readonly density: number;\n readonly speed: number;\n readonly scale: number;\n}\n\n/** Fire / flame effect — value-noise FBM flame rising upward from a sprite. */\nexport interface FireEffect extends SpriteEffect {\n readonly type: 'fire';\n readonly color: SpriteColor;\n readonly tipColor: SpriteColor;\n readonly speed: number;\n readonly intensity: number;\n}\n\n/**\n * Drop-shadow effect — a single offset, blurred, tinted copy of the sprite drawn\n * beneath it for ship / boss grounding. Time-invariant. Applied via a Skia\n * `ImageFilter.MakeDropShadow` on a reused paint (sprite + shadow in one pass);\n * the offset, blur, and tint all live in the filter, so no extra draw pass.\n */\nexport interface DropShadowEffect extends SpriteEffect {\n readonly type: 'drop-shadow';\n /** Horizontal shadow offset in design px. */\n readonly offsetX: number;\n /** Vertical shadow offset in design px; positive = downward (grounding). */\n readonly offsetY: number;\n /** Gaussian blur sigma applied to the shadow. 0 = hard edge. */\n readonly blur: number;\n /** Shadow color; `a` is the shadow opacity. */\n readonly color: SpriteColor;\n}\n\n// ---------------------------------------------------------------------------\n// Zone background effects — procedural-silhouette SkSL bg layers\n// ---------------------------------------------------------------------------\n\n/** Rooftop city skyline — AABB silhouettes via SDF hash fields. */\nexport interface RoofsCityEffect extends SpriteEffect {\n readonly type: 'roofs-city';\n readonly skyColor: SpriteColor;\n readonly roofColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly density: number;\n /** Destination render width in logical pixels — used as shader resolution.x. */\n readonly resolutionW: number;\n /** Destination render height in logical pixels — used as shader resolution.y. */\n readonly resolutionH: number;\n}\n\n/** Forest canopy — value-noise tree line. */\nexport interface ForestCanopyEffect extends SpriteEffect {\n readonly type: 'forest-canopy';\n readonly skyColor: SpriteColor;\n readonly leafColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly density: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** River surface — 1D height-field wave with foam crests. */\nexport interface RiverWaveEffect extends SpriteEffect {\n readonly type: 'river-wave';\n readonly waterColor: SpriteColor;\n readonly foamColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly waveScale: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Moon surface — polar-SDF craters on a flat disc. */\nexport interface MoonCraterEffect extends SpriteEffect {\n readonly type: 'moon-crater';\n readonly moonColor: SpriteColor;\n readonly craterColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly craterCount: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Cave interior — stalactites hanging from the top edge. */\nexport interface CaveInteriorEffect extends SpriteEffect {\n readonly type: 'cave-interior';\n readonly caveColor: SpriteColor;\n readonly glowColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly stalactiteCount: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Mountain / hill ridge — FBM height-field silhouette. */\nexport interface MountainHillsEffect extends SpriteEffect {\n readonly type: 'mountain-hills';\n readonly skyColor: SpriteColor;\n readonly hillColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly ridgeScale: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Any concrete sprite effect. */\nexport type AnySpriteEffect =\n | TintEffect\n | FlashEffect\n | GrayscaleEffect\n | OutlineEffect\n | CustomShaderEffect\n | WaterEffect\n | FogEffect\n | FireEffect\n | DropShadowEffect\n | RoofsCityEffect\n | ForestCanopyEffect\n | RiverWaveEffect\n | MoonCraterEffect\n | CaveInteriorEffect\n | MountainHillsEffect;\n\n/** Create a tint effect. */\nexport function tint(color: SpriteColor, amount: number): TintEffect {\n return { type: 'tint', color, amount };\n}\n\n/** Create a flash effect. */\nexport function flash(color: SpriteColor, durationSec: number): FlashEffect {\n return { type: 'flash', color, duration: durationSec, elapsed: 0 };\n}\n\n/** Create a grayscale effect. */\nexport function grayscale(amount: number): GrayscaleEffect {\n return { type: 'grayscale', amount };\n}\n\n/** Create an outline effect. */\nexport function outline(color: SpriteColor, width: number): OutlineEffect {\n return { type: 'outline', color, width };\n}\n\n/** Create a custom shader effect. `shader` must be pre-compiled; compiling\n * SkSL per frame is expensive. `uniforms` may be mutated externally between\n * frames to animate parameters. */\nexport function custom(shader: CompiledShader, uniforms: ShaderUniforms): CustomShaderEffect {\n return { type: 'custom', shader, uniforms };\n}\n\n/** Create a water surface effect. Time is passed to the renderer each frame\n * via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function water(\n color: SpriteColor,\n foamColor: SpriteColor,\n waveSpeed: number,\n waveAmplitude: number,\n): WaterEffect {\n return { type: 'water', color, foamColor, waveSpeed, waveAmplitude };\n}\n\n/** Create a fog / volumetric haze effect. Time is passed to the renderer each\n * frame via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function fog(color: SpriteColor, density = 0.5, speed = 0.3, scale = 2.0): FogEffect {\n return { type: 'fog', color, density, speed, scale };\n}\n\n/** Create a fire / flame effect. Time is passed to the renderer each\n * frame via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function fire(opts: {\n color: SpriteColor;\n tipColor: SpriteColor;\n speed?: number;\n intensity?: number;\n}): FireEffect {\n return {\n type: 'fire',\n color: opts.color,\n tipColor: opts.tipColor,\n speed: opts.speed ?? 1.0,\n intensity: opts.intensity ?? 0.8,\n };\n}\n\n/** Create a drop-shadow effect. Defaults to a soft shadow offset downward for\n * grounding; `color.a` is the shadow opacity. */\nexport function dropShadow(opts: {\n color: SpriteColor;\n offsetX?: number;\n offsetY?: number;\n blur?: number;\n}): DropShadowEffect {\n return {\n type: 'drop-shadow',\n color: opts.color,\n offsetX: opts.offsetX ?? 0,\n offsetY: opts.offsetY ?? 6,\n blur: opts.blur ?? 4,\n };\n}\n\n/** Create a rooftop city skyline effect. */\nexport function roofsCity(opts: {\n skyColor: SpriteColor;\n roofColor: SpriteColor;\n scrollOffsetY?: number;\n density?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): RoofsCityEffect {\n return {\n type: 'roofs-city',\n skyColor: opts.skyColor,\n roofColor: opts.roofColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n density: opts.density ?? 6,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a forest canopy effect. */\nexport function forestCanopy(opts: {\n skyColor: SpriteColor;\n leafColor: SpriteColor;\n scrollOffsetY?: number;\n density?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): ForestCanopyEffect {\n return {\n type: 'forest-canopy',\n skyColor: opts.skyColor,\n leafColor: opts.leafColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n density: opts.density ?? 5,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a river wave surface effect. */\nexport function riverWave(opts: {\n waterColor: SpriteColor;\n foamColor: SpriteColor;\n scrollOffsetY?: number;\n waveScale?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): RiverWaveEffect {\n return {\n type: 'river-wave',\n waterColor: opts.waterColor,\n foamColor: opts.foamColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n waveScale: opts.waveScale ?? 2,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a moon crater surface effect. */\nexport function moonCrater(opts: {\n moonColor: SpriteColor;\n craterColor: SpriteColor;\n scrollOffsetY?: number;\n craterCount?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): MoonCraterEffect {\n return {\n type: 'moon-crater',\n moonColor: opts.moonColor,\n craterColor: opts.craterColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n craterCount: opts.craterCount ?? 4,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a cave interior effect. */\nexport function caveInterior(opts: {\n caveColor: SpriteColor;\n glowColor: SpriteColor;\n scrollOffsetY?: number;\n stalactiteCount?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): CaveInteriorEffect {\n return {\n type: 'cave-interior',\n caveColor: opts.caveColor,\n glowColor: opts.glowColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n stalactiteCount: opts.stalactiteCount ?? 8,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a mountain / hill ridge effect. */\nexport function mountainHills(opts: {\n skyColor: SpriteColor;\n hillColor: SpriteColor;\n scrollOffsetY?: number;\n ridgeScale?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): MountainHillsEffect {\n return {\n type: 'mountain-hills',\n skyColor: opts.skyColor,\n hillColor: opts.hillColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n ridgeScale: opts.ridgeScale ?? 1.5,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/**\n * Advance a time-based effect by `dt` seconds.\n *\n * Returns a new effect object if state changed (e.g. flash elapsed time\n * advances), the same reference if the effect is time-invariant (tint,\n * grayscale, outline, custom), or `null` if the effect has expired.\n *\n * Effects are immutable — callers must replace their reference with the\n * returned value. Sharing an effect instance across entities is safe.\n */\nexport function updateEffect(effect: AnySpriteEffect, dt: number): AnySpriteEffect | null {\n if (effect.type === 'flash') {\n const elapsed = effect.elapsed + dt;\n if (elapsed >= effect.duration) return null;\n return { type: 'flash', color: effect.color, duration: effect.duration, elapsed };\n }\n return effect;\n}\n\n/**\n * Get the current intensity of a flash effect (1 at start, 0 at end).\n */\nexport function flashIntensity(effect: FlashEffect): number {\n if (effect.duration <= 0) return 0;\n const t = effect.elapsed / effect.duration;\n return Math.max(0, 1 - t);\n}\n","export const FIRE_SKSL = /* sksl */ `\nuniform shader src;\nuniform float2 resolution;\nuniform float3 color;\nuniform float3 tipColor;\nuniform float speed;\nuniform float intensity;\nuniform float time;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat vnoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + float2(1.0, 0.0));\n float c = hash(i + float2(0.0, 1.0));\n float d = hash(i + float2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(float2 p) {\n return 0.5 * vnoise(p) + 0.25 * vnoise(p * 2.0);\n}\n\nhalf4 main(float2 coord) {\n half4 srcColor = src.eval(coord);\n float2 uv = coord / max(resolution, float2(1.0));\n float2 np = float2(uv.x * 4.0, uv.y * 6.0 - time * speed * 2.0);\n float n = fbm(np);\n float flameMask = smoothstep(0.0, intensity, n * (1.0 - uv.y));\n float3 fireRgb = mix(color, tipColor, uv.y);\n half4 fireColor = half4(fireRgb.r, fireRgb.g, fireRgb.b, flameMask);\n return half4(fireColor.rgb, fireColor.a * srcColor.a);\n}\n`;\n\nexport const FOG_SKSL = /* sksl */ `\nuniform shader src;\nuniform float2 resolution;\nuniform float3 color;\nuniform float density;\nuniform float speed;\nuniform float scale;\nuniform float time;\n\nfloat hash(float2 p) {\n return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453);\n}\n\nfloat valueNoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + float2(1.0, 0.0));\n float c = hash(i + float2(0.0, 1.0));\n float d = hash(i + float2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(float2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n for (int i = 0; i < 4; i++) {\n value += amplitude * valueNoise(p);\n p *= 2.0;\n amplitude *= 0.5;\n }\n return value;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float2 drift = float2(time * speed, 0.3 * time * speed);\n float n = clamp(fbm(uv * scale + drift), 0.0, 1.0);\n half4 srcColor = src.eval(coord);\n half3 blended = mix(srcColor.rgb, half3(color.r, color.g, color.b), half(density * n));\n return half4(blended, srcColor.a);\n}\n`;\n\nexport const WATER_SKSL = /* sksl */ `\nuniform shader src;\nuniform float2 resolution;\nuniform float3 color;\nuniform float3 foamColor;\nuniform float waveSpeed;\nuniform float waveAmplitude;\nuniform float time;\n\nhalf4 main(float2 coord) {\n float2 uv = coord / resolution;\n float wave1 = sin(uv.x * 6.2832 * 3.0 + time * waveSpeed * 2.0) * waveAmplitude;\n float wave2 = sin(uv.x * 6.2832 * 5.0 - time * waveSpeed * 1.3 + 1.5) * waveAmplitude * 0.5;\n float totalWave = wave1 + wave2;\n float2 displaced = float2(uv.x, uv.y + totalWave);\n displaced = clamp(displaced, float2(0.0), float2(1.0));\n half4 c = src.eval(displaced * resolution);\n float waveDeriv = cos(uv.x * 6.2832 * 3.0 + time * waveSpeed * 2.0) * waveAmplitude * 2.0\n + cos(uv.x * 6.2832 * 5.0 - time * waveSpeed * 1.3 + 1.5) * waveAmplitude;\n float foam = smoothstep(waveAmplitude * 0.6, waveAmplitude * 1.5, waveDeriv);\n half3 tinted = mix(c.rgb, half3(color.r, color.g, color.b), half(0.35));\n half3 final = mix(tinted, half3(foamColor.r, foamColor.g, foamColor.b), half(foam));\n return half4(final, c.a);\n}\n`;\n\n// ---------------------------------------------------------------------------\n// Zone background shaders — procedural-silhouette SkSL\n// Each shader generates a recognizable background silhouette via SDFs / noise.\n// All accept a scrollOffsetY uniform driven by Skia-bridge time so the\n// background parallax-scrolls without CPU-side UV manipulation.\n// ---------------------------------------------------------------------------\n\nexport const ROOFS_CITY_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 roofColor;\nuniform float scrollOffsetY;\nuniform float density;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat buildingTop(float2 uv, float density) {\n float col = floor(uv.x * density);\n float h = 0.35 + 0.35 * hash(col);\n float w = 0.6 + 0.3 * hash(col + 7.3);\n float edge = step(abs(fract(uv.x * density) - 0.5), w * 0.5);\n return edge * step(uv.y, h);\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n uv.y = fract(uv.y);\n float buildings = buildingTop(uv, density);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(roofColor.r, roofColor.g, roofColor.b),\n half(buildings));\n return half4(col, 1.0);\n}\n`;\n\nexport const FOREST_CANOPY_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 leafColor;\nuniform float scrollOffsetY;\nuniform float density;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat valueNoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n return mix(mix(hash(i), hash(i + float2(1.0, 0.0)), u.x),\n mix(hash(i + float2(0.0, 1.0)), hash(i + float2(1.0, 1.0)), u.x), u.y);\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float n = valueNoise(float2(uv.x * density, uv.y * 3.0));\n float treeLine = 0.55 + 0.25 * valueNoise(float2(uv.x * density * 0.5, 0.0));\n float canopy = step(uv.y, treeLine) * smoothstep(0.3, 0.7, n);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(leafColor.r, leafColor.g, leafColor.b),\n half(canopy));\n return half4(col, 1.0);\n}\n`;\n\nexport const RIVER_WAVE_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 waterColor;\nuniform float3 foamColor;\nuniform float scrollOffsetY;\nuniform float waveScale;\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float scroll = scrollOffsetY / max(resolution.y, 1.0);\n float2 p = float2(uv.x, uv.y + scroll);\n float w1 = sin(p.x * 6.2832 * waveScale + p.y * 4.0) * 0.04;\n float w2 = sin(p.x * 6.2832 * waveScale * 1.7 - p.y * 2.5 + 1.2) * 0.02;\n float surface = 0.5 + w1 + w2;\n float inWater = step(uv.y, surface);\n float crest = smoothstep(surface - 0.015, surface, uv.y) * (1.0 - inWater) +\n smoothstep(surface + 0.015, surface, uv.y) * inWater;\n half3 base = mix(half3(foamColor.r, foamColor.g, foamColor.b),\n half3(waterColor.r, waterColor.g, waterColor.b),\n half(inWater));\n half3 col = mix(base, half3(foamColor.r, foamColor.g, foamColor.b), half(crest * 0.6));\n return half4(col, 1.0);\n}\n`;\n\nexport const MOON_CRATER_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 moonColor;\nuniform float3 craterColor;\nuniform float scrollOffsetY;\nuniform float craterCount;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat crater(float2 uv, float2 center, float r) {\n float d = length(uv - center);\n float rim = smoothstep(r, r * 0.85, d) - smoothstep(r * 0.85, r * 0.65, d);\n float floor_ = smoothstep(r * 0.65, r * 0.4, d);\n return rim * 0.6 + floor_ * 0.25;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float c = 0.0;\n for (int i = 0; i < 6; i++) {\n float fi = float(i);\n float2 center = float2(hash(float2(fi, 0.0)), hash(float2(fi, 1.0)));\n float r = 0.04 + 0.07 * hash(float2(fi, 2.0));\n c += crater(fract(uv), center, r) * step(fi, craterCount - 1.0);\n }\n c = clamp(c, 0.0, 1.0);\n half3 col = mix(half3(moonColor.r, moonColor.g, moonColor.b),\n half3(craterColor.r, craterColor.g, craterColor.b),\n half(c));\n return half4(col, 1.0);\n}\n`;\n\nexport const CAVE_INTERIOR_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 caveColor;\nuniform float3 glowColor;\nuniform float scrollOffsetY;\nuniform float stalactiteCount;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat stalactite(float2 uv, float count) {\n float col = floor(uv.x * count);\n float tip = 0.1 + 0.2 * hash(col);\n float w = 0.3 + 0.3 * hash(col + 5.0);\n float dx = abs(fract(uv.x * count) - 0.5) / (w * 0.5);\n float shape = step(dx, 1.0) * step(uv.y, tip * (1.0 - clamp(dx, 0.0, 1.0)));\n return shape;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = 1.0 - uv.y;\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float stalac = stalactite(uv, stalactiteCount);\n float glow = smoothstep(0.8, 1.0, uv.y) * (1.0 - stalac);\n half3 col = mix(half3(caveColor.r, caveColor.g, caveColor.b),\n half3(glowColor.r, glowColor.g, glowColor.b),\n half(glow));\n col = mix(col, half3(caveColor.r, caveColor.g, caveColor.b) * half(0.3), half(stalac));\n return half4(col, 1.0);\n}\n`;\n\nexport const MOUNTAIN_HILLS_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 hillColor;\nuniform float scrollOffsetY;\nuniform float ridgeScale;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat heightField(float x, float scale) {\n float h = 0.0;\n float amp = 0.5;\n float freq = scale;\n for (int i = 0; i < 4; i++) {\n float ix = floor(x * freq);\n float fx = fract(x * freq);\n fx = fx * fx * (3.0 - 2.0 * fx);\n h += amp * mix(hash(ix), hash(ix + 1.0), fx);\n freq *= 2.0;\n amp *= 0.5;\n }\n return h;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float scroll = scrollOffsetY / max(resolution.y, 1.0);\n float horizon = 0.4 + 0.3 * heightField(uv.x + scroll * 0.3, ridgeScale);\n float mountains = step(uv.y, horizon);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(hillColor.r, hillColor.g, hillColor.b),\n half(mountains));\n return half4(col, 1.0);\n}\n`;\n","import type { SkImageLike } from '@flare-engine/assets';\nimport type { CanvasLike } from './renderer.js';\nimport {\n CAVE_INTERIOR_SKSL,\n FIRE_SKSL,\n FOG_SKSL,\n FOREST_CANOPY_SKSL,\n MOON_CRATER_SKSL,\n MOUNTAIN_HILLS_SKSL,\n RIVER_WAVE_SKSL,\n ROOFS_CITY_SKSL,\n WATER_SKSL,\n} from './shaders.js';\nimport type { SpriteDrawCommand } from './sprite-batch.js';\nimport {\n type AnySpriteEffect,\n type CaveInteriorEffect,\n type CustomShaderEffect,\n type DropShadowEffect,\n type FireEffect,\n type FlashEffect,\n type FogEffect,\n type ForestCanopyEffect,\n type GrayscaleEffect,\n type MoonCraterEffect,\n type MountainHillsEffect,\n type OutlineEffect,\n type RiverWaveEffect,\n type RoofsCityEffect,\n type SpriteColor,\n type TintEffect,\n type WaterEffect,\n flashIntensity,\n} from './sprite-effects.js';\n\n// ---------------------------------------------------------------------------\n// Structural interfaces — no @shopify/react-native-skia import anywhere here.\n// ---------------------------------------------------------------------------\n\n/** A 4-float RGBA color buffer (r, g, b, a in [0, 1]). */\nexport type ColorLike = Float32Array;\n\n/**\n * A mutable axis-aligned rect.\n *\n * The `x`, `y`, `width`, `height` fields are required so that RN-Skia's\n * `SkHostRect` (which carries those fields) structurally satisfies this\n * interface in contra-variant parameter positions.\n */\nexport interface RectLike {\n x: number;\n y: number;\n width: number;\n height: number;\n setXYWH(x: number, y: number, w: number, h: number): void;\n}\n\n/**\n * A mutable similarity transform.\n *\n * The `scos`, `ssin`, `tx`, `ty` fields are readable so that CK-backed\n * canvas wrappers can flatten pools into Float32Arrays without allocation.\n * RN-Skia's `SkRSXform` exposes all four fields.\n */\nexport interface RSXformLike {\n scos: number;\n ssin: number;\n tx: number;\n ty: number;\n set(scos: number, ssin: number, tx: number, ty: number): void;\n}\n\n/** A compiled SkSL runtime effect. */\nexport interface RuntimeEffectLike {\n makeShader(uniforms: number[]): unknown;\n getUniformCount(): number;\n getUniformFloatCount(): number;\n getUniformName(index: number): string;\n getUniform(index: number): { columns: number; rows: number; slot: number };\n}\n\n/** Color filter factory — subset used by the frame-renderer. */\nexport interface ColorFilterFactoryLike {\n MakeBlend(color: Float32Array, mode: number): unknown;\n MakeMatrix(matrix: number[]): unknown;\n}\n\n/** Image filter factory — optional; absent on CPU/test surfaces. */\nexport interface ImageFilterFactoryLike {\n MakeDropShadow(\n dx: number,\n dy: number,\n sigmaX: number,\n sigmaY: number,\n color: Float32Array,\n input: null,\n ): unknown;\n}\n\n/** Runtime-effect factory — optional; absent when SkSL is unsupported. */\nexport interface RuntimeEffectFactoryLike {\n Make(sksl: string): RuntimeEffectLike | null;\n}\n\n/** Paint — the writable subset the frame-renderer mutates. */\nexport interface PaintLike {\n setAlphaf(alpha: number): void;\n setColor?(color: Float32Array): void;\n setColorFilter(filter: unknown): void;\n setShader(shader: unknown): void;\n setImageFilter(filter: unknown): void;\n}\n\n/** Minimal draw surface the frame-renderer calls into. */\nexport interface DrawSurfaceLike {\n save(): number;\n restore(): void;\n translate(x: number, y: number): void;\n scale(sx: number, sy: number): void;\n /** rotate(radians, cx, cy) — always in radians. Web shims must convert. */\n rotate(radians: number, cx: number, cy: number): void;\n clear(color: Float32Array): void;\n drawAtlas(image: unknown, srcRects: RectLike[], dstXforms: RSXformLike[], paint: PaintLike): void;\n drawImageRect(image: unknown, src: RectLike, dst: RectLike, paint: PaintLike): void;\n drawRect(rect: RectLike, paint: PaintLike): void;\n}\n\n/**\n * Opaque picture (recorded draw commands).\n * Typed as `object` so both `SkPicture` (RN-Skia) and CanvasKit's\n * `SkPicture` satisfy it without structural mismatches.\n * Callers that need concrete type methods cast to the platform type.\n */\nexport type PictureLike = object;\n\n/** Picture recorder. */\nexport interface PictureRecorderLike {\n beginRecording(bounds: RectLike): DrawSurfaceLike;\n finishRecordingAsPicture(): PictureLike;\n}\n\n/**\n * Minimal structural view of the Skia module the frame-renderer needs.\n * Declared here so both `@flare-engine/react` and `@flare-engine/render-web`\n * can pass their respective backends without coupling to a concrete Skia SDK.\n *\n * The `Surface` and `ImageFilter` slots are optional because they are absent\n * on CPU surfaces and in test mocks.\n */\nexport interface FrameRendererModule {\n XYWHRect(x: number, y: number, w: number, h: number): RectLike;\n RSXform(scos: number, ssin: number, tx: number, ty: number): RSXformLike;\n Paint(): PaintLike;\n PictureRecorder(): PictureRecorderLike;\n ColorFilter: ColorFilterFactoryLike;\n RuntimeEffect?: RuntimeEffectFactoryLike;\n ImageFilter?: ImageFilterFactoryLike;\n}\n\n/**\n * A single compiled postfx pass descriptor produced by the runtime.\n */\nexport interface FrameRendererCompiledPass {\n readonly id: string;\n readonly shader: {\n makeShaderWithChildren(uniforms: number[], children: unknown[]): unknown | null;\n getUniformCount?(): number;\n getUniformName?(index: number): string;\n getUniform?(index: number): { columns: number; rows: number; slot: number };\n };\n readonly uniforms: Float32Array;\n readonly children: readonly unknown[];\n}\n\n/** Minimal structural view of a PostFxRuntime that the frame-renderer needs. */\nexport interface FrameRendererPostFxRuntime {\n readonly supported: boolean;\n buildPass?(\n chain: FrameRendererPostFxChain,\n width: number,\n height: number,\n ): readonly FrameRendererCompiledPass[];\n}\n\n/** Minimal structural view of an EffectChain the frame-renderer needs. */\nexport interface FrameRendererPostFxChain {\n enabled(): Iterable<unknown>;\n}\n\n/** A resolved postfx pass descriptor with named uniforms (for React JSX). */\nexport interface FrameRendererPostFxDescriptor {\n readonly id: string;\n readonly source: unknown;\n readonly uniforms: Record<string, number | readonly number[]>;\n readonly children: readonly unknown[];\n}\n\n/**\n * Frame renderer that bridges a `SpriteBatch` to a picture-backed draw surface.\n *\n * Each frame:\n * 1. Call `beginFrame(viewportWidth, viewportHeight)`.\n * 2. Call `drawAtlas(image, commands)` one or more times.\n * 3. Call `endFrame()` — returns the recorded picture.\n */\nexport interface FrameRenderer {\n readonly canvas: CanvasLike;\n beginFrame(\n viewportWidth: number,\n viewportHeight: number,\n time?: number,\n pixelDensity?: number,\n ): void;\n drawAtlas(image: SkImageLike, commands: SpriteDrawCommand[]): void;\n buildPostFxPasses(\n runtime: FrameRendererPostFxRuntime | null,\n chain: FrameRendererPostFxChain | null,\n width: number,\n height: number,\n ): readonly FrameRendererPostFxDescriptor[];\n setPostFxBinding(\n runtime: FrameRendererPostFxRuntime | null,\n chain: FrameRendererPostFxChain | null,\n ): void;\n endFrame(): PictureLike | null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants\n// ---------------------------------------------------------------------------\n\nconst BLEND_SRC_OVER = 3;\nconst BLEND_SRC_IN = 5;\n\nconst OUTLINE_OFFSETS: ReadonlyArray<readonly [number, number]> = [\n [1, 0],\n [-1, 0],\n [0, 1],\n [0, -1],\n [Math.SQRT1_2, Math.SQRT1_2],\n [Math.SQRT1_2, -Math.SQRT1_2],\n [-Math.SQRT1_2, Math.SQRT1_2],\n [-Math.SQRT1_2, -Math.SQRT1_2],\n];\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `FrameRenderer` backed by the given structural Skia module.\n *\n * Both `@flare-engine/react` (passing the real RN-Skia module) and\n * `@flare-engine/render-web` (passing a CanvasKit shim) use this factory.\n */\nexport function createFrameRenderer(Skia: FrameRendererModule): FrameRenderer {\n const defaultPaint: PaintLike = Skia.Paint();\n\n let tintPaint: PaintLike | null = null;\n let flashPaint: PaintLike | null = null;\n let grayscalePaint: PaintLike | null = null;\n let outlinePaint: PaintLike | null = null;\n let customPaint: PaintLike | null = null;\n let waterPaint: PaintLike | null = null;\n let waterRuntimeEffect: RuntimeEffectLike | null = null;\n let fogPaint: PaintLike | null = null;\n let fogRuntimeEffect: RuntimeEffectLike | null = null;\n let firePaint: PaintLike | null = null;\n let fireRuntimeEffect: RuntimeEffectLike | null = null;\n let dropShadowPaint: PaintLike | null = null;\n\n let roofsCityPaint: PaintLike | null = null;\n let roofsCityEffect: RuntimeEffectLike | null = null;\n let forestCanopyPaint: PaintLike | null = null;\n let forestCanopyEffect: RuntimeEffectLike | null = null;\n let riverWavePaint: PaintLike | null = null;\n let riverWaveEffect: RuntimeEffectLike | null = null;\n let moonCraterPaint: PaintLike | null = null;\n let moonCraterEffect: RuntimeEffectLike | null = null;\n let caveInteriorPaint: PaintLike | null = null;\n let caveInteriorEffect: RuntimeEffectLike | null = null;\n let mountainHillsPaint: PaintLike | null = null;\n let mountainHillsEffect: RuntimeEffectLike | null = null;\n\n const roofsCityUniforms: number[] = new Array(10).fill(0);\n const forestCanopyUniforms: number[] = new Array(10).fill(0);\n const riverWaveUniforms: number[] = new Array(10).fill(0);\n const moonCraterUniforms: number[] = new Array(10).fill(0);\n const caveInteriorUniforms: number[] = new Array(10).fill(0);\n const mountainHillsUniforms: number[] = new Array(10).fill(0);\n\n const recorder: PictureRecorderLike = Skia.PictureRecorder();\n const recordingBounds: RectLike = Skia.XYWHRect(0, 0, 0, 0);\n\n const srcRects: RectLike[] = [];\n const xforms: RSXformLike[] = [];\n\n const stretchSrcRects: RectLike[] = [];\n const stretchDstRects: RectLike[] = [];\n let stretchPaint: PaintLike | null = null;\n\n const zoneBgRects: RectLike[] = [];\n\n let _cachedSrcSlice: RectLike[] | null = null;\n let _cachedSrcSliceLen = 0;\n let _cachedXformSlice: RSXformLike[] | null = null;\n let _cachedXformSliceLen = 0;\n\n const clearColor: Float32Array = new Float32Array(4);\n const tintColor: Float32Array = new Float32Array(4);\n const flashColor: Float32Array = new Float32Array(4);\n const outlineColor: Float32Array = new Float32Array(4);\n const dropShadowColor: Float32Array = new Float32Array(4);\n\n const grayMatrix: number[] = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0];\n const customUniforms: number[] = [];\n\n const waterUniforms: number[] = new Array(11).fill(0);\n const fogUniforms: number[] = new Array(9).fill(0);\n const fireUniforms: number[] = new Array(11).fill(0);\n\n let currentFrameTime = 0;\n let current: DrawSurfaceLike | null = null;\n\n let _boundRuntime: FrameRendererPostFxRuntime | null = null;\n let _boundChain: FrameRendererPostFxChain | null = null;\n\n const _loggedOnce = new Set<string>();\n function logOnce(msg: string): void {\n if (_loggedOnce.has(msg)) return;\n _loggedOnce.add(msg);\n console.warn(`[postfx] ${msg}`);\n }\n\n const canvas: CanvasLike = {\n save: () => current?.save() ?? 0,\n restore: () => {\n current?.restore();\n },\n translate: (x, y) => {\n current?.translate(x, y);\n },\n scale: (sx, sy) => {\n current?.scale(sx, sy);\n },\n rotate: (radians, cx, cy) => {\n current?.rotate(radians, cx, cy);\n },\n clear: (argb) => {\n if (current === null) return;\n clearColor[0] = ((argb >>> 16) & 0xff) / 255;\n clearColor[1] = ((argb >>> 8) & 0xff) / 255;\n clearColor[2] = (argb & 0xff) / 255;\n clearColor[3] = ((argb >>> 24) & 0xff) / 255;\n current.clear(clearColor);\n },\n };\n\n function ensurePoolCapacity(n: number): void {\n while (srcRects.length < n) {\n srcRects.push(Skia.XYWHRect(0, 0, 0, 0));\n xforms.push(Skia.RSXform(1, 0, 0, 0));\n }\n }\n\n function ensureStretchPoolCapacity(n: number): void {\n while (stretchSrcRects.length < n) {\n stretchSrcRects.push(Skia.XYWHRect(0, 0, 0, 0));\n stretchDstRects.push(Skia.XYWHRect(0, 0, 0, 0));\n }\n }\n\n function ensureZoneBgPoolCapacity(n: number): void {\n while (zoneBgRects.length < n) {\n zoneBgRects.push(Skia.XYWHRect(0, 0, 0, 0));\n }\n }\n\n function getCachedSrcSlice(n: number): RectLike[] {\n if (_cachedSrcSlice !== null && _cachedSrcSliceLen === n) return _cachedSrcSlice;\n _cachedSrcSlice = srcRects.length === n ? srcRects : srcRects.slice(0, n);\n _cachedSrcSliceLen = n;\n return _cachedSrcSlice;\n }\n\n function getCachedXformSlice(n: number): RSXformLike[] {\n if (_cachedXformSlice !== null && _cachedXformSliceLen === n) return _cachedXformSlice;\n _cachedXformSlice = xforms.length === n ? xforms : xforms.slice(0, n);\n _cachedXformSliceLen = n;\n return _cachedXformSlice;\n }\n\n function fillSrcRects(commands: SpriteDrawCommand[], n: number): void {\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n srcRects[i]?.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);\n }\n }\n\n function fillXforms(commands: SpriteDrawCommand[], n: number, dx: number, dy: number): void {\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n const scale = cmd.srcW === 0 ? 1 : cmd.dstW / cmd.srcW;\n if (cmd.rotation === 0) {\n xforms[i]?.set(scale, 0, cmd.dstX + dx, cmd.dstY + dy);\n } else {\n const c = Math.cos(cmd.rotation);\n const s = Math.sin(cmd.rotation);\n const scos = scale * c;\n const ssin = scale * s;\n const px = cmd.dstX + cmd.anchorX * cmd.dstW + dx;\n const py = cmd.dstY + cmd.anchorY * cmd.dstH + dy;\n const axSrc = cmd.anchorX * cmd.srcW;\n const aySrc = cmd.anchorY * cmd.srcH;\n xforms[i]?.set(\n scos,\n ssin,\n px - scos * axSrc + ssin * aySrc,\n py - ssin * axSrc - scos * aySrc,\n );\n }\n }\n }\n\n function applyTint(effect: TintEffect): PaintLike {\n if (tintPaint === null) tintPaint = Skia.Paint();\n writeColor(tintColor, effect.color, effect.amount);\n tintPaint.setColorFilter(Skia.ColorFilter.MakeBlend(tintColor, BLEND_SRC_OVER));\n return tintPaint;\n }\n\n function applyFlash(effect: FlashEffect): PaintLike {\n if (flashPaint === null) flashPaint = Skia.Paint();\n writeColor(flashColor, effect.color, flashIntensity(effect));\n flashPaint.setColorFilter(Skia.ColorFilter.MakeBlend(flashColor, BLEND_SRC_OVER));\n return flashPaint;\n }\n\n function applyGrayscale(effect: GrayscaleEffect): PaintLike {\n if (grayscalePaint === null) grayscalePaint = Skia.Paint();\n writeGrayscaleMatrix(grayMatrix, effect.amount);\n grayscalePaint.setColorFilter(Skia.ColorFilter.MakeMatrix(grayMatrix));\n return grayscalePaint;\n }\n\n function applyOutlinePaint(effect: OutlineEffect): PaintLike {\n if (outlinePaint === null) outlinePaint = Skia.Paint();\n writeColor(outlineColor, effect.color, 1);\n outlinePaint.setColorFilter(Skia.ColorFilter.MakeBlend(outlineColor, BLEND_SRC_IN));\n return outlinePaint;\n }\n\n function applyCustom(effect: CustomShaderEffect): PaintLike {\n if (customPaint === null) customPaint = Skia.Paint();\n const shader = effect.shader as RuntimeEffectLike;\n const floatCount = shader.getUniformFloatCount();\n while (customUniforms.length < floatCount) customUniforms.push(0);\n customUniforms.length = floatCount;\n fillCustomUniforms(effect, shader, customUniforms);\n customPaint.setShader(shader.makeShader(customUniforms));\n return customPaint;\n }\n\n function applyWater(effect: WaterEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (waterRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(WATER_SKSL);\n if (compiled === null) return defaultPaint;\n waterRuntimeEffect = compiled;\n }\n if (waterPaint === null) waterPaint = Skia.Paint();\n waterUniforms[0] = 0;\n waterUniforms[1] = 0;\n waterUniforms[2] = effect.color.r;\n waterUniforms[3] = effect.color.g;\n waterUniforms[4] = effect.color.b;\n waterUniforms[5] = effect.foamColor.r;\n waterUniforms[6] = effect.foamColor.g;\n waterUniforms[7] = effect.foamColor.b;\n waterUniforms[8] = effect.waveSpeed;\n waterUniforms[9] = effect.waveAmplitude;\n waterUniforms[10] = time;\n waterPaint.setShader(waterRuntimeEffect.makeShader(waterUniforms));\n return waterPaint;\n }\n\n function applyFog(effect: FogEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (fogRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FOG_SKSL);\n if (compiled === null) return defaultPaint;\n fogRuntimeEffect = compiled;\n }\n if (fogPaint === null) fogPaint = Skia.Paint();\n fogUniforms[0] = 0;\n fogUniforms[1] = 0;\n fogUniforms[2] = effect.color.r;\n fogUniforms[3] = effect.color.g;\n fogUniforms[4] = effect.color.b;\n fogUniforms[5] = effect.density;\n fogUniforms[6] = effect.speed;\n fogUniforms[7] = effect.scale;\n fogUniforms[8] = time;\n fogPaint.setShader(fogRuntimeEffect.makeShader(fogUniforms));\n return fogPaint;\n }\n\n function applyFire(effect: FireEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (fireRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FIRE_SKSL);\n if (compiled === null) return defaultPaint;\n fireRuntimeEffect = compiled;\n }\n if (firePaint === null) firePaint = Skia.Paint();\n fireUniforms[0] = 0;\n fireUniforms[1] = 0;\n fireUniforms[2] = effect.color.r;\n fireUniforms[3] = effect.color.g;\n fireUniforms[4] = effect.color.b;\n fireUniforms[5] = effect.tipColor.r;\n fireUniforms[6] = effect.tipColor.g;\n fireUniforms[7] = effect.tipColor.b;\n fireUniforms[8] = effect.speed;\n fireUniforms[9] = effect.intensity;\n fireUniforms[10] = time;\n firePaint.setShader(fireRuntimeEffect.makeShader(fireUniforms));\n return firePaint;\n }\n\n function applyDropShadow(effect: DropShadowEffect): PaintLike {\n if (Skia.ImageFilter === undefined) return defaultPaint;\n if (dropShadowPaint === null) dropShadowPaint = Skia.Paint();\n writeColor(dropShadowColor, effect.color, 1);\n dropShadowPaint.setImageFilter(\n Skia.ImageFilter.MakeDropShadow(\n effect.offsetX,\n effect.offsetY,\n effect.blur,\n effect.blur,\n dropShadowColor,\n null,\n ),\n );\n return dropShadowPaint;\n }\n\n function applyRoofsCity(effect: RoofsCityEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (roofsCityEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(ROOFS_CITY_SKSL);\n if (compiled === null) return defaultPaint;\n roofsCityEffect = compiled;\n }\n if (roofsCityPaint === null) roofsCityPaint = Skia.Paint();\n roofsCityUniforms[0] = effect.resolutionW;\n roofsCityUniforms[1] = effect.resolutionH;\n roofsCityUniforms[2] = effect.skyColor.r;\n roofsCityUniforms[3] = effect.skyColor.g;\n roofsCityUniforms[4] = effect.skyColor.b;\n roofsCityUniforms[5] = effect.roofColor.r;\n roofsCityUniforms[6] = effect.roofColor.g;\n roofsCityUniforms[7] = effect.roofColor.b;\n roofsCityUniforms[8] = effect.scrollOffsetY;\n roofsCityUniforms[9] = effect.density;\n roofsCityPaint.setShader(roofsCityEffect.makeShader(roofsCityUniforms));\n return roofsCityPaint;\n }\n\n function applyForestCanopy(effect: ForestCanopyEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (forestCanopyEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FOREST_CANOPY_SKSL);\n if (compiled === null) return defaultPaint;\n forestCanopyEffect = compiled;\n }\n if (forestCanopyPaint === null) forestCanopyPaint = Skia.Paint();\n forestCanopyUniforms[0] = effect.resolutionW;\n forestCanopyUniforms[1] = effect.resolutionH;\n forestCanopyUniforms[2] = effect.skyColor.r;\n forestCanopyUniforms[3] = effect.skyColor.g;\n forestCanopyUniforms[4] = effect.skyColor.b;\n forestCanopyUniforms[5] = effect.leafColor.r;\n forestCanopyUniforms[6] = effect.leafColor.g;\n forestCanopyUniforms[7] = effect.leafColor.b;\n forestCanopyUniforms[8] = effect.scrollOffsetY;\n forestCanopyUniforms[9] = effect.density;\n forestCanopyPaint.setShader(forestCanopyEffect.makeShader(forestCanopyUniforms));\n return forestCanopyPaint;\n }\n\n function applyRiverWave(effect: RiverWaveEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (riverWaveEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(RIVER_WAVE_SKSL);\n if (compiled === null) return defaultPaint;\n riverWaveEffect = compiled;\n }\n if (riverWavePaint === null) riverWavePaint = Skia.Paint();\n riverWaveUniforms[0] = effect.resolutionW;\n riverWaveUniforms[1] = effect.resolutionH;\n riverWaveUniforms[2] = effect.waterColor.r;\n riverWaveUniforms[3] = effect.waterColor.g;\n riverWaveUniforms[4] = effect.waterColor.b;\n riverWaveUniforms[5] = effect.foamColor.r;\n riverWaveUniforms[6] = effect.foamColor.g;\n riverWaveUniforms[7] = effect.foamColor.b;\n riverWaveUniforms[8] = effect.scrollOffsetY;\n riverWaveUniforms[9] = effect.waveScale;\n riverWavePaint.setShader(riverWaveEffect.makeShader(riverWaveUniforms));\n return riverWavePaint;\n }\n\n function applyMoonCrater(effect: MoonCraterEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (moonCraterEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(MOON_CRATER_SKSL);\n if (compiled === null) return defaultPaint;\n moonCraterEffect = compiled;\n }\n if (moonCraterPaint === null) moonCraterPaint = Skia.Paint();\n moonCraterUniforms[0] = effect.resolutionW;\n moonCraterUniforms[1] = effect.resolutionH;\n moonCraterUniforms[2] = effect.moonColor.r;\n moonCraterUniforms[3] = effect.moonColor.g;\n moonCraterUniforms[4] = effect.moonColor.b;\n moonCraterUniforms[5] = effect.craterColor.r;\n moonCraterUniforms[6] = effect.craterColor.g;\n moonCraterUniforms[7] = effect.craterColor.b;\n moonCraterUniforms[8] = effect.scrollOffsetY;\n moonCraterUniforms[9] = effect.craterCount;\n moonCraterPaint.setShader(moonCraterEffect.makeShader(moonCraterUniforms));\n return moonCraterPaint;\n }\n\n function applyCaveInterior(effect: CaveInteriorEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (caveInteriorEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(CAVE_INTERIOR_SKSL);\n if (compiled === null) return defaultPaint;\n caveInteriorEffect = compiled;\n }\n if (caveInteriorPaint === null) caveInteriorPaint = Skia.Paint();\n caveInteriorUniforms[0] = effect.resolutionW;\n caveInteriorUniforms[1] = effect.resolutionH;\n caveInteriorUniforms[2] = effect.caveColor.r;\n caveInteriorUniforms[3] = effect.caveColor.g;\n caveInteriorUniforms[4] = effect.caveColor.b;\n caveInteriorUniforms[5] = effect.glowColor.r;\n caveInteriorUniforms[6] = effect.glowColor.g;\n caveInteriorUniforms[7] = effect.glowColor.b;\n caveInteriorUniforms[8] = effect.scrollOffsetY;\n caveInteriorUniforms[9] = effect.stalactiteCount;\n caveInteriorPaint.setShader(caveInteriorEffect.makeShader(caveInteriorUniforms));\n return caveInteriorPaint;\n }\n\n function applyMountainHills(effect: MountainHillsEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (mountainHillsEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(MOUNTAIN_HILLS_SKSL);\n if (compiled === null) return defaultPaint;\n mountainHillsEffect = compiled;\n }\n if (mountainHillsPaint === null) mountainHillsPaint = Skia.Paint();\n mountainHillsUniforms[0] = effect.resolutionW;\n mountainHillsUniforms[1] = effect.resolutionH;\n mountainHillsUniforms[2] = effect.skyColor.r;\n mountainHillsUniforms[3] = effect.skyColor.g;\n mountainHillsUniforms[4] = effect.skyColor.b;\n mountainHillsUniforms[5] = effect.hillColor.r;\n mountainHillsUniforms[6] = effect.hillColor.g;\n mountainHillsUniforms[7] = effect.hillColor.b;\n mountainHillsUniforms[8] = effect.scrollOffsetY;\n mountainHillsUniforms[9] = effect.ridgeScale;\n mountainHillsPaint.setShader(mountainHillsEffect.makeShader(mountainHillsUniforms));\n return mountainHillsPaint;\n }\n\n function drawOutlined(\n cvs: DrawSurfaceLike,\n image: unknown,\n commands: SpriteDrawCommand[],\n n: number,\n effect: OutlineEffect,\n alpha: number,\n ): void {\n const paint = applyOutlinePaint(effect);\n paint.setAlphaf(alpha);\n const srcs = getCachedSrcSlice(n);\n for (let k = 0; k < OUTLINE_OFFSETS.length; k++) {\n const offset = OUTLINE_OFFSETS[k] as readonly [number, number];\n fillXforms(commands, n, offset[0] * effect.width, offset[1] * effect.width);\n const dsts = getCachedXformSlice(n);\n cvs.drawAtlas(image, srcs, dsts, paint);\n }\n paint.setAlphaf(1);\n defaultPaint.setAlphaf(alpha);\n fillXforms(commands, n, 0, 0);\n const mainDsts = getCachedXformSlice(n);\n cvs.drawAtlas(image, srcs, mainDsts, defaultPaint);\n defaultPaint.setAlphaf(1);\n }\n\n function resolveEffectPaint(effect: AnySpriteEffect): PaintLike {\n switch (effect.type) {\n case 'tint':\n return applyTint(effect);\n case 'flash':\n return applyFlash(effect);\n case 'grayscale':\n return applyGrayscale(effect);\n case 'custom':\n return applyCustom(effect);\n case 'water':\n return applyWater(effect, currentFrameTime);\n case 'fog':\n return applyFog(effect, currentFrameTime);\n case 'fire':\n return applyFire(effect, currentFrameTime);\n case 'drop-shadow':\n return applyDropShadow(effect);\n case 'outline':\n return applyOutlinePaint(effect);\n case 'roofs-city':\n return applyRoofsCity(effect);\n case 'forest-canopy':\n return applyForestCanopy(effect);\n case 'river-wave':\n return applyRiverWave(effect);\n case 'moon-crater':\n return applyMoonCrater(effect);\n case 'cave-interior':\n return applyCaveInterior(effect);\n case 'mountain-hills':\n return applyMountainHills(effect);\n }\n }\n\n return {\n canvas,\n\n beginFrame(viewportWidth, viewportHeight, time = 0, pixelDensity = 1): void {\n if (viewportWidth === 0 || viewportHeight === 0) return;\n _boundRuntime = null;\n _boundChain = null;\n currentFrameTime = time;\n const pd = pixelDensity;\n recordingBounds.setXYWH(0, 0, viewportWidth * pd, viewportHeight * pd);\n current = recorder.beginRecording(recordingBounds);\n if (pd !== 1) current.scale(pd, pd);\n },\n\n drawAtlas(image, commands): void {\n if (current === null) return;\n const n = commands.length;\n if (n === 0) return;\n\n const effect = commands[0]?.effect;\n const alpha = commands[0]?.alpha ?? 1;\n\n if (\n effect?.type === 'roofs-city' ||\n effect?.type === 'forest-canopy' ||\n effect?.type === 'river-wave' ||\n effect?.type === 'moon-crater' ||\n effect?.type === 'cave-interior' ||\n effect?.type === 'mountain-hills'\n ) {\n const paint = resolveEffectPaint(effect);\n paint.setAlphaf(alpha);\n ensureZoneBgPoolCapacity(n);\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n zoneBgRects[i]!.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);\n current.drawRect(zoneBgRects[i]!, paint);\n }\n paint.setAlphaf(1);\n return;\n }\n\n if (commands[0]?.stretch) {\n if (stretchPaint === null) stretchPaint = Skia.Paint();\n ensureStretchPoolCapacity(n);\n stretchPaint.setAlphaf(alpha);\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n const srcR = stretchSrcRects[i]!;\n const dstR = stretchDstRects[i]!;\n srcR.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);\n dstR.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);\n current.drawImageRect(image, srcR, dstR, stretchPaint);\n }\n stretchPaint.setAlphaf(1);\n return;\n }\n\n ensurePoolCapacity(n);\n fillSrcRects(commands, n);\n\n if (effect?.type === 'outline') {\n drawOutlined(current, image, commands, n, effect, alpha);\n return;\n }\n\n fillXforms(commands, n, 0, 0);\n const srcs = getCachedSrcSlice(n);\n const dsts = getCachedXformSlice(n);\n const paint = effect === undefined ? defaultPaint : resolveEffectPaint(effect);\n paint.setAlphaf(alpha);\n current.drawAtlas(image, srcs, dsts, paint);\n paint.setAlphaf(1);\n },\n\n setPostFxBinding(runtime, chain): void {\n _boundRuntime = runtime;\n _boundChain = chain;\n },\n\n buildPostFxPasses(runtime, chain, width, height): readonly FrameRendererPostFxDescriptor[] {\n const rt = runtime ?? _boundRuntime;\n const ch = chain ?? _boundChain;\n if (rt === null || !rt.supported) return [];\n if (rt.buildPass === undefined) {\n logOnce('buildPostFxPasses: runtime.supported but buildPass absent — no passes');\n return [];\n }\n if (ch === null) return [];\n const passes = rt.buildPass(ch, width, height);\n if (passes.length === 0) return [];\n const out: FrameRendererPostFxDescriptor[] = [];\n for (const pass of passes) {\n const shader = pass.shader;\n const uniformCount =\n typeof shader.getUniformCount === 'function' ? shader.getUniformCount() : 0;\n const uniforms: Record<string, number | readonly number[]> = {};\n for (let i = 0; i < uniformCount; i++) {\n const name =\n typeof shader.getUniformName === 'function' ? shader.getUniformName(i) : String(i);\n const info =\n typeof shader.getUniform === 'function'\n ? shader.getUniform(i)\n : { columns: 1, rows: 1, slot: i };\n const size = info.columns * info.rows;\n if (size === 1) {\n uniforms[name] = pass.uniforms[info.slot] ?? 0;\n } else {\n const vec: number[] = [];\n for (let k = 0; k < size; k++) {\n vec.push(pass.uniforms[info.slot + k] ?? 0);\n }\n uniforms[name] = vec;\n }\n }\n out.push({\n id: pass.id,\n source: shader,\n uniforms,\n children: pass.children,\n });\n }\n return out;\n },\n\n endFrame(): PictureLike | null {\n if (current === null) return null;\n current = null;\n return recorder.finishRecordingAsPicture();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers (module-private, not exported)\n// ---------------------------------------------------------------------------\n\nfunction writeColor(out: Float32Array, color: SpriteColor, alphaMul: number): void {\n out[0] = color.r;\n out[1] = color.g;\n out[2] = color.b;\n out[3] = color.a * alphaMul;\n}\n\nfunction writeGrayscaleMatrix(out: number[], amount: number): void {\n const inv = 1 - amount;\n const lr = 0.299 * amount;\n const lg = 0.587 * amount;\n const lb = 0.114 * amount;\n out[0] = inv + lr;\n out[1] = lg;\n out[2] = lb;\n out[3] = 0;\n out[4] = 0;\n out[5] = lr;\n out[6] = inv + lg;\n out[7] = lb;\n out[8] = 0;\n out[9] = 0;\n out[10] = lr;\n out[11] = lg;\n out[12] = inv + lb;\n out[13] = 0;\n out[14] = 0;\n out[15] = 0;\n out[16] = 0;\n out[17] = 0;\n out[18] = 1;\n out[19] = 0;\n}\n\nfunction fillCustomUniforms(\n effect: CustomShaderEffect,\n shader: RuntimeEffectLike,\n out: number[],\n): void {\n const count = shader.getUniformCount();\n for (let i = 0; i < out.length; i++) out[i] = 0;\n for (let i = 0; i < count; i++) {\n const info = shader.getUniform(i);\n const name = shader.getUniformName(i);\n const value = effect.uniforms[name];\n const size = info.columns * info.rows;\n if (value === undefined) continue;\n if (typeof value === 'number') {\n out[info.slot] = value;\n continue;\n }\n for (let k = 0; k < size; k++) {\n out[info.slot + k] = value[k] ?? 0;\n }\n }\n}\n","import type { SkImageLike } from '@flare-engine/assets';\nimport type { SpriteBatch } from './sprite-batch.js';\n\n/** A single glyph in a bitmap font. */\nexport interface Glyph {\n /** Character code (Unicode). */\n id: number;\n /** Source rect in the font texture. */\n x: number;\n y: number;\n width: number;\n height: number;\n /** Draw offset. */\n xOffset: number;\n yOffset: number;\n /** Horizontal advance after this glyph. */\n xAdvance: number;\n}\n\n/** Kerning pair. */\nexport interface KerningPair {\n first: number;\n second: number;\n amount: number;\n}\n\n/** Parsed bitmap font data. */\nexport interface BitmapFont {\n /** Line height in pixels. */\n lineHeight: number;\n /** Baseline offset. */\n base: number;\n /** Glyph map by character code. */\n glyphs: Map<number, Glyph>;\n /** Kerning map. Key = `first << 16 | second`. */\n kernings: Map<number, number>;\n}\n\n/**\n * Parse BMFont JSON format (common output from tools like Hiero, BMFont, msdf-bmfont).\n */\nexport function parseBMFontJSON(json: unknown): BitmapFont {\n const data = json as {\n common: { lineHeight: number; base: number };\n chars: Array<{\n id: number;\n x: number;\n y: number;\n width: number;\n height: number;\n xoffset: number;\n yoffset: number;\n xadvance: number;\n }>;\n kernings?: Array<{ first: number; second: number; amount: number }>;\n };\n\n const glyphs = new Map<number, Glyph>();\n for (const c of data.chars) {\n glyphs.set(c.id, {\n id: c.id,\n x: c.x,\n y: c.y,\n width: c.width,\n height: c.height,\n xOffset: c.xoffset,\n yOffset: c.yoffset,\n xAdvance: c.xadvance,\n });\n }\n\n const kernings = new Map<number, number>();\n if (data.kernings) {\n for (const k of data.kernings) {\n kernings.set((k.first << 16) | k.second, k.amount);\n }\n }\n\n return {\n lineHeight: data.common.lineHeight,\n base: data.common.base,\n glyphs,\n kernings,\n };\n}\n\n/** Get kerning between two characters. */\nexport function getKerning(font: BitmapFont, first: number, second: number): number {\n return font.kernings.get((first << 16) | second) ?? 0;\n}\n\n/** Measure the width of a text string in pixels. */\nexport function measureText(font: BitmapFont, text: string): number {\n let width = 0;\n let prevCode = -1;\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const glyph = font.glyphs.get(code);\n if (!glyph) continue;\n if (prevCode >= 0) {\n width += getKerning(font, prevCode, code);\n }\n width += glyph.xAdvance;\n prevCode = code;\n }\n return width;\n}\n\n/**\n * Draw text using a SpriteBatch.\n * Characters are batched as individual sprites from the font texture.\n */\nexport function drawText(\n batch: SpriteBatch,\n font: BitmapFont,\n image: SkImageLike,\n text: string,\n x: number,\n y: number,\n scale = 1,\n): void {\n let cursorX = x;\n let prevCode = -1;\n\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const glyph = font.glyphs.get(code);\n if (!glyph) continue;\n\n if (prevCode >= 0) {\n cursorX += getKerning(font, prevCode, code) * scale;\n }\n\n batch.drawRaw(\n image,\n glyph.x,\n glyph.y,\n glyph.width,\n glyph.height,\n cursorX + glyph.xOffset * scale,\n y + glyph.yOffset * scale,\n glyph.width * scale,\n glyph.height * scale,\n );\n\n cursorX += glyph.xAdvance * scale;\n prevCode = code;\n }\n}\n","import type { SkImageLike } from '@flare-engine/assets';\nimport type { SpriteFrame } from '@flare-engine/assets';\nimport type { World } from '@flare-engine/ecs';\nimport type { Query } from '@flare-engine/ecs';\nimport { type ComponentDef, defineComponent } from '@flare-engine/ecs';\nimport { sortByDrawOrder } from './draw-order.js';\nimport type { SpriteBatch } from './sprite-batch.js';\nimport type { AnySpriteEffect } from './sprite-effects.js';\n\n/** Position component data expected by the render system. */\nexport interface PositionData {\n x: number;\n y: number;\n}\n\n/** Sprite anchor as a fraction of the destination rect — `(0, 0)` is\n * top-left (default), `(0.5, 0.5)` is center, `(1, 1)` is bottom-right.\n * When present, `Position` refers to the anchor point rather than the\n * sprite's top-left corner. */\nexport interface SpriteAnchor {\n x: number;\n y: number;\n}\n\n/** Sprite component data expected by the render system. */\nexport interface SpriteData {\n image: SkImageLike;\n frame: SpriteFrame;\n z: number;\n width?: number;\n height?: number;\n /** Optional per-sprite effect (tint, flash, grayscale, outline, custom).\n * Shared instances are batched together — prefer reusing an effect\n * reference across entities that should render identically. */\n effect?: AnySpriteEffect;\n /** Optional pivot point for the sprite. Defaults to top-left `(0, 0)`. */\n anchor?: SpriteAnchor;\n /** Per-instance alpha multiplier. Range [0, 1]. Default 1 (fully opaque).\n * Mutate directly each frame — e.g. `sprite.alpha = lerp(1, 0, t)`.\n * Sprites with different alphas are placed in separate draw batches. */\n alpha?: number;\n /** Rotation in radians, clockwise (Skia Y-down convention). Default 0.\n * Applied about the sprite's anchor point. Non-uniform scale combined with\n * rotation is unsupported — scale is inferred uniformly from dstW/srcW. */\n rotation?: number;\n /**\n * When true, draw via `drawImageRect` so `width` and `height` are honored\n * independently (non-uniform scale). Use for background tiles or any sprite\n * that must fill a non-square destination rect exactly.\n *\n * Default (absent/false): drawn via the batched `drawAtlas` uniform-scale\n * path. Stretched sprites are NOT batched with regular sprites.\n *\n * Rotation is unsupported in stretch mode — combine only with `rotation = 0`.\n */\n stretch?: boolean;\n}\n\n/** Cached entry for sorting. Reused each frame to avoid allocs. */\ninterface RenderEntry {\n entityId: number;\n z: number;\n}\n\n/**\n * Create a render system function.\n *\n * This is not a defineSystem() wrapper because it needs extra parameters\n * (SpriteBatch) beyond what the ECS system interface provides. Instead,\n * it returns a function to call in your game loop.\n *\n * The returned function only **queues** sprite draws into `batch` — it\n * does not call `batch.flush()`. Flushing is owned by the frame owner\n * (e.g. `<GameView>`) so that all draw sources (ECS, particles, custom)\n * can contribute to a single flush at the render phase.\n *\n * @param world The ECS world.\n * @param query Query that matches entities with Position + Sprite.\n * @param positionDef ComponentDef for Position.\n * @param spriteDef ComponentDef for Sprite.\n * @param batch SpriteBatch instance to queue draws into.\n */\nexport function createRenderSystem(\n world: World,\n query: Query,\n positionDef: ComponentDef<PositionData>,\n spriteDef: ComponentDef<SpriteData>,\n batch: SpriteBatch,\n): () => void {\n // Pre-allocated sorted array — grows as needed, never shrinks\n const entries: RenderEntry[] = [];\n\n return () => {\n // Collect entities\n let i = 0;\n for (const entityId of query) {\n const sprite = world.get(entityId, spriteDef);\n if (!sprite) continue;\n\n if (i >= entries.length) {\n entries.push({ entityId, z: 0 });\n }\n const entry = entries[i]!;\n entry.entityId = entityId;\n entry.z = sprite.z;\n i++;\n }\n\n // Shrink policy — avoid thrashing on variable entity counts.\n const count = i;\n if (entries.length > count * 2 && entries.length > 64) {\n entries.length = count;\n }\n // Zero-alloc: sort the pooled array in place, bounded to live count.\n sortByDrawOrder(entries, count);\n\n // Queue draws in z-order\n for (let j = 0; j < count; j++) {\n const entry = entries[j]!;\n const pos = world.get(entry.entityId, positionDef);\n const sprite = world.get(entry.entityId, spriteDef);\n if (!pos || !sprite) continue;\n\n const anchor = sprite.anchor;\n batch.draw(\n sprite.image,\n sprite.frame,\n pos.x,\n pos.y,\n sprite.width,\n sprite.height,\n sprite.effect,\n anchor ? anchor.x : 0,\n anchor ? anchor.y : 0,\n sprite.alpha ?? 1,\n sprite.rotation ?? 0,\n sprite.stretch ?? false,\n );\n }\n };\n}\n\n/** Canonical Sprite component — queried by the render system alongside `Position`. */\nexport const Sprite = defineComponent<SpriteData>('Sprite', () => ({\n image: null as unknown as SkImageLike,\n frame: { name: '', x: 0, y: 0, w: 0, h: 0 },\n z: 0,\n}));\n"],"mappings":"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","names":["paint"]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/camera2d.ts","../src/draw-order.ts","../src/shaders.ts","../src/sprite-effects.ts","../src/frame-renderer.ts","../src/render-system.ts","../src/renderer.ts","../src/sprite-batch.ts","../src/sprite-font.ts"],"sourcesContent":["import type { Rect, Vec2 } from '@flare-engine/math';\n\n/** Camera follow smoothing options. */\nexport interface FollowOptions {\n /** Smoothing factor (0 = instant, lower = faster, default 0.1). */\n smoothing?: number;\n /** Offset from target (e.g. look-ahead). */\n offset?: Vec2;\n /** Dead zone rect — camera won't move while target is inside. */\n deadZone?: Rect;\n}\n\n/** Camera shake state. */\ninterface ShakeState {\n intensity: number;\n duration: number;\n elapsed: number;\n offsetX: number;\n offsetY: number;\n}\n\n/** Simple pseudo-random for deterministic shake. Avoids Math.random() allocs. */\nfunction pseudoRandom(seed: number): number {\n const x = Math.sin(seed * 12.9898 + seed * 78.233) * 43758.5453;\n return x - Math.floor(x);\n}\n\n// ─── Simplex-like 2D noise for shakePerlin ───────────────────────────────────\n// 256-entry permutation table — written once at module load, never mutated.\nconst PERM = new Uint8Array(256);\n{\n for (let i = 0; i < 256; i++) PERM[i] = i;\n // Deterministic Fisher-Yates with a fixed seed (no Math.random()).\n let s = 0x9e3779b9;\n for (let i = 255; i > 0; i--) {\n s = (s ^ (s >>> 15)) >>> 0;\n s = Math.imul(s, 0x85ebca6b) >>> 0;\n s = (s ^ (s >>> 13)) >>> 0;\n const j = s % (i + 1);\n const t = PERM[i]!;\n PERM[i] = PERM[j]!;\n PERM[j] = t;\n }\n}\n\n// 8 evenly-spaced 2D gradient directions (unit vectors × 1 for integer math).\nconst S = Math.SQRT1_2; // ≈ 0.7071 — diagonal component\nconst GRADS: ReadonlyArray<readonly [number, number]> = [\n [1, 0],\n [S, S],\n [0, 1],\n [-S, S],\n [-1, 0],\n [-S, -S],\n [0, -1],\n [S, -S],\n];\n\nfunction grad2(hash: number, x: number, y: number): number {\n const g = GRADS[hash & 7]!;\n return g[0] * x + g[1] * y;\n}\n\nfunction fade(t: number): number {\n return t * t * t * (t * (t * 6 - 15) + 10);\n}\n\n/** 2D value-gradient noise in [-1, 1]. */\nfunction noise2d(x: number, y: number): number {\n const xi = Math.floor(x) & 255;\n const yi = Math.floor(y) & 255;\n const xf = x - Math.floor(x);\n const yf = y - Math.floor(y);\n const u = fade(xf);\n const v = fade(yf);\n\n const aa = PERM[PERM[xi]! + yi]!;\n const ab = PERM[PERM[xi]! + yi + 1]!;\n const ba = PERM[PERM[(xi + 1) & 255]! + yi]!;\n const bb = PERM[PERM[(xi + 1) & 255]! + yi + 1]!;\n\n const x1 = xf;\n const y1 = yf;\n const x2 = xf - 1;\n const y2 = yf - 1;\n\n const n00 = grad2(aa, x1, y1);\n const n10 = grad2(ba, x2, y1);\n const n01 = grad2(ab, x1, y2);\n const n11 = grad2(bb, x2, y2);\n\n const lerpX0 = n00 + u * (n10 - n00);\n const lerpX1 = n01 + u * (n11 - n01);\n return lerpX0 + v * (lerpX1 - lerpX0);\n}\n\n/** 2-octave FBM for shake displacement — result in [-1, 1]. */\nfunction fbmNoise2d(x: number, y: number): number {\n return 0.6667 * noise2d(x, y) + 0.3333 * noise2d(x * 2, y * 2);\n}\n\n/**\n * Camera2D — 2D camera with position, zoom, rotation, follow, and shake.\n * Pure data + methods — no Skia dependency. The renderer reads camera state\n * to build the view transform.\n */\nexport class Camera2D {\n /** Camera position (world-space center). */\n x = 0;\n y = 0;\n\n /** Zoom level (1 = 100%). */\n zoom = 1;\n\n /** Rotation in radians. */\n rotation = 0;\n\n /** Viewport width in pixels. */\n viewportWidth = 0;\n\n /** Viewport height in pixels. */\n viewportHeight = 0;\n\n /** Optional world bounds to clamp camera position. */\n bounds: Rect | null = null;\n\n private _follow: { target: Vec2; options: FollowOptions } | null = null;\n private _shake: ShakeState | null = null;\n private _shakePerlin: ShakeState | null = null;\n private _shakeSeed = 0;\n private _shakePerlinSeed = 0;\n private _motionScale = 1;\n\n /** Set viewport dimensions. */\n setViewport(width: number, height: number): void {\n this.viewportWidth = width;\n this.viewportHeight = height;\n }\n\n /** Start following a target position. */\n follow(target: Vec2, options?: FollowOptions): void {\n this._follow = { target, options: options ?? {} };\n }\n\n /** Stop following. */\n unfollow(): void {\n this._follow = null;\n }\n\n /**\n * Global motion multiplier applied to subsequent `shake()` calls. Scales\n * both amplitude and duration. Default 1 (no-op). 0 fully suppresses\n * shake. Intended to be driven by `@flare-engine/a11y` reduced-motion so\n * callers don't have to guard every shake site.\n *\n * Only affects future `shake()` calls — does not retroactively modify an\n * in-flight shake.\n *\n * Clamped to `>= 0`.\n */\n setMotionScale(scale: number): void {\n this._motionScale = scale < 0 ? 0 : scale;\n }\n\n /** Current motion scale (default 1). */\n get motionScale(): number {\n return this._motionScale;\n }\n\n /** Start camera shake. */\n shake(intensity: number, duration: number): void {\n const k = this._motionScale;\n if (k <= 0) {\n this._shake = null;\n return;\n }\n this._shake = {\n intensity: intensity * k,\n duration: duration * k,\n elapsed: 0,\n offsetX: 0,\n offsetY: 0,\n };\n this._shakeSeed++;\n }\n\n /**\n * Start a Perlin-noise-based camera shake. Overwrites any in-flight\n * shakePerlin (same semantics as shake()). Respects motionScale.\n */\n shakePerlin(intensity: number, duration: number): void {\n const k = this._motionScale;\n if (k <= 0) {\n this._shakePerlin = null;\n return;\n }\n this._shakePerlin = {\n intensity: intensity * k,\n duration: duration * k,\n elapsed: 0,\n offsetX: 0,\n offsetY: 0,\n };\n this._shakePerlinSeed++;\n }\n\n /**\n * Update camera. Call once per frame.\n * @param dt Delta time in seconds.\n */\n update(dt: number): void {\n // Follow target\n if (this._follow) {\n const { target, options } = this._follow;\n const smoothing = options.smoothing ?? 0.1;\n const offsetX = options.offset?.x ?? 0;\n const offsetY = options.offset?.y ?? 0;\n const goalX = target.x + offsetX;\n const goalY = target.y + offsetY;\n\n if (options.deadZone) {\n const dz = options.deadZone;\n const relX = target.x - this.x;\n const relY = target.y - this.y;\n\n // Only move if target is outside dead zone.\n // Each branch moves the camera by exactly the excess beyond the boundary,\n // including the look-ahead offset. relX appears once — old code double-counted it.\n if (relX < dz.x) {\n this.x += (relX + offsetX - dz.x) * (1 - smoothing ** dt);\n } else if (relX > dz.x + dz.width) {\n this.x += (relX + offsetX - dz.x - dz.width) * (1 - smoothing ** dt);\n }\n if (relY < dz.y) {\n this.y += (relY + offsetY - dz.y) * (1 - smoothing ** dt);\n } else if (relY > dz.y + dz.height) {\n this.y += (relY + offsetY - dz.y - dz.height) * (1 - smoothing ** dt);\n }\n } else {\n // Exponential smoothing\n const factor = 1 - smoothing ** dt;\n this.x += (goalX - this.x) * factor;\n this.y += (goalY - this.y) * factor;\n }\n }\n\n // Clamp to bounds\n if (this.bounds) {\n this._clampToBounds();\n }\n\n // Shake (pseudo-random)\n if (this._shake) {\n this._shake.elapsed += dt;\n if (this._shake.elapsed >= this._shake.duration) {\n this._shake = null;\n } else {\n const progress = this._shake.elapsed / this._shake.duration;\n const decay = 1 - progress;\n const seed = this._shakeSeed + this._shake.elapsed * 100;\n this._shake.offsetX = (pseudoRandom(seed) * 2 - 1) * this._shake.intensity * decay;\n this._shake.offsetY = (pseudoRandom(seed + 1) * 2 - 1) * this._shake.intensity * decay;\n }\n }\n\n // Shake (Perlin / FBM noise)\n if (this._shakePerlin) {\n this._shakePerlin.elapsed += dt;\n if (this._shakePerlin.elapsed >= this._shakePerlin.duration) {\n this._shakePerlin = null;\n } else {\n const progress = this._shakePerlin.elapsed / this._shakePerlin.duration;\n const decay = 1 - progress;\n const t = this._shakePerlin.elapsed * 4.7 + this._shakePerlinSeed * 17.3;\n this._shakePerlin.offsetX = fbmNoise2d(t, 0) * this._shakePerlin.intensity * decay;\n this._shakePerlin.offsetY = fbmNoise2d(0, t + 5.4) * this._shakePerlin.intensity * decay;\n }\n }\n }\n\n /** Get the effective camera X (including shake + shakePerlin offsets). */\n get effectiveX(): number {\n return this.x + (this._shake?.offsetX ?? 0) + (this._shakePerlin?.offsetX ?? 0);\n }\n\n /** Get the effective camera Y (including shake + shakePerlin offsets). */\n get effectiveY(): number {\n return this.y + (this._shake?.offsetY ?? 0) + (this._shakePerlin?.offsetY ?? 0);\n }\n\n /** Convert world coordinates to screen coordinates. */\n worldToScreen(worldX: number, worldY: number): Vec2 {\n return this.worldToScreenInto(worldX, worldY, { x: 0, y: 0 });\n }\n\n /** Zero-alloc variant — writes screen coordinates into `out`. */\n worldToScreenInto(worldX: number, worldY: number, out: Vec2): Vec2 {\n const cos = Math.cos(-this.rotation);\n const sin = Math.sin(-this.rotation);\n const dx = (worldX - this.effectiveX) * this.zoom;\n const dy = (worldY - this.effectiveY) * this.zoom;\n out.x = cos * dx - sin * dy + this.viewportWidth / 2;\n out.y = sin * dx + cos * dy + this.viewportHeight / 2;\n return out;\n }\n\n /** Convert screen coordinates to world coordinates. */\n screenToWorld(screenX: number, screenY: number): Vec2 {\n return this.screenToWorldInto(screenX, screenY, { x: 0, y: 0 });\n }\n\n /** Zero-alloc variant — writes world coordinates into `out`. */\n screenToWorldInto(screenX: number, screenY: number, out: Vec2): Vec2 {\n const cx = screenX - this.viewportWidth / 2;\n const cy = screenY - this.viewportHeight / 2;\n const cos = Math.cos(this.rotation);\n const sin = Math.sin(this.rotation);\n const rx = cos * cx - sin * cy;\n const ry = sin * cx + cos * cy;\n out.x = rx / this.zoom + this.effectiveX;\n out.y = ry / this.zoom + this.effectiveY;\n return out;\n }\n\n /** Get the visible world-space bounding rect (allocates a fresh Rect). */\n getBounds(): Rect {\n return this.getBoundsInto({ x: 0, y: 0, width: 0, height: 0 });\n }\n\n /** Zero-alloc variant — writes the visible bounds into `out`. */\n getBoundsInto(out: Rect): Rect {\n const halfW = this.viewportWidth / (2 * this.zoom);\n const halfH = this.viewportHeight / (2 * this.zoom);\n out.x = this.effectiveX - halfW;\n out.y = this.effectiveY - halfH;\n out.width = halfW * 2;\n out.height = halfH * 2;\n return out;\n }\n\n private _clampToBounds(): void {\n const b = this.bounds!;\n const halfW = this.viewportWidth / (2 * this.zoom);\n const halfH = this.viewportHeight / (2 * this.zoom);\n\n const minX = b.x + halfW;\n const maxX = b.x + b.width - halfW;\n const minY = b.y + halfH;\n const maxY = b.y + b.height - halfH;\n\n if (minX <= maxX) {\n this.x = Math.max(minX, Math.min(maxX, this.x));\n } else {\n // Viewport larger than bounds — center\n this.x = b.x + b.width / 2;\n }\n\n if (minY <= maxY) {\n this.y = Math.max(minY, Math.min(maxY, this.y));\n } else {\n this.y = b.y + b.height / 2;\n }\n }\n}\n","/** A drawable item with a z-order for sorting. */\nexport interface Drawable {\n /** Z-index for draw ordering. Higher values draw on top. */\n z: number;\n}\n\n/**\n * Sort an array of drawables by z-index (ascending = back to front).\n * Uses insertion sort which is fast for nearly-sorted arrays\n * (typical in game rendering where order rarely changes).\n *\n * Mutates the input array in place. Returns the same array.\n *\n * @param items The array to sort in place.\n * @param length Optional upper bound — when provided, only sorts `items[0..length)`.\n * Use this when passing a pooled array that may hold stale tail entries.\n */\nexport function sortByDrawOrder<T extends Drawable>(items: T[], length?: number): T[] {\n const n = length ?? items.length;\n // Insertion sort — O(n) for nearly-sorted, no allocations\n for (let i = 1; i < n; i++) {\n const item = items[i]!;\n let j = i - 1;\n while (j >= 0 && items[j]!.z > item.z) {\n items[j + 1] = items[j]!;\n j--;\n }\n items[j + 1] = item;\n }\n return items;\n}\n\n/**\n * Compare function for use with Array.sort when insertion sort isn't desired.\n * Stable relative order for equal z values (returns 0).\n */\nexport function drawOrderCompare(a: Drawable, b: Drawable): number {\n return a.z - b.z;\n}\n","export const FIRE_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 color;\nuniform float3 tipColor;\nuniform float speed;\nuniform float intensity;\nuniform float time;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat vnoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + float2(1.0, 0.0));\n float c = hash(i + float2(0.0, 1.0));\n float d = hash(i + float2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(float2 p) {\n return 0.5 * vnoise(p) + 0.25 * vnoise(p * 2.0);\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float2 np = float2(uv.x * 4.0, uv.y * 6.0 - time * speed * 2.0);\n float n = fbm(np);\n float flameMask = smoothstep(0.0, intensity, n * (1.0 - uv.y));\n float3 fireRgb = mix(color, tipColor, uv.y);\n return half4(fireRgb.r, fireRgb.g, fireRgb.b, flameMask);\n}\n`;\n\nexport const FOG_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 color;\nuniform float density;\nuniform float speed;\nuniform float scale;\nuniform float time;\n\nfloat hash(float2 p) {\n return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453);\n}\n\nfloat valueNoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + float2(1.0, 0.0));\n float c = hash(i + float2(0.0, 1.0));\n float d = hash(i + float2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(float2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n for (int i = 0; i < 4; i++) {\n value += amplitude * valueNoise(p);\n p *= 2.0;\n amplitude *= 0.5;\n }\n return value;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float2 drift = float2(time * speed, 0.3 * time * speed);\n float n = clamp(fbm(uv * scale + drift), 0.0, 1.0);\n return half4(color.r, color.g, color.b, clamp(density * n, 0.0, 1.0));\n}\n`;\n\nexport const WATER_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 color;\nuniform float3 foamColor;\nuniform float waveSpeed;\nuniform float waveAmplitude;\nuniform float time;\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float waveDeriv = cos(uv.x * 6.2832 * 3.0 + time * waveSpeed * 2.0) * waveAmplitude * 2.0\n + cos(uv.x * 6.2832 * 5.0 - time * waveSpeed * 1.3 + 1.5) * waveAmplitude;\n float foam = smoothstep(waveAmplitude * 0.6, waveAmplitude * 1.5, waveDeriv);\n half3 tinted = half3(color.r, color.g, color.b);\n half3 final = mix(tinted, half3(foamColor.r, foamColor.g, foamColor.b), half(foam));\n return half4(final, 1.0);\n}\n`;\n\n// ---------------------------------------------------------------------------\n// Zone background shaders — procedural-silhouette SkSL\n// Each shader generates a recognizable background silhouette via SDFs / noise.\n// All accept a scrollOffsetY uniform driven by Skia-bridge time so the\n// background parallax-scrolls without CPU-side UV manipulation.\n// ---------------------------------------------------------------------------\n\nexport const ROOFS_CITY_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 roofColor;\nuniform float scrollOffsetY;\nuniform float density;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat buildingTop(float2 uv, float density) {\n float col = floor(uv.x * density);\n float h = 0.35 + 0.35 * hash(col);\n float w = 0.6 + 0.3 * hash(col + 7.3);\n float edge = step(abs(fract(uv.x * density) - 0.5), w * 0.5);\n return edge * step(uv.y, h);\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n uv.y = fract(uv.y);\n float buildings = buildingTop(uv, density);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(roofColor.r, roofColor.g, roofColor.b),\n half(buildings));\n return half4(col, 1.0);\n}\n`;\n\nexport const FOREST_CANOPY_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 leafColor;\nuniform float scrollOffsetY;\nuniform float density;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat valueNoise(float2 p) {\n float2 i = floor(p);\n float2 f = fract(p);\n float2 u = f * f * (3.0 - 2.0 * f);\n return mix(mix(hash(i), hash(i + float2(1.0, 0.0)), u.x),\n mix(hash(i + float2(0.0, 1.0)), hash(i + float2(1.0, 1.0)), u.x), u.y);\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float n = valueNoise(float2(uv.x * density, uv.y * 3.0));\n float treeLine = 0.55 + 0.25 * valueNoise(float2(uv.x * density * 0.5, 0.0));\n float canopy = step(uv.y, treeLine) * smoothstep(0.3, 0.7, n);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(leafColor.r, leafColor.g, leafColor.b),\n half(canopy));\n return half4(col, 1.0);\n}\n`;\n\nexport const RIVER_WAVE_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 waterColor;\nuniform float3 foamColor;\nuniform float scrollOffsetY;\nuniform float waveScale;\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float scroll = scrollOffsetY / max(resolution.y, 1.0);\n float2 p = float2(uv.x, uv.y + scroll);\n float w1 = sin(p.x * 6.2832 * waveScale + p.y * 4.0) * 0.04;\n float w2 = sin(p.x * 6.2832 * waveScale * 1.7 - p.y * 2.5 + 1.2) * 0.02;\n float surface = 0.5 + w1 + w2;\n float inWater = step(uv.y, surface);\n float crest = smoothstep(surface - 0.015, surface, uv.y) * (1.0 - inWater) +\n smoothstep(surface + 0.015, surface, uv.y) * inWater;\n half3 base = mix(half3(foamColor.r, foamColor.g, foamColor.b),\n half3(waterColor.r, waterColor.g, waterColor.b),\n half(inWater));\n half3 col = mix(base, half3(foamColor.r, foamColor.g, foamColor.b), half(crest * 0.6));\n return half4(col, 1.0);\n}\n`;\n\nexport const MOON_CRATER_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 moonColor;\nuniform float3 craterColor;\nuniform float scrollOffsetY;\nuniform float craterCount;\n\nfloat hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }\n\nfloat crater(float2 uv, float2 center, float r) {\n float d = length(uv - center);\n float rim = smoothstep(r, r * 0.85, d) - smoothstep(r * 0.85, r * 0.65, d);\n float floor_ = smoothstep(r * 0.65, r * 0.4, d);\n return rim * 0.6 + floor_ * 0.25;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float c = 0.0;\n for (int i = 0; i < 6; i++) {\n float fi = float(i);\n float2 center = float2(hash(float2(fi, 0.0)), hash(float2(fi, 1.0)));\n float r = 0.04 + 0.07 * hash(float2(fi, 2.0));\n c += crater(fract(uv), center, r) * step(fi, craterCount - 1.0);\n }\n c = clamp(c, 0.0, 1.0);\n half3 col = mix(half3(moonColor.r, moonColor.g, moonColor.b),\n half3(craterColor.r, craterColor.g, craterColor.b),\n half(c));\n return half4(col, 1.0);\n}\n`;\n\nexport const CAVE_INTERIOR_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 caveColor;\nuniform float3 glowColor;\nuniform float scrollOffsetY;\nuniform float stalactiteCount;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat stalactite(float2 uv, float count) {\n float col = floor(uv.x * count);\n float tip = 0.1 + 0.2 * hash(col);\n float w = 0.3 + 0.3 * hash(col + 5.0);\n float dx = abs(fract(uv.x * count) - 0.5) / (w * 0.5);\n float shape = step(dx, 1.0) * step(uv.y, tip * (1.0 - clamp(dx, 0.0, 1.0)));\n return shape;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n uv.y = 1.0 - uv.y;\n uv.y = uv.y + scrollOffsetY / max(resolution.y, 1.0);\n float stalac = stalactite(uv, stalactiteCount);\n float glow = smoothstep(0.8, 1.0, uv.y) * (1.0 - stalac);\n half3 col = mix(half3(caveColor.r, caveColor.g, caveColor.b),\n half3(glowColor.r, glowColor.g, glowColor.b),\n half(glow));\n col = mix(col, half3(caveColor.r, caveColor.g, caveColor.b) * half(0.3), half(stalac));\n return half4(col, 1.0);\n}\n`;\n\nexport const MOUNTAIN_HILLS_SKSL = /* sksl */ `\nuniform float2 resolution;\nuniform float3 skyColor;\nuniform float3 hillColor;\nuniform float scrollOffsetY;\nuniform float ridgeScale;\n\nfloat hash(float x) { return fract(sin(x * 127.1) * 43758.5453); }\n\nfloat heightField(float x, float scale) {\n float h = 0.0;\n float amp = 0.5;\n float freq = scale;\n for (int i = 0; i < 4; i++) {\n float ix = floor(x * freq);\n float fx = fract(x * freq);\n fx = fx * fx * (3.0 - 2.0 * fx);\n h += amp * mix(hash(ix), hash(ix + 1.0), fx);\n freq *= 2.0;\n amp *= 0.5;\n }\n return h;\n}\n\nhalf4 main(float2 coord) {\n float2 uv = coord / max(resolution, float2(1.0));\n float scroll = scrollOffsetY / max(resolution.y, 1.0);\n float horizon = 0.4 + 0.3 * heightField(uv.x + scroll * 0.3, ridgeScale);\n float mountains = step(uv.y, horizon);\n half3 col = mix(half3(skyColor.r, skyColor.g, skyColor.b),\n half3(hillColor.r, hillColor.g, hillColor.b),\n half(mountains));\n return half4(col, 1.0);\n}\n`;\n","/** Color as RGBA (0-1 per channel). */\nexport interface SpriteColor {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n readonly a: number;\n}\n\n/** A sprite effect that can be applied during rendering. */\nexport interface SpriteEffect {\n readonly type: string;\n}\n\n/** Tint effect — blend a color onto the sprite. */\nexport interface TintEffect extends SpriteEffect {\n readonly type: 'tint';\n readonly color: SpriteColor;\n readonly amount: number;\n}\n\n/** Flash effect — overlay a solid color that decays. */\nexport interface FlashEffect extends SpriteEffect {\n readonly type: 'flash';\n readonly color: SpriteColor;\n readonly duration: number;\n readonly elapsed: number;\n}\n\n/** Grayscale effect — desaturate the sprite. */\nexport interface GrayscaleEffect extends SpriteEffect {\n readonly type: 'grayscale';\n readonly amount: number;\n}\n\n/** Outline effect — draw an outline around the sprite. */\nexport interface OutlineEffect extends SpriteEffect {\n readonly type: 'outline';\n readonly color: SpriteColor;\n readonly width: number;\n}\n\n/**\n * Opaque pre-compiled shader program. Typically a Skia `RuntimeEffect` built\n * once at asset load time via `Skia.RuntimeEffect.Make(sksl)`. The render\n * package stays Skia-agnostic; the `@flare-engine/react` layer narrows this to the\n * concrete Skia type when applying the effect.\n */\nexport type CompiledShader = object;\n\n/** Uniform values keyed by name. Scalars for floats, arrays for vec/mat. */\nexport type ShaderUniforms = Record<string, number | readonly number[]>;\n\n/** Custom shader effect — apply a pre-compiled runtime shader with uniforms. */\nexport interface CustomShaderEffect extends SpriteEffect {\n readonly type: 'custom';\n readonly shader: CompiledShader;\n readonly uniforms: ShaderUniforms;\n}\n\n/** Water surface effect — animated UV-displaced sprite with foam crests. */\nexport interface WaterEffect extends SpriteEffect {\n readonly type: 'water';\n readonly color: SpriteColor;\n readonly foamColor: SpriteColor;\n readonly waveSpeed: number;\n readonly waveAmplitude: number;\n}\n\n/** Fog / volumetric haze effect — animated FBM noise layered over a sprite. */\nexport interface FogEffect extends SpriteEffect {\n readonly type: 'fog';\n readonly color: SpriteColor;\n readonly density: number;\n readonly speed: number;\n readonly scale: number;\n}\n\n/** Fire / flame effect — value-noise FBM flame rising upward from a sprite. */\nexport interface FireEffect extends SpriteEffect {\n readonly type: 'fire';\n readonly color: SpriteColor;\n readonly tipColor: SpriteColor;\n readonly speed: number;\n readonly intensity: number;\n}\n\n/**\n * Drop-shadow effect — a single offset, blurred, tinted copy of the sprite drawn\n * beneath it for ship / boss grounding. Time-invariant. Applied via a Skia\n * `ImageFilter.MakeDropShadow` on a reused paint (sprite + shadow in one pass);\n * the offset, blur, and tint all live in the filter, so no extra draw pass.\n */\nexport interface DropShadowEffect extends SpriteEffect {\n readonly type: 'drop-shadow';\n /** Horizontal shadow offset in design px. */\n readonly offsetX: number;\n /** Vertical shadow offset in design px; positive = downward (grounding). */\n readonly offsetY: number;\n /** Gaussian blur sigma applied to the shadow. 0 = hard edge. */\n readonly blur: number;\n /** Shadow color; `a` is the shadow opacity. */\n readonly color: SpriteColor;\n}\n\n// ---------------------------------------------------------------------------\n// Zone background effects — procedural-silhouette SkSL bg layers\n// ---------------------------------------------------------------------------\n\n/** Rooftop city skyline — AABB silhouettes via SDF hash fields. */\nexport interface RoofsCityEffect extends SpriteEffect {\n readonly type: 'roofs-city';\n readonly skyColor: SpriteColor;\n readonly roofColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly density: number;\n /** Destination render width in logical pixels — used as shader resolution.x. */\n readonly resolutionW: number;\n /** Destination render height in logical pixels — used as shader resolution.y. */\n readonly resolutionH: number;\n}\n\n/** Forest canopy — value-noise tree line. */\nexport interface ForestCanopyEffect extends SpriteEffect {\n readonly type: 'forest-canopy';\n readonly skyColor: SpriteColor;\n readonly leafColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly density: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** River surface — 1D height-field wave with foam crests. */\nexport interface RiverWaveEffect extends SpriteEffect {\n readonly type: 'river-wave';\n readonly waterColor: SpriteColor;\n readonly foamColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly waveScale: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Moon surface — polar-SDF craters on a flat disc. */\nexport interface MoonCraterEffect extends SpriteEffect {\n readonly type: 'moon-crater';\n readonly moonColor: SpriteColor;\n readonly craterColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly craterCount: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Cave interior — stalactites hanging from the top edge. */\nexport interface CaveInteriorEffect extends SpriteEffect {\n readonly type: 'cave-interior';\n readonly caveColor: SpriteColor;\n readonly glowColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly stalactiteCount: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Mountain / hill ridge — FBM height-field silhouette. */\nexport interface MountainHillsEffect extends SpriteEffect {\n readonly type: 'mountain-hills';\n readonly skyColor: SpriteColor;\n readonly hillColor: SpriteColor;\n readonly scrollOffsetY: number;\n readonly ridgeScale: number;\n readonly resolutionW: number;\n readonly resolutionH: number;\n}\n\n/** Any concrete sprite effect. */\nexport type AnySpriteEffect =\n | TintEffect\n | FlashEffect\n | GrayscaleEffect\n | OutlineEffect\n | CustomShaderEffect\n | WaterEffect\n | FogEffect\n | FireEffect\n | DropShadowEffect\n | RoofsCityEffect\n | ForestCanopyEffect\n | RiverWaveEffect\n | MoonCraterEffect\n | CaveInteriorEffect\n | MountainHillsEffect;\n\n/** Create a tint effect. */\nexport function tint(color: SpriteColor, amount: number): TintEffect {\n return { type: 'tint', color, amount };\n}\n\n/** Create a flash effect. */\nexport function flash(color: SpriteColor, durationSec: number): FlashEffect {\n return { type: 'flash', color, duration: durationSec, elapsed: 0 };\n}\n\n/** Create a grayscale effect. */\nexport function grayscale(amount: number): GrayscaleEffect {\n return { type: 'grayscale', amount };\n}\n\n/** Create an outline effect. */\nexport function outline(color: SpriteColor, width: number): OutlineEffect {\n return { type: 'outline', color, width };\n}\n\n/** Create a custom shader effect. `shader` must be pre-compiled; compiling\n * SkSL per frame is expensive. `uniforms` may be mutated externally between\n * frames to animate parameters. */\nexport function custom(shader: CompiledShader, uniforms: ShaderUniforms): CustomShaderEffect {\n return { type: 'custom', shader, uniforms };\n}\n\n/** Create a water surface effect. Time is passed to the renderer each frame\n * via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function water(\n color: SpriteColor,\n foamColor: SpriteColor,\n waveSpeed: number,\n waveAmplitude: number,\n): WaterEffect {\n return { type: 'water', color, foamColor, waveSpeed, waveAmplitude };\n}\n\n/** Create a fog / volumetric haze effect. Time is passed to the renderer each\n * frame via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function fog(color: SpriteColor, density = 0.5, speed = 0.3, scale = 2.0): FogEffect {\n return { type: 'fog', color, density, speed, scale };\n}\n\n/** Create a fire / flame effect. Time is passed to the renderer each\n * frame via `beginFrame(w, h, time)` — the effect itself is time-invariant. */\nexport function fire(opts: {\n color: SpriteColor;\n tipColor: SpriteColor;\n speed?: number;\n intensity?: number;\n}): FireEffect {\n return {\n type: 'fire',\n color: opts.color,\n tipColor: opts.tipColor,\n speed: opts.speed ?? 1.0,\n intensity: opts.intensity ?? 0.8,\n };\n}\n\n/** Create a drop-shadow effect. Defaults to a soft shadow offset downward for\n * grounding; `color.a` is the shadow opacity. */\nexport function dropShadow(opts: {\n color: SpriteColor;\n offsetX?: number;\n offsetY?: number;\n blur?: number;\n}): DropShadowEffect {\n return {\n type: 'drop-shadow',\n color: opts.color,\n offsetX: opts.offsetX ?? 0,\n offsetY: opts.offsetY ?? 6,\n blur: opts.blur ?? 4,\n };\n}\n\n/** Create a rooftop city skyline effect. */\nexport function roofsCity(opts: {\n skyColor: SpriteColor;\n roofColor: SpriteColor;\n scrollOffsetY?: number;\n density?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): RoofsCityEffect {\n return {\n type: 'roofs-city',\n skyColor: opts.skyColor,\n roofColor: opts.roofColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n density: opts.density ?? 6,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a forest canopy effect. */\nexport function forestCanopy(opts: {\n skyColor: SpriteColor;\n leafColor: SpriteColor;\n scrollOffsetY?: number;\n density?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): ForestCanopyEffect {\n return {\n type: 'forest-canopy',\n skyColor: opts.skyColor,\n leafColor: opts.leafColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n density: opts.density ?? 5,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a river wave surface effect. */\nexport function riverWave(opts: {\n waterColor: SpriteColor;\n foamColor: SpriteColor;\n scrollOffsetY?: number;\n waveScale?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): RiverWaveEffect {\n return {\n type: 'river-wave',\n waterColor: opts.waterColor,\n foamColor: opts.foamColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n waveScale: opts.waveScale ?? 2,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a moon crater surface effect. */\nexport function moonCrater(opts: {\n moonColor: SpriteColor;\n craterColor: SpriteColor;\n scrollOffsetY?: number;\n craterCount?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): MoonCraterEffect {\n return {\n type: 'moon-crater',\n moonColor: opts.moonColor,\n craterColor: opts.craterColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n craterCount: opts.craterCount ?? 4,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a cave interior effect. */\nexport function caveInterior(opts: {\n caveColor: SpriteColor;\n glowColor: SpriteColor;\n scrollOffsetY?: number;\n stalactiteCount?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): CaveInteriorEffect {\n return {\n type: 'cave-interior',\n caveColor: opts.caveColor,\n glowColor: opts.glowColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n stalactiteCount: opts.stalactiteCount ?? 8,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/** Create a mountain / hill ridge effect. */\nexport function mountainHills(opts: {\n skyColor: SpriteColor;\n hillColor: SpriteColor;\n scrollOffsetY?: number;\n ridgeScale?: number;\n resolutionW?: number;\n resolutionH?: number;\n}): MountainHillsEffect {\n return {\n type: 'mountain-hills',\n skyColor: opts.skyColor,\n hillColor: opts.hillColor,\n scrollOffsetY: opts.scrollOffsetY ?? 0,\n ridgeScale: opts.ridgeScale ?? 1.5,\n resolutionW: opts.resolutionW ?? 0,\n resolutionH: opts.resolutionH ?? 0,\n };\n}\n\n/**\n * Advance a time-based effect by `dt` seconds.\n *\n * Returns a new effect object if state changed (e.g. flash elapsed time\n * advances), the same reference if the effect is time-invariant (tint,\n * grayscale, outline, custom), or `null` if the effect has expired.\n *\n * Effects are immutable — callers must replace their reference with the\n * returned value. Sharing an effect instance across entities is safe.\n */\nexport function updateEffect(effect: AnySpriteEffect, dt: number): AnySpriteEffect | null {\n if (effect.type === 'flash') {\n const elapsed = effect.elapsed + dt;\n if (elapsed >= effect.duration) return null;\n return { type: 'flash', color: effect.color, duration: effect.duration, elapsed };\n }\n return effect;\n}\n\n/**\n * Get the current intensity of a flash effect (1 at start, 0 at end).\n */\nexport function flashIntensity(effect: FlashEffect): number {\n if (effect.duration <= 0) return 0;\n const t = effect.elapsed / effect.duration;\n return Math.max(0, 1 - t);\n}\n","/**\n * Dev-flag access for the per-sprite transform counters below.\n *\n * Read as a PROPERTY of a stable object, never as the bare `__DEV__` identifier\n * behind a `typeof` guard. JSC has no inline cache for an unresolvable global\n * identifier, so that form costs ~270 ns *per evaluation* wherever the global\n * is undeclared — and this expression sits once per sprite per transform pass,\n * nine times over under an outline effect. A property miss on `globalThis` is\n * inline-cached at ~0.46 ns, ~400x cheaper, and stays togglable at runtime\n * exactly as before.\n *\n * (Phrased without the literal old expression on purpose: the dist canary in\n * `tests/frame-renderer-dist.test.ts` greps the built artifact as text, and a\n * comment quoting it would read as a reintroduction.)\n *\n * React Native always defines `__DEV__`, so RN builds never paid this; web /\n * CanvasKit and any Node or Bun consumer did. Measured B28, retired PERF-10:\n * `render.flush 1000 / 1 atlas` in the UNDECLARED configuration went 412-450 µs\n * before this change to 56-88 µs after — the same band the declared-false\n * configuration occupies, so the two are no longer distinguishable.\n *\n * Non-RN runtimes set `globalThis.__DEV__`, which is the contract api-specs\n * already documents for `isDevMode()`. Truthiness is deliberate: it matches the\n * previous guard for every possible value, so no consumer loses a dev check.\n */\n\nimport type { SkImageLike } from '@flare-engine/assets';\nimport type { CanvasLike } from './renderer.js';\nimport {\n CAVE_INTERIOR_SKSL,\n FIRE_SKSL,\n FOG_SKSL,\n FOREST_CANOPY_SKSL,\n MOON_CRATER_SKSL,\n MOUNTAIN_HILLS_SKSL,\n RIVER_WAVE_SKSL,\n ROOFS_CITY_SKSL,\n WATER_SKSL,\n} from './shaders.js';\nimport type { SpriteDrawCommand } from './sprite-batch.js';\nimport {\n type AnySpriteEffect,\n type CaveInteriorEffect,\n type CustomShaderEffect,\n type DropShadowEffect,\n type FireEffect,\n type FlashEffect,\n type FogEffect,\n type ForestCanopyEffect,\n flashIntensity,\n type GrayscaleEffect,\n type MoonCraterEffect,\n type MountainHillsEffect,\n type OutlineEffect,\n type RiverWaveEffect,\n type RoofsCityEffect,\n type SpriteColor,\n type TintEffect,\n type WaterEffect,\n} from './sprite-effects.js';\n\n/** See the dev-flag note at the top of this file. */\nconst G = globalThis as { __DEV__?: boolean };\n\n// ---------------------------------------------------------------------------\n// Structural interfaces — no @shopify/react-native-skia import anywhere here.\n// ---------------------------------------------------------------------------\n\n/** A 4-float RGBA color buffer (r, g, b, a in [0, 1]). */\nexport type ColorLike = Float32Array;\n\n/**\n * A mutable axis-aligned rect.\n *\n * The `x`, `y`, `width`, `height` fields are required so that RN-Skia's\n * `SkHostRect` (which carries those fields) structurally satisfies this\n * interface in contra-variant parameter positions.\n */\nexport interface RectLike {\n x: number;\n y: number;\n width: number;\n height: number;\n setXYWH(x: number, y: number, w: number, h: number): void;\n}\n\n/**\n * A mutable similarity transform.\n *\n * The `scos`, `ssin`, `tx`, `ty` fields are readable so that CK-backed\n * canvas wrappers can flatten pools into Float32Arrays without allocation.\n * RN-Skia's `SkRSXform` exposes all four fields.\n */\nexport interface RSXformLike {\n scos: number;\n ssin: number;\n tx: number;\n ty: number;\n set(scos: number, ssin: number, tx: number, ty: number): void;\n}\n\n/** A compiled SkSL runtime effect. */\nexport interface RuntimeEffectLike {\n makeShader(uniforms: number[]): unknown;\n getUniformCount(): number;\n getUniformFloatCount(): number;\n getUniformName(index: number): string;\n getUniform(index: number): { columns: number; rows: number; slot: number };\n}\n\n/** Color filter factory — subset used by the frame-renderer. */\nexport interface ColorFilterFactoryLike {\n MakeBlend(color: Float32Array, mode: number): unknown;\n MakeMatrix(matrix: number[]): unknown;\n}\n\n/** Image filter factory — optional; absent on CPU/test surfaces. */\nexport interface ImageFilterFactoryLike {\n MakeDropShadow(\n dx: number,\n dy: number,\n sigmaX: number,\n sigmaY: number,\n color: Float32Array,\n input: null,\n ): unknown;\n}\n\n/** Runtime-effect factory — optional; absent when SkSL is unsupported. */\nexport interface RuntimeEffectFactoryLike {\n Make(sksl: string): RuntimeEffectLike | null;\n}\n\n/** Paint — the writable subset the frame-renderer mutates. */\nexport interface PaintLike {\n setAlphaf(alpha: number): void;\n setColor?(color: Float32Array): void;\n setColorFilter(filter: unknown): void;\n setShader(shader: unknown): void;\n setImageFilter(filter: unknown): void;\n}\n\n/** Minimal draw surface the frame-renderer calls into. */\nexport interface DrawSurfaceLike {\n save(): number;\n restore(): void;\n translate(x: number, y: number): void;\n scale(sx: number, sy: number): void;\n /** rotate(degrees, cx, cy) — DEGREES, forwarded verbatim (both Skia backends take degrees). */\n rotate(degrees: number, cx: number, cy: number): void;\n clear(color: Float32Array): void;\n drawAtlas(image: unknown, srcRects: RectLike[], dstXforms: RSXformLike[], paint: PaintLike): void;\n drawImageRect(image: unknown, src: RectLike, dst: RectLike, paint: PaintLike): void;\n drawRect(rect: RectLike, paint: PaintLike): void;\n}\n\n/**\n * Opaque picture (recorded draw commands).\n * Typed as `object` so both `SkPicture` (RN-Skia) and CanvasKit's\n * `SkPicture` satisfy it without structural mismatches.\n * Callers that need concrete type methods cast to the platform type.\n */\nexport type PictureLike = object;\n\n/** Picture recorder. */\nexport interface PictureRecorderLike {\n beginRecording(bounds: RectLike): DrawSurfaceLike;\n finishRecordingAsPicture(): PictureLike;\n}\n\n/**\n * Minimal structural view of the Skia module the frame-renderer needs.\n * Declared here so both `@flare-engine/react` and `@flare-engine/render-web`\n * can pass their respective backends without coupling to a concrete Skia SDK.\n *\n * The `Surface` and `ImageFilter` slots are optional because they are absent\n * on CPU surfaces and in test mocks.\n */\nexport interface FrameRendererModule {\n XYWHRect(x: number, y: number, w: number, h: number): RectLike;\n RSXform(scos: number, ssin: number, tx: number, ty: number): RSXformLike;\n Paint(): PaintLike;\n PictureRecorder(): PictureRecorderLike;\n ColorFilter: ColorFilterFactoryLike;\n RuntimeEffect?: RuntimeEffectFactoryLike;\n ImageFilter?: ImageFilterFactoryLike;\n}\n\n/**\n * A single compiled postfx pass descriptor produced by the runtime.\n */\nexport interface FrameRendererCompiledPass {\n readonly id: string;\n readonly shader: {\n makeShaderWithChildren(uniforms: number[], children: unknown[]): unknown | null;\n getUniformCount?(): number;\n getUniformName?(index: number): string;\n getUniform?(index: number): { columns: number; rows: number; slot: number };\n };\n readonly uniforms: Float32Array;\n readonly children: readonly unknown[];\n}\n\n/** Minimal structural view of a PostFxRuntime that the frame-renderer needs. */\nexport interface FrameRendererPostFxRuntime {\n readonly supported: boolean;\n buildPass?(\n chain: FrameRendererPostFxChain,\n width: number,\n height: number,\n ): readonly FrameRendererCompiledPass[];\n}\n\n/** Minimal structural view of an EffectChain the frame-renderer needs. */\nexport interface FrameRendererPostFxChain {\n enabled(): Iterable<unknown>;\n}\n\n/** A resolved postfx pass descriptor with named uniforms (for React JSX). */\nexport interface FrameRendererPostFxDescriptor {\n readonly id: string;\n readonly source: unknown;\n readonly uniforms: Record<string, number | readonly number[]>;\n readonly children: readonly unknown[];\n}\n\n/**\n * Frame renderer that bridges a `SpriteBatch` to a picture-backed draw surface.\n *\n * Each frame:\n * 1. Call `beginFrame(viewportWidth, viewportHeight)`.\n * 2. Call `drawAtlas(image, commands)` one or more times.\n * 3. Call `endFrame()` — returns the recorded picture.\n */\nexport interface FrameRenderer {\n readonly canvas: CanvasLike;\n beginFrame(\n viewportWidth: number,\n viewportHeight: number,\n time?: number,\n pixelDensity?: number,\n ): void;\n drawAtlas(image: SkImageLike, commands: SpriteDrawCommand[]): void;\n buildPostFxPasses(\n runtime: FrameRendererPostFxRuntime | null,\n chain: FrameRendererPostFxChain | null,\n width: number,\n height: number,\n ): readonly FrameRendererPostFxDescriptor[];\n setPostFxBinding(\n runtime: FrameRendererPostFxRuntime | null,\n chain: FrameRendererPostFxChain | null,\n ): void;\n /**\n * Whether `buildPostFxPasses` would attempt to build passes from the bound\n * runtime and chain. Pure null/flag checks — never calls `runtime.buildPass`,\n * so it is safe on platforms where building throws (React Native Web).\n */\n hasPostFxBinding(): boolean;\n endFrame(): PictureLike | null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal constants\n// ---------------------------------------------------------------------------\n\nconst BLEND_SRC_OVER = 3;\nconst BLEND_SRC_IN = 5;\n\nconst OUTLINE_OFFSETS: ReadonlyArray<readonly [number, number]> = [\n [1, 0],\n [-1, 0],\n [0, 1],\n [0, -1],\n [Math.SQRT1_2, Math.SQRT1_2],\n [Math.SQRT1_2, -Math.SQRT1_2],\n [-Math.SQRT1_2, Math.SQRT1_2],\n [-Math.SQRT1_2, -Math.SQRT1_2],\n];\n\nlet _xformUnrotatedCount = 0;\nlet _xformRotatedCount = 0;\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a `FrameRenderer` backed by the given structural Skia module.\n *\n * Both `@flare-engine/react` (passing the real RN-Skia module) and\n * `@flare-engine/render-web` (passing a CanvasKit shim) use this factory.\n */\nexport function createFrameRenderer(Skia: FrameRendererModule): FrameRenderer {\n const defaultPaint: PaintLike = Skia.Paint();\n\n let tintPaint: PaintLike | null = null;\n let flashPaint: PaintLike | null = null;\n let grayscalePaint: PaintLike | null = null;\n let outlinePaint: PaintLike | null = null;\n let customPaint: PaintLike | null = null;\n let waterPaint: PaintLike | null = null;\n let waterRuntimeEffect: RuntimeEffectLike | null = null;\n let fogPaint: PaintLike | null = null;\n let fogRuntimeEffect: RuntimeEffectLike | null = null;\n let firePaint: PaintLike | null = null;\n let fireRuntimeEffect: RuntimeEffectLike | null = null;\n let dropShadowPaint: PaintLike | null = null;\n\n let roofsCityPaint: PaintLike | null = null;\n let roofsCityEffect: RuntimeEffectLike | null = null;\n let forestCanopyPaint: PaintLike | null = null;\n let forestCanopyEffect: RuntimeEffectLike | null = null;\n let riverWavePaint: PaintLike | null = null;\n let riverWaveEffect: RuntimeEffectLike | null = null;\n let moonCraterPaint: PaintLike | null = null;\n let moonCraterEffect: RuntimeEffectLike | null = null;\n let caveInteriorPaint: PaintLike | null = null;\n let caveInteriorEffect: RuntimeEffectLike | null = null;\n let mountainHillsPaint: PaintLike | null = null;\n let mountainHillsEffect: RuntimeEffectLike | null = null;\n\n const roofsCityUniforms: number[] = new Array(10).fill(0);\n const forestCanopyUniforms: number[] = new Array(10).fill(0);\n const riverWaveUniforms: number[] = new Array(10).fill(0);\n const moonCraterUniforms: number[] = new Array(10).fill(0);\n const caveInteriorUniforms: number[] = new Array(10).fill(0);\n const mountainHillsUniforms: number[] = new Array(10).fill(0);\n\n const recorder: PictureRecorderLike = Skia.PictureRecorder();\n const recordingBounds: RectLike = Skia.XYWHRect(0, 0, 0, 0);\n\n const srcRects: RectLike[] = [];\n const xforms: RSXformLike[] = [];\n\n const stretchSrcRects: RectLike[] = [];\n const stretchDstRects: RectLike[] = [];\n let stretchPaint: PaintLike | null = null;\n\n const zoneBgRects: RectLike[] = [];\n\n let _cachedSrcSlice: RectLike[] | null = null;\n let _cachedSrcSliceLen = 0;\n let _cachedXformSlice: RSXformLike[] | null = null;\n let _cachedXformSliceLen = 0;\n\n const clearColor: Float32Array = new Float32Array(4);\n const tintColor: Float32Array = new Float32Array(4);\n const flashColor: Float32Array = new Float32Array(4);\n const outlineColor: Float32Array = new Float32Array(4);\n const dropShadowColor: Float32Array = new Float32Array(4);\n\n const grayMatrix: number[] = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0];\n const customUniforms: number[] = [];\n\n const waterUniforms: number[] = new Array(11).fill(0);\n const fogUniforms: number[] = new Array(9).fill(0);\n const fireUniforms: number[] = new Array(11).fill(0);\n\n let currentFrameTime = 0;\n let currentSpriteW = 0;\n let currentSpriteH = 0;\n let current: DrawSurfaceLike | null = null;\n\n let _boundRuntime: FrameRendererPostFxRuntime | null = null;\n let _boundChain: FrameRendererPostFxChain | null = null;\n\n const _loggedOnce = new Set<string>();\n function logOnce(msg: string): void {\n if (_loggedOnce.has(msg)) return;\n _loggedOnce.add(msg);\n console.warn(`[postfx] ${msg}`);\n }\n\n const canvas: CanvasLike = {\n save: () => current?.save() ?? 0,\n restore: () => {\n current?.restore();\n },\n translate: (x, y) => {\n current?.translate(x, y);\n },\n scale: (sx, sy) => {\n current?.scale(sx, sy);\n },\n rotate: (degrees, cx, cy) => {\n current?.rotate(degrees, cx, cy);\n },\n clear: (argb) => {\n if (current === null) return;\n clearColor[0] = ((argb >>> 16) & 0xff) / 255;\n clearColor[1] = ((argb >>> 8) & 0xff) / 255;\n clearColor[2] = (argb & 0xff) / 255;\n clearColor[3] = ((argb >>> 24) & 0xff) / 255;\n current.clear(clearColor);\n },\n };\n\n function ensurePoolCapacity(n: number): void {\n while (srcRects.length < n) {\n srcRects.push(Skia.XYWHRect(0, 0, 0, 0));\n xforms.push(Skia.RSXform(1, 0, 0, 0));\n }\n }\n\n function ensureStretchPoolCapacity(n: number): void {\n while (stretchSrcRects.length < n) {\n stretchSrcRects.push(Skia.XYWHRect(0, 0, 0, 0));\n stretchDstRects.push(Skia.XYWHRect(0, 0, 0, 0));\n }\n }\n\n function ensureZoneBgPoolCapacity(n: number): void {\n while (zoneBgRects.length < n) {\n zoneBgRects.push(Skia.XYWHRect(0, 0, 0, 0));\n }\n }\n\n function getCachedSrcSlice(n: number): RectLike[] {\n if (_cachedSrcSlice !== null && _cachedSrcSliceLen === n) return _cachedSrcSlice;\n _cachedSrcSlice = srcRects.length === n ? srcRects : srcRects.slice(0, n);\n _cachedSrcSliceLen = n;\n return _cachedSrcSlice;\n }\n\n function getCachedXformSlice(n: number): RSXformLike[] {\n if (_cachedXformSlice !== null && _cachedXformSliceLen === n) return _cachedXformSlice;\n _cachedXformSlice = xforms.length === n ? xforms : xforms.slice(0, n);\n _cachedXformSliceLen = n;\n return _cachedXformSlice;\n }\n\n function fillSrcRects(commands: SpriteDrawCommand[], n: number): void {\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n srcRects[i]?.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);\n }\n }\n\n function fillXforms(commands: SpriteDrawCommand[], n: number, dx: number, dy: number): void {\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n const scale = cmd.srcW === 0 ? 1 : cmd.dstW / cmd.srcW;\n if (cmd.rotation === 0) {\n xforms[i]?.set(scale, 0, cmd.dstX + dx, cmd.dstY + dy);\n if (G.__DEV__) _xformUnrotatedCount++;\n } else {\n const c = Math.cos(cmd.rotation);\n const s = Math.sin(cmd.rotation);\n const scos = scale * c;\n const ssin = scale * s;\n const px = cmd.dstX + cmd.anchorX * cmd.dstW + dx;\n const py = cmd.dstY + cmd.anchorY * cmd.dstH + dy;\n const axSrc = cmd.anchorX * cmd.srcW;\n const aySrc = cmd.anchorY * cmd.srcH;\n xforms[i]?.set(\n scos,\n ssin,\n px - scos * axSrc + ssin * aySrc,\n py - ssin * axSrc - scos * aySrc,\n );\n if (G.__DEV__) _xformRotatedCount++;\n }\n }\n }\n\n function applyTint(effect: TintEffect): PaintLike {\n if (tintPaint === null) tintPaint = Skia.Paint();\n writeColor(tintColor, effect.color, effect.amount);\n tintPaint.setColorFilter(Skia.ColorFilter.MakeBlend(tintColor, BLEND_SRC_OVER));\n return tintPaint;\n }\n\n function applyFlash(effect: FlashEffect): PaintLike {\n if (flashPaint === null) flashPaint = Skia.Paint();\n writeColor(flashColor, effect.color, flashIntensity(effect));\n flashPaint.setColorFilter(Skia.ColorFilter.MakeBlend(flashColor, BLEND_SRC_OVER));\n return flashPaint;\n }\n\n function applyGrayscale(effect: GrayscaleEffect): PaintLike {\n if (grayscalePaint === null) grayscalePaint = Skia.Paint();\n writeGrayscaleMatrix(grayMatrix, effect.amount);\n grayscalePaint.setColorFilter(Skia.ColorFilter.MakeMatrix(grayMatrix));\n return grayscalePaint;\n }\n\n function applyOutlinePaint(effect: OutlineEffect): PaintLike {\n if (outlinePaint === null) outlinePaint = Skia.Paint();\n writeColor(outlineColor, effect.color, 1);\n outlinePaint.setColorFilter(Skia.ColorFilter.MakeBlend(outlineColor, BLEND_SRC_IN));\n return outlinePaint;\n }\n\n function applyCustom(effect: CustomShaderEffect): PaintLike {\n if (customPaint === null) customPaint = Skia.Paint();\n const shader = effect.shader as RuntimeEffectLike;\n const floatCount = shader.getUniformFloatCount();\n while (customUniforms.length < floatCount) customUniforms.push(0);\n customUniforms.length = floatCount;\n fillCustomUniforms(effect, shader, customUniforms);\n customPaint.setShader(shader.makeShader(customUniforms));\n return customPaint;\n }\n\n function applyWater(effect: WaterEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (waterRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(WATER_SKSL);\n if (compiled === null) return defaultPaint;\n waterRuntimeEffect = compiled;\n }\n if (waterPaint === null) waterPaint = Skia.Paint();\n waterUniforms[0] = currentSpriteW;\n waterUniforms[1] = currentSpriteH;\n waterUniforms[2] = effect.color.r;\n waterUniforms[3] = effect.color.g;\n waterUniforms[4] = effect.color.b;\n waterUniforms[5] = effect.foamColor.r;\n waterUniforms[6] = effect.foamColor.g;\n waterUniforms[7] = effect.foamColor.b;\n waterUniforms[8] = effect.waveSpeed;\n waterUniforms[9] = effect.waveAmplitude;\n waterUniforms[10] = time;\n waterPaint.setShader(waterRuntimeEffect.makeShader(waterUniforms));\n return waterPaint;\n }\n\n function applyFog(effect: FogEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (fogRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FOG_SKSL);\n if (compiled === null) return defaultPaint;\n fogRuntimeEffect = compiled;\n }\n if (fogPaint === null) fogPaint = Skia.Paint();\n fogUniforms[0] = currentSpriteW;\n fogUniforms[1] = currentSpriteH;\n fogUniforms[2] = effect.color.r;\n fogUniforms[3] = effect.color.g;\n fogUniforms[4] = effect.color.b;\n fogUniforms[5] = effect.density;\n fogUniforms[6] = effect.speed;\n fogUniforms[7] = effect.scale;\n fogUniforms[8] = time;\n fogPaint.setShader(fogRuntimeEffect.makeShader(fogUniforms));\n return fogPaint;\n }\n\n function applyFire(effect: FireEffect, time: number): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (fireRuntimeEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FIRE_SKSL);\n if (compiled === null) return defaultPaint;\n fireRuntimeEffect = compiled;\n }\n if (firePaint === null) firePaint = Skia.Paint();\n fireUniforms[0] = currentSpriteW;\n fireUniforms[1] = currentSpriteH;\n fireUniforms[2] = effect.color.r;\n fireUniforms[3] = effect.color.g;\n fireUniforms[4] = effect.color.b;\n fireUniforms[5] = effect.tipColor.r;\n fireUniforms[6] = effect.tipColor.g;\n fireUniforms[7] = effect.tipColor.b;\n fireUniforms[8] = effect.speed;\n fireUniforms[9] = effect.intensity;\n fireUniforms[10] = time;\n firePaint.setShader(fireRuntimeEffect.makeShader(fireUniforms));\n return firePaint;\n }\n\n function applyDropShadow(effect: DropShadowEffect): PaintLike {\n if (Skia.ImageFilter === undefined) return defaultPaint;\n if (dropShadowPaint === null) dropShadowPaint = Skia.Paint();\n writeColor(dropShadowColor, effect.color, 1);\n dropShadowPaint.setImageFilter(\n Skia.ImageFilter.MakeDropShadow(\n effect.offsetX,\n effect.offsetY,\n effect.blur,\n effect.blur,\n dropShadowColor,\n null,\n ),\n );\n return dropShadowPaint;\n }\n\n function applyRoofsCity(effect: RoofsCityEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (roofsCityEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(ROOFS_CITY_SKSL);\n if (compiled === null) return defaultPaint;\n roofsCityEffect = compiled;\n }\n if (roofsCityPaint === null) roofsCityPaint = Skia.Paint();\n roofsCityUniforms[0] = effect.resolutionW;\n roofsCityUniforms[1] = effect.resolutionH;\n roofsCityUniforms[2] = effect.skyColor.r;\n roofsCityUniforms[3] = effect.skyColor.g;\n roofsCityUniforms[4] = effect.skyColor.b;\n roofsCityUniforms[5] = effect.roofColor.r;\n roofsCityUniforms[6] = effect.roofColor.g;\n roofsCityUniforms[7] = effect.roofColor.b;\n roofsCityUniforms[8] = effect.scrollOffsetY;\n roofsCityUniforms[9] = effect.density;\n roofsCityPaint.setShader(roofsCityEffect.makeShader(roofsCityUniforms));\n return roofsCityPaint;\n }\n\n function applyForestCanopy(effect: ForestCanopyEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (forestCanopyEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(FOREST_CANOPY_SKSL);\n if (compiled === null) return defaultPaint;\n forestCanopyEffect = compiled;\n }\n if (forestCanopyPaint === null) forestCanopyPaint = Skia.Paint();\n forestCanopyUniforms[0] = effect.resolutionW;\n forestCanopyUniforms[1] = effect.resolutionH;\n forestCanopyUniforms[2] = effect.skyColor.r;\n forestCanopyUniforms[3] = effect.skyColor.g;\n forestCanopyUniforms[4] = effect.skyColor.b;\n forestCanopyUniforms[5] = effect.leafColor.r;\n forestCanopyUniforms[6] = effect.leafColor.g;\n forestCanopyUniforms[7] = effect.leafColor.b;\n forestCanopyUniforms[8] = effect.scrollOffsetY;\n forestCanopyUniforms[9] = effect.density;\n forestCanopyPaint.setShader(forestCanopyEffect.makeShader(forestCanopyUniforms));\n return forestCanopyPaint;\n }\n\n function applyRiverWave(effect: RiverWaveEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (riverWaveEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(RIVER_WAVE_SKSL);\n if (compiled === null) return defaultPaint;\n riverWaveEffect = compiled;\n }\n if (riverWavePaint === null) riverWavePaint = Skia.Paint();\n riverWaveUniforms[0] = effect.resolutionW;\n riverWaveUniforms[1] = effect.resolutionH;\n riverWaveUniforms[2] = effect.waterColor.r;\n riverWaveUniforms[3] = effect.waterColor.g;\n riverWaveUniforms[4] = effect.waterColor.b;\n riverWaveUniforms[5] = effect.foamColor.r;\n riverWaveUniforms[6] = effect.foamColor.g;\n riverWaveUniforms[7] = effect.foamColor.b;\n riverWaveUniforms[8] = effect.scrollOffsetY;\n riverWaveUniforms[9] = effect.waveScale;\n riverWavePaint.setShader(riverWaveEffect.makeShader(riverWaveUniforms));\n return riverWavePaint;\n }\n\n function applyMoonCrater(effect: MoonCraterEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (moonCraterEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(MOON_CRATER_SKSL);\n if (compiled === null) return defaultPaint;\n moonCraterEffect = compiled;\n }\n if (moonCraterPaint === null) moonCraterPaint = Skia.Paint();\n moonCraterUniforms[0] = effect.resolutionW;\n moonCraterUniforms[1] = effect.resolutionH;\n moonCraterUniforms[2] = effect.moonColor.r;\n moonCraterUniforms[3] = effect.moonColor.g;\n moonCraterUniforms[4] = effect.moonColor.b;\n moonCraterUniforms[5] = effect.craterColor.r;\n moonCraterUniforms[6] = effect.craterColor.g;\n moonCraterUniforms[7] = effect.craterColor.b;\n moonCraterUniforms[8] = effect.scrollOffsetY;\n moonCraterUniforms[9] = effect.craterCount;\n moonCraterPaint.setShader(moonCraterEffect.makeShader(moonCraterUniforms));\n return moonCraterPaint;\n }\n\n function applyCaveInterior(effect: CaveInteriorEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (caveInteriorEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(CAVE_INTERIOR_SKSL);\n if (compiled === null) return defaultPaint;\n caveInteriorEffect = compiled;\n }\n if (caveInteriorPaint === null) caveInteriorPaint = Skia.Paint();\n caveInteriorUniforms[0] = effect.resolutionW;\n caveInteriorUniforms[1] = effect.resolutionH;\n caveInteriorUniforms[2] = effect.caveColor.r;\n caveInteriorUniforms[3] = effect.caveColor.g;\n caveInteriorUniforms[4] = effect.caveColor.b;\n caveInteriorUniforms[5] = effect.glowColor.r;\n caveInteriorUniforms[6] = effect.glowColor.g;\n caveInteriorUniforms[7] = effect.glowColor.b;\n caveInteriorUniforms[8] = effect.scrollOffsetY;\n caveInteriorUniforms[9] = effect.stalactiteCount;\n caveInteriorPaint.setShader(caveInteriorEffect.makeShader(caveInteriorUniforms));\n return caveInteriorPaint;\n }\n\n function applyMountainHills(effect: MountainHillsEffect): PaintLike {\n if (Skia.RuntimeEffect === undefined) return defaultPaint;\n if (mountainHillsEffect === null) {\n const compiled = Skia.RuntimeEffect.Make(MOUNTAIN_HILLS_SKSL);\n if (compiled === null) return defaultPaint;\n mountainHillsEffect = compiled;\n }\n if (mountainHillsPaint === null) mountainHillsPaint = Skia.Paint();\n mountainHillsUniforms[0] = effect.resolutionW;\n mountainHillsUniforms[1] = effect.resolutionH;\n mountainHillsUniforms[2] = effect.skyColor.r;\n mountainHillsUniforms[3] = effect.skyColor.g;\n mountainHillsUniforms[4] = effect.skyColor.b;\n mountainHillsUniforms[5] = effect.hillColor.r;\n mountainHillsUniforms[6] = effect.hillColor.g;\n mountainHillsUniforms[7] = effect.hillColor.b;\n mountainHillsUniforms[8] = effect.scrollOffsetY;\n mountainHillsUniforms[9] = effect.ridgeScale;\n mountainHillsPaint.setShader(mountainHillsEffect.makeShader(mountainHillsUniforms));\n return mountainHillsPaint;\n }\n\n function drawOutlined(\n cvs: DrawSurfaceLike,\n image: unknown,\n commands: SpriteDrawCommand[],\n n: number,\n effect: OutlineEffect,\n alpha: number,\n ): void {\n const paint = applyOutlinePaint(effect);\n paint.setAlphaf(alpha);\n const srcs = getCachedSrcSlice(n);\n for (let k = 0; k < OUTLINE_OFFSETS.length; k++) {\n const offset = OUTLINE_OFFSETS[k] as readonly [number, number];\n fillXforms(commands, n, offset[0] * effect.width, offset[1] * effect.width);\n const dsts = getCachedXformSlice(n);\n cvs.drawAtlas(image, srcs, dsts, paint);\n }\n paint.setAlphaf(1);\n defaultPaint.setAlphaf(alpha);\n fillXforms(commands, n, 0, 0);\n const mainDsts = getCachedXformSlice(n);\n cvs.drawAtlas(image, srcs, mainDsts, defaultPaint);\n defaultPaint.setAlphaf(1);\n }\n\n function resolveEffectPaint(effect: AnySpriteEffect): PaintLike {\n switch (effect.type) {\n case 'tint':\n return applyTint(effect);\n case 'flash':\n return applyFlash(effect);\n case 'grayscale':\n return applyGrayscale(effect);\n case 'custom':\n return applyCustom(effect);\n case 'water':\n return applyWater(effect, currentFrameTime);\n case 'fog':\n return applyFog(effect, currentFrameTime);\n case 'fire':\n return applyFire(effect, currentFrameTime);\n case 'drop-shadow':\n return applyDropShadow(effect);\n case 'outline':\n return applyOutlinePaint(effect);\n case 'roofs-city':\n return applyRoofsCity(effect);\n case 'forest-canopy':\n return applyForestCanopy(effect);\n case 'river-wave':\n return applyRiverWave(effect);\n case 'moon-crater':\n return applyMoonCrater(effect);\n case 'cave-interior':\n return applyCaveInterior(effect);\n case 'mountain-hills':\n return applyMountainHills(effect);\n }\n }\n\n return {\n canvas,\n\n beginFrame(viewportWidth, viewportHeight, time = 0, pixelDensity = 1): void {\n if (viewportWidth === 0 || viewportHeight === 0) return;\n _boundRuntime = null;\n _boundChain = null;\n currentFrameTime = time;\n const pd = pixelDensity;\n recordingBounds.setXYWH(0, 0, viewportWidth * pd, viewportHeight * pd);\n current = recorder.beginRecording(recordingBounds);\n if (pd !== 1) current.scale(pd, pd);\n },\n\n drawAtlas(image, commands): void {\n if (current === null) return;\n const n = commands.length;\n if (n === 0) return;\n\n const effect = commands[0]?.effect;\n const alpha = commands[0]?.alpha ?? 1;\n currentSpriteW = commands[0]?.dstW ?? 0;\n currentSpriteH = commands[0]?.dstH ?? 0;\n\n if (\n effect?.type === 'roofs-city' ||\n effect?.type === 'forest-canopy' ||\n effect?.type === 'river-wave' ||\n effect?.type === 'moon-crater' ||\n effect?.type === 'cave-interior' ||\n effect?.type === 'mountain-hills'\n ) {\n const paint = resolveEffectPaint(effect);\n paint.setAlphaf(alpha);\n ensureZoneBgPoolCapacity(n);\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n zoneBgRects[i]!.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);\n current.drawRect(zoneBgRects[i]!, paint);\n }\n paint.setAlphaf(1);\n return;\n }\n\n if (commands[0]?.stretch) {\n ensureStretchPoolCapacity(n);\n if (stretchPaint === null) stretchPaint = Skia.Paint();\n const sp = stretchPaint;\n // outline requires two passes (SRC_IN silhouette + source) which is\n // incompatible with the single-drawImageRect stretch path; fall back\n // to alpha-only like the pre-effect baseline.\n const sPaint =\n effect === undefined || effect.type === 'outline' ? sp : resolveEffectPaint(effect);\n sPaint.setAlphaf(alpha);\n for (let i = 0; i < n; i++) {\n const cmd = commands[i] as SpriteDrawCommand;\n const srcR = stretchSrcRects[i]!;\n const dstR = stretchDstRects[i]!;\n srcR.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);\n dstR.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);\n current.drawImageRect(image, srcR, dstR, sPaint);\n }\n sPaint.setAlphaf(1);\n return;\n }\n\n ensurePoolCapacity(n);\n fillSrcRects(commands, n);\n\n if (effect?.type === 'outline') {\n drawOutlined(current, image, commands, n, effect, alpha);\n return;\n }\n\n fillXforms(commands, n, 0, 0);\n const srcs = getCachedSrcSlice(n);\n const dsts = getCachedXformSlice(n);\n const paint = effect === undefined ? defaultPaint : resolveEffectPaint(effect);\n paint.setAlphaf(alpha);\n current.drawAtlas(image, srcs, dsts, paint);\n paint.setAlphaf(1);\n },\n\n setPostFxBinding(runtime, chain): void {\n _boundRuntime = runtime;\n _boundChain = chain;\n },\n\n hasPostFxBinding(): boolean {\n return (\n _boundRuntime !== null &&\n !!_boundRuntime.supported &&\n _boundRuntime.buildPass !== undefined &&\n _boundChain !== null\n );\n },\n\n buildPostFxPasses(runtime, chain, width, height): readonly FrameRendererPostFxDescriptor[] {\n const rt = runtime ?? _boundRuntime;\n const ch = chain ?? _boundChain;\n if (rt === null || !rt.supported) return [];\n if (rt.buildPass === undefined) {\n logOnce('buildPostFxPasses: runtime.supported but buildPass absent — no passes');\n return [];\n }\n if (ch === null) return [];\n const passes = rt.buildPass(ch, width, height);\n if (passes.length === 0) return [];\n const out: FrameRendererPostFxDescriptor[] = [];\n for (const pass of passes) {\n const shader = pass.shader;\n const uniformCount =\n typeof shader.getUniformCount === 'function' ? shader.getUniformCount() : 0;\n const uniforms: Record<string, number | readonly number[]> = {};\n for (let i = 0; i < uniformCount; i++) {\n const name =\n typeof shader.getUniformName === 'function' ? shader.getUniformName(i) : String(i);\n const info =\n typeof shader.getUniform === 'function'\n ? shader.getUniform(i)\n : { columns: 1, rows: 1, slot: i };\n const size = info.columns * info.rows;\n if (size === 1) {\n uniforms[name] = pass.uniforms[info.slot] ?? 0;\n } else {\n const vec: number[] = [];\n for (let k = 0; k < size; k++) {\n vec.push(pass.uniforms[info.slot + k] ?? 0);\n }\n uniforms[name] = vec;\n }\n }\n out.push({\n id: pass.id,\n source: shader,\n uniforms,\n children: pass.children,\n });\n }\n return out;\n },\n\n endFrame(): PictureLike | null {\n if (current === null) return null;\n current = null;\n return recorder.finishRecordingAsPicture();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Xform branch counters — observable surface for bench/diagnostic use\n// ---------------------------------------------------------------------------\n\n/** Zero both branch counters unconditionally (no __DEV__ guard). */\nexport function resetXformCounters(): void {\n _xformUnrotatedCount = 0;\n _xformRotatedCount = 0;\n}\n\n/**\n * Cumulative `fillXforms` branch hit counts since the last reset. Both stay 0\n * when `__DEV__` is falsy or absent.\n *\n * These count transform WRITES, not sprites: a run drawn with an outline effect\n * passes through `fillXforms` 9 times (8 offset passes + 1 main pass), so it\n * contributes 9 hits per command — the transform work the renderer actually\n * performs. The counters are process-global: every `createFrameRenderer`\n * instance increments the same pair, so readings sum across live renderers.\n *\n * Allocates one object per call — never call it from the render loop.\n */\nexport function getXformCounters(): { rotated: number; unrotated: number } {\n return { rotated: _xformRotatedCount, unrotated: _xformUnrotatedCount };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers (module-private, not exported)\n// ---------------------------------------------------------------------------\n\nfunction writeColor(out: Float32Array, color: SpriteColor, alphaMul: number): void {\n out[0] = color.r;\n out[1] = color.g;\n out[2] = color.b;\n out[3] = color.a * alphaMul;\n}\n\nfunction writeGrayscaleMatrix(out: number[], amount: number): void {\n const inv = 1 - amount;\n const lr = 0.299 * amount;\n const lg = 0.587 * amount;\n const lb = 0.114 * amount;\n out[0] = inv + lr;\n out[1] = lg;\n out[2] = lb;\n out[3] = 0;\n out[4] = 0;\n out[5] = lr;\n out[6] = inv + lg;\n out[7] = lb;\n out[8] = 0;\n out[9] = 0;\n out[10] = lr;\n out[11] = lg;\n out[12] = inv + lb;\n out[13] = 0;\n out[14] = 0;\n out[15] = 0;\n out[16] = 0;\n out[17] = 0;\n out[18] = 1;\n out[19] = 0;\n}\n\nfunction fillCustomUniforms(\n effect: CustomShaderEffect,\n shader: RuntimeEffectLike,\n out: number[],\n): void {\n const count = shader.getUniformCount();\n for (let i = 0; i < out.length; i++) out[i] = 0;\n for (let i = 0; i < count; i++) {\n const info = shader.getUniform(i);\n const name = shader.getUniformName(i);\n const value = effect.uniforms[name];\n const size = info.columns * info.rows;\n if (value === undefined) continue;\n if (typeof value === 'number') {\n out[info.slot] = value;\n continue;\n }\n for (let k = 0; k < size; k++) {\n out[info.slot + k] = value[k] ?? 0;\n }\n }\n}\n","import type { SkImageLike, SpriteFrame } from '@flare-engine/assets';\nimport type { Query, World } from '@flare-engine/ecs';\nimport { type ComponentDef, defineComponent } from '@flare-engine/ecs';\nimport { sortByDrawOrder } from './draw-order.js';\nimport type { SpriteBatch } from './sprite-batch.js';\nimport type { AnySpriteEffect } from './sprite-effects.js';\n\n/** Position component data expected by the render system. */\nexport interface PositionData {\n x: number;\n y: number;\n}\n\n/** Sprite anchor as a fraction of the destination rect — `(0, 0)` is\n * top-left (default), `(0.5, 0.5)` is center, `(1, 1)` is bottom-right.\n * When present, `Position` refers to the anchor point rather than the\n * sprite's top-left corner. */\nexport interface SpriteAnchor {\n x: number;\n y: number;\n}\n\n/** Sprite component data expected by the render system. */\nexport interface SpriteData {\n image: SkImageLike;\n frame: SpriteFrame;\n z: number;\n width?: number;\n height?: number;\n /** Optional per-sprite effect (tint, flash, grayscale, outline, custom).\n * Shared instances are batched together — prefer reusing an effect\n * reference across entities that should render identically. */\n effect?: AnySpriteEffect;\n /** Optional pivot point for the sprite. Defaults to top-left `(0, 0)`. */\n anchor?: SpriteAnchor;\n /** Per-instance alpha multiplier. Range [0, 1]. Default 1 (fully opaque).\n * Mutate directly each frame — e.g. `sprite.alpha = lerp(1, 0, t)`.\n * Sprites with different alphas are placed in separate draw batches. */\n alpha?: number;\n /** Rotation in radians, clockwise (Skia Y-down convention). Default 0.\n * Applied about the sprite's anchor point. Non-uniform scale combined with\n * rotation is unsupported — scale is inferred uniformly from dstW/srcW. */\n rotation?: number;\n /**\n * When true, draw via `drawImageRect` so `width` and `height` are honored\n * independently (non-uniform scale). Use for background tiles or any sprite\n * that must fill a non-square destination rect exactly.\n *\n * Default (absent/false): drawn via the batched `drawAtlas` uniform-scale\n * path. Stretched sprites are NOT batched with regular sprites.\n *\n * Rotation is unsupported in stretch mode — combine only with `rotation = 0`.\n */\n stretch?: boolean;\n}\n\n/** Cached entry for sorting. Reused each frame to avoid allocs. */\ninterface RenderEntry {\n entityId: number;\n z: number;\n}\n\n/**\n * Create a render system function.\n *\n * This is not a defineSystem() wrapper because it needs extra parameters\n * (SpriteBatch) beyond what the ECS system interface provides. Instead,\n * it returns a function to call in your game loop.\n *\n * The returned function only **queues** sprite draws into `batch` — it\n * does not call `batch.flush()`. Flushing is owned by the frame owner\n * (e.g. `<GameView>`) so that all draw sources (ECS, particles, custom)\n * can contribute to a single flush at the render phase.\n *\n * @param world The ECS world.\n * @param query Query that matches entities with Position + Sprite.\n * @param positionDef ComponentDef for Position.\n * @param spriteDef ComponentDef for Sprite.\n * @param batch SpriteBatch instance to queue draws into.\n */\nexport function createRenderSystem(\n world: World,\n query: Query,\n positionDef: ComponentDef<PositionData>,\n spriteDef: ComponentDef<SpriteData>,\n batch: SpriteBatch,\n): () => void {\n // Pre-allocated sorted array — grows as needed, never shrinks\n const entries: RenderEntry[] = [];\n\n return () => {\n // Collect entities\n let i = 0;\n for (const entityId of query) {\n const sprite = world.get(entityId, spriteDef);\n if (!sprite) continue;\n\n if (i >= entries.length) {\n entries.push({ entityId, z: 0 });\n }\n const entry = entries[i]!;\n entry.entityId = entityId;\n entry.z = sprite.z;\n i++;\n }\n\n // Shrink policy — avoid thrashing on variable entity counts.\n const count = i;\n if (entries.length > count * 2 && entries.length > 64) {\n entries.length = count;\n }\n // Zero-alloc: sort the pooled array in place, bounded to live count.\n sortByDrawOrder(entries, count);\n\n // Queue draws in z-order\n for (let j = 0; j < count; j++) {\n const entry = entries[j]!;\n const pos = world.get(entry.entityId, positionDef);\n const sprite = world.get(entry.entityId, spriteDef);\n if (!pos || !sprite) continue;\n\n const anchor = sprite.anchor;\n batch.draw(\n sprite.image,\n sprite.frame,\n pos.x,\n pos.y,\n sprite.width,\n sprite.height,\n sprite.effect,\n anchor ? anchor.x : 0,\n anchor ? anchor.y : 0,\n sprite.alpha ?? 1,\n sprite.rotation ?? 0,\n sprite.stretch ?? false,\n );\n }\n };\n}\n\n/** Canonical Sprite component — queried by the render system alongside `Position`. */\nexport const Sprite = defineComponent<SpriteData>('Sprite', () => ({\n image: null as unknown as SkImageLike,\n frame: { name: '', x: 0, y: 0, w: 0, h: 0 },\n z: 0,\n}));\n","/** Minimal Skia Canvas-like interface for the Renderer. */\nexport interface CanvasLike {\n save(): number;\n restore(): void;\n translate(x: number, y: number): void;\n scale(sx: number, sy: number): void;\n /** Rotate around (cx, cy). Angle in DEGREES — both Skia backends take degrees. */\n rotate(degrees: number, cx: number, cy: number): void;\n clear(color: number): void;\n}\n\n/** Stats tracked per frame. */\nexport interface RenderStats {\n /**\n * Number of SpriteBatch flush runs emitted this frame — one per contiguous\n * same-key (image, effect, alpha, stretch) run, NOT the number of raw canvas\n * calls. An outline run fans into 9 canvas drawAtlas calls, a stretch run\n * into n drawImageRect calls, a zone-bg run into n drawRect calls.\n * Equal to batchCount whenever SpriteBatch.flush is the only writer;\n * recordDrawCall()/recordBatch() are public and can diverge them if a\n * custom batching system drives them independently.\n */\n drawCalls: number;\n /**\n * Number of SpriteBatch flush runs — incremented alongside drawCalls at\n * the same two sites in SpriteBatch.flush. Equal to drawCalls when only\n * SpriteBatch.flush writes both; recordDrawCall()/recordBatch() are\n * independent public methods and can produce different values if called\n * directly by a custom batching system.\n */\n batchCount: number;\n}\n\n/**\n * Renderer — manages the Skia Canvas lifecycle for a frame.\n * `begin()` to start, draw your content, `end()` to finish.\n */\nexport class Renderer {\n /** Current frame stats. */\n readonly stats: RenderStats = { drawCalls: 0, batchCount: 0 };\n\n private _canvas: CanvasLike | null = null;\n private _inFrame = false;\n\n /** Set the canvas to render to. Called once when canvas is available. */\n setCanvas(canvas: CanvasLike): void {\n this._canvas = canvas;\n }\n\n /** Get the current canvas. Throws if not in a frame. */\n get canvas(): CanvasLike {\n if (!this._canvas) {\n throw new Error('Renderer: no canvas set');\n }\n return this._canvas;\n }\n\n /** Whether we are currently between begin() and end(). */\n get inFrame(): boolean {\n return this._inFrame;\n }\n\n /**\n * Begin a new frame. Clears the canvas and resets stats.\n * @param clearColor 32-bit ARGB color to clear with (default black = 0xFF000000).\n */\n begin(clearColor = 0xff000000): void {\n if (this._inFrame) {\n throw new Error('Renderer: begin() called while already in frame');\n }\n this._inFrame = true;\n this.stats.drawCalls = 0;\n this.stats.batchCount = 0;\n this.canvas.save();\n this.canvas.clear(clearColor);\n }\n\n /**\n * End the current frame. Restores canvas state.\n */\n end(): void {\n if (!this._inFrame) {\n throw new Error('Renderer: end() called without begin()');\n }\n this.canvas.restore();\n this._inFrame = false;\n }\n\n /**\n * Apply camera transform to the canvas.\n * Call after begin() and before drawing.\n */\n applyCamera(\n cameraX: number,\n cameraY: number,\n zoom: number,\n rotation: number,\n viewportWidth: number,\n viewportHeight: number,\n ): void {\n const c = this.canvas;\n // Move origin to viewport center\n c.translate(viewportWidth / 2, viewportHeight / 2);\n // Apply zoom\n c.scale(zoom, zoom);\n // Apply rotation around center. Camera2D.rotation is radians; the CanvasLike\n // rotate contract is degrees — convert once here (the single camera seam).\n if (rotation !== 0) {\n c.rotate((-rotation * 180) / Math.PI, 0, 0);\n }\n // Offset by camera position\n c.translate(-cameraX, -cameraY);\n }\n\n /** Increment draw call counter. Invoked in tandem with recordBatch, once per\n * flush run in SpriteBatch.flush. */\n recordDrawCall(): void {\n this.stats.drawCalls++;\n }\n\n /** Increment batch counter. Invoked in tandem with recordDrawCall, once per\n * flush run in SpriteBatch.flush. */\n recordBatch(): void {\n this.stats.batchCount++;\n }\n}\n","import type { SkImageLike, SpriteFrame } from '@flare-engine/assets';\nimport type { Renderer } from './renderer.js';\nimport type { AnySpriteEffect } from './sprite-effects.js';\n\n/** A single sprite draw request in the batch. */\nexport interface SpriteDrawCommand {\n /** Texture (Skia SkImage). */\n image: SkImageLike;\n /** Source rectangle in the texture. */\n srcX: number;\n srcY: number;\n srcW: number;\n srcH: number;\n /** Destination position and size. */\n dstX: number;\n dstY: number;\n dstW: number;\n dstH: number;\n /** Optional per-sprite effect. Shared by reference across commands in a\n * batch — the render adapter applies it once per batch. */\n effect: AnySpriteEffect | undefined;\n /** Per-instance alpha multiplier. Range [0, 1]. Default 1 (fully opaque).\n * Participates in the batch key — sprites with different alphas are\n * placed in separate batches so the renderer can apply each alpha once\n * per draw call without per-sprite overhead. */\n alpha: number;\n /** Rotation in radians, clockwise (Skia Y-down convention). Default 0.\n * Rotation is applied about the anchor point. */\n rotation: number;\n /** Anchor X fraction stored for the bridge rotation path. Range [0, 1]. */\n anchorX: number;\n /** Anchor Y fraction stored for the bridge rotation path. Range [0, 1]. */\n anchorY: number;\n /**\n * When true, the sprite is drawn via `drawImageRect` so `dstW` and `dstH`\n * are honored independently (non-uniform scale). A 256×256 source image\n * will fill an arbitrary W×H destination rect.\n *\n * Default (false/absent): drawn via the batched `drawAtlas` path (uniform\n * scale — dstW/srcW drives both axes). Stretched sprites are NOT batched\n * with regular sprites; each is an independent `drawImageRect` call.\n *\n * Rotation is unsupported in stretch mode — the RSXform similarity-transform\n * limit does not apply, but the bridge draws a plain axis-aligned rect.\n * Combine with a zero rotation only.\n */\n stretch: boolean;\n}\n\n/** Internal run-bucket — one reused across all runs per flush. */\ninterface Batch {\n image: SkImageLike;\n effect: AnySpriteEffect | undefined;\n alpha: number;\n stretch: boolean;\n commands: SpriteDrawCommand[];\n}\n\n/**\n * SpriteBatch — collects sprite draw commands and groups them by texture\n * (and optionally by effect) for efficient batched rendering.\n *\n * Usage:\n * 1. Call `draw()` for each sprite, optionally passing an effect\n * 2. Call `flush()` with an atlas draw callback to execute all batched draws\n * 3. Batching groups consecutive commands by `(image, effect, alpha, stretch)`\n * identity, preserving global insertion (z-sorted) order. Sharing an effect\n * reference and the same alpha across consecutive entities maximizes batch\n * size while keeping sprites in correct draw order.\n *\n * The actual Skia Atlas draw call is delegated to a callback so this module\n * remains testable without Skia.\n */\nexport class SpriteBatch {\n private _commands: SpriteDrawCommand[] = [];\n private _pool: SpriteDrawCommand[] = [];\n private _batches: Batch[] = [];\n private _allocCount = 0;\n\n /** Number of queued draw commands. */\n get count(): number {\n return this._commands.length;\n }\n\n /** Cumulative number of SpriteDrawCommand objects allocated because the internal\n * command pool was empty. Steady state should be 0 after warm-up. */\n get allocCount(): number {\n return this._allocCount;\n }\n\n /** Queue a sprite draw from a frame.\n *\n * `anchorX` / `anchorY` define the pivot point of the sprite as a\n * fraction of its destination size — `(0, 0)` = top-left (default),\n * `(0.5, 0.5)` = center, `(1, 1)` = bottom-right. `dstX` / `dstY`\n * then refer to the anchor point instead of the top-left corner.\n *\n * `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */\n draw(\n image: SkImageLike,\n frame: SpriteFrame,\n dstX: number,\n dstY: number,\n dstW?: number,\n dstH?: number,\n effect?: AnySpriteEffect,\n anchorX = 0,\n anchorY = 0,\n alpha = 1,\n rotation = 0,\n stretch = false,\n ): void {\n const cmd = this._acquire();\n const w = dstW ?? frame.w;\n const h = dstH ?? frame.h;\n cmd.image = image;\n cmd.srcX = frame.x;\n cmd.srcY = frame.y;\n cmd.srcW = frame.w;\n cmd.srcH = frame.h;\n cmd.dstX = dstX - anchorX * w;\n cmd.dstY = dstY - anchorY * h;\n cmd.dstW = w;\n cmd.dstH = h;\n cmd.effect = effect;\n cmd.alpha = alpha;\n cmd.rotation = rotation;\n cmd.anchorX = anchorX;\n cmd.anchorY = anchorY;\n cmd.stretch = stretch;\n this._commands.push(cmd);\n }\n\n /** Queue a raw sprite draw with explicit src/dst rects. `dstX` / `dstY`\n * refer to the anchor point — offset by `anchorX * dstW` / `anchorY * dstH`.\n *\n * `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */\n drawRaw(\n image: SkImageLike,\n srcX: number,\n srcY: number,\n srcW: number,\n srcH: number,\n dstX: number,\n dstY: number,\n dstW: number,\n dstH: number,\n effect?: AnySpriteEffect,\n anchorX = 0,\n anchorY = 0,\n alpha = 1,\n rotation = 0,\n stretch = false,\n ): void {\n const cmd = this._acquire();\n cmd.image = image;\n cmd.srcX = srcX;\n cmd.srcY = srcY;\n cmd.srcW = srcW;\n cmd.srcH = srcH;\n cmd.dstX = dstX - anchorX * dstW;\n cmd.dstY = dstY - anchorY * dstH;\n cmd.dstW = dstW;\n cmd.dstH = dstH;\n cmd.effect = effect;\n cmd.alpha = alpha;\n cmd.rotation = rotation;\n cmd.anchorX = anchorX;\n cmd.anchorY = anchorY;\n cmd.stretch = stretch;\n this._commands.push(cmd);\n }\n\n /**\n * Flush all queued commands in insertion order, grouping maximal consecutive\n * runs that share the same `(image, effect, alpha, stretch)` key into a\n * single `drawAtlas` call. This preserves global z-order (the caller is\n * responsible for queuing in z-sorted order) while still batching runs of\n * same-key sprites for efficiency.\n *\n * @param drawAtlas Callback invoked once per run. Receives\n * `(image, commands)` — all commands in a run share the same key and are\n * contiguous in the original insertion order.\n * @param renderer Optional renderer to record stats.\n */\n flush(\n drawAtlas: (image: SkImageLike, commands: SpriteDrawCommand[]) => void,\n renderer?: Renderer,\n ): void {\n if (this._commands.length === 0) return;\n\n // Ensure one reusable run-bucket exists (allocated once, never in steady state).\n let run = this._batches[0];\n if (run === undefined) {\n run = { image: null!, effect: undefined, alpha: 1, stretch: false, commands: [] };\n this._batches[0] = run;\n }\n\n // Walk commands in insertion order. Emit a drawAtlas call whenever the\n // run key changes, then reset the run bucket for the next run.\n for (const cmd of this._commands) {\n if (\n run.commands.length > 0 &&\n (cmd.image !== run.image ||\n cmd.effect !== run.effect ||\n cmd.alpha !== run.alpha ||\n cmd.stretch !== run.stretch)\n ) {\n drawAtlas(run.image, run.commands);\n if (renderer) {\n renderer.recordDrawCall();\n renderer.recordBatch();\n }\n run.commands.length = 0;\n }\n run.image = cmd.image;\n run.effect = cmd.effect;\n run.alpha = cmd.alpha;\n run.stretch = cmd.stretch;\n run.commands.push(cmd);\n }\n\n // Flush the final run.\n if (run.commands.length > 0) {\n drawAtlas(run.image, run.commands);\n if (renderer) {\n renderer.recordDrawCall();\n renderer.recordBatch();\n }\n run.commands.length = 0;\n }\n\n // Return commands to pool. Field values remain on the command until\n // the next draw() overwrites them — consumers must read inside the\n // drawAtlas callback, not after flush returns.\n for (const cmd of this._commands) {\n this._pool.push(cmd);\n }\n this._commands.length = 0;\n }\n\n /** Clear all queued commands without drawing. */\n clear(): void {\n for (const cmd of this._commands) {\n this._pool.push(cmd);\n }\n this._commands.length = 0;\n }\n\n private _acquire(): SpriteDrawCommand {\n if (this._pool.length > 0) {\n return this._pool.pop()!;\n }\n this._allocCount++;\n return {\n image: null!,\n srcX: 0,\n srcY: 0,\n srcW: 0,\n srcH: 0,\n dstX: 0,\n dstY: 0,\n dstW: 0,\n dstH: 0,\n effect: undefined,\n alpha: 1,\n rotation: 0,\n anchorX: 0,\n anchorY: 0,\n stretch: false,\n };\n }\n}\n","import type { SkImageLike } from '@flare-engine/assets';\nimport type { SpriteBatch } from './sprite-batch.js';\n\n/** A single glyph in a bitmap font. */\nexport interface Glyph {\n /** Character code (Unicode). */\n id: number;\n /** Source rect in the font texture. */\n x: number;\n y: number;\n width: number;\n height: number;\n /** Draw offset. */\n xOffset: number;\n yOffset: number;\n /** Horizontal advance after this glyph. */\n xAdvance: number;\n}\n\n/** Kerning pair. */\nexport interface KerningPair {\n first: number;\n second: number;\n amount: number;\n}\n\n/** Parsed bitmap font data. */\nexport interface BitmapFont {\n /** Line height in pixels. */\n lineHeight: number;\n /** Baseline offset. */\n base: number;\n /** Glyph map by character code. */\n glyphs: Map<number, Glyph>;\n /** Kerning map. Key = `first << 16 | second`. */\n kernings: Map<number, number>;\n}\n\n/**\n * Parse BMFont JSON format (common output from tools like Hiero, BMFont, msdf-bmfont).\n */\nexport function parseBMFontJSON(json: unknown): BitmapFont {\n const data = json as {\n common: { lineHeight: number; base: number };\n chars: Array<{\n id: number;\n x: number;\n y: number;\n width: number;\n height: number;\n xoffset: number;\n yoffset: number;\n xadvance: number;\n }>;\n kernings?: Array<{ first: number; second: number; amount: number }>;\n };\n\n const glyphs = new Map<number, Glyph>();\n for (const c of data.chars) {\n glyphs.set(c.id, {\n id: c.id,\n x: c.x,\n y: c.y,\n width: c.width,\n height: c.height,\n xOffset: c.xoffset,\n yOffset: c.yoffset,\n xAdvance: c.xadvance,\n });\n }\n\n const kernings = new Map<number, number>();\n if (data.kernings) {\n for (const k of data.kernings) {\n kernings.set((k.first << 16) | k.second, k.amount);\n }\n }\n\n return {\n lineHeight: data.common.lineHeight,\n base: data.common.base,\n glyphs,\n kernings,\n };\n}\n\n/** Get kerning between two characters. */\nexport function getKerning(font: BitmapFont, first: number, second: number): number {\n return font.kernings.get((first << 16) | second) ?? 0;\n}\n\n/** Measure the width of a text string in pixels. */\nexport function measureText(font: BitmapFont, text: string): number {\n let width = 0;\n let prevCode = -1;\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const glyph = font.glyphs.get(code);\n if (!glyph) continue;\n if (prevCode >= 0) {\n width += getKerning(font, prevCode, code);\n }\n width += glyph.xAdvance;\n prevCode = code;\n }\n return width;\n}\n\n/**\n * Draw text using a SpriteBatch.\n * Characters are batched as individual sprites from the font texture.\n */\nexport function drawText(\n batch: SpriteBatch,\n font: BitmapFont,\n image: SkImageLike,\n text: string,\n x: number,\n y: number,\n scale = 1,\n): void {\n let cursorX = x;\n let prevCode = -1;\n\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const glyph = font.glyphs.get(code);\n if (!glyph) continue;\n\n if (prevCode >= 0) {\n cursorX += getKerning(font, prevCode, code) * scale;\n }\n\n batch.drawRaw(\n image,\n glyph.x,\n glyph.y,\n glyph.width,\n glyph.height,\n cursorX + glyph.xOffset * scale,\n y + glyph.yOffset * scale,\n glyph.width * scale,\n glyph.height * scale,\n );\n\n cursorX += glyph.xAdvance * scale;\n prevCode = code;\n }\n}\n"],"mappings":";;;;AAsBA,SAAS,aAAa,MAAsB;CAC1C,MAAM,IAAI,KAAK,IAAI,OAAO,UAAU,OAAO,MAAM,IAAI;CACrD,OAAO,IAAI,KAAK,MAAM,CAAC;AACzB;AAIA,MAAM,uBAAO,IAAI,WAAW,GAAG;AAC/B;CACE,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,KAAK,KAAK,KAAK;CAExC,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK;EAC5B,KAAK,IAAK,MAAM,QAAS;EACzB,IAAI,KAAK,KAAK,GAAG,UAAU,MAAM;EACjC,KAAK,IAAK,MAAM,QAAS;EACzB,MAAM,IAAI,KAAK,IAAI;EACnB,MAAM,IAAI,KAAK;EACf,KAAK,KAAK,KAAK;EACf,KAAK,KAAK;CACZ;AACF;AAGA,MAAM,IAAI,KAAK;AACf,MAAM,QAAkD;CACtD,CAAC,GAAG,CAAC;CACL,CAAC,GAAG,CAAC;CACL,CAAC,GAAG,CAAC;CACL,CAAC,CAAC,GAAG,CAAC;CACN,CAAC,IAAI,CAAC;CACN,CAAC,CAAC,GAAG,CAAC,CAAC;CACP,CAAC,GAAG,EAAE;CACN,CAAC,GAAG,CAAC,CAAC;AACR;AAEA,SAAS,MAAM,MAAc,GAAW,GAAmB;CACzD,MAAM,IAAI,MAAM,OAAO;CACvB,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK;AAC3B;AAEA,SAAS,KAAK,GAAmB;CAC/B,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM;AACzC;;AAGA,SAAS,QAAQ,GAAW,GAAmB;CAC7C,MAAM,KAAK,KAAK,MAAM,CAAC,IAAI;CAC3B,MAAM,KAAK,KAAK,MAAM,CAAC,IAAI;CAC3B,MAAM,KAAK,IAAI,KAAK,MAAM,CAAC;CAC3B,MAAM,KAAK,IAAI,KAAK,MAAM,CAAC;CAC3B,MAAM,IAAI,KAAK,EAAE;CACjB,MAAM,IAAI,KAAK,EAAE;CAEjB,MAAM,KAAK,KAAK,KAAK,MAAO;CAC5B,MAAM,KAAK,KAAK,KAAK,MAAO,KAAK;CACjC,MAAM,KAAK,KAAK,KAAM,KAAK,IAAK,OAAQ;CACxC,MAAM,KAAK,KAAK,KAAM,KAAK,IAAK,OAAQ,KAAK;CAE7C,MAAM,KAAK;CACX,MAAM,KAAK;CACX,MAAM,KAAK,KAAK;CAChB,MAAM,KAAK,KAAK;CAEhB,MAAM,MAAM,MAAM,IAAI,IAAI,EAAE;CAC5B,MAAM,MAAM,MAAM,IAAI,IAAI,EAAE;CAC5B,MAAM,MAAM,MAAM,IAAI,IAAI,EAAE;CAC5B,MAAM,MAAM,MAAM,IAAI,IAAI,EAAE;CAE5B,MAAM,SAAS,MAAM,KAAK,MAAM;CAEhC,OAAO,SAAS,KADD,MAAM,KAAK,MAAM,OACF;AAChC;;AAGA,SAAS,WAAW,GAAW,GAAmB;CAChD,OAAO,QAAS,QAAQ,GAAG,CAAC,IAAI,QAAS,QAAQ,IAAI,GAAG,IAAI,CAAC;AAC/D;;;;;;AAOA,IAAa,WAAb,MAAsB;;CAEpB,IAAI;CACJ,IAAI;;CAGJ,OAAO;;CAGP,WAAW;;CAGX,gBAAgB;;CAGhB,iBAAiB;;CAGjB,SAAsB;CAEtB,UAAmE;CACnE,SAAoC;CACpC,eAA0C;CAC1C,aAAqB;CACrB,mBAA2B;CAC3B,eAAuB;;CAGvB,YAAY,OAAe,QAAsB;EAC/C,KAAK,gBAAgB;EACrB,KAAK,iBAAiB;CACxB;;CAGA,OAAO,QAAc,SAA+B;EAClD,KAAK,UAAU;GAAE;GAAQ,SAAS,WAAW,CAAC;EAAE;CAClD;;CAGA,WAAiB;EACf,KAAK,UAAU;CACjB;;;;;;;;;;;;CAaA,eAAe,OAAqB;EAClC,KAAK,eAAe,QAAQ,IAAI,IAAI;CACtC;;CAGA,IAAI,cAAsB;EACxB,OAAO,KAAK;CACd;;CAGA,MAAM,WAAmB,UAAwB;EAC/C,MAAM,IAAI,KAAK;EACf,IAAI,KAAK,GAAG;GACV,KAAK,SAAS;GACd;EACF;EACA,KAAK,SAAS;GACZ,WAAW,YAAY;GACvB,UAAU,WAAW;GACrB,SAAS;GACT,SAAS;GACT,SAAS;EACX;EACA,KAAK;CACP;;;;;CAMA,YAAY,WAAmB,UAAwB;EACrD,MAAM,IAAI,KAAK;EACf,IAAI,KAAK,GAAG;GACV,KAAK,eAAe;GACpB;EACF;EACA,KAAK,eAAe;GAClB,WAAW,YAAY;GACvB,UAAU,WAAW;GACrB,SAAS;GACT,SAAS;GACT,SAAS;EACX;EACA,KAAK;CACP;;;;;CAMA,OAAO,IAAkB;EAEvB,IAAI,KAAK,SAAS;GAChB,MAAM,EAAE,QAAQ,YAAY,KAAK;GACjC,MAAM,YAAY,QAAQ,aAAa;GACvC,MAAM,UAAU,QAAQ,QAAQ,KAAK;GACrC,MAAM,UAAU,QAAQ,QAAQ,KAAK;GACrC,MAAM,QAAQ,OAAO,IAAI;GACzB,MAAM,QAAQ,OAAO,IAAI;GAEzB,IAAI,QAAQ,UAAU;IACpB,MAAM,KAAK,QAAQ;IACnB,MAAM,OAAO,OAAO,IAAI,KAAK;IAC7B,MAAM,OAAO,OAAO,IAAI,KAAK;IAK7B,IAAI,OAAO,GAAG,GACZ,KAAK,MAAM,OAAO,UAAU,GAAG,MAAM,IAAI,aAAa;SACjD,IAAI,OAAO,GAAG,IAAI,GAAG,OAC1B,KAAK,MAAM,OAAO,UAAU,GAAG,IAAI,GAAG,UAAU,IAAI,aAAa;IAEnE,IAAI,OAAO,GAAG,GACZ,KAAK,MAAM,OAAO,UAAU,GAAG,MAAM,IAAI,aAAa;SACjD,IAAI,OAAO,GAAG,IAAI,GAAG,QAC1B,KAAK,MAAM,OAAO,UAAU,GAAG,IAAI,GAAG,WAAW,IAAI,aAAa;GAEtE,OAAO;IAEL,MAAM,SAAS,IAAI,aAAa;IAChC,KAAK,MAAM,QAAQ,KAAK,KAAK;IAC7B,KAAK,MAAM,QAAQ,KAAK,KAAK;GAC/B;EACF;EAGA,IAAI,KAAK,QACP,KAAK,eAAe;EAItB,IAAI,KAAK,QAAQ;GACf,KAAK,OAAO,WAAW;GACvB,IAAI,KAAK,OAAO,WAAW,KAAK,OAAO,UACrC,KAAK,SAAS;QACT;IAEL,MAAM,QAAQ,IADG,KAAK,OAAO,UAAU,KAAK,OAAO;IAEnD,MAAM,OAAO,KAAK,aAAa,KAAK,OAAO,UAAU;IACrD,KAAK,OAAO,WAAW,aAAa,IAAI,IAAI,IAAI,KAAK,KAAK,OAAO,YAAY;IAC7E,KAAK,OAAO,WAAW,aAAa,OAAO,CAAC,IAAI,IAAI,KAAK,KAAK,OAAO,YAAY;GACnF;EACF;EAGA,IAAI,KAAK,cAAc;GACrB,KAAK,aAAa,WAAW;GAC7B,IAAI,KAAK,aAAa,WAAW,KAAK,aAAa,UACjD,KAAK,eAAe;QACf;IAEL,MAAM,QAAQ,IADG,KAAK,aAAa,UAAU,KAAK,aAAa;IAE/D,MAAM,IAAI,KAAK,aAAa,UAAU,MAAM,KAAK,mBAAmB;IACpE,KAAK,aAAa,UAAU,WAAW,GAAG,CAAC,IAAI,KAAK,aAAa,YAAY;IAC7E,KAAK,aAAa,UAAU,WAAW,GAAG,IAAI,GAAG,IAAI,KAAK,aAAa,YAAY;GACrF;EACF;CACF;;CAGA,IAAI,aAAqB;EACvB,OAAO,KAAK,KAAK,KAAK,QAAQ,WAAW,MAAM,KAAK,cAAc,WAAW;CAC/E;;CAGA,IAAI,aAAqB;EACvB,OAAO,KAAK,KAAK,KAAK,QAAQ,WAAW,MAAM,KAAK,cAAc,WAAW;CAC/E;;CAGA,cAAc,QAAgB,QAAsB;EAClD,OAAO,KAAK,kBAAkB,QAAQ,QAAQ;GAAE,GAAG;GAAG,GAAG;EAAE,CAAC;CAC9D;;CAGA,kBAAkB,QAAgB,QAAgB,KAAiB;EACjE,MAAM,MAAM,KAAK,IAAI,CAAC,KAAK,QAAQ;EACnC,MAAM,MAAM,KAAK,IAAI,CAAC,KAAK,QAAQ;EACnC,MAAM,MAAM,SAAS,KAAK,cAAc,KAAK;EAC7C,MAAM,MAAM,SAAS,KAAK,cAAc,KAAK;EAC7C,IAAI,IAAI,MAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB;EACnD,IAAI,IAAI,MAAM,KAAK,MAAM,KAAK,KAAK,iBAAiB;EACpD,OAAO;CACT;;CAGA,cAAc,SAAiB,SAAuB;EACpD,OAAO,KAAK,kBAAkB,SAAS,SAAS;GAAE,GAAG;GAAG,GAAG;EAAE,CAAC;CAChE;;CAGA,kBAAkB,SAAiB,SAAiB,KAAiB;EACnE,MAAM,KAAK,UAAU,KAAK,gBAAgB;EAC1C,MAAM,KAAK,UAAU,KAAK,iBAAiB;EAC3C,MAAM,MAAM,KAAK,IAAI,KAAK,QAAQ;EAClC,MAAM,MAAM,KAAK,IAAI,KAAK,QAAQ;EAClC,MAAM,KAAK,MAAM,KAAK,MAAM;EAC5B,MAAM,KAAK,MAAM,KAAK,MAAM;EAC5B,IAAI,IAAI,KAAK,KAAK,OAAO,KAAK;EAC9B,IAAI,IAAI,KAAK,KAAK,OAAO,KAAK;EAC9B,OAAO;CACT;;CAGA,YAAkB;EAChB,OAAO,KAAK,cAAc;GAAE,GAAG;GAAG,GAAG;GAAG,OAAO;GAAG,QAAQ;EAAE,CAAC;CAC/D;;CAGA,cAAc,KAAiB;EAC7B,MAAM,QAAQ,KAAK,iBAAiB,IAAI,KAAK;EAC7C,MAAM,QAAQ,KAAK,kBAAkB,IAAI,KAAK;EAC9C,IAAI,IAAI,KAAK,aAAa;EAC1B,IAAI,IAAI,KAAK,aAAa;EAC1B,IAAI,QAAQ,QAAQ;EACpB,IAAI,SAAS,QAAQ;EACrB,OAAO;CACT;CAEA,iBAA+B;EAC7B,MAAM,IAAI,KAAK;EACf,MAAM,QAAQ,KAAK,iBAAiB,IAAI,KAAK;EAC7C,MAAM,QAAQ,KAAK,kBAAkB,IAAI,KAAK;EAE9C,MAAM,OAAO,EAAE,IAAI;EACnB,MAAM,OAAO,EAAE,IAAI,EAAE,QAAQ;EAC7B,MAAM,OAAO,EAAE,IAAI;EACnB,MAAM,OAAO,EAAE,IAAI,EAAE,SAAS;EAE9B,IAAI,QAAQ,MACV,KAAK,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC,CAAC;OAG9C,KAAK,IAAI,EAAE,IAAI,EAAE,QAAQ;EAG3B,IAAI,QAAQ,MACV,KAAK,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC,CAAC;OAE9C,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS;CAE9B;AACF;;;;;;;;;;;;;;AC1VA,SAAgB,gBAAoC,OAAY,QAAsB;CACpF,MAAM,IAAI,UAAU,MAAM;CAE1B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;EAC1B,MAAM,OAAO,MAAM;EACnB,IAAI,IAAI,IAAI;EACZ,OAAO,KAAK,KAAK,MAAM,EAAE,CAAE,IAAI,KAAK,GAAG;GACrC,MAAM,IAAI,KAAK,MAAM;GACrB;EACF;EACA,MAAM,IAAI,KAAK;CACjB;CACA,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,GAAa,GAAqB;CACjE,OAAO,EAAE,IAAI,EAAE;AACjB;;;ACtCA,MAAa,YAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCpC,MAAa,WAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CnC,MAAa,aAAwB;;;;;;;;;;;;;;;;;;AA0BrC,MAAa,kBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6B1C,MAAa,qBAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B7C,MAAa,kBAA6B;;;;;;;;;;;;;;;;;;;;;;;;AAyB1C,MAAa,mBAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkC3C,MAAa,qBAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC7C,MAAa,sBAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1D9C,SAAgB,KAAK,OAAoB,QAA4B;CACnE,OAAO;EAAE,MAAM;EAAQ;EAAO;CAAO;AACvC;;AAGA,SAAgB,MAAM,OAAoB,aAAkC;CAC1E,OAAO;EAAE,MAAM;EAAS;EAAO,UAAU;EAAa,SAAS;CAAE;AACnE;;AAGA,SAAgB,UAAU,QAAiC;CACzD,OAAO;EAAE,MAAM;EAAa;CAAO;AACrC;;AAGA,SAAgB,QAAQ,OAAoB,OAA8B;CACxE,OAAO;EAAE,MAAM;EAAW;EAAO;CAAM;AACzC;;;;AAKA,SAAgB,OAAO,QAAwB,UAA8C;CAC3F,OAAO;EAAE,MAAM;EAAU;EAAQ;CAAS;AAC5C;;;AAIA,SAAgB,MACd,OACA,WACA,WACA,eACa;CACb,OAAO;EAAE,MAAM;EAAS;EAAO;EAAW;EAAW;CAAc;AACrE;;;AAIA,SAAgB,IAAI,OAAoB,UAAU,IAAK,QAAQ,IAAK,QAAQ,GAAgB;CAC1F,OAAO;EAAE,MAAM;EAAO;EAAO;EAAS;EAAO;CAAM;AACrD;;;AAIA,SAAgB,KAAK,MAKN;CACb,OAAO;EACL,MAAM;EACN,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,OAAO,KAAK,SAAS;EACrB,WAAW,KAAK,aAAa;CAC/B;AACF;;;AAIA,SAAgB,WAAW,MAKN;CACnB,OAAO;EACL,MAAM;EACN,OAAO,KAAK;EACZ,SAAS,KAAK,WAAW;EACzB,SAAS,KAAK,WAAW;EACzB,MAAM,KAAK,QAAQ;CACrB;AACF;;AAGA,SAAgB,UAAU,MAON;CAClB,OAAO;EACL,MAAM;EACN,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,eAAe,KAAK,iBAAiB;EACrC,SAAS,KAAK,WAAW;EACzB,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;AAGA,SAAgB,aAAa,MAON;CACrB,OAAO;EACL,MAAM;EACN,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,eAAe,KAAK,iBAAiB;EACrC,SAAS,KAAK,WAAW;EACzB,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;AAGA,SAAgB,UAAU,MAON;CAClB,OAAO;EACL,MAAM;EACN,YAAY,KAAK;EACjB,WAAW,KAAK;EAChB,eAAe,KAAK,iBAAiB;EACrC,WAAW,KAAK,aAAa;EAC7B,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;AAGA,SAAgB,WAAW,MAON;CACnB,OAAO;EACL,MAAM;EACN,WAAW,KAAK;EAChB,aAAa,KAAK;EAClB,eAAe,KAAK,iBAAiB;EACrC,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;AAGA,SAAgB,aAAa,MAON;CACrB,OAAO;EACL,MAAM;EACN,WAAW,KAAK;EAChB,WAAW,KAAK;EAChB,eAAe,KAAK,iBAAiB;EACrC,iBAAiB,KAAK,mBAAmB;EACzC,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;AAGA,SAAgB,cAAc,MAON;CACtB,OAAO;EACL,MAAM;EACN,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,eAAe,KAAK,iBAAiB;EACrC,YAAY,KAAK,cAAc;EAC/B,aAAa,KAAK,eAAe;EACjC,aAAa,KAAK,eAAe;CACnC;AACF;;;;;;;;;;;AAYA,SAAgB,aAAa,QAAyB,IAAoC;CACxF,IAAI,OAAO,SAAS,SAAS;EAC3B,MAAM,UAAU,OAAO,UAAU;EACjC,IAAI,WAAW,OAAO,UAAU,OAAO;EACvC,OAAO;GAAE,MAAM;GAAS,OAAO,OAAO;GAAO,UAAU,OAAO;GAAU;EAAQ;CAClF;CACA,OAAO;AACT;;;;AAKA,SAAgB,eAAe,QAA6B;CAC1D,IAAI,OAAO,YAAY,GAAG,OAAO;CACjC,MAAM,IAAI,OAAO,UAAU,OAAO;CAClC,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;AAC1B;;;;ACpWA,MAAM,IAAI;AA4MV,MAAM,iBAAiB;AACvB,MAAM,eAAe;AAErB,MAAM,kBAA4D;CAChE,CAAC,GAAG,CAAC;CACL,CAAC,IAAI,CAAC;CACN,CAAC,GAAG,CAAC;CACL,CAAC,GAAG,EAAE;CACN,CAAC,KAAK,SAAS,KAAK,OAAO;CAC3B,CAAC,KAAK,SAAS,CAAC,KAAK,OAAO;CAC5B,CAAC,CAAC,KAAK,SAAS,KAAK,OAAO;CAC5B,CAAC,CAAC,KAAK,SAAS,CAAC,KAAK,OAAO;AAC/B;AAEA,IAAI,uBAAuB;AAC3B,IAAI,qBAAqB;;;;;;;AAYzB,SAAgB,oBAAoB,MAA0C;CAC5E,MAAM,eAA0B,KAAK,MAAM;CAE3C,IAAI,YAA8B;CAClC,IAAI,aAA+B;CACnC,IAAI,iBAAmC;CACvC,IAAI,eAAiC;CACrC,IAAI,cAAgC;CACpC,IAAI,aAA+B;CACnC,IAAI,qBAA+C;CACnD,IAAI,WAA6B;CACjC,IAAI,mBAA6C;CACjD,IAAI,YAA8B;CAClC,IAAI,oBAA8C;CAClD,IAAI,kBAAoC;CAExC,IAAI,iBAAmC;CACvC,IAAI,kBAA4C;CAChD,IAAI,oBAAsC;CAC1C,IAAI,qBAA+C;CACnD,IAAI,iBAAmC;CACvC,IAAI,kBAA4C;CAChD,IAAI,kBAAoC;CACxC,IAAI,mBAA6C;CACjD,IAAI,oBAAsC;CAC1C,IAAI,qBAA+C;CACnD,IAAI,qBAAuC;CAC3C,IAAI,sBAAgD;CAEpD,MAAM,oBAA8B,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CACxD,MAAM,uBAAiC,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CAC3D,MAAM,oBAA8B,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CACxD,MAAM,qBAA+B,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CACzD,MAAM,uBAAiC,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CAC3D,MAAM,wBAAkC,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CAE5D,MAAM,WAAgC,KAAK,gBAAgB;CAC3D,MAAM,kBAA4B,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC;CAE1D,MAAM,WAAuB,CAAC;CAC9B,MAAM,SAAwB,CAAC;CAE/B,MAAM,kBAA8B,CAAC;CACrC,MAAM,kBAA8B,CAAC;CACrC,IAAI,eAAiC;CAErC,MAAM,cAA0B,CAAC;CAEjC,IAAI,kBAAqC;CACzC,IAAI,qBAAqB;CACzB,IAAI,oBAA0C;CAC9C,IAAI,uBAAuB;CAE3B,MAAM,6BAA2B,IAAI,aAAa,CAAC;CACnD,MAAM,4BAA0B,IAAI,aAAa,CAAC;CAClD,MAAM,6BAA2B,IAAI,aAAa,CAAC;CACnD,MAAM,+BAA6B,IAAI,aAAa,CAAC;CACrD,MAAM,kCAAgC,IAAI,aAAa,CAAC;CAExD,MAAM,aAAuB;EAAC;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;EAAG;CAAC;CACxF,MAAM,iBAA2B,CAAC;CAElC,MAAM,gBAA0B,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CACpD,MAAM,cAAwB,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;CACjD,MAAM,eAAyB,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC;CAEnD,IAAI,mBAAmB;CACvB,IAAI,iBAAiB;CACrB,IAAI,iBAAiB;CACrB,IAAI,UAAkC;CAEtC,IAAI,gBAAmD;CACvD,IAAI,cAA+C;CAEnD,MAAM,8BAAc,IAAI,IAAY;CACpC,SAAS,QAAQ,KAAmB;EAClC,IAAI,YAAY,IAAI,GAAG,GAAG;EAC1B,YAAY,IAAI,GAAG;EACnB,QAAQ,KAAK,YAAY,KAAK;CAChC;CAEA,MAAM,SAAqB;EACzB,YAAY,SAAS,KAAK,KAAK;EAC/B,eAAe;GACb,SAAS,QAAQ;EACnB;EACA,YAAY,GAAG,MAAM;GACnB,SAAS,UAAU,GAAG,CAAC;EACzB;EACA,QAAQ,IAAI,OAAO;GACjB,SAAS,MAAM,IAAI,EAAE;EACvB;EACA,SAAS,SAAS,IAAI,OAAO;GAC3B,SAAS,OAAO,SAAS,IAAI,EAAE;EACjC;EACA,QAAQ,SAAS;GACf,IAAI,YAAY,MAAM;GACtB,WAAW,MAAO,SAAS,KAAM,OAAQ;GACzC,WAAW,MAAO,SAAS,IAAK,OAAQ;GACxC,WAAW,MAAM,OAAO,OAAQ;GAChC,WAAW,MAAO,SAAS,KAAM,OAAQ;GACzC,QAAQ,MAAM,UAAU;EAC1B;CACF;CAEA,SAAS,mBAAmB,GAAiB;EAC3C,OAAO,SAAS,SAAS,GAAG;GAC1B,SAAS,KAAK,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC;GACvC,OAAO,KAAK,KAAK,QAAQ,GAAG,GAAG,GAAG,CAAC,CAAC;EACtC;CACF;CAEA,SAAS,0BAA0B,GAAiB;EAClD,OAAO,gBAAgB,SAAS,GAAG;GACjC,gBAAgB,KAAK,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC;GAC9C,gBAAgB,KAAK,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC;EAChD;CACF;CAEA,SAAS,yBAAyB,GAAiB;EACjD,OAAO,YAAY,SAAS,GAC1B,YAAY,KAAK,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC;CAE9C;CAEA,SAAS,kBAAkB,GAAuB;EAChD,IAAI,oBAAoB,QAAQ,uBAAuB,GAAG,OAAO;EACjE,kBAAkB,SAAS,WAAW,IAAI,WAAW,SAAS,MAAM,GAAG,CAAC;EACxE,qBAAqB;EACrB,OAAO;CACT;CAEA,SAAS,oBAAoB,GAA0B;EACrD,IAAI,sBAAsB,QAAQ,yBAAyB,GAAG,OAAO;EACrE,oBAAoB,OAAO,WAAW,IAAI,SAAS,OAAO,MAAM,GAAG,CAAC;EACpE,uBAAuB;EACvB,OAAO;CACT;CAEA,SAAS,aAAa,UAA+B,GAAiB;EACpE,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,MAAM,MAAM,SAAS;GACrB,SAAS,EAAE,EAAE,QAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI;EAC7D;CACF;CAEA,SAAS,WAAW,UAA+B,GAAW,IAAY,IAAkB;EAC1F,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,MAAM,MAAM,SAAS;GACrB,MAAM,QAAQ,IAAI,SAAS,IAAI,IAAI,IAAI,OAAO,IAAI;GAClD,IAAI,IAAI,aAAa,GAAG;IACtB,OAAO,EAAE,EAAE,IAAI,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI,OAAO,EAAE;IACrD,IAAI,EAAE,SAAS;GACjB,OAAO;IACL,MAAM,IAAI,KAAK,IAAI,IAAI,QAAQ;IAC/B,MAAM,IAAI,KAAK,IAAI,IAAI,QAAQ;IAC/B,MAAM,OAAO,QAAQ;IACrB,MAAM,OAAO,QAAQ;IACrB,MAAM,KAAK,IAAI,OAAO,IAAI,UAAU,IAAI,OAAO;IAC/C,MAAM,KAAK,IAAI,OAAO,IAAI,UAAU,IAAI,OAAO;IAC/C,MAAM,QAAQ,IAAI,UAAU,IAAI;IAChC,MAAM,QAAQ,IAAI,UAAU,IAAI;IAChC,OAAO,EAAE,EAAE,IACT,MACA,MACA,KAAK,OAAO,QAAQ,OAAO,OAC3B,KAAK,OAAO,QAAQ,OAAO,KAC7B;IACA,IAAI,EAAE,SAAS;GACjB;EACF;CACF;CAEA,SAAS,UAAU,QAA+B;EAChD,IAAI,cAAc,MAAM,YAAY,KAAK,MAAM;EAC/C,WAAW,WAAW,OAAO,OAAO,OAAO,MAAM;EACjD,UAAU,eAAe,KAAK,YAAY,UAAU,WAAW,cAAc,CAAC;EAC9E,OAAO;CACT;CAEA,SAAS,WAAW,QAAgC;EAClD,IAAI,eAAe,MAAM,aAAa,KAAK,MAAM;EACjD,WAAW,YAAY,OAAO,OAAO,eAAe,MAAM,CAAC;EAC3D,WAAW,eAAe,KAAK,YAAY,UAAU,YAAY,cAAc,CAAC;EAChF,OAAO;CACT;CAEA,SAAS,eAAe,QAAoC;EAC1D,IAAI,mBAAmB,MAAM,iBAAiB,KAAK,MAAM;EACzD,qBAAqB,YAAY,OAAO,MAAM;EAC9C,eAAe,eAAe,KAAK,YAAY,WAAW,UAAU,CAAC;EACrE,OAAO;CACT;CAEA,SAAS,kBAAkB,QAAkC;EAC3D,IAAI,iBAAiB,MAAM,eAAe,KAAK,MAAM;EACrD,WAAW,cAAc,OAAO,OAAO,CAAC;EACxC,aAAa,eAAe,KAAK,YAAY,UAAU,cAAc,YAAY,CAAC;EAClF,OAAO;CACT;CAEA,SAAS,YAAY,QAAuC;EAC1D,IAAI,gBAAgB,MAAM,cAAc,KAAK,MAAM;EACnD,MAAM,SAAS,OAAO;EACtB,MAAM,aAAa,OAAO,qBAAqB;EAC/C,OAAO,eAAe,SAAS,YAAY,eAAe,KAAK,CAAC;EAChE,eAAe,SAAS;EACxB,mBAAmB,QAAQ,QAAQ,cAAc;EACjD,YAAY,UAAU,OAAO,WAAW,cAAc,CAAC;EACvD,OAAO;CACT;CAEA,SAAS,WAAW,QAAqB,MAAyB;EAChE,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,uBAAuB,MAAM;GAC/B,MAAM,WAAW,KAAK,cAAc,KAAK,UAAU;GACnD,IAAI,aAAa,MAAM,OAAO;GAC9B,qBAAqB;EACvB;EACA,IAAI,eAAe,MAAM,aAAa,KAAK,MAAM;EACjD,cAAc,KAAK;EACnB,cAAc,KAAK;EACnB,cAAc,KAAK,OAAO,MAAM;EAChC,cAAc,KAAK,OAAO,MAAM;EAChC,cAAc,KAAK,OAAO,MAAM;EAChC,cAAc,KAAK,OAAO,UAAU;EACpC,cAAc,KAAK,OAAO,UAAU;EACpC,cAAc,KAAK,OAAO,UAAU;EACpC,cAAc,KAAK,OAAO;EAC1B,cAAc,KAAK,OAAO;EAC1B,cAAc,MAAM;EACpB,WAAW,UAAU,mBAAmB,WAAW,aAAa,CAAC;EACjE,OAAO;CACT;CAEA,SAAS,SAAS,QAAmB,MAAyB;EAC5D,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,qBAAqB,MAAM;GAC7B,MAAM,WAAW,KAAK,cAAc,KAAK,QAAQ;GACjD,IAAI,aAAa,MAAM,OAAO;GAC9B,mBAAmB;EACrB;EACA,IAAI,aAAa,MAAM,WAAW,KAAK,MAAM;EAC7C,YAAY,KAAK;EACjB,YAAY,KAAK;EACjB,YAAY,KAAK,OAAO,MAAM;EAC9B,YAAY,KAAK,OAAO,MAAM;EAC9B,YAAY,KAAK,OAAO,MAAM;EAC9B,YAAY,KAAK,OAAO;EACxB,YAAY,KAAK,OAAO;EACxB,YAAY,KAAK,OAAO;EACxB,YAAY,KAAK;EACjB,SAAS,UAAU,iBAAiB,WAAW,WAAW,CAAC;EAC3D,OAAO;CACT;CAEA,SAAS,UAAU,QAAoB,MAAyB;EAC9D,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,sBAAsB,MAAM;GAC9B,MAAM,WAAW,KAAK,cAAc,KAAK,SAAS;GAClD,IAAI,aAAa,MAAM,OAAO;GAC9B,oBAAoB;EACtB;EACA,IAAI,cAAc,MAAM,YAAY,KAAK,MAAM;EAC/C,aAAa,KAAK;EAClB,aAAa,KAAK;EAClB,aAAa,KAAK,OAAO,MAAM;EAC/B,aAAa,KAAK,OAAO,MAAM;EAC/B,aAAa,KAAK,OAAO,MAAM;EAC/B,aAAa,KAAK,OAAO,SAAS;EAClC,aAAa,KAAK,OAAO,SAAS;EAClC,aAAa,KAAK,OAAO,SAAS;EAClC,aAAa,KAAK,OAAO;EACzB,aAAa,KAAK,OAAO;EACzB,aAAa,MAAM;EACnB,UAAU,UAAU,kBAAkB,WAAW,YAAY,CAAC;EAC9D,OAAO;CACT;CAEA,SAAS,gBAAgB,QAAqC;EAC5D,IAAI,KAAK,gBAAgB,KAAA,GAAW,OAAO;EAC3C,IAAI,oBAAoB,MAAM,kBAAkB,KAAK,MAAM;EAC3D,WAAW,iBAAiB,OAAO,OAAO,CAAC;EAC3C,gBAAgB,eACd,KAAK,YAAY,eACf,OAAO,SACP,OAAO,SACP,OAAO,MACP,OAAO,MACP,iBACA,IACF,CACF;EACA,OAAO;CACT;CAEA,SAAS,eAAe,QAAoC;EAC1D,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,oBAAoB,MAAM;GAC5B,MAAM,WAAW,KAAK,cAAc,KAAK,eAAe;GACxD,IAAI,aAAa,MAAM,OAAO;GAC9B,kBAAkB;EACpB;EACA,IAAI,mBAAmB,MAAM,iBAAiB,KAAK,MAAM;EACzD,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO,SAAS;EACvC,kBAAkB,KAAK,OAAO,SAAS;EACvC,kBAAkB,KAAK,OAAO,SAAS;EACvC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO;EAC9B,eAAe,UAAU,gBAAgB,WAAW,iBAAiB,CAAC;EACtE,OAAO;CACT;CAEA,SAAS,kBAAkB,QAAuC;EAChE,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,uBAAuB,MAAM;GAC/B,MAAM,WAAW,KAAK,cAAc,KAAK,kBAAkB;GAC3D,IAAI,aAAa,MAAM,OAAO;GAC9B,qBAAqB;EACvB;EACA,IAAI,sBAAsB,MAAM,oBAAoB,KAAK,MAAM;EAC/D,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO,SAAS;EAC1C,qBAAqB,KAAK,OAAO,SAAS;EAC1C,qBAAqB,KAAK,OAAO,SAAS;EAC1C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO;EACjC,kBAAkB,UAAU,mBAAmB,WAAW,oBAAoB,CAAC;EAC/E,OAAO;CACT;CAEA,SAAS,eAAe,QAAoC;EAC1D,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,oBAAoB,MAAM;GAC5B,MAAM,WAAW,KAAK,cAAc,KAAK,eAAe;GACxD,IAAI,aAAa,MAAM,OAAO;GAC9B,kBAAkB;EACpB;EACA,IAAI,mBAAmB,MAAM,iBAAiB,KAAK,MAAM;EACzD,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO,WAAW;EACzC,kBAAkB,KAAK,OAAO,WAAW;EACzC,kBAAkB,KAAK,OAAO,WAAW;EACzC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO,UAAU;EACxC,kBAAkB,KAAK,OAAO;EAC9B,kBAAkB,KAAK,OAAO;EAC9B,eAAe,UAAU,gBAAgB,WAAW,iBAAiB,CAAC;EACtE,OAAO;CACT;CAEA,SAAS,gBAAgB,QAAqC;EAC5D,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,qBAAqB,MAAM;GAC7B,MAAM,WAAW,KAAK,cAAc,KAAK,gBAAgB;GACzD,IAAI,aAAa,MAAM,OAAO;GAC9B,mBAAmB;EACrB;EACA,IAAI,oBAAoB,MAAM,kBAAkB,KAAK,MAAM;EAC3D,mBAAmB,KAAK,OAAO;EAC/B,mBAAmB,KAAK,OAAO;EAC/B,mBAAmB,KAAK,OAAO,UAAU;EACzC,mBAAmB,KAAK,OAAO,UAAU;EACzC,mBAAmB,KAAK,OAAO,UAAU;EACzC,mBAAmB,KAAK,OAAO,YAAY;EAC3C,mBAAmB,KAAK,OAAO,YAAY;EAC3C,mBAAmB,KAAK,OAAO,YAAY;EAC3C,mBAAmB,KAAK,OAAO;EAC/B,mBAAmB,KAAK,OAAO;EAC/B,gBAAgB,UAAU,iBAAiB,WAAW,kBAAkB,CAAC;EACzE,OAAO;CACT;CAEA,SAAS,kBAAkB,QAAuC;EAChE,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,uBAAuB,MAAM;GAC/B,MAAM,WAAW,KAAK,cAAc,KAAK,kBAAkB;GAC3D,IAAI,aAAa,MAAM,OAAO;GAC9B,qBAAqB;EACvB;EACA,IAAI,sBAAsB,MAAM,oBAAoB,KAAK,MAAM;EAC/D,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO,UAAU;EAC3C,qBAAqB,KAAK,OAAO;EACjC,qBAAqB,KAAK,OAAO;EACjC,kBAAkB,UAAU,mBAAmB,WAAW,oBAAoB,CAAC;EAC/E,OAAO;CACT;CAEA,SAAS,mBAAmB,QAAwC;EAClE,IAAI,KAAK,kBAAkB,KAAA,GAAW,OAAO;EAC7C,IAAI,wBAAwB,MAAM;GAChC,MAAM,WAAW,KAAK,cAAc,KAAK,mBAAmB;GAC5D,IAAI,aAAa,MAAM,OAAO;GAC9B,sBAAsB;EACxB;EACA,IAAI,uBAAuB,MAAM,qBAAqB,KAAK,MAAM;EACjE,sBAAsB,KAAK,OAAO;EAClC,sBAAsB,KAAK,OAAO;EAClC,sBAAsB,KAAK,OAAO,SAAS;EAC3C,sBAAsB,KAAK,OAAO,SAAS;EAC3C,sBAAsB,KAAK,OAAO,SAAS;EAC3C,sBAAsB,KAAK,OAAO,UAAU;EAC5C,sBAAsB,KAAK,OAAO,UAAU;EAC5C,sBAAsB,KAAK,OAAO,UAAU;EAC5C,sBAAsB,KAAK,OAAO;EAClC,sBAAsB,KAAK,OAAO;EAClC,mBAAmB,UAAU,oBAAoB,WAAW,qBAAqB,CAAC;EAClF,OAAO;CACT;CAEA,SAAS,aACP,KACA,OACA,UACA,GACA,QACA,OACM;EACN,MAAM,QAAQ,kBAAkB,MAAM;EACtC,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,kBAAkB,CAAC;EAChC,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;GAC/C,MAAM,SAAS,gBAAgB;GAC/B,WAAW,UAAU,GAAG,OAAO,KAAK,OAAO,OAAO,OAAO,KAAK,OAAO,KAAK;GAC1E,MAAM,OAAO,oBAAoB,CAAC;GAClC,IAAI,UAAU,OAAO,MAAM,MAAM,KAAK;EACxC;EACA,MAAM,UAAU,CAAC;EACjB,aAAa,UAAU,KAAK;EAC5B,WAAW,UAAU,GAAG,GAAG,CAAC;EAC5B,MAAM,WAAW,oBAAoB,CAAC;EACtC,IAAI,UAAU,OAAO,MAAM,UAAU,YAAY;EACjD,aAAa,UAAU,CAAC;CAC1B;CAEA,SAAS,mBAAmB,QAAoC;EAC9D,QAAQ,OAAO,MAAf;GACE,KAAK,QACH,OAAO,UAAU,MAAM;GACzB,KAAK,SACH,OAAO,WAAW,MAAM;GAC1B,KAAK,aACH,OAAO,eAAe,MAAM;GAC9B,KAAK,UACH,OAAO,YAAY,MAAM;GAC3B,KAAK,SACH,OAAO,WAAW,QAAQ,gBAAgB;GAC5C,KAAK,OACH,OAAO,SAAS,QAAQ,gBAAgB;GAC1C,KAAK,QACH,OAAO,UAAU,QAAQ,gBAAgB;GAC3C,KAAK,eACH,OAAO,gBAAgB,MAAM;GAC/B,KAAK,WACH,OAAO,kBAAkB,MAAM;GACjC,KAAK,cACH,OAAO,eAAe,MAAM;GAC9B,KAAK,iBACH,OAAO,kBAAkB,MAAM;GACjC,KAAK,cACH,OAAO,eAAe,MAAM;GAC9B,KAAK,eACH,OAAO,gBAAgB,MAAM;GAC/B,KAAK,iBACH,OAAO,kBAAkB,MAAM;GACjC,KAAK,kBACH,OAAO,mBAAmB,MAAM;EACpC;CACF;CAEA,OAAO;EACL;EAEA,WAAW,eAAe,gBAAgB,OAAO,GAAG,eAAe,GAAS;GAC1E,IAAI,kBAAkB,KAAK,mBAAmB,GAAG;GACjD,gBAAgB;GAChB,cAAc;GACd,mBAAmB;GACnB,MAAM,KAAK;GACX,gBAAgB,QAAQ,GAAG,GAAG,gBAAgB,IAAI,iBAAiB,EAAE;GACrE,UAAU,SAAS,eAAe,eAAe;GACjD,IAAI,OAAO,GAAG,QAAQ,MAAM,IAAI,EAAE;EACpC;EAEA,UAAU,OAAO,UAAgB;GAC/B,IAAI,YAAY,MAAM;GACtB,MAAM,IAAI,SAAS;GACnB,IAAI,MAAM,GAAG;GAEb,MAAM,SAAS,SAAS,EAAE,EAAE;GAC5B,MAAM,QAAQ,SAAS,EAAE,EAAE,SAAS;GACpC,iBAAiB,SAAS,EAAE,EAAE,QAAQ;GACtC,iBAAiB,SAAS,EAAE,EAAE,QAAQ;GAEtC,IACE,QAAQ,SAAS,gBACjB,QAAQ,SAAS,mBACjB,QAAQ,SAAS,gBACjB,QAAQ,SAAS,iBACjB,QAAQ,SAAS,mBACjB,QAAQ,SAAS,kBACjB;IACA,MAAM,QAAQ,mBAAmB,MAAM;IACvC,MAAM,UAAU,KAAK;IACrB,yBAAyB,CAAC;IAC1B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;KAC1B,MAAM,MAAM,SAAS;KACrB,YAAY,EAAE,CAAE,QAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI;KAC9D,QAAQ,SAAS,YAAY,IAAK,KAAK;IACzC;IACA,MAAM,UAAU,CAAC;IACjB;GACF;GAEA,IAAI,SAAS,EAAE,EAAE,SAAS;IACxB,0BAA0B,CAAC;IAC3B,IAAI,iBAAiB,MAAM,eAAe,KAAK,MAAM;IACrD,MAAM,KAAK;IAIX,MAAM,SACJ,WAAW,KAAA,KAAa,OAAO,SAAS,YAAY,KAAK,mBAAmB,MAAM;IACpF,OAAO,UAAU,KAAK;IACtB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;KAC1B,MAAM,MAAM,SAAS;KACrB,MAAM,OAAO,gBAAgB;KAC7B,MAAM,OAAO,gBAAgB;KAC7B,KAAK,QAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI;KACnD,KAAK,QAAQ,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI;KACnD,QAAQ,cAAc,OAAO,MAAM,MAAM,MAAM;IACjD;IACA,OAAO,UAAU,CAAC;IAClB;GACF;GAEA,mBAAmB,CAAC;GACpB,aAAa,UAAU,CAAC;GAExB,IAAI,QAAQ,SAAS,WAAW;IAC9B,aAAa,SAAS,OAAO,UAAU,GAAG,QAAQ,KAAK;IACvD;GACF;GAEA,WAAW,UAAU,GAAG,GAAG,CAAC;GAC5B,MAAM,OAAO,kBAAkB,CAAC;GAChC,MAAM,OAAO,oBAAoB,CAAC;GAClC,MAAM,QAAQ,WAAW,KAAA,IAAY,eAAe,mBAAmB,MAAM;GAC7E,MAAM,UAAU,KAAK;GACrB,QAAQ,UAAU,OAAO,MAAM,MAAM,KAAK;GAC1C,MAAM,UAAU,CAAC;EACnB;EAEA,iBAAiB,SAAS,OAAa;GACrC,gBAAgB;GAChB,cAAc;EAChB;EAEA,mBAA4B;GAC1B,OACE,kBAAkB,QAClB,CAAC,CAAC,cAAc,aAChB,cAAc,cAAc,KAAA,KAC5B,gBAAgB;EAEpB;EAEA,kBAAkB,SAAS,OAAO,OAAO,QAAkD;GACzF,MAAM,KAAK,WAAW;GACtB,MAAM,KAAK,SAAS;GACpB,IAAI,OAAO,QAAQ,CAAC,GAAG,WAAW,OAAO,CAAC;GAC1C,IAAI,GAAG,cAAc,KAAA,GAAW;IAC9B,QAAQ,uEAAuE;IAC/E,OAAO,CAAC;GACV;GACA,IAAI,OAAO,MAAM,OAAO,CAAC;GACzB,MAAM,SAAS,GAAG,UAAU,IAAI,OAAO,MAAM;GAC7C,IAAI,OAAO,WAAW,GAAG,OAAO,CAAC;GACjC,MAAM,MAAuC,CAAC;GAC9C,KAAK,MAAM,QAAQ,QAAQ;IACzB,MAAM,SAAS,KAAK;IACpB,MAAM,eACJ,OAAO,OAAO,oBAAoB,aAAa,OAAO,gBAAgB,IAAI;IAC5E,MAAM,WAAuD,CAAC;IAC9D,KAAK,IAAI,IAAI,GAAG,IAAI,cAAc,KAAK;KACrC,MAAM,OACJ,OAAO,OAAO,mBAAmB,aAAa,OAAO,eAAe,CAAC,IAAI,OAAO,CAAC;KACnF,MAAM,OACJ,OAAO,OAAO,eAAe,aACzB,OAAO,WAAW,CAAC,IACnB;MAAE,SAAS;MAAG,MAAM;MAAG,MAAM;KAAE;KACrC,MAAM,OAAO,KAAK,UAAU,KAAK;KACjC,IAAI,SAAS,GACX,SAAS,QAAQ,KAAK,SAAS,KAAK,SAAS;UACxC;MACL,MAAM,MAAgB,CAAC;MACvB,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KACxB,IAAI,KAAK,KAAK,SAAS,KAAK,OAAO,MAAM,CAAC;MAE5C,SAAS,QAAQ;KACnB;IACF;IACA,IAAI,KAAK;KACP,IAAI,KAAK;KACT,QAAQ;KACR;KACA,UAAU,KAAK;IACjB,CAAC;GACH;GACA,OAAO;EACT;EAEA,WAA+B;GAC7B,IAAI,YAAY,MAAM,OAAO;GAC7B,UAAU;GACV,OAAO,SAAS,yBAAyB;EAC3C;CACF;AACF;;AAOA,SAAgB,qBAA2B;CACzC,uBAAuB;CACvB,qBAAqB;AACvB;;;;;;;;;;;;;AAcA,SAAgB,mBAA2D;CACzE,OAAO;EAAE,SAAS;EAAoB,WAAW;CAAqB;AACxE;AAMA,SAAS,WAAW,KAAmB,OAAoB,UAAwB;CACjF,IAAI,KAAK,MAAM;CACf,IAAI,KAAK,MAAM;CACf,IAAI,KAAK,MAAM;CACf,IAAI,KAAK,MAAM,IAAI;AACrB;AAEA,SAAS,qBAAqB,KAAe,QAAsB;CACjE,MAAM,MAAM,IAAI;CAChB,MAAM,KAAK,OAAQ;CACnB,MAAM,KAAK,OAAQ;CACnB,MAAM,KAAK,OAAQ;CACnB,IAAI,KAAK,MAAM;CACf,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,KAAK,MAAM;CACf,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM,MAAM;CAChB,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM;CACV,IAAI,MAAM;AACZ;AAEA,SAAS,mBACP,QACA,QACA,KACM;CACN,MAAM,QAAQ,OAAO,gBAAgB;CACrC,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,IAAI,KAAK;CAC9C,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,OAAO,OAAO,WAAW,CAAC;EAChC,MAAM,OAAO,OAAO,eAAe,CAAC;EACpC,MAAM,QAAQ,OAAO,SAAS;EAC9B,MAAM,OAAO,KAAK,UAAU,KAAK;EACjC,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,OAAO,UAAU,UAAU;GAC7B,IAAI,KAAK,QAAQ;GACjB;EACF;EACA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KACxB,IAAI,KAAK,OAAO,KAAK,MAAM,MAAM;CAErC;AACF;;;;;;;;;;;;;;;;;;;;;ACx6BA,SAAgB,mBACd,OACA,OACA,aACA,WACA,OACY;CAEZ,MAAM,UAAyB,CAAC;CAEhC,aAAa;EAEX,IAAI,IAAI;EACR,KAAK,MAAM,YAAY,OAAO;GAC5B,MAAM,SAAS,MAAM,IAAI,UAAU,SAAS;GAC5C,IAAI,CAAC,QAAQ;GAEb,IAAI,KAAK,QAAQ,QACf,QAAQ,KAAK;IAAE;IAAU,GAAG;GAAE,CAAC;GAEjC,MAAM,QAAQ,QAAQ;GACtB,MAAM,WAAW;GACjB,MAAM,IAAI,OAAO;GACjB;EACF;EAGA,MAAM,QAAQ;EACd,IAAI,QAAQ,SAAS,QAAQ,KAAK,QAAQ,SAAS,IACjD,QAAQ,SAAS;EAGnB,gBAAgB,SAAS,KAAK;EAG9B,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;GAC9B,MAAM,QAAQ,QAAQ;GACtB,MAAM,MAAM,MAAM,IAAI,MAAM,UAAU,WAAW;GACjD,MAAM,SAAS,MAAM,IAAI,MAAM,UAAU,SAAS;GAClD,IAAI,CAAC,OAAO,CAAC,QAAQ;GAErB,MAAM,SAAS,OAAO;GACtB,MAAM,KACJ,OAAO,OACP,OAAO,OACP,IAAI,GACJ,IAAI,GACJ,OAAO,OACP,OAAO,QACP,OAAO,QACP,SAAS,OAAO,IAAI,GACpB,SAAS,OAAO,IAAI,GACpB,OAAO,SAAS,GAChB,OAAO,YAAY,GACnB,OAAO,WAAW,KACpB;EACF;CACF;AACF;;AAGA,MAAa,UAAA,GAAA,kBAAA,gBAAA,CAAqC,iBAAiB;CACjE,OAAO;CACP,OAAO;EAAE,MAAM;EAAI,GAAG;EAAG,GAAG;EAAG,GAAG;EAAG,GAAG;CAAE;CAC1C,GAAG;AACL,EAAE;;;;;;;AC5GF,IAAa,WAAb,MAAsB;;CAEpB,QAA8B;EAAE,WAAW;EAAG,YAAY;CAAE;CAE5D,UAAqC;CACrC,WAAmB;;CAGnB,UAAU,QAA0B;EAClC,KAAK,UAAU;CACjB;;CAGA,IAAI,SAAqB;EACvB,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,MAAM,yBAAyB;EAE3C,OAAO,KAAK;CACd;;CAGA,IAAI,UAAmB;EACrB,OAAO,KAAK;CACd;;;;;CAMA,MAAM,aAAa,YAAkB;EACnC,IAAI,KAAK,UACP,MAAM,IAAI,MAAM,iDAAiD;EAEnE,KAAK,WAAW;EAChB,KAAK,MAAM,YAAY;EACvB,KAAK,MAAM,aAAa;EACxB,KAAK,OAAO,KAAK;EACjB,KAAK,OAAO,MAAM,UAAU;CAC9B;;;;CAKA,MAAY;EACV,IAAI,CAAC,KAAK,UACR,MAAM,IAAI,MAAM,wCAAwC;EAE1D,KAAK,OAAO,QAAQ;EACpB,KAAK,WAAW;CAClB;;;;;CAMA,YACE,SACA,SACA,MACA,UACA,eACA,gBACM;EACN,MAAM,IAAI,KAAK;EAEf,EAAE,UAAU,gBAAgB,GAAG,iBAAiB,CAAC;EAEjD,EAAE,MAAM,MAAM,IAAI;EAGlB,IAAI,aAAa,GACf,EAAE,OAAQ,CAAC,WAAW,MAAO,KAAK,IAAI,GAAG,CAAC;EAG5C,EAAE,UAAU,CAAC,SAAS,CAAC,OAAO;CAChC;;;CAIA,iBAAuB;EACrB,KAAK,MAAM;CACb;;;CAIA,cAAoB;EAClB,KAAK,MAAM;CACb;AACF;;;;;;;;;;;;;;;;;;ACpDA,IAAa,cAAb,MAAyB;CACvB,YAAyC,CAAC;CAC1C,QAAqC,CAAC;CACtC,WAA4B,CAAC;CAC7B,cAAsB;;CAGtB,IAAI,QAAgB;EAClB,OAAO,KAAK,UAAU;CACxB;;;CAIA,IAAI,aAAqB;EACvB,OAAO,KAAK;CACd;;;;;;;;;CAUA,KACE,OACA,OACA,MACA,MACA,MACA,MACA,QACA,UAAU,GACV,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,OACJ;EACN,MAAM,MAAM,KAAK,SAAS;EAC1B,MAAM,IAAI,QAAQ,MAAM;EACxB,MAAM,IAAI,QAAQ,MAAM;EACxB,IAAI,QAAQ;EACZ,IAAI,OAAO,MAAM;EACjB,IAAI,OAAO,MAAM;EACjB,IAAI,OAAO,MAAM;EACjB,IAAI,OAAO,MAAM;EACjB,IAAI,OAAO,OAAO,UAAU;EAC5B,IAAI,OAAO,OAAO,UAAU;EAC5B,IAAI,OAAO;EACX,IAAI,OAAO;EACX,IAAI,SAAS;EACb,IAAI,QAAQ;EACZ,IAAI,WAAW;EACf,IAAI,UAAU;EACd,IAAI,UAAU;EACd,IAAI,UAAU;EACd,KAAK,UAAU,KAAK,GAAG;CACzB;;;;;CAMA,QACE,OACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,QACA,UAAU,GACV,UAAU,GACV,QAAQ,GACR,WAAW,GACX,UAAU,OACJ;EACN,MAAM,MAAM,KAAK,SAAS;EAC1B,IAAI,QAAQ;EACZ,IAAI,OAAO;EACX,IAAI,OAAO;EACX,IAAI,OAAO;EACX,IAAI,OAAO;EACX,IAAI,OAAO,OAAO,UAAU;EAC5B,IAAI,OAAO,OAAO,UAAU;EAC5B,IAAI,OAAO;EACX,IAAI,OAAO;EACX,IAAI,SAAS;EACb,IAAI,QAAQ;EACZ,IAAI,WAAW;EACf,IAAI,UAAU;EACd,IAAI,UAAU;EACd,IAAI,UAAU;EACd,KAAK,UAAU,KAAK,GAAG;CACzB;;;;;;;;;;;;;CAcA,MACE,WACA,UACM;EACN,IAAI,KAAK,UAAU,WAAW,GAAG;EAGjC,IAAI,MAAM,KAAK,SAAS;EACxB,IAAI,QAAQ,KAAA,GAAW;GACrB,MAAM;IAAE,OAAO;IAAO,QAAQ,KAAA;IAAW,OAAO;IAAG,SAAS;IAAO,UAAU,CAAC;GAAE;GAChF,KAAK,SAAS,KAAK;EACrB;EAIA,KAAK,MAAM,OAAO,KAAK,WAAW;GAChC,IACE,IAAI,SAAS,SAAS,MACrB,IAAI,UAAU,IAAI,SACjB,IAAI,WAAW,IAAI,UACnB,IAAI,UAAU,IAAI,SAClB,IAAI,YAAY,IAAI,UACtB;IACA,UAAU,IAAI,OAAO,IAAI,QAAQ;IACjC,IAAI,UAAU;KACZ,SAAS,eAAe;KACxB,SAAS,YAAY;IACvB;IACA,IAAI,SAAS,SAAS;GACxB;GACA,IAAI,QAAQ,IAAI;GAChB,IAAI,SAAS,IAAI;GACjB,IAAI,QAAQ,IAAI;GAChB,IAAI,UAAU,IAAI;GAClB,IAAI,SAAS,KAAK,GAAG;EACvB;EAGA,IAAI,IAAI,SAAS,SAAS,GAAG;GAC3B,UAAU,IAAI,OAAO,IAAI,QAAQ;GACjC,IAAI,UAAU;IACZ,SAAS,eAAe;IACxB,SAAS,YAAY;GACvB;GACA,IAAI,SAAS,SAAS;EACxB;EAKA,KAAK,MAAM,OAAO,KAAK,WACrB,KAAK,MAAM,KAAK,GAAG;EAErB,KAAK,UAAU,SAAS;CAC1B;;CAGA,QAAc;EACZ,KAAK,MAAM,OAAO,KAAK,WACrB,KAAK,MAAM,KAAK,GAAG;EAErB,KAAK,UAAU,SAAS;CAC1B;CAEA,WAAsC;EACpC,IAAI,KAAK,MAAM,SAAS,GACtB,OAAO,KAAK,MAAM,IAAI;EAExB,KAAK;EACL,OAAO;GACL,OAAO;GACP,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACN,QAAQ,KAAA;GACR,OAAO;GACP,UAAU;GACV,SAAS;GACT,SAAS;GACT,SAAS;EACX;CACF;AACF;;;;;;ACvOA,SAAgB,gBAAgB,MAA2B;CACzD,MAAM,OAAO;CAeb,MAAM,yBAAS,IAAI,IAAmB;CACtC,KAAK,MAAM,KAAK,KAAK,OACnB,OAAO,IAAI,EAAE,IAAI;EACf,IAAI,EAAE;EACN,GAAG,EAAE;EACL,GAAG,EAAE;EACL,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,SAAS,EAAE;EACX,SAAS,EAAE;EACX,UAAU,EAAE;CACd,CAAC;CAGH,MAAM,2BAAW,IAAI,IAAoB;CACzC,IAAI,KAAK,UACP,KAAK,MAAM,KAAK,KAAK,UACnB,SAAS,IAAK,EAAE,SAAS,KAAM,EAAE,QAAQ,EAAE,MAAM;CAIrD,OAAO;EACL,YAAY,KAAK,OAAO;EACxB,MAAM,KAAK,OAAO;EAClB;EACA;CACF;AACF;;AAGA,SAAgB,WAAW,MAAkB,OAAe,QAAwB;CAClF,OAAO,KAAK,SAAS,IAAK,SAAS,KAAM,MAAM,KAAK;AACtD;;AAGA,SAAgB,YAAY,MAAkB,MAAsB;CAClE,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK,WAAW,CAAC;EAC9B,MAAM,QAAQ,KAAK,OAAO,IAAI,IAAI;EAClC,IAAI,CAAC,OAAO;EACZ,IAAI,YAAY,GACd,SAAS,WAAW,MAAM,UAAU,IAAI;EAE1C,SAAS,MAAM;EACf,WAAW;CACb;CACA,OAAO;AACT;;;;;AAMA,SAAgB,SACd,OACA,MACA,OACA,MACA,GACA,GACA,QAAQ,GACF;CACN,IAAI,UAAU;CACd,IAAI,WAAW;CAEf,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,OAAO,KAAK,WAAW,CAAC;EAC9B,MAAM,QAAQ,KAAK,OAAO,IAAI,IAAI;EAClC,IAAI,CAAC,OAAO;EAEZ,IAAI,YAAY,GACd,WAAW,WAAW,MAAM,UAAU,IAAI,IAAI;EAGhD,MAAM,QACJ,OACA,MAAM,GACN,MAAM,GACN,MAAM,OACN,MAAM,QACN,UAAU,MAAM,UAAU,OAC1B,IAAI,MAAM,UAAU,OACpB,MAAM,QAAQ,OACd,MAAM,SAAS,KACjB;EAEA,WAAW,MAAM,WAAW;EAC5B,WAAW;CACb;AACF"}