@flare-engine/render 0.1.0 → 0.2.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/README.md CHANGED
@@ -25,7 +25,7 @@ batch.flush(skia.drawAtlas, renderer);
25
25
  renderer.end();
26
26
  ```
27
27
 
28
- See [API spec](../../docs/design/api-specs.md#flare-enginerender--10kb-gzip) and [skill reference](../../.github/skills/flare-render/SKILL.md).
28
+ See [API spec](../../docs/design/api-specs.md#flare-enginerender--10kb-gzip).
29
29
 
30
30
  ## License
31
31
 
package/dist/index.d.mts CHANGED
@@ -233,6 +233,23 @@ interface FireEffect extends SpriteEffect {
233
233
  readonly speed: number;
234
234
  readonly intensity: number;
235
235
  }
236
+ /**
237
+ * Drop-shadow effect — a single offset, blurred, tinted copy of the sprite drawn
238
+ * beneath it for ship / boss grounding. Time-invariant. Applied via a Skia
239
+ * `ImageFilter.MakeDropShadow` on a reused paint (sprite + shadow in one pass);
240
+ * the offset, blur, and tint all live in the filter, so no extra draw pass.
241
+ */
242
+ interface DropShadowEffect extends SpriteEffect {
243
+ readonly type: 'drop-shadow';
244
+ /** Horizontal shadow offset in design px. */
245
+ readonly offsetX: number;
246
+ /** Vertical shadow offset in design px; positive = downward (grounding). */
247
+ readonly offsetY: number;
248
+ /** Gaussian blur sigma applied to the shadow. 0 = hard edge. */
249
+ readonly blur: number;
250
+ /** Shadow color; `a` is the shadow opacity. */
251
+ readonly color: SpriteColor;
252
+ }
236
253
  /** Rooftop city skyline — AABB silhouettes via SDF hash fields. */
237
254
  interface RoofsCityEffect extends SpriteEffect {
238
255
  readonly type: 'roofs-city';
@@ -296,7 +313,7 @@ interface MountainHillsEffect extends SpriteEffect {
296
313
  readonly resolutionH: number;
297
314
  }
298
315
  /** Any concrete sprite effect. */
299
- type AnySpriteEffect = TintEffect | FlashEffect | GrayscaleEffect | OutlineEffect | CustomShaderEffect | WaterEffect | FogEffect | FireEffect | RoofsCityEffect | ForestCanopyEffect | RiverWaveEffect | MoonCraterEffect | CaveInteriorEffect | MountainHillsEffect;
316
+ type AnySpriteEffect = TintEffect | FlashEffect | GrayscaleEffect | OutlineEffect | CustomShaderEffect | WaterEffect | FogEffect | FireEffect | DropShadowEffect | RoofsCityEffect | ForestCanopyEffect | RiverWaveEffect | MoonCraterEffect | CaveInteriorEffect | MountainHillsEffect;
300
317
  /** Create a tint effect. */
301
318
  declare function tint(color: SpriteColor, amount: number): TintEffect;
302
319
  /** Create a flash effect. */
@@ -323,6 +340,14 @@ declare function fire(opts: {
323
340
  speed?: number;
324
341
  intensity?: number;
325
342
  }): FireEffect;
343
+ /** Create a drop-shadow effect. Defaults to a soft shadow offset downward for
344
+ * grounding; `color.a` is the shadow opacity. */
345
+ declare function dropShadow(opts: {
346
+ color: SpriteColor;
347
+ offsetX?: number;
348
+ offsetY?: number;
349
+ blur?: number;
350
+ }): DropShadowEffect;
326
351
  /** Create a rooftop city skyline effect. */
327
352
  declare function roofsCity(opts: {
328
353
  skyColor: SpriteColor;
@@ -495,6 +520,169 @@ declare const MOON_CRATER_SKSL = "\nuniform float2 resolution;\nuniform float3 m
495
520
  declare const CAVE_INTERIOR_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";
496
521
  declare const MOUNTAIN_HILLS_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";
497
522
 
523
+ /** A 4-float RGBA color buffer (r, g, b, a in [0, 1]). */
524
+ type ColorLike = Float32Array;
525
+ /**
526
+ * A mutable axis-aligned rect.
527
+ *
528
+ * The `x`, `y`, `width`, `height` fields are required so that RN-Skia's
529
+ * `SkHostRect` (which carries those fields) structurally satisfies this
530
+ * interface in contra-variant parameter positions.
531
+ */
532
+ interface RectLike {
533
+ x: number;
534
+ y: number;
535
+ width: number;
536
+ height: number;
537
+ setXYWH(x: number, y: number, w: number, h: number): void;
538
+ }
539
+ /**
540
+ * A mutable similarity transform.
541
+ *
542
+ * The `scos`, `ssin`, `tx`, `ty` fields are readable so that CK-backed
543
+ * canvas wrappers can flatten pools into Float32Arrays without allocation.
544
+ * RN-Skia's `SkRSXform` exposes all four fields.
545
+ */
546
+ interface RSXformLike {
547
+ scos: number;
548
+ ssin: number;
549
+ tx: number;
550
+ ty: number;
551
+ set(scos: number, ssin: number, tx: number, ty: number): void;
552
+ }
553
+ /** A compiled SkSL runtime effect. */
554
+ interface RuntimeEffectLike {
555
+ makeShader(uniforms: number[]): unknown;
556
+ getUniformCount(): number;
557
+ getUniformFloatCount(): number;
558
+ getUniformName(index: number): string;
559
+ getUniform(index: number): {
560
+ columns: number;
561
+ rows: number;
562
+ slot: number;
563
+ };
564
+ }
565
+ /** Color filter factory — subset used by the frame-renderer. */
566
+ interface ColorFilterFactoryLike {
567
+ MakeBlend(color: Float32Array, mode: number): unknown;
568
+ MakeMatrix(matrix: number[]): unknown;
569
+ }
570
+ /** Image filter factory — optional; absent on CPU/test surfaces. */
571
+ interface ImageFilterFactoryLike {
572
+ MakeDropShadow(dx: number, dy: number, sigmaX: number, sigmaY: number, color: Float32Array, input: null): unknown;
573
+ }
574
+ /** Runtime-effect factory — optional; absent when SkSL is unsupported. */
575
+ interface RuntimeEffectFactoryLike {
576
+ Make(sksl: string): RuntimeEffectLike | null;
577
+ }
578
+ /** Paint — the writable subset the frame-renderer mutates. */
579
+ interface PaintLike {
580
+ setAlphaf(alpha: number): void;
581
+ setColor?(color: Float32Array): void;
582
+ setColorFilter(filter: unknown): void;
583
+ setShader(shader: unknown): void;
584
+ setImageFilter(filter: unknown): void;
585
+ }
586
+ /** Minimal draw surface the frame-renderer calls into. */
587
+ interface DrawSurfaceLike {
588
+ save(): number;
589
+ restore(): void;
590
+ translate(x: number, y: number): void;
591
+ scale(sx: number, sy: number): void;
592
+ /** rotate(radians, cx, cy) — always in radians. Web shims must convert. */
593
+ rotate(radians: number, cx: number, cy: number): void;
594
+ clear(color: Float32Array): void;
595
+ drawAtlas(image: unknown, srcRects: RectLike[], dstXforms: RSXformLike[], paint: PaintLike): void;
596
+ drawImageRect(image: unknown, src: RectLike, dst: RectLike, paint: PaintLike): void;
597
+ drawRect(rect: RectLike, paint: PaintLike): void;
598
+ }
599
+ /**
600
+ * Opaque picture (recorded draw commands).
601
+ * Typed as `object` so both `SkPicture` (RN-Skia) and CanvasKit's
602
+ * `SkPicture` satisfy it without structural mismatches.
603
+ * Callers that need concrete type methods cast to the platform type.
604
+ */
605
+ type PictureLike = object;
606
+ /** Picture recorder. */
607
+ interface PictureRecorderLike {
608
+ beginRecording(bounds: RectLike): DrawSurfaceLike;
609
+ finishRecordingAsPicture(): PictureLike;
610
+ }
611
+ /**
612
+ * Minimal structural view of the Skia module the frame-renderer needs.
613
+ * Declared here so both `@flare-engine/react` and `@flare-engine/render-web`
614
+ * can pass their respective backends without coupling to a concrete Skia SDK.
615
+ *
616
+ * The `Surface` and `ImageFilter` slots are optional because they are absent
617
+ * on CPU surfaces and in test mocks.
618
+ */
619
+ interface FrameRendererModule {
620
+ XYWHRect(x: number, y: number, w: number, h: number): RectLike;
621
+ RSXform(scos: number, ssin: number, tx: number, ty: number): RSXformLike;
622
+ Paint(): PaintLike;
623
+ PictureRecorder(): PictureRecorderLike;
624
+ ColorFilter: ColorFilterFactoryLike;
625
+ RuntimeEffect?: RuntimeEffectFactoryLike;
626
+ ImageFilter?: ImageFilterFactoryLike;
627
+ }
628
+ /**
629
+ * A single compiled postfx pass descriptor produced by the runtime.
630
+ */
631
+ interface FrameRendererCompiledPass {
632
+ readonly id: string;
633
+ readonly shader: {
634
+ makeShaderWithChildren(uniforms: number[], children: unknown[]): unknown | null;
635
+ getUniformCount?(): number;
636
+ getUniformName?(index: number): string;
637
+ getUniform?(index: number): {
638
+ columns: number;
639
+ rows: number;
640
+ slot: number;
641
+ };
642
+ };
643
+ readonly uniforms: Float32Array;
644
+ readonly children: readonly unknown[];
645
+ }
646
+ /** Minimal structural view of a PostFxRuntime that the frame-renderer needs. */
647
+ interface FrameRendererPostFxRuntime {
648
+ readonly supported: boolean;
649
+ buildPass?(chain: FrameRendererPostFxChain, width: number, height: number): readonly FrameRendererCompiledPass[];
650
+ }
651
+ /** Minimal structural view of an EffectChain the frame-renderer needs. */
652
+ interface FrameRendererPostFxChain {
653
+ enabled(): Iterable<unknown>;
654
+ }
655
+ /** A resolved postfx pass descriptor with named uniforms (for React JSX). */
656
+ interface FrameRendererPostFxDescriptor {
657
+ readonly id: string;
658
+ readonly source: unknown;
659
+ readonly uniforms: Record<string, number | readonly number[]>;
660
+ readonly children: readonly unknown[];
661
+ }
662
+ /**
663
+ * Frame renderer that bridges a `SpriteBatch` to a picture-backed draw surface.
664
+ *
665
+ * Each frame:
666
+ * 1. Call `beginFrame(viewportWidth, viewportHeight)`.
667
+ * 2. Call `drawAtlas(image, commands)` one or more times.
668
+ * 3. Call `endFrame()` — returns the recorded picture.
669
+ */
670
+ interface FrameRenderer {
671
+ readonly canvas: CanvasLike;
672
+ beginFrame(viewportWidth: number, viewportHeight: number, time?: number, pixelDensity?: number): void;
673
+ drawAtlas(image: SkImageLike, commands: SpriteDrawCommand[]): void;
674
+ buildPostFxPasses(runtime: FrameRendererPostFxRuntime | null, chain: FrameRendererPostFxChain | null, width: number, height: number): readonly FrameRendererPostFxDescriptor[];
675
+ setPostFxBinding(runtime: FrameRendererPostFxRuntime | null, chain: FrameRendererPostFxChain | null): void;
676
+ endFrame(): PictureLike | null;
677
+ }
678
+ /**
679
+ * Create a `FrameRenderer` backed by the given structural Skia module.
680
+ *
681
+ * Both `@flare-engine/react` (passing the real RN-Skia module) and
682
+ * `@flare-engine/render-web` (passing a CanvasKit shim) use this factory.
683
+ */
684
+ declare function createFrameRenderer(Skia: FrameRendererModule): FrameRenderer;
685
+
498
686
  /** A single glyph in a bitmap font. */
499
687
  interface Glyph {
500
688
  /** Character code (Unicode). */
@@ -609,4 +797,4 @@ declare function createRenderSystem(world: World, query: Query, positionDef: Com
609
797
  /** Canonical Sprite component — queried by the render system alongside `Position`. */
610
798
  declare const Sprite: ComponentDef<SpriteData>;
611
799
 
612
- export { type AnySpriteEffect, type BitmapFont, CAVE_INTERIOR_SKSL, Camera2D, type CanvasLike, type CaveInteriorEffect, type CompiledShader, type CustomShaderEffect, type Drawable, FIRE_SKSL, FOG_SKSL, FOREST_CANOPY_SKSL, type FireEffect, type FlashEffect, type FogEffect, type FollowOptions, type ForestCanopyEffect, type Glyph, type GrayscaleEffect, type KerningPair, MOON_CRATER_SKSL, MOUNTAIN_HILLS_SKSL, type MoonCraterEffect, type MountainHillsEffect, type OutlineEffect, type PositionData, RIVER_WAVE_SKSL, ROOFS_CITY_SKSL, type RenderStats, Renderer, type RiverWaveEffect, type RoofsCityEffect, type ShaderUniforms, Sprite, type SpriteAnchor, SpriteBatch, type SpriteColor, type SpriteData, type SpriteDrawCommand, type SpriteEffect, type TintEffect, WATER_SKSL, type WaterEffect, caveInterior, createRenderSystem, custom, drawOrderCompare, drawText, fire, flash, flashIntensity, fog, forestCanopy, getKerning, grayscale, measureText, moonCrater, mountainHills, outline, parseBMFontJSON, riverWave, roofsCity, sortByDrawOrder, tint, updateEffect, water };
800
+ export { type AnySpriteEffect, type BitmapFont, CAVE_INTERIOR_SKSL, Camera2D, type CanvasLike, type CaveInteriorEffect, type ColorFilterFactoryLike, type ColorLike, type CompiledShader, type CustomShaderEffect, type DrawSurfaceLike, type Drawable, type DropShadowEffect, FIRE_SKSL, FOG_SKSL, FOREST_CANOPY_SKSL, type FireEffect, type FlashEffect, type FogEffect, type FollowOptions, type ForestCanopyEffect, type FrameRenderer, type FrameRendererCompiledPass, type FrameRendererModule, type FrameRendererPostFxChain, type FrameRendererPostFxDescriptor, type FrameRendererPostFxRuntime, type Glyph, type GrayscaleEffect, type ImageFilterFactoryLike, type KerningPair, MOON_CRATER_SKSL, MOUNTAIN_HILLS_SKSL, type MoonCraterEffect, type MountainHillsEffect, type OutlineEffect, type PaintLike, type PictureLike, type PictureRecorderLike, type PositionData, RIVER_WAVE_SKSL, ROOFS_CITY_SKSL, type RSXformLike, type RectLike, type RenderStats, Renderer, type RiverWaveEffect, type RoofsCityEffect, type RuntimeEffectFactoryLike, type RuntimeEffectLike, type ShaderUniforms, Sprite, type SpriteAnchor, SpriteBatch, type SpriteColor, type SpriteData, type SpriteDrawCommand, type SpriteEffect, type TintEffect, WATER_SKSL, type WaterEffect, caveInterior, createFrameRenderer, createRenderSystem, custom, drawOrderCompare, drawText, dropShadow, fire, flash, flashIntensity, fog, forestCanopy, getKerning, grayscale, measureText, moonCrater, mountainHills, outline, parseBMFontJSON, riverWave, roofsCity, sortByDrawOrder, tint, updateEffect, water };
package/dist/index.d.ts CHANGED
@@ -233,6 +233,23 @@ interface FireEffect extends SpriteEffect {
233
233
  readonly speed: number;
234
234
  readonly intensity: number;
235
235
  }
236
+ /**
237
+ * Drop-shadow effect — a single offset, blurred, tinted copy of the sprite drawn
238
+ * beneath it for ship / boss grounding. Time-invariant. Applied via a Skia
239
+ * `ImageFilter.MakeDropShadow` on a reused paint (sprite + shadow in one pass);
240
+ * the offset, blur, and tint all live in the filter, so no extra draw pass.
241
+ */
242
+ interface DropShadowEffect extends SpriteEffect {
243
+ readonly type: 'drop-shadow';
244
+ /** Horizontal shadow offset in design px. */
245
+ readonly offsetX: number;
246
+ /** Vertical shadow offset in design px; positive = downward (grounding). */
247
+ readonly offsetY: number;
248
+ /** Gaussian blur sigma applied to the shadow. 0 = hard edge. */
249
+ readonly blur: number;
250
+ /** Shadow color; `a` is the shadow opacity. */
251
+ readonly color: SpriteColor;
252
+ }
236
253
  /** Rooftop city skyline — AABB silhouettes via SDF hash fields. */
237
254
  interface RoofsCityEffect extends SpriteEffect {
238
255
  readonly type: 'roofs-city';
@@ -296,7 +313,7 @@ interface MountainHillsEffect extends SpriteEffect {
296
313
  readonly resolutionH: number;
297
314
  }
298
315
  /** Any concrete sprite effect. */
299
- type AnySpriteEffect = TintEffect | FlashEffect | GrayscaleEffect | OutlineEffect | CustomShaderEffect | WaterEffect | FogEffect | FireEffect | RoofsCityEffect | ForestCanopyEffect | RiverWaveEffect | MoonCraterEffect | CaveInteriorEffect | MountainHillsEffect;
316
+ type AnySpriteEffect = TintEffect | FlashEffect | GrayscaleEffect | OutlineEffect | CustomShaderEffect | WaterEffect | FogEffect | FireEffect | DropShadowEffect | RoofsCityEffect | ForestCanopyEffect | RiverWaveEffect | MoonCraterEffect | CaveInteriorEffect | MountainHillsEffect;
300
317
  /** Create a tint effect. */
301
318
  declare function tint(color: SpriteColor, amount: number): TintEffect;
302
319
  /** Create a flash effect. */
@@ -323,6 +340,14 @@ declare function fire(opts: {
323
340
  speed?: number;
324
341
  intensity?: number;
325
342
  }): FireEffect;
343
+ /** Create a drop-shadow effect. Defaults to a soft shadow offset downward for
344
+ * grounding; `color.a` is the shadow opacity. */
345
+ declare function dropShadow(opts: {
346
+ color: SpriteColor;
347
+ offsetX?: number;
348
+ offsetY?: number;
349
+ blur?: number;
350
+ }): DropShadowEffect;
326
351
  /** Create a rooftop city skyline effect. */
327
352
  declare function roofsCity(opts: {
328
353
  skyColor: SpriteColor;
@@ -495,6 +520,169 @@ declare const MOON_CRATER_SKSL = "\nuniform float2 resolution;\nuniform float3 m
495
520
  declare const CAVE_INTERIOR_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";
496
521
  declare const MOUNTAIN_HILLS_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";
497
522
 
523
+ /** A 4-float RGBA color buffer (r, g, b, a in [0, 1]). */
524
+ type ColorLike = Float32Array;
525
+ /**
526
+ * A mutable axis-aligned rect.
527
+ *
528
+ * The `x`, `y`, `width`, `height` fields are required so that RN-Skia's
529
+ * `SkHostRect` (which carries those fields) structurally satisfies this
530
+ * interface in contra-variant parameter positions.
531
+ */
532
+ interface RectLike {
533
+ x: number;
534
+ y: number;
535
+ width: number;
536
+ height: number;
537
+ setXYWH(x: number, y: number, w: number, h: number): void;
538
+ }
539
+ /**
540
+ * A mutable similarity transform.
541
+ *
542
+ * The `scos`, `ssin`, `tx`, `ty` fields are readable so that CK-backed
543
+ * canvas wrappers can flatten pools into Float32Arrays without allocation.
544
+ * RN-Skia's `SkRSXform` exposes all four fields.
545
+ */
546
+ interface RSXformLike {
547
+ scos: number;
548
+ ssin: number;
549
+ tx: number;
550
+ ty: number;
551
+ set(scos: number, ssin: number, tx: number, ty: number): void;
552
+ }
553
+ /** A compiled SkSL runtime effect. */
554
+ interface RuntimeEffectLike {
555
+ makeShader(uniforms: number[]): unknown;
556
+ getUniformCount(): number;
557
+ getUniformFloatCount(): number;
558
+ getUniformName(index: number): string;
559
+ getUniform(index: number): {
560
+ columns: number;
561
+ rows: number;
562
+ slot: number;
563
+ };
564
+ }
565
+ /** Color filter factory — subset used by the frame-renderer. */
566
+ interface ColorFilterFactoryLike {
567
+ MakeBlend(color: Float32Array, mode: number): unknown;
568
+ MakeMatrix(matrix: number[]): unknown;
569
+ }
570
+ /** Image filter factory — optional; absent on CPU/test surfaces. */
571
+ interface ImageFilterFactoryLike {
572
+ MakeDropShadow(dx: number, dy: number, sigmaX: number, sigmaY: number, color: Float32Array, input: null): unknown;
573
+ }
574
+ /** Runtime-effect factory — optional; absent when SkSL is unsupported. */
575
+ interface RuntimeEffectFactoryLike {
576
+ Make(sksl: string): RuntimeEffectLike | null;
577
+ }
578
+ /** Paint — the writable subset the frame-renderer mutates. */
579
+ interface PaintLike {
580
+ setAlphaf(alpha: number): void;
581
+ setColor?(color: Float32Array): void;
582
+ setColorFilter(filter: unknown): void;
583
+ setShader(shader: unknown): void;
584
+ setImageFilter(filter: unknown): void;
585
+ }
586
+ /** Minimal draw surface the frame-renderer calls into. */
587
+ interface DrawSurfaceLike {
588
+ save(): number;
589
+ restore(): void;
590
+ translate(x: number, y: number): void;
591
+ scale(sx: number, sy: number): void;
592
+ /** rotate(radians, cx, cy) — always in radians. Web shims must convert. */
593
+ rotate(radians: number, cx: number, cy: number): void;
594
+ clear(color: Float32Array): void;
595
+ drawAtlas(image: unknown, srcRects: RectLike[], dstXforms: RSXformLike[], paint: PaintLike): void;
596
+ drawImageRect(image: unknown, src: RectLike, dst: RectLike, paint: PaintLike): void;
597
+ drawRect(rect: RectLike, paint: PaintLike): void;
598
+ }
599
+ /**
600
+ * Opaque picture (recorded draw commands).
601
+ * Typed as `object` so both `SkPicture` (RN-Skia) and CanvasKit's
602
+ * `SkPicture` satisfy it without structural mismatches.
603
+ * Callers that need concrete type methods cast to the platform type.
604
+ */
605
+ type PictureLike = object;
606
+ /** Picture recorder. */
607
+ interface PictureRecorderLike {
608
+ beginRecording(bounds: RectLike): DrawSurfaceLike;
609
+ finishRecordingAsPicture(): PictureLike;
610
+ }
611
+ /**
612
+ * Minimal structural view of the Skia module the frame-renderer needs.
613
+ * Declared here so both `@flare-engine/react` and `@flare-engine/render-web`
614
+ * can pass their respective backends without coupling to a concrete Skia SDK.
615
+ *
616
+ * The `Surface` and `ImageFilter` slots are optional because they are absent
617
+ * on CPU surfaces and in test mocks.
618
+ */
619
+ interface FrameRendererModule {
620
+ XYWHRect(x: number, y: number, w: number, h: number): RectLike;
621
+ RSXform(scos: number, ssin: number, tx: number, ty: number): RSXformLike;
622
+ Paint(): PaintLike;
623
+ PictureRecorder(): PictureRecorderLike;
624
+ ColorFilter: ColorFilterFactoryLike;
625
+ RuntimeEffect?: RuntimeEffectFactoryLike;
626
+ ImageFilter?: ImageFilterFactoryLike;
627
+ }
628
+ /**
629
+ * A single compiled postfx pass descriptor produced by the runtime.
630
+ */
631
+ interface FrameRendererCompiledPass {
632
+ readonly id: string;
633
+ readonly shader: {
634
+ makeShaderWithChildren(uniforms: number[], children: unknown[]): unknown | null;
635
+ getUniformCount?(): number;
636
+ getUniformName?(index: number): string;
637
+ getUniform?(index: number): {
638
+ columns: number;
639
+ rows: number;
640
+ slot: number;
641
+ };
642
+ };
643
+ readonly uniforms: Float32Array;
644
+ readonly children: readonly unknown[];
645
+ }
646
+ /** Minimal structural view of a PostFxRuntime that the frame-renderer needs. */
647
+ interface FrameRendererPostFxRuntime {
648
+ readonly supported: boolean;
649
+ buildPass?(chain: FrameRendererPostFxChain, width: number, height: number): readonly FrameRendererCompiledPass[];
650
+ }
651
+ /** Minimal structural view of an EffectChain the frame-renderer needs. */
652
+ interface FrameRendererPostFxChain {
653
+ enabled(): Iterable<unknown>;
654
+ }
655
+ /** A resolved postfx pass descriptor with named uniforms (for React JSX). */
656
+ interface FrameRendererPostFxDescriptor {
657
+ readonly id: string;
658
+ readonly source: unknown;
659
+ readonly uniforms: Record<string, number | readonly number[]>;
660
+ readonly children: readonly unknown[];
661
+ }
662
+ /**
663
+ * Frame renderer that bridges a `SpriteBatch` to a picture-backed draw surface.
664
+ *
665
+ * Each frame:
666
+ * 1. Call `beginFrame(viewportWidth, viewportHeight)`.
667
+ * 2. Call `drawAtlas(image, commands)` one or more times.
668
+ * 3. Call `endFrame()` — returns the recorded picture.
669
+ */
670
+ interface FrameRenderer {
671
+ readonly canvas: CanvasLike;
672
+ beginFrame(viewportWidth: number, viewportHeight: number, time?: number, pixelDensity?: number): void;
673
+ drawAtlas(image: SkImageLike, commands: SpriteDrawCommand[]): void;
674
+ buildPostFxPasses(runtime: FrameRendererPostFxRuntime | null, chain: FrameRendererPostFxChain | null, width: number, height: number): readonly FrameRendererPostFxDescriptor[];
675
+ setPostFxBinding(runtime: FrameRendererPostFxRuntime | null, chain: FrameRendererPostFxChain | null): void;
676
+ endFrame(): PictureLike | null;
677
+ }
678
+ /**
679
+ * Create a `FrameRenderer` backed by the given structural Skia module.
680
+ *
681
+ * Both `@flare-engine/react` (passing the real RN-Skia module) and
682
+ * `@flare-engine/render-web` (passing a CanvasKit shim) use this factory.
683
+ */
684
+ declare function createFrameRenderer(Skia: FrameRendererModule): FrameRenderer;
685
+
498
686
  /** A single glyph in a bitmap font. */
499
687
  interface Glyph {
500
688
  /** Character code (Unicode). */
@@ -609,4 +797,4 @@ declare function createRenderSystem(world: World, query: Query, positionDef: Com
609
797
  /** Canonical Sprite component — queried by the render system alongside `Position`. */
610
798
  declare const Sprite: ComponentDef<SpriteData>;
611
799
 
612
- export { type AnySpriteEffect, type BitmapFont, CAVE_INTERIOR_SKSL, Camera2D, type CanvasLike, type CaveInteriorEffect, type CompiledShader, type CustomShaderEffect, type Drawable, FIRE_SKSL, FOG_SKSL, FOREST_CANOPY_SKSL, type FireEffect, type FlashEffect, type FogEffect, type FollowOptions, type ForestCanopyEffect, type Glyph, type GrayscaleEffect, type KerningPair, MOON_CRATER_SKSL, MOUNTAIN_HILLS_SKSL, type MoonCraterEffect, type MountainHillsEffect, type OutlineEffect, type PositionData, RIVER_WAVE_SKSL, ROOFS_CITY_SKSL, type RenderStats, Renderer, type RiverWaveEffect, type RoofsCityEffect, type ShaderUniforms, Sprite, type SpriteAnchor, SpriteBatch, type SpriteColor, type SpriteData, type SpriteDrawCommand, type SpriteEffect, type TintEffect, WATER_SKSL, type WaterEffect, caveInterior, createRenderSystem, custom, drawOrderCompare, drawText, fire, flash, flashIntensity, fog, forestCanopy, getKerning, grayscale, measureText, moonCrater, mountainHills, outline, parseBMFontJSON, riverWave, roofsCity, sortByDrawOrder, tint, updateEffect, water };
800
+ export { type AnySpriteEffect, type BitmapFont, CAVE_INTERIOR_SKSL, Camera2D, type CanvasLike, type CaveInteriorEffect, type ColorFilterFactoryLike, type ColorLike, type CompiledShader, type CustomShaderEffect, type DrawSurfaceLike, type Drawable, type DropShadowEffect, FIRE_SKSL, FOG_SKSL, FOREST_CANOPY_SKSL, type FireEffect, type FlashEffect, type FogEffect, type FollowOptions, type ForestCanopyEffect, type FrameRenderer, type FrameRendererCompiledPass, type FrameRendererModule, type FrameRendererPostFxChain, type FrameRendererPostFxDescriptor, type FrameRendererPostFxRuntime, type Glyph, type GrayscaleEffect, type ImageFilterFactoryLike, type KerningPair, MOON_CRATER_SKSL, MOUNTAIN_HILLS_SKSL, type MoonCraterEffect, type MountainHillsEffect, type OutlineEffect, type PaintLike, type PictureLike, type PictureRecorderLike, type PositionData, RIVER_WAVE_SKSL, ROOFS_CITY_SKSL, type RSXformLike, type RectLike, type RenderStats, Renderer, type RiverWaveEffect, type RoofsCityEffect, type RuntimeEffectFactoryLike, type RuntimeEffectLike, type ShaderUniforms, Sprite, type SpriteAnchor, SpriteBatch, type SpriteColor, type SpriteData, type SpriteDrawCommand, type SpriteEffect, type TintEffect, WATER_SKSL, type WaterEffect, caveInterior, createFrameRenderer, createRenderSystem, custom, drawOrderCompare, drawText, dropShadow, fire, flash, flashIntensity, fog, forestCanopy, getKerning, grayscale, measureText, moonCrater, mountainHills, outline, parseBMFontJSON, riverWave, roofsCity, sortByDrawOrder, tint, updateEffect, water };