emf-converter 4.4.14 → 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
@@ -30534,11 +30534,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30534
30534
  if (dstLen <= 0 || srcLen <= 0) {
30535
30535
  return taps;
30536
30536
  }
30537
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30537
+ const at = (k) => start + k;
30538
30538
  if (dstLen >= srcLen) {
30539
30539
  for (let i = 0; i < dstLen; i++) {
30540
30540
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30541
30541
  }
30542
+ if (reverse) taps.reverse();
30542
30543
  return taps;
30543
30544
  }
30544
30545
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30554,6 +30555,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30554
30555
  }
30555
30556
  taps.push(run);
30556
30557
  }
30558
+ if (reverse) taps.reverse();
30557
30559
  return taps;
30558
30560
  }
30559
30561
  var SPECKLE_OVERSHOOT = 4;
@@ -30569,8 +30571,22 @@ function halftoneDespeckle(px, w, h) {
30569
30571
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30570
30572
  let hClip = 0;
30571
30573
  let vClip = 0;
30574
+ let alternating = 0;
30572
30575
  for (let c = 0; c < 3; c++) {
30573
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++;
30574
30590
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30575
30591
  hClip++;
30576
30592
  }
@@ -30578,7 +30594,7 @@ function halftoneDespeckle(px, w, h) {
30578
30594
  vClip++;
30579
30595
  }
30580
30596
  }
30581
- if (hClip < 2 || vClip < 2) {
30597
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30582
30598
  continue;
30583
30599
  }
30584
30600
  for (let c = 0; c < 3; c++) {
@@ -30632,15 +30648,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30632
30648
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30633
30649
  const reducing = W < SW && H < SH;
30634
30650
  const mixed = W > SW && H < SH || W < SW && H > SH;
30635
- 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) {
30636
30654
  halftoneDespeckle(rect, SW, SH);
30637
30655
  }
30638
30656
  if (adjust && !adjustAfterSampling) {
30639
30657
  adjust(rect);
30640
30658
  }
30641
- const cols = halftoneAxis(0, SW, W, flipX);
30642
- const rows = halftoneAxis(0, SH, H, flipY);
30643
- 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) {
30644
30674
  const linear = (taps, src2, dst, reverse) => {
30645
30675
  if (dst <= src2) return;
30646
30676
  for (let i = 0; i < dst; i++) {
@@ -30653,10 +30683,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30653
30683
  linear(cols, SW, W, flipX);
30654
30684
  linear(rows, SH, H, flipY);
30655
30685
  }
30656
- const out = new Int32Array(W * H * 3);
30657
- 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++) {
30658
30688
  const ys = rows[y];
30659
- for (let x = 0; x < W; x++) {
30689
+ for (let x = 0; x < sampleW; x++) {
30660
30690
  const xs = cols[x];
30661
30691
  let r = 0;
30662
30692
  let g = 0;
@@ -30672,16 +30702,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30672
30702
  b += rect[i + 2] * w;
30673
30703
  }
30674
30704
  }
30675
- const o = (y * W + x) * 3;
30676
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30677
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30678
- 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);
30679
30710
  }
30680
30711
  }
30681
30712
  if (reducing) {
30682
30713
  halftoneSharpen(out, W, H);
30683
30714
  }
30684
- 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) {
30685
30750
  const source = out.slice();
30686
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];
30687
30752
  for (let y = 0; y < H; y++) {
package/dist/browser.mjs CHANGED
@@ -30532,11 +30532,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30532
30532
  if (dstLen <= 0 || srcLen <= 0) {
30533
30533
  return taps;
30534
30534
  }
30535
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30535
+ const at = (k) => start + k;
30536
30536
  if (dstLen >= srcLen) {
30537
30537
  for (let i = 0; i < dstLen; i++) {
30538
30538
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30539
30539
  }
30540
+ if (reverse) taps.reverse();
30540
30541
  return taps;
30541
30542
  }
30542
30543
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30552,6 +30553,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30552
30553
  }
30553
30554
  taps.push(run);
30554
30555
  }
30556
+ if (reverse) taps.reverse();
30555
30557
  return taps;
30556
30558
  }
30557
30559
  var SPECKLE_OVERSHOOT = 4;
@@ -30567,8 +30569,22 @@ function halftoneDespeckle(px, w, h) {
30567
30569
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30568
30570
  let hClip = 0;
30569
30571
  let vClip = 0;
30572
+ let alternating = 0;
30570
30573
  for (let c = 0; c < 3; c++) {
30571
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++;
30572
30588
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30573
30589
  hClip++;
30574
30590
  }
@@ -30576,7 +30592,7 @@ function halftoneDespeckle(px, w, h) {
30576
30592
  vClip++;
30577
30593
  }
30578
30594
  }
30579
- if (hClip < 2 || vClip < 2) {
30595
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30580
30596
  continue;
30581
30597
  }
30582
30598
  for (let c = 0; c < 3; c++) {
@@ -30630,15 +30646,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30630
30646
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30631
30647
  const reducing = W < SW && H < SH;
30632
30648
  const mixed = W > SW && H < SH || W < SW && H > SH;
30633
- 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) {
30634
30652
  halftoneDespeckle(rect, SW, SH);
30635
30653
  }
30636
30654
  if (adjust && !adjustAfterSampling) {
30637
30655
  adjust(rect);
30638
30656
  }
30639
- const cols = halftoneAxis(0, SW, W, flipX);
30640
- const rows = halftoneAxis(0, SH, H, flipY);
30641
- 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) {
30642
30672
  const linear = (taps, src2, dst, reverse) => {
30643
30673
  if (dst <= src2) return;
30644
30674
  for (let i = 0; i < dst; i++) {
@@ -30651,10 +30681,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30651
30681
  linear(cols, SW, W, flipX);
30652
30682
  linear(rows, SH, H, flipY);
30653
30683
  }
30654
- const out = new Int32Array(W * H * 3);
30655
- 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++) {
30656
30686
  const ys = rows[y];
30657
- for (let x = 0; x < W; x++) {
30687
+ for (let x = 0; x < sampleW; x++) {
30658
30688
  const xs = cols[x];
30659
30689
  let r = 0;
30660
30690
  let g = 0;
@@ -30670,16 +30700,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30670
30700
  b += rect[i + 2] * w;
30671
30701
  }
30672
30702
  }
30673
- const o = (y * W + x) * 3;
30674
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30675
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30676
- 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);
30677
30708
  }
30678
30709
  }
30679
30710
  if (reducing) {
30680
30711
  halftoneSharpen(out, W, H);
30681
30712
  }
30682
- 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) {
30683
30748
  const source = out.slice();
30684
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];
30685
30750
  for (let y = 0; y < H; y++) {
package/dist/index.js CHANGED
@@ -30535,11 +30535,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30535
30535
  if (dstLen <= 0 || srcLen <= 0) {
30536
30536
  return taps;
30537
30537
  }
30538
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30538
+ const at = (k) => start + k;
30539
30539
  if (dstLen >= srcLen) {
30540
30540
  for (let i = 0; i < dstLen; i++) {
30541
30541
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30542
30542
  }
30543
+ if (reverse) taps.reverse();
30543
30544
  return taps;
30544
30545
  }
30545
30546
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30555,6 +30556,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30555
30556
  }
30556
30557
  taps.push(run);
30557
30558
  }
30559
+ if (reverse) taps.reverse();
30558
30560
  return taps;
30559
30561
  }
30560
30562
  var SPECKLE_OVERSHOOT = 4;
@@ -30570,8 +30572,22 @@ function halftoneDespeckle(px, w, h) {
30570
30572
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30571
30573
  let hClip = 0;
30572
30574
  let vClip = 0;
30575
+ let alternating = 0;
30573
30576
  for (let c = 0; c < 3; c++) {
30574
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++;
30575
30591
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30576
30592
  hClip++;
30577
30593
  }
@@ -30579,7 +30595,7 @@ function halftoneDespeckle(px, w, h) {
30579
30595
  vClip++;
30580
30596
  }
30581
30597
  }
30582
- if (hClip < 2 || vClip < 2) {
30598
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30583
30599
  continue;
30584
30600
  }
30585
30601
  for (let c = 0; c < 3; c++) {
@@ -30633,15 +30649,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30633
30649
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30634
30650
  const reducing = W < SW && H < SH;
30635
30651
  const mixed = W > SW && H < SH || W < SW && H > SH;
30636
- 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) {
30637
30655
  halftoneDespeckle(rect, SW, SH);
30638
30656
  }
30639
30657
  if (adjust && !adjustAfterSampling) {
30640
30658
  adjust(rect);
30641
30659
  }
30642
- const cols = halftoneAxis(0, SW, W, flipX);
30643
- const rows = halftoneAxis(0, SH, H, flipY);
30644
- 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) {
30645
30675
  const linear = (taps, src2, dst, reverse) => {
30646
30676
  if (dst <= src2) return;
30647
30677
  for (let i = 0; i < dst; i++) {
@@ -30654,10 +30684,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30654
30684
  linear(cols, SW, W, flipX);
30655
30685
  linear(rows, SH, H, flipY);
30656
30686
  }
30657
- const out = new Int32Array(W * H * 3);
30658
- 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++) {
30659
30689
  const ys = rows[y];
30660
- for (let x = 0; x < W; x++) {
30690
+ for (let x = 0; x < sampleW; x++) {
30661
30691
  const xs = cols[x];
30662
30692
  let r = 0;
30663
30693
  let g = 0;
@@ -30673,16 +30703,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30673
30703
  b += rect[i + 2] * w;
30674
30704
  }
30675
30705
  }
30676
- const o = (y * W + x) * 3;
30677
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30678
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30679
- 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);
30680
30711
  }
30681
30712
  }
30682
30713
  if (reducing) {
30683
30714
  halftoneSharpen(out, W, H);
30684
30715
  }
30685
- 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) {
30686
30751
  const source = out.slice();
30687
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];
30688
30753
  for (let y = 0; y < H; y++) {
package/dist/index.mjs CHANGED
@@ -30533,11 +30533,12 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30533
30533
  if (dstLen <= 0 || srcLen <= 0) {
30534
30534
  return taps;
30535
30535
  }
30536
- const at = (k) => reverse ? start + srcLen - 1 - k : start + k;
30536
+ const at = (k) => start + k;
30537
30537
  if (dstLen >= srcLen) {
30538
30538
  for (let i = 0; i < dstLen; i++) {
30539
30539
  taps.push([[at(halftoneNearest(i, srcLen, dstLen)), 1]]);
30540
30540
  }
30541
+ if (reverse) taps.reverse();
30541
30542
  return taps;
30542
30543
  }
30543
30544
  const step = Math.ceil(srcLen * FIX / dstLen);
@@ -30553,6 +30554,7 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
30553
30554
  }
30554
30555
  taps.push(run);
30555
30556
  }
30557
+ if (reverse) taps.reverse();
30556
30558
  return taps;
30557
30559
  }
30558
30560
  var SPECKLE_OVERSHOOT = 4;
@@ -30568,8 +30570,22 @@ function halftoneDespeckle(px, w, h) {
30568
30570
  const right = x < w - 1 ? x + 1 : Math.max(0, x - 1);
30569
30571
  let hClip = 0;
30570
30572
  let vClip = 0;
30573
+ let alternating = 0;
30571
30574
  for (let c = 0; c < 3; c++) {
30572
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++;
30573
30589
  if (over(6 * v - at(left, y, c) - at(right, y, c))) {
30574
30590
  hClip++;
30575
30591
  }
@@ -30577,7 +30593,7 @@ function halftoneDespeckle(px, w, h) {
30577
30593
  vClip++;
30578
30594
  }
30579
30595
  }
30580
- if (hClip < 2 || vClip < 2) {
30596
+ if (hClip < 2 || vClip < 2 || alternating < 2) {
30581
30597
  continue;
30582
30598
  }
30583
30599
  for (let c = 0; c < 3; c++) {
@@ -30631,15 +30647,29 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30631
30647
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30632
30648
  const reducing = W < SW && H < SH;
30633
30649
  const mixed = W > SW && H < SH || W < SW && H > SH;
30634
- 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) {
30635
30653
  halftoneDespeckle(rect, SW, SH);
30636
30654
  }
30637
30655
  if (adjust && !adjustAfterSampling) {
30638
30656
  adjust(rect);
30639
30657
  }
30640
- const cols = halftoneAxis(0, SW, W, flipX);
30641
- const rows = halftoneAxis(0, SH, H, flipY);
30642
- 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) {
30643
30673
  const linear = (taps, src2, dst, reverse) => {
30644
30674
  if (dst <= src2) return;
30645
30675
  for (let i = 0; i < dst; i++) {
@@ -30652,10 +30682,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30652
30682
  linear(cols, SW, W, flipX);
30653
30683
  linear(rows, SH, H, flipY);
30654
30684
  }
30655
- const out = new Int32Array(W * H * 3);
30656
- 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++) {
30657
30687
  const ys = rows[y];
30658
- for (let x = 0; x < W; x++) {
30688
+ for (let x = 0; x < sampleW; x++) {
30659
30689
  const xs = cols[x];
30660
30690
  let r = 0;
30661
30691
  let g = 0;
@@ -30671,16 +30701,51 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
30671
30701
  b += rect[i + 2] * w;
30672
30702
  }
30673
30703
  }
30674
- const o = (y * W + x) * 3;
30675
- out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30676
- out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30677
- 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);
30678
30709
  }
30679
30710
  }
30680
30711
  if (reducing) {
30681
30712
  halftoneSharpen(out, W, H);
30682
30713
  }
30683
- 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) {
30684
30749
  const source = out.slice();
30685
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];
30686
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.14",
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",