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