emf-converter 4.14.0 → 4.15.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/browser.js CHANGED
@@ -16513,6 +16513,45 @@ function lerpUniformPathArgb(a, b, t) {
16513
16513
  };
16514
16514
  return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16515
16515
  }
16516
+ function pathGradientQuantum(boundary, matrix = IDENTITY) {
16517
+ let x0 = Infinity;
16518
+ let y0 = Infinity;
16519
+ let x1 = -Infinity;
16520
+ let y1 = -Infinity;
16521
+ for (const p of boundary) {
16522
+ const x = matrix[0] * p.x + matrix[2] * p.y + matrix[4];
16523
+ const y = matrix[1] * p.x + matrix[3] * p.y + matrix[5];
16524
+ x0 = Math.min(x0, x);
16525
+ y0 = Math.min(y0, y);
16526
+ x1 = Math.max(x1, x);
16527
+ y1 = Math.max(y1, y);
16528
+ }
16529
+ const steps = Math.ceil(2 * Math.hypot(x1 - x0, y1 - y0));
16530
+ return Number.isFinite(steps) && steps > 0 ? steps : 0;
16531
+ }
16532
+ var TIE_EPSILON = 1e-7;
16533
+ function quantisedPathArgb(surround, centre, t, quantum) {
16534
+ const scaled2 = t * quantum;
16535
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16536
+ const k = tie ? scaled2 : Math.floor(scaled2 + 0.5);
16537
+ const fromBoundary = t < 0.5;
16538
+ const w = (fromBoundary ? k : quantum - k) / quantum;
16539
+ const aS = surround >>> 24 & 255;
16540
+ const aC = centre >>> 24 & 255;
16541
+ const mix2 = (s, c) => fromBoundary ? s + (c - s) * w : c + (s - c) * w;
16542
+ const alpha = Math.floor(mix2(aS, aC) + 0.5);
16543
+ if (alpha <= 0) return 0;
16544
+ const ch = (shift) => {
16545
+ const p = Math.floor(mix2((surround >>> shift & 255) * aS / 255, (centre >>> shift & 255) * aC / 255) + 0.5);
16546
+ return Math.min(255, Math.max(0, Math.round(p * 255 / alpha)));
16547
+ };
16548
+ return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16549
+ }
16550
+ function quantisedPosition(pos, quantum) {
16551
+ const scaled2 = pos * quantum;
16552
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16553
+ return (tie ? scaled2 : Math.floor(scaled2 + 0.5)) / quantum;
16554
+ }
16516
16555
  function piecewise2(xs, ys, x) {
16517
16556
  const n = Math.min(xs.length, ys.length);
16518
16557
  if (n === 0) {
@@ -16547,11 +16586,13 @@ function stopColorAt(stops, t) {
16547
16586
  }
16548
16587
  return stops[stops.length - 1].argb;
16549
16588
  }
16550
- function pathGradientColorAt(shape, x, y, focusPoint) {
16589
+ function pathGradientColorAt(shape, x, y, focusPoint, quantum) {
16551
16590
  const { center, boundary, boundaryArgb } = shape;
16552
16591
  const n = boundary.length;
16553
16592
  const px = x - center.x;
16554
16593
  const py = y - center.y;
16594
+ const ux = (focusPoint?.x ?? x) - center.x;
16595
+ const uy = (focusPoint?.y ?? y) - center.y;
16555
16596
  let found = -1;
16556
16597
  let bestS = 0;
16557
16598
  let bestU = 0;
@@ -16573,8 +16614,10 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16573
16614
  continue;
16574
16615
  }
16575
16616
  found = i;
16576
- bestS = Math.max(0, alpha);
16577
- bestU = alpha > 0 ? Math.min(1, Math.max(0, beta / alpha)) : 0;
16617
+ const alphaU = quantum ? (ux * ey - uy * ex) / det : alpha;
16618
+ const betaU = quantum ? (ax * uy - ay * ux) / det : beta;
16619
+ bestS = Math.max(0, alphaU);
16620
+ bestU = alphaU > 0 ? Math.min(1, Math.max(0, betaU / alphaU)) : 0;
16578
16621
  }
16579
16622
  let s = found < 0 ? Infinity : bestS;
16580
16623
  if (shape.focus) {
@@ -16649,7 +16692,8 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16649
16692
  }
16650
16693
  }
