emf-converter 4.8.1 → 4.8.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/README.md +1 -1
- package/dist/browser.js +231 -80
- package/dist/browser.mjs +231 -80
- package/dist/index.js +231 -80
- package/dist/index.mjs +231 -80
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -217,7 +217,7 @@ Everything is measured against output painted by Windows itself; `src/gdi-parity
|
|
|
217
217
|
- **Colour adjustment and image effects**: all eleven EMF+ image effects are supported. Complete native tables cover every legal ColorCurve intensity. Deferred draws retain their effects. JPEG, GIF and TIFF have bundled decoders for environments without Canvas. HSL, tint, red-eye, blur, sharpen and GDI colour adjustment retain measured differences; see [the detailed limitations](docs/limitations.md) and the native fixture bounds.
|
|
218
218
|
- **Pen transforms**: EMF+ pen transforms (uniform, nonuniform and skewed) match GDI+ exactly on the 17 pen-transform fixtures, including dashes, which GDI+ lays out along the path in world space at the untransformed pen width. Native fixtures verify dashes under both uniform and non-uniform scales. GDI+ rejects singular pen transforms; the converter ignores such malformed transform data. Square, round, diamond and arrow anchor caps use their native shapes; the new centered-cap fixtures match every pixel with and without antialiasing.
|
|
219
219
|
- **Text**: ANSI, multi-string and small-text records now render; the Windows text fixtures retain a few glyph-edge differences and a `PolyTextOut` C1 control-glyph difference (under 0.1% of pixels; bounds in `src/emf-text-records.fixture.test.ts`). ANSI decoding uses the host's `TextDecoder` for common Windows code pages; Johab and OEM CP437 use bundled Windows mappings. Vertical `ETO_PDY` advances work with and without the `fonts` engine. Without `fonts`, SVG text measurements use deterministic estimates, so precise justification requires supplying the matching fonts.
|
|
220
|
-
- **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide
|
|
220
|
+
- **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide curves follow `WidenPath` (exact for round and flat caps at one logical unit per pixel; square caps differ slightly at path ends); at most 0.2% of pixels on the fixtures. `EMR_WIDENPATH` does not reproduce the extra inner join triangles GDI's own `WidenPath` emits (visible only when the widened outline is itself stroked). EMF+ 1-pixel antialiased lines can differ by one antialiasing sample at their ends, some closed widened outlines by one sample along an edge, and Inset or compound pens on closed figures are approximate.
|
|
221
221
|
- **GM_COMPATIBLE recordings**: EMF files do not record the graphics mode, and Windows plays RoundRect, Arc, Chord, Pie and null-pen Ellipse records back differently from how a GM_COMPATIBLE application drew them on screen; the converter follows Windows' playback.
|
|
222
222
|
- **Small EMF+ residuals**: rotated `HighQualityBicubic` `DrawImage` edge pixels (0.14%), one-level differences at exact half-level `Blend` knots, and a few pixels of a metafile nested in `DrawImage` under a scale.
|
|
223
223
|
- **WMF**: `PS_INSIDEFRAME` boxes can come out a pixel short at non-integer scales, right-to-left (`LAYOUT_RTL`) layouts differ by single pixels on mirrored diagonals, and metric map modes assume a 96 dpi reference device (Windows derives them from the physical display, so its own output varies per machine).
|
package/dist/browser.js
CHANGED
|
@@ -17039,13 +17039,17 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
|
|
|
17039
17039
|
const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0, true) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
|
|
17040
17040
|
const w = x1 - x0;
|
|
17041
17041
|
const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
|
|
17042
|
+
const halo = new Uint8ClampedArray((y1 - y0) * 4);
|
|
17042
17043
|
for (let y = 0; y < y1 - y0; y++) {
|
|
17043
17044
|
const from = ((y + r) * bw + r) * 4;
|
|
17044
17045
|
for (let i = 0; i < w * 4; i++) {
|
|
17045
17046
|
out[y * w * 4 + i] = Math.round(blurred[from + i]);
|
|
17046
17047
|
}
|
|
17048
|
+
for (let i = 0; i < 4; i++) {
|
|
17049
|
+
halo[y * 4 + i] = Math.round(blurred[from + w * 4 + i]);
|
|
17050
|
+
}
|
|
17047
17051
|
}
|
|
17048
|
-
return out;
|
|
17052
|
+
return { out, halo };
|
|
17049
17053
|
}
|
|
17050
17054
|
function applyImageEffectToRect(rgba, width, height, effect, src) {
|
|
17051
17055
|
if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
@@ -17078,7 +17082,12 @@ function applyImageEffectToRect(rgba, width, height, effect, src) {
|
|
|
17078
17082
|
};
|
|
17079
17083
|
if (src && effect.kind === "blur" && effect.expandEdge) {
|
|
17080
17084
|
const cropped = Math.ceil(src.x + src.w) < width && Math.ceil(src.y + src.h) < height;
|
|
17081
|
-
|
|
17085
|
+
const { out: out2, halo } = expandedBlur(rgba, width, height, effect.radius, x0, y0, x1, y1, cropped);
|
|
17086
|
+
const region2 = result(out2);
|
|
17087
|
+
if (shownWidth === w && shownHeight === h) {
|
|
17088
|
+
region2.haloRight = halo;
|
|
17089
|
+
}
|
|
17090
|
+
return region2;
|
|
17082
17091
|
}
|
|
17083
17092
|
let region = rgba;
|
|
17084
17093
|
if (w !== width || h !== height) {
|
|
@@ -20450,6 +20459,22 @@ function cubicIntegral(t, a) {
|
|
|
20450
20459
|
}
|
|
20451
20460
|
return Math.sign(t) * v;
|
|
20452
20461
|
}
|
|
20462
|
+
var FAR_FADE = [1, 1, 0.985, 0.95, 0.91, 0.85, 0.8, 0.745, 0.69, 0.63, 0.56, 0.5, 0.43, 0.35, 0.28, 0.22, 0.16, 0.11, 0.065, 0.03, 0];
|
|
20463
|
+
function fadeAt(d, bilinear) {
|
|
20464
|
+
if (d >= 1) {
|
|
20465
|
+
return 0;
|
|
20466
|
+
}
|
|
20467
|
+
if (bilinear) {
|
|
20468
|
+
return 1 - Math.max(0, d);
|
|
20469
|
+
}
|
|
20470
|
+
const x = Math.max(0, d) * 20;
|
|
20471
|
+
const i = Math.floor(x);
|
|
20472
|
+
return FAR_FADE[i] + (FAR_FADE[i + 1] - FAR_FADE[i]) * (x - i);
|
|
20473
|
+
}
|
|
20474
|
+
function nearSourceExtent(deviceLen, srcLen) {
|
|
20475
|
+
const d = Math.ceil(deviceLen - 1e-6) - srcLen;
|
|
20476
|
+
return d >= -1 && d <= 1;
|
|
20477
|
+
}
|
|
20453
20478
|
var BICUBIC_A = -0.5;
|
|
20454
20479
|
var HQ_BICUBIC_A = -1;
|
|
20455
20480
|
function axisFilter(kernel, scale, copy) {
|
|
@@ -20554,13 +20579,39 @@ function mirroredOrigin(o) {
|
|
|
20554
20579
|
return f === 0 ? o : Math.floor(o) + 1 - f;
|
|
20555
20580
|
}
|
|
20556
20581
|
function resampleImage(rgba, width, height, spec, surface) {
|
|
20582
|
+
if (spec.rightHalo && spec.rightHalo.length === height * 4) {
|
|
20583
|
+
const grown = new Uint8ClampedArray((width + 1) * height * 4);
|
|
20584
|
+
for (let y = 0; y < height; y++) {
|
|
20585
|
+
grown.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width + 1) * 4);
|
|
20586
|
+
grown.set(spec.rightHalo.subarray(y * 4, y * 4 + 4), (y * (width + 1) + width) * 4);
|
|
20587
|
+
}
|
|
20588
|
+
return resampleImage(grown, width + 1, height, { ...spec, rightHalo: void 0 }, surface);
|
|
20589
|
+
}
|
|
20557
20590
|
if (spec.kernel === "nearest" && !spec.wrap) {
|
|
20558
20591
|
return resampleNearest(rgba, width, height, spec, surface);
|
|
20559
20592
|
}
|
|
20560
20593
|
let m = snapToDeviceGrid(spec);
|
|
20561
|
-
|
|
20594
|
+
let kernel = spec.kernel;
|
|
20562
20595
|
const hq = kernel === "hq-bilinear" || kernel === "hq-bicubic";
|
|
20563
20596
|
const axisAligned = m[1] === 0 && m[2] === 0;
|
|
20597
|
+
let farFade = false;
|
|
20598
|
+
let shiftX = 0;
|
|
20599
|
+
let shiftY = 0;
|
|
20600
|
+
if (hq && !axisAligned) {
|
|
20601
|
+
const extentU = Math.hypot(m[0], m[1]) * spec.srcW;
|
|
20602
|
+
const extentV = Math.hypot(m[2], m[3]) * spec.srcH;
|
|
20603
|
+
if (nearSourceExtent(extentU, spec.srcW) || nearSourceExtent(extentV, spec.srcH)) {
|
|
20604
|
+
kernel = kernel === "hq-bicubic" ? "bicubic" : "bilinear";
|
|
20605
|
+
} else {
|
|
20606
|
+
farFade = true;
|
|
20607
|
+
if (spec.halfPixelOffset) {
|
|
20608
|
+
const lu = Math.hypot(m[0], m[1]);
|
|
20609
|
+
const lv = Math.hypot(m[2], m[3]);
|
|
20610
|
+
shiftX = 0.5 * m[0] / lu + 0.5 * m[2] / lv;
|
|
20611
|
+
shiftY = 0.5 * m[1] / lu + 0.5 * m[3] / lv;
|
|
20612
|
+
}
|
|
20613
|
+
}
|
|
20614
|
+
}
|
|
20564
20615
|
if (hq && axisAligned) {
|
|
20565
20616
|
const ox = m[0] * spec.srcX + m[4];
|
|
20566
20617
|
m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5]];
|
|
@@ -20615,6 +20666,8 @@ function resampleImage(rgba, width, height, spec, surface) {
|
|
|
20615
20666
|
const vMax = spec.srcY + spec.srcH;
|
|
20616
20667
|
const wu = [];
|
|
20617
20668
|
const wv = [];
|
|
20669
|
+
const gradU = Math.hypot(inv2[0], inv2[2]);
|
|
20670
|
+
const gradV = Math.hypot(inv2[1], inv2[3]);
|
|
20618
20671
|
const c = spec.clampArgb ?? 0;
|
|
20619
20672
|
const edge = {
|
|
20620
20673
|
wrap: spec.wrap,
|
|
@@ -20626,8 +20679,8 @@ function resampleImage(rgba, width, height, spec, surface) {
|
|
|
20626
20679
|
const py = by0 + j + o;
|
|
20627
20680
|
for (let i = 0; i < w; i++) {
|
|
20628
20681
|
const px = bx0 + i + o;
|
|
20629
|
-
const u = inv2[0] * px + inv2[2] * py + inv2[4];
|
|
20630
|
-
const v = inv2[1] * px + inv2[3] * py + inv2[5];
|
|
20682
|
+
const u = inv2[0] * (px - shiftX) + inv2[2] * (py - shiftY) + inv2[4];
|
|
20683
|
+
const v = inv2[1] * (px - shiftX) + inv2[3] * (py - shiftY) + inv2[5];
|
|
20631
20684
|
const cx = px + COVERAGE_NUDGE_X;
|
|
20632
20685
|
const cy = py + COVERAGE_NUDGE_Y;
|
|
20633
20686
|
const eu = inv2[0] * cx + inv2[2] * cy + inv2[4];
|
|
@@ -20635,31 +20688,35 @@ function resampleImage(rgba, width, height, spec, surface) {
|
|
|
20635
20688
|
if (eu < spec.srcX || ev < spec.srcY || eu >= uMax || ev >= vMax) {
|
|
20636
20689
|
continue;
|
|
20637
20690
|
}
|
|
20638
|
-
const
|
|
20639
|
-
const
|
|
20640
|
-
|
|
20641
|
-
|
|
20642
|
-
|
|
20643
|
-
|
|
20644
|
-
|
|
20645
|
-
|
|
20646
|
-
|
|
20647
|
-
|
|
20648
|
-
|
|
20649
|
-
|
|
20650
|
-
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
|
|
20655
|
-
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
|
|
20662
|
-
|
|
20691
|
+
const box = { x0: 0, y0: 0, x1: width, y1: height };
|
|
20692
|
+
const sampleAt = (cu, cv) => {
|
|
20693
|
+
const iu0 = Math.ceil(cu - fu.radius);
|
|
20694
|
+
const iu1 = Math.floor(cu + fu.radius);
|
|
20695
|
+
const iv0 = Math.ceil(cv - fv.radius);
|
|
20696
|
+
const iv1 = Math.floor(cv + fv.radius);
|
|
20697
|
+
wu.length = 0;
|
|
20698
|
+
wv.length = 0;
|
|
20699
|
+
for (let t = iu0; t <= iu1; t++) {
|
|
20700
|
+
wu.push(fu.weight(t, cu));
|
|
20701
|
+
}
|
|
20702
|
+
for (let t = iv0; t <= iv1; t++) {
|
|
20703
|
+
wv.push(fv.weight(t, cv));
|
|
20704
|
+
}
|
|
20705
|
+
return blendSeparable(rgba, width, box, iu0, wu, iv0, wv, kernel === "bicubic", edge);
|
|
20706
|
+
};
|
|
20707
|
+
let [r, g, b, a] = sampleAt(u, v);
|
|
20708
|
+
if (farFade && !spec.wrap && a > 0) {
|
|
20709
|
+
const du = (uMax - u) / gradU;
|
|
20710
|
+
const dv = (vMax - v) / gradV;
|
|
20711
|
+
const k = 1 - (du < 1 ? fadeAt(du, kernel === "hq-bilinear") * (sampleAt(uMax, v)[3] / a) : 0) - (dv < 1 ? fadeAt(dv, kernel === "hq-bilinear") * (sampleAt(u, vMax)[3] / a) : 0);
|
|
20712
|
+
if (k < 1) {
|
|
20713
|
+
const f = Math.max(0, k);
|
|
20714
|
+
r *= f;
|
|
20715
|
+
g *= f;
|
|
20716
|
+
b *= f;
|
|
20717
|
+
a *= f;
|
|
20718
|
+
}
|
|
20719
|
+
}
|
|
20663
20720
|
if (a <= 0) {
|
|
20664
20721
|
continue;
|
|
20665
20722
|
}
|
|
@@ -23486,7 +23543,8 @@ function effectedDraw(img, bitmap, effect, draw, source) {
|
|
|
23486
23543
|
srcY,
|
|
23487
23544
|
srcW,
|
|
23488
23545
|
srcH,
|
|
23489
|
-
toDevice: mulMatrix(draw.resample.toDevice, shift)
|
|
23546
|
+
toDevice: mulMatrix(draw.resample.toDevice, shift),
|
|
23547
|
+
rightHalo: region.haloRight
|
|
23490
23548
|
}
|
|
23491
23549
|
}
|
|
23492
23550
|
};
|
|
@@ -29228,9 +29286,9 @@ function perpendicularVectors(width, dx0, dy0) {
|
|
|
29228
29286
|
const vx = rounded[0], vy = rounded[1];
|
|
29229
29287
|
return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
|
|
29230
29288
|
}
|
|
29231
|
-
function squareExtension(width, dx, dy) {
|
|
29289
|
+
function squareExtension(width, dx, dy, scale = 1) {
|
|
29232
29290
|
const len = Math.hypot(dx, dy);
|
|
29233
|
-
const r = width / 2;
|
|
29291
|
+
const r = width / 2 * scale;
|
|
29234
29292
|
return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
|
|
29235
29293
|
}
|
|
29236
29294
|
function turnSign(ax, ay, bx, by) {
|
|
@@ -29269,7 +29327,7 @@ var Outliner = class {
|
|
|
29269
29327
|
const [L, R] = drawVertices(this.pen, d[0], d[1]);
|
|
29270
29328
|
const perpendicular = curve && drawDir ? drawDir : [dx, dy];
|
|
29271
29329
|
const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
|
|
29272
|
-
return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
|
|
29330
|
+
return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
|
|
29273
29331
|
}
|
|
29274
29332
|
push(p, v) {
|
|
29275
29333
|
this.pts.push([p[0] + v[0], p[1] + v[1]]);
|
|
@@ -29364,7 +29422,7 @@ var Outliner = class {
|
|
|
29364
29422
|
return side === "R" ? s.v : [-s.v[0], -s.v[1]];
|
|
29365
29423
|
}
|
|
29366
29424
|
/** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
|
|
29367
|
-
cap(p, s, start) {
|
|
29425
|
+
cap(p, s, start, scale = 1) {
|
|
29368
29426
|
const from = start ? "L" : "R";
|
|
29369
29427
|
const to = start ? "R" : "L";
|
|
29370
29428
|
const { cap } = this.opts;
|
|
@@ -29382,7 +29440,8 @@ var Outliner = class {
|
|
|
29382
29440
|
const sv = this.capSide(s, from);
|
|
29383
29441
|
const ev = this.capSide(s, to);
|
|
29384
29442
|
if (cap === "square") {
|
|
29385
|
-
const
|
|
29443
|
+
const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
|
|
29444
|
+
const e = start ? [-e0[0], -e0[1]] : e0;
|
|
29386
29445
|
this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
|
|
29387
29446
|
this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
|
|
29388
29447
|
} else {
|
|
@@ -29478,7 +29537,7 @@ var Outliner = class {
|
|
|
29478
29537
|
* lets a single point stand for a zero-length dash along `dirs[0]`;
|
|
29479
29538
|
* `draws` (curve end tangents) picks draw vertices only.
|
|
29480
29539
|
*/
|
|
29481
|
-
open(P, dirs, draws, curves) {
|
|
29540
|
+
open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
|
|
29482
29541
|
if (P.length < 2 && !dirs?.[0]) {
|
|
29483
29542
|
this.dot(P[0]);
|
|
29484
29543
|
return;
|
|
@@ -29489,12 +29548,12 @@ var Outliner = class {
|
|
|
29489
29548
|
}
|
|
29490
29549
|
if (segs2.length === 0) {
|
|
29491
29550
|
const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
|
|
29492
|
-
this.cap(P[0], s, true);
|
|
29493
|
-
this.cap(P[0], s, false);
|
|
29551
|
+
this.cap(P[0], s, true, startScale);
|
|
29552
|
+
this.cap(P[0], s, false, endScale);
|
|
29494
29553
|
this.flush();
|
|
29495
29554
|
return;
|
|
29496
29555
|
}
|
|
29497
|
-
this.cap(P[0], segs2[0], true);
|
|
29556
|
+
this.cap(P[0], segs2[0], true, startScale);
|
|
29498
29557
|
for (let i = 0; i + 1 < segs2.length; i++) {
|
|
29499
29558
|
const s = segs2[i];
|
|
29500
29559
|
const t = segs2[i + 1];
|
|
@@ -29505,7 +29564,7 @@ var Outliner = class {
|
|
|
29505
29564
|
this.join(P[i + 1], s, t, "R", tr < 0);
|
|
29506
29565
|
}
|
|
29507
29566
|
}
|
|
29508
|
-
this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
|
|
29567
|
+
this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
|
|
29509
29568
|
for (let i = segs2.length - 1; i > 0; i--) {
|
|
29510
29569
|
const s = segs2[i];
|
|
29511
29570
|
const t = segs2[i - 1];
|
|
@@ -29561,7 +29620,7 @@ var Outliner = class {
|
|
|
29561
29620
|
this.flush();
|
|
29562
29621
|
}
|
|
29563
29622
|
};
|
|
29564
|
-
function dashPieces(P, tangents, pattern, shorten) {
|
|
29623
|
+
function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
|
|
29565
29624
|
const out = [];
|
|
29566
29625
|
const dashes = [];
|
|
29567
29626
|
for (let i = 0; i < pattern.length; i += 2) {
|
|
@@ -29576,12 +29635,22 @@ function dashPieces(P, tangents, pattern, shorten) {
|
|
|
29576
29635
|
let idx = 0;
|
|
29577
29636
|
let left = dashes[0];
|
|
29578
29637
|
let on = true;
|
|
29579
|
-
let cur = { pts: [P[0]], dirs: [], draws: [] };
|
|
29638
|
+
let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
|
|
29580
29639
|
for (let i = 0; i + 1 < P.length; i++) {
|
|
29581
29640
|
const [x0, y0] = P[i];
|
|
29582
29641
|
const [x1, y1] = P[i + 1];
|
|
29583
29642
|
const dir = [x1 - x0, y1 - y0];
|
|
29584
|
-
const
|
|
29643
|
+
const real = Math.hypot(dir[0], dir[1]);
|
|
29644
|
+
const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
|
|
29645
|
+
if (len === 0) {
|
|
29646
|
+
if (on && cur) {
|
|
29647
|
+
cur.pts.push(P[i + 1]);
|
|
29648
|
+
cur.dirs.push(dir);
|
|
29649
|
+
cur.draws.push(tangents[i]);
|
|
29650
|
+
cur.curves.push(curveFlags[i]);
|
|
29651
|
+
}
|
|
29652
|
+
continue;
|
|
29653
|
+
}
|
|
29585
29654
|
let t = 0;
|
|
29586
29655
|
while (len - t > left || left === 0 && on) {
|
|
29587
29656
|
t += left;
|
|
@@ -29590,10 +29659,12 @@ function dashPieces(P, tangents, pattern, shorten) {
|
|
|
29590
29659
|
cur.pts.push(q);
|
|
29591
29660
|
cur.dirs.push(dir);
|
|
29592
29661
|
cur.draws.push(tangents[i]);
|
|
29662
|
+
cur.curves.push(curveFlags[i]);
|
|
29663
|
+
cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
|
|
29593
29664
|
out.push(cur);
|
|
29594
29665
|
cur = null;
|
|
29595
29666
|
} else {
|
|
29596
|
-
cur = { pts: [q], dirs: [], draws: [] };
|
|
29667
|
+
cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
|
|
29597
29668
|
}
|
|
29598
29669
|
on = !on;
|
|
29599
29670
|
idx = (idx + 1) % dashes.length;
|
|
@@ -29604,9 +29675,12 @@ function dashPieces(P, tangents, pattern, shorten) {
|
|
|
29604
29675
|
cur.pts.push(P[i + 1]);
|
|
29605
29676
|
cur.dirs.push(dir);
|
|
29606
29677
|
cur.draws.push(tangents[i]);
|
|
29678
|
+
cur.curves.push(curveFlags[i]);
|
|
29679
|
+
cur.endScale = real / len;
|
|
29607
29680
|
}
|
|
29608
29681
|
}
|
|
29609
29682
|
if (on && cur && cur.dirs.length > 0) {
|
|
29683
|
+
cur.endScale = 1;
|
|
29610
29684
|
out.push(cur);
|
|
29611
29685
|
}
|
|
29612
29686
|
return out;
|
|
@@ -29641,10 +29715,13 @@ function widenPath(path, opts) {
|
|
|
29641
29715
|
if (dashed) {
|
|
29642
29716
|
const run = closed ? [...P, P[0]] : P;
|
|
29643
29717
|
const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
|
|
29644
|
-
|
|
29718
|
+
const runTangents = closed ? [...dirs, void 0] : dirs;
|
|
29719
|
+
const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
|
|
29720
|
+
for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
|
|
29645
29721
|
const pts = [piece.pts[0]];
|
|
29646
29722
|
const pdirs = [];
|
|
29647
29723
|
const pdraws = [];
|
|
29724
|
+
const pcurves = [];
|
|
29648
29725
|
for (let i = 1; i < piece.pts.length; i++) {
|
|
29649
29726
|
const q = piece.pts[i];
|
|
29650
29727
|
const last = pts[pts.length - 1];
|
|
@@ -29652,9 +29729,10 @@ function widenPath(path, opts) {
|
|
|
29652
29729
|
pts.push(q);
|
|
29653
29730
|
pdirs.push(piece.dirs[i - 1]);
|
|
29654
29731
|
pdraws.push(piece.draws[i - 1]);
|
|
29732
|
+
pcurves.push(piece.curves[i - 1]);
|
|
29655
29733
|
}
|
|
29656
29734
|
}
|
|
29657
|
-
outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]],
|
|
29735
|
+
outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
|
|
29658
29736
|
}
|
|
29659
29737
|
} else if (closed && P.length >= 3) {
|
|
29660
29738
|
outliner.closed(P, dirs, curves);
|
|
@@ -29862,7 +29940,10 @@ function penWidenOptions(rCtx, opts = {}) {
|
|
|
29862
29940
|
join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
|
|
29863
29941
|
miterLimit: state.miterLimit ?? 10,
|
|
29864
29942
|
dashes: dashes ? dashes.map((v) => v * 16) : null,
|
|
29865
|
-
shortenDashes: (flags & 15) !== 7
|
|
29943
|
+
shortenDashes: (flags & 15) !== 7,
|
|
29944
|
+
// One logical unit per device pixel, unrotated: GDI measures dash
|
|
29945
|
+
// segments in whole pixels.
|
|
29946
|
+
wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
|
|
29866
29947
|
};
|
|
29867
29948
|
}
|
|
29868
29949
|
function hatchBackgroundMask(hatch) {
|
|
@@ -31402,6 +31483,15 @@ function illuminantCube(index) {
|
|
|
31402
31483
|
}
|
|
31403
31484
|
return cube;
|
|
31404
31485
|
}
|
|
31486
|
+
var ILLUMINANT_MATRICES = {
|
|
31487
|
+
1: [[1.64737, -0.59174, -0.05494], [0.02963, 0.96126, 881e-5], [0.2911, 0.40189, 0.30769]],
|
|
31488
|
+
2: [[1.22319, -0.17765, -0.04515], [275e-5, 0.99054, 645e-5], [0.05514, 0.12795, 0.81816]],
|
|
31489
|
+
3: [[1.02833, -0.01775, -0.01086], [861e-5, 0.98431, 728e-5], [-0.01616, -0.0448, 1.05995]],
|
|
31490
|
+
4: [[1.17271, -0.14059, -0.03174], [-474e-5, 1.00266, 183e-5], [0.05783, 0.14663, 0.79683]],
|
|
31491
|
+
5: [[1.10431, -0.08327, -0.02067], [-336e-5, 1.0025, 67e-5], [0.03344, 0.09072, 0.87702]],
|
|
31492
|
+
7: [[0.92515, 0.05628, 0.01822], [318e-5, 0.9967, 34e-5], [-0.02086, -0.06817, 1.08783]],
|
|
31493
|
+
8: [[1.31275, -0.26259, -0.04977], [-244e-5, 0.99695, 524e-5], [0.10854, 0.23443, 0.65827]]
|
|
31494
|
+
};
|
|
31405
31495
|
function hasIlluminant(ca) {
|
|
31406
31496
|
return illuminantCube(ca.illuminant) !== null;
|
|
31407
31497
|
}
|
|
@@ -31472,9 +31562,9 @@ var BRIGHTNESS_STEP = 0.95625;
|
|
|
31472
31562
|
var COLORFULNESS_SCALE = 1 / 100;
|
|
31473
31563
|
var TINT_RADIANS = -Math.PI / 3;
|
|
31474
31564
|
var RGB_TO_XYZ = [
|
|
31475
|
-
[0.
|
|
31476
|
-
[0.
|
|
31477
|
-
[0.
|
|
31565
|
+
[0.4123908, 0.3575843, 0.1804808],
|
|
31566
|
+
[0.212639, 0.7151687, 0.0721923],
|
|
31567
|
+
[0.0193308, 0.1191948, 0.9505322]
|
|
31478
31568
|
];
|
|
31479
31569
|
var XYZ_TO_RGB = invert3(RGB_TO_XYZ);
|
|
31480
31570
|
var WHITE = RGB_TO_XYZ.map((row2) => row2[0] + row2[1] + row2[2]);
|
|
@@ -31502,13 +31592,13 @@ function adjustChroma(r, g, b, scale, angle) {
|
|
|
31502
31592
|
const z = RGB_TO_XYZ[2][0] * r + RGB_TO_XYZ[2][1] * g + RGB_TO_XYZ[2][2] * b;
|
|
31503
31593
|
const d = x + 15 * y + 3 * z;
|
|
31504
31594
|
if (d <= 1e-9 || y <= 1e-9) {
|
|
31505
|
-
return [r, g, b];
|
|
31595
|
+
return [clamp255(r), clamp255(g), clamp255(b)];
|
|
31506
31596
|
}
|
|
31507
31597
|
const du = 4 * x / d - WHITE_U;
|
|
31508
31598
|
const dv = 9 * y / d - WHITE_V;
|
|
31509
31599
|
const cos = Math.cos(angle) * scale;
|
|
31510
31600
|
const sin = Math.sin(angle) * scale;
|
|
31511
|
-
const u = WHITE_U + du * cos - dv * sin;
|
|
31601
|
+
const u = Math.max(0, WHITE_U + du * cos - dv * sin);
|
|
31512
31602
|
const v = Math.max(1e-6, WHITE_V + du * sin + dv * cos);
|
|
31513
31603
|
const x2 = y * 9 * u / (4 * v);
|
|
31514
31604
|
const z2 = y * (12 - 3 * u - 20 * v) / (4 * v);
|
|
@@ -31527,6 +31617,7 @@ function adjustChroma(r, g, b, scale, angle) {
|
|
|
31527
31617
|
}
|
|
31528
31618
|
function colorAdjustmentMapper(ca) {
|
|
31529
31619
|
const illuminant = illuminantCube(ca.illuminant);
|
|
31620
|
+
const matrix = ILLUMINANT_MATRICES[ca.illuminant];
|
|
31530
31621
|
const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
|
|
31531
31622
|
const black = ca.referenceBlack / 1e4 * 255;
|
|
31532
31623
|
const span = Math.max(1e-6, ca.referenceWhite / 1e4 * 255 - black);
|
|
@@ -31555,16 +31646,17 @@ function colorAdjustmentMapper(ca) {
|
|
|
31555
31646
|
}
|
|
31556
31647
|
return (rgb2) => {
|
|
31557
31648
|
let c = [rgb2 >> 16 & 255, rgb2 >> 8 & 255, rgb2 & 255];
|
|
31558
|
-
if (illuminant) {
|
|
31559
|
-
const mapped = lookupIlluminant(illuminant, c[0] / 255, c[1] / 255, c[2] / 255);
|
|
31560
|
-
c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
|
|
31561
|
-
}
|
|
31562
31649
|
if (chroma) {
|
|
31563
|
-
|
|
31564
|
-
|
|
31650
|
+
c = c.map(exactPaletteLevel);
|
|
31651
|
+
if (matrix) {
|
|
31652
|
+
const [r, g, b] = c;
|
|
31653
|
+
c = matrix.map((row2) => row2[0] * r + row2[1] * g + row2[2] * b);
|
|
31565
31654
|
}
|
|
31566
|
-
c = adjustChroma(
|
|
31655
|
+
c = adjustChroma(c[0], c[1], c[2], scale, angle);
|
|
31567
31656
|
c = c.map(roundHalfUp);
|
|
31657
|
+
} else if (illuminant) {
|
|
31658
|
+
const mapped = lookupIlluminant(illuminant, c[0] / 255, c[1] / 255, c[2] / 255);
|
|
31659
|
+
c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
|
|
31568
31660
|
}
|
|
31569
31661
|
return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
|
|
31570
31662
|
};
|
|
@@ -31918,20 +32010,6 @@ function halftoneDespeckle(px, w, h) {
|
|
|
31918
32010
|
}
|
|
31919
32011
|
}
|
|
31920
32012
|
}
|
|
31921
|
-
function halftoneSharpen(px, w, h) {
|
|
31922
|
-
const src = px.slice();
|
|
31923
|
-
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];
|
|
31924
|
-
for (let y = 0; y < h; y++) {
|
|
31925
|
-
for (let x = 0; x < w; x++) {
|
|
31926
|
-
for (let c = 0; c < 3; c++) {
|
|
31927
|
-
const v = at(x, y, c);
|
|
31928
|
-
const sum = at(x - 1, y, c) + at(x + 1, y, c) + at(x, y - 1, c) + at(x, y + 1, c);
|
|
31929
|
-
const out = v + Math.floor((4 * v - sum) / 8);
|
|
31930
|
-
px[(y * w + x) * 3 + c] = out < 0 ? 0 : out > 255 ? 255 : out;
|
|
31931
|
-
}
|
|
31932
|
-
}
|
|
31933
|
-
}
|
|
31934
|
-
}
|
|
31935
32013
|
function halftonePairDespeckle(px, w, h, rows) {
|
|
31936
32014
|
const n = rows ? w : h;
|
|
31937
32015
|
const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
|
|
@@ -31954,6 +32032,46 @@ function halftoneKeepLast(px, w, h, rows) {
|
|
|
31954
32032
|
}
|
|
31955
32033
|
return out;
|
|
31956
32034
|
}
|
|
32035
|
+
function halftoneReduceBoth(rect, sw, sh, dw, dh, flipX, flipY) {
|
|
32036
|
+
const cols = halftoneReduceTaps(sw, dw, flipX);
|
|
32037
|
+
const rows = halftoneReduceTaps(sh, dh, flipY);
|
|
32038
|
+
const wide = new Int32Array(dw * sh * 3);
|
|
32039
|
+
for (let y = 0; y < sh; y++) {
|
|
32040
|
+
for (let x = 0; x < dw; x++) {
|
|
32041
|
+
let total = 0;
|
|
32042
|
+
for (const [, w] of cols[x]) total += w;
|
|
32043
|
+
for (let c = 0; c < 3; c++) {
|
|
32044
|
+
let sum = 0;
|
|
32045
|
+
for (const [xx, w] of cols[x]) sum += rect[(y * sw + xx) * 3 + c] * w;
|
|
32046
|
+
wide[(y * dw + x) * 3 + c] = Math.floor((2 * sum + total) / (2 * total));
|
|
32047
|
+
}
|
|
32048
|
+
}
|
|
32049
|
+
}
|
|
32050
|
+
const avg = new Float64Array(dw * dh * 3);
|
|
32051
|
+
for (let y = 0; y < dh; y++) {
|
|
32052
|
+
let total = 0;
|
|
32053
|
+
for (const [, w] of rows[y]) total += w;
|
|
32054
|
+
for (let x = 0; x < dw; x++) {
|
|
32055
|
+
for (let c = 0; c < 3; c++) {
|
|
32056
|
+
let sum = 0;
|
|
32057
|
+
for (const [yy, w] of rows[y]) sum += wide[(yy * dw + x) * 3 + c] * w;
|
|
32058
|
+
avg[(y * dw + x) * 3 + c] = sum / total;
|
|
32059
|
+
}
|
|
32060
|
+
}
|
|
32061
|
+
}
|
|
32062
|
+
const at = (x, y, c) => avg[(Math.max(0, Math.min(dh - 1, y)) * dw + Math.max(0, Math.min(dw - 1, x))) * 3 + c];
|
|
32063
|
+
const out = new Int32Array(dw * dh * 3);
|
|
32064
|
+
for (let y = 0; y < dh; y++) {
|
|
32065
|
+
for (let x = 0; x < dw; x++) {
|
|
32066
|
+
for (let c = 0; c < 3; c++) {
|
|
32067
|
+
const v = at(x, y, c);
|
|
32068
|
+
const sum = at(x - 1, y, c) + at(x + 1, y, c) + at(x, y - 1, c) + at(x, y + 1, c);
|
|
32069
|
+
out[(y * dw + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (4 * v - sum) / 8)));
|
|
32070
|
+
}
|
|
32071
|
+
}
|
|
32072
|
+
}
|
|
32073
|
+
return out;
|
|
32074
|
+
}
|
|
31957
32075
|
function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
|
|
31958
32076
|
const W = Math.max(0, Math.round(Math.abs(dw)));
|
|
31959
32077
|
const H = Math.max(0, Math.round(Math.abs(dh)));
|
|
@@ -32005,11 +32123,18 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
|
|
|
32005
32123
|
}
|
|
32006
32124
|
const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
|
|
32007
32125
|
const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
|
|
32126
|
+
const extendRow = ditherAtSource && nativeMixed && W < SW && H > SH && !flipY;
|
|
32127
|
+
if (extendRow) {
|
|
32128
|
+
const bigger = new Int32Array((SH + 1) * SW * 3);
|
|
32129
|
+
bigger.set(rect);
|
|
32130
|
+
bigger.copyWithin(SH * SW * 3, (SH - 1) * SW * 3, SH * SW * 3);
|
|
32131
|
+
rect = bigger;
|
|
32132
|
+
}
|
|
32008
32133
|
if (ditherAtSource) {
|
|
32009
32134
|
ditherPixels(
|
|
32010
32135
|
rect,
|
|
32011
32136
|
SW,
|
|
32012
|
-
SH,
|
|
32137
|
+
extendRow ? SH + 1 : SH,
|
|
32013
32138
|
dither,
|
|
32014
32139
|
false,
|
|
32015
32140
|
false,
|
|
@@ -32049,7 +32174,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
|
|
|
32049
32174
|
linear(rows, SH, H, flipY);
|
|
32050
32175
|
}
|
|
32051
32176
|
let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
|
|
32052
|
-
|
|
32177
|
+
if (reducing) {
|
|
32178
|
+
out = halftoneReduceBoth(rect, SW, SH, W, H, flipX, flipY);
|
|
32179
|
+
}
|
|
32180
|
+
for (let y = 0; !reducing && y < sampleH; y++) {
|
|
32053
32181
|
const ys = rows[y];
|
|
32054
32182
|
for (let x = 0; x < sampleW; x++) {
|
|
32055
32183
|
const xs = cols[x];
|
|
@@ -32074,15 +32202,38 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
|
|
|
32074
32202
|
out[o + 2] = round(b);
|
|
32075
32203
|
}
|
|
32076
32204
|
}
|
|
32077
|
-
if (reducing) {
|
|
32078
|
-
halftoneSharpen(out, W, H);
|
|
32079
|
-
}
|
|
32080
32205
|
if (nativeMixed) {
|
|
32081
32206
|
const reduceHorizontal = W < SW;
|
|
32207
|
+
let extension;
|
|
32208
|
+
if (extendRow) {
|
|
32209
|
+
const reduced = new Float64Array(sampleW * 3);
|
|
32210
|
+
for (let x = 0; x < sampleW; x++) {
|
|
32211
|
+
let total = 0;
|
|
32212
|
+
const sums = [0, 0, 0];
|
|
32213
|
+
for (const [xx, wx] of cols[x]) {
|
|
32214
|
+
total += wx;
|
|
32215
|
+
for (let c = 0; c < 3; c++) sums[c] += rect[(SH * SW + xx) * 3 + c] * wx;
|
|
32216
|
+
}
|
|
32217
|
+
for (let c = 0; c < 3; c++) reduced[x * 3 + c] = sums[c] / total;
|
|
32218
|
+
}
|
|
32219
|
+
extension = reduced;
|
|
32220
|
+
}
|
|
32082
32221
|
for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
|
|
32083
32222
|
const source2 = out;
|
|
32084
32223
|
out = new Int32Array(source2.length);
|
|
32085
|
-
const
|
|
32224
|
+
const ext = extension;
|
|
32225
|
+
const at = (x, y, c) => y >= sampleH && ext ? ext[Math.max(0, Math.min(sampleW - 1, x)) * 3 + c] : source2[(Math.max(0, Math.min(sampleH - 1, y)) * sampleW + Math.max(0, Math.min(sampleW - 1, x))) * 3 + c];
|
|
32226
|
+
if (horizontal && ext) {
|
|
32227
|
+
const next = new Int32Array(sampleW * 3);
|
|
32228
|
+
for (let x = 0; x < sampleW; x++) {
|
|
32229
|
+
for (let c = 0; c < 3; c++) {
|
|
32230
|
+
const v = ext[x * 3 + c];
|
|
32231
|
+
const sum = at(x - 1, sampleH, c) + at(x + 1, sampleH, c);
|
|
32232
|
+
next[x * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
|
|
32233
|
+
}
|
|
32234
|
+
}
|
|
32235
|
+
extension = next;
|
|
32236
|
+
}
|
|
32086
32237
|
for (let y = 0; y < sampleH; y++) {
|
|
32087
32238
|
for (let x = 0; x < sampleW; x++) {
|
|
32088
32239
|
for (let c = 0; c < 3; c++) {
|