emf-converter 4.3.1 → 4.3.2
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/README.md +1 -1
- package/dist/index.js +170 -70
- package/dist/index.mjs +170 -70
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ A three-phase pipeline: **parse → replay → export**. The header parser reads
|
|
|
196
196
|
|
|
197
197
|
Everything below is verified against output painted by Windows itself; `src/gdi-parity.fixture.test.ts` holds the per-fixture bounds.
|
|
198
198
|
|
|
199
|
-
- **GDI shapes** (`gdi-raster.ts`, `gdi-raster-widen.ts`): 28.4 fixed-point geometry; GDI's fill rule (ALTERNATE/WINDING); one-pixel lines by GDI's diamond rule with its tie-breaks; GDI's own Bezier flattener, ellipse, rounded-rectangle, arc (GDI's trigonometry table and `SetArcDirection`), chord and pie construction; cosmetic dash styles (dash 18/6, dot 3/3, ...) and geometric dashes; wide pens widened from GDI's own pen polygons with every cap and join and the miter limit; rotated and skewed world transforms. Pixel-exact on the shape fixtures.
|
|
199
|
+
- **GDI shapes** (`gdi-raster.ts`, `gdi-raster-widen.ts`): 28.4 fixed-point geometry; GDI's fill rule (ALTERNATE/WINDING); one-pixel lines by GDI's diamond rule with its tie-breaks; GDI's own Bezier flattener, ellipse, rounded-rectangle, arc (GDI's trigonometry table and `SetArcDirection`), chord and pie construction; cosmetic dash styles (dash 18/6, dot 3/3, ...) and geometric dashes; wide pens widened from GDI's own pen polygons with every cap and join and the miter limit; rotated and skewed world transforms. Pixel-exact on the shape fixtures. A path bracket's GDI geometry has the points, point types, figure starts and direction that Windows' `GetPath` reports (checked against the Windows data in Wine's `gdi32` path tests).
|
|
200
200
|
- **Raster operations**: all 256 ROP3 codes for `BitBlt`/`StretchBlt`/`StretchDIBits`/`PatBlt`, exact per bit against the destination, brush and source, with GDI's stretch modes, mirrored rectangles and rotated destinations (each device pixel mapped back to one source texel). Every `SetROP2` mode, including the bitwise AND/OR/XOR family, for shapes, paths and pattern-brush fills.
|
|
201
201
|
- **Brushes**: hatch, monochrome and DIB pattern brushes anchored to the brush origin, with the background mode; GDI+ solid, hatch, texture (bilinear, WrapMode-aware, as GDI+ samples them), linear gradients (GDI+'s own interpolation table: preset colours, blend shapes, gamma correction, every WrapMode) and path gradients (true boundary-shaped falloff, every WrapMode).
|
|
202
202
|
- **Clipping**: every GDI and GDI+ region combine mode exact for every clip (vector where possible, otherwise scan-converted to pixel regions, which is how GDI stores them), path clips with their fill mode, and region offsets.
|
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,
|
|
1777
|
+
P(full, rightUpper);
|
|
1759
1778
|
P(hCtl, zero);
|
|
1760
1779
|
P(hMidLo, zero);
|
|
1761
1780
|
P(hCtl2, zero);
|
|
1762
|
-
P(zero,
|
|
1781
|
+
P(zero, leftUpper);
|
|
1763
1782
|
P(zero, vMidLo);
|
|
1764
|
-
P(zero,
|
|
1783
|
+
P(zero, leftLower);
|
|
1765
1784
|
P(hCtl2, full);
|
|
1766
1785
|
P(hMidHi, full);
|
|
1767
1786
|
P(hCtl, full);
|
|
1768
|
-
P(full,
|
|
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
|
-
|
|
1786
|
-
|
|
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 =
|
|
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() {
|
|
@@ -16653,8 +16708,11 @@ function boxCorners(box) {
|
|
|
16653
16708
|
const { ax, ay, exx, exy, eyx, eyy } = box;
|
|
16654
16709
|
return [ax + exx, ay + exy, ax, ay, ax + eyx, ay + eyy, ax + exx + eyx, ay + exy + eyy];
|
|
16655
16710
|
}
|
|
16656
|
-
function
|
|
16657
|
-
|
|
16711
|
+
function flipBox(box) {
|
|
16712
|
+
return { ax: box.ax + box.eyx, ay: box.ay + box.eyy, exx: box.exx, exy: box.exy, eyx: -box.eyx, eyy: -box.eyy };
|
|
16713
|
+
}
|
|
16714
|
+
function rectRasterPath(box, clockwise = false) {
|
|
16715
|
+
const c = boxCorners(clockwise ? flipBox(box) : box);
|
|
16658
16716
|
const path = new GdiRasterPath();
|
|
16659
16717
|
path.moveTo(c[0], c[1]);
|
|
16660
16718
|
path.lineTo(c[2], c[3]);
|
|
@@ -16663,9 +16721,9 @@ function rectRasterPath(box) {
|
|
|
16663
16721
|
path.closeFigure();
|
|
16664
16722
|
return path;
|
|
16665
16723
|
}
|
|
16666
|
-
function ellipseRasterPath(box) {
|
|
16724
|
+
function ellipseRasterPath(box, clockwise = false) {
|
|
16667
16725
|
const path = new GdiRasterPath();
|
|
16668
|
-
path.addBeziers(ellipseBeziersBox(box), true);
|
|
16726
|
+
path.addBeziers(ellipseBeziersBox(box, clockwise), true);
|
|
16669
16727
|
path.closeFigure();
|
|
16670
16728
|
return path;
|
|
16671
16729
|
}
|
|
@@ -16691,13 +16749,16 @@ function boxFrame(box) {
|
|
|
16691
16749
|
const h = Math.round(Math.hypot(box.eyx, box.eyy));
|
|
16692
16750
|
return { l: 0, t: 0, r: w, b: h };
|
|
16693
16751
|
}
|
|
16694
|
-
function roundRectRasterPath(box, cw, ch) {
|
|
16752
|
+
function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
|
|
16753
|
+
if (rectangle) {
|
|
16754
|
+
return rectRasterPath(box, clockwise);
|
|
16755
|
+
}
|
|
16695
16756
|
const f = boxFrame(box);
|
|
16696
16757
|
const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
|
|
16697
16758
|
const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
|
|
16698
16759
|
const pts = [];
|
|
16699
16760
|
for (let i = 0; i < q.length; i += 2) {
|
|
16700
|
-
pts.push(...map(q[i], q[i + 1]));
|
|
16761
|
+
pts.push(...map(q[i], clockwise ? f.t + f.b - q[i + 1] : q[i + 1]));
|
|
16701
16762
|
}
|
|
16702
16763
|
const path = new GdiRasterPath();
|
|
16703
16764
|
for (let c = 0; c < 4; c++) {
|
|
@@ -16740,7 +16801,7 @@ function arcRasterPath(box, s, e, clockwise, kind, from, path = new GdiRasterPat
|
|
|
16740
16801
|
pts.push(...map(bz[i], bz[i + 1]));
|
|
16741
16802
|
}
|
|
16742
16803
|
if (kind === "arcto" && from) {
|
|
16743
|
-
path.
|
|
16804
|
+
path.continueAt(from[0], from[1]);
|
|
16744
16805
|
path.addBeziers(pts, false);
|
|
16745
16806
|
} else {
|
|
16746
16807
|
path.addBeziers(pts, true);
|
|
@@ -19460,6 +19521,15 @@ function rasterPathOf(rCtx) {
|
|
|
19460
19521
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
19461
19522
|
return rCtx.rasterPath;
|
|
19462
19523
|
}
|
|
19524
|
+
function continueFigure(rCtx) {
|
|
19525
|
+
const path = rasterPathOf(rCtx);
|
|
19526
|
+
const [x, y] = currentFix(rCtx);
|
|
19527
|
+
const before = path.figures.length;
|
|
19528
|
+
path.continueAt(x, y);
|
|
19529
|
+
if (path.figures.length !== before) {
|
|
19530
|
+
gdiPathRecorder(rCtx).moveTo(x / 16, y / 16);
|
|
19531
|
+
}
|
|
19532
|
+
}
|
|
19463
19533
|
function lineStyleOf(rCtx) {
|
|
19464
19534
|
rCtx.lineStyle ?? (rCtx.lineStyle = { pos: 0 });
|
|
19465
19535
|
return rCtx.lineStyle;
|
|
@@ -19475,6 +19545,9 @@ function currentFix(rCtx) {
|
|
|
19475
19545
|
}
|
|
19476
19546
|
return fixPoint(rCtx, state.curX, state.curY);
|
|
19477
19547
|
}
|
|
19548
|
+
function uprightFixBox(rCtx, l, t, r, b) {
|
|
19549
|
+
return fixBox(rCtx, Math.min(l, r), Math.min(t, b), Math.max(l, r), Math.max(t, b));
|
|
19550
|
+
}
|
|
19478
19551
|
function fixExtent(rCtx, v, axis) {
|
|
19479
19552
|
const m = gdiDeviceMatrix(rCtx);
|
|
19480
19553
|
const k = axis === 0 ? Math.hypot(m[0], m[1]) : Math.hypot(m[2], m[3]);
|
|
@@ -19517,6 +19590,7 @@ function handleLineTo(rCtx, dataOff, recSize) {
|
|
|
19517
19590
|
const to = rotated ? gmapPoint(rCtx, lx, ly) : { x: gmx(rCtx, lx), y: gmy(rCtx, ly) };
|
|
19518
19591
|
const fixTo = fixPoint(rCtx, lx, ly);
|
|
19519
19592
|
if (inPath) {
|
|
19593
|
+
continueFigure(rCtx);
|
|
19520
19594
|
gdiPathRecorder(rCtx).lineTo(to.x, to.y);
|
|
19521
19595
|
rasterPathOf(rCtx).lineTo(fixTo[0], fixTo[1]);
|
|
19522
19596
|
} else {
|
|
@@ -19551,7 +19625,8 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19551
19625
|
const t = view.getInt32(dataOff + 4, true);
|
|
19552
19626
|
const r = view.getInt32(dataOff + 8, true);
|
|
19553
19627
|
const b = view.getInt32(dataOff + 12, true);
|
|
19554
|
-
const box =
|
|
19628
|
+
const box = uprightFixBox(rCtx, l, t, r, b);
|
|
19629
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19555
19630
|
if (!inPath) {
|
|
19556
19631
|
resetLineStyle(rCtx);
|
|
19557
19632
|
}
|
|
@@ -19570,14 +19645,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19570
19645
|
};
|
|
19571
19646
|
if (inPath) {
|
|
19572
19647
|
appendRect(gdiPathRecorder(rCtx));
|
|
19573
|
-
rasterPathOf(rCtx).append(rectRasterPath(box));
|
|
19648
|
+
rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
|
|
19574
19649
|
} else {
|
|
19575
19650
|
paintGdiShape(rCtx, {
|
|
19576
19651
|
build: (c) => {
|
|
19577
19652
|
c.beginPath();
|
|
19578
19653
|
appendRect(c);
|
|
19579
19654
|
},
|
|
19580
|
-
raster: () => rectRasterPath(box),
|
|
19655
|
+
raster: () => rectRasterPath(box, clockwise),
|
|
19581
19656
|
rectangle: true,
|
|
19582
19657
|
fill: true,
|
|
19583
19658
|
stroke: true,
|
|
@@ -19592,14 +19667,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19592
19667
|
const h = gmh(rCtx, b - t);
|
|
19593
19668
|
if (inPath) {
|
|
19594
19669
|
gdiPathRecorder(rCtx).rect(x, y, w, h);
|
|
19595
|
-
rasterPathOf(rCtx).append(rectRasterPath(box));
|
|
19670
|
+
rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
|
|
19596
19671
|
} else if (needsPathBasedRectangle(rCtx)) {
|
|
19597
19672
|
paintGdiShape(rCtx, {
|
|
19598
19673
|
build: (c) => {
|
|
19599
19674
|
c.beginPath();
|
|
19600
19675
|
c.rect(x, y, w, h);
|
|
19601
19676
|
},
|
|
19602
|
-
raster: () => rectRasterPath(box),
|
|
19677
|
+
raster: () => rectRasterPath(box, clockwise),
|
|
19603
19678
|
rectangle: true,
|
|
19604
19679
|
fill: true,
|
|
19605
19680
|
stroke: true,
|
|
@@ -19712,7 +19787,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19712
19787
|
gmh(rCtx, cornerH) / 2,
|
|
19713
19788
|
null
|
|
19714
19789
|
);
|
|
19715
|
-
const
|
|
19790
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19791
|
+
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
19792
|
if (inPath) {
|
|
19717
19793
|
drawRoundRect(gdiPathRecorder(rCtx));
|
|
19718
19794
|
rasterPathOf(rCtx).append(raster());
|
|
@@ -19723,7 +19799,7 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19723
19799
|
c.beginPath();
|
|
19724
19800
|
drawRoundRect(c);
|
|
19725
19801
|
},
|
|
19726
|
-
raster: () => raster(curvedFixBox(rCtx, l, t, r, b)),
|
|
19802
|
+
raster: () => raster(curvedFixBox(rCtx, l, t, r, b, true)),
|
|
19727
19803
|
roundPen: true,
|
|
19728
19804
|
fill: true,
|
|
19729
19805
|
stroke: true
|
|
@@ -19732,8 +19808,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19732
19808
|
}
|
|
19733
19809
|
return true;
|
|
19734
19810
|
}
|
|
19735
|
-
function curvedFixBox(rCtx, l, t, r, b) {
|
|
19736
|
-
const box = fixBox(rCtx, l, t, r, b);
|
|
19811
|
+
function curvedFixBox(rCtx, l, t, r, b, upright = false) {
|
|
19812
|
+
const box = upright ? uprightFixBox(rCtx, l, t, r, b) : fixBox(rCtx, l, t, r, b);
|
|
19737
19813
|
if (rCtx.state.penStyle !== 5) {
|
|
19738
19814
|
return box;
|
|
19739
19815
|
}
|
|
@@ -19759,6 +19835,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19759
19835
|
const t = view.getInt32(dataOff + 4, true);
|
|
19760
19836
|
const r = view.getInt32(dataOff + 8, true);
|
|
19761
19837
|
const b = view.getInt32(dataOff + 12, true);
|
|
19838
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19762
19839
|
const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
|
|
19763
19840
|
cx: gmx(rCtx, (l + r) / 2),
|
|
19764
19841
|
cy: gmy(rCtx, (t + b) / 2),
|
|
@@ -19768,7 +19845,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19768
19845
|
};
|
|
19769
19846
|
if (inPath) {
|
|
19770
19847
|
gdiPathRecorder(rCtx).ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
19771
|
-
rasterPathOf(rCtx).append(ellipseRasterPath(
|
|
19848
|
+
rasterPathOf(rCtx).append(ellipseRasterPath(uprightFixBox(rCtx, l, t, r, b), clockwise));
|
|
19772
19849
|
} else {
|
|
19773
19850
|
resetLineStyle(rCtx);
|
|
19774
19851
|
paintGdiShape(rCtx, {
|
|
@@ -19776,7 +19853,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19776
19853
|
c.beginPath();
|
|
19777
19854
|
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
19778
19855
|
},
|
|
19779
|
-
raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b)),
|
|
19856
|
+
raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b, true), clockwise),
|
|
19780
19857
|
roundPen: true,
|
|
19781
19858
|
fill: true,
|
|
19782
19859
|
stroke: true
|
|
@@ -19834,6 +19911,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
|
19834
19911
|
from: currentFix(rCtx)
|
|
19835
19912
|
});
|
|
19836
19913
|
if (inPath) {
|
|
19914
|
+
if (isArcTo) {
|
|
19915
|
+
continueFigure(rCtx);
|
|
19916
|
+
}
|
|
19837
19917
|
build(gdiPathRecorder(rCtx));
|
|
19838
19918
|
const a = rasterArgs();
|
|
19839
19919
|
arcRasterPath(a.box, a.s, a.e, clockwise, kind, a.from, rasterPathOf(rCtx));
|
|
@@ -19910,9 +19990,7 @@ function handleAngleArc(rCtx, dataOff, recSize) {
|
|
|
19910
19990
|
}
|
|
19911
19991
|
const e = [fixPts[fixPts.length - 2], fixPts[fixPts.length - 1]];
|
|
19912
19992
|
const buildRaster = (path, from) => {
|
|
19913
|
-
|
|
19914
|
-
path.moveTo(from[0], from[1]);
|
|
19915
|
-
}
|
|
19993
|
+
path.continueAt(from[0], from[1]);
|
|
19916
19994
|
path.addBeziers(fixPts, false);
|
|
19917
19995
|
};
|
|
19918
19996
|
const params = gdiEllipseParams(rCtx, cx, cy, radius, radius);
|
|
@@ -19926,9 +20004,9 @@ function handleAngleArc(rCtx, dataOff, recSize) {
|
|
|
19926
20004
|
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, -a0, clockwise ? -a0 + span : -a0 - span, !clockwise);
|
|
19927
20005
|
};
|
|
19928
20006
|
if (inPath) {
|
|
19929
|
-
|
|
19930
|
-
buildCanvas(
|
|
19931
|
-
buildRaster(rasterPathOf(rCtx),
|
|
20007
|
+
continueFigure(rCtx);
|
|
20008
|
+
buildCanvas(gdiPathRecorder(rCtx));
|
|
20009
|
+
buildRaster(rasterPathOf(rCtx), currentFix(rCtx));
|
|
19932
20010
|
} else {
|
|
19933
20011
|
resetLineStyle(rCtx);
|
|
19934
20012
|
const from = currentFix(rCtx);
|
|
@@ -22775,9 +22853,12 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
|
|
|
22775
22853
|
}
|
|
22776
22854
|
};
|
|
22777
22855
|
if (inPath) {
|
|
22856
|
+
if (isTo) {
|
|
22857
|
+
continueFigure(rCtx);
|
|
22858
|
+
}
|
|
22778
22859
|
build(gdiPathRecorder(rCtx));
|
|
22779
22860
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
22780
|
-
buildRaster(rCtx.rasterPath,
|
|
22861
|
+
buildRaster(rCtx.rasterPath, null);
|
|
22781
22862
|
} else {
|
|
22782
22863
|
const from = isTo ? currentFix(rCtx) : null;
|
|
22783
22864
|
const start = isTo ? gmapPoint(rCtx, state.curX, state.curY) : null;
|
|
@@ -22800,10 +22881,11 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
|
|
|
22800
22881
|
fillRule: state.polyFillMode === 2 ? "nonzero" : "evenodd"
|
|
22801
22882
|
});
|
|
22802
22883
|
}
|
|
22803
|
-
if (count > 0) {
|
|
22884
|
+
if (isTo && count > 0) {
|
|
22804
22885
|
const [x, y] = readPt(count - 1);
|
|
22805
22886
|
state.curX = x;
|
|
22806
22887
|
state.curY = y;
|
|
22888
|
+
rCtx.curFix = void 0;
|
|
22807
22889
|
}
|
|
22808
22890
|
}
|
|
22809
22891
|
function handlePoly32(rCtx, recType, offset, dataOff, recSize) {
|
|
@@ -22908,9 +22990,11 @@ function handlePolyDraw(rCtx, offset, dataOff, recSize, pointSize) {
|
|
|
22908
22990
|
};
|
|
22909
22991
|
const startsWithMove = (typeAt(0) & ~PT_CLOSEFIGURE) === PT_MOVETO;
|
|
22910
22992
|
if (inPath) {
|
|
22993
|
+
if (!startsWithMove) {
|
|
22994
|
+
continueFigure(rCtx);
|
|
22995
|
+
}
|
|
22911
22996
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
22912
|
-
|
|
22913
|
-
walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, !startsWithMove && fresh ? currentFix(rCtx) : null, null);
|
|
22997
|
+
walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, null, null);
|
|
22914
22998
|
} else {
|
|
22915
22999
|
const from = startsWithMove ? null : currentFix(rCtx);
|
|
22916
23000
|
const fromPx = startsWithMove ? null : gmapPoint(rCtx, state.curX, state.curY);
|
|
@@ -27366,7 +27450,8 @@ function wmfSetDibToDev(p, d, end) {
|
|
|
27366
27450
|
const hv = new DataView(bmi.buffer);
|
|
27367
27451
|
const fullH = hv.getInt32(8, true);
|
|
27368
27452
|
hv.setInt32(8, fullH < 0 ? -scanCount : scanCount, true);
|
|
27369
|
-
const [
|
|
27453
|
+
const [mx, oy] = deviceDest(p, dx, dy, 0, 0);
|
|
27454
|
+
const ox = p.layout & 1 ? mx - w : mx;
|
|
27370
27455
|
playStretchDibits(p, 13369376, ox, oy + (sy + h - hi), w, hi - lo, sx, lo - startScan, w, hi - lo, { bmi, bits: dib.bits }, true);
|
|
27371
27456
|
}
|
|
27372
27457
|
|
|
@@ -27713,25 +27798,30 @@ function wmfRectangle(p, l, t, r, b) {
|
|
|
27713
27798
|
c.beginPath();
|
|
27714
27799
|
c.rect(cr.x, cr.y, cr.w, cr.h);
|
|
27715
27800
|
},
|
|
27716
|
-
raster: () => rectRasterPath(fixBoxOf(box)),
|
|
27801
|
+
raster: () => rectRasterPath(fixBoxOf(box), clockwiseOf(p)),
|
|
27717
27802
|
rectangle: true,
|
|
27718
27803
|
fill: true,
|
|
27719
27804
|
stroke: true,
|
|
27720
27805
|
axisRect: { interior: rectInterior(p, box) }
|
|
27721
27806
|
});
|
|
27722
27807
|
}
|
|
27808
|
+
function compatCorner(p, w, h) {
|
|
27809
|
+
const m = gdiDeviceMatrix(p.rCtx);
|
|
27810
|
+
const cw = Math.round(Math.abs(w * m[0]) * 16);
|
|
27811
|
+
const ch = Math.round(Math.abs(h * m[3]) * 16);
|
|
27812
|
+
const ux = 16 * p.kx;
|
|
27813
|
+
const uy = 16 * p.ky;
|
|
27814
|
+
if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
|
|
27815
|
+
return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
|
|
27816
|
+
}
|
|
27817
|
+
return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
|
|
27818
|
+
}
|
|
27723
27819
|
function wmfRoundRect(p, l, t, r, b, w, h) {
|
|
27724
27820
|
const box = compatBox(p, l, t, r, b, { curved: true });
|
|
27725
27821
|
if (box.x1 < box.x0 || box.y1 < box.y0) {
|
|
27726
27822
|
return;
|
|
27727
27823
|
}
|
|
27728
|
-
const
|
|
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
|
-
}
|
|
27824
|
+
const [cw, ch] = compatCorner(p, w, h);
|
|
27735
27825
|
const cr = canvasRect(box);
|
|
27736
27826
|
paintGdiShape(p.rCtx, {
|
|
27737
27827
|
build: (c) => {
|
|
@@ -27756,7 +27846,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
|
|
|
27756
27846
|
c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
|
|
27757
27847
|
c.closePath();
|
|
27758
27848
|
},
|
|
27759
|
-
raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch),
|
|
27849
|
+
raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
|
|
27760
27850
|
roundPen: true,
|
|
27761
27851
|
fill: true,
|
|
27762
27852
|
stroke: true
|
|
@@ -27773,24 +27863,31 @@ function wmfEllipse(p, l, t, r, b) {
|
|
|
27773
27863
|
c.beginPath();
|
|
27774
27864
|
c.ellipse(cr.x + cr.w / 2, cr.y + cr.h / 2, cr.w / 2, cr.h / 2, 0, 0, Math.PI * 2);
|
|
27775
27865
|
},
|
|
27776
|
-
raster: () => ellipseRasterPath(fixBoxOf(box)),
|
|
27866
|
+
raster: () => ellipseRasterPath(fixBoxOf(box), clockwiseOf(p)),
|
|
27777
27867
|
roundPen: true,
|
|
27778
27868
|
fill: true,
|
|
27779
27869
|
stroke: true
|
|
27780
27870
|
});
|
|
27781
27871
|
}
|
|
27782
|
-
function
|
|
27783
|
-
const box = compatBox(p, l, t, r, b, { curved: kind
|
|
27872
|
+
function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
27873
|
+
const box = compatBox(p, l, t, r, b, { curved: kind === "chord" || kind === "pie" });
|
|
27784
27874
|
if (box.x1 < box.x0 || box.y1 < box.y0) {
|
|
27785
|
-
return;
|
|
27875
|
+
return null;
|
|
27786
27876
|
}
|
|
27787
27877
|
const raw = compatBox(p, l, t, r, b, { exclusive: false });
|
|
27788
|
-
const
|
|
27789
|
-
const
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27793
|
-
|
|
27878
|
+
const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
|
|
27879
|
+
const radial = (x, y) => {
|
|
27880
|
+
const [fx, fy] = fixPoint(p.rCtx, x, y);
|
|
27881
|
+
return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
|
|
27882
|
+
};
|
|
27883
|
+
return { box, s: radial(xs, ys), e: radial(xe, ye) };
|
|
27884
|
+
}
|
|
27885
|
+
function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
27886
|
+
const geom = compatArc(p, kind, l, t, r, b, xs, ys, xe, ye);
|
|
27887
|
+
if (!geom) {
|
|
27888
|
+
return;
|
|
27889
|
+
}
|
|
27890
|
+
const { box, s, e } = geom;
|
|
27794
27891
|
const clockwise = p.rCtx.state.arcDirection === 2;
|
|
27795
27892
|
const cr = canvasRect(box);
|
|
27796
27893
|
const cx = cr.x + cr.w / 2;
|
|
@@ -27816,6 +27913,9 @@ function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
|
27816
27913
|
stroke: true
|
|
27817
27914
|
});
|
|
27818
27915
|
}
|
|
27916
|
+
function clockwiseOf(p) {
|
|
27917
|
+
return p.rCtx.state.arcDirection === 2;
|
|
27918
|
+
}
|
|
27819
27919
|
function penIsNull(p) {
|
|
27820
27920
|
return (p.rCtx.state.penStyle & 15) === PS_NULL3;
|
|
27821
27921
|
}
|
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,
|
|
1775
|
+
P(full, rightUpper);
|
|
1757
1776
|
P(hCtl, zero);
|
|
1758
1777
|
P(hMidLo, zero);
|
|
1759
1778
|
P(hCtl2, zero);
|
|
1760
|
-
P(zero,
|
|
1779
|
+
P(zero, leftUpper);
|
|
1761
1780
|
P(zero, vMidLo);
|
|
1762
|
-
P(zero,
|
|
1781
|
+
P(zero, leftLower);
|
|
1763
1782
|
P(hCtl2, full);
|
|
1764
1783
|
P(hMidHi, full);
|
|
1765
1784
|
P(hCtl, full);
|
|
1766
|
-
P(full,
|
|
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
|
-
|
|
1784
|
-
|
|
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 =
|
|
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() {
|
|
@@ -16651,8 +16706,11 @@ function boxCorners(box) {
|
|
|
16651
16706
|
const { ax, ay, exx, exy, eyx, eyy } = box;
|
|
16652
16707
|
return [ax + exx, ay + exy, ax, ay, ax + eyx, ay + eyy, ax + exx + eyx, ay + exy + eyy];
|
|
16653
16708
|
}
|
|
16654
|
-
function
|
|
16655
|
-
|
|
16709
|
+
function flipBox(box) {
|
|
16710
|
+
return { ax: box.ax + box.eyx, ay: box.ay + box.eyy, exx: box.exx, exy: box.exy, eyx: -box.eyx, eyy: -box.eyy };
|
|
16711
|
+
}
|
|
16712
|
+
function rectRasterPath(box, clockwise = false) {
|
|
16713
|
+
const c = boxCorners(clockwise ? flipBox(box) : box);
|
|
16656
16714
|
const path = new GdiRasterPath();
|
|
16657
16715
|
path.moveTo(c[0], c[1]);
|
|
16658
16716
|
path.lineTo(c[2], c[3]);
|
|
@@ -16661,9 +16719,9 @@ function rectRasterPath(box) {
|
|
|
16661
16719
|
path.closeFigure();
|
|
16662
16720
|
return path;
|
|
16663
16721
|
}
|
|
16664
|
-
function ellipseRasterPath(box) {
|
|
16722
|
+
function ellipseRasterPath(box, clockwise = false) {
|
|
16665
16723
|
const path = new GdiRasterPath();
|
|
16666
|
-
path.addBeziers(ellipseBeziersBox(box), true);
|
|
16724
|
+
path.addBeziers(ellipseBeziersBox(box, clockwise), true);
|
|
16667
16725
|
path.closeFigure();
|
|
16668
16726
|
return path;
|
|
16669
16727
|
}
|
|
@@ -16689,13 +16747,16 @@ function boxFrame(box) {
|
|
|
16689
16747
|
const h = Math.round(Math.hypot(box.eyx, box.eyy));
|
|
16690
16748
|
return { l: 0, t: 0, r: w, b: h };
|
|
16691
16749
|
}
|
|
16692
|
-
function roundRectRasterPath(box, cw, ch) {
|
|
16750
|
+
function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
|
|
16751
|
+
if (rectangle) {
|
|
16752
|
+
return rectRasterPath(box, clockwise);
|
|
16753
|
+
}
|
|
16693
16754
|
const f = boxFrame(box);
|
|
16694
16755
|
const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
|
|
16695
16756
|
const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
|
|
16696
16757
|
const pts = [];
|
|
16697
16758
|
for (let i = 0; i < q.length; i += 2) {
|
|
16698
|
-
pts.push(...map(q[i], q[i + 1]));
|
|
16759
|
+
pts.push(...map(q[i], clockwise ? f.t + f.b - q[i + 1] : q[i + 1]));
|
|
16699
16760
|
}
|
|
16700
16761
|
const path = new GdiRasterPath();
|
|
16701
16762
|
for (let c = 0; c < 4; c++) {
|
|
@@ -16738,7 +16799,7 @@ function arcRasterPath(box, s, e, clockwise, kind, from, path = new GdiRasterPat
|
|
|
16738
16799
|
pts.push(...map(bz[i], bz[i + 1]));
|
|
16739
16800
|
}
|
|
16740
16801
|
if (kind === "arcto" && from) {
|
|
16741
|
-
path.
|
|
16802
|
+
path.continueAt(from[0], from[1]);
|
|
16742
16803
|
path.addBeziers(pts, false);
|
|
16743
16804
|
} else {
|
|
16744
16805
|
path.addBeziers(pts, true);
|
|
@@ -19458,6 +19519,15 @@ function rasterPathOf(rCtx) {
|
|
|
19458
19519
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
19459
19520
|
return rCtx.rasterPath;
|
|
19460
19521
|
}
|
|
19522
|
+
function continueFigure(rCtx) {
|
|
19523
|
+
const path = rasterPathOf(rCtx);
|
|
19524
|
+
const [x, y] = currentFix(rCtx);
|
|
19525
|
+
const before = path.figures.length;
|
|
19526
|
+
path.continueAt(x, y);
|
|
19527
|
+
if (path.figures.length !== before) {
|
|
19528
|
+
gdiPathRecorder(rCtx).moveTo(x / 16, y / 16);
|
|
19529
|
+
}
|
|
19530
|
+
}
|
|
19461
19531
|
function lineStyleOf(rCtx) {
|
|
19462
19532
|
rCtx.lineStyle ?? (rCtx.lineStyle = { pos: 0 });
|
|
19463
19533
|
return rCtx.lineStyle;
|
|
@@ -19473,6 +19543,9 @@ function currentFix(rCtx) {
|
|
|
19473
19543
|
}
|
|
19474
19544
|
return fixPoint(rCtx, state.curX, state.curY);
|
|
19475
19545
|
}
|
|
19546
|
+
function uprightFixBox(rCtx, l, t, r, b) {
|
|
19547
|
+
return fixBox(rCtx, Math.min(l, r), Math.min(t, b), Math.max(l, r), Math.max(t, b));
|
|
19548
|
+
}
|
|
19476
19549
|
function fixExtent(rCtx, v, axis) {
|
|
19477
19550
|
const m = gdiDeviceMatrix(rCtx);
|
|
19478
19551
|
const k = axis === 0 ? Math.hypot(m[0], m[1]) : Math.hypot(m[2], m[3]);
|
|
@@ -19515,6 +19588,7 @@ function handleLineTo(rCtx, dataOff, recSize) {
|
|
|
19515
19588
|
const to = rotated ? gmapPoint(rCtx, lx, ly) : { x: gmx(rCtx, lx), y: gmy(rCtx, ly) };
|
|
19516
19589
|
const fixTo = fixPoint(rCtx, lx, ly);
|
|
19517
19590
|
if (inPath) {
|
|
19591
|
+
continueFigure(rCtx);
|
|
19518
19592
|
gdiPathRecorder(rCtx).lineTo(to.x, to.y);
|
|
19519
19593
|
rasterPathOf(rCtx).lineTo(fixTo[0], fixTo[1]);
|
|
19520
19594
|
} else {
|
|
@@ -19549,7 +19623,8 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19549
19623
|
const t = view.getInt32(dataOff + 4, true);
|
|
19550
19624
|
const r = view.getInt32(dataOff + 8, true);
|
|
19551
19625
|
const b = view.getInt32(dataOff + 12, true);
|
|
19552
|
-
const box =
|
|
19626
|
+
const box = uprightFixBox(rCtx, l, t, r, b);
|
|
19627
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19553
19628
|
if (!inPath) {
|
|
19554
19629
|
resetLineStyle(rCtx);
|
|
19555
19630
|
}
|
|
@@ -19568,14 +19643,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19568
19643
|
};
|
|
19569
19644
|
if (inPath) {
|
|
19570
19645
|
appendRect(gdiPathRecorder(rCtx));
|
|
19571
|
-
rasterPathOf(rCtx).append(rectRasterPath(box));
|
|
19646
|
+
rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
|
|
19572
19647
|
} else {
|
|
19573
19648
|
paintGdiShape(rCtx, {
|
|
19574
19649
|
build: (c) => {
|
|
19575
19650
|
c.beginPath();
|
|
19576
19651
|
appendRect(c);
|
|
19577
19652
|
},
|
|
19578
|
-
raster: () => rectRasterPath(box),
|
|
19653
|
+
raster: () => rectRasterPath(box, clockwise),
|
|
19579
19654
|
rectangle: true,
|
|
19580
19655
|
fill: true,
|
|
19581
19656
|
stroke: true,
|
|
@@ -19590,14 +19665,14 @@ function handleRectangle(rCtx, dataOff, recSize) {
|
|
|
19590
19665
|
const h = gmh(rCtx, b - t);
|
|
19591
19666
|
if (inPath) {
|
|
19592
19667
|
gdiPathRecorder(rCtx).rect(x, y, w, h);
|
|
19593
|
-
rasterPathOf(rCtx).append(rectRasterPath(box));
|
|
19668
|
+
rasterPathOf(rCtx).append(rectRasterPath(box, clockwise));
|
|
19594
19669
|
} else if (needsPathBasedRectangle(rCtx)) {
|
|
19595
19670
|
paintGdiShape(rCtx, {
|
|
19596
19671
|
build: (c) => {
|
|
19597
19672
|
c.beginPath();
|
|
19598
19673
|
c.rect(x, y, w, h);
|
|
19599
19674
|
},
|
|
19600
|
-
raster: () => rectRasterPath(box),
|
|
19675
|
+
raster: () => rectRasterPath(box, clockwise),
|
|
19601
19676
|
rectangle: true,
|
|
19602
19677
|
fill: true,
|
|
19603
19678
|
stroke: true,
|
|
@@ -19710,7 +19785,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19710
19785
|
gmh(rCtx, cornerH) / 2,
|
|
19711
19786
|
null
|
|
19712
19787
|
);
|
|
19713
|
-
const
|
|
19788
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19789
|
+
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
19790
|
if (inPath) {
|
|
19715
19791
|
drawRoundRect(gdiPathRecorder(rCtx));
|
|
19716
19792
|
rasterPathOf(rCtx).append(raster());
|
|
@@ -19721,7 +19797,7 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19721
19797
|
c.beginPath();
|
|
19722
19798
|
drawRoundRect(c);
|
|
19723
19799
|
},
|
|
19724
|
-
raster: () => raster(curvedFixBox(rCtx, l, t, r, b)),
|
|
19800
|
+
raster: () => raster(curvedFixBox(rCtx, l, t, r, b, true)),
|
|
19725
19801
|
roundPen: true,
|
|
19726
19802
|
fill: true,
|
|
19727
19803
|
stroke: true
|
|
@@ -19730,8 +19806,8 @@ function handleRoundRect(rCtx, dataOff, recSize) {
|
|
|
19730
19806
|
}
|
|
19731
19807
|
return true;
|
|
19732
19808
|
}
|
|
19733
|
-
function curvedFixBox(rCtx, l, t, r, b) {
|
|
19734
|
-
const box = fixBox(rCtx, l, t, r, b);
|
|
19809
|
+
function curvedFixBox(rCtx, l, t, r, b, upright = false) {
|
|
19810
|
+
const box = upright ? uprightFixBox(rCtx, l, t, r, b) : fixBox(rCtx, l, t, r, b);
|
|
19735
19811
|
if (rCtx.state.penStyle !== 5) {
|
|
19736
19812
|
return box;
|
|
19737
19813
|
}
|
|
@@ -19757,6 +19833,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19757
19833
|
const t = view.getInt32(dataOff + 4, true);
|
|
19758
19834
|
const r = view.getInt32(dataOff + 8, true);
|
|
19759
19835
|
const b = view.getInt32(dataOff + 12, true);
|
|
19836
|
+
const clockwise = rCtx.state.arcDirection === 2;
|
|
19760
19837
|
const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
|
|
19761
19838
|
cx: gmx(rCtx, (l + r) / 2),
|
|
19762
19839
|
cy: gmy(rCtx, (t + b) / 2),
|
|
@@ -19766,7 +19843,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19766
19843
|
};
|
|
19767
19844
|
if (inPath) {
|
|
19768
19845
|
gdiPathRecorder(rCtx).ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
19769
|
-
rasterPathOf(rCtx).append(ellipseRasterPath(
|
|
19846
|
+
rasterPathOf(rCtx).append(ellipseRasterPath(uprightFixBox(rCtx, l, t, r, b), clockwise));
|
|
19770
19847
|
} else {
|
|
19771
19848
|
resetLineStyle(rCtx);
|
|
19772
19849
|
paintGdiShape(rCtx, {
|
|
@@ -19774,7 +19851,7 @@ function handleEllipse(rCtx, dataOff, recSize) {
|
|
|
19774
19851
|
c.beginPath();
|
|
19775
19852
|
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
19776
19853
|
},
|
|
19777
|
-
raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b)),
|
|
19854
|
+
raster: () => ellipseRasterPath(curvedFixBox(rCtx, l, t, r, b, true), clockwise),
|
|
19778
19855
|
roundPen: true,
|
|
19779
19856
|
fill: true,
|
|
19780
19857
|
stroke: true
|
|
@@ -19832,6 +19909,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
|
19832
19909
|
from: currentFix(rCtx)
|
|
19833
19910
|
});
|
|
19834
19911
|
if (inPath) {
|
|
19912
|
+
if (isArcTo) {
|
|
19913
|
+
continueFigure(rCtx);
|
|
19914
|
+
}
|
|
19835
19915
|
build(gdiPathRecorder(rCtx));
|
|
19836
19916
|
const a = rasterArgs();
|
|
19837
19917
|
arcRasterPath(a.box, a.s, a.e, clockwise, kind, a.from, rasterPathOf(rCtx));
|
|
@@ -19908,9 +19988,7 @@ function handleAngleArc(rCtx, dataOff, recSize) {
|
|
|
19908
19988
|
}
|
|
19909
19989
|
const e = [fixPts[fixPts.length - 2], fixPts[fixPts.length - 1]];
|
|
19910
19990
|
const buildRaster = (path, from) => {
|
|
19911
|
-
|
|
19912
|
-
path.moveTo(from[0], from[1]);
|
|
19913
|
-
}
|
|
19991
|
+
path.continueAt(from[0], from[1]);
|
|
19914
19992
|
path.addBeziers(fixPts, false);
|
|
19915
19993
|
};
|
|
19916
19994
|
const params = gdiEllipseParams(rCtx, cx, cy, radius, radius);
|
|
@@ -19924,9 +20002,9 @@ function handleAngleArc(rCtx, dataOff, recSize) {
|
|
|
19924
20002
|
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, -a0, clockwise ? -a0 + span : -a0 - span, !clockwise);
|
|
19925
20003
|
};
|
|
19926
20004
|
if (inPath) {
|
|
19927
|
-
|
|
19928
|
-
buildCanvas(
|
|
19929
|
-
buildRaster(rasterPathOf(rCtx),
|
|
20005
|
+
continueFigure(rCtx);
|
|
20006
|
+
buildCanvas(gdiPathRecorder(rCtx));
|
|
20007
|
+
buildRaster(rasterPathOf(rCtx), currentFix(rCtx));
|
|
19930
20008
|
} else {
|
|
19931
20009
|
resetLineStyle(rCtx);
|
|
19932
20010
|
const from = currentFix(rCtx);
|
|
@@ -22773,9 +22851,12 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
|
|
|
22773
22851
|
}
|
|
22774
22852
|
};
|
|
22775
22853
|
if (inPath) {
|
|
22854
|
+
if (isTo) {
|
|
22855
|
+
continueFigure(rCtx);
|
|
22856
|
+
}
|
|
22776
22857
|
build(gdiPathRecorder(rCtx));
|
|
22777
22858
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
22778
|
-
buildRaster(rCtx.rasterPath,
|
|
22859
|
+
buildRaster(rCtx.rasterPath, null);
|
|
22779
22860
|
} else {
|
|
22780
22861
|
const from = isTo ? currentFix(rCtx) : null;
|
|
22781
22862
|
const start = isTo ? gmapPoint(rCtx, state.curX, state.curY) : null;
|
|
@@ -22798,10 +22879,11 @@ function handlePoly(rCtx, recType, count, readPt, kinds) {
|
|
|
22798
22879
|
fillRule: state.polyFillMode === 2 ? "nonzero" : "evenodd"
|
|
22799
22880
|
});
|
|
22800
22881
|
}
|
|
22801
|
-
if (count > 0) {
|
|
22882
|
+
if (isTo && count > 0) {
|
|
22802
22883
|
const [x, y] = readPt(count - 1);
|
|
22803
22884
|
state.curX = x;
|
|
22804
22885
|
state.curY = y;
|
|
22886
|
+
rCtx.curFix = void 0;
|
|
22805
22887
|
}
|
|
22806
22888
|
}
|
|
22807
22889
|
function handlePoly32(rCtx, recType, offset, dataOff, recSize) {
|
|
@@ -22906,9 +22988,11 @@ function handlePolyDraw(rCtx, offset, dataOff, recSize, pointSize) {
|
|
|
22906
22988
|
};
|
|
22907
22989
|
const startsWithMove = (typeAt(0) & ~PT_CLOSEFIGURE) === PT_MOVETO;
|
|
22908
22990
|
if (inPath) {
|
|
22991
|
+
if (!startsWithMove) {
|
|
22992
|
+
continueFigure(rCtx);
|
|
22993
|
+
}
|
|
22909
22994
|
rCtx.rasterPath ?? (rCtx.rasterPath = new GdiRasterPath());
|
|
22910
|
-
|
|
22911
|
-
walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, !startsWithMove && fresh ? currentFix(rCtx) : null, null);
|
|
22995
|
+
walk2(gdiPathRecorder(rCtx), rCtx.rasterPath, null, null);
|
|
22912
22996
|
} else {
|
|
22913
22997
|
const from = startsWithMove ? null : currentFix(rCtx);
|
|
22914
22998
|
const fromPx = startsWithMove ? null : gmapPoint(rCtx, state.curX, state.curY);
|
|
@@ -27364,7 +27448,8 @@ function wmfSetDibToDev(p, d, end) {
|
|
|
27364
27448
|
const hv = new DataView(bmi.buffer);
|
|
27365
27449
|
const fullH = hv.getInt32(8, true);
|
|
27366
27450
|
hv.setInt32(8, fullH < 0 ? -scanCount : scanCount, true);
|
|
27367
|
-
const [
|
|
27451
|
+
const [mx, oy] = deviceDest(p, dx, dy, 0, 0);
|
|
27452
|
+
const ox = p.layout & 1 ? mx - w : mx;
|
|
27368
27453
|
playStretchDibits(p, 13369376, ox, oy + (sy + h - hi), w, hi - lo, sx, lo - startScan, w, hi - lo, { bmi, bits: dib.bits }, true);
|
|
27369
27454
|
}
|
|
27370
27455
|
|
|
@@ -27711,25 +27796,30 @@ function wmfRectangle(p, l, t, r, b) {
|
|
|
27711
27796
|
c.beginPath();
|
|
27712
27797
|
c.rect(cr.x, cr.y, cr.w, cr.h);
|
|
27713
27798
|
},
|
|
27714
|
-
raster: () => rectRasterPath(fixBoxOf(box)),
|
|
27799
|
+
raster: () => rectRasterPath(fixBoxOf(box), clockwiseOf(p)),
|
|
27715
27800
|
rectangle: true,
|
|
27716
27801
|
fill: true,
|
|
27717
27802
|
stroke: true,
|
|
27718
27803
|
axisRect: { interior: rectInterior(p, box) }
|
|
27719
27804
|
});
|
|
27720
27805
|
}
|
|
27806
|
+
function compatCorner(p, w, h) {
|
|
27807
|
+
const m = gdiDeviceMatrix(p.rCtx);
|
|
27808
|
+
const cw = Math.round(Math.abs(w * m[0]) * 16);
|
|
27809
|
+
const ch = Math.round(Math.abs(h * m[3]) * 16);
|
|
27810
|
+
const ux = 16 * p.kx;
|
|
27811
|
+
const uy = 16 * p.ky;
|
|
27812
|
+
if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
|
|
27813
|
+
return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
|
|
27814
|
+
}
|
|
27815
|
+
return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
|
|
27816
|
+
}
|
|
27721
27817
|
function wmfRoundRect(p, l, t, r, b, w, h) {
|
|
27722
27818
|
const box = compatBox(p, l, t, r, b, { curved: true });
|
|
27723
27819
|
if (box.x1 < box.x0 || box.y1 < box.y0) {
|
|
27724
27820
|
return;
|
|
27725
27821
|
}
|
|
27726
|
-
const
|
|
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
|
-
}
|
|
27822
|
+
const [cw, ch] = compatCorner(p, w, h);
|
|
27733
27823
|
const cr = canvasRect(box);
|
|
27734
27824
|
paintGdiShape(p.rCtx, {
|
|
27735
27825
|
build: (c) => {
|
|
@@ -27754,7 +27844,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
|
|
|
27754
27844
|
c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
|
|
27755
27845
|
c.closePath();
|
|
27756
27846
|
},
|
|
27757
|
-
raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch),
|
|
27847
|
+
raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
|
|
27758
27848
|
roundPen: true,
|
|
27759
27849
|
fill: true,
|
|
27760
27850
|
stroke: true
|
|
@@ -27771,24 +27861,31 @@ function wmfEllipse(p, l, t, r, b) {
|
|
|
27771
27861
|
c.beginPath();
|
|
27772
27862
|
c.ellipse(cr.x + cr.w / 2, cr.y + cr.h / 2, cr.w / 2, cr.h / 2, 0, 0, Math.PI * 2);
|
|
27773
27863
|
},
|
|
27774
|
-
raster: () => ellipseRasterPath(fixBoxOf(box)),
|
|
27864
|
+
raster: () => ellipseRasterPath(fixBoxOf(box), clockwiseOf(p)),
|
|
27775
27865
|
roundPen: true,
|
|
27776
27866
|
fill: true,
|
|
27777
27867
|
stroke: true
|
|
27778
27868
|
});
|
|
27779
27869
|
}
|
|
27780
|
-
function
|
|
27781
|
-
const box = compatBox(p, l, t, r, b, { curved: kind
|
|
27870
|
+
function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
27871
|
+
const box = compatBox(p, l, t, r, b, { curved: kind === "chord" || kind === "pie" });
|
|
27782
27872
|
if (box.x1 < box.x0 || box.y1 < box.y0) {
|
|
27783
|
-
return;
|
|
27873
|
+
return null;
|
|
27784
27874
|
}
|
|
27785
27875
|
const raw = compatBox(p, l, t, r, b, { exclusive: false });
|
|
27786
|
-
const
|
|
27787
|
-
const
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27876
|
+
const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
|
|
27877
|
+
const radial = (x, y) => {
|
|
27878
|
+
const [fx, fy] = fixPoint(p.rCtx, x, y);
|
|
27879
|
+
return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
|
|
27880
|
+
};
|
|
27881
|
+
return { box, s: radial(xs, ys), e: radial(xe, ye) };
|
|
27882
|
+
}
|
|
27883
|
+
function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
27884
|
+
const geom = compatArc(p, kind, l, t, r, b, xs, ys, xe, ye);
|
|
27885
|
+
if (!geom) {
|
|
27886
|
+
return;
|
|
27887
|
+
}
|
|
27888
|
+
const { box, s, e } = geom;
|
|
27792
27889
|
const clockwise = p.rCtx.state.arcDirection === 2;
|
|
27793
27890
|
const cr = canvasRect(box);
|
|
27794
27891
|
const cx = cr.x + cr.w / 2;
|
|
@@ -27814,6 +27911,9 @@ function wmfArcFamily(p, kind, l, t, r, b, xs, ys, xe, ye) {
|
|
|
27814
27911
|
stroke: true
|
|
27815
27912
|
});
|
|
27816
27913
|
}
|
|
27914
|
+
function clockwiseOf(p) {
|
|
27915
|
+
return p.rCtx.state.arcDirection === 2;
|
|
27916
|
+
}
|
|
27817
27917
|
function penIsNull(p) {
|
|
27818
27918
|
return (p.rCtx.state.penStyle & 15) === PS_NULL3;
|
|
27819
27919
|
}
|