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