emf-converter 4.3.1 → 4.3.3

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
@@ -1735,7 +1735,21 @@ var KAPPA = 0.5522847498307936;
1735
1735
  function axisBox(l, t, r, b) {
1736
1736
  return { ax: l, ay: t, exx: r - l, exy: 0, eyx: 0, eyy: b - t };
1737
1737
  }
1738
- function ellipseBeziersBox(box) {
1738
+ function ellipseBeziersBox(box, clockwise = false) {
1739
+ if (!clockwise) {
1740
+ return ellipsePoints(box, false);
1741
+ }
1742
+ const e = clockwiseEllipseBeziersBox(box);
1743
+ const out = [];
1744
+ for (let i = e.length - 2; i >= 0; i -= 2) {
1745
+ out.push(e[i], e[i + 1]);
1746
+ }
1747
+ return out;
1748
+ }
1749
+ function clockwiseEllipseBeziersBox(box) {
1750
+ return ellipsePoints(box, true);
1751
+ }
1752
+ function ellipsePoints(box, cwControls) {
1739
1753
  const half = (v, up) => up ? Math.ceil(v / 2) : Math.floor(v / 2);
1740
1754
  const hMidLo = (v) => half(v, false);
1741
1755
  const hMidHi = (v) => half(v, true);
@@ -1745,6 +1759,11 @@ function ellipseBeziersBox(box) {
1745
1759
  const vMidHi = (v) => half(v, true);
1746
1760
  const vCtl = (v) => Math.floor(v / 2) - Math.floor(KAPPA * Math.floor(v / 2));
1747
1761
  const vCtlR = (v) => v - vCtl(v);
1762
+ const vDist = (v) => Math.ceil(KAPPA * Math.floor(v / 2));
1763
+ const rightUpper = cwControls ? (v) => vMidHi(v) - vDist(v) : vCtl;
1764
+ const leftUpper = cwControls ? (v) => vMidLo(v) - vDist(v) : vCtl;
1765
+ const leftLower = cwControls ? (v) => vMidLo(v) + vDist(v) : vCtlR;
1766
+ const rightLower = cwControls ? (v) => vMidHi(v) + vDist(v) : vCtlR;
1748
1767
  const zero = () => 0;
1749
1768
  const full = (v) => v;
1750
1769
  const { ax, ay, exx, exy, eyx, eyy } = box;
@@ -1753,17 +1772,17 @@ function ellipseBeziersBox(box) {
1753
1772
  out.push(ax + gx(exx) + gy(eyx), ay + gx(exy) + gy(eyy));
1754
1773
  };
1755
1774
  P(full, vMidHi);
1756
- P(full, vCtl);
1775
+ P(full, rightUpper);
1757
1776
  P(hCtl, zero);
1758
1777
  P(hMidLo, zero);
1759
1778
  P(hCtl2, zero);
1760
- P(zero, vCtl);
1779
+ P(zero, leftUpper);
1761
1780
  P(zero, vMidLo);
1762
- P(zero, vCtlR);
1781
+ P(zero, leftLower);
1763
1782
  P(hCtl2, full);
1764
1783
  P(hMidHi, full);
1765
1784
  P(hCtl, full);
1766
- P(full, vCtlR);
1785
+ P(full, rightLower);
1767
1786
  P(full, vMidHi);
1768
1787
  return out;
1769
1788
  }
@@ -1780,8 +1799,12 @@ function flattenBezierPath(pts) {
1780
1799
  function roundRectCorners(l, t, r, b, cw, ch) {
1781
1800
  const w = r - l;
1782
1801
  const h = b - t;
1783
- const ew = Math.min(Math.abs(cw), w);
1784
- const eh = Math.min(Math.abs(ch), h);
1802
+ let ew = Math.min(Math.abs(cw), w);
1803
+ let eh = Math.min(Math.abs(ch), h);
1804
+ if (ew === 0 || eh === 0) {
1805
+ ew = 0;
1806
+ eh = 0;
1807
+ }
1785
1808
  const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
1786
1809
  const topX = Math.floor(r - ew / 2);
1787
1810
  const hx = r - topX;
@@ -1890,12 +1913,7 @@ function angleArcFix(box, startDeg, sweepDeg) {
1890
1913
  const cx = l + w / 2;
1891
1914
  const cy = t + h / 2;
1892
1915
  const E = ellipseBeziers(l, t, l + w, t + h);
1893
- const Ecw = E.slice();
1894
- const d = Math.ceil(KAPPA * Math.floor(h / 2));
1895
- Ecw[3] = Ecw[1] - d;
1896
- Ecw[11] = Ecw[13] - d;
1897
- Ecw[15] = Ecw[13] + d;
1898
- Ecw[23] = Ecw[1] + d;
1916
+ const Ecw = clockwiseEllipseBeziersBox(axisBox(l, t, l + w, t + h));
1899
1917
  const start = startDeg * Math.PI / 180;
1900
1918
  const out = [Math.round(cx + w / 2 * tableCos(start)), Math.round(cy - h / 2 * tableSin(start))];
1901
1919
  for (const p of angleArcPieces(startDeg, sweepDeg)) {
@@ -1937,15 +1955,7 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
1937
1955
  }
1938
1956
  const px = (a2) => cx + rx * tableCos(a2);
1939
1957
  const py = (a2) => cy - ry * tableSin(a2);
1940
- const E = ellipseBeziers(l, t, r, b);
1941
- if (clockwise) {
1942
- const fh = Math.floor((b - t) / 2);
1943
- const d = Math.ceil(KAPPA * fh);
1944
- E[3] = E[1] - d;
1945
- E[11] = E[13] - d;
1946
- E[15] = E[13] + d;
1947
- E[23] = E[1] + d;
1948
- }
1958
+ const E = clockwise ? clockwiseEllipseBeziersBox(axisBox(l, t, r, b)) : ellipseBeziers(l, t, r, b);
1949
1959
  const out = [Math.round(px(a0)), Math.round(py(a0))];
1950
1960
  let a = a0;
1951
1961
  for (let guard = 0; guard < 8 && (s > 0 ? a < a1 - 1e-12 : a > a1 + 1e-12); guard++) {
@@ -1978,12 +1988,45 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
1978
1988
  var GdiRasterPath = class {
1979
1989
  constructor() {
1980
1990
  this.figures = [];
1991
+ /**
1992
+ * The path as `GetPath` reports it, before flattening: FIX points
1993
+ * (`[x0, y0, ...]`) and their `PT_*` types (`PT_MOVETO` 6, `PT_LINETO`
1994
+ * 2, `PT_BEZIERTO` 4, `PT_CLOSEFIGURE` 1 or-ed onto a figure's last
1995
+ * point). Used to check the geometry against Windows' own paths.
1996
+ */
1997
+ this.getPath = { pts: [], types: [] };
1981
1998
  this.current = null;
1982
1999
  }
1983
2000
  /** Starts a new figure at (`x`, `y`). */
1984
2001
  moveTo(x, y) {
1985
2002
  this.current = { pts: [x, y], closed: false };
1986
2003
  this.figures.push(this.current);
2004
+ this.log(x, y, 6);
2005
+ }
2006
+ /**
2007
+ * Starts a new figure at the current position (`x`, `y`) unless the open
2008
+ * figure already ends there. GDI opens a new figure for a drawing call
2009
+ * that continues from the current position (`LineTo`, `PolylineTo`,
2010
+ * `ArcTo`, ...) when the path is empty, its last figure is closed, or
2011
+ * the current position has moved away from the path's last point (a
2012
+ * `MoveTo`, or a call such as `Polyline`, `Arc` or `PolyPolygon` that
2013
+ * adds to the path without moving the current position; Windows'
2014
+ * `GetPath`, Wine `gdi32/tests/path.c`, `test_all_functions`).
2015
+ */
2016
+ continueAt(x, y) {
2017
+ const f = this.current;
2018
+ if (f && f.pts[f.pts.length - 2] === x && f.pts[f.pts.length - 1] === y) {
2019
+ return;
2020
+ }
2021
+ this.moveTo(x, y);
2022
+ }
2023
+ /** True when a figure is open (not closed and not ended by an appended path). */
2024
+ hasOpenFigure() {
2025
+ return this.current !== null;
2026
+ }
2027
+ log(x, y, type) {
2028
+ this.getPath.pts.push(x, y);
2029
+ this.getPath.types.push(type);
1987
2030
  }
1988
2031
  /** Adds a line to (`x`, `y`), starting a figure there when none is open. */
1989
2032
  lineTo(x, y) {
@@ -1992,6 +2035,7 @@ var GdiRasterPath = class {
1992
2035
  this.moveTo(last ? last[0] : x, last ? last[1] : y);
1993
2036
  }
1994
2037
  this.current.pts.push(x, y);
2038
+ this.log(x, y, 2);
1995
2039
  }
1996
2040
  /** Adds a cubic Bezier from the current point. */
1997
2041
  bezierTo(c1x, c1y, c2x, c2y, x, y) {
@@ -2001,6 +2045,9 @@ var GdiRasterPath = class {
2001
2045
  }
2002
2046
  const p = this.current.pts;
2003
2047
  this.flattenInto(p[p.length - 2], p[p.length - 1], c1x, c1y, c2x, c2y, x, y);
2048
+ this.log(c1x, c1y, 4);
2049
+ this.log(c2x, c2y, 4);
2050
+ this.log(x, y, 4);
2004
2051
  }
2005
2052
  /** Flattens one Bezier onto the open figure, recording its end tangents. */
2006
2053
  flattenInto(x0, y0, c1x, c1y, c2x, c2y, x3, y3) {
@@ -2035,6 +2082,9 @@ var GdiRasterPath = class {
2035
2082
  }
2036
2083
  for (let i = 2; i + 5 < pts.length; i += 6) {
2037
2084
  this.flattenInto(pts[i - 2], pts[i - 1], pts[i], pts[i + 1], pts[i + 2], pts[i + 3], pts[i + 4], pts[i + 5]);
2085
+ for (let k = i; k < i + 6; k += 2) {
2086
+ this.log(pts[k], pts[k + 1], 4);
2087
+ }
2038
2088
  }
2039
2089
  }
2040
2090
  /** Closes the open figure (the next drawing call starts a new one). */
@@ -2042,6 +2092,8 @@ var GdiRasterPath = class {
2042
2092
  if (this.current) {
2043
2093
  this.current.closed = true;
2044
2094
  this.current = null;
2095
+ const t = this.getPath.types;
2096
+ t[t.length - 1] |= 1;
2045
2097
  }
2046
2098
  }
2047
2099
  /** Appends every figure of `other`. */
@@ -2049,6 +2101,9 @@ var GdiRasterPath = class {
2049
2101
  for (const f of other.figures) {
2050
2102
  this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents) });
2051
2103
  }
2104
+ for (let i = 0; i < other.getPath.types.length; i++) {
2105
+ this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
2106
+ }
2052
2107
  this.current = null;
2053
2108
  }
2054
2109
  lastPoint() {
@@ -12645,7 +12700,10 @@ function dashFigure(pts, closed, pen) {
12645
12700
  for (let i = 0; i + 1 < path.length; i++) {
12646
12701
  const a = path[i];
12647
12702
  const b = path[i + 1];
12648
- let segLen = Math.hypot(b.x - a.x, b.y - a.y);
12703
+ const m = pen.dashMetric;
12704
+ const vx = b.x - a.x;
12705
+ const vy = b.y - a.y;
12706
+ let segLen = m ? Math.hypot(m[0] * vx + m[2] * vy, m[1] * vx + m[3] * vy) : Math.hypot(vx, vy);
12649
12707
  let t0 = 0;
12650
12708
  while (segLen - t0 > remaining + 1e-9) {
12651
12709
  t0 += remaining;
@@ -12673,6 +12731,26 @@ function dashFigure(pts, closed, pen) {
12673
12731
  }
12674
12732
  return out.filter((d) => d.pts.length > 1);
12675
12733
  }
12734
+ var ARROW_ANCHOR = 20;
12735
+ function arrowAnchor(pp, atEnd, half) {
12736
+ const w = 2 * half;
12737
+ const tip = atEnd ? pp[pp.length - 1] : pp[0];
12738
+ const next = atEnd ? pp[pp.length - 2] : pp[1];
12739
+ const len = Math.hypot(next.x - tip.x, next.y - tip.y);
12740
+ const ux = (next.x - tip.x) / len;
12741
+ const uy = (next.y - tip.y) / len;
12742
+ const depth = 2 * w;
12743
+ const across = 2 * w / Math.sqrt(3);
12744
+ const base = { x: tip.x + ux * depth, y: tip.y + uy * depth };
12745
+ if (len > depth) {
12746
+ if (atEnd) {
12747
+ pp[pp.length - 1] = base;
12748
+ } else {
12749
+ pp[0] = base;
12750
+ }
12751
+ }
12752
+ return [tip, { x: base.x - uy * across, y: base.y + ux * across }, { x: base.x + uy * across, y: base.y - ux * across }];
12753
+ }
12676
12754
  var MIN_DASH = 1 / 256;
12677
12755
  function signedArea(pts) {
12678
12756
  let a = 0;
@@ -12723,6 +12801,19 @@ function widenFigures(figures, pen) {
12723
12801
  if (pp.length < 2) {
12724
12802
  continue;
12725
12803
  }
12804
+ const arrows = [];
12805
+ let startCapOverride = null;
12806
+ let endCapOverride = null;
12807
+ if (!pieceClosed && !(pen.dash && closed)) {
12808
+ if (piece.first && pen.startCap === ARROW_ANCHOR) {
12809
+ arrows.push(arrowAnchor(pp, false, h));
12810
+ startCapOverride = 0;
12811
+ }
12812
+ if (piece.last && pen.endCap === ARROW_ANCHOR) {
12813
+ arrows.push(arrowAnchor(pp, true, h));
12814
+ endCapOverride = 0;
12815
+ }
12816
+ }
12726
12817
  for (const [o1, o2] of bands) {
12727
12818
  if (pieceClosed) {
12728
12819
  polys.push(sidePoints(pp, true, o2, pen));
@@ -12733,11 +12824,16 @@ function widenFigures(figures, pen) {
12733
12824
  const right = sidePoints(pp, false, o1, pen).reverse();
12734
12825
  const fEnd = frame(pp[pp.length - 2], pp[pp.length - 1]);
12735
12826
  const f0 = frame(pp[1], pp[0]);
12736
- const endCap = piece.last && !(pen.dash && closed) ? pen.endCap : pen.dashCap;
12737
- const startCap = piece.first && !(pen.dash && closed) ? pen.startCap : pen.dashCap;
12827
+ const endCap = endCapOverride ?? (piece.last && !(pen.dash && closed) ? pen.endCap : pen.dashCap);
12828
+ const startCap = startCapOverride ?? (piece.first && !(pen.dash && closed) ? pen.startCap : pen.dashCap);
12738
12829
  const capEnd = capPoints(pp[pp.length - 1], fEnd, o1, o2, endCap, h);
12739
12830
  const capStart = capPoints(pp[0], f0, -o2, -o1, startCap, h);
12740
- polys.push([...left.slice(0, -1), ...capEnd, ...right.slice(1, -1), ...capStart]);
12831
+ const poly = [...left.slice(0, -1), ...capEnd, ...right.slice(1, -1), ...capStart];
12832
+ polys.push(poly);
12833
+ const sign = Math.sign(signedArea(poly));
12834
+ for (const arrow of arrows) {
12835
+ polys.push(Math.sign(signedArea(arrow)) === sign ? arrow : arrow.slice().reverse());
12836
+ }
12741
12837
  }
12742
12838
  }
12743
12839
  }
@@ -16651,8 +16747,11 @@ function boxCorners(box) {
16651
16747
  const { ax, ay, exx, exy, eyx, eyy } = box;
16652
16748
  return [ax + exx, ay + exy, ax, ay, ax + eyx, ay + eyy, ax + exx + eyx, ay + exy + eyy];
16653
16749
  }
16654
- function rectRasterPath(box) {
16655
- const c = boxCorners(box);
16750
+ function flipBox(box) {
16751
+ return { ax: box.ax + box.eyx, ay: box.ay + box.eyy, exx: box.exx, exy: box.exy, eyx: -box.eyx, eyy: -box.eyy };
16752
+ }
16753
+ function rectRasterPath(box, clockwise = false) {
16754
+ const c = boxCorners(clockwise ? flipBox(box) : box);
16656
16755
  const path = new GdiRasterPath();
16657
16756
  path.moveTo(c[0], c[1]);
16658
16757
  path.lineTo(c[2], c[3]);
@@ -16661,9 +16760,9 @@ function rectRasterPath(box) {
16661
16760
  path.closeFigure();
16662
16761
  return path;
16663
16762
  }
16664
- function ellipseRasterPath(box) {
16763
+ function ellipseRasterPath(box, clockwise = false) {
16665
16764
  const path = new GdiRasterPath();
16666
- path.addBeziers(ellipseBeziersBox(box), true);
16765
+ path.addBeziers(ellipseBeziersBox(box, clockwise), true);
16667
16766
  path.closeFigure();
16668
16767
  return path;
16669
16768
  }
@@ -16689,13 +16788,16 @@ function boxFrame(box) {
16689
16788
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
16690
16789
  return { l: 0, t: 0, r: w, b: h };
16691
16790
  }
16692
- function roundRectRasterPath(box, cw, ch) {
16791
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
16792
+ if (rectangle) {
16793
+ return rectRasterPath(box, clockwise);
16794
+ }
16693
16795
  const f = boxFrame(box);
16694
16796
  const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
16695
16797
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
16696
16798
  const pts = [];
16697
16799
  for (let i = 0; i < q.length; i += 2) {
16698
- pts.push(...map(q[i], q[i + 1]));
16800
+ pts.push(...map(q[i], clockwise ? f.t + f.b - q[i + 1] : q[i + 1]));
16699
16801
  }
16700
16802
  const path = new GdiRasterPath();
16701
16803
  for (let c = 0; c < 4; c++) {
@@ -16738,7 +16840,7 @@ function arcRasterPath(box, s, e, clockwise, kind, from, path = new GdiRasterPat
16738
16840
  pts.push(...map(bz[i], bz[i + 1]));
16739
16841
  }
16740
16842
  if (kind === "arcto" && from) {
16741
- path.lineTo(from[0], from[1]);
16843
+ path.continueAt(from[0], from[1]);
16742
16844
  path.addBeziers(pts, false);
16743
16845
  } else {
16744
16846
  path.addBeziers(pts, true);
@@ -19458,6 +19560,15 @@ function rasterPathOf(rCtx) {
19458
19560
  rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
19459
19561
  return rCtx.rasterPath;
19460
19562
  }
19563
+ function continueFigure(rCtx) {
19564
+ const path = rasterPathOf(rCtx);
19565
+ const [x, y] = currentFix(rCtx);
19566
+ const before = path.figures.length;
19567
+ path.continueAt(x, y);
19568
+ if (path.figures.length !== before) {
19569
+ gdiPathRecorder(rCtx).moveTo(x / 16, y / 16);
19570
+ }
19571
+ }
19461
19572
  function lineStyleOf(rCtx) {
19462
19573
  rCtx.lineStyle ?? (rCtx.lineStyle = { pos: 0 });
19463
19574
  return rCtx.lineStyle;
@@ -19473,6 +19584,9 @@ function currentFix(rCtx) {
19473
19584
  }
19474
19585
  return fixPoint(rCtx, state.curX, state.curY);
19475
19586
  }
19587
+ function uprightFixBox(rCtx, l, t, r, b) {
19588
+ return fixBox(rCtx, Math.min(l, r), Math.min(t, b), Math.max(l, r), Math.max(t, b));
19589
+ }
19476
19590
  function fixExtent(rCtx, v, axis) {
19477
19591
  const m = gdiDeviceMatrix(rCtx);
19478
19592
  const k = axis === 0 ? Math.hypot(m[0], m[1]) : Math.hypot(m[2], m[3]);
@@ -19515,6 +19629,7 @@ function handleLineTo(rCtx, dataOff, recSize) {
19515
19629
  const to = rotated ? gmapPoint(rCtx, lx, ly) : { x: gmx(rCtx, lx), y: gmy(rCtx, ly) };
19516
19630
  const fixTo = fixPoint(rCtx, lx, ly);
19517
19631
  if (inPath) {
19632
+ continueFigure(rCtx);
19518
19633
  gdiPathRecorder(rCtx).lineTo(to.x, to.y);
19519
19634
  rasterPathOf(rCtx).lineTo(fixTo[0], fixTo[1]);
19520
19635
  } else {
@@ -19549,7 +19664,8 @@ function handleRectangle(rCtx, dataOff, recSize) {
19549
19664
  const t = view.getInt32(dataOff + 4, true);
19550
19665
  const r = view.getInt32(dataOff + 8, true);
19551
19666
  const b = view.getInt32(dataOff + 12, true);
19552
- const box = fixBox(rCtx, l, t, r, b);
19667
+ const box = uprightFixBox(rCtx, l, t, r, b);
19668
+ const clockwise = rCtx.state.arcDirection === 2;
19553
19669
  if (!inPath) {
19554
19670
  resetLineStyle(rCtx);
19555
19671
  }
@@ -19568,14 +19684,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
19568
19684
  };
19569
19685
  if (inPath) {
19570
19686
  appendRect(gdiPathRecorder(rCtx));
19571
- rasterPathOf(rCtx).append(rectRasterPath(box));
19687
+ rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
19572
19688
  } else {
19573
19689
  paintGdiShape(rCtx, {
19574
19690
  build: (c) => {
19575
19691
  c.beginPath();
19576
19692
  appendRect(c);
19577
19693
  },
19578
- raster: () => rectRasterPath(box),
19694
+ raster: () => rectRasterPath(box, clockwise),
19579
19695
  rectangle: true,
19580
19696
  fill: true,
19581
19697
  stroke: true,
@@ -19590,14 +19706,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
19590
19706
  const h = gmh(rCtx, b - t);
19591
19707
  if (inPath) {
19592
19708
  gdiPathRecorder(rCtx).rect(x, y, w, h);
19593
- rasterPathOf(rCtx).append(rectRasterPath(box));
19709
+ rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
19594
19710
  } else if (needsPathBasedRectangle(rCtx)) {
19595
19711
  paintGdiShape(rCtx, {
19596
19712
  build: (c) => {
19597
19713
  c.beginPath();
19598
19714
  c.rect(x, y, w, h);
19599
19715
  },
19600
- raster: () => rectRasterPath(box),
19716
+ raster: () => rectRasterPath(box, clockwise),
19601
19717
  rectangle: true,
19602
19718
  fill: true,
19603
19719
  stroke: true,
@@ -19710,7 +19826,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
19710
19826
  gmh(rCtx, cornerH) / 2,
19711
19827
  null
19712
19828
  );
19713
- const raster = (box = fixBox(rCtx, l, t, r, b)) => roundRectRasterPath(box, fixExtent(rCtx, cornerW, 0), fixExtent(rCtx, cornerH, 1));
19829
+ const clockwise = rCtx.state.arcDirection === 2;
19830
+ const raster = (box = uprightFixBox(rCtx, l, t, r, b)) => roundRectRasterPath(box, fixExtent(rCtx, cornerW, 0), fixExtent(rCtx, cornerH, 1), clockwise, cornerW === 0 || cornerH === 0);
19714
19831
  if (inPath) {
19715
19832
  drawRoundRect(gdiPathRecorder(rCtx));
19716
19833
  rasterPathOf(rCtx).append(raster());
@@ -19721,7 +19838,7 @@ function handleRoundRect(rCtx, dataOff, recSize) {
19721
19838
  c.beginPath();
19722
19839
  drawRoundRect(c);
19723
19840
  },
19724
- raster: () => raster(curvedFixBox(rCtx, l, t, r, b)),
19841
+ raster: () => raster(curvedFixBox(rCtx, l, t, r, b, true)),
19725
19842
  roundPen: true,
19726
19843
  fill: true,
19727
19844
  stroke: true
@@ -19730,8 +19847,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
19730
19847
  }
19731
19848
  return true;
19732
19849
  }
19733
- function curvedFixBox(rCtx, l, t, r, b) {
19734
- const box = fixBox(rCtx, l, t, r, b);
19850
+ function curvedFixBox(rCtx, l, t, r, b, upright = false) {
19851
+ const box = upright ? uprightFixBox(rCtx, l, t, r, b) : fixBox(rCtx, l, t, r, b);
19735
19852
  if (rCtx.state.penStyle !== 5) {
19736
19853
  return box;
19737
19854
  }
@@ -19757,6 +19874,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
19757
19874
  const t = view.getInt32(dataOff + 4, true);
19758
19875
  const r = view.getInt32(dataOff + 8, true);
19759
19876
  const b = view.getInt32(dataOff + 12, true);
19877
+ const clockwise = rCtx.state.arcDirection === 2;
19760
19878
  const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
19761
19879
  cx: gmx(rCtx, (l + r) / 2),
19762
19880
  cy: gmy(rCtx, (t + b) / 2),
@@ -19766,7 +19884,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
19766
19884
  };
19767
19885
  if (inPath) {
19768
19886
  gdiPathRecorder(rCtx).ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
19769
- rasterPathOf(rCtx).append(ellipseRasterPath(fixBox(rCtx, l, t, r, b)));
19887
+ rasterPathOf(rCtx).append(ellipseRasterPath(uprightFixBox(rCtx, l, t, r, b), clockwise));
19770
19888
  } else {
19771
19889
  resetLineStyle(rCtx);
19772
19890
  paintGdiShape(rCtx, {
@@ -19774,7 +19892,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
19774
19892
  c.beginPath();
19775
19893
  c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
19776
19894
  },
19777
- raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b)),
19895
+ raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b, true), clockwise),
19778
19896
  roundPen: true,
19779
19897
  fill: true,
19780
19898
  stroke: true
@@ -19832,6 +19950,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
19832
19950
  from: currentFix(rCtx)
19833
19951
  });
19834
19952
  if (inPath) {
19953
+ if (isArcTo) {
19954
+ continueFigure(rCtx);
19955
+ }
19835
19956
  build(gdiPathRecorder(rCtx));
19836
19957
  const a = rasterArgs();
19837
19958
  arcRasterPath(a.box, a.s, a.e, clockwise, kind, a.from, rasterPathOf(rCtx));
@@ -19908,9 +20029,7 @@ function handleAngleArc(rCtx, dataOff, recSize) {
19908
20029
  }
19909
20030
  const e = [fixPts[fixPts.length - 2], fixPts[fixPts.length - 1]];
19910
20031
  const buildRaster = (path, from) => {
19911
- if (from) {
19912
- path.moveTo(from[0], from[1]);
19913
- }
20032
+ path.continueAt(from[0], from[1]);
19914
20033
  path.addBeziers(fixPts, false);
19915
20034
  };
19916
20035
  const params = gdiEllipseParams(rCtx, cx, cy, radius, radius);
@@ -19924,9 +20043,9 @@ function handleAngleArc(rCtx, dataOff, recSize) {
19924
20043
  c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, -a0, clockwise ? -a0 + span : -a0 - span, !clockwise);
19925
20044
  };
19926
20045
  if (inPath) {
19927
- const rec = gdiPathRecorder(rCtx);
19928
- buildCanvas(rec);
19929
- buildRaster(rasterPathOf(rCtx), rasterPathOf(rCtx).figures.length === 0 ? currentFix(rCtx) : null);
20046
+ continueFigure(rCtx);
20047
+ buildCanvas(gdiPathRecorder(rCtx));
20048
+ buildRaster(rasterPathOf(rCtx), currentFix(rCtx));
19930
20049
  } else {
19931
20050
  resetLineStyle(rCtx);
19932
20051
  const from = currentFix(rCtx);
@@ -20005,50 +20124,111 @@ function readColorAdjustment(view, off) {
20005
20124
  function isIdentityColorAdjustment(ca) {
20006
20125
  return !ca || ca.flags === 0 && ca.redGamma === 1e4 && ca.greenGamma === 1e4 && ca.blueGamma === 1e4 && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
20007
20126
  }
20008
- function colorfulnessScale(c) {
20009
- return c > 0 ? 1 + 2 * c / 100 : 1 + c / 100;
20127
+ var CONTRAST_GAIN = 1.88;
20128
+ var BRIGHTNESS_GAIN = 78.5;
20129
+ var COLORFULNESS_GAIN = 1.335;
20130
+ var TINT_RADIANS = -1.135;
20131
+ var RGB_TO_XYZ = [
20132
+ [0.4124, 0.3576, 0.1805],
20133
+ [0.2126, 0.7152, 0.0722],
20134
+ [0.0193, 0.1192, 0.9505]
20135
+ ];
20136
+ var XYZ_TO_RGB = invert3(RGB_TO_XYZ);
20137
+ var WHITE = RGB_TO_XYZ.map((row) => row[0] + row[1] + row[2]);
20138
+ var WHITE_D = WHITE[0] + 15 * WHITE[1] + 3 * WHITE[2];
20139
+ var WHITE_U = 4 * WHITE[0] / WHITE_D;
20140
+ var WHITE_V = 9 * WHITE[1] / WHITE_D;
20141
+ function invert3(m) {
20142
+ const [[a, b, c], [d, e, f], [g, h, i]] = m;
20143
+ const A = e * i - f * h;
20144
+ const B = f * g - d * i;
20145
+ const C = d * h - e * g;
20146
+ const det = a * A + b * B + c * C;
20147
+ return [
20148
+ [A / det, (c * h - b * i) / det, (b * f - c * e) / det],
20149
+ [B / det, (a * i - c * g) / det, (c * d - a * f) / det],
20150
+ [C / det, (b * g - a * h) / det, (a * e - b * d) / det]
20151
+ ];
20152
+ }
20153
+ function lightness(t) {
20154
+ return t > 8856e-6 ? 116 * Math.cbrt(t) - 16 : 903.3 * t;
20155
+ }
20156
+ function fromLightness(l) {
20157
+ return l > 8 ? ((l + 16) / 116) ** 3 : l / 903.3;
20010
20158
  }
20011
20159
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
20012
20160
  function colorAdjustmentMapper(ca) {
20013
20161
  const black = ca.referenceBlack / 1e4;
20014
20162
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
20015
20163
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
20016
- const curves = gammas.map((gamma) => {
20164
+ const negative = (ca.flags & CA_NEGATIVE) !== 0;
20165
+ const linear = gammas.map((gamma) => {
20017
20166
  const table = new Float64Array(256);
20018
20167
  for (let i = 0; i < 256; i++) {
20019
- let v = clamp012((i / 255 - black) / span);
20020
- v = Math.pow(v, gamma);
20021
- table[i] = ca.flags & CA_NEGATIVE ? 1 - v : v;
20168
+ const v = negative ? 1 - i / 255 : i / 255;
20169
+ table[i] = clamp012((v - black) / span) ** gamma;
20022
20170
  }
20023
20171
  return table;
20024
20172
  });
20025
- const chroma = colorfulnessScale(ca.colorfulness);
20026
- const contrast = 1 + ca.contrast / 100;
20027
- const brightness = 0.8 * ca.brightness / 100;
20028
- const tint = 0.3 * ca.redGreenTint / 100;
20173
+ const chroma = ca.colorfulness < 0 ? 1 + ca.colorfulness / 100 : 1 + COLORFULNESS_GAIN * ca.colorfulness / 100;
20174
+ const angle = TINT_RADIANS * ca.redGreenTint / 100;
20175
+ const cos = Math.cos(angle) * chroma;
20176
+ const sin = Math.sin(angle) * chroma;
20177
+ const slope = Math.exp(CONTRAST_GAIN * ca.contrast / 100);
20178
+ const offset = 50 - 50 * slope + BRIGHTNESS_GAIN * ca.brightness / 100;
20029
20179
  const log = (ca.flags & CA_LOG_FILTER) !== 0;
20030
- const out = (v) => {
20031
- v = clamp012(v);
20180
+ const colour = ca.colorfulness !== 0 || ca.redGreenTint !== 0;
20181
+ const out = (v, c) => {
20182
+ v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
20032
20183
  if (log) {
20033
20184
  v = Math.log10(1 + 9 * v);
20034
20185
  }
20035
20186
  return Math.round(v * 255);
20036
20187
  };
20037
20188
  return (rgb2) => {
20038
- const r = curves[0][rgb2 >> 16 & 255];
20039
- const g = curves[1][rgb2 >> 8 & 255];
20040
- const b = curves[2][rgb2 & 255];
20041
- let y = 0.299 * r + 0.587 * g + 0.114 * b;
20042
- let cr = (r - y) * chroma;
20043
- const cb = (b - y) * chroma;
20044
- y = (y - 0.5) * contrast + 0.5 + brightness;
20045
- cr += tint;
20046
- const r2 = y + cr;
20047
- const b2 = y + cb;
20048
- const g2 = (y - 0.299 * r2 - 0.114 * b2) / 0.587;
20049
- return out(r2) << 16 | out(g2) << 8 | out(b2);
20189
+ let r = linear[0][rgb2 >> 16 & 255];
20190
+ let g = linear[1][rgb2 >> 8 & 255];
20191
+ let b = linear[2][rgb2 & 255];
20192
+ if (colour) {
20193
+ const x = RGB_TO_XYZ[0][0] * r + RGB_TO_XYZ[0][1] * g + RGB_TO_XYZ[0][2] * b;
20194
+ const y = RGB_TO_XYZ[1][0] * r + RGB_TO_XYZ[1][1] * g + RGB_TO_XYZ[1][2] * b;
20195
+ const z = RGB_TO_XYZ[2][0] * r + RGB_TO_XYZ[2][1] * g + RGB_TO_XYZ[2][2] * b;
20196
+ const d = x + 15 * y + 3 * z;
20197
+ if (d > 1e-12 && y > 1e-12) {
20198
+ const du = 4 * x / d - WHITE_U;
20199
+ const dv = 9 * y / d - WHITE_V;
20200
+ const u = WHITE_U + du * cos - dv * sin;
20201
+ const v = Math.max(1e-6, WHITE_V + du * sin + dv * cos);
20202
+ const x2 = y * 9 * u / (4 * v);
20203
+ const z2 = y * (12 - 3 * u - 20 * v) / (4 * v);
20204
+ r = XYZ_TO_RGB[0][0] * x2 + XYZ_TO_RGB[0][1] * y + XYZ_TO_RGB[0][2] * z2;
20205
+ g = XYZ_TO_RGB[1][0] * x2 + XYZ_TO_RGB[1][1] * y + XYZ_TO_RGB[1][2] * z2;
20206
+ b = XYZ_TO_RGB[2][0] * x2 + XYZ_TO_RGB[2][1] * y + XYZ_TO_RGB[2][2] * z2;
20207
+ }
20208
+ }
20209
+ return out(r, 0) << 16 | out(g, 1) << 8 | out(b, 2);
20050
20210
  };
20051
20211
  }
20212
+ function colorAdjustRgb(rgbs, ca) {
20213
+ if (!ca || isIdentityColorAdjustment(ca)) {
20214
+ return;
20215
+ }
20216
+ const map = colorAdjustmentMapper(ca);
20217
+ const cache = /* @__PURE__ */ new Map();
20218
+ for (let i = 0; i + 2 < rgbs.length; i += 3) {
20219
+ const rgb2 = rgbs[i] << 16 | rgbs[i + 1] << 8 | rgbs[i + 2];
20220
+ let v = cache.get(rgb2);
20221
+ if (v === void 0) {
20222
+ v = map(rgb2);
20223
+ if (cache.size < 65536) {
20224
+ cache.set(rgb2, v);
20225
+ }
20226
+ }
20227
+ rgbs[i] = v >> 16 & 255;
20228
+ rgbs[i + 1] = v >> 8 & 255;
20229
+ rgbs[i + 2] = v & 255;
20230
+ }
20231
+ }
20052
20232
  function applyColorAdjustment(pixels, ca) {
20053
20233
  if (!ca || isIdentityColorAdjustment(ca)) {
20054
20234
  return;
@@ -20143,64 +20323,160 @@ function stretchGdi(src, sx, sy, sw, sh, dw, dh, mode) {
20143
20323
  }
20144
20324
  return { width: W, height: H, data };
20145
20325
  }
20146
- function axisBoxWeights(srcStart, srcLen, dstLen, reverse) {
20147
- const out = [];
20326
+ var FIX = 65536;
20327
+ function halftoneNearest(i, srcLen, dstLen) {
20328
+ const num = (2 * i + 1) * srcLen;
20329
+ const den = 2 * dstLen;
20330
+ let k = Math.floor(num / den);
20331
+ if (k * den === num) {
20332
+ k--;
20333
+ }
20334
+ return Math.max(0, Math.min(srcLen - 1, k));
20335
+ }
20336
+ function halftoneAxis(start, srcLen, dstLen, reverse) {
20337
+ const taps = [];
20148
20338
  if (dstLen <= 0 || srcLen <= 0) {
20149
- return out;
20339
+ return taps;
20150
20340
  }
20151
- const at = (k) => reverse ? srcStart + srcLen - 1 - k : srcStart + k;
20341
+ const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
20342
+ if (dstLen >= srcLen) {
20343
+ for (let i = 0; i < dstLen; i++) {
20344
+ taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
20345
+ }
20346
+ return taps;
20347
+ }
20348
+ const step = Math.ceil(srcLen * FIX / dstLen);
20152
20349
  for (let i = 0; i < dstLen; i++) {
20153
- const a = i * srcLen;
20154
- const b = (i + 1) * srcLen;
20155
- const taps = [];
20156
- for (let k = Math.floor(a / dstLen); k * dstLen < b; k++) {
20157
- const overlap = Math.min(b, (k + 1) * dstLen) - Math.max(a, k * dstLen);
20350
+ const a = i * step;
20351
+ const b = (i + 1) * step;
20352
+ const run = [];
20353
+ for (let k = Math.floor(a / FIX); k * FIX < b && k < srcLen; k++) {
20354
+ const overlap = Math.min(b, (k + 1) * FIX) - Math.max(a, k * FIX);
20158
20355
  if (overlap > 0) {
20159
- taps.push([at(k), overlap / srcLen]);
20356
+ run.push([at(k), overlap]);
20357
+ }
20358
+ }
20359
+ taps.push(run);
20360
+ }
20361
+ return taps;
20362
+ }
20363
+ var SPECKLE_OVERSHOOT = 4;
20364
+ function halftoneDespeckle(px, w, h) {
20365
+ const src = px.slice();
20366
+ const at = (x, y, c) => src[(y * w + x) * 3 + c];
20367
+ const over = (v) => v < -SPECKLE_OVERSHOOT * 4 || v > (255 + SPECKLE_OVERSHOOT) * 4;
20368
+ for (let y = 0; y < h; y++) {
20369
+ const up = y > 0 ? y - 1 : Math.min(h - 1, y + 1);
20370
+ const down = y < h - 1 ? y + 1 : Math.max(0, y - 1);
20371
+ for (let x = 0; x < w; x++) {
20372
+ const left = x > 0 ? x - 1 : Math.min(w - 1, x + 1);
20373
+ const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
20374
+ let hClip = 0;
20375
+ let vClip = 0;
20376
+ for (let c = 0; c < 3; c++) {
20377
+ const v = at(x, y, c);
20378
+ if (over(6 * v - at(left, y, c) - at(right, y, c))) {
20379
+ hClip++;
20380
+ }
20381
+ if (over(6 * v - at(x, up, c) - at(x, down, c))) {
20382
+ vClip++;
20383
+ }
20384
+ }
20385
+ if (hClip < 2 || vClip < 2) {
20386
+ continue;
20387
+ }
20388
+ for (let c = 0; c < 3; c++) {
20389
+ px[(y * w + x) * 3 + c] = at(left, y, c) + 2 * at(x, y, c) + at(right, y, c) + 2 >> 2;
20160
20390
  }
20161
20391
  }
20162
- out.push(taps);
20163
20392
  }
20164
- return out;
20165
20393
  }
20166
- function stretchHalftone(src, sx, sy, sw, sh, dw, dh) {
20394
+ function halftoneSharpen(px, w, h) {
20395
+ const src = px.slice();
20396
+ const at = (x, y, c) => src[(Math.max(0, Math.min(h - 1, y)) * w + Math.max(0, Math.min(w - 1, x))) * 3 + c];
20397
+ for (let y = 0; y < h; y++) {
20398
+ for (let x = 0; x < w; x++) {
20399
+ for (let c = 0; c < 3; c++) {
20400
+ const v = at(x, y, c);
20401
+ const sum = at(x - 1, y, c) + at(x + 1, y, c) + at(x, y - 1, c) + at(x, y + 1, c);
20402
+ const out = v + Math.floor((4 * v - sum) / 8);
20403
+ px[(y * w + x) * 3 + c] = out < 0 ? 0 : out > 255 ? 255 : out;
20404
+ }
20405
+ }
20406
+ }
20407
+ }
20408
+ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
20167
20409
  const W = Math.max(0, Math.round(Math.abs(dw)));
20168
20410
  const H = Math.max(0, Math.round(Math.abs(dh)));
20411
+ const SW = Math.round(Math.abs(sw));
20412
+ const SH = Math.round(Math.abs(sh));
20413
+ const x0 = Math.round(Math.min(sx, sx + sw));
20414
+ const y0 = Math.round(Math.min(sy, sy + sh));
20169
20415
  const flipX = dw < 0 !== sw < 0;
20170
20416
  const flipY = dh < 0 !== sh < 0;
20171
- const cols = axisBoxWeights(Math.round(Math.min(sx, sx + sw)), Math.round(Math.abs(sw)), W, flipX);
20172
- const rows = axisBoxWeights(Math.round(Math.min(sy, sy + sh)), Math.round(Math.abs(sh)), H, flipY);
20173
20417
  const data = new Uint8ClampedArray(W * H * 4);
20174
- const s = src.data;
20418
+ if (W === 0 || H === 0 || SW === 0 || SH === 0) {
20419
+ return { width: W, height: H, data };
20420
+ }
20421
+ const rect = new Int32Array(SW * SH * 3);
20422
+ for (let y = 0; y < SH; y++) {
20423
+ const yy = y0 + y;
20424
+ for (let x = 0; x < SW; x++) {
20425
+ const xx = x0 + x;
20426
+ if (yy < 0 || yy >= src.height || xx < 0 || xx >= src.width) {
20427
+ continue;
20428
+ }
20429
+ const i = (yy * src.width + xx) * 4;
20430
+ const o = (y * SW + x) * 3;
20431
+ rect[o] = src.data[i];
20432
+ rect[o + 1] = src.data[i + 1];
20433
+ rect[o + 2] = src.data[i + 2];
20434
+ }
20435
+ }
20436
+ const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
20437
+ const reducing = W < SW && H < SH;
20438
+ if (enlarging) {
20439
+ halftoneDespeckle(rect, SW, SH);
20440
+ }
20441
+ if (adjust) {
20442
+ adjust(rect);
20443
+ }
20444
+ const cols = halftoneAxis(0, SW, W, flipX);
20445
+ const rows = halftoneAxis(0, SH, H, flipY);
20446
+ const out = new Int32Array(W * H * 3);
20175
20447
  for (let y = 0; y < H; y++) {
20176
- const ys = rows[y] ?? [];
20448
+ const ys = rows[y];
20177
20449
  for (let x = 0; x < W; x++) {
20178
- const xs = cols[x] ?? [];
20450
+ const xs = cols[x];
20179
20451
  let r = 0;
20180
20452
  let g = 0;
20181
20453
  let b = 0;
20454
+ let total = 0;
20182
20455
  for (const [yy, wy] of ys) {
20183
- if (yy < 0 || yy >= src.height) {
20184
- continue;
20185
- }
20186
20456
  for (const [xx, wx] of xs) {
20187
- if (xx < 0 || xx >= src.width) {
20188
- continue;
20189
- }
20190
20457
  const w = wx * wy;
20191
- const i = (yy * src.width + xx) * 4;
20192
- r += s[i] * w;
20193
- g += s[i + 1] * w;
20194
- b += s[i + 2] * w;
20458
+ total += w;
20459
+ const i = (yy * SW + xx) * 3;
20460
+ r += rect[i] * w;
20461
+ g += rect[i + 1] * w;
20462
+ b += rect[i + 2] * w;
20195
20463
  }
20196
20464
  }
20197
- const o = (y * W + x) * 4;
20198
- data[o] = Math.floor(r + 1e-6);
20199
- data[o + 1] = Math.floor(g + 1e-6);
20200
- data[o + 2] = Math.floor(b + 1e-6);
20201
- data[o + 3] = 255;
20465
+ const o = (y * W + x) * 3;
20466
+ out[o] = Math.floor((2 * r + total) / (2 * total));
20467
+ out[o + 1] = Math.floor((2 * g + total) / (2 * total));
20468
+ out[o + 2] = Math.floor((2 * b + total) / (2 * total));
20202
20469
  }
20203
20470
  }
20471
+ if (reducing) {
20472
+ halftoneSharpen(out, W, H);
20473
+ }
20474
+ for (let i = 0, o = 0; i < W * H; i++, o += 3) {
20475
+ data[i * 4] = out[o];
20476
+ data[i * 4 + 1] = out[o + 1];
20477
+ data[i * 4 + 2] = out[o + 2];
20478
+ data[i * 4 + 3] = 255;
20479
+ }
20204
20480
  return { width: W, height: H, data };
20205
20481
  }
20206
20482
 
@@ -20226,7 +20502,7 @@ function patternOperand(rCtx, brush) {
20226
20502
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
20227
20503
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
20228
20504
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
20229
- const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
20505
+ const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, decoded.adjust) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
20230
20506
  if (px.width === 0 || px.height === 0) {
20231
20507
  return;
20232
20508
  }
@@ -20254,15 +20530,14 @@ function decodeSource(rCtx, req) {
20254
20530
  if (!image) {
20255
20531
  return null;
20256
20532
  }
20257
- if (req.stretch && rCtx.state.stretchBltMode === HALFTONE) {
20258
- applyColorAdjustment(image, rCtx.state.colorAdjustment);
20259
- }
20533
+ const ca = rCtx.state.colorAdjustment;
20534
+ const adjust = req.stretch && rCtx.state.stretchBltMode === HALFTONE && ca ? (rgb2) => colorAdjustRgb(rgb2, ca) : void 0;
20260
20535
  let { sy } = req;
20261
20536
  if (req.dibOrigin === "bottom-left" && rCtx.view.getInt32(req.source.bmi + 8, true) > 0) {
20262
20537
  sy = image.height - sy - req.sh;
20263
20538
  }
20264
20539
  return {
20265
- decoded: { pixels: image },
20540
+ decoded: { pixels: image, adjust },
20266
20541
  req: { ...req, sy }
20267
20542
  };
20268
20543
  }
@@ -20418,6 +20693,9 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
20418
20693
  return;
20419
20694
  }
20420
20695
  src = decoded.decoded.pixels;
20696
+ if (decoded.decoded.adjust) {
20697
+ applyColorAdjustment(src, rCtx.state.colorAdjustment);
20698
+ }
20421
20699
  srcX = decoded.req.sx;
20422
20700
  srcY = decoded.req.sy;
20423
20701
  }
@@ -22773,9 +23051,12 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
22773
23051
  }
22774
23052
  };
22775
23053
  if (inPath) {
23054
+ if (isTo) {
23055
+ continueFigure(rCtx);
23056
+ }
22776
23057
  build(gdiPathRecorder(rCtx));
22777
23058
  rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
22778
- buildRaster(rCtx.rasterPath, isTo && rCtx.rasterPath.figures.length === 0 ? currentFix(rCtx) : null);
23059
+ buildRaster(rCtx.rasterPath, null);
22779
23060
  } else {
22780
23061
  const from = isTo ? currentFix(rCtx) : null;
22781
23062
  const start = isTo ? gmapPoint(rCtx, state.curX, state.curY) : null;
@@ -22798,10 +23079,11 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
22798
23079
  fillRule: state.polyFillMode === 2 ? "nonzero" : "evenodd"
22799
23080
  });
22800
23081
  }
22801
- if (count > 0) {
23082
+ if (isTo && count > 0) {
22802
23083
  const [x, y] = readPt(count - 1);
22803
23084
  state.curX = x;
22804
23085
  state.curY = y;
23086
+ rCtx.curFix = void 0;
22805
23087
  }
22806
23088
  }
22807
23089
  function handlePoly32(rCtx, recType, offset, dataOff, recSize) {
@@ -22906,9 +23188,11 @@ function handlePolyDraw(rCtx, offset, dataOff, recSize, pointSize) {
22906
23188
  };
22907
23189
  const startsWithMove = (typeAt(0) & ~PT_CLOSEFIGURE) === PT_MOVETO;
22908
23190
  if (inPath) {
23191
+ if (!startsWithMove) {
23192
+ continueFigure(rCtx);
23193
+ }
22909
23194
  rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
22910
- const fresh = rCtx.rasterPath.figures.length === 0;
22911
- walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, !startsWithMove && fresh ? currentFix(rCtx) : null, null);
23195
+ walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, null, null);
22912
23196
  } else {
22913
23197
  const from = startsWithMove ? null : currentFix(rCtx);
22914
23198
  const fromPx = startsWithMove ? null : gmapPoint(rCtx, state.curX, state.curY);
@@ -23879,12 +24163,12 @@ function hslToRgb(h, s, l) {
23879
24163
  const [r, g, b] = hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];
23880
24164
  return [r + m, g + m, b + m];
23881
24165
  }
23882
- function applyHueSaturationLightness(src, hue, saturation, lightness) {
24166
+ function applyHueSaturationLightness(src, hue, saturation, lightness2) {
23883
24167
  const out = new Uint8ClampedArray(src);
23884
24168
  const push = (v, t) => t >= 0 ? v + (1 - v) * (t / 100) : v * (1 + t / 100);
23885
24169
  for (let i = 0; i < src.length; i += 4) {
23886
24170
  const [h, s, l] = rgbToHsl(src[i] / 255, src[i + 1] / 255, src[i + 2] / 255);
23887
- const [r, g, b] = hslToRgb(h + hue, push(s, saturation), push(l, lightness));
24171
+ const [r, g, b] = hslToRgb(h + hue, push(s, saturation), push(l, lightness2));
23888
24172
  out[i] = Math.round(r * 255);
23889
24173
  out[i + 1] = Math.round(g * 255);
23890
24174
  out[i + 2] = Math.round(b * 255);
@@ -24575,6 +24859,7 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
24575
24859
  }
24576
24860
  };
24577
24861
  if (pen) {
24862
+ const nibDashed = !!nib && penDashArray(pen).length > 0;
24578
24863
  applyPlusPenStyle(ctx, pen, widthScale);
24579
24864
  const mode = plusRasterMode(rCtx);
24580
24865
  let sampler = pen.brush && !isSvgContext(ctx) ? brushSampler(rCtx, pen.brush) : null;
@@ -24589,7 +24874,7 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
24589
24874
  if (mode !== "canvas" && size && strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, closedFigure)) {
24590
24875
  return;
24591
24876
  }
24592
- const outline = hasCustomCap(pen) ? penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) : null;
24877
+ const outline = hasCustomCap(pen) || nibDashed ? penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) : null;
24593
24878
  let box = null;
24594
24879
  if (size && outline) {
24595
24880
  box = deviceBounds(outline.flat(), device, size, 2);
@@ -24619,7 +24904,7 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
24619
24904
  }
24620
24905
  }
24621
24906
  const paint = pen.brush ? brushPaint2(rCtx, pen.brush) : pen.color;
24622
- if ((hasCustomCap(pen) || nib && typeof paint !== "string") && fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure)) {
24907
+ if ((hasCustomCap(pen) || nibDashed || nib && typeof paint !== "string") && fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure)) {
24623
24908
  return;
24624
24909
  }
24625
24910
  ctx.strokeStyle = paint;
@@ -24641,7 +24926,9 @@ function resolvePenTransform(pen) {
24641
24926
  return { pen: { ...plain, width: pen.width === 0 ? 1 : pen.width }, nib: null };
24642
24927
  }
24643
24928
  if (Math.abs(sx - sy) <= 1e-6 * sx && Math.abs(dot) <= 1e-6 * sx * sy) {
24644
- return { pen: { ...plain, width: pen.width * sx }, nib: null };
24929
+ const pattern = pen.dashStyle === 5 ? pen.dashPattern : DASH_PATTERNS[pen.dashStyle];
24930
+ const dashed = pattern && pattern.length > 0 ? { dashStyle: 5, dashPattern: pattern.map((v) => v / sx), dashOffset: (pen.dashOffset ?? 0) / sx } : {};
24931
+ return { pen: { ...plain, ...dashed, width: pen.width * sx }, nib: null };
24645
24932
  }
24646
24933
  return { pen: plain, nib: [a, b, c, d, 0, 0] };
24647
24934
  }
@@ -24666,7 +24953,7 @@ function penFrame(device, nib) {
24666
24953
  toDevice: mulMatrix(full, [1 / unit, 0, 0, 1 / unit, 0, 0])
24667
24954
  };
24668
24955
  }
24669
- function framePen(pen, unit) {
24956
+ function framePen(pen, unit, nib = null) {
24670
24957
  const dash = penDashArray(pen).map((v) => v * unit);
24671
24958
  return {
24672
24959
  half: (pen.width || 1) * unit / 2,
@@ -24677,6 +24964,7 @@ function framePen(pen, unit) {
24677
24964
  dashCap: pen.dashCap ?? 0,
24678
24965
  dash: dash.length > 0 ? dash : null,
24679
24966
  dashOffset: (pen.dashOffset ?? 0) * (pen.width || 1) * unit,
24967
+ dashMetric: nib ? [nib[0], nib[1], nib[2], nib[3]] : void 0,
24680
24968
  compound: pen.compound ?? null,
24681
24969
  inset: pen.alignment === 1
24682
24970
  };
@@ -24704,7 +24992,7 @@ function strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, c
24704
24992
  figures = figures.map((f) => ({ ...f, closed: true }));
24705
24993
  }
24706
24994
  const width = (pen.width || 1) * frame2.unit;
24707
- const devicePen = framePen(pen, frame2.unit);
24995
+ const devicePen = framePen(pen, frame2.unit, nib);
24708
24996
  const antialias = mode === "gdiplus-aa";
24709
24997
  const toFix = (poly) => {
24710
24998
  const m = frame2.toDevice;
@@ -24796,7 +25084,7 @@ function penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) {
24796
25084
  figures = figures.map((f) => ({ ...f, closed: true }));
24797
25085
  }
24798
25086
  const width = (pen.width || 1) * frame2.unit;
24799
- const devicePen = framePen(pen, frame2.unit);
25087
+ const devicePen = framePen(pen, frame2.unit, nib);
24800
25088
  const outline = hasCustomCap(pen) ? customCapOutline(figures, width, devicePen, pen) : widenFigures(figures, devicePen);
24801
25089
  const m = mulMatrix(deviceInv, frame2.toDevice);
24802
25090
  return outline.map((poly) => poly.map((p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] })));
@@ -27364,7 +27652,8 @@ function wmfSetDibToDev(p, d, end) {
27364
27652
  const hv = new DataView(bmi.buffer);
27365
27653
  const fullH = hv.getInt32(8, true);
27366
27654
  hv.setInt32(8, fullH < 0 ? -scanCount : scanCount, true);
27367
- const [ox, oy] = deviceDest(p, dx, dy, 0, 0);
27655
+ const [mx, oy] = deviceDest(p, dx, dy, 0, 0);
27656
+ const ox = p.layout & 1 ? mx - w : mx;
27368
27657
  playStretchDibits(p, 13369376, ox, oy + (sy + h - hi), w, hi - lo, sx, lo - startScan, w, hi - lo, { bmi, bits: dib.bits }, true);
27369
27658
  }
27370
27659
 
@@ -27711,25 +28000,30 @@ function wmfRectangle(p, l, t, r, b) {
27711
28000
  c.beginPath();
27712
28001
  c.rect(cr.x, cr.y, cr.w, cr.h);
27713
28002
  },
27714
- raster: () => rectRasterPath(fixBoxOf(box)),
28003
+ raster: () => rectRasterPath(fixBoxOf(box), clockwiseOf(p)),
27715
28004
  rectangle: true,
27716
28005
  fill: true,
27717
28006
  stroke: true,
27718
28007
  axisRect: { interior: rectInterior(p, box) }
27719
28008
  });
27720
28009
  }
28010
+ function compatCorner(p, w, h) {
28011
+ const m = gdiDeviceMatrix(p.rCtx);
28012
+ const cw = Math.round(Math.abs(w * m[0]) * 16);
28013
+ const ch = Math.round(Math.abs(h * m[3]) * 16);
28014
+ const ux = 16 * p.kx;
28015
+ const uy = 16 * p.ky;
28016
+ if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
28017
+ return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
28018
+ }
28019
+ return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
28020
+ }
27721
28021
  function wmfRoundRect(p, l, t, r, b, w, h) {
27722
28022
  const box = compatBox(p, l, t, r, b, { curved: true });
27723
28023
  if (box.x1 < box.x0 || box.y1 < box.y0) {
27724
28024
  return;
27725
28025
  }
27726
- const m = gdiDeviceMatrix(p.rCtx);
27727
- let cw = Math.round(Math.abs(w * m[0]) * 16);
27728
- let ch = Math.round(Math.abs(h * m[3]) * 16);
27729
- if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
27730
- cw = Math.floor(cw / 32) * 32;
27731
- ch = Math.floor(ch / 32) * 32;
27732
- }
28026
+ const [cw, ch] = compatCorner(p, w, h);
27733
28027
  const cr = canvasRect(box);
27734
28028
  paintGdiShape(p.rCtx, {
27735
28029
  build: (c) => {
@@ -27754,7 +28048,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
27754
28048
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
27755
28049
  c.closePath();
27756
28050
  },
27757
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch),
28051
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
27758
28052
  roundPen: true,
27759
28053
  fill: true,
27760
28054
  stroke: true
@@ -27771,24 +28065,31 @@ function wmfEllipse(p, l, t, r, b) {
27771
28065
  c.beginPath();
27772
28066
  c.ellipse(cr.x + cr.w / 2, cr.y + cr.h / 2, cr.w / 2, cr.h / 2, 0, 0, Math.PI * 2);
27773
28067
  },
27774
- raster: () => ellipseRasterPath(fixBoxOf(box)),
28068
+ raster: () => ellipseRasterPath(fixBoxOf(box), clockwiseOf(p)),
27775
28069
  roundPen: true,
27776
28070
  fill: true,
27777
28071
  stroke: true
27778
28072
  });
27779
28073
  }
27780
- function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
27781
- const box = compatBox(p, l, t, r, b, { curved: kind !== "arc" });
28074
+ function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
28075
+ const box = compatBox(p, l, t, r, b, { curved: kind === "chord" || kind === "pie" });
27782
28076
  if (box.x1 < box.x0 || box.y1 < box.y0) {
27783
- return;
28077
+ return null;
27784
28078
  }
27785
28079
  const raw = compatBox(p, l, t, r, b, { exclusive: false });
27786
- const rdx = (box.x0 + box.x1 - raw.x0 - raw.x1) / 2;
27787
- const rdy = (box.y0 + box.y1 - raw.y0 - raw.y1) / 2;
27788
- const s0 = fixPoint(p.rCtx, xs, ys);
27789
- const e0 = fixPoint(p.rCtx, xe, ye);
27790
- const s = [s0[0] + rdx, s0[1] + rdy];
27791
- const e = [e0[0] + rdx, e0[1] + rdy];
28080
+ const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
28081
+ const radial = (x, y) => {
28082
+ const [fx, fy] = fixPoint(p.rCtx, x, y);
28083
+ return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
28084
+ };
28085
+ return { box, s: radial(xs, ys), e: radial(xe, ye) };
28086
+ }
28087
+ function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
28088
+ const geom = compatArc(p, kind, l, t, r, b, xs, ys, xe, ye);
28089
+ if (!geom) {
28090
+ return;
28091
+ }
28092
+ const { box, s, e } = geom;
27792
28093
  const clockwise = p.rCtx.state.arcDirection === 2;
27793
28094
  const cr = canvasRect(box);
27794
28095
  const cx = cr.x + cr.w / 2;
@@ -27814,6 +28115,9 @@ function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
27814
28115
  stroke: true
27815
28116
  });
27816
28117
  }
28118
+ function clockwiseOf(p) {
28119
+ return p.rCtx.state.arcDirection === 2;
28120
+ }
27817
28121
  function penIsNull(p) {
27818
28122
  return (p.rCtx.state.penStyle & 15) === PS_NULL3;
27819
28123
  }