16651
16694
  if (found < 0 || !Number.isFinite(s)) return null;
16652
- const pos = 1 - s;
16695
+ const exactPos = 1 - s;
16696
+ const pos = quantum && quantum > 0 ? quantisedPosition(exactPos, quantum) : exactPos;
16653
16697
  if (shape.preset && shape.preset.positions.length > 0) {
16654
16698
  const { positions, argb } = shape.preset;
16655
16699
  if (pos <= positions[0]) {
@@ -16668,6 +16712,9 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16668
16712
  const surround = lerpArgb(c0, c1, bestU);
16669
16713
  const factor = shape.blend ? piecewise2(shape.blend.positions, shape.blend.factors, pos) : pos;
16670
16714
  const t = Math.min(1, Math.max(0, factor));
16715
+ if (quantum && quantum > 0 && !shape.blend && boundaryArgb.every((color) => color === boundaryArgb[0])) {
16716
+ return quantisedPathArgb(surround, shape.centerArgb, Math.min(1, Math.max(0, exactPos)), quantum);
16717
+ }
16671
16718
  return surround >>> 24 !== shape.centerArgb >>> 24 && boundaryArgb.every((color) => color === boundaryArgb[0]) ? lerpUniformPathArgb(surround, shape.centerArgb, t) : lerpArgb(surround, shape.centerArgb, t);
16672
16719
  }
16673
16720
  var MAX_TILE = 2048;
@@ -31225,6 +31272,21 @@ function foldIntoTile(v, origin, size, mirror2, lag) {
31225
31272
  }
31226
31273
  return origin + local;
31227
31274
  }
