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/dist/index.js CHANGED
@@ -17040,13 +17040,17 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
17040
17040
  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)));
17041
17041
  const w = x1 - x0;
17042
17042
  const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
17043
+ const halo = new Uint8ClampedArray((y1 - y0) * 4);
17043
17044
  for (let y = 0; y < y1 - y0; y++) {
17044
17045
  const from = ((y + r) * bw + r) * 4;
17045
17046
  for (let i = 0; i < w * 4; i++) {
17046
17047
  out[y * w * 4 + i] = Math.round(blurred[from + i]);
17047
17048
  }
17049
+ for (let i = 0; i < 4; i++) {
17050
+ halo[y * 4 + i] = Math.round(blurred[from + w * 4 + i]);
17051
+ }
17048
17052
  }
17049
- return out;
17053
+ return { out, halo };
17050
17054
  }
17051
17055
  function applyImageEffectToRect(rgba, width, height, effect, src) {
17052
17056
  if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
@@ -17079,7 +17083,12 @@ function applyImageEffectToRect(rgba, width, height, effect, src) {
17079
17083
  };
17080
17084
  if (src && effect.kind === "blur" && effect.expandEdge) {
17081
17085
  const cropped = Math.ceil(src.x + src.w) < width && Math.ceil(src.y + src.h) < height;
17082
- return result(expandedBlur(rgba, width, height, effect.radius, x0, y0, x1, y1, cropped));
17086
+ const { out: out2, halo } = expandedBlur(rgba, width, height, effect.radius, x0, y0, x1, y1, cropped);
17087
+ const region2 = result(out2);
17088
+ if (shownWidth === w && shownHeight === h) {
17089
+ region2.haloRight = halo;
17090
+ }
17091
+ return region2;
17083
17092
  }
17084
17093
  let region = rgba;
17085
17094
  if (w !== width || h !== height) {
@@ -20451,6 +20460,22 @@ function cubicIntegral(t, a) {
20451
20460
  }
20452
20461
  return Math.sign(t) * v;
20453
20462
  }
20463
+ 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];
20464
+ function fadeAt(d, bilinear) {
20465
+ if (d >= 1) {
20466
+ return 0;
20467
+ }
20468
+ if (bilinear) {
20469
+ return 1 - Math.max(0, d);
20470
+ }
20471
+ const x = Math.max(0, d) * 20;
20472
+ const i = Math.floor(x);
20473
+ return FAR_FADE[i] + (FAR_FADE[i + 1] - FAR_FADE[i]) * (x - i);
20474
+ }
20475
+ function nearSourceExtent(deviceLen, srcLen) {
20476
+ const d = Math.ceil(deviceLen - 1e-6) - srcLen;
20477
+ return d >= -1 && d <= 1;
20478
+ }
20454
20479
  var BICUBIC_A = -0.5;
20455
20480
  var HQ_BICUBIC_A = -1;
20456
20481
  function axisFilter(kernel, scale, copy) {
@@ -20555,13 +20580,39 @@ function mirroredOrigin(o) {
20555
20580
  return f === 0 ? o : Math.floor(o) + 1 - f;
20556
20581
  }
20557
20582
  function resampleImage(rgba, width, height, spec, surface) {
20583
+ if (spec.rightHalo && spec.rightHalo.length === height * 4) {
20584
+ const grown = new Uint8ClampedArray((width + 1) * height * 4);
20585
+ for (let y = 0; y < height; y++) {
20586
+ grown.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * (width + 1) * 4);
20587
+ grown.set(spec.rightHalo.subarray(y * 4, y * 4 + 4), (y * (width + 1) + width) * 4);
20588
+ }
20589
+ return resampleImage(grown, width + 1, height, { ...spec, rightHalo: void 0 }, surface);
20590
+ }
20558
20591
  if (spec.kernel === "nearest" && !spec.wrap) {
20559
20592
  return resampleNearest(rgba, width, height, spec, surface);
20560
20593
  }
20561
20594
  let m = snapToDeviceGrid(spec);
20562
- const kernel = spec.kernel;
20595
+ let kernel = spec.kernel;
20563
20596
  const hq = kernel === "hq-bilinear" || kernel === "hq-bicubic";
20564
20597
  const axisAligned = m[1] === 0 && m[2] === 0;
20598
+ let farFade = false;
20599
+ let shiftX = 0;
20600
+ let shiftY = 0;
20601
+ if (hq && !axisAligned) {
20602
+ const extentU = Math.hypot(m[0], m[1]) * spec.srcW;
20603
+ const extentV = Math.hypot(m[2], m[3]) * spec.srcH;
20604
+ if (nearSourceExtent(extentU, spec.srcW) || nearSourceExtent(extentV, spec.srcH)) {
20605
+ kernel = kernel === "hq-bicubic" ? "bicubic" : "bilinear";
20606
+ } else {
20607
+ farFade = true;
20608
+ if (spec.halfPixelOffset) {
20609
+ const lu = Math.hypot(m[0], m[1]);
20610
+ const lv = Math.hypot(m[2], m[3]);
20611
+ shiftX = 0.5 * m[0] / lu + 0.5 * m[2] / lv;
20612
+ shiftY = 0.5 * m[1] / lu + 0.5 * m[3] / lv;
20613
+ }
20614
+ }
20615
+ }
20565
20616
  if (hq && axisAligned) {
20566
20617
  const ox = m[0] * spec.srcX + m[4];
20567
20618
  m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5]];
@@ -20616,6 +20667,8 @@ function resampleImage(rgba, width, height, spec, surface) {
20616
20667
  const vMax = spec.srcY + spec.srcH;
20617
20668
  const wu = [];
20618
20669
  const wv = [];
20670
+ const gradU = Math.hypot(inv2[0], inv2[2]);
20671
+ const gradV = Math.hypot(inv2[1], inv2[3]);
20619
20672
  const c = spec.clampArgb ?? 0;
20620
20673
  const edge = {
20621
20674
  wrap: spec.wrap,
@@ -20627,8 +20680,8 @@ function resampleImage(rgba, width, height, spec, surface) {
20627
20680
  const py = by0 + j + o;
20628
20681
  for (let i = 0; i < w; i++) {
20629
20682
  const px = bx0 + i + o;
20630
- const u = inv2[0] * px + inv2[2] * py + inv2[4];
20631
- const v = inv2[1] * px + inv2[3] * py + inv2[5];
20683
+ const u = inv2[0] * (px - shiftX) + inv2[2] * (py - shiftY) + inv2[4];
20684
+ const v = inv2[1] * (px - shiftX) + inv2[3] * (py - shiftY) + inv2[5];
20632
20685
  const cx = px + COVERAGE_NUDGE_X;
20633
20686
  const cy = py + COVERAGE_NUDGE_Y;
20634
20687
  const eu = inv2[0] * cx + inv2[2] * cy + inv2[4];
@@ -20636,31 +20689,35 @@ function resampleImage(rgba, width, height, spec, surface) {
20636
20689
  if (eu < spec.srcX || ev < spec.srcY || eu >= uMax || ev >= vMax) {
20637
20690
  continue;
20638
20691
  }
20639
- const cu = u;
20640
- const cv = v;
20641
- const iu0 = Math.ceil(cu - fu.radius);
20642
- const iu1 = Math.floor(cu + fu.radius);
20643
- const iv0 = Math.ceil(cv - fv.radius);
20644
- const iv1 = Math.floor(cv + fv.radius);
20645
- wu.length = 0;
20646
- wv.length = 0;
20647
- for (let t = iu0; t <= iu1; t++) {
20648
- wu.push(fu.weight(t, cu));
20649
- }
20650
- for (let t = iv0; t <= iv1; t++) {
20651
- wv.push(fv.weight(t, cv));
20652
- }
20653
- const [r, g, b, a] = blendSeparable(
20654
- rgba,
20655
- width,
20656
- spec.wrap ? { x0: 0, y0: 0, x1: width, y1: height } : { x0: sx0, y0: sy0, x1: sx1, y1: sy1 },
20657
- iu0,
20658
- wu,
20659
- iv0,
20660
- wv,
20661
- kernel === "bicubic",
20662
- edge
20663
- );
20692
+ const box = { x0: 0, y0: 0, x1: width, y1: height };
20693
+ const sampleAt = (cu, cv) => {
20694
+ const iu0 = Math.ceil(cu - fu.radius);
20695
+ const iu1 = Math.floor(cu + fu.radius);
20696
+ const iv0 = Math.ceil(cv - fv.radius);
20697
+ const iv1 = Math.floor(cv + fv.radius);
20698
+ wu.length = 0;
20699
+ wv.length = 0;
20700
+ for (let t = iu0; t <= iu1; t++) {
20701
+ wu.push(fu.weight(t, cu));
20702
+ }
20703
+ for (let t = iv0; t <= iv1; t++) {
20704
+ wv.push(fv.weight(t, cv));
20705
+ }
20706
+ return blendSeparable(rgba, width, box, iu0, wu, iv0, wv, kernel === "bicubic", edge);
20707
+ };
20708
+ let [r, g, b, a] = sampleAt(u, v);
20709
+ if (farFade && !spec.wrap && a > 0) {
20710
+ const du = (uMax - u) / gradU;
20711
+ const dv = (vMax - v) / gradV;
20712
+ 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);
20713
+ if (k < 1) {
20714
+ const f = Math.max(0, k);
20715
+ r *= f;
20716
+ g *= f;
20717
+ b *= f;
20718
+ a *= f;
20719
+ }
20720
+ }
20664
20721
  if (a <= 0) {
20665
20722
  continue;
20666
20723
  }
@@ -23487,7 +23544,8 @@ function effectedDraw(img, bitmap, effect, draw, source) {
23487
23544
  srcY,
23488
23545
  srcW,
23489
23546
  srcH,
23490
- toDevice: mulMatrix(draw.resample.toDevice, shift)
23547
+ toDevice: mulMatrix(draw.resample.toDevice, shift),
23548
+ rightHalo: region.haloRight
23491
23549
  }
23492
23550
  }
23493
23551
  };
@@ -29229,9 +29287,9 @@ function perpendicularVectors(width, dx0, dy0) {
29229
29287
  const vx = rounded[0], vy = rounded[1];
29230
29288
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29231
29289
  }
29232
- function squareExtension(width, dx, dy) {
29290
+ function squareExtension(width, dx, dy, scale = 1) {
29233
29291
  const len = Math.hypot(dx, dy);
29234
- const r = width / 2;
29292
+ const r = width / 2 * scale;
29235
29293
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29236
29294
  }
29237
29295
  function turnSign(ax, ay, bx, by) {
@@ -29270,7 +29328,7 @@ var Outliner = class {
29270
29328
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29271
29329
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29272
29330
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29273
- 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 };
29331
+ 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 };
29274
29332
  }
29275
29333
  push(p, v) {
29276
29334
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29365,7 +29423,7 @@ var Outliner = class {
29365
29423
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29366
29424
  }
29367
29425
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29368
- cap(p, s, start) {
29426
+ cap(p, s, start, scale = 1) {
29369
29427
  const from = start ? "L" : "R";
29370
29428
  const to = start ? "R" : "L";
29371
29429
  const { cap } = this.opts;
@@ -29383,7 +29441,8 @@ var Outliner = class {
29383
29441
  const sv = this.capSide(s, from);
29384
29442
  const ev = this.capSide(s, to);
29385
29443
  if (cap === "square") {
29386
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29444
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29445
+ const e = start ? [-e0[0], -e0[1]] : e0;
29387
29446
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29388
29447
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29389
29448
  } else {
@@ -29479,7 +29538,7 @@ var Outliner = class {
29479
29538
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29480
29539
  * `draws` (curve end tangents) picks draw vertices only.
29481
29540
  */
29482
- open(P, dirs, draws, curves) {
29541
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29483
29542
  if (P.length < 2 && !dirs?.[0]) {
29484
29543
  this.dot(P[0]);
29485
29544
  return;
@@ -29490,12 +29549,12 @@ var Outliner = class {
29490
29549
  }
29491
29550
  if (segs2.length === 0) {
29492
29551
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29493
- this.cap(P[0], s, true);
29494
- this.cap(P[0], s, false);
29552
+ this.cap(P[0], s, true, startScale);
29553
+ this.cap(P[0], s, false, endScale);
29495
29554
  this.flush();
29496
29555
  return;
29497
29556
  }
29498
- this.cap(P[0], segs2[0], true);
29557
+ this.cap(P[0], segs2[0], true, startScale);
29499
29558
  for (let i = 0; i + 1 < segs2.length; i++) {
29500
29559
  const s = segs2[i];
29501
29560
  const t = segs2[i + 1];
@@ -29506,7 +29565,7 @@ var Outliner = class {
29506
29565
  this.join(P[i + 1], s, t, "R", tr < 0);
29507
29566
  }
29508
29567
  }
29509
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29568
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29510
29569
  for (let i = segs2.length - 1; i > 0; i--) {
29511
29570
  const s = segs2[i];
29512
29571
  const t = segs2[i - 1];
@@ -29562,7 +29621,7 @@ var Outliner = class {
29562
29621
  this.flush();
29563
29622
  }
29564
29623
  };
29565
- function dashPieces(P, tangents, pattern, shorten) {
29624
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29566
29625
  const out = [];
29567
29626
  const dashes = [];
29568
29627
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29577,12 +29636,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29577
29636
  let idx = 0;
29578
29637
  let left = dashes[0];
29579
29638
  let on = true;
29580
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29639
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29581
29640
  for (let i = 0; i + 1 < P.length; i++) {
29582
29641
  const [x0, y0] = P[i];
29583
29642
  const [x1, y1] = P[i + 1];
29584
29643
  const dir = [x1 - x0, y1 - y0];
29585
- const len = Math.hypot(dir[0], dir[1]);
29644
+ const real = Math.hypot(dir[0], dir[1]);
29645
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29646
+ if (len === 0) {
29647
+ if (on && cur) {
29648
+ cur.pts.push(P[i + 1]);
29649
+ cur.dirs.push(dir);
29650
+ cur.draws.push(tangents[i]);
29651
+ cur.curves.push(curveFlags[i]);
29652
+ }
29653
+ continue;
29654
+ }
29586
29655
  let t = 0;
29587
29656
  while (len - t > left || left === 0 && on) {
29588
29657
  t += left;
@@ -29591,10 +29660,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29591
29660
  cur.pts.push(q);
29592
29661
  cur.dirs.push(dir);
29593
29662
  cur.draws.push(tangents[i]);
29663
+ cur.curves.push(curveFlags[i]);
29664
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29594
29665
  out.push(cur);
29595
29666
  cur = null;
29596
29667
  } else {
29597
- cur = { pts: [q], dirs: [], draws: [] };
29668
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29598
29669
  }
29599
29670
  on = !on;
29600
29671
  idx = (idx + 1) % dashes.length;
@@ -29605,9 +29676,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29605
29676
  cur.pts.push(P[i + 1]);
29606
29677
  cur.dirs.push(dir);
29607
29678
  cur.draws.push(tangents[i]);
29679
+ cur.curves.push(curveFlags[i]);
29680
+ cur.endScale = real / len;
29608
29681
  }
29609
29682
  }
29610
29683
  if (on && cur && cur.dirs.length > 0) {
29684
+ cur.endScale = 1;
29611
29685
  out.push(cur);
29612
29686
  }
29613
29687
  return out;
@@ -29642,10 +29716,13 @@ function widenPath(path, opts) {
29642
29716
  if (dashed) {
29643
29717
  const run = closed ? [...P, P[0]] : P;
29644
29718
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29645
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29719
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29720
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29721
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29646
29722
  const pts = [piece.pts[0]];
29647
29723
  const pdirs = [];
29648
29724
  const pdraws = [];
29725
+ const pcurves = [];
29649
29726
  for (let i = 1; i < piece.pts.length; i++) {
29650
29727
  const q = piece.pts[i];
29651
29728
  const last = pts[pts.length - 1];
@@ -29653,9 +29730,10 @@ function widenPath(path, opts) {
29653
29730
  pts.push(q);
29654
29731
  pdirs.push(piece.dirs[i - 1]);
29655
29732
  pdraws.push(piece.draws[i - 1]);
29733
+ pcurves.push(piece.curves[i - 1]);
29656
29734
  }
29657
29735
  }
29658
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29736
+ 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);
29659
29737
  }
29660
29738
  } else if (closed && P.length >= 3) {
29661
29739
  outliner.closed(P, dirs, curves);
@@ -29863,7 +29941,10 @@ function penWidenOptions(rCtx, opts = {}) {
29863
29941
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29864
29942
  miterLimit: state.miterLimit ?? 10,
29865
29943
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29866
- shortenDashes: (flags & 15) !== 7
29944
+ shortenDashes: (flags & 15) !== 7,
29945
+ // One logical unit per device pixel, unrotated: GDI measures dash
29946
+ // segments in whole pixels.
29947
+ 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
29867
29948
  };
29868
29949
  }
29869
29950
  function hatchBackgroundMask(hatch) {
@@ -31403,6 +31484,15 @@ function illuminantCube(index) {
31403
31484
  }
31404
31485
  return cube;
31405
31486
  }
31487
+ var ILLUMINANT_MATRICES = {
31488
+ 1: [[1.64737, -0.59174, -0.05494], [0.02963, 0.96126, 881e-5], [0.2911, 0.40189, 0.30769]],
31489
+ 2: [[1.22319, -0.17765, -0.04515], [275e-5, 0.99054, 645e-5], [0.05514, 0.12795, 0.81816]],
31490
+ 3: [[1.02833, -0.01775, -0.01086], [861e-5, 0.98431, 728e-5], [-0.01616, -0.0448, 1.05995]],
31491
+ 4: [[1.17271, -0.14059, -0.03174], [-474e-5, 1.00266, 183e-5], [0.05783, 0.14663, 0.79683]],
31492
+ 5: [[1.10431, -0.08327, -0.02067], [-336e-5, 1.0025, 67e-5], [0.03344, 0.09072, 0.87702]],
31493
+ 7: [[0.92515, 0.05628, 0.01822], [318e-5, 0.9967, 34e-5], [-0.02086, -0.06817, 1.08783]],
31494
+ 8: [[1.31275, -0.26259, -0.04977], [-244e-5, 0.99695, 524e-5], [0.10854, 0.23443, 0.65827]]
31495
+ };
31406
31496
  function hasIlluminant(ca) {
31407
31497
  return illuminantCube(ca.illuminant) !== null;
31408
31498
  }
@@ -31473,9 +31563,9 @@ var BRIGHTNESS_STEP = 0.95625;
31473
31563
  var COLORFULNESS_SCALE = 1 / 100;
31474
31564
  var TINT_RADIANS = -Math.PI / 3;
31475
31565
  var RGB_TO_XYZ = [
31476
- [0.4124, 0.3576, 0.1805],
31477
- [0.2126, 0.7152, 0.0722],
31478
- [0.0193, 0.1192, 0.9505]
31566
+ [0.4123908, 0.3575843, 0.1804808],
31567
+ [0.212639, 0.7151687, 0.0721923],
31568
+ [0.0193308, 0.1191948, 0.9505322]
31479
31569
  ];
31480
31570
  var XYZ_TO_RGB = invert3(RGB_TO_XYZ);
31481
31571
  var WHITE = RGB_TO_XYZ.map((row2) => row2[0] + row2[1] + row2[2]);
@@ -31503,13 +31593,13 @@ function adjustChroma(r, g, b, scale, angle) {
31503
31593
  const z = RGB_TO_XYZ[2][0] * r + RGB_TO_XYZ[2][1] * g + RGB_TO_XYZ[2][2] * b;
31504
31594
  const d = x + 15 * y + 3 * z;
31505
31595
  if (d <= 1e-9 || y <= 1e-9) {
31506
- return [r, g, b];
31596
+ return [clamp255(r), clamp255(g), clamp255(b)];
31507
31597
  }
31508
31598
  const du = 4 * x / d - WHITE_U;
31509
31599
  const dv = 9 * y / d - WHITE_V;
31510
31600
  const cos = Math.cos(angle) * scale;
31511
31601
  const sin = Math.sin(angle) * scale;
31512
- const u = WHITE_U + du * cos - dv * sin;
31602
+ const u = Math.max(0, WHITE_U + du * cos - dv * sin);
31513
31603
  const v = Math.max(1e-6, WHITE_V + du * sin + dv * cos);
31514
31604
  const x2 = y * 9 * u / (4 * v);
31515
31605
  const z2 = y * (12 - 3 * u - 20 * v) / (4 * v);
@@ -31528,6 +31618,7 @@ function adjustChroma(r, g, b, scale, angle) {
31528
31618
  }
31529
31619
  function colorAdjustmentMapper(ca) {
31530
31620
  const illuminant = illuminantCube(ca.illuminant);
31621
+ const matrix = ILLUMINANT_MATRICES[ca.illuminant];
31531
31622
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
31532
31623
  const black = ca.referenceBlack / 1e4 * 255;
31533
31624
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 * 255 - black);
@@ -31556,16 +31647,17 @@ function colorAdjustmentMapper(ca) {
31556
31647
  }
31557
31648
  return (rgb2) => {
31558
31649
  let c = [rgb2 >> 16 & 255, rgb2 >> 8 & 255, rgb2 & 255];
31559
- if (illuminant) {
31560
- const mapped = lookupIlluminant(illuminant, c[0] / 255, c[1] / 255, c[2] / 255);
31561
- c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31562
- }
31563
31650
  if (chroma) {
31564
- if (!illuminant) {
31565
- c = c.map(exactPaletteLevel);
31651
+ c = c.map(exactPaletteLevel);
31652
+ if (matrix) {
31653
+ const [r, g, b] = c;
31654
+ c = matrix.map((row2) => row2[0] * r + row2[1] * g + row2[2] * b);
31566
31655
  }
31567
- c = adjustChroma(clamp255(c[0]), clamp255(c[1]), clamp255(c[2]), scale, angle);
31656
+ c = adjustChroma(c[0], c[1], c[2], scale, angle);
31568
31657
  c = c.map(roundHalfUp);
31658
+ } else if (illuminant) {
31659
+ const mapped = lookupIlluminant(illuminant, c[0] / 255, c[1] / 255, c[2] / 255);
31660
+ c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31569
31661
  }
31570
31662
  return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
31571
31663
  };
@@ -31919,20 +32011,6 @@ function halftoneDespeckle(px, w, h) {
31919
32011
  }
31920
32012
  }
31921
32013
  }
31922
- function halftoneSharpen(px, w, h) {
31923
- const src = px.slice();
31924
- 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];
31925
- for (let y = 0; y < h; y++) {
31926
- for (let x = 0; x < w; x++) {
31927
- for (let c = 0; c < 3; c++) {
31928
- const v = at(x, y, c);
31929
- const sum = at(x - 1, y, c) + at(x + 1, y, c) + at(x, y - 1, c) + at(x, y + 1, c);
31930
- const out = v + Math.floor((4 * v - sum) / 8);
31931
- px[(y * w + x) * 3 + c] = out < 0 ? 0 : out > 255 ? 255 : out;
31932
- }
31933
- }
31934
- }
31935
- }
31936
32014
  function halftonePairDespeckle(px, w, h, rows) {
31937
32015
  const n = rows ? w : h;
31938
32016
  const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
@@ -31955,6 +32033,46 @@ function halftoneKeepLast(px, w, h, rows) {
31955
32033
  }
31956
32034
  return out;
31957
32035
  }
32036
+ function halftoneReduceBoth(rect, sw, sh, dw, dh, flipX, flipY) {
32037
+ const cols = halftoneReduceTaps(sw, dw, flipX);
32038
+ const rows = halftoneReduceTaps(sh, dh, flipY);
32039
+ const wide = new Int32Array(dw * sh * 3);
32040
+ for (let y = 0; y < sh; y++) {
32041
+ for (let x = 0; x < dw; x++) {
32042
+ let total = 0;
32043
+ for (const [, w] of cols[x]) total += w;
32044
+ for (let c = 0; c < 3; c++) {
32045
+ let sum = 0;
32046
+ for (const [xx, w] of cols[x]) sum += rect[(y * sw + xx) * 3 + c] * w;
32047
+ wide[(y * dw + x) * 3 + c] = Math.floor((2 * sum + total) / (2 * total));
32048
+ }
32049
+ }
32050
+ }
32051
+ const avg = new Float64Array(dw * dh * 3);
32052
+ for (let y = 0; y < dh; y++) {
32053
+ let total = 0;
32054
+ for (const [, w] of rows[y]) total += w;
32055
+ for (let x = 0; x < dw; x++) {
32056
+ for (let c = 0; c < 3; c++) {
32057
+ let sum = 0;
32058
+ for (const [yy, w] of rows[y]) sum += wide[(yy * dw + x) * 3 + c] * w;
32059
+ avg[(y * dw + x) * 3 + c] = sum / total;
32060
+ }
32061
+ }
32062
+ }
32063
+ 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];
32064
+ const out = new Int32Array(dw * dh * 3);
32065
+ for (let y = 0; y < dh; y++) {
32066
+ for (let x = 0; x < dw; x++) {
32067
+ for (let c = 0; c < 3; c++) {
32068
+ const v = at(x, y, c);
32069
+ const sum = at(x - 1, y, c) + at(x + 1, y, c) + at(x, y - 1, c) + at(x, y + 1, c);
32070
+ out[(y * dw + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (4 * v - sum) / 8)));
32071
+ }
32072
+ }
32073
+ }
32074
+ return out;
32075
+ }
31958
32076
  function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
31959
32077
  const W = Math.max(0, Math.round(Math.abs(dw)));
31960
32078
  const H = Math.max(0, Math.round(Math.abs(dh)));
@@ -32006,11 +32124,18 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
32006
32124
  }
32007
32125
  const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
32008
32126
  const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
32127
+ const extendRow = ditherAtSource && nativeMixed && W < SW && H > SH && !flipY;
32128
+ if (extendRow) {
32129
+ const bigger = new Int32Array((SH + 1) * SW * 3);
32130
+ bigger.set(rect);
32131
+ bigger.copyWithin(SH * SW * 3, (SH - 1) * SW * 3, SH * SW * 3);
32132
+ rect = bigger;
32133
+ }
32009
32134
  if (ditherAtSource) {
32010
32135
  ditherPixels(
32011
32136
  rect,
32012
32137
  SW,
32013
- SH,
32138
+ extendRow ? SH + 1 : SH,
32014
32139
  dither,
32015
32140
  false,
32016
32141
  false,
@@ -32050,7 +32175,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
32050
32175
  linear(rows, SH, H, flipY);
32051
32176
  }
32052
32177
  let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
32053
- for (let y = 0; y < sampleH; y++) {
32178
+ if (reducing) {
32179
+ out = halftoneReduceBoth(rect, SW, SH, W, H, flipX, flipY);
32180
+ }
32181
+ for (let y = 0; !reducing && y < sampleH; y++) {
32054
32182
  const ys = rows[y];
32055
32183
  for (let x = 0; x < sampleW; x++) {
32056
32184
  const xs = cols[x];
@@ -32075,15 +32203,38 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
32075
32203
  out[o + 2] = round(b);
32076
32204
  }
32077
32205
  }
32078
- if (reducing) {
32079
- halftoneSharpen(out, W, H);
32080
- }
32081
32206
  if (nativeMixed) {
32082
32207
  const reduceHorizontal = W < SW;
32208
+ let extension;
32209
+ if (extendRow) {
32210
+ const reduced = new Float64Array(sampleW * 3);
32211
+ for (let x = 0; x < sampleW; x++) {
32212
+ let total = 0;
32213
+ const sums = [0, 0, 0];
32214
+ for (const [xx, wx] of cols[x]) {
32215
+ total += wx;
32216
+ for (let c = 0; c < 3; c++) sums[c] += rect[(SH * SW + xx) * 3 + c] * wx;
32217
+ }
32218
+ for (let c = 0; c < 3; c++) reduced[x * 3 + c] = sums[c] / total;
32219
+ }
32220
+ extension = reduced;
32221
+ }
32083
32222
  for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
32084
32223
  const source2 = out;
32085
32224
  out = new Int32Array(source2.length);
32086
- const at = (x, y, c) => source2[(Math.max(0, Math.min(sampleH - 1, y)) * sampleW + Math.max(0, Math.min(sampleW - 1, x))) * 3 + c];
32225
+ const ext = extension;
32226
+ 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];
32227
+ if (horizontal && ext) {
32228
+ const next = new Int32Array(sampleW * 3);
32229
+ for (let x = 0; x < sampleW; x++) {
32230
+ for (let c = 0; c < 3; c++) {
32231
+ const v = ext[x * 3 + c];
32232
+ const sum = at(x - 1, sampleH, c) + at(x + 1, sampleH, c);
32233
+ next[x * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
32234
+ }
32235
+ }
32236
+ extension = next;
32237
+ }
32087
32238
  for (let y = 0; y < sampleH; y++) {
32088
32239
  for (let x = 0; x < sampleW; x++) {
32089
32240
  for (let c = 0; c < 3; c++) {