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