31275
+ function pixelAligned(boundary, m) {
31276
+ let x0 = Infinity;
31277
+ let y0 = Infinity;
31278
+ let x1 = -Infinity;
31279
+ let y1 = -Infinity;
31280
+ for (const p of boundary) {
31281
+ const x = m[0] * p.x + m[2] * p.y + m[4];
31282
+ const y = m[1] * p.x + m[3] * p.y + m[5];
31283
+ x0 = Math.min(x0, x);
31284
+ y0 = Math.min(y0, y);
31285
+ x1 = Math.max(x1, x);
31286
+ y1 = Math.max(y1, y);
31287
+ }
31288
+ return [x0, y0, x1, y1].every((v) => Math.abs(v - Math.round(v)) < 1e-6);
31289
+ }
31228
31290
  function pathGradientSampler(shape, wrap, device) {
31229
31291
  const box = boundaryBox2(shape.boundary);
31230
31292
  const full = mulMatrix(device, shape.transform ?? IDENTITY4);
@@ -31232,6 +31294,10 @@ function pathGradientSampler(shape, wrap, device) {
31232
31294
  if (!box || !inv2) {
31233
31295
  return null;
31234
31296
  }
31297
+ const anisotropicFocus = !!shape.focus && Math.abs(shape.focus.x - shape.focus.y) > 1e-9;
31298
+ const uniformSurround = shape.boundaryArgb.every((color) => color === shape.boundaryArgb[0]);
31299
+ const smooth = !uniformSurround && !shape.preset || anisotropicFocus || wrap !== "clamp" && !pixelAligned(shape.boundary, full);
31300
+ const quantum = smooth ? 0 : pathGradientQuantum(shape.boundary, full);
31235
31301
  const pxX = Math.hypot(full[0], full[1]);
31236
31302
  const pxY = Math.hypot(full[2], full[3]);
31237
31303
  const lagX = pxX > 0 ? 1 / pxX : 0;
@@ -31251,8 +31317,8 @@ function pathGradientSampler(shape, wrap, device) {
31251
31317
  by = foldIntoTile(by, box.y, box.h, mirrorY, lagY);
31252
31318
  }
31253
31319
  if (wrap === "clamp" && by >= box.y + box.h) continue;
31254
- const focusPoint = bias && shape.focus?.x === 0 ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31255
- const c = pathGradientColorAt(shape, bx, by, focusPoint);
31320
+ const focusPoint = bias ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31321
+ const c = pathGradientColorAt(shape, bx, by, focusPoint, quantum);
31256
31322
  if (c === null) {
31257
31323
  continue;
31258
31324
  }
package/dist/browser.mjs CHANGED
@@ -16511,6 +16511,45 @@ function lerpUniformPathArgb(a, b, t) {
16511
16511
  };
16512
16512
  return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16513
16513
  }
16514
+ function pathGradientQuantum(boundary, matrix = IDENTITY) {
16515
+ let x0 = Infinity;
16516
+ let y0 = Infinity;
16517
+ let x1 = -Infinity;
16518
+ let y1 = -Infinity;
16519
+ for (const p of boundary) {
16520
+ const x = matrix[0] * p.x + matrix[2] * p.y + matrix[4];
16521
+ const y = matrix[1] * p.x + matrix[3] * p.y + matrix[5];
16522
+ x0 = Math.min(x0, x);
16523
+ y0 = Math.min(y0, y);
16524
+ x1 = Math.max(x1, x);
16525
+ y1 = Math.max(y1, y);
16526
+ }
16527
+ const steps = Math.ceil(2 * Math.hypot(x1 - x0, y1 - y0));
16528
+ return Number.isFinite(steps) && steps > 0 ? steps : 0;
16529
+ }
16530
+ var TIE_EPSILON = 1e-7;
16531
+ function quantisedPathArgb(surround, centre, t, quantum) {
16532
+ const scaled2 = t * quantum;
16533
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16534
+ const k = tie ? scaled2 : Math.floor(scaled2 + 0.5);
16535
+ const fromBoundary = t < 0.5;
16536
+ const w = (fromBoundary ? k : quantum - k) / quantum;
16537
+ const aS = surround >>> 24 & 255;
16538
+ const aC = centre >>> 24 & 255;
16539
+ const mix2 = (s, c) => fromBoundary ? s + (c - s) * w : c + (s - c) * w;
16540
+ const alpha = Math.floor(mix2(aS, aC) + 0.5);
16541
+ if (alpha <= 0) return 0;
16542
+ const ch = (shift) => {
16543
+ const p = Math.floor(mix2((surround >>> shift & 255) * aS / 255, (centre >>> shift & 255) * aC / 255) + 0.5);
16544
+ return Math.min(255, Math.max(0, Math.round(p * 255 / alpha)));
16545
+ };
16546
+ return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16547
+ }
16548
+ function quantisedPosition(pos, quantum) {
16549
+ const scaled2 = pos * quantum;
16550
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16551
+ return (tie ? scaled2 : Math.floor(scaled2 + 0.5)) / quantum;
16552
+ }
16514
16553
  function piecewise2(xs, ys, x) {
16515
16554
  const n = Math.min(xs.length, ys.length);
16516
16555
  if (n === 0) {
@@ -16545,11 +16584,13 @@ function stopColorAt(stops, t) {
16545
16584
  }
16546
16585
  return stops[stops.length - 1].argb;
16547
16586
  }
16548
- function pathGradientColorAt(shape, x, y, focusPoint) {
16587
+ function pathGradientColorAt(shape, x, y, focusPoint, quantum) {
16549
16588
  const { center, boundary, boundaryArgb } = shape;
16550
16589
  const n = boundary.length;
16551
16590
  const px = x - center.x;
16552
16591
  const py = y - center.y;
16592
+ const ux = (focusPoint?.x ?? x) - center.x;
16593
+ const uy = (focusPoint?.y ?? y) - center.y;
16553
16594
  let found = -1;
16554
16595
  let bestS = 0;
16555
16596
  let bestU = 0;
@@ -16571,8 +16612,10 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16571
16612
  continue;
16572
16613
  }
16573
16614
  found = i;
16574
- bestS = Math.max(0, alpha);
16575
- bestU = alpha > 0 ? Math.min(1, Math.max(0, beta / alpha)) : 0;
16615
+ const alphaU = quantum ? (ux * ey - uy * ex) / det : alpha;
16616
+ const betaU = quantum ? (ax * uy - ay * ux) / det : beta;
16617
+ bestS = Math.max(0, alphaU);
16618
+ bestU = alphaU > 0 ? Math.min(1, Math.max(0, betaU / alphaU)) : 0;
16576
16619
  }
16577
16620
  let s = found < 0 ? Infinity : bestS;
16578
16621
  if (shape.focus) {
@@ -16647,7 +16690,8 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16647
16690
  }
16648
16691
  }
16649
16692
  if (found < 0 || !Number.isFinite(s)) return null;
16650
- const pos = 1 - s;
16693
+ const exactPos = 1 - s;
16694
+ const pos = quantum && quantum > 0 ? quantisedPosition(exactPos, quantum) : exactPos;
16651
16695
  if (shape.preset && shape.preset.positions.length > 0) {
16652
16696
  const { positions, argb } = shape.preset;
16653
16697
  if (pos <= positions[0]) {
@@ -16666,6 +16710,9 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16666
16710
  const surround = lerpArgb(c0, c1, bestU);
16667
16711
  const factor = shape.blend ? piecewise2(shape.blend.positions, shape.blend.factors, pos) : pos;
16668
16712
  const t = Math.min(1, Math.max(0, factor));
16713
+ if (quantum && quantum > 0 && !shape.blend && boundaryArgb.every((color) => color === boundaryArgb[0])) {
16714
+ return quantisedPathArgb(surround, shape.centerArgb, Math.min(1, Math.max(0, exactPos)), quantum);
16715
+ }
16669
16716
  return surround >>> 24 !== shape.centerArgb >>> 24 && boundaryArgb.every((color) => color === boundaryArgb[0]) ? lerpUniformPathArgb(surround, shape.centerArgb, t) : lerpArgb(surround, shape.centerArgb, t);
16670
16717
  }
16671
16718
  var MAX_TILE = 2048;
@@ -31223,6 +31270,21 @@ function foldIntoTile(v, origin, size, mirror2, lag) {
31223
31270
  }
31224
31271
  return origin + local;
31225
31272
  }
31273
+ function pixelAligned(boundary, m) {
31274
+ let x0 = Infinity;
31275
+ let y0 = Infinity;
31276
+ let x1 = -Infinity;
31277
+ let y1 = -Infinity;
31278
+ for (const p of boundary) {
31279
+ const x = m[0] * p.x + m[2] * p.y + m[4];
31280
+ const y = m[1] * p.x + m[3] * p.y + m[5];
31281
+ x0 = Math.min(x0, x);
31282
+ y0 = Math.min(y0, y);
31283
+ x1 = Math.max(x1, x);
31284
+ y1 = Math.max(y1, y);
31285
+ }
31286
+ return [x0, y0, x1, y1].every((v) => Math.abs(v - Math.round(v)) < 1e-6);
31287
+ }
31226
31288
  function pathGradientSampler(shape, wrap, device) {
31227
31289
  const box = boundaryBox2(shape.boundary);
31228
31290
  const full = mulMatrix(device, shape.transform ?? IDENTITY4);
@@ -31230,6 +31292,10 @@ function pathGradientSampler(shape, wrap, device) {
31230
31292
  if (!box || !inv2) {
31231
31293
  return null;
31232
31294
  }
31295
+ const anisotropicFocus = !!shape.focus && Math.abs(shape.focus.x - shape.focus.y) > 1e-9;
31296
+ const uniformSurround = shape.boundaryArgb.every((color) => color === shape.boundaryArgb[0]);
31297
+ const smooth = !uniformSurround && !shape.preset || anisotropicFocus || wrap !== "clamp" && !pixelAligned(shape.boundary, full);
31298
+ const quantum = smooth ? 0 : pathGradientQuantum(shape.boundary, full);
31233
31299
  const pxX = Math.hypot(full[0], full[1]);
31234
31300
  const pxY = Math.hypot(full[2], full[3]);
31235
31301
  const lagX = pxX > 0 ? 1 / pxX : 0;
@@ -31249,8 +31315,8 @@ function pathGradientSampler(shape, wrap, device) {
31249
31315
  by = foldIntoTile(by, box.y, box.h, mirrorY, lagY);
31250
31316
  }
31251
31317
  if (wrap === "clamp" && by >= box.y + box.h) continue;
31252
- const focusPoint = bias && shape.focus?.x === 0 ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31253
- const c = pathGradientColorAt(shape, bx, by, focusPoint);
31318
+ const focusPoint = bias ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31319
+ const c = pathGradientColorAt(shape, bx, by, focusPoint, quantum);
31254
31320
  if (c === null) {
31255
31321
  continue;
31256
31322
  }
package/dist/index.js CHANGED
@@ -16514,6 +16514,45 @@ function lerpUniformPathArgb(a, b, t) {
16514
16514
  };
16515
16515
  return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16516
16516
  }
16517
+ function pathGradientQuantum(boundary, matrix = IDENTITY) {
16518
+ let x0 = Infinity;
16519
+ let y0 = Infinity;
16520
+ let x1 = -Infinity;
16521
+ let y1 = -Infinity;
16522
+ for (const p of boundary) {
16523
+ const x = matrix[0] * p.x + matrix[2] * p.y + matrix[4];
16524
+ const y = matrix[1] * p.x + matrix[3] * p.y + matrix[5];
16525
+ x0 = Math.min(x0, x);
16526
+ y0 = Math.min(y0, y);
16527
+ x1 = Math.max(x1, x);
16528
+ y1 = Math.max(y1, y);
16529
+ }
16530
+ const steps = Math.ceil(2 * Math.hypot(x1 - x0, y1 - y0));
16531
+ return Number.isFinite(steps) && steps > 0 ? steps : 0;
16532
+ }
16533
+ var TIE_EPSILON = 1e-7;
16534
+ function quantisedPathArgb(surround, centre, t, quantum) {
16535
+ const scaled2 = t * quantum;
16536
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16537
+ const k = tie ? scaled2 : Math.floor(scaled2 + 0.5);
16538
+ const fromBoundary = t < 0.5;
16539
+ const w = (fromBoundary ? k : quantum - k) / quantum;
16540
+ const aS = surround >>> 24 & 255;
16541
+ const aC = centre >>> 24 & 255;
16542
+ const mix2 = (s, c) => fromBoundary ? s + (c - s) * w : c + (s - c) * w;
16543
+ const alpha = Math.floor(mix2(aS, aC) + 0.5);
16544
+ if (alpha <= 0) return 0;
16545
+ const ch = (shift) => {
16546
+ const p = Math.floor(mix2((surround >>> shift & 255) * aS / 255, (centre >>> shift & 255) * aC / 255) + 0.5);
16547
+ return Math.min(255, Math.max(0, Math.round(p * 255 / alpha)));
16548
+ };
16549
+ return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16550
+ }
16551
+ function quantisedPosition(pos, quantum) {
16552
+ const scaled2 = pos * quantum;
16553
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16554
+ return (tie ? scaled2 : Math.floor(scaled2 + 0.5)) / quantum;
16555
+ }
16517
16556
  function piecewise2(xs, ys, x) {
16518
16557
  const n = Math.min(xs.length, ys.length);
16519
16558
  if (n === 0) {
@@ -16548,11 +16587,13 @@ function stopColorAt(stops, t) {
16548
16587
  }
16549
16588
  return stops[stops.length - 1].argb;
16550
16589
  }
16551
- function pathGradientColorAt(shape, x, y, focusPoint) {
16590
+ function pathGradientColorAt(shape, x, y, focusPoint, quantum) {
16552
16591
  const { center, boundary, boundaryArgb } = shape;
16553
16592
  const n = boundary.length;
16554
16593
  const px = x - center.x;
16555
16594
  const py = y - center.y;
16595
+ const ux = (focusPoint?.x ?? x) - center.x;
16596
+ const uy = (focusPoint?.y ?? y) - center.y;
16556
16597
  let found = -1;
16557
16598
  let bestS = 0;
16558
16599
  let bestU = 0;
@@ -16574,8 +16615,10 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16574
16615
  continue;
16575
16616
  }
16576
16617
  found = i;
16577
- bestS = Math.max(0, alpha);
16578
- bestU = alpha > 0 ? Math.min(1, Math.max(0, beta / alpha)) : 0;
16618
+ const alphaU = quantum ? (ux * ey - uy * ex) / det : alpha;
16619
+ const betaU = quantum ? (ax * uy - ay * ux) / det : beta;
16620
+ bestS = Math.max(0, alphaU);
16621
+ bestU = alphaU > 0 ? Math.min(1, Math.max(0, betaU / alphaU)) : 0;
16579
16622
  }
16580
16623
  let s = found < 0 ? Infinity : bestS;
16581
16624
  if (shape.focus) {
@@ -16650,7 +16693,8 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16650
16693
  }
16651
16694
  }
16652
16695
  if (found < 0 || !Number.isFinite(s)) return null;
16653
- const pos = 1 - s;
16696
+ const exactPos = 1 - s;
16697
+ const pos = quantum && quantum > 0 ? quantisedPosition(exactPos, quantum) : exactPos;
16654
16698
  if (shape.preset && shape.preset.positions.length > 0) {
16655
16699
  const { positions, argb } = shape.preset;
16656
16700
  if (pos <= positions[0]) {
@@ -16669,6 +16713,9 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16669
16713
  const surround = lerpArgb(c0, c1, bestU);
16670
16714
  const factor = shape.blend ? piecewise2(shape.blend.positions, shape.blend.factors, pos) : pos;
16671
16715
  const t = Math.min(1, Math.max(0, factor));
16716
+ if (quantum && quantum > 0 && !shape.blend && boundaryArgb.every((color) => color === boundaryArgb[0])) {
16717
+ return quantisedPathArgb(surround, shape.centerArgb, Math.min(1, Math.max(0, exactPos)), quantum);
16718
+ }
16672
16719
  return surround >>> 24 !== shape.centerArgb >>> 24 && boundaryArgb.every((color) => color === boundaryArgb[0]) ? lerpUniformPathArgb(surround, shape.centerArgb, t) : lerpArgb(surround, shape.centerArgb, t);
16673
16720
  }
16674
16721
  var MAX_TILE = 2048;
@@ -31226,6 +31273,21 @@ function foldIntoTile(v, origin, size, mirror2, lag) {
31226
31273
  }
31227
31274
  return origin + local;
31228
31275
  }
31276
+ function pixelAligned(boundary, m) {
31277
+ let x0 = Infinity;
31278
+ let y0 = Infinity;
31279
+ let x1 = -Infinity;
31280
+ let y1 = -Infinity;
31281
+ for (const p of boundary) {
31282
+ const x = m[0] * p.x + m[2] * p.y + m[4];
31283
+ const y = m[1] * p.x + m[3] * p.y + m[5];
31284
+ x0 = Math.min(x0, x);
31285
+ y0 = Math.min(y0, y);
31286
+ x1 = Math.max(x1, x);
31287
+ y1 = Math.max(y1, y);
31288
+ }
31289
+ return [x0, y0, x1, y1].every((v) => Math.abs(v - Math.round(v)) < 1e-6);
31290
+ }
31229
31291
  function pathGradientSampler(shape, wrap, device) {
31230
31292
  const box = boundaryBox2(shape.boundary);
31231
31293
  const full = mulMatrix(device, shape.transform ?? IDENTITY4);
@@ -31233,6 +31295,10 @@ function pathGradientSampler(shape, wrap, device) {
31233
31295
  if (!box || !inv2) {
31234
31296
  return null;
31235
31297
  }
31298
+ const anisotropicFocus = !!shape.focus && Math.abs(shape.focus.x - shape.focus.y) > 1e-9;
31299
+ const uniformSurround = shape.boundaryArgb.every((color) => color === shape.boundaryArgb[0]);
31300
+ const smooth = !uniformSurround && !shape.preset || anisotropicFocus || wrap !== "clamp" && !pixelAligned(shape.boundary, full);
31301
+ const quantum = smooth ? 0 : pathGradientQuantum(shape.boundary, full);
31236
31302
  const pxX = Math.hypot(full[0], full[1]);
31237
31303
  const pxY = Math.hypot(full[2], full[3]);
31238
31304
  const lagX = pxX > 0 ? 1 / pxX : 0;
@@ -31252,8 +31318,8 @@ function pathGradientSampler(shape, wrap, device) {
31252
31318
  by = foldIntoTile(by, box.y, box.h, mirrorY, lagY);
31253
31319
  }
31254
31320
  if (wrap === "clamp" && by >= box.y + box.h) continue;
31255
- const focusPoint = bias && shape.focus?.x === 0 ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31256
- const c = pathGradientColorAt(shape, bx, by, focusPoint);
31321
+ const focusPoint = bias ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31322
+ const c = pathGradientColorAt(shape, bx, by, focusPoint, quantum);
31257
31323
  if (c === null) {
31258
31324
  continue;
31259
31325
  }
package/dist/index.mjs CHANGED
@@ -16512,6 +16512,45 @@ function lerpUniformPathArgb(a, b, t) {
16512
16512
  };
16513
16513
  return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16514
16514
  }
16515
+ function pathGradientQuantum(boundary, matrix = IDENTITY) {
16516
+ let x0 = Infinity;
16517
+ let y0 = Infinity;
16518
+ let x1 = -Infinity;
16519
+ let y1 = -Infinity;
16520
+ for (const p of boundary) {
16521
+ const x = matrix[0] * p.x + matrix[2] * p.y + matrix[4];
16522
+ const y = matrix[1] * p.x + matrix[3] * p.y + matrix[5];
16523
+ x0 = Math.min(x0, x);
16524
+ y0 = Math.min(y0, y);
16525
+ x1 = Math.max(x1, x);
16526
+ y1 = Math.max(y1, y);
16527
+ }
16528
+ const steps = Math.ceil(2 * Math.hypot(x1 - x0, y1 - y0));
16529
+ return Number.isFinite(steps) && steps > 0 ? steps : 0;
16530
+ }
16531
+ var TIE_EPSILON = 1e-7;
16532
+ function quantisedPathArgb(surround, centre, t, quantum) {
16533
+ const scaled2 = t * quantum;
16534
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16535
+ const k = tie ? scaled2 : Math.floor(scaled2 + 0.5);
16536
+ const fromBoundary = t < 0.5;
16537
+ const w = (fromBoundary ? k : quantum - k) / quantum;
16538
+ const aS = surround >>> 24 & 255;
16539
+ const aC = centre >>> 24 & 255;
16540
+ const mix2 = (s, c) => fromBoundary ? s + (c - s) * w : c + (s - c) * w;
16541
+ const alpha = Math.floor(mix2(aS, aC) + 0.5);
16542
+ if (alpha <= 0) return 0;
16543
+ const ch = (shift) => {
16544
+ const p = Math.floor(mix2((surround >>> shift & 255) * aS / 255, (centre >>> shift & 255) * aC / 255) + 0.5);
16545
+ return Math.min(255, Math.max(0, Math.round(p * 255 / alpha)));
16546
+ };
16547
+ return (alpha << 24 | ch(16) << 16 | ch(8) << 8 | ch(0)) >>> 0;
16548
+ }
16549
+ function quantisedPosition(pos, quantum) {
16550
+ const scaled2 = pos * quantum;
16551
+ const tie = Math.abs(scaled2 - Math.floor(scaled2) - 0.5) < TIE_EPSILON;
16552
+ return (tie ? scaled2 : Math.floor(scaled2 + 0.5)) / quantum;
16553
+ }
16515
16554
  function piecewise2(xs, ys, x) {
16516
16555
  const n = Math.min(xs.length, ys.length);
16517
16556
  if (n === 0) {
@@ -16546,11 +16585,13 @@ function stopColorAt(stops, t) {
16546
16585
  }
16547
16586
  return stops[stops.length - 1].argb;
16548
16587
  }
16549
- function pathGradientColorAt(shape, x, y, focusPoint) {
16588
+ function pathGradientColorAt(shape, x, y, focusPoint, quantum) {
16550
16589
  const { center, boundary, boundaryArgb } = shape;
16551
16590
  const n = boundary.length;
16552
16591
  const px = x - center.x;
16553
16592
  const py = y - center.y;
16593
+ const ux = (focusPoint?.x ?? x) - center.x;
16594
+ const uy = (focusPoint?.y ?? y) - center.y;
16554
16595
  let found = -1;
16555
16596
  let bestS = 0;
16556
16597
  let bestU = 0;
@@ -16572,8 +16613,10 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16572
16613
  continue;
16573
16614
  }
16574
16615
  found = i;
16575
- bestS = Math.max(0, alpha);
16576
- bestU = alpha > 0 ? Math.min(1, Math.max(0, beta / alpha)) : 0;
16616
+ const alphaU = quantum ? (ux * ey - uy * ex) / det : alpha;
16617
+ const betaU = quantum ? (ax * uy - ay * ux) / det : beta;
16618
+ bestS = Math.max(0, alphaU);
16619
+ bestU = alphaU > 0 ? Math.min(1, Math.max(0, betaU / alphaU)) : 0;
16577
16620
  }
16578
16621
  let s = found < 0 ? Infinity : bestS;
16579
16622
  if (shape.focus) {
@@ -16648,7 +16691,8 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16648
16691
  }
16649
16692
  }
16650
16693
  if (found < 0 || !Number.isFinite(s)) return null;
16651
- const pos = 1 - s;
16694
+ const exactPos = 1 - s;
16695
+ const pos = quantum && quantum > 0 ? quantisedPosition(exactPos, quantum) : exactPos;
16652
16696
  if (shape.preset && shape.preset.positions.length > 0) {
16653
16697
  const { positions, argb } = shape.preset;
16654
16698
  if (pos <= positions[0]) {
@@ -16667,6 +16711,9 @@ function pathGradientColorAt(shape, x, y, focusPoint) {
16667
16711
  const surround = lerpArgb(c0, c1, bestU);
16668
16712
  const factor = shape.blend ? piecewise2(shape.blend.positions, shape.blend.factors, pos) : pos;
16669
16713
  const t = Math.min(1, Math.max(0, factor));
16714
+ if (quantum && quantum > 0 && !shape.blend && boundaryArgb.every((color) => color === boundaryArgb[0])) {
16715
+ return quantisedPathArgb(surround, shape.centerArgb, Math.min(1, Math.max(0, exactPos)), quantum);
16716
+ }
16670
16717
  return surround >>> 24 !== shape.centerArgb >>> 24 && boundaryArgb.every((color) => color === boundaryArgb[0]) ? lerpUniformPathArgb(surround, shape.centerArgb, t) : lerpArgb(surround, shape.centerArgb, t);
16671
16718
  }
16672
16719
  var MAX_TILE = 2048;
@@ -31224,6 +31271,21 @@ function foldIntoTile(v, origin, size, mirror2, lag) {
31224
31271
  }
31225
31272
  return origin + local;
31226
31273
  }
31274
+ function pixelAligned(boundary, m) {
31275
+ let x0 = Infinity;
31276
+ let y0 = Infinity;
31277
+ let x1 = -Infinity;
31278
+ let y1 = -Infinity;
31279
+ for (const p of boundary) {
31280
+ const x = m[0] * p.x + m[2] * p.y + m[4];
31281
+ const y = m[1] * p.x + m[3] * p.y + m[5];
31282
+ x0 = Math.min(x0, x);
31283
+ y0 = Math.min(y0, y);
31284
+ x1 = Math.max(x1, x);
31285
+ y1 = Math.max(y1, y);
31286
+ }
31287
+ return [x0, y0, x1, y1].every((v) => Math.abs(v - Math.round(v)) < 1e-6);
31288
+ }
31227
31289
  function pathGradientSampler(shape, wrap, device) {
31228
31290
  const box = boundaryBox2(shape.boundary);
31229
31291
  const full = mulMatrix(device, shape.transform ?? IDENTITY4);
@@ -31231,6 +31293,10 @@ function pathGradientSampler(shape, wrap, device) {
31231
31293
  if (!box || !inv2) {
31232
31294
  return null;
31233
31295
  }
31296
+ const anisotropicFocus = !!shape.focus && Math.abs(shape.focus.x - shape.focus.y) > 1e-9;
31297
+ const uniformSurround = shape.boundaryArgb.every((color) => color === shape.boundaryArgb[0]);
31298
+ const smooth = !uniformSurround && !shape.preset || anisotropicFocus || wrap !== "clamp" && !pixelAligned(shape.boundary, full);
31299
+ const quantum = smooth ? 0 : pathGradientQuantum(shape.boundary, full);
31234
31300
  const pxX = Math.hypot(full[0], full[1]);
31235
31301
  const pxY = Math.hypot(full[2], full[3]);
31236
31302
  const lagX = pxX > 0 ? 1 / pxX : 0;
@@ -31250,8 +31316,8 @@ function pathGradientSampler(shape, wrap, device) {
31250
31316
  by = foldIntoTile(by, box.y, box.h, mirrorY, lagY);
31251
31317
  }
31252
31318
  if (wrap === "clamp" && by >= box.y + box.h) continue;
31253
- const focusPoint = bias && shape.focus?.x === 0 ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31254
- const c = pathGradientColorAt(shape, bx, by, focusPoint);
31319
+ const focusPoint = bias ? { x: bx - inv2[0] * bias, y: by - inv2[1] * bias } : void 0;
31320
+ const c = pathGradientColorAt(shape, bx, by, focusPoint, quantum);
31255
31321
  if (c === null) {
31256
31322
  continue;
31257
31323
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.14.0",
3
+ "version": "4.15.0",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",