@flare-engine/render 0.3.0 → 0.4.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.d.mts +100 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +100 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +152 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -789,6 +789,8 @@ function flashIntensity(effect) {
|
|
|
789
789
|
const G = globalThis;
|
|
790
790
|
const BLEND_SRC_OVER = 3;
|
|
791
791
|
const BLEND_SRC_IN = 5;
|
|
792
|
+
/** Skia BlendMode.Plus — additive: dst = clamp(src + dst). */
|
|
793
|
+
const BLEND_PLUS = 12;
|
|
792
794
|
const OUTLINE_OFFSETS = [
|
|
793
795
|
[1, 0],
|
|
794
796
|
[-1, 0],
|
|
@@ -1254,12 +1256,17 @@ function createFrameRenderer(Skia) {
|
|
|
1254
1256
|
if (current === null) return;
|
|
1255
1257
|
const n = commands.length;
|
|
1256
1258
|
if (n === 0) return;
|
|
1257
|
-
const
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1259
|
+
const cmd0 = commands[0];
|
|
1260
|
+
const effect = cmd0.effect;
|
|
1261
|
+
const alpha = cmd0.alpha;
|
|
1262
|
+
const blendAdditive = cmd0.blend === "additive";
|
|
1263
|
+
const flipX = cmd0.flipX;
|
|
1264
|
+
const flipY = cmd0.flipY;
|
|
1265
|
+
currentSpriteW = cmd0.dstW;
|
|
1266
|
+
currentSpriteH = cmd0.dstH;
|
|
1261
1267
|
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") {
|
|
1262
1268
|
const paint = resolveEffectPaint(effect);
|
|
1269
|
+
if (blendAdditive) paint.setBlendMode?.(BLEND_PLUS);
|
|
1263
1270
|
paint.setAlphaf(alpha);
|
|
1264
1271
|
ensureZoneBgPoolCapacity(n);
|
|
1265
1272
|
for (let i = 0; i < n; i++) {
|
|
@@ -1268,13 +1275,15 @@ function createFrameRenderer(Skia) {
|
|
|
1268
1275
|
current.drawRect(zoneBgRects[i], paint);
|
|
1269
1276
|
}
|
|
1270
1277
|
paint.setAlphaf(1);
|
|
1278
|
+
if (blendAdditive) paint.setBlendMode?.(BLEND_SRC_OVER);
|
|
1271
1279
|
return;
|
|
1272
1280
|
}
|
|
1273
|
-
if (
|
|
1281
|
+
if (cmd0.stretch || flipX || flipY) {
|
|
1274
1282
|
ensureStretchPoolCapacity(n);
|
|
1275
1283
|
if (stretchPaint === null) stretchPaint = Skia.Paint();
|
|
1276
1284
|
const sp = stretchPaint;
|
|
1277
1285
|
const sPaint = effect === void 0 || effect.type === "outline" ? sp : resolveEffectPaint(effect);
|
|
1286
|
+
if (blendAdditive) sPaint.setBlendMode?.(BLEND_PLUS);
|
|
1278
1287
|
sPaint.setAlphaf(alpha);
|
|
1279
1288
|
for (let i = 0; i < n; i++) {
|
|
1280
1289
|
const cmd = commands[i];
|
|
@@ -1282,24 +1291,44 @@ function createFrameRenderer(Skia) {
|
|
|
1282
1291
|
const dstR = stretchDstRects[i];
|
|
1283
1292
|
srcR.setXYWH(cmd.srcX, cmd.srcY, cmd.srcW, cmd.srcH);
|
|
1284
1293
|
dstR.setXYWH(cmd.dstX, cmd.dstY, cmd.dstW, cmd.dstH);
|
|
1285
|
-
|
|
1294
|
+
if (cmd.flipX || cmd.flipY) {
|
|
1295
|
+
const cx = cmd.dstX + cmd.dstW * .5;
|
|
1296
|
+
const cy = cmd.dstY + cmd.dstH * .5;
|
|
1297
|
+
current.save();
|
|
1298
|
+
current.translate(cx, cy);
|
|
1299
|
+
current.scale(cmd.flipX ? -1 : 1, cmd.flipY ? -1 : 1);
|
|
1300
|
+
current.translate(-cx, -cy);
|
|
1301
|
+
current.drawImageRect(image, srcR, dstR, sPaint);
|
|
1302
|
+
current.restore();
|
|
1303
|
+
} else current.drawImageRect(image, srcR, dstR, sPaint);
|
|
1286
1304
|
}
|
|
1287
1305
|
sPaint.setAlphaf(1);
|
|
1306
|
+
if (blendAdditive) sPaint.setBlendMode?.(BLEND_SRC_OVER);
|
|
1288
1307
|
return;
|
|
1289
1308
|
}
|
|
1290
1309
|
ensurePoolCapacity(n);
|
|
1291
1310
|
fillSrcRects(commands, n);
|
|
1292
1311
|
if (effect?.type === "outline") {
|
|
1312
|
+
if (blendAdditive) {
|
|
1313
|
+
outlinePaint?.setBlendMode?.(BLEND_PLUS);
|
|
1314
|
+
defaultPaint.setBlendMode?.(BLEND_PLUS);
|
|
1315
|
+
}
|
|
1293
1316
|
drawOutlined(current, image, commands, n, effect, alpha);
|
|
1317
|
+
if (blendAdditive) {
|
|
1318
|
+
outlinePaint?.setBlendMode?.(BLEND_SRC_OVER);
|
|
1319
|
+
defaultPaint.setBlendMode?.(BLEND_SRC_OVER);
|
|
1320
|
+
}
|
|
1294
1321
|
return;
|
|
1295
1322
|
}
|
|
1296
1323
|
fillXforms(commands, n, 0, 0);
|
|
1297
1324
|
const srcs = getCachedSrcSlice(n);
|
|
1298
1325
|
const dsts = getCachedXformSlice(n);
|
|
1299
1326
|
const paint = effect === void 0 ? defaultPaint : resolveEffectPaint(effect);
|
|
1327
|
+
if (blendAdditive) paint.setBlendMode?.(BLEND_PLUS);
|
|
1300
1328
|
paint.setAlphaf(alpha);
|
|
1301
1329
|
current.drawAtlas(image, srcs, dsts, paint);
|
|
1302
1330
|
paint.setAlphaf(1);
|
|
1331
|
+
if (blendAdditive) paint.setBlendMode?.(BLEND_SRC_OVER);
|
|
1303
1332
|
},
|
|
1304
1333
|
setPostFxBinding(runtime, chain) {
|
|
1305
1334
|
_boundRuntime = runtime;
|
|
@@ -1427,6 +1456,70 @@ function fillCustomUniforms(effect, shader, out) {
|
|
|
1427
1456
|
}
|
|
1428
1457
|
}
|
|
1429
1458
|
//#endregion
|
|
1459
|
+
//#region src/nine-slice.ts
|
|
1460
|
+
/**
|
|
1461
|
+
* Draw a sprite using a 9-slice (scale-9) layout.
|
|
1462
|
+
*
|
|
1463
|
+
* The source frame is divided into a 3×3 grid by `inset` pixels from each
|
|
1464
|
+
* edge. Corners draw at natural size; edges stretch along one axis; the
|
|
1465
|
+
* centre stretches in both axes. This preserves crisp borders at any
|
|
1466
|
+
* destination size — the canonical use case is UI panels and buttons.
|
|
1467
|
+
*
|
|
1468
|
+
* All 9 slices use the `drawImageRect` (`stretch=true`) path, so they are
|
|
1469
|
+
* NOT batched with regular Atlas sprites; they form their own consecutive run
|
|
1470
|
+
* in the batch. The function calls `batch.drawRaw()` for each non-degenerate
|
|
1471
|
+
* slice (a slice with zero source or destination area is skipped silently).
|
|
1472
|
+
*
|
|
1473
|
+
* **Coarse culling:** if `batch.cullBounds` is set, the entire nine-slice is
|
|
1474
|
+
* skipped when the destination rectangle `[x, y, x+w, y+h]` does not
|
|
1475
|
+
* intersect the bounds. Individual slice culling is not performed — the
|
|
1476
|
+
* caller should set tight bounds if per-slice savings matter.
|
|
1477
|
+
*
|
|
1478
|
+
* @param batch Target SpriteBatch — must outlive this call.
|
|
1479
|
+
* @param image Source texture.
|
|
1480
|
+
* @param frame Sub-region of the texture to use as the nine-slice source.
|
|
1481
|
+
* @param x Destination left edge (screen / world coords).
|
|
1482
|
+
* @param y Destination top edge.
|
|
1483
|
+
* @param w Destination width.
|
|
1484
|
+
* @param h Destination height.
|
|
1485
|
+
* @param inset Border size in source-texture pixels, applied uniformly to
|
|
1486
|
+
* all four edges. Must be ≥ 0; clamped to half the frame
|
|
1487
|
+
* dimension so corners never overlap.
|
|
1488
|
+
* @param effect Optional sprite effect applied to every slice identically.
|
|
1489
|
+
* @param alpha Per-instance opacity multiplier in [0, 1]. Default 1.
|
|
1490
|
+
*/
|
|
1491
|
+
function drawNineSlice(batch, image, frame, x, y, w, h, inset, effect, alpha = 1) {
|
|
1492
|
+
const cb = batch.cullBounds;
|
|
1493
|
+
if (cb !== null) {
|
|
1494
|
+
if (x + w <= cb.minX || x >= cb.maxX || y + h <= cb.minY || y >= cb.maxY) return;
|
|
1495
|
+
}
|
|
1496
|
+
const si = Math.min(inset, frame.w * .5, frame.h * .5);
|
|
1497
|
+
const di = Math.min(si, w * .5, h * .5);
|
|
1498
|
+
const fx = frame.x;
|
|
1499
|
+
const fy = frame.y;
|
|
1500
|
+
const fw = frame.w;
|
|
1501
|
+
const fh = frame.h;
|
|
1502
|
+
const srcMidW = fw - 2 * si;
|
|
1503
|
+
const srcMidH = fh - 2 * si;
|
|
1504
|
+
const dstMidW = w - 2 * di;
|
|
1505
|
+
const dstMidH = h - 2 * di;
|
|
1506
|
+
const a = alpha;
|
|
1507
|
+
const e = effect;
|
|
1508
|
+
_slice(batch, image, fx, fy, si, si, x, y, di, di, e, a);
|
|
1509
|
+
_slice(batch, image, fx + si, fy, srcMidW, si, x + di, y, dstMidW, di, e, a);
|
|
1510
|
+
_slice(batch, image, fx + fw - si, fy, si, si, x + w - di, y, di, di, e, a);
|
|
1511
|
+
_slice(batch, image, fx, fy + si, si, srcMidH, x, y + di, di, dstMidH, e, a);
|
|
1512
|
+
_slice(batch, image, fx + si, fy + si, srcMidW, srcMidH, x + di, y + di, dstMidW, dstMidH, e, a);
|
|
1513
|
+
_slice(batch, image, fx + fw - si, fy + si, si, srcMidH, x + w - di, y + di, di, dstMidH, e, a);
|
|
1514
|
+
_slice(batch, image, fx, fy + fh - si, si, si, x, y + h - di, di, di, e, a);
|
|
1515
|
+
_slice(batch, image, fx + si, fy + fh - si, srcMidW, si, x + di, y + h - di, dstMidW, di, e, a);
|
|
1516
|
+
_slice(batch, image, fx + fw - si, fy + fh - si, si, si, x + w - di, y + h - di, di, di, e, a);
|
|
1517
|
+
}
|
|
1518
|
+
function _slice(batch, image, sx, sy, sw, sh, dx, dy, dw, dh, effect, alpha) {
|
|
1519
|
+
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) return;
|
|
1520
|
+
batch.drawRaw(image, sx, sy, sw, sh, dx, dy, dw, dh, effect, 0, 0, alpha, 0, true);
|
|
1521
|
+
}
|
|
1522
|
+
//#endregion
|
|
1430
1523
|
//#region src/render-system.ts
|
|
1431
1524
|
/**
|
|
1432
1525
|
* Create a render system function.
|
|
@@ -1450,7 +1543,9 @@ function createRenderSystem(world, query, positionDef, spriteDef, batch) {
|
|
|
1450
1543
|
const entries = [];
|
|
1451
1544
|
return () => {
|
|
1452
1545
|
let i = 0;
|
|
1453
|
-
|
|
1546
|
+
const ids = query.entities;
|
|
1547
|
+
for (let n = 0; n < ids.length; n++) {
|
|
1548
|
+
const entityId = ids[n];
|
|
1454
1549
|
const sprite = world.get(entityId, spriteDef);
|
|
1455
1550
|
if (!sprite) continue;
|
|
1456
1551
|
if (i >= entries.length) entries.push({
|
|
@@ -1471,7 +1566,7 @@ function createRenderSystem(world, query, positionDef, spriteDef, batch) {
|
|
|
1471
1566
|
const sprite = world.get(entry.entityId, spriteDef);
|
|
1472
1567
|
if (!pos || !sprite) continue;
|
|
1473
1568
|
const anchor = sprite.anchor;
|
|
1474
|
-
batch.draw(sprite.image, sprite.frame, pos.x, pos.y, sprite.width, sprite.height, sprite.effect, anchor ? anchor.x : 0, anchor ? anchor.y : 0, sprite.alpha ?? 1, sprite.rotation ?? 0, sprite.stretch ?? false);
|
|
1569
|
+
batch.draw(sprite.image, sprite.frame, pos.x, pos.y, sprite.width, sprite.height, sprite.effect, anchor ? anchor.x : 0, anchor ? anchor.y : 0, sprite.alpha ?? 1, sprite.rotation ?? 0, sprite.stretch ?? false, sprite.blend ?? "normal", sprite.flipX ?? false, sprite.flipY ?? false);
|
|
1475
1570
|
}
|
|
1476
1571
|
};
|
|
1477
1572
|
}
|
|
@@ -1578,6 +1673,7 @@ var SpriteBatch = class {
|
|
|
1578
1673
|
_pool = [];
|
|
1579
1674
|
_batches = [];
|
|
1580
1675
|
_allocCount = 0;
|
|
1676
|
+
_cull = null;
|
|
1581
1677
|
/** Number of queued draw commands. */
|
|
1582
1678
|
get count() {
|
|
1583
1679
|
return this._commands.length;
|
|
@@ -1587,6 +1683,34 @@ var SpriteBatch = class {
|
|
|
1587
1683
|
get allocCount() {
|
|
1588
1684
|
return this._allocCount;
|
|
1589
1685
|
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Current cull bounds, or `null` when culling is disabled.
|
|
1688
|
+
* Read by `drawNineSlice` for coarse AABB precheck.
|
|
1689
|
+
* Set via `setCullBounds()` / `clearCullBounds()`.
|
|
1690
|
+
*/
|
|
1691
|
+
get cullBounds() {
|
|
1692
|
+
return this._cull;
|
|
1693
|
+
}
|
|
1694
|
+
/** Set the viewport cull bounds for this batch.
|
|
1695
|
+
* Zero-alloc in steady state — the internal object is allocated once and then mutated. */
|
|
1696
|
+
setCullBounds(minX, minY, maxX, maxY) {
|
|
1697
|
+
if (this._cull === null) this._cull = {
|
|
1698
|
+
minX,
|
|
1699
|
+
minY,
|
|
1700
|
+
maxX,
|
|
1701
|
+
maxY
|
|
1702
|
+
};
|
|
1703
|
+
else {
|
|
1704
|
+
this._cull.minX = minX;
|
|
1705
|
+
this._cull.minY = minY;
|
|
1706
|
+
this._cull.maxX = maxX;
|
|
1707
|
+
this._cull.maxY = maxY;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
/** Disable viewport culling (e.g. during UI rendering). */
|
|
1711
|
+
clearCullBounds() {
|
|
1712
|
+
this._cull = null;
|
|
1713
|
+
}
|
|
1590
1714
|
/** Queue a sprite draw from a frame.
|
|
1591
1715
|
*
|
|
1592
1716
|
* `anchorX` / `anchorY` define the pivot point of the sprite as a
|
|
@@ -1595,7 +1719,7 @@ var SpriteBatch = class {
|
|
|
1595
1719
|
* then refer to the anchor point instead of the top-left corner.
|
|
1596
1720
|
*
|
|
1597
1721
|
* `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */
|
|
1598
|
-
draw(image, frame, dstX, dstY, dstW, dstH, effect, anchorX = 0, anchorY = 0, alpha = 1, rotation = 0, stretch = false) {
|
|
1722
|
+
draw(image, frame, dstX, dstY, dstW, dstH, effect, anchorX = 0, anchorY = 0, alpha = 1, rotation = 0, stretch = false, blend = "normal", flipX = false, flipY = false) {
|
|
1599
1723
|
const cmd = this._acquire();
|
|
1600
1724
|
const w = dstW ?? frame.w;
|
|
1601
1725
|
const h = dstH ?? frame.h;
|
|
@@ -1614,13 +1738,16 @@ var SpriteBatch = class {
|
|
|
1614
1738
|
cmd.anchorX = anchorX;
|
|
1615
1739
|
cmd.anchorY = anchorY;
|
|
1616
1740
|
cmd.stretch = stretch;
|
|
1741
|
+
cmd.blend = blend;
|
|
1742
|
+
cmd.flipX = flipX;
|
|
1743
|
+
cmd.flipY = flipY;
|
|
1617
1744
|
this._commands.push(cmd);
|
|
1618
1745
|
}
|
|
1619
1746
|
/** Queue a raw sprite draw with explicit src/dst rects. `dstX` / `dstY`
|
|
1620
1747
|
* refer to the anchor point — offset by `anchorX * dstW` / `anchorY * dstH`.
|
|
1621
1748
|
*
|
|
1622
1749
|
* `alpha` is a per-instance opacity multiplier in [0, 1]. Default 1. */
|
|
1623
|
-
drawRaw(image, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH, effect, anchorX = 0, anchorY = 0, alpha = 1, rotation = 0, stretch = false) {
|
|
1750
|
+
drawRaw(image, srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH, effect, anchorX = 0, anchorY = 0, alpha = 1, rotation = 0, stretch = false, blend = "normal", flipX = false, flipY = false) {
|
|
1624
1751
|
const cmd = this._acquire();
|
|
1625
1752
|
cmd.image = image;
|
|
1626
1753
|
cmd.srcX = srcX;
|
|
@@ -1637,6 +1764,9 @@ var SpriteBatch = class {
|
|
|
1637
1764
|
cmd.anchorX = anchorX;
|
|
1638
1765
|
cmd.anchorY = anchorY;
|
|
1639
1766
|
cmd.stretch = stretch;
|
|
1767
|
+
cmd.blend = blend;
|
|
1768
|
+
cmd.flipX = flipX;
|
|
1769
|
+
cmd.flipY = flipY;
|
|
1640
1770
|
this._commands.push(cmd);
|
|
1641
1771
|
}
|
|
1642
1772
|
/**
|
|
@@ -1660,12 +1790,15 @@ var SpriteBatch = class {
|
|
|
1660
1790
|
effect: void 0,
|
|
1661
1791
|
alpha: 1,
|
|
1662
1792
|
stretch: false,
|
|
1793
|
+
blend: "normal",
|
|
1794
|
+
flipX: false,
|
|
1795
|
+
flipY: false,
|
|
1663
1796
|
commands: []
|
|
1664
1797
|
};
|
|
1665
1798
|
this._batches[0] = run;
|
|
1666
1799
|
}
|
|
1667
1800
|
for (const cmd of this._commands) {
|
|
1668
|
-
if (run.commands.length > 0 && (cmd.image !== run.image || cmd.effect !== run.effect || cmd.alpha !== run.alpha || cmd.stretch !== run.stretch)) {
|
|
1801
|
+
if (run.commands.length > 0 && (cmd.image !== run.image || cmd.effect !== run.effect || cmd.alpha !== run.alpha || cmd.stretch !== run.stretch || cmd.blend !== run.blend || cmd.flipX !== run.flipX || cmd.flipY !== run.flipY)) {
|
|
1669
1802
|
drawAtlas(run.image, run.commands);
|
|
1670
1803
|
if (renderer) {
|
|
1671
1804
|
renderer.recordDrawCall();
|
|
@@ -1677,6 +1810,9 @@ var SpriteBatch = class {
|
|
|
1677
1810
|
run.effect = cmd.effect;
|
|
1678
1811
|
run.alpha = cmd.alpha;
|
|
1679
1812
|
run.stretch = cmd.stretch;
|
|
1813
|
+
run.blend = cmd.blend;
|
|
1814
|
+
run.flipX = cmd.flipX;
|
|
1815
|
+
run.flipY = cmd.flipY;
|
|
1680
1816
|
run.commands.push(cmd);
|
|
1681
1817
|
}
|
|
1682
1818
|
if (run.commands.length > 0) {
|
|
@@ -1713,7 +1849,10 @@ var SpriteBatch = class {
|
|
|
1713
1849
|
rotation: 0,
|
|
1714
1850
|
anchorX: 0,
|
|
1715
1851
|
anchorY: 0,
|
|
1716
|
-
stretch: false
|
|
1852
|
+
stretch: false,
|
|
1853
|
+
blend: "normal",
|
|
1854
|
+
flipX: false,
|
|
1855
|
+
flipY: false
|
|
1717
1856
|
};
|
|
1718
1857
|
}
|
|
1719
1858
|
};
|
|
@@ -1797,6 +1936,7 @@ exports.caveInterior = caveInterior;
|
|
|
1797
1936
|
exports.createFrameRenderer = createFrameRenderer;
|
|
1798
1937
|
exports.createRenderSystem = createRenderSystem;
|
|
1799
1938
|
exports.custom = custom;
|
|
1939
|
+
exports.drawNineSlice = drawNineSlice;
|
|
1800
1940
|
exports.drawOrderCompare = drawOrderCompare;
|
|
1801
1941
|
exports.drawText = drawText;
|
|
1802
1942
|
exports.dropShadow = dropShadow;
|