emf-converter 4.8.4 → 4.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11707,6 +11707,11 @@ function ellipsePoints(box, cwControls) {
11707
11707
  P(hCtl, full);
11708
11708
  P(full, rightLower);
11709
11709
  P(full, vMidHi);
11710
+ if (box.halfX) {
11711
+ for (const k of [0, 1, 2, 3, 4, 5, 12]) {
11712
+ out[2 * k]++;
11713
+ }
11714
+ }
11710
11715
  return out;
11711
11716
  }
11712
11717
  function ellipseBeziers(l, t, r, b) {
@@ -11776,76 +11781,15 @@ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = fa
11776
11781
  ];
11777
11782
  }
11778
11783
  var TRIG_STEP = 2 * Math.PI / 128;
11779
- var TRIG_NODES = [
11780
- 1,
11781
- 0.99879566,
11782
- 0.99518639,
11783
- 0.98917961,
11784
- 0.98079146,
11785
- 0.97003981,
11786
- 0.95695228,
11787
- 0.94155978,
11788
- 0.92389844,
11789
- 0.90401145,
11790
- 0.88194619,
11791
- 0.85775575,
11792
- 0.83149879,
11793
- 0.80323775,
11794
- 0.77303903,
11795
- 0.74098864,
11796
- 0.70710648,
11797
- 0.67151859,
11798
- 0.63435836,
11799
- 0.59565849,
11800
- 0.55552672,
11801
- 0.51405765,
11802
- 0.47135039,
11803
- 0.42750875,
11804
- 0.38263811,
11805
- 0.33684632,
11806
- 0.29024443,
11807
- 0.24294646,
11808
- 0.19505974,
11809
- 0.14670808,
11810
- 0.0980012,
11811
- 0.04906089,
11812
- 0
11813
- ];
11814
- var TRIG_TABLE = (() => {
11815
- const cos = [];
11816
- const sin = [];
11817
- for (let k = 0; k <= 128; k++) {
11818
- const q = k >> 5;
11819
- const i = k & 31;
11820
- const c = TRIG_NODES[i];
11821
- const s = TRIG_NODES[32 - i];
11822
- if (k === 128) {
11823
- cos.push(1);
11824
- sin.push(0);
11825
- continue;
11826
- }
11827
- cos.push([c, -s, -c, s][q]);
11828
- sin.push([s, c, -s, -c][q]);
11829
- }
11830
- return { cos, sin };
11831
- })();
11832
- function tableTrig(table) {
11833
- return (a) => {
11834
- let b = a % (2 * Math.PI);
11835
- if (b < 0) {
11836
- b += 2 * Math.PI;
11837
- }
11838
- const x = b / TRIG_STEP;
11839
- const i = Math.min(127, Math.floor(x));
11840
- const f = x - i;
11841
- return table[i] * (1 - f) + table[i + 1] * f;
11842
- };
11843
- }
11844
- var tableCos = tableTrig(TRIG_TABLE.cos);
11845
- var tableSin = tableTrig(TRIG_TABLE.sin);
11784
+ var tableCos = (angle) => polygonTrig(angle)[0];
11785
+ var tableSin = (angle) => polygonTrig(angle)[1];
11846
11786
  function angleArcPieces(startDeg, sweepDeg) {
11847
11787
  const out = [];
11848
11788
  const arc = (from, to) => {
11789
+ if (Math.abs(to - from) < 90) {
11790
+ out.push({ from, to, first: true });
11791
+ return;
11792
+ }
11849
11793
  const lo = Math.min(from, to);
11850
11794
  const hi = Math.max(from, to);
11851
11795
  const pieces = Math.floor(hi / 90) - Math.floor(lo / 90) + 1;
@@ -11894,7 +11838,8 @@ function angleArcFix(box, startDeg, sweepDeg) {
11894
11838
  const E = ellipseBeziers(l, t, l + w, t + h);
11895
11839
  const Ecw = clockwiseEllipseBeziersBox(axisBox(l, t, l + w, t + h));
11896
11840
  const start = startDeg * Math.PI / 180;
11897
- const out = [Math.round(cx + w / 2 * tableCos(start)), Math.round(cy - h / 2 * tableSin(start))];
11841
+ const startUnit = Math.abs(sweepDeg) <= 3 ? [Math.cos(start), Math.sin(start)] : polygonTrig(start);
11842
+ const out = [Math.round(cx + w / 2 * startUnit[0]), Math.round(cy - h / 2 * startUnit[1])];
11898
11843
  for (const p of angleArcPieces(startDeg, sweepDeg)) {
11899
11844
  const span = p.to - p.from;
11900
11845
  if (!p.first && Math.abs(span) === 90 && p.from % 90 === 0) {
@@ -11907,43 +11852,80 @@ function angleArcFix(box, startDeg, sweepDeg) {
11907
11852
  }
11908
11853
  continue;
11909
11854
  }
11910
- for (const v of circularArcBezier(cx, cy, w / 2, h / 2, p.from, p.to)) {
11911
- out.push(Math.round(v));
11855
+ const piece = unitPiece(p.from * Math.PI / 180, p.to * Math.PI / 180, Math.abs(sweepDeg) <= 3);
11856
+ for (let i = 0; i < 6; i += 2) {
11857
+ out.push(Math.round(cx + w / 2 * piece.ctrl[i]), Math.round(cy - h / 2 * piece.ctrl[i + 1]));
11912
11858
  }
11913
11859
  }
11914
11860
  return out;
11915
11861
  }
11916
11862
  var SMALL_SWEEP = 3 * Math.PI / 180;
11917
- function anchoredPiece(boundary, free, rx, ry, cx, cy, smallArc) {
11918
- const sweep = Math.abs(free - boundary);
11919
- const small = smallArc;
11920
- const cos = small ? Math.cos : tableCos;
11921
- const sin = small ? Math.sin : tableSin;
11922
- const anchor = [Math.round(cx + rx * Math.cos(boundary)), Math.round(cy - ry * Math.sin(boundary))];
11923
- const point = [cx + rx * cos(free), cy - ry * sin(free)];
11924
- let t;
11925
- let kappa;
11926
- if (small) {
11927
- t = Math.tan(sweep / 2);
11928
- kappa = 2 / 3 * (1 - Math.tan(sweep / 4) ** 2);
11863
+ var POLYGON_NODES = (() => {
11864
+ const cos = [];
11865
+ const sin = [];
11866
+ for (let k = 0; k <= 129; k++) {
11867
+ cos.push(Math.cos(k * TRIG_STEP));
11868
+ sin.push(Math.sin(k * TRIG_STEP));
11869
+ }
11870
+ for (let k = 0; k <= 128; k += 32) {
11871
+ cos[k] = [1, 0, -1, 0, 1][k / 32];
11872
+ sin[k] = [0, 1, 0, -1, 0][k / 32];
11873
+ }
11874
+ return { cos, sin };
11875
+ })();
11876
+ function polygonTrig(angle) {
11877
+ let b = angle % (2 * Math.PI);
11878
+ if (b < 0) {
11879
+ b += 2 * Math.PI;
11880
+ }
11881
+ const x = b / TRIG_STEP;
11882
+ const i = Math.min(127, Math.floor(x));
11883
+ const f = x - i;
11884
+ return [
11885
+ POLYGON_NODES.cos[i] * (1 - f) + POLYGON_NODES.cos[i + 1] * f,
11886
+ POLYGON_NODES.sin[i] * (1 - f) + POLYGON_NODES.sin[i + 1] * f
11887
+ ];
11888
+ }
11889
+ var ATAN_KNOTS = Array.from({ length: 33 }, (_, j) => Math.atan(j / 32));
11890
+ function interpolatedAtan(t) {
11891
+ const x = t * 32;
11892
+ const j = Math.min(31, Math.floor(x));
11893
+ return ATAN_KNOTS[j] + (ATAN_KNOTS[j + 1] - ATAN_KNOTS[j]) * (x - j);
11894
+ }
11895
+ function approximateArcAngle(x, y) {
11896
+ const ax = Math.abs(x);
11897
+ const ay = Math.abs(y);
11898
+ if (ax === 0 && ay === 0) {
11899
+ return 0;
11900
+ }
11901
+ const a = ay <= ax ? interpolatedAtan(ay / ax) : Math.PI / 2 - interpolatedAtan(ax / ay);
11902
+ if (x >= 0) {
11903
+ return y >= 0 ? a : 2 * Math.PI - a;
11904
+ }
11905
+ return y >= 0 ? Math.PI - a : Math.PI + a;
11906
+ }
11907
+ function unitPiece(from, to, small) {
11908
+ const trig = small ? (a) => [Math.cos(a), Math.sin(a)] : polygonTrig;
11909
+ const p0 = trig(from);
11910
+ const p3 = trig(to);
11911
+ const det = p0[0] * p3[1] - p0[1] * p3[0];
11912
+ let ctrl;
11913
+ if (Math.abs(det) < 1e-12) {
11914
+ ctrl = [p0[0], p0[1], p3[0], p3[1], p3[0], p3[1]];
11929
11915
  } else {
11930
- const c = tableCos(sweep);
11931
- const sn = tableSin(sweep);
11932
- t = (1 - c) / sn;
11933
- const q = t / (1 + Math.sqrt(1 + t * t));
11934
- kappa = 2 / 3 * (1 - q * q);
11935
- }
11936
- const dir = free > boundary ? 1 : -1;
11937
- const tx = -Math.round(Math.sin(boundary));
11938
- const ty = -Math.round(Math.cos(boundary));
11939
- const length = tx !== 0 ? rx : ry;
11940
- const X = [anchor[0] + dir * tx * length * t, anchor[1] + dir * ty * length * t];
11941
- return {
11942
- anchor,
11943
- free: point,
11944
- cAnchor: [anchor[0] + kappa * (X[0] - anchor[0]), anchor[1] + kappa * (X[1] - anchor[1])],
11945
- cFree: [point[0] + kappa * (X[0] - point[0]), point[1] + kappa * (X[1] - point[1])]
11946
- };
11916
+ const X = [(p3[1] - p0[1]) / det, (p0[0] - p3[0]) / det];
11917
+ const c = polygonTrig(Math.abs(to - from) / 2)[0];
11918
+ const kappa = 4 / 3 * c / (1 + c);
11919
+ ctrl = [
11920
+ p0[0] + kappa * (X[0] - p0[0]),
11921
+ p0[1] + kappa * (X[1] - p0[1]),
11922
+ p3[0] + kappa * (X[0] - p3[0]),
11923
+ p3[1] + kappa * (X[1] - p3[1]),
11924
+ p3[0],
11925
+ p3[1]
11926
+ ];
11927
+ }
11928
+ return { start: p0, ctrl };
11947
11929
  }
11948
11930
  function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11949
11931
  const w = r - l;
@@ -11955,24 +11937,30 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11955
11937
  const cy = t + Math.ceil(h / 2);
11956
11938
  const tcx = (l + r) / 2;
11957
11939
  const tcy = (t + b) / 2;
11958
- const angle = (x, y) => Math.atan2(-(y - tcy) / (h / 2 || 1), (x - tcx) / (w / 2 || 1));
11940
+ const angle = (x, y) => approximateArcAngle((x - tcx) / (w / 2 || 1), -(y - tcy) / (h / 2 || 1));
11959
11941
  const a0 = angle(xs, ys);
11960
11942
  let a1 = angle(xe, ye);
11943
+ const small = a1 !== a0 && Math.abs(a1 - a0) <= SMALL_SWEEP;
11961
11944
  const s = clockwise ? -1 : 1;
11962
11945
  if (s > 0) {
11963
11946
  while (a1 <= a0) a1 += 2 * Math.PI;
11964
11947
  } else {
11965
11948
  while (a1 >= a0) a1 -= 2 * Math.PI;
11966
11949
  }
11967
- const px = (a2) => cx + rx * tableCos(a2);
11968
- const py = (a2) => cy - ry * tableSin(a2);
11950
+ const px = (u) => Math.round(cx + rx * u);
11951
+ const py = (v) => Math.round(cy - ry * v);
11969
11952
  const E = clockwise ? clockwiseEllipseBeziersBox(axisBox(l, t, r, b)) : ellipseBeziers(l, t, r, b);
11970
- const out = [Math.round(px(a0)), Math.round(py(a0))];
11953
+ const startUnit = small ? [Math.cos(a0), Math.sin(a0)] : polygonTrig(a0);
11954
+ const out = [px(startUnit[0]), py(startUnit[1])];
11971
11955
  let a = a0;
11972
11956
  for (let guard = 0; guard < 8 && (s > 0 ? a < a1 - 1e-12 : a > a1 + 1e-12); guard++) {
11973
11957
  const next = s > 0 ? Math.floor(a / Q + 1e-9) * Q + Q : Math.ceil(a / Q - 1e-9) * Q - Q;
11974
11958
  const onBoundary = Math.abs(a / Q - Math.round(a / Q)) < 1e-9;
11975
- if (onBoundary && (s > 0 ? next <= a1 + 1e-12 : next >= a1 - 1e-12)) {
11959
+ const reachesEnd = s > 0 ? next >= a1 - 1e-12 : next <= a1 + 1e-12;
11960
+ const quadrantFrom = (Math.round(a / Q) % 4 + 4) % 4;
11961
+ const quadrantTo = (Math.round(next / Q) % 4 + 4) % 4;
11962
+ const generic = guard === 0 && (s > 0 ? quadrantFrom === 0 || quadrantFrom === 3 : quadrantFrom === 0 || quadrantFrom === 1) || reachesEnd && (s > 0 ? quadrantTo === 1 || quadrantTo === 2 : quadrantTo === 2 || quadrantTo === 3);
11963
+ if (!generic && onBoundary && (s > 0 ? next <= a1 + 1e-12 : next >= a1 - 1e-12)) {
11976
11964
  const qi = (Math.round(Math.min(a, next) / Q) % 4 + 4) % 4 * 6;
11977
11965
  if (s > 0) {
11978
11966
  out.push(E[qi + 2], E[qi + 3], E[qi + 4], E[qi + 5], E[qi + 6], E[qi + 7]);
@@ -11983,30 +11971,10 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11983
11971
  continue;
11984
11972
  }
11985
11973
  const e = s > 0 ? Math.min(next, a1) : Math.max(next, a1);
11986
- const endOnBoundary = Math.abs(e / Q - Math.round(e / Q)) < 1e-9;
11987
- if (onBoundary !== endOnBoundary) {
11988
- const piece = anchoredPiece(onBoundary ? a : e, onBoundary ? e : a, rx, ry, cx, cy, Math.abs(a1 - a0) <= SMALL_SWEEP);
11989
- if (onBoundary) {
11990
- out.push(...[piece.cAnchor, piece.cFree, piece.free].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11991
- } else {
11992
- if (out.length === 2) {
11993
- out[0] = Math.round(piece.free[0]);
11994
- out[1] = Math.round(piece.free[1]);
11995
- }
11996
- out.push(...[piece.cFree, piece.cAnchor, piece.anchor].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11997
- }
11998
- a = e;
11999
- continue;
11974
+ const piece = unitPiece(a, e, small);
11975
+ for (let i = 0; i < 6; i += 2) {
11976
+ out.push(px(piece.ctrl[i]), py(piece.ctrl[i + 1]));
12000
11977
  }
12001
- const k = 4 / 3 * Math.tan((e - a) / 4);
12002
- out.push(
12003
- Math.round(px(a) - k * rx * tableSin(a)),
12004
- Math.round(py(a) - k * ry * tableCos(a)),
12005
- Math.round(px(e) + k * rx * tableSin(e)),
12006
- Math.round(py(e) + k * ry * tableCos(e)),
12007
- Math.round(px(e)),
12008
- Math.round(py(e))
12009
- );
12010
11978
  a = e;
12011
11979
  }
12012
11980
  return out;
@@ -16567,153 +16535,26 @@ function applyTint(src, hue, amount) {
16567
16535
  var TINT_FULL = 254;
16568
16536
  function applyRedEyeCorrection(src, width, height, areas) {
16569
16537
  const out = new Uint8ClampedArray(src);
16538
+ const carry = { x: 0, y: 0 };
16570
16539
  for (const a of areas) {
16571
- correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16540
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height), carry);
16572
16541
  }
16573
16542
  return out;
16574
16543
  }
16575
- var RED_EYE_FALLOFF = new Float64Array(
16576
- [
16577
- 24930,
16578
- 24570,
16579
- 24201,
16580
- 23836,
16581
- 23471,
16582
- 23107,
16583
- 22744,
16584
- 22367,
16585
- 21976,
16586
- 21587,
16587
- 21198,
16588
- 20824,
16589
- 20490,
16590
- 20165,
16591
- 19832,
16592
- 19499,
16593
- 19157,
16594
- 18817,
16595
- 18480,
16596
- 18151,
16597
- 17820,
16598
- 17490,
16599
- 17160,
16600
- 16831,
16601
- 16506,
16602
- 16189,
16603
- 15878,
16604
- 15569,
16605
- 15273,
16606
- 14973,
16607
- 14668,
16608
- 14362,
16609
- 14059,
16610
- 13768,
16611
- 13484,
16612
- 13203,
16613
- 12923,
16614
- 12642,
16615
- 12366,
16616
- 12094,
16617
- 11828,
16618
- 11564,
16619
- 11301,
16620
- 11034,
16621
- 10770,
16622
- 10507,
16623
- 10253,
16624
- 10011,
16625
- 9771,
16626
- 9530,
16627
- 9290,
16628
- 9053,
16629
- 8819,
16630
- 8588,
16631
- 8359,
16632
- 8134,
16633
- 7914,
16634
- 7697,
16635
- 7481,
16636
- 7267,
16637
- 7056,
16638
- 6851,
16639
- 6651,
16640
- 6453,
16641
- 6256,
16642
- 6061,
16643
- 5871,
16644
- 5684,
16645
- 5499,
16646
- 5317,
16647
- 5138,
16648
- 4962,
16649
- 4789,
16650
- 4619,
16651
- 4452,
16652
- 4289,
16653
- 4130,
16654
- 3974,
16655
- 3820,
16656
- 3669,
16657
- 3520,
16658
- 3375,
16659
- 3232,
16660
- 3093,
16661
- 2958,
16662
- 2825,
16663
- 2695,
16664
- 2568,
16665
- 2444,
16666
- 2325,
16667
- 2207,
16668
- 2092,
16669
- 1980,
16670
- 1872,
16671
- 1768,
16672
- 1666,
16673
- 1567,
16674
- 1470,
16675
- 1377,
16676
- 1287,
16677
- 1200,
16678
- 1116,
16679
- 1034,
16680
- 956,
16681
- 881,
16682
- 808,
16683
- 739,
16684
- 674,
16685
- 612,
16686
- 541,
16687
- 462,
16688
- 381,
16689
- 299,
16690
- 220,
16691
- 147,
16692
- 85,
16693
- 36,
16694
- 3,
16695
- 0
16696
- ].map((v) => v / 1e5)
16697
- );
16698
- function redEyeFalloff(u) {
16699
- const t = u * 128;
16700
- const i = Math.floor(t);
16701
- if (i >= RED_EYE_FALLOFF.length - 1) {
16702
- return 0;
16703
- }
16704
- return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16705
- }
16706
16544
  var RED_EYE_SECTORS = 60;
16707
16545
  var RED_EYE_BLUE_WEIGHT = 0.225;
16708
- var RED_EYE_WEIGHT_CAP = 6;
16709
- var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16710
- var RED_EYE_DARK_STRENGTH = 0.49;
16711
- var RED_EYE_DARK_RATIO = 0.6;
16712
- var RED_EYE_GREEN_SHARE = 80 / 255;
16713
- function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16546
+ var RED_EYE_PURE_WEIGHT = 1 / (1 + RED_EYE_BLUE_WEIGHT);
16547
+ var RED_EYE_CARRY = 0.81;
16548
+ var RED_EYE_GREEN_SHARE = 5 / 16;
16549
+ var RED_EYE_SPREAD_STEPS = [31.5, 50, 98.78];
16550
+ var RED_EYE_STRENGTHS = [0.25, 0.5, 0.661, 0.75];
16551
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1, carry) {
16714
16552
  const w = x1 - x0;
16715
16553
  const h = y1 - y0;
16716
16554
  const count = w * h;
16555
+ if (!(count > 0)) {
16556
+ return;
16557
+ }
16717
16558
  const redness = new Float64Array(count);
16718
16559
  const dark = new Float64Array(count);
16719
16560
  let sx = 0;
@@ -16726,34 +16567,44 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16726
16567
  const g = out[i + 1];
16727
16568
  const b = out[i + 2];
16728
16569
  const k = y * w + x;
16729
- dark[k] = Math.min(g, b);
16570
+ const d = g + RED_EYE_BLUE_WEIGHT * b;
16571
+ dark[k] = d;
16730
16572
  const v = r - Math.max(g, b);
16731
16573
  if (v > 0) {
16732
16574
  redness[k] = v;
16733
- const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16734
- {
16735
- const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16736
- sx += weight * (x + 0.5);
16737
- sy += weight * (y + 0.5);
16738
- sw += weight;
16739
- }
16575
+ const weight = d >= 1 ? v / d : RED_EYE_PURE_WEIGHT;
16576
+ sx += weight * (x + 0.5);
16577
+ sy += weight * (y + 0.5);
16578
+ sw += weight;
16740
16579
  }
16741
16580
  }
16742
16581
  }
16743
16582
  if (sw === 0) {
16744
- if (!redness.some((v) => v > 0)) {
16745
- return;
16746
- }
16747
- sx = w / 2;
16748
- sy = h / 2;
16749
- sw = 1;
16583
+ return;
16750
16584
  }
16751
- const cx = sx / sw;
16752
- const cy = sy / sw;
16753
- const radius = Math.min(cx, cy) - 0.5;
16585
+ let cx = (sx + RED_EYE_CARRY * carry.x) / sw;
16586
+ let cy = (sy + RED_EYE_CARRY * carry.y) / sw;
16587
+ carry.x = x0 + cx;
16588
+ carry.y = y0 + cy;
16589
+ if (Math.hypot(cx - w / 2, cy - h / 2) >= Math.max(Math.min(w, h) / 2, (w + h) / 6)) {
16590
+ cx = w / 2;
16591
+ cy = h / 2;
16592
+ }
16593
+ const ax = x0 + cx;
16594
+ const ay = y0 + cy;
16595
+ const edgeX = Math.round(cx) > w / 2 ? w - cx + 0.5 : cx - 0.5;
16596
+ const edgeY = Math.round(cy) > h / 2 ? h - cy + 0.5 : cy - 0.5;
16597
+ const radius = ax > ay ? Math.min(edgeX, ay - 0.5) : Math.min(edgeY, ax - 0.5);
16754
16598
  if (!(radius > 0)) {
16755
16599
  return;
16756
16600
  }
16601
+ const sorted = Float64Array.from(dark).sort();
16602
+ const spread = sorted[Math.floor(0.7 * count)] - sorted[Math.floor(0.3 * count)];
16603
+ let level = 0;
16604
+ while (level < RED_EYE_SPREAD_STEPS.length && spread >= RED_EYE_SPREAD_STEPS[level]) {
16605
+ level++;
16606
+ }
16607
+ const strength = RED_EYE_STRENGTHS[level];
16757
16608
  const sector = new Uint8Array(count);
16758
16609
  const distance = new Float64Array(count);
16759
16610
  const sectorSum = new Float64Array(RED_EYE_SECTORS);
@@ -16791,7 +16642,8 @@ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16791
16642
  const mean = sectorSum[s] / sectorCount[s];
16792
16643
  const meanDark = sectorDark[s] / sectorCount[s];
16793
16644
  const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16794
- const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16645
+ const u = distance[k] / radius;
16646
+ const falloff = Math.max(strength * (1 - u) * (1 - u), 0.5 * Math.max(0, 1 - ratio) ** 2);
16795
16647
  const rest = (1 - falloff) * mean;
16796
16648
  if (rest >= v) {
16797
16649
  continue;
@@ -29851,6 +29703,11 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = fal
29851
29703
  for (let i = 0; i < q.length; i += 2) {
29852
29704
  pts.push(...map(q[i], clockwise ? f.t + f.b - q[i + 1] : q[i + 1]));
29853
29705
  }
29706
+ if (box.halfX && !clockwise) {
29707
+ for (const k of [1, 2, 3, 4, 5, 6, 8, 15]) {
29708
+ pts[2 * k]++;
29709
+ }
29710
+ }
29854
29711
  return pts;
29855
29712
  }
29856
29713
  function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
@@ -30932,6 +30789,51 @@ function fixExtent(rCtx, v, axis) {
30932
30789
  const k = axis === 0 ? Math.hypot(m[0], m[1]) : Math.hypot(m[2], m[3]);
30933
30790
  return Math.round(Math.abs(v) * k * 16);
30934
30791
  }
30792
+ var PS_INSIDEFRAME = 6;
30793
+ function insideFrameInset(rCtx, box, rectangle = false) {
30794
+ if ((rCtx.state.penStyle & 15) !== PS_INSIDEFRAME || !isAxisBox(box) || hasWorldRotation(rCtx)) {
30795
+ return null;
30796
+ }
30797
+ const m = gdiDeviceMatrix(rCtx);
30798
+ const px = gdiDevicePixelX(rCtx);
30799
+ const py = gdiDevicePixelY(rCtx);
30800
+ const widthX = Math.abs(rCtx.state.penWidth * m[0]) / px * 16;
30801
+ const widthY = Math.abs(rCtx.state.penWidth * m[3]) / py * 16;
30802
+ if (Math.max(widthX, widthY) < 24) {
30803
+ return null;
30804
+ }
30805
+ const wx = Math.round(widthX);
30806
+ const wy = Math.round(widthY);
30807
+ if (Math.abs(box.exx) / px < wx || Math.abs(box.eyy) / py < wy) {
30808
+ return null;
30809
+ }
30810
+ const lo = Math.floor(wx / 2);
30811
+ const hi = Math.ceil(wx / 2);
30812
+ const y = Math.round(Math.ceil(wy / 2) * py);
30813
+ return {
30814
+ left: Math.round((rectangle ? hi : lo) * px),
30815
+ right: Math.round((rectangle ? lo : hi) * px),
30816
+ top: y,
30817
+ bottom: y,
30818
+ half: wx % 2 === 1 && !rectangle
30819
+ };
30820
+ }
30821
+ function insetFixBox(box, inset) {
30822
+ const sx = box.exx < 0 ? -1 : 1;
30823
+ const sy = box.eyy < 0 ? -1 : 1;
30824
+ const framed = {
30825
+ ax: box.ax + sx * inset.left,
30826
+ ay: box.ay + sy * inset.top,
30827
+ exx: box.exx - sx * (inset.left + inset.right),
30828
+ exy: 0,
30829
+ eyx: 0,
30830
+ eyy: box.eyy - sy * (inset.top + inset.bottom)
30831
+ };
30832
+ if (inset.half && sx > 0) {
30833
+ framed.halfX = true;
30834
+ }
30835
+ return framed;
30836
+ }
30935
30837
  function handleSetPixelV(rCtx, dataOff, recSize) {
30936
30838
  const { ctx, view } = rCtx;
30937
30839
  if (recSize >= 20) {
@@ -31004,7 +30906,9 @@ function handleRectangle(rCtx, dataOff, recSize) {
31004
30906
  const t = view.getInt32(dataOff + 4, true);
31005
30907
  const r = view.getInt32(dataOff + 8, true);
31006
30908
  const b = view.getInt32(dataOff + 12, true);
31007
- const box = uprightFixBox(rCtx, l, t, r, b);
30909
+ const unframed = uprightFixBox(rCtx, l, t, r, b);
30910
+ const inset = insideFrameInset(rCtx, unframed, true);
30911
+ const box = inset ? insetFixBox(unframed, inset) : unframed;
31008
30912
  const clockwise = rCtx.state.arcDirection === 2;
31009
30913
  if (!inPath) {
31010
30914
  resetLineStyle(rCtx);
@@ -31039,10 +30943,16 @@ function handleRectangle(rCtx, dataOff, recSize) {
31039
30943
  }
31040
30944
  return true;
31041
30945
  }
31042
- const x = gmx(rCtx, l);
31043
- const y = gmy(rCtx, t);
31044
- const w = gmw(rCtx, r - l);
31045
- const h = gmh(rCtx, b - t);
30946
+ let x = gmx(rCtx, l);
30947
+ let y = gmy(rCtx, t);
30948
+ let w = gmw(rCtx, r - l);
30949
+ let h = gmh(rCtx, b - t);
30950
+ if (inset) {
30951
+ x = Math.min(box.ax, box.ax + box.exx) / 16;
30952
+ y = Math.min(box.ay, box.ay + box.eyy) / 16;
30953
+ w = Math.abs(box.exx) / 16;
30954
+ h = Math.abs(box.eyy) / 16;
30955
+ }
31046
30956
  if (inPath) {
31047
30957
  recordRectangle(rCtx, box, clockwise);
31048
30958
  } else if (needsPathBasedRectangle(rCtx)) {
@@ -31154,7 +31064,26 @@ function handleRoundRect(rCtx, dataOff, recSize) {
31154
31064
  const b = view.getInt32(dataOff + 12, true);
31155
31065
  const cornerW = view.getInt32(dataOff + 16, true);
31156
31066
  const cornerH = view.getInt32(dataOff + 20, true);
31157
- const drawRoundRect = hasWorldRotation(rCtx) ? (c) => appendRoundRectPath(c, l, t, r, b, cornerW / 2, cornerH / 2, (x, y) => gmapPoint(rCtx, x, y)) : (c) => appendRoundRectPath(
31067
+ const clockwise = rCtx.state.arcDirection === 2;
31068
+ const unframed = uprightFixBox(rCtx, l, t, r, b);
31069
+ const inset = insideFrameInset(rCtx, unframed);
31070
+ const framed = inset ? insetFixBox(unframed, inset) : unframed;
31071
+ const devM = gdiDeviceMatrix(rCtx);
31072
+ const cornerFixW = inset ? Math.abs(cornerW * devM[0]) * 16 : fixExtent(rCtx, cornerW, 0);
31073
+ const cornerFixH = inset ? Math.abs(cornerH * devM[3]) * 16 : fixExtent(rCtx, cornerH, 1);
31074
+ const cornerOnFrame = (fix, size, framedSize) => inset && size !== 0 ? Math.floor(Math.min(fix, size) * framedSize / size) : fix;
31075
+ const cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
31076
+ const ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
31077
+ const drawRoundRect = inset ? (c) => appendRoundRectPath(
31078
+ c,
31079
+ Math.min(framed.ax, framed.ax + framed.exx) / 16,
31080
+ Math.min(framed.ay, framed.ay + framed.eyy) / 16,
31081
+ Math.max(framed.ax, framed.ax + framed.exx) / 16,
31082
+ Math.max(framed.ay, framed.ay + framed.eyy) / 16,
31083
+ cw / 32,
31084
+ ch / 32,
31085
+ null
31086
+ ) : hasWorldRotation(rCtx) ? (c) => appendRoundRectPath(c, l, t, r, b, cornerW / 2, cornerH / 2, (x, y) => gmapPoint(rCtx, x, y)) : (c) => appendRoundRectPath(
31158
31087
  c,
31159
31088
  gmx(rCtx, l),
31160
31089
  gmy(rCtx, t),
@@ -31164,13 +31093,12 @@ function handleRoundRect(rCtx, dataOff, recSize) {
31164
31093
  gmh(rCtx, cornerH) / 2,
31165
31094
  null
31166
31095
  );
31167
- const clockwise = rCtx.state.arcDirection === 2;
31168
- const raster = (box = uprightFixBox(rCtx, l, t, r, b)) => roundRectRasterPath(box, fixExtent(rCtx, cornerW, 0), fixExtent(rCtx, cornerH, 1), clockwise, cornerW === 0 || cornerH === 0);
31096
+ const raster = (box = framed) => roundRectRasterPath(box, cw, ch, clockwise, cornerW === 0 || cornerH === 0);
31169
31097
  if (inPath) {
31170
- const box = uprightFixBox(rCtx, l, t, r, b);
31098
+ const box = framed;
31171
31099
  if (cornerW === 0 || cornerH === 0) recordRectangle(rCtx, box, clockwise);
31172
31100
  else {
31173
- recordBezierShape(rCtx, roundRectDeviceBeziers(box, fixExtent(rCtx, cornerW, 0), fixExtent(rCtx, cornerH, 1), clockwise), 8);
31101
+ recordBezierShape(rCtx, roundRectDeviceBeziers(box, cw, ch, clockwise), 8);
31174
31102
  rasterPathOf(rCtx).append(raster(box));
31175
31103
  }
31176
31104
  } else {
@@ -31180,7 +31108,7 @@ function handleRoundRect(rCtx, dataOff, recSize) {
31180
31108
  c.beginPath();
31181
31109
  drawRoundRect(c);
31182
31110
  },
31183
- raster: () => raster(curvedFixBox(rCtx, l, t, r, b, true)),
31111
+ raster: () => raster(inset ? framed : curvedFixBox(rCtx, l, t, r, b, true)),
31184
31112
  roundPen: true,
31185
31113
  fill: true,
31186
31114
  stroke: true
@@ -31217,7 +31145,10 @@ function handleEllipse(rCtx, dataOff, recSize) {
31217
31145
  const r = view.getInt32(dataOff + 8, true);
31218
31146
  const b = view.getInt32(dataOff + 12, true);
31219
31147
  const clockwise = rCtx.state.arcDirection === 2;
31220
- const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
31148
+ const unframed = uprightFixBox(rCtx, l, t, r, b);
31149
+ const inset = insideFrameInset(rCtx, unframed);
31150
+ const framed = inset ? insetFixBox(unframed, inset) : unframed;
31151
+ const params = inset ? { cx: (framed.ax + framed.exx / 2) / 16, cy: (framed.ay + framed.eyy / 2) / 16, rx: Math.abs(framed.exx) / 32, ry: Math.abs(framed.eyy) / 32, rotation: 0 } : hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
31221
31152
  cx: gmx(rCtx, (l + r) / 2),
31222
31153
  cy: gmy(rCtx, (t + b) / 2),
31223
31154
  rx: Math.abs(gmw(rCtx, r - l)) / 2,
@@ -31225,7 +31156,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
31225
31156
  rotation: 0
31226
31157
  };
31227
31158
  if (inPath) {
31228
- const box = uprightFixBox(rCtx, l, t, r, b);
31159
+ const box = framed;
31229
31160
  recordBezierShape(rCtx, ellipseBeziersBox(box, clockwise), 6);
31230
31161
  rasterPathOf(rCtx).append(ellipseRasterPath(box, clockwise));
31231
31162
  } else {
@@ -31235,7 +31166,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
31235
31166
  c.beginPath();
31236
31167
  c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
31237
31168
  },
31238
- raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b, true), clockwise),
31169
+ raster: () => ellipseRasterPath(inset ? framed : curvedFixBox(rCtx, l, t, r, b, true), clockwise),
31239
31170
  roundPen: true,
31240
31171
  fill: true,
31241
31172
  stroke: true
@@ -31262,7 +31193,10 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31262
31193
  const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
31263
31194
  const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
31264
31195
  const rotated = hasWorldRotation(rCtx);
31265
- const params = rotated ? gdiEllipseParams(rCtx, cxA, cyA, rx, ry) : {
31196
+ const unframed = fixBox(rCtx, l, t, r, b);
31197
+ const inset = insideFrameInset(rCtx, unframed);
31198
+ const framed = inset ? insetFixBox(unframed, inset) : unframed;
31199
+ const params = inset ? { cx: (framed.ax + framed.exx / 2) / 16, cy: (framed.ay + framed.eyy / 2) / 16, rx: Math.abs(framed.exx) / 32, ry: Math.abs(framed.eyy) / 32, rotation: 0 } : rotated ? gdiEllipseParams(rCtx, cxA, cyA, rx, ry) : {
31266
31200
  cx: gmx(rCtx, cxA),
31267
31201
  cy: gmy(rCtx, cyA),
31268
31202
  rx: Math.abs(gmw(rCtx, rx)),
@@ -31286,10 +31220,18 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31286
31220
  }
31287
31221
  };
31288
31222
  const kind = isArcTo ? "arcto" : recType === EMR_PIE ? "pie" : recType === EMR_CHORD ? "chord" : "arc";
31223
+ const radialOnBox = (p) => {
31224
+ if (!inset) {
31225
+ return p;
31226
+ }
31227
+ const kx = unframed.exx !== 0 ? framed.exx / unframed.exx : 1;
31228
+ const ky = unframed.eyy !== 0 ? framed.eyy / unframed.eyy : 1;
31229
+ return [framed.ax + framed.exx / 2 + (p[0] - (unframed.ax + unframed.exx / 2)) * kx, framed.ay + framed.eyy / 2 + (p[1] - (unframed.ay + unframed.eyy / 2)) * ky];
31230
+ };
31289
31231
  const rasterArgs = (immediate = false) => ({
31290
- box: immediate && needsFill ? curvedFixBox(rCtx, l, t, r, b) : fixBox(rCtx, l, t, r, b),
31291
- s: fixPoint(rCtx, startX, startY),
31292
- e: fixPoint(rCtx, endX, endY),
31232
+ box: inset ? framed : immediate && needsFill ? curvedFixBox(rCtx, l, t, r, b) : unframed,
31233
+ s: radialOnBox(fixPoint(rCtx, startX, startY)),
31234
+ e: radialOnBox(fixPoint(rCtx, endX, endY)),
31293
31235
  from: currentFix(rCtx)
31294
31236
  });
31295
31237
  if (inPath) {
@@ -39324,7 +39266,7 @@ function wmfExtFloodFill(p, x, y, colorRef, type) {
39324
39266
 
39325
39267
  // src/wmf-shapes.ts
39326
39268
  var PS_NULL3 = 5;
39327
- var PS_INSIDEFRAME = 6;
39269
+ var PS_INSIDEFRAME2 = 6;
39328
39270
  function compatBox(p, l, t, r, b, opts = {}) {
39329
39271
  const exclusive = opts.exclusive !== false;
39330
39272
  const a = fixPoint(p.rCtx, l, t);
@@ -39346,23 +39288,32 @@ function compatBox(p, l, t, r, b, opts = {}) {
39346
39288
  box.x1 -= Math.round(ux * 3 / 4);
39347
39289
  box.y1 -= Math.round(uy * 3 / 4);
39348
39290
  }
39349
- if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME) {
39291
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME2) {
39350
39292
  const w = Math.round(penDeviceWidth(p.rCtx) / p.kx);
39351
39293
  if (w > 1) {
39352
39294
  const matrix = gdiDeviceMatrix(p.rCtx);
39353
- const rawWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) / p.kx;
39295
+ const wx = Math.round(Math.abs(p.rCtx.state.penWidth * matrix[0]) / p.kx * 16);
39296
+ const wy = Math.round(Math.abs(p.rCtx.state.penWidth * matrix[3]) / p.ky * 16);
39354
39297
  box.x1 += Math.round(ux);
39355
39298
  box.y1 += Math.round(uy);
39356
- box.x0 += Math.round(rawWidth * ux / 2);
39357
- box.y0 += Math.round(rawWidth * uy / 2);
39358
- box.x1 -= Math.round(rawWidth * ux / 2);
39359
- box.y1 -= Math.round(rawWidth * uy / 2);
39299
+ box.x0 += Math.round((opts.curved || opts.roundFrame ? Math.floor(wx / 2) : Math.ceil(wx / 2)) * ux / 16);
39300
+ box.y0 += Math.round(Math.ceil(wy / 2) * uy / 16);
39301
+ const half = !!opts.half && wx % 2 === 1;
39302
+ box.x1 -= Math.round((half ? Math.ceil(wx / 2) : Math.floor(wx / 2)) * ux / 16);
39303
+ box.y1 -= Math.round(Math.ceil(wy / 2) * uy / 16);
39304
+ if (half) {
39305
+ box.halfX = true;
39306
+ }
39360
39307
  }
39361
39308
  }
39362
39309
  return box;
39363
39310
  }
39364
39311
  function fixBoxOf(box) {
39365
- return axisBox(box.x0, box.y0, Math.max(box.x0, box.x1), Math.max(box.y0, box.y1));
39312
+ const fix = axisBox(box.x0, box.y0, Math.max(box.x0, box.x1), Math.max(box.y0, box.y1));
39313
+ if (box.halfX) {
39314
+ fix.halfX = true;
39315
+ }
39316
+ return fix;
39366
39317
  }
39367
39318
  function canvasRect(box) {
39368
39319
  return { x: box.x0 / 16, y: box.y0 / 16, w: Math.max(0, box.x1 - box.x0) / 16, h: Math.max(0, box.y1 - box.y0) / 16 };
@@ -39416,14 +39367,15 @@ function wmfRectangle(p, l, t, r, b) {
39416
39367
  }
39417
39368
  function compatCorner(p, w, h, box, logical) {
39418
39369
  const m = gdiDeviceMatrix(p.rCtx);
39419
- const cw = Math.round(Math.abs(w * m[0]) * 16);
39420
- const ch = Math.round(Math.abs(h * m[3]) * 16);
39370
+ Math.round(Math.abs(w * m[0]) * 16);
39371
+ Math.round(Math.abs(h * m[3]) * 16);
39421
39372
  const ux = 16 * p.kx;
39422
39373
  const uy = 16 * p.ky;
39423
- if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
39424
- const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
39374
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME2 && !penIsCosmetic(p.rCtx)) {
39425
39375
  const width2 = box.x1 - box.x0, height2 = box.y1 - box.y0;
39426
- return [Math.floor(Math.min(cw, width2 + penWidth) * width2 / (width2 + penWidth)), Math.floor(Math.min(ch, height2 + penWidth) * height2 / (height2 + penWidth))];
39376
+ const fullW2 = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width2;
39377
+ const fullH2 = Math.abs((logical[3] - logical[1]) * m[3]) * 16 || height2;
39378
+ return [Math.floor(Math.min(Math.abs(w * m[0]) * 16, fullW2) * width2 / fullW2), Math.floor(Math.min(Math.abs(h * m[3]) * 16, fullH2) * height2 / fullH2)];
39427
39379
  }
39428
39380
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
39429
39381
  const fullW = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width + ux;
@@ -39432,7 +39384,7 @@ function compatCorner(p, w, h, box, logical) {
39432
39384
  return penIsCosmetic(p.rCtx) && !penIsNull(p) ? scaled2 : [Math.floor(scaled2[0]), Math.floor(scaled2[1])];
39433
39385
  }
39434
39386
  function wmfRoundRect(p, l, t, r, b, w, h) {
39435
- const box = compatBox(p, l, t, r, b, { curved: true });
39387
+ const box = compatBox(p, l, t, r, b, { curved: true, half: true });
39436
39388
  if (box.x1 < box.x0 || box.y1 < box.y0) {
39437
39389
  return;
39438
39390
  }
@@ -39468,7 +39420,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
39468
39420
  });
39469
39421
  }
39470
39422
  function wmfEllipse(p, l, t, r, b) {
39471
- const box = compatBox(p, l, t, r, b, { curved: true });
39423
+ const box = compatBox(p, l, t, r, b, { curved: true, half: true });
39472
39424
  if (box.x1 < box.x0 || box.y1 < box.y0) {
39473
39425
  return;
39474
39426
  }
@@ -39485,7 +39437,7 @@ function wmfEllipse(p, l, t, r, b) {
39485
39437
  });
39486
39438
  }
39487
39439
  function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
39488
- const box = compatBox(p, l, t, r, b, { curved: kind === "chord" || kind === "pie" });
39440
+ const box = compatBox(p, l, t, r, b, { curved: kind === "chord" || kind === "pie", roundFrame: true });
39489
39441
  if (box.x1 < box.x0 || box.y1 < box.y0) {
39490
39442
  return null;
39491
39443
  }