emf-converter 4.4.13 → 4.4.15

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/browser.js CHANGED
@@ -28141,7 +28141,7 @@ function drawVertices(pen, dx, dy) {
28141
28141
  function halfPixel(v) {
28142
28142
  return Math.sign(v) * 8 * Math.floor((Math.abs(v) + 4) / 8);
28143
28143
  }
28144
- function flatVector(width, dx0, dy0) {
28144
+ function perpendicularVectors(width, dx0, dy0) {
28145
28145
  let dx = dx0;
28146
28146
  let dy = dy0;
28147
28147
  const flip = dx < 0 || dx === 0 && dy < 0;
@@ -28175,9 +28175,9 @@ function flatVector(width, dx0, dy0) {
28175
28175
  const ox = off ? sgn * off[0] : 0;
28176
28176
  const oy = off ? sgn * off[1] : 0;
28177
28177
  const r = (v) => 8 * Math.floor((v + 4) / 8);
28178
- const vx = r(D[0] + (B[0] - D[0]) * w + 0.5 * Math.sign(dy) + ox);
28179
- const vy = r(D[1] + (B[1] - D[1]) * w + 0.5 * Math.sign(dx) + oy);
28180
- return flip ? [-vx, -vy] : [vx, vy];
28178
+ const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28179
+ const vx = r(x + 0.5 * Math.sign(dy) + ox), vy = r(y + 0.5 * Math.sign(dx) + oy);
28180
+ return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28181
28181
  }
28182
28182
  function squareExtension(width, dx, dy) {
28183
28183
  const len = Math.hypot(dx, dy);
@@ -28220,7 +28220,8 @@ var Outliner = class {
28220
28220
  const d = drawDir ?? [dx, dy];
28221
28221
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28222
28222
  const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28223
- return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28223
+ const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28224
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28224
28225
  }
28225
28226
  push(p, v) {
28226
28227
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28316,7 +28317,7 @@ var Outliner = class {
28316
28317
  if (this.rr) {
28317
28318
  this.walk(p, s[from], s[to], false, false);
28318
28319
  } else {
28319
- const A = this.capSide(s, from);
28320
+ const A = from === "R" ? s.vRaw : [-s.vRaw[0], -s.vRaw[1]];
28320
28321
  this.wedge(p, A, [-A[0], -A[1]], true, false);
28321
28322
  }
28322
28323
  this.push(p, this.capSide(s, to));
@@ -28343,6 +28344,8 @@ var Outliner = class {
28343
28344
  const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28344
28345
  let sa = this.joinSide(a, side);
28345
28346
  let sb = this.joinSide(b, side);
28347
+ const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
28348
+ const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
28346
28349
  if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28347
28350
  sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28348
28351
  sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
@@ -28363,10 +28366,10 @@ var Outliner = class {
28363
28366
  this.walk(p, Da, Db, false, incl);
28364
28367
  } else {
28365
28368
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
28366
- this.wedge(p, sa, sb, !anti, !anti);
28369
+ this.wedge(p, rayA, rayB, !anti, !anti);
28367
28370
  }
28368
28371
  } else if (join === "miter") {
28369
- const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit);
28372
+ const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
28370
28373
  if (m) {
28371
28374
  this.push(p, m);
28372
28375
  }
@@ -28380,7 +28383,7 @@ var Outliner = class {
28380
28383
  }
28381
28384
  if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28382
28385
  this.push(p, sb);
28383
- this.wedge(p, sb, sa, false, false);
28386
+ this.wedge(p, rayB, rayA, false, false);
28384
28387
  this.push(p, sa);
28385
28388
  this.pts.push([p[0], p[1]]);
28386
28389
  }
@@ -28599,7 +28602,7 @@ function widenPath(path, opts) {
28599
28602
  }
28600
28603
  return out;
28601
28604
  }
28602
- function miterPoint(sa, da, sb, db, width, limit) {
28605
+ function miterPoint(sa, da, sb, db, width, limit, side) {
28603
28606
  const den = da[0] * db[1] - da[1] * db[0];
28604
28607
  if (den === 0) {
28605
28608
  return null;
@@ -28609,10 +28612,13 @@ function miterPoint(sa, da, sb, db, width, limit) {
28609
28612
  const t = (wx * db[1] - wy * db[0]) / den;
28610
28613
  const mx = sa[0] + da[0] * t;
28611
28614
  const my = sa[1] + da[1] * t;
28612
- if (Math.hypot(mx, my) > limit * width / 2) {
28615
+ const x = Math.sign(mx) * Math.floor(Math.abs(mx) + 0.5);
28616
+ const y = Math.sign(my) * Math.floor(Math.abs(my) + 0.5);
28617
+ const pixel = side === "L" ? Math.ceil : Math.floor;
28618
+ if (Math.hypot(pixel(x / 16), pixel(y / 16)) > limit * width / 32) {
28613
28619
  return null;
28614
28620
  }
28615
- return [Math.floor(mx + 0.5), Math.floor(my + 0.5)];
28621
+ return [x, y];
28616
28622
  }
28617
28623
 
28618
28624
  // src/emf-gdi-raster-shapes.ts
@@ -30528,11 +30534,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30528
30534
  if (dstLen <= 0 || srcLen <= 0) {
30529
30535
  return taps;
30530
30536
  }
30531
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30537
+ const at = (k) => start + k;
30532
30538
  if (dstLen >= srcLen) {
30533
30539
  for (let i = 0; i < dstLen; i++) {
30534
30540
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30535
30541
  }
30542
+ if (reverse) taps.reverse();
30536
30543
  return taps;
30537
30544
  }
30538
30545
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30548,6 +30555,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30548
30555
  }
30549
30556
  taps.push(run);
30550
30557
  }
30558
+ if (reverse) taps.reverse();
30551
30559
  return taps;
30552
30560
  }
30553
30561
  var SPECKLE_OVERSHOOT = 4;
@@ -30563,8 +30571,22 @@ function halftoneDespeckle(px, w, h) {
30563
30571
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30564
30572
  let hClip = 0;
30565
30573
  let vClip = 0;
30574
+ let alternating = 0;
30566
30575
  for (let c = 0; c < 3; c++) {
30567
30576
  const v = at(x, y, c);
30577
+ const axial = Math.max(
30578
+ Math.abs(v - at(left, y, c)),
30579
+ Math.abs(v - at(right, y, c)),
30580
+ Math.abs(v - at(x, up, c)),
30581
+ Math.abs(v - at(x, down, c))
30582
+ );
30583
+ const diagonal = Math.min(
30584
+ Math.abs(v - at(left, up, c)),
30585
+ Math.abs(v - at(right, up, c)),
30586
+ Math.abs(v - at(left, down, c)),
30587
+ Math.abs(v - at(right, down, c))
30588
+ );
30589
+ if (diagonal < axial) alternating++;
30568
30590
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30569
30591
  hClip++;
30570
30592
  }
@@ -30572,7 +30594,7 @@ function halftoneDespeckle(px, w, h) {
30572
30594
  vClip++;
30573
30595
  }
30574
30596
  }
30575
- if (hClip < 2 || vClip < 2) {
30597
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30576
30598
  continue;
30577
30599
  }
30578
30600
  for (let c = 0; c < 3; c++) {
@@ -30626,15 +30648,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30626
30648
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30627
30649
  const reducing = W < SW && H < SH;
30628
30650
  const mixed = W > SW && H < SH || W < SW && H > SH;
30629
- if (enlarging && !directDib) {
30651
+ const nearestMixed = mixed && (!adjust || adjustAfterSampling) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
30652
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W === SW * 2 && H * 2 <= SH || H === SH * 2 && W * 2 <= SW);
30653
+ if ((enlarging || nearestMixed) && !directDib) {
30630
30654
  halftoneDespeckle(rect, SW, SH);
30631
30655
  }
30632
30656
  if (adjust && !adjustAfterSampling) {
30633
30657
  adjust(rect);
30634
30658
  }
30635
- const cols = halftoneAxis(0, SW, W, flipX);
30636
- const rows = halftoneAxis(0, SH, H, flipY);
30637
- if (mixed) {
30659
+ const sampleW = nativeMixed ? Math.min(SW, W) : W;
30660
+ const sampleH = nativeMixed ? Math.min(SH, H) : H;
30661
+ const cols = halftoneAxis(0, SW, sampleW, flipX);
30662
+ const rows = halftoneAxis(0, SH, sampleH, flipY);
30663
+ if (nearestMixed) {
30664
+ const nearest = (taps, src2, dst, reverse) => {
30665
+ for (let i = 0; i < dst; i++) {
30666
+ const index = reverse ? dst - 1 - i : i;
30667
+ const k = dst < src2 ? Math.floor(((index + 1) * 2 * src2 - dst) / (2 * dst)) : halftoneNearest(index, src2, dst);
30668
+ taps[i] = [[Math.max(0, Math.min(src2 - 1, k)), 1]];
30669
+ }
30670
+ };
30671
+ nearest(cols, SW, W, flipX);
30672
+ nearest(rows, SH, H, flipY);
30673
+ } else if (mixed && !nativeMixed) {
30638
30674
  const linear = (taps, src2, dst, reverse) => {
30639
30675
  if (dst <= src2) return;
30640
30676
  for (let i = 0; i < dst; i++) {
@@ -30647,10 +30683,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30647
30683
  linear(cols, SW, W, flipX);
30648
30684
  linear(rows, SH, H, flipY);
30649
30685
  }
30650
- const out = new Int32Array(W * H * 3);
30651
- for (let y = 0; y < H; y++) {
30686
+ let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
30687
+ for (let y = 0; y < sampleH; y++) {
30652
30688
  const ys = rows[y];
30653
- for (let x = 0; x < W; x++) {
30689
+ for (let x = 0; x < sampleW; x++) {
30654
30690
  const xs = cols[x];
30655
30691
  let r = 0;
30656
30692
  let g = 0;
@@ -30666,16 +30702,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30666
30702
  b += rect[i + 2] * w;
30667
30703
  }
30668
30704
  }
30669
- const o = (y * W + x) * 3;
30670
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30671
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30672
- out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30705
+ const o = (y * sampleW + x) * 3;
30706
+ const round = (value) => nativeMixed ? value / total : mixed ? Math.ceil(value / total - 0.5) : Math.floor((2 * value + total) / (2 * total));
30707
+ out[o] = round(r);
30708
+ out[o + 1] = round(g);
30709
+ out[o + 2] = round(b);
30673
30710
  }
30674
30711
  }
30675
30712
  if (reducing) {
30676
30713
  halftoneSharpen(out, W, H);
30677
30714
  }
30678
- if (mixed) {
30715
+ if (nativeMixed) {
30716
+ const reduceHorizontal = W < SW;
30717
+ for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
30718
+ const source2 = out;
30719
+ out = new Int32Array(source2.length);
30720
+ 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];
30721
+ for (let y = 0; y < sampleH; y++) {
30722
+ for (let x = 0; x < sampleW; x++) {
30723
+ for (let c = 0; c < 3; c++) {
30724
+ const v = at(x, y, c);
30725
+ const sum = horizontal ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30726
+ out[(y * sampleW + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
30727
+ }
30728
+ }
30729
+ }
30730
+ }
30731
+ const source = out;
30732
+ out = new Int32Array(W * H * 3);
30733
+ for (let y = 0; y < H; y++) {
30734
+ const py = Math.max(0, Math.min(sampleH - 1, (y + 0.5) * sampleH / H - 0.5));
30735
+ const y02 = Math.floor(py), fy = py - y02;
30736
+ for (let x = 0; x < W; x++) {
30737
+ const px = Math.max(0, Math.min(sampleW - 1, (x + 0.5) * sampleW / W - 0.5));
30738
+ const x02 = Math.floor(px), fx = px - x02;
30739
+ for (let c = 0; c < 3; c++) {
30740
+ const at = (xx, yy) => source[(yy * sampleW + xx) * 3 + c];
30741
+ const x1 = Math.min(sampleW - 1, x02 + 1), y1 = Math.min(sampleH - 1, y02 + 1);
30742
+ const top = at(x02, y02) * (1 - fx) + at(x1, y02) * fx;
30743
+ const bottom = at(x02, y1) * (1 - fx) + at(x1, y1) * fx;
30744
+ out[(y * W + x) * 3 + c] = Math.floor(top * (1 - fy) + bottom * fy + 0.5);
30745
+ }
30746
+ }
30747
+ }
30748
+ }
30749
+ if (mixed && !nativeMixed && !nearestMixed) {
30679
30750
  const source = out.slice();
30680
30751
  const at = (x, y, c) => source[(Math.max(0, Math.min(H - 1, y)) * W + Math.max(0, Math.min(W - 1, x))) * 3 + c];
30681
30752
  for (let y = 0; y < H; y++) {
package/dist/browser.mjs CHANGED
@@ -28139,7 +28139,7 @@ function drawVertices(pen, dx, dy) {
28139
28139
  function halfPixel(v) {
28140
28140
  return Math.sign(v) * 8 * Math.floor((Math.abs(v) + 4) / 8);
28141
28141
  }
28142
- function flatVector(width, dx0, dy0) {
28142
+ function perpendicularVectors(width, dx0, dy0) {
28143
28143
  let dx = dx0;
28144
28144
  let dy = dy0;
28145
28145
  const flip = dx < 0 || dx === 0 && dy < 0;
@@ -28173,9 +28173,9 @@ function flatVector(width, dx0, dy0) {
28173
28173
  const ox = off ? sgn * off[0] : 0;
28174
28174
  const oy = off ? sgn * off[1] : 0;
28175
28175
  const r = (v) => 8 * Math.floor((v + 4) / 8);
28176
- const vx = r(D[0] + (B[0] - D[0]) * w + 0.5 * Math.sign(dy) + ox);
28177
- const vy = r(D[1] + (B[1] - D[1]) * w + 0.5 * Math.sign(dx) + oy);
28178
- return flip ? [-vx, -vy] : [vx, vy];
28176
+ const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28177
+ const vx = r(x + 0.5 * Math.sign(dy) + ox), vy = r(y + 0.5 * Math.sign(dx) + oy);
28178
+ return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28179
28179
  }
28180
28180
  function squareExtension(width, dx, dy) {
28181
28181
  const len = Math.hypot(dx, dy);
@@ -28218,7 +28218,8 @@ var Outliner = class {
28218
28218
  const d = drawDir ?? [dx, dy];
28219
28219
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28220
28220
  const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28221
- return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28221
+ const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28222
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28222
28223
  }
28223
28224
  push(p, v) {
28224
28225
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28314,7 +28315,7 @@ var Outliner = class {
28314
28315
  if (this.rr) {
28315
28316
  this.walk(p, s[from], s[to], false, false);
28316
28317
  } else {
28317
- const A = this.capSide(s, from);
28318
+ const A = from === "R" ? s.vRaw : [-s.vRaw[0], -s.vRaw[1]];
28318
28319
  this.wedge(p, A, [-A[0], -A[1]], true, false);
28319
28320
  }
28320
28321
  this.push(p, this.capSide(s, to));
@@ -28341,6 +28342,8 @@ var Outliner = class {
28341
28342
  const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28342
28343
  let sa = this.joinSide(a, side);
28343
28344
  let sb = this.joinSide(b, side);
28345
+ const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
28346
+ const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
28344
28347
  if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28345
28348
  sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28346
28349
  sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
@@ -28361,10 +28364,10 @@ var Outliner = class {
28361
28364
  this.walk(p, Da, Db, false, incl);
28362
28365
  } else {
28363
28366
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
28364
- this.wedge(p, sa, sb, !anti, !anti);
28367
+ this.wedge(p, rayA, rayB, !anti, !anti);
28365
28368
  }
28366
28369
  } else if (join === "miter") {
28367
- const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit);
28370
+ const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
28368
28371
  if (m) {
28369
28372
  this.push(p, m);
28370
28373
  }
@@ -28378,7 +28381,7 @@ var Outliner = class {
28378
28381
  }
28379
28382
  if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28380
28383
  this.push(p, sb);
28381
- this.wedge(p, sb, sa, false, false);
28384
+ this.wedge(p, rayB, rayA, false, false);
28382
28385
  this.push(p, sa);
28383
28386
  this.pts.push([p[0], p[1]]);
28384
28387
  }
@@ -28597,7 +28600,7 @@ function widenPath(path, opts) {
28597
28600
  }
28598
28601
  return out;
28599
28602
  }
28600
- function miterPoint(sa, da, sb, db, width, limit) {
28603
+ function miterPoint(sa, da, sb, db, width, limit, side) {
28601
28604
  const den = da[0] * db[1] - da[1] * db[0];
28602
28605
  if (den === 0) {
28603
28606
  return null;
@@ -28607,10 +28610,13 @@ function miterPoint(sa, da, sb, db, width, limit) {
28607
28610
  const t = (wx * db[1] - wy * db[0]) / den;
28608
28611
  const mx = sa[0] + da[0] * t;
28609
28612
  const my = sa[1] + da[1] * t;
28610
- if (Math.hypot(mx, my) > limit * width / 2) {
28613
+ const x = Math.sign(mx) * Math.floor(Math.abs(mx) + 0.5);
28614
+ const y = Math.sign(my) * Math.floor(Math.abs(my) + 0.5);
28615
+ const pixel = side === "L" ? Math.ceil : Math.floor;
28616
+ if (Math.hypot(pixel(x / 16), pixel(y / 16)) > limit * width / 32) {
28611
28617
  return null;
28612
28618
  }
28613
- return [Math.floor(mx + 0.5), Math.floor(my + 0.5)];
28619
+ return [x, y];
28614
28620
  }
28615
28621
 
28616
28622
  // src/emf-gdi-raster-shapes.ts
@@ -30526,11 +30532,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30526
30532
  if (dstLen <= 0 || srcLen <= 0) {
30527
30533
  return taps;
30528
30534
  }
30529
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30535
+ const at = (k) => start + k;
30530
30536
  if (dstLen >= srcLen) {
30531
30537
  for (let i = 0; i < dstLen; i++) {
30532
30538
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30533
30539
  }
30540
+ if (reverse) taps.reverse();
30534
30541
  return taps;
30535
30542
  }
30536
30543
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30546,6 +30553,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30546
30553
  }
30547
30554
  taps.push(run);
30548
30555
  }
30556
+ if (reverse) taps.reverse();
30549
30557
  return taps;
30550
30558
  }
30551
30559
  var SPECKLE_OVERSHOOT = 4;
@@ -30561,8 +30569,22 @@ function halftoneDespeckle(px, w, h) {
30561
30569
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30562
30570
  let hClip = 0;
30563
30571
  let vClip = 0;
30572
+ let alternating = 0;
30564
30573
  for (let c = 0; c < 3; c++) {
30565
30574
  const v = at(x, y, c);
30575
+ const axial = Math.max(
30576
+ Math.abs(v - at(left, y, c)),
30577
+ Math.abs(v - at(right, y, c)),
30578
+ Math.abs(v - at(x, up, c)),
30579
+ Math.abs(v - at(x, down, c))
30580
+ );
30581
+ const diagonal = Math.min(
30582
+ Math.abs(v - at(left, up, c)),
30583
+ Math.abs(v - at(right, up, c)),
30584
+ Math.abs(v - at(left, down, c)),
30585
+ Math.abs(v - at(right, down, c))
30586
+ );
30587
+ if (diagonal < axial) alternating++;
30566
30588
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30567
30589
  hClip++;
30568
30590
  }
@@ -30570,7 +30592,7 @@ function halftoneDespeckle(px, w, h) {
30570
30592
  vClip++;
30571
30593
  }
30572
30594
  }
30573
- if (hClip < 2 || vClip < 2) {
30595
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30574
30596
  continue;
30575
30597
  }
30576
30598
  for (let c = 0; c < 3; c++) {
@@ -30624,15 +30646,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30624
30646
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30625
30647
  const reducing = W < SW && H < SH;
30626
30648
  const mixed = W > SW && H < SH || W < SW && H > SH;
30627
- if (enlarging && !directDib) {
30649
+ const nearestMixed = mixed && (!adjust || adjustAfterSampling) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
30650
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W === SW * 2 && H * 2 <= SH || H === SH * 2 && W * 2 <= SW);
30651
+ if ((enlarging || nearestMixed) && !directDib) {
30628
30652
  halftoneDespeckle(rect, SW, SH);
30629
30653
  }
30630
30654
  if (adjust && !adjustAfterSampling) {
30631
30655
  adjust(rect);
30632
30656
  }
30633
- const cols = halftoneAxis(0, SW, W, flipX);
30634
- const rows = halftoneAxis(0, SH, H, flipY);
30635
- if (mixed) {
30657
+ const sampleW = nativeMixed ? Math.min(SW, W) : W;
30658
+ const sampleH = nativeMixed ? Math.min(SH, H) : H;
30659
+ const cols = halftoneAxis(0, SW, sampleW, flipX);
30660
+ const rows = halftoneAxis(0, SH, sampleH, flipY);
30661
+ if (nearestMixed) {
30662
+ const nearest = (taps, src2, dst, reverse) => {
30663
+ for (let i = 0; i < dst; i++) {
30664
+ const index = reverse ? dst - 1 - i : i;
30665
+ const k = dst < src2 ? Math.floor(((index + 1) * 2 * src2 - dst) / (2 * dst)) : halftoneNearest(index, src2, dst);
30666
+ taps[i] = [[Math.max(0, Math.min(src2 - 1, k)), 1]];
30667
+ }
30668
+ };
30669
+ nearest(cols, SW, W, flipX);
30670
+ nearest(rows, SH, H, flipY);
30671
+ } else if (mixed && !nativeMixed) {
30636
30672
  const linear = (taps, src2, dst, reverse) => {
30637
30673
  if (dst <= src2) return;
30638
30674
  for (let i = 0; i < dst; i++) {
@@ -30645,10 +30681,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30645
30681
  linear(cols, SW, W, flipX);
30646
30682
  linear(rows, SH, H, flipY);
30647
30683
  }
30648
- const out = new Int32Array(W * H * 3);
30649
- for (let y = 0; y < H; y++) {
30684
+ let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
30685
+ for (let y = 0; y < sampleH; y++) {
30650
30686
  const ys = rows[y];
30651
- for (let x = 0; x < W; x++) {
30687
+ for (let x = 0; x < sampleW; x++) {
30652
30688
  const xs = cols[x];
30653
30689
  let r = 0;
30654
30690
  let g = 0;
@@ -30664,16 +30700,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30664
30700
  b += rect[i + 2] * w;
30665
30701
  }
30666
30702
  }
30667
- const o = (y * W + x) * 3;
30668
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30669
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30670
- out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30703
+ const o = (y * sampleW + x) * 3;
30704
+ const round = (value) => nativeMixed ? value / total : mixed ? Math.ceil(value / total - 0.5) : Math.floor((2 * value + total) / (2 * total));
30705
+ out[o] = round(r);
30706
+ out[o + 1] = round(g);
30707
+ out[o + 2] = round(b);
30671
30708
  }
30672
30709
  }
30673
30710
  if (reducing) {
30674
30711
  halftoneSharpen(out, W, H);
30675
30712
  }
30676
- if (mixed) {
30713
+ if (nativeMixed) {
30714
+ const reduceHorizontal = W < SW;
30715
+ for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
30716
+ const source2 = out;
30717
+ out = new Int32Array(source2.length);
30718
+ 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];
30719
+ for (let y = 0; y < sampleH; y++) {
30720
+ for (let x = 0; x < sampleW; x++) {
30721
+ for (let c = 0; c < 3; c++) {
30722
+ const v = at(x, y, c);
30723
+ const sum = horizontal ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30724
+ out[(y * sampleW + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
30725
+ }
30726
+ }
30727
+ }
30728
+ }
30729
+ const source = out;
30730
+ out = new Int32Array(W * H * 3);
30731
+ for (let y = 0; y < H; y++) {
30732
+ const py = Math.max(0, Math.min(sampleH - 1, (y + 0.5) * sampleH / H - 0.5));
30733
+ const y02 = Math.floor(py), fy = py - y02;
30734
+ for (let x = 0; x < W; x++) {
30735
+ const px = Math.max(0, Math.min(sampleW - 1, (x + 0.5) * sampleW / W - 0.5));
30736
+ const x02 = Math.floor(px), fx = px - x02;
30737
+ for (let c = 0; c < 3; c++) {
30738
+ const at = (xx, yy) => source[(yy * sampleW + xx) * 3 + c];
30739
+ const x1 = Math.min(sampleW - 1, x02 + 1), y1 = Math.min(sampleH - 1, y02 + 1);
30740
+ const top = at(x02, y02) * (1 - fx) + at(x1, y02) * fx;
30741
+ const bottom = at(x02, y1) * (1 - fx) + at(x1, y1) * fx;
30742
+ out[(y * W + x) * 3 + c] = Math.floor(top * (1 - fy) + bottom * fy + 0.5);
30743
+ }
30744
+ }
30745
+ }
30746
+ }
30747
+ if (mixed && !nativeMixed && !nearestMixed) {
30677
30748
  const source = out.slice();
30678
30749
  const at = (x, y, c) => source[(Math.max(0, Math.min(H - 1, y)) * W + Math.max(0, Math.min(W - 1, x))) * 3 + c];
30679
30750
  for (let y = 0; y < H; y++) {
package/dist/index.js CHANGED
@@ -28142,7 +28142,7 @@ function drawVertices(pen, dx, dy) {
28142
28142
  function halfPixel(v) {
28143
28143
  return Math.sign(v) * 8 * Math.floor((Math.abs(v) + 4) / 8);
28144
28144
  }
28145
- function flatVector(width, dx0, dy0) {
28145
+ function perpendicularVectors(width, dx0, dy0) {
28146
28146
  let dx = dx0;
28147
28147
  let dy = dy0;
28148
28148
  const flip = dx < 0 || dx === 0 && dy < 0;
@@ -28176,9 +28176,9 @@ function flatVector(width, dx0, dy0) {
28176
28176
  const ox = off ? sgn * off[0] : 0;
28177
28177
  const oy = off ? sgn * off[1] : 0;
28178
28178
  const r = (v) => 8 * Math.floor((v + 4) / 8);
28179
- const vx = r(D[0] + (B[0] - D[0]) * w + 0.5 * Math.sign(dy) + ox);
28180
- const vy = r(D[1] + (B[1] - D[1]) * w + 0.5 * Math.sign(dx) + oy);
28181
- return flip ? [-vx, -vy] : [vx, vy];
28179
+ const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28180
+ const vx = r(x + 0.5 * Math.sign(dy) + ox), vy = r(y + 0.5 * Math.sign(dx) + oy);
28181
+ return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28182
28182
  }
28183
28183
  function squareExtension(width, dx, dy) {
28184
28184
  const len = Math.hypot(dx, dy);
@@ -28221,7 +28221,8 @@ var Outliner = class {
28221
28221
  const d = drawDir ?? [dx, dy];
28222
28222
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28223
28223
  const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28224
- return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28224
+ const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28225
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28225
28226
  }
28226
28227
  push(p, v) {
28227
28228
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28317,7 +28318,7 @@ var Outliner = class {
28317
28318
  if (this.rr) {
28318
28319
  this.walk(p, s[from], s[to], false, false);
28319
28320
  } else {
28320
- const A = this.capSide(s, from);
28321
+ const A = from === "R" ? s.vRaw : [-s.vRaw[0], -s.vRaw[1]];
28321
28322
  this.wedge(p, A, [-A[0], -A[1]], true, false);
28322
28323
  }
28323
28324
  this.push(p, this.capSide(s, to));
@@ -28344,6 +28345,8 @@ var Outliner = class {
28344
28345
  const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28345
28346
  let sa = this.joinSide(a, side);
28346
28347
  let sb = this.joinSide(b, side);
28348
+ const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
28349
+ const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
28347
28350
  if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28348
28351
  sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28349
28352
  sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
@@ -28364,10 +28367,10 @@ var Outliner = class {
28364
28367
  this.walk(p, Da, Db, false, incl);
28365
28368
  } else {
28366
28369
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
28367
- this.wedge(p, sa, sb, !anti, !anti);
28370
+ this.wedge(p, rayA, rayB, !anti, !anti);
28368
28371
  }
28369
28372
  } else if (join === "miter") {
28370
- const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit);
28373
+ const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
28371
28374
  if (m) {
28372
28375
  this.push(p, m);
28373
28376
  }
@@ -28381,7 +28384,7 @@ var Outliner = class {
28381
28384
  }
28382
28385
  if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28383
28386
  this.push(p, sb);
28384
- this.wedge(p, sb, sa, false, false);
28387
+ this.wedge(p, rayB, rayA, false, false);
28385
28388
  this.push(p, sa);
28386
28389
  this.pts.push([p[0], p[1]]);
28387
28390
  }
@@ -28600,7 +28603,7 @@ function widenPath(path, opts) {
28600
28603
  }
28601
28604
  return out;
28602
28605
  }
28603
- function miterPoint(sa, da, sb, db, width, limit) {
28606
+ function miterPoint(sa, da, sb, db, width, limit, side) {
28604
28607
  const den = da[0] * db[1] - da[1] * db[0];
28605
28608
  if (den === 0) {
28606
28609
  return null;
@@ -28610,10 +28613,13 @@ function miterPoint(sa, da, sb, db, width, limit) {
28610
28613
  const t = (wx * db[1] - wy * db[0]) / den;
28611
28614
  const mx = sa[0] + da[0] * t;
28612
28615
  const my = sa[1] + da[1] * t;
28613
- if (Math.hypot(mx, my) > limit * width / 2) {
28616
+ const x = Math.sign(mx) * Math.floor(Math.abs(mx) + 0.5);
28617
+ const y = Math.sign(my) * Math.floor(Math.abs(my) + 0.5);
28618
+ const pixel = side === "L" ? Math.ceil : Math.floor;
28619
+ if (Math.hypot(pixel(x / 16), pixel(y / 16)) > limit * width / 32) {
28614
28620
  return null;
28615
28621
  }
28616
- return [Math.floor(mx + 0.5), Math.floor(my + 0.5)];
28622
+ return [x, y];
28617
28623
  }
28618
28624
 
28619
28625
  // src/emf-gdi-raster-shapes.ts
@@ -30529,11 +30535,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30529
30535
  if (dstLen <= 0 || srcLen <= 0) {
30530
30536
  return taps;
30531
30537
  }
30532
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30538
+ const at = (k) => start + k;
30533
30539
  if (dstLen >= srcLen) {
30534
30540
  for (let i = 0; i < dstLen; i++) {
30535
30541
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30536
30542
  }
30543
+ if (reverse) taps.reverse();
30537
30544
  return taps;
30538
30545
  }
30539
30546
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30549,6 +30556,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30549
30556
  }
30550
30557
  taps.push(run);
30551
30558
  }
30559
+ if (reverse) taps.reverse();
30552
30560
  return taps;
30553
30561
  }
30554
30562
  var SPECKLE_OVERSHOOT = 4;
@@ -30564,8 +30572,22 @@ function halftoneDespeckle(px, w, h) {
30564
30572
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30565
30573
  let hClip = 0;
30566
30574
  let vClip = 0;
30575
+ let alternating = 0;
30567
30576
  for (let c = 0; c < 3; c++) {
30568
30577
  const v = at(x, y, c);
30578
+ const axial = Math.max(
30579
+ Math.abs(v - at(left, y, c)),
30580
+ Math.abs(v - at(right, y, c)),
30581
+ Math.abs(v - at(x, up, c)),
30582
+ Math.abs(v - at(x, down, c))
30583
+ );
30584
+ const diagonal = Math.min(
30585
+ Math.abs(v - at(left, up, c)),
30586
+ Math.abs(v - at(right, up, c)),
30587
+ Math.abs(v - at(left, down, c)),
30588
+ Math.abs(v - at(right, down, c))
30589
+ );
30590
+ if (diagonal < axial) alternating++;
30569
30591
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30570
30592
  hClip++;
30571
30593
  }
@@ -30573,7 +30595,7 @@ function halftoneDespeckle(px, w, h) {
30573
30595
  vClip++;
30574
30596
  }
30575
30597
  }
30576
- if (hClip < 2 || vClip < 2) {
30598
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30577
30599
  continue;
30578
30600
  }
30579
30601
  for (let c = 0; c < 3; c++) {
@@ -30627,15 +30649,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30627
30649
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30628
30650
  const reducing = W < SW && H < SH;
30629
30651
  const mixed = W > SW && H < SH || W < SW && H > SH;
30630
- if (enlarging && !directDib) {
30652
+ const nearestMixed = mixed && (!adjust || adjustAfterSampling) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
30653
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W === SW * 2 && H * 2 <= SH || H === SH * 2 && W * 2 <= SW);
30654
+ if ((enlarging || nearestMixed) && !directDib) {
30631
30655
  halftoneDespeckle(rect, SW, SH);
30632
30656
  }
30633
30657
  if (adjust && !adjustAfterSampling) {
30634
30658
  adjust(rect);
30635
30659
  }
30636
- const cols = halftoneAxis(0, SW, W, flipX);
30637
- const rows = halftoneAxis(0, SH, H, flipY);
30638
- if (mixed) {
30660
+ const sampleW = nativeMixed ? Math.min(SW, W) : W;
30661
+ const sampleH = nativeMixed ? Math.min(SH, H) : H;
30662
+ const cols = halftoneAxis(0, SW, sampleW, flipX);
30663
+ const rows = halftoneAxis(0, SH, sampleH, flipY);
30664
+ if (nearestMixed) {
30665
+ const nearest = (taps, src2, dst, reverse) => {
30666
+ for (let i = 0; i < dst; i++) {
30667
+ const index = reverse ? dst - 1 - i : i;
30668
+ const k = dst < src2 ? Math.floor(((index + 1) * 2 * src2 - dst) / (2 * dst)) : halftoneNearest(index, src2, dst);
30669
+ taps[i] = [[Math.max(0, Math.min(src2 - 1, k)), 1]];
30670
+ }
30671
+ };
30672
+ nearest(cols, SW, W, flipX);
30673
+ nearest(rows, SH, H, flipY);
30674
+ } else if (mixed && !nativeMixed) {
30639
30675
  const linear = (taps, src2, dst, reverse) => {
30640
30676
  if (dst <= src2) return;
30641
30677
  for (let i = 0; i < dst; i++) {
@@ -30648,10 +30684,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30648
30684
  linear(cols, SW, W, flipX);
30649
30685
  linear(rows, SH, H, flipY);
30650
30686
  }
30651
- const out = new Int32Array(W * H * 3);
30652
- for (let y = 0; y < H; y++) {
30687
+ let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
30688
+ for (let y = 0; y < sampleH; y++) {
30653
30689
  const ys = rows[y];
30654
- for (let x = 0; x < W; x++) {
30690
+ for (let x = 0; x < sampleW; x++) {
30655
30691
  const xs = cols[x];
30656
30692
  let r = 0;
30657
30693
  let g = 0;
@@ -30667,16 +30703,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30667
30703
  b += rect[i + 2] * w;
30668
30704
  }
30669
30705
  }
30670
- const o = (y * W + x) * 3;
30671
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30672
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30673
- out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30706
+ const o = (y * sampleW + x) * 3;
30707
+ const round = (value) => nativeMixed ? value / total : mixed ? Math.ceil(value / total - 0.5) : Math.floor((2 * value + total) / (2 * total));
30708
+ out[o] = round(r);
30709
+ out[o + 1] = round(g);
30710
+ out[o + 2] = round(b);
30674
30711
  }
30675
30712
  }
30676
30713
  if (reducing) {
30677
30714
  halftoneSharpen(out, W, H);
30678
30715
  }
30679
- if (mixed) {
30716
+ if (nativeMixed) {
30717
+ const reduceHorizontal = W < SW;
30718
+ for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
30719
+ const source2 = out;
30720
+ out = new Int32Array(source2.length);
30721
+ 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];
30722
+ for (let y = 0; y < sampleH; y++) {
30723
+ for (let x = 0; x < sampleW; x++) {
30724
+ for (let c = 0; c < 3; c++) {
30725
+ const v = at(x, y, c);
30726
+ const sum = horizontal ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30727
+ out[(y * sampleW + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
30728
+ }
30729
+ }
30730
+ }
30731
+ }
30732
+ const source = out;
30733
+ out = new Int32Array(W * H * 3);
30734
+ for (let y = 0; y < H; y++) {
30735
+ const py = Math.max(0, Math.min(sampleH - 1, (y + 0.5) * sampleH / H - 0.5));
30736
+ const y02 = Math.floor(py), fy = py - y02;
30737
+ for (let x = 0; x < W; x++) {
30738
+ const px = Math.max(0, Math.min(sampleW - 1, (x + 0.5) * sampleW / W - 0.5));
30739
+ const x02 = Math.floor(px), fx = px - x02;
30740
+ for (let c = 0; c < 3; c++) {
30741
+ const at = (xx, yy) => source[(yy * sampleW + xx) * 3 + c];
30742
+ const x1 = Math.min(sampleW - 1, x02 + 1), y1 = Math.min(sampleH - 1, y02 + 1);
30743
+ const top = at(x02, y02) * (1 - fx) + at(x1, y02) * fx;
30744
+ const bottom = at(x02, y1) * (1 - fx) + at(x1, y1) * fx;
30745
+ out[(y * W + x) * 3 + c] = Math.floor(top * (1 - fy) + bottom * fy + 0.5);
30746
+ }
30747
+ }
30748
+ }
30749
+ }
30750
+ if (mixed && !nativeMixed && !nearestMixed) {
30680
30751
  const source = out.slice();
30681
30752
  const at = (x, y, c) => source[(Math.max(0, Math.min(H - 1, y)) * W + Math.max(0, Math.min(W - 1, x))) * 3 + c];
30682
30753
  for (let y = 0; y < H; y++) {
package/dist/index.mjs CHANGED
@@ -28140,7 +28140,7 @@ function drawVertices(pen, dx, dy) {
28140
28140
  function halfPixel(v) {
28141
28141
  return Math.sign(v) * 8 * Math.floor((Math.abs(v) + 4) / 8);
28142
28142
  }
28143
- function flatVector(width, dx0, dy0) {
28143
+ function perpendicularVectors(width, dx0, dy0) {
28144
28144
  let dx = dx0;
28145
28145
  let dy = dy0;
28146
28146
  const flip = dx < 0 || dx === 0 && dy < 0;
@@ -28174,9 +28174,9 @@ function flatVector(width, dx0, dy0) {
28174
28174
  const ox = off ? sgn * off[0] : 0;
28175
28175
  const oy = off ? sgn * off[1] : 0;
28176
28176
  const r = (v) => 8 * Math.floor((v + 4) / 8);
28177
- const vx = r(D[0] + (B[0] - D[0]) * w + 0.5 * Math.sign(dy) + ox);
28178
- const vy = r(D[1] + (B[1] - D[1]) * w + 0.5 * Math.sign(dx) + oy);
28179
- return flip ? [-vx, -vy] : [vx, vy];
28177
+ const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28178
+ const vx = r(x + 0.5 * Math.sign(dy) + ox), vy = r(y + 0.5 * Math.sign(dx) + oy);
28179
+ return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28180
28180
  }
28181
28181
  function squareExtension(width, dx, dy) {
28182
28182
  const len = Math.hypot(dx, dy);
@@ -28219,7 +28219,8 @@ var Outliner = class {
28219
28219
  const d = drawDir ?? [dx, dy];
28220
28220
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28221
28221
  const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28222
- return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28222
+ const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28223
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28223
28224
  }
28224
28225
  push(p, v) {
28225
28226
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28315,7 +28316,7 @@ var Outliner = class {
28315
28316
  if (this.rr) {
28316
28317
  this.walk(p, s[from], s[to], false, false);
28317
28318
  } else {
28318
- const A = this.capSide(s, from);
28319
+ const A = from === "R" ? s.vRaw : [-s.vRaw[0], -s.vRaw[1]];
28319
28320
  this.wedge(p, A, [-A[0], -A[1]], true, false);
28320
28321
  }
28321
28322
  this.push(p, this.capSide(s, to));
@@ -28342,6 +28343,8 @@ var Outliner = class {
28342
28343
  const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28343
28344
  let sa = this.joinSide(a, side);
28344
28345
  let sb = this.joinSide(b, side);
28346
+ const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
28347
+ const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
28345
28348
  if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28346
28349
  sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28347
28350
  sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
@@ -28362,10 +28365,10 @@ var Outliner = class {
28362
28365
  this.walk(p, Da, Db, false, incl);
28363
28366
  } else {
28364
28367
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
28365
- this.wedge(p, sa, sb, !anti, !anti);
28368
+ this.wedge(p, rayA, rayB, !anti, !anti);
28366
28369
  }
28367
28370
  } else if (join === "miter") {
28368
- const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit);
28371
+ const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
28369
28372
  if (m) {
28370
28373
  this.push(p, m);
28371
28374
  }
@@ -28379,7 +28382,7 @@ var Outliner = class {
28379
28382
  }
28380
28383
  if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28381
28384
  this.push(p, sb);
28382
- this.wedge(p, sb, sa, false, false);
28385
+ this.wedge(p, rayB, rayA, false, false);
28383
28386
  this.push(p, sa);
28384
28387
  this.pts.push([p[0], p[1]]);
28385
28388
  }
@@ -28598,7 +28601,7 @@ function widenPath(path, opts) {
28598
28601
  }
28599
28602
  return out;
28600
28603
  }
28601
- function miterPoint(sa, da, sb, db, width, limit) {
28604
+ function miterPoint(sa, da, sb, db, width, limit, side) {
28602
28605
  const den = da[0] * db[1] - da[1] * db[0];
28603
28606
  if (den === 0) {
28604
28607
  return null;
@@ -28608,10 +28611,13 @@ function miterPoint(sa, da, sb, db, width, limit) {
28608
28611
  const t = (wx * db[1] - wy * db[0]) / den;
28609
28612
  const mx = sa[0] + da[0] * t;
28610
28613
  const my = sa[1] + da[1] * t;
28611
- if (Math.hypot(mx, my) > limit * width / 2) {
28614
+ const x = Math.sign(mx) * Math.floor(Math.abs(mx) + 0.5);
28615
+ const y = Math.sign(my) * Math.floor(Math.abs(my) + 0.5);
28616
+ const pixel = side === "L" ? Math.ceil : Math.floor;
28617
+ if (Math.hypot(pixel(x / 16), pixel(y / 16)) > limit * width / 32) {
28612
28618
  return null;
28613
28619
  }
28614
- return [Math.floor(mx + 0.5), Math.floor(my + 0.5)];
28620
+ return [x, y];
28615
28621
  }
28616
28622
 
28617
28623
  // src/emf-gdi-raster-shapes.ts
@@ -30527,11 +30533,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30527
30533
  if (dstLen <= 0 || srcLen <= 0) {
30528
30534
  return taps;
30529
30535
  }
30530
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30536
+ const at = (k) => start + k;
30531
30537
  if (dstLen >= srcLen) {
30532
30538
  for (let i = 0; i < dstLen; i++) {
30533
30539
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30534
30540
  }
30541
+ if (reverse) taps.reverse();
30535
30542
  return taps;
30536
30543
  }
30537
30544
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30547,6 +30554,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30547
30554
  }
30548
30555
  taps.push(run);
30549
30556
  }
30557
+ if (reverse) taps.reverse();
30550
30558
  return taps;
30551
30559
  }
30552
30560
  var SPECKLE_OVERSHOOT = 4;
@@ -30562,8 +30570,22 @@ function halftoneDespeckle(px, w, h) {
30562
30570
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30563
30571
  let hClip = 0;
30564
30572
  let vClip = 0;
30573
+ let alternating = 0;
30565
30574
  for (let c = 0; c < 3; c++) {
30566
30575
  const v = at(x, y, c);
30576
+ const axial = Math.max(
30577
+ Math.abs(v - at(left, y, c)),
30578
+ Math.abs(v - at(right, y, c)),
30579
+ Math.abs(v - at(x, up, c)),
30580
+ Math.abs(v - at(x, down, c))
30581
+ );
30582
+ const diagonal = Math.min(
30583
+ Math.abs(v - at(left, up, c)),
30584
+ Math.abs(v - at(right, up, c)),
30585
+ Math.abs(v - at(left, down, c)),
30586
+ Math.abs(v - at(right, down, c))
30587
+ );
30588
+ if (diagonal < axial) alternating++;
30567
30589
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30568
30590
  hClip++;
30569
30591
  }
@@ -30571,7 +30593,7 @@ function halftoneDespeckle(px, w, h) {
30571
30593
  vClip++;
30572
30594
  }
30573
30595
  }
30574
- if (hClip < 2 || vClip < 2) {
30596
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30575
30597
  continue;
30576
30598
  }
30577
30599
  for (let c = 0; c < 3; c++) {
@@ -30625,15 +30647,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30625
30647
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30626
30648
  const reducing = W < SW && H < SH;
30627
30649
  const mixed = W > SW && H < SH || W < SW && H > SH;
30628
- if (enlarging && !directDib) {
30650
+ const nearestMixed = mixed && (!adjust || adjustAfterSampling) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
30651
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W === SW * 2 && H * 2 <= SH || H === SH * 2 && W * 2 <= SW);
30652
+ if ((enlarging || nearestMixed) && !directDib) {
30629
30653
  halftoneDespeckle(rect, SW, SH);
30630
30654
  }
30631
30655
  if (adjust && !adjustAfterSampling) {
30632
30656
  adjust(rect);
30633
30657
  }
30634
- const cols = halftoneAxis(0, SW, W, flipX);
30635
- const rows = halftoneAxis(0, SH, H, flipY);
30636
- if (mixed) {
30658
+ const sampleW = nativeMixed ? Math.min(SW, W) : W;
30659
+ const sampleH = nativeMixed ? Math.min(SH, H) : H;
30660
+ const cols = halftoneAxis(0, SW, sampleW, flipX);
30661
+ const rows = halftoneAxis(0, SH, sampleH, flipY);
30662
+ if (nearestMixed) {
30663
+ const nearest = (taps, src2, dst, reverse) => {
30664
+ for (let i = 0; i < dst; i++) {
30665
+ const index = reverse ? dst - 1 - i : i;
30666
+ const k = dst < src2 ? Math.floor(((index + 1) * 2 * src2 - dst) / (2 * dst)) : halftoneNearest(index, src2, dst);
30667
+ taps[i] = [[Math.max(0, Math.min(src2 - 1, k)), 1]];
30668
+ }
30669
+ };
30670
+ nearest(cols, SW, W, flipX);
30671
+ nearest(rows, SH, H, flipY);
30672
+ } else if (mixed && !nativeMixed) {
30637
30673
  const linear = (taps, src2, dst, reverse) => {
30638
30674
  if (dst <= src2) return;
30639
30675
  for (let i = 0; i < dst; i++) {
@@ -30646,10 +30682,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30646
30682
  linear(cols, SW, W, flipX);
30647
30683
  linear(rows, SH, H, flipY);
30648
30684
  }
30649
- const out = new Int32Array(W * H * 3);
30650
- for (let y = 0; y < H; y++) {
30685
+ let out = nativeMixed ? new Float64Array(sampleW * sampleH * 3) : new Int32Array(W * H * 3);
30686
+ for (let y = 0; y < sampleH; y++) {
30651
30687
  const ys = rows[y];
30652
- for (let x = 0; x < W; x++) {
30688
+ for (let x = 0; x < sampleW; x++) {
30653
30689
  const xs = cols[x];
30654
30690
  let r = 0;
30655
30691
  let g = 0;
@@ -30665,16 +30701,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30665
30701
  b += rect[i + 2] * w;
30666
30702
  }
30667
30703
  }
30668
- const o = (y * W + x) * 3;
30669
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30670
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30671
- out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30704
+ const o = (y * sampleW + x) * 3;
30705
+ const round = (value) => nativeMixed ? value / total : mixed ? Math.ceil(value / total - 0.5) : Math.floor((2 * value + total) / (2 * total));
30706
+ out[o] = round(r);
30707
+ out[o + 1] = round(g);
30708
+ out[o + 2] = round(b);
30672
30709
  }
30673
30710
  }
30674
30711
  if (reducing) {
30675
30712
  halftoneSharpen(out, W, H);
30676
30713
  }
30677
- if (mixed) {
30714
+ if (nativeMixed) {
30715
+ const reduceHorizontal = W < SW;
30716
+ for (const horizontal of [reduceHorizontal, !reduceHorizontal]) {
30717
+ const source2 = out;
30718
+ out = new Int32Array(source2.length);
30719
+ 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];
30720
+ for (let y = 0; y < sampleH; y++) {
30721
+ for (let x = 0; x < sampleW; x++) {
30722
+ for (let c = 0; c < 3; c++) {
30723
+ const v = at(x, y, c);
30724
+ const sum = horizontal ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30725
+ out[(y * sampleW + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + (2 * v - sum) / 4)));
30726
+ }
30727
+ }
30728
+ }
30729
+ }
30730
+ const source = out;
30731
+ out = new Int32Array(W * H * 3);
30732
+ for (let y = 0; y < H; y++) {
30733
+ const py = Math.max(0, Math.min(sampleH - 1, (y + 0.5) * sampleH / H - 0.5));
30734
+ const y02 = Math.floor(py), fy = py - y02;
30735
+ for (let x = 0; x < W; x++) {
30736
+ const px = Math.max(0, Math.min(sampleW - 1, (x + 0.5) * sampleW / W - 0.5));
30737
+ const x02 = Math.floor(px), fx = px - x02;
30738
+ for (let c = 0; c < 3; c++) {
30739
+ const at = (xx, yy) => source[(yy * sampleW + xx) * 3 + c];
30740
+ const x1 = Math.min(sampleW - 1, x02 + 1), y1 = Math.min(sampleH - 1, y02 + 1);
30741
+ const top = at(x02, y02) * (1 - fx) + at(x1, y02) * fx;
30742
+ const bottom = at(x02, y1) * (1 - fx) + at(x1, y1) * fx;
30743
+ out[(y * W + x) * 3 + c] = Math.floor(top * (1 - fy) + bottom * fy + 0.5);
30744
+ }
30745
+ }
30746
+ }
30747
+ }
30748
+ if (mixed && !nativeMixed && !nearestMixed) {
30678
30749
  const source = out.slice();
30679
30750
  const at = (x, y, c) => source[(Math.max(0, Math.min(H - 1, y)) * W + Math.max(0, Math.min(W - 1, x))) * 3 + c];
30680
30751
  for (let y = 0; y < H; y++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.4.13",
3
+ "version": "4.4.15",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",