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