@flare-engine/render 0.1.0 → 0.2.1

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.mjs CHANGED
@@ -533,6 +533,15 @@ function fire(opts) {
533
533
  intensity: opts.intensity ?? 0.8
534
534
  };
535
535
  }
536
+ function dropShadow(opts) {
537
+ return {
538
+ type: "drop-shadow",
539
+ color: opts.color,
540
+ offsetX: opts.offsetX ?? 0,
541
+ offsetY: opts.offsetY ?? 6,
542
+ blur: opts.blur ?? 4
543
+ };
544
+ }
536
545
  function roofsCity(opts) {
537
546
  return {
538
547
  type: "roofs-city",
@@ -928,6 +937,616 @@ half4 main(float2 coord) {
928
937
  `
929
938
  );
930
939
 
940
+ // src/frame-renderer.ts
941
+ var BLEND_SRC_OVER = 3;
942
+ var BLEND_SRC_IN = 5;
943
+ var OUTLINE_OFFSETS = [
944
+ [1, 0],
945
+ [-1, 0],
946
+ [0, 1],
947
+ [0, -1],
948
+ [Math.SQRT1_2, Math.SQRT1_2],
949
+ [Math.SQRT1_2, -Math.SQRT1_2],
950
+ [-Math.SQRT1_2, Math.SQRT1_2],
951
+ [-Math.SQRT1_2, -Math.SQRT1_2]
952
+ ];
953
+ function createFrameRenderer(Skia) {
954
+ const defaultPaint = Skia.Paint();
955
+ let tintPaint = null;
956
+ let flashPaint = null;
957
+ let grayscalePaint = null;
958
+ let outlinePaint = null;
959
+ let customPaint = null;
960
+ let waterPaint = null;
961
+ let waterRuntimeEffect = null;
962
+ let fogPaint = null;
963
+ let fogRuntimeEffect = null;
964
+ let firePaint = null;
965
+ let fireRuntimeEffect = null;
966
+ let dropShadowPaint = null;
967
+ let roofsCityPaint = null;
968
+ let roofsCityEffect = null;
969
+ let forestCanopyPaint = null;
970
+ let forestCanopyEffect = null;
971
+ let riverWavePaint = null;
972
+ let riverWaveEffect = null;
973
+ let moonCraterPaint = null;
974
+ let moonCraterEffect = null;
975
+ let caveInteriorPaint = null;
976
+ let caveInteriorEffect = null;
977
+ let mountainHillsPaint = null;
978
+ let mountainHillsEffect = null;
979
+ const roofsCityUniforms = new Array(10).fill(0);
980
+ const forestCanopyUniforms = new Array(10).fill(0);
981
+ const riverWaveUniforms = new Array(10).fill(0);
982
+ const moonCraterUniforms = new Array(10).fill(0);
983
+ const caveInteriorUniforms = new Array(10).fill(0);
984
+ const mountainHillsUniforms = new Array(10).fill(0);
985
+ const recorder = Skia.PictureRecorder();
986
+ const recordingBounds = Skia.XYWHRect(0, 0, 0, 0);
987
+ const srcRects = [];
988
+ const xforms = [];
989
+ const stretchSrcRects = [];
990
+ const stretchDstRects = [];
991
+ let stretchPaint = null;
992
+ const zoneBgRects = [];
993
+ let _cachedSrcSlice = null;
994
+ let _cachedSrcSliceLen = 0;
995
+ let _cachedXformSlice = null;
996
+ let _cachedXformSliceLen = 0;
997
+ const clearColor = new Float32Array(4);
998
+ const tintColor = new Float32Array(4);
999
+ const flashColor = new Float32Array(4);
1000
+ const outlineColor = new Float32Array(4);
1001
+ const dropShadowColor = new Float32Array(4);
1002
+ const grayMatrix = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0];
1003
+ const customUniforms = [];
1004
+ const waterUniforms = new Array(11).fill(0);
1005
+ const fogUniforms = new Array(9).fill(0);
1006
+ const fireUniforms = new Array(11).fill(0);
1007
+ let currentFrameTime = 0;
1008
+ let current = null;
1009
+ let _boundRuntime = null;
1010
+ let _boundChain = null;
1011
+ const _loggedOnce = /* @__PURE__ */ new Set();
1012
+ function logOnce(msg) {
1013
+ if (_loggedOnce.has(msg)) return;
1014
+ _loggedOnce.add(msg);
1015
+ console.warn(`[postfx] ${msg}`);
1016
+ }
1017
+ const canvas = {
1018
+ save: () => current?.save() ?? 0,
1019
+ restore: () => {
1020
+ current?.restore();
1021
+ },
1022
+ translate: (x, y) => {
1023
+ current?.translate(x, y);
1024
+ },
1025
+ scale: (sx, sy) => {
1026
+ current?.scale(sx, sy);
1027
+ },
1028
+ rotate: (radians, cx, cy) => {
1029
+ current?.rotate(radians, cx, cy);
1030
+ },
1031
+ clear: (argb) => {
1032
+ if (current === null) return;
1033
+ clearColor[0] = (argb >>> 16 & 255) / 255;
1034
+ clearColor[1] = (argb >>> 8 & 255) / 255;
1035
+ clearColor[2] = (argb & 255) / 255;
1036
+ clearColor[3] = (argb >>> 24 & 255) / 255;
1037
+ current.clear(clearColor);
1038
+ }
1039
+ };
1040
+ function ensurePoolCapacity(n) {
1041
+ while (srcRects.length < n) {
1042
+ srcRects.push(Skia.XYWHRect(0, 0, 0, 0));
1043
+ xforms.push(Skia.RSXform(1, 0, 0, 0));
1044
+ }
1045
+ }
1046
+ function ensureStretchPoolCapacity(n) {
1047
+ while (stretchSrcRects.length < n) {
1048
+ stretchSrcRects.push(Skia.XYWHRect(0, 0, 0, 0));
1049
+ stretchDstRects.push(Skia.XYWHRect(0, 0, 0, 0));
1050
+ }
1051
+ }
1052
+ function ensureZoneBgPoolCapacity(n) {
1053
+ while (zoneBgRects.length < n) {
1054
+ zoneBgRects.push(Skia.XYWHRect(0, 0, 0, 0));
1055
+ }
1056
+ }
1057
+ function getCachedSrcSlice(n) {
1058
+ if (_cachedSrcSlice !== null && _cachedSrcSliceLen === n) return _cachedSrcSlice;
1059
+ _cachedSrcSlice = srcRects.length === n ? srcRects : srcRects.slice(0, n);
1060
+ _cachedSrcSliceLen = n;
1061
+ return _cachedSrcSlice;
1062
+ }
1063
+ function getCachedXformSlice(n) {
1064
+ if (_cachedXformSlice !== null && _cachedXformSliceLen === n) return _cachedXformSlice;
1065
+ _cachedXformSlice = xforms.length === n ? xforms : xforms.slice(0, n);
1066
+ _cachedXformSliceLen = n;
1067
+ return _cachedXformSlice;
1068
+ }
1069
+ function fillSrcRects(commands, n) {
1070
+ for (let i = 0; i < n; i++) {
1071
+ const cmd = commands[i];
1072
+ srcRects[i]?.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);
1073
+ }
1074
+ }
1075
+ function fillXforms(commands, n, dx, dy) {
1076
+ for (let i = 0; i < n; i++) {
1077
+ const cmd = commands[i];
1078
+ const scale = cmd.srcW === 0 ? 1 : cmd.dstW / cmd.srcW;
1079
+ if (cmd.rotation === 0) {
1080
+ xforms[i]?.set(scale, 0, cmd.dstX + dx, cmd.dstY + dy);
1081
+ } else {
1082
+ const c = Math.cos(cmd.rotation);
1083
+ const s = Math.sin(cmd.rotation);
1084
+ const scos = scale * c;
1085
+ const ssin = scale * s;
1086
+ const px = cmd.dstX + cmd.anchorX * cmd.dstW + dx;
1087
+ const py = cmd.dstY + cmd.anchorY * cmd.dstH + dy;
1088
+ const axSrc = cmd.anchorX * cmd.srcW;
1089
+ const aySrc = cmd.anchorY * cmd.srcH;
1090
+ xforms[i]?.set(
1091
+ scos,
1092
+ ssin,
1093
+ px - scos * axSrc + ssin * aySrc,
1094
+ py - ssin * axSrc - scos * aySrc
1095
+ );
1096
+ }
1097
+ }
1098
+ }
1099
+ function applyTint(effect) {
1100
+ if (tintPaint === null) tintPaint = Skia.Paint();
1101
+ writeColor(tintColor, effect.color, effect.amount);
1102
+ tintPaint.setColorFilter(Skia.ColorFilter.MakeBlend(tintColor, BLEND_SRC_OVER));
1103
+ return tintPaint;
1104
+ }
1105
+ function applyFlash(effect) {
1106
+ if (flashPaint === null) flashPaint = Skia.Paint();
1107
+ writeColor(flashColor, effect.color, flashIntensity(effect));
1108
+ flashPaint.setColorFilter(Skia.ColorFilter.MakeBlend(flashColor, BLEND_SRC_OVER));
1109
+ return flashPaint;
1110
+ }
1111
+ function applyGrayscale(effect) {
1112
+ if (grayscalePaint === null) grayscalePaint = Skia.Paint();
1113
+ writeGrayscaleMatrix(grayMatrix, effect.amount);
1114
+ grayscalePaint.setColorFilter(Skia.ColorFilter.MakeMatrix(grayMatrix));
1115
+ return grayscalePaint;
1116
+ }
1117
+ function applyOutlinePaint(effect) {
1118
+ if (outlinePaint === null) outlinePaint = Skia.Paint();
1119
+ writeColor(outlineColor, effect.color, 1);
1120
+ outlinePaint.setColorFilter(Skia.ColorFilter.MakeBlend(outlineColor, BLEND_SRC_IN));
1121
+ return outlinePaint;
1122
+ }
1123
+ function applyCustom(effect) {
1124
+ if (customPaint === null) customPaint = Skia.Paint();
1125
+ const shader = effect.shader;
1126
+ const floatCount = shader.getUniformFloatCount();
1127
+ while (customUniforms.length < floatCount) customUniforms.push(0);
1128
+ customUniforms.length = floatCount;
1129
+ fillCustomUniforms(effect, shader, customUniforms);
1130
+ customPaint.setShader(shader.makeShader(customUniforms));
1131
+ return customPaint;
1132
+ }
1133
+ function applyWater(effect, time) {
1134
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1135
+ if (waterRuntimeEffect === null) {
1136
+ const compiled = Skia.RuntimeEffect.Make(WATER_SKSL);
1137
+ if (compiled === null) return defaultPaint;
1138
+ waterRuntimeEffect = compiled;
1139
+ }
1140
+ if (waterPaint === null) waterPaint = Skia.Paint();
1141
+ waterUniforms[0] = 0;
1142
+ waterUniforms[1] = 0;
1143
+ waterUniforms[2] = effect.color.r;
1144
+ waterUniforms[3] = effect.color.g;
1145
+ waterUniforms[4] = effect.color.b;
1146
+ waterUniforms[5] = effect.foamColor.r;
1147
+ waterUniforms[6] = effect.foamColor.g;
1148
+ waterUniforms[7] = effect.foamColor.b;
1149
+ waterUniforms[8] = effect.waveSpeed;
1150
+ waterUniforms[9] = effect.waveAmplitude;
1151
+ waterUniforms[10] = time;
1152
+ waterPaint.setShader(waterRuntimeEffect.makeShader(waterUniforms));
1153
+ return waterPaint;
1154
+ }
1155
+ function applyFog(effect, time) {
1156
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1157
+ if (fogRuntimeEffect === null) {
1158
+ const compiled = Skia.RuntimeEffect.Make(FOG_SKSL);
1159
+ if (compiled === null) return defaultPaint;
1160
+ fogRuntimeEffect = compiled;
1161
+ }
1162
+ if (fogPaint === null) fogPaint = Skia.Paint();
1163
+ fogUniforms[0] = 0;
1164
+ fogUniforms[1] = 0;
1165
+ fogUniforms[2] = effect.color.r;
1166
+ fogUniforms[3] = effect.color.g;
1167
+ fogUniforms[4] = effect.color.b;
1168
+ fogUniforms[5] = effect.density;
1169
+ fogUniforms[6] = effect.speed;
1170
+ fogUniforms[7] = effect.scale;
1171
+ fogUniforms[8] = time;
1172
+ fogPaint.setShader(fogRuntimeEffect.makeShader(fogUniforms));
1173
+ return fogPaint;
1174
+ }
1175
+ function applyFire(effect, time) {
1176
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1177
+ if (fireRuntimeEffect === null) {
1178
+ const compiled = Skia.RuntimeEffect.Make(FIRE_SKSL);
1179
+ if (compiled === null) return defaultPaint;
1180
+ fireRuntimeEffect = compiled;
1181
+ }
1182
+ if (firePaint === null) firePaint = Skia.Paint();
1183
+ fireUniforms[0] = 0;
1184
+ fireUniforms[1] = 0;
1185
+ fireUniforms[2] = effect.color.r;
1186
+ fireUniforms[3] = effect.color.g;
1187
+ fireUniforms[4] = effect.color.b;
1188
+ fireUniforms[5] = effect.tipColor.r;
1189
+ fireUniforms[6] = effect.tipColor.g;
1190
+ fireUniforms[7] = effect.tipColor.b;
1191
+ fireUniforms[8] = effect.speed;
1192
+ fireUniforms[9] = effect.intensity;
1193
+ fireUniforms[10] = time;
1194
+ firePaint.setShader(fireRuntimeEffect.makeShader(fireUniforms));
1195
+ return firePaint;
1196
+ }
1197
+ function applyDropShadow(effect) {
1198
+ if (Skia.ImageFilter === void 0) return defaultPaint;
1199
+ if (dropShadowPaint === null) dropShadowPaint = Skia.Paint();
1200
+ writeColor(dropShadowColor, effect.color, 1);
1201
+ dropShadowPaint.setImageFilter(
1202
+ Skia.ImageFilter.MakeDropShadow(
1203
+ effect.offsetX,
1204
+ effect.offsetY,
1205
+ effect.blur,
1206
+ effect.blur,
1207
+ dropShadowColor,
1208
+ null
1209
+ )
1210
+ );
1211
+ return dropShadowPaint;
1212
+ }
1213
+ function applyRoofsCity(effect) {
1214
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1215
+ if (roofsCityEffect === null) {
1216
+ const compiled = Skia.RuntimeEffect.Make(ROOFS_CITY_SKSL);
1217
+ if (compiled === null) return defaultPaint;
1218
+ roofsCityEffect = compiled;
1219
+ }
1220
+ if (roofsCityPaint === null) roofsCityPaint = Skia.Paint();
1221
+ roofsCityUniforms[0] = effect.resolutionW;
1222
+ roofsCityUniforms[1] = effect.resolutionH;
1223
+ roofsCityUniforms[2] = effect.skyColor.r;
1224
+ roofsCityUniforms[3] = effect.skyColor.g;
1225
+ roofsCityUniforms[4] = effect.skyColor.b;
1226
+ roofsCityUniforms[5] = effect.roofColor.r;
1227
+ roofsCityUniforms[6] = effect.roofColor.g;
1228
+ roofsCityUniforms[7] = effect.roofColor.b;
1229
+ roofsCityUniforms[8] = effect.scrollOffsetY;
1230
+ roofsCityUniforms[9] = effect.density;
1231
+ roofsCityPaint.setShader(roofsCityEffect.makeShader(roofsCityUniforms));
1232
+ return roofsCityPaint;
1233
+ }
1234
+ function applyForestCanopy(effect) {
1235
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1236
+ if (forestCanopyEffect === null) {
1237
+ const compiled = Skia.RuntimeEffect.Make(FOREST_CANOPY_SKSL);
1238
+ if (compiled === null) return defaultPaint;
1239
+ forestCanopyEffect = compiled;
1240
+ }
1241
+ if (forestCanopyPaint === null) forestCanopyPaint = Skia.Paint();
1242
+ forestCanopyUniforms[0] = effect.resolutionW;
1243
+ forestCanopyUniforms[1] = effect.resolutionH;
1244
+ forestCanopyUniforms[2] = effect.skyColor.r;
1245
+ forestCanopyUniforms[3] = effect.skyColor.g;
1246
+ forestCanopyUniforms[4] = effect.skyColor.b;
1247
+ forestCanopyUniforms[5] = effect.leafColor.r;
1248
+ forestCanopyUniforms[6] = effect.leafColor.g;
1249
+ forestCanopyUniforms[7] = effect.leafColor.b;
1250
+ forestCanopyUniforms[8] = effect.scrollOffsetY;
1251
+ forestCanopyUniforms[9] = effect.density;
1252
+ forestCanopyPaint.setShader(forestCanopyEffect.makeShader(forestCanopyUniforms));
1253
+ return forestCanopyPaint;
1254
+ }
1255
+ function applyRiverWave(effect) {
1256
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1257
+ if (riverWaveEffect === null) {
1258
+ const compiled = Skia.RuntimeEffect.Make(RIVER_WAVE_SKSL);
1259
+ if (compiled === null) return defaultPaint;
1260
+ riverWaveEffect = compiled;
1261
+ }
1262
+ if (riverWavePaint === null) riverWavePaint = Skia.Paint();
1263
+ riverWaveUniforms[0] = effect.resolutionW;
1264
+ riverWaveUniforms[1] = effect.resolutionH;
1265
+ riverWaveUniforms[2] = effect.waterColor.r;
1266
+ riverWaveUniforms[3] = effect.waterColor.g;
1267
+ riverWaveUniforms[4] = effect.waterColor.b;
1268
+ riverWaveUniforms[5] = effect.foamColor.r;
1269
+ riverWaveUniforms[6] = effect.foamColor.g;
1270
+ riverWaveUniforms[7] = effect.foamColor.b;
1271
+ riverWaveUniforms[8] = effect.scrollOffsetY;
1272
+ riverWaveUniforms[9] = effect.waveScale;
1273
+ riverWavePaint.setShader(riverWaveEffect.makeShader(riverWaveUniforms));
1274
+ return riverWavePaint;
1275
+ }
1276
+ function applyMoonCrater(effect) {
1277
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1278
+ if (moonCraterEffect === null) {
1279
+ const compiled = Skia.RuntimeEffect.Make(MOON_CRATER_SKSL);
1280
+ if (compiled === null) return defaultPaint;
1281
+ moonCraterEffect = compiled;
1282
+ }
1283
+ if (moonCraterPaint === null) moonCraterPaint = Skia.Paint();
1284
+ moonCraterUniforms[0] = effect.resolutionW;
1285
+ moonCraterUniforms[1] = effect.resolutionH;
1286
+ moonCraterUniforms[2] = effect.moonColor.r;
1287
+ moonCraterUniforms[3] = effect.moonColor.g;
1288
+ moonCraterUniforms[4] = effect.moonColor.b;
1289
+ moonCraterUniforms[5] = effect.craterColor.r;
1290
+ moonCraterUniforms[6] = effect.craterColor.g;
1291
+ moonCraterUniforms[7] = effect.craterColor.b;
1292
+ moonCraterUniforms[8] = effect.scrollOffsetY;
1293
+ moonCraterUniforms[9] = effect.craterCount;
1294
+ moonCraterPaint.setShader(moonCraterEffect.makeShader(moonCraterUniforms));
1295
+ return moonCraterPaint;
1296
+ }
1297
+ function applyCaveInterior(effect) {
1298
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1299
+ if (caveInteriorEffect === null) {
1300
+ const compiled = Skia.RuntimeEffect.Make(CAVE_INTERIOR_SKSL);
1301
+ if (compiled === null) return defaultPaint;
1302
+ caveInteriorEffect = compiled;
1303
+ }
1304
+ if (caveInteriorPaint === null) caveInteriorPaint = Skia.Paint();
1305
+ caveInteriorUniforms[0] = effect.resolutionW;
1306
+ caveInteriorUniforms[1] = effect.resolutionH;
1307
+ caveInteriorUniforms[2] = effect.caveColor.r;
1308
+ caveInteriorUniforms[3] = effect.caveColor.g;
1309
+ caveInteriorUniforms[4] = effect.caveColor.b;
1310
+ caveInteriorUniforms[5] = effect.glowColor.r;
1311
+ caveInteriorUniforms[6] = effect.glowColor.g;
1312
+ caveInteriorUniforms[7] = effect.glowColor.b;
1313
+ caveInteriorUniforms[8] = effect.scrollOffsetY;
1314
+ caveInteriorUniforms[9] = effect.stalactiteCount;
1315
+ caveInteriorPaint.setShader(caveInteriorEffect.makeShader(caveInteriorUniforms));
1316
+ return caveInteriorPaint;
1317
+ }
1318
+ function applyMountainHills(effect) {
1319
+ if (Skia.RuntimeEffect === void 0) return defaultPaint;
1320
+ if (mountainHillsEffect === null) {
1321
+ const compiled = Skia.RuntimeEffect.Make(MOUNTAIN_HILLS_SKSL);
1322
+ if (compiled === null) return defaultPaint;
1323
+ mountainHillsEffect = compiled;
1324
+ }
1325
+ if (mountainHillsPaint === null) mountainHillsPaint = Skia.Paint();
1326
+ mountainHillsUniforms[0] = effect.resolutionW;
1327
+ mountainHillsUniforms[1] = effect.resolutionH;
1328
+ mountainHillsUniforms[2] = effect.skyColor.r;
1329
+ mountainHillsUniforms[3] = effect.skyColor.g;
1330
+ mountainHillsUniforms[4] = effect.skyColor.b;
1331
+ mountainHillsUniforms[5] = effect.hillColor.r;
1332
+ mountainHillsUniforms[6] = effect.hillColor.g;
1333
+ mountainHillsUniforms[7] = effect.hillColor.b;
1334
+ mountainHillsUniforms[8] = effect.scrollOffsetY;
1335
+ mountainHillsUniforms[9] = effect.ridgeScale;
1336
+ mountainHillsPaint.setShader(mountainHillsEffect.makeShader(mountainHillsUniforms));
1337
+ return mountainHillsPaint;
1338
+ }
1339
+ function drawOutlined(cvs, image, commands, n, effect, alpha) {
1340
+ const paint = applyOutlinePaint(effect);
1341
+ paint.setAlphaf(alpha);
1342
+ const srcs = getCachedSrcSlice(n);
1343
+ for (let k = 0; k < OUTLINE_OFFSETS.length; k++) {
1344
+ const offset = OUTLINE_OFFSETS[k];
1345
+ fillXforms(commands, n, offset[0] * effect.width, offset[1] * effect.width);
1346
+ const dsts = getCachedXformSlice(n);
1347
+ cvs.drawAtlas(image, srcs, dsts, paint);
1348
+ }
1349
+ paint.setAlphaf(1);
1350
+ defaultPaint.setAlphaf(alpha);
1351
+ fillXforms(commands, n, 0, 0);
1352
+ const mainDsts = getCachedXformSlice(n);
1353
+ cvs.drawAtlas(image, srcs, mainDsts, defaultPaint);
1354
+ defaultPaint.setAlphaf(1);
1355
+ }
1356
+ function resolveEffectPaint(effect) {
1357
+ switch (effect.type) {
1358
+ case "tint":
1359
+ return applyTint(effect);
1360
+ case "flash":
1361
+ return applyFlash(effect);
1362
+ case "grayscale":
1363
+ return applyGrayscale(effect);
1364
+ case "custom":
1365
+ return applyCustom(effect);
1366
+ case "water":
1367
+ return applyWater(effect, currentFrameTime);
1368
+ case "fog":
1369
+ return applyFog(effect, currentFrameTime);
1370
+ case "fire":
1371
+ return applyFire(effect, currentFrameTime);
1372
+ case "drop-shadow":
1373
+ return applyDropShadow(effect);
1374
+ case "outline":
1375
+ return applyOutlinePaint(effect);
1376
+ case "roofs-city":
1377
+ return applyRoofsCity(effect);
1378
+ case "forest-canopy":
1379
+ return applyForestCanopy(effect);
1380
+ case "river-wave":
1381
+ return applyRiverWave(effect);
1382
+ case "moon-crater":
1383
+ return applyMoonCrater(effect);
1384
+ case "cave-interior":
1385
+ return applyCaveInterior(effect);
1386
+ case "mountain-hills":
1387
+ return applyMountainHills(effect);
1388
+ }
1389
+ }
1390
+ return {
1391
+ canvas,
1392
+ beginFrame(viewportWidth, viewportHeight, time = 0, pixelDensity = 1) {
1393
+ if (viewportWidth === 0 || viewportHeight === 0) return;
1394
+ _boundRuntime = null;
1395
+ _boundChain = null;
1396
+ currentFrameTime = time;
1397
+ const pd = pixelDensity;
1398
+ recordingBounds.setXYWH(0, 0, viewportWidth * pd, viewportHeight * pd);
1399
+ current = recorder.beginRecording(recordingBounds);
1400
+ if (pd !== 1) current.scale(pd, pd);
1401
+ },
1402
+ drawAtlas(image, commands) {
1403
+ if (current === null) return;
1404
+ const n = commands.length;
1405
+ if (n === 0) return;
1406
+ const effect = commands[0]?.effect;
1407
+ const alpha = commands[0]?.alpha ?? 1;
1408
+ if (effect?.type === "roofs-city" || effect?.type === "forest-canopy" || effect?.type === "river-wave" || effect?.type === "moon-crater" || effect?.type === "cave-interior" || effect?.type === "mountain-hills") {
1409
+ const paint2 = resolveEffectPaint(effect);
1410
+ paint2.setAlphaf(alpha);
1411
+ ensureZoneBgPoolCapacity(n);
1412
+ for (let i = 0; i < n; i++) {
1413
+ const cmd = commands[i];
1414
+ zoneBgRects[i].setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);
1415
+ current.drawRect(zoneBgRects[i], paint2);
1416
+ }
1417
+ paint2.setAlphaf(1);
1418
+ return;
1419
+ }
1420
+ if (commands[0]?.stretch) {
1421
+ if (stretchPaint === null) stretchPaint = Skia.Paint();
1422
+ ensureStretchPoolCapacity(n);
1423
+ stretchPaint.setAlphaf(alpha);
1424
+ for (let i = 0; i < n; i++) {
1425
+ const cmd = commands[i];
1426
+ const srcR = stretchSrcRects[i];
1427
+ const dstR = stretchDstRects[i];
1428
+ srcR.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);
1429
+ dstR.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);
1430
+ current.drawImageRect(image, srcR, dstR, stretchPaint);
1431
+ }
1432
+ stretchPaint.setAlphaf(1);
1433
+ return;
1434
+ }
1435
+ ensurePoolCapacity(n);
1436
+ fillSrcRects(commands, n);
1437
+ if (effect?.type === "outline") {
1438
+ drawOutlined(current, image, commands, n, effect, alpha);
1439
+ return;
1440
+ }
1441
+ fillXforms(commands, n, 0, 0);
1442
+ const srcs = getCachedSrcSlice(n);
1443
+ const dsts = getCachedXformSlice(n);
1444
+ const paint = effect === void 0 ? defaultPaint : resolveEffectPaint(effect);
1445
+ paint.setAlphaf(alpha);
1446
+ current.drawAtlas(image, srcs, dsts, paint);
1447
+ paint.setAlphaf(1);
1448
+ },
1449
+ setPostFxBinding(runtime, chain) {
1450
+ _boundRuntime = runtime;
1451
+ _boundChain = chain;
1452
+ },
1453
+ buildPostFxPasses(runtime, chain, width, height) {
1454
+ const rt = runtime ?? _boundRuntime;
1455
+ const ch = chain ?? _boundChain;
1456
+ if (rt === null || !rt.supported) return [];
1457
+ if (rt.buildPass === void 0) {
1458
+ logOnce("buildPostFxPasses: runtime.supported but buildPass absent \u2014 no passes");
1459
+ return [];
1460
+ }
1461
+ if (ch === null) return [];
1462
+ const passes = rt.buildPass(ch, width, height);
1463
+ if (passes.length === 0) return [];
1464
+ const out = [];
1465
+ for (const pass of passes) {
1466
+ const shader = pass.shader;
1467
+ const uniformCount = typeof shader.getUniformCount === "function" ? shader.getUniformCount() : 0;
1468
+ const uniforms = {};
1469
+ for (let i = 0; i < uniformCount; i++) {
1470
+ const name = typeof shader.getUniformName === "function" ? shader.getUniformName(i) : String(i);
1471
+ const info = typeof shader.getUniform === "function" ? shader.getUniform(i) : { columns: 1, rows: 1, slot: i };
1472
+ const size = info.columns * info.rows;
1473
+ if (size === 1) {
1474
+ uniforms[name] = pass.uniforms[info.slot] ?? 0;
1475
+ } else {
1476
+ const vec = [];
1477
+ for (let k = 0; k < size; k++) {
1478
+ vec.push(pass.uniforms[info.slot + k] ?? 0);
1479
+ }
1480
+ uniforms[name] = vec;
1481
+ }
1482
+ }
1483
+ out.push({
1484
+ id: pass.id,
1485
+ source: shader,
1486
+ uniforms,
1487
+ children: pass.children
1488
+ });
1489
+ }
1490
+ return out;
1491
+ },
1492
+ endFrame() {
1493
+ if (current === null) return null;
1494
+ current = null;
1495
+ return recorder.finishRecordingAsPicture();
1496
+ }
1497
+ };
1498
+ }
1499
+ function writeColor(out, color, alphaMul) {
1500
+ out[0] = color.r;
1501
+ out[1] = color.g;
1502
+ out[2] = color.b;
1503
+ out[3] = color.a * alphaMul;
1504
+ }
1505
+ function writeGrayscaleMatrix(out, amount) {
1506
+ const inv = 1 - amount;
1507
+ const lr = 0.299 * amount;
1508
+ const lg = 0.587 * amount;
1509
+ const lb = 0.114 * amount;
1510
+ out[0] = inv + lr;
1511
+ out[1] = lg;
1512
+ out[2] = lb;
1513
+ out[3] = 0;
1514
+ out[4] = 0;
1515
+ out[5] = lr;
1516
+ out[6] = inv + lg;
1517
+ out[7] = lb;
1518
+ out[8] = 0;
1519
+ out[9] = 0;
1520
+ out[10] = lr;
1521
+ out[11] = lg;
1522
+ out[12] = inv + lb;
1523
+ out[13] = 0;
1524
+ out[14] = 0;
1525
+ out[15] = 0;
1526
+ out[16] = 0;
1527
+ out[17] = 0;
1528
+ out[18] = 1;
1529
+ out[19] = 0;
1530
+ }
1531
+ function fillCustomUniforms(effect, shader, out) {
1532
+ const count = shader.getUniformCount();
1533
+ for (let i = 0; i < out.length; i++) out[i] = 0;
1534
+ for (let i = 0; i < count; i++) {
1535
+ const info = shader.getUniform(i);
1536
+ const name = shader.getUniformName(i);
1537
+ const value = effect.uniforms[name];
1538
+ const size = info.columns * info.rows;
1539
+ if (value === void 0) continue;
1540
+ if (typeof value === "number") {
1541
+ out[info.slot] = value;
1542
+ continue;
1543
+ }
1544
+ for (let k = 0; k < size; k++) {
1545
+ out[info.slot + k] = value[k] ?? 0;
1546
+ }
1547
+ }
1548
+ }
1549
+
931
1550
  // src/sprite-font.ts
932
1551
  function parseBMFontJSON(json) {
933
1552
  const data = json;
@@ -1066,10 +1685,12 @@ export {
1066
1685
  SpriteBatch,
1067
1686
  WATER_SKSL,
1068
1687
  caveInterior,
1688
+ createFrameRenderer,
1069
1689
  createRenderSystem,
1070
1690
  custom,
1071
1691
  drawOrderCompare,
1072
1692
  drawText,
1693
+ dropShadow,
1073
1694
  fire,
1074
1695
  flash,
1075
1696
  flashIntensity,