emf-converter 4.4.2 → 4.4.4

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
@@ -10985,7 +10985,7 @@ function flattenBezierPath(pts) {
10985
10985
  }
10986
10986
  return out;
10987
10987
  }
10988
- function roundRectCorners(l, t, r, b, cw, ch) {
10988
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = false) {
10989
10989
  const w = r - l;
10990
10990
  const h = b - t;
10991
10991
  let ew = Math.min(Math.abs(cw), w);
@@ -10994,10 +10994,12 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10994
10994
  ew = 0;
10995
10995
  eh = 0;
10996
10996
  }
10997
- const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10998
- const topX = Math.floor(r - ew / 2);
10997
+ const hy = compatible ? Math.round(eh / 2) : Math.floor(h / 2) - Math.floor((h - eh) / 2);
10998
+ const topX = compatible ? Math.round(r - ew / 2) : Math.floor(r - ew / 2);
10999
10999
  const hx = r - topX;
11000
- const q = [r, t + hy, r, t + hy - Math.floor(KAPPA * hy), r - hx + Math.ceil(KAPPA * hx), t, topX, t];
11000
+ const verticalControl = compatible && clockwise ? Math.ceil(KAPPA * hy) : Math.floor(KAPPA * hy);
11001
+ const horizontalControl = Math.ceil(KAPPA * hx);
11002
+ const q = [r, t + hy, r, t + hy - verticalControl, r - hx + horizontalControl, t, topX, t];
11001
11003
  const mx = (x) => l + r - x;
11002
11004
  const my = (y) => t + b - y;
11003
11005
  return [
@@ -15698,10 +15700,9 @@ function blurKernel(radius) {
15698
15700
  k[0] = 1;
15699
15701
  return k;
15700
15702
  }
15701
- const sigma = radius / 1.98;
15702
15703
  let sum = 0;
15703
15704
  for (let i = -taps; i <= taps; i++) {
15704
- const w = Math.exp(-(i * i) / (2 * sigma * sigma));
15705
+ const w = Math.exp(-1.96 * i * i / (radius * radius));
15705
15706
  k[i + taps] = w;
15706
15707
  sum += w;
15707
15708
  }
@@ -15722,6 +15723,8 @@ function mirror(p, n) {
15722
15723
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15723
15724
  const k = blurKernel(radius);
15724
15725
  const taps = (k.length - 1) / 2;
15726
+ const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15727
+ const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15725
15728
  const out = new Float64Array(w * h * 4);
15726
15729
  for (let y = 0; y < h; y++) {
15727
15730
  const row = y * w * 4;
@@ -15731,7 +15734,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15731
15734
  let s2 = 0;
15732
15735
  let s3 = 0;
15733
15736
  for (let j = -taps; j <= taps; j++) {
15734
- const p = row + mirror(x + j, w) * 4;
15737
+ const p = row + horizontalIndex(x + j) * 4;
15735
15738
  const kw = k[j + taps];
15736
15739
  s0 += kw * src[p];
15737
15740
  s1 += kw * src[p + 1];
@@ -15739,38 +15742,37 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15739
15742
  s3 += kw * src[p + 3];
15740
15743
  }
15741
15744
  const o = row + x * 4;
15742
- out[o] = s0;
15743
- out[o + 1] = s1;
15744
- out[o + 2] = s2;
15745
- out[o + 3] = s3;
15745
+ out[o] = Math.round(s0);
15746
+ out[o + 1] = Math.round(s1);
15747
+ out[o + 2] = Math.round(s2);
15748
+ out[o + 3] = Math.round(s3);
15746
15749
  }
15747
15750
  }
15748
15751
  if (taps > 0 && vRow >= 0 && vRow < h) {
15749
- const col = new Float64Array(w * 4);
15750
- for (let j = -taps; j <= taps; j++) {
15751
- const yy = vRow + j;
15752
- if (vZeroAbove && yy < vRow) {
15753
- continue;
15754
- }
15755
- const base = mirror(yy, h) * w * 4;
15756
- for (let i = 0; i < w * 4; i++) {
15757
- col[i] += k[j + taps] * out[base + i];
15752
+ const horizontal = out.slice();
15753
+ const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15754
+ for (let row = vRow; row < endRow; row++) {
15755
+ const col = new Float64Array(w * 4);
15756
+ for (let j = -taps; j <= taps; j++) {
15757
+ const yy = row + j;
15758
+ if (vZeroAbove && yy < vRow) {
15759
+ continue;
15760
+ }
15761
+ const base = verticalIndex(yy) * w * 4;
15762
+ for (let i = 0; i < w * 4; i++) {
15763
+ col[i] += k[j + taps] * horizontal[base + i];
15764
+ }
15758
15765
  }
15766
+ out.set(col, row * w * 4);
15759
15767
  }
15760
- out.set(col, vRow * w * 4);
15761
15768
  }
15762
15769
  return out;
15763
15770
  }
15764
15771
  function applyBlur(src, w, h, radius) {
15765
15772
  return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15766
15773
  }
15767
- var SHARPEN_FALLOFF = Math.log2(96 / 25);
15768
- function sharpenGain(radius, amount) {
15769
- const x = amount / 100;
15770
- if (radius <= 3) {
15771
- return x * 2 * radius / 3;
15772
- }
15773
- return x * 2 * (3 / radius) ** SHARPEN_FALLOFF;
15774
+ function sharpenGain(_radius, amount) {
15775
+ return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15774
15776
  }
15775
15777
  function applySharpen(src, w, h, radius, amount) {
15776
15778
  const blurred = gdipBlur(src, w, h, radius);
@@ -28497,9 +28499,9 @@ function boxFrame(box) {
28497
28499
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28498
28500
  return { l: 0, t: 0, r: w, b: h };
28499
28501
  }
28500
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28502
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28501
28503
  const f = boxFrame(box);
28502
- const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
28504
+ const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch, compatible, clockwise);
28503
28505
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28504
28506
  const pts = [];
28505
28507
  for (let i = 0; i < q.length; i += 2) {
@@ -28507,9 +28509,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28507
28509
  }
28508
28510
  return pts;
28509
28511
  }
28510
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28512
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28511
28513
  if (rectangle) return rectRasterPath(box, clockwise);
28512
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28514
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28513
28515
  const path = new GdiRasterPath();
28514
28516
  for (let c = 0; c < 4; c++) {
28515
28517
  const o = c * 8;
@@ -30087,6 +30089,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30087
30089
  var CA_NEGATIVE = 1;
30088
30090
  var CA_LOG_FILTER = 2;
30089
30091
  var COLOR_ADJUSTMENT_SIZE = 24;
30092
+ function isChannelOnlyColorAdjustment(ca) {
30093
+ return !!ca && (ca.flags & ~CA_LOG_FILTER) === 0 && (ca.illuminant === 0 || ca.illuminant === 6) && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
30094
+ }
30090
30095
  function readColorAdjustment(view, off) {
30091
30096
  const ca = {
30092
30097
  flags: view.getUint16(off + 2, true),
@@ -30142,6 +30147,19 @@ function fromLightness(l) {
30142
30147
  }
30143
30148
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30144
30149
  function colorAdjustmentMapper(ca) {
30150
+ if (ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0) {
30151
+ const tables = [ca.redGamma, ca.greenGamma, ca.blueGamma].map((gamma) => {
30152
+ const table = new Uint8Array(256);
30153
+ for (let i = 0; i < 256; i++) {
30154
+ const input = (ca.flags & CA_NEGATIVE) !== 0 ? 255 - i : i;
30155
+ let value = (input / 255) ** (gamma / 1e4);
30156
+ if ((ca.flags & CA_LOG_FILTER) !== 0) value = Math.log2(1 + 7 * value) / 3;
30157
+ table[i] = Math.round(value * 255);
30158
+ }
30159
+ return table;
30160
+ });
30161
+ return (rgb2) => tables[0][rgb2 >> 16 & 255] << 16 | tables[1][rgb2 >> 8 & 255] << 8 | tables[2][rgb2 & 255];
30162
+ }
30145
30163
  const black = ca.referenceBlack / 1e4;
30146
30164
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30147
30165
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30165,7 +30183,7 @@ function colorAdjustmentMapper(ca) {
30165
30183
  const out = (v, c) => {
30166
30184
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30167
30185
  if (log) {
30168
- v = Math.log10(1 + 9 * v);
30186
+ v = Math.log2(1 + 7 * v) / 3;
30169
30187
  }
30170
30188
  return Math.round(v * 255);
30171
30189
  };
@@ -30389,7 +30407,7 @@ function halftoneSharpen(px, w, h) {
30389
30407
  }
30390
30408
  }
30391
30409
  }
30392
- function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30410
+ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false) {
30393
30411
  const W = Math.max(0, Math.round(Math.abs(dw)));
30394
30412
  const H = Math.max(0, Math.round(Math.abs(dh)));
30395
30413
  const SW = Math.round(Math.abs(sw));
@@ -30420,10 +30438,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30420
30438
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30421
30439
  const reducing = W < SW && H < SH;
30422
30440
  const mixed = W > SW && H < SH || W < SW && H > SH;
30423
- if (enlarging) {
30441
+ if (enlarging && !directDib) {
30424
30442
  halftoneDespeckle(rect, SW, SH);
30425
30443
  }
30426
- if (adjust) {
30444
+ if (adjust && !adjustAfterSampling) {
30427
30445
  adjust(rect);
30428
30446
  }
30429
30447
  const cols = halftoneAxis(0, SW, W, flipX);
@@ -30482,6 +30500,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30482
30500
  }
30483
30501
  }
30484
30502
  }
30503
+ if (adjust && adjustAfterSampling) adjust(out);
30485
30504
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30486
30505
  data[i * 4] = out[o];
30487
30506
  data[i * 4 + 1] = out[o + 1];
@@ -30513,7 +30532,18 @@ function patternOperand(rCtx, brush) {
30513
30532
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30514
30533
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30515
30534
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30516
- const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, decoded.adjust) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30535
+ const px = mode === HALFTONE ? stretchHalftone(
30536
+ decoded.pixels,
30537
+ req.sx,
30538
+ req.sy,
30539
+ req.sw,
30540
+ req.sh,
30541
+ req.dw,
30542
+ req.dh,
30543
+ decoded.adjust,
30544
+ decoded.adjustAfterSampling,
30545
+ req.dibOrigin === "bottom-left"
30546
+ ) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30517
30547
  if (px.width === 0 || px.height === 0) {
30518
30548
  return;
30519
30549
  }
@@ -30548,7 +30578,7 @@ function decodeSource(rCtx, req) {
30548
30578
  sy = image.height - sy - req.sh;
30549
30579
  }
30550
30580
  return {
30551
- decoded: { pixels: image, adjust },
30581
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30552
30582
  req: { ...req, sy }
30553
30583
  };
30554
30584
  }
@@ -37531,12 +37561,16 @@ function wmfRectangle(p, l, t, r, b) {
37531
37561
  axisRect: { interior: rectInterior(p, box) }
37532
37562
  });
37533
37563
  }
37534
- function compatCorner(p, w, h) {
37564
+ function compatCorner(p, w, h, box) {
37535
37565
  const m = gdiDeviceMatrix(p.rCtx);
37536
37566
  const cw = Math.round(Math.abs(w * m[0]) * 16);
37537
37567
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37538
37568
  const ux = 16 * p.kx;
37539
37569
  const uy = 16 * p.ky;
37570
+ if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37571
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37572
+ return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
37573
+ }
37540
37574
  if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
37541
37575
  return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
37542
37576
  }
@@ -37547,7 +37581,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37547
37581
  if (box.x1 < box.x0 || box.y1 < box.y0) {
37548
37582
  return;
37549
37583
  }
37550
- const [cw, ch] = compatCorner(p, w, h);
37584
+ const [cw, ch] = compatCorner(p, w, h, box);
37551
37585
  const cr = canvasRect(box);
37552
37586
  paintGdiShape(p.rCtx, {
37553
37587
  build: (c) => {
@@ -37572,7 +37606,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37572
37606
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37573
37607
  c.closePath();
37574
37608
  },
37575
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37609
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, !penIsNull(p) && penIsCosmetic(p.rCtx)),
37576
37610
  roundPen: true,
37577
37611
  fill: true,
37578
37612
  stroke: true
package/dist/browser.mjs CHANGED
@@ -10983,7 +10983,7 @@ function flattenBezierPath(pts) {
10983
10983
  }
10984
10984
  return out;
10985
10985
  }
10986
- function roundRectCorners(l, t, r, b, cw, ch) {
10986
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = false) {
10987
10987
  const w = r - l;
10988
10988
  const h = b - t;
10989
10989
  let ew = Math.min(Math.abs(cw), w);
@@ -10992,10 +10992,12 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10992
10992
  ew = 0;
10993
10993
  eh = 0;
10994
10994
  }
10995
- const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10996
- const topX = Math.floor(r - ew / 2);
10995
+ const hy = compatible ? Math.round(eh / 2) : Math.floor(h / 2) - Math.floor((h - eh) / 2);
10996
+ const topX = compatible ? Math.round(r - ew / 2) : Math.floor(r - ew / 2);
10997
10997
  const hx = r - topX;
10998
- const q = [r, t + hy, r, t + hy - Math.floor(KAPPA * hy), r - hx + Math.ceil(KAPPA * hx), t, topX, t];
10998
+ const verticalControl = compatible && clockwise ? Math.ceil(KAPPA * hy) : Math.floor(KAPPA * hy);
10999
+ const horizontalControl = Math.ceil(KAPPA * hx);
11000
+ const q = [r, t + hy, r, t + hy - verticalControl, r - hx + horizontalControl, t, topX, t];
10999
11001
  const mx = (x) => l + r - x;
11000
11002
  const my = (y) => t + b - y;
11001
11003
  return [
@@ -15696,10 +15698,9 @@ function blurKernel(radius) {
15696
15698
  k[0] = 1;
15697
15699
  return k;
15698
15700
  }
15699
- const sigma = radius / 1.98;
15700
15701
  let sum = 0;
15701
15702
  for (let i = -taps; i <= taps; i++) {
15702
- const w = Math.exp(-(i * i) / (2 * sigma * sigma));
15703
+ const w = Math.exp(-1.96 * i * i / (radius * radius));
15703
15704
  k[i + taps] = w;
15704
15705
  sum += w;
15705
15706
  }
@@ -15720,6 +15721,8 @@ function mirror(p, n) {
15720
15721
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15721
15722
  const k = blurKernel(radius);
15722
15723
  const taps = (k.length - 1) / 2;
15724
+ const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15725
+ const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15723
15726
  const out = new Float64Array(w * h * 4);
15724
15727
  for (let y = 0; y < h; y++) {
15725
15728
  const row = y * w * 4;
@@ -15729,7 +15732,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15729
15732
  let s2 = 0;
15730
15733
  let s3 = 0;
15731
15734
  for (let j = -taps; j <= taps; j++) {
15732
- const p = row + mirror(x + j, w) * 4;
15735
+ const p = row + horizontalIndex(x + j) * 4;
15733
15736
  const kw = k[j + taps];
15734
15737
  s0 += kw * src[p];
15735
15738
  s1 += kw * src[p + 1];
@@ -15737,38 +15740,37 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15737
15740
  s3 += kw * src[p + 3];
15738
15741
  }
15739
15742
  const o = row + x * 4;
15740
- out[o] = s0;
15741
- out[o + 1] = s1;
15742
- out[o + 2] = s2;
15743
- out[o + 3] = s3;
15743
+ out[o] = Math.round(s0);
15744
+ out[o + 1] = Math.round(s1);
15745
+ out[o + 2] = Math.round(s2);
15746
+ out[o + 3] = Math.round(s3);
15744
15747
  }
15745
15748
  }
15746
15749
  if (taps > 0 && vRow >= 0 && vRow < h) {
15747
- const col = new Float64Array(w * 4);
15748
- for (let j = -taps; j <= taps; j++) {
15749
- const yy = vRow + j;
15750
- if (vZeroAbove && yy < vRow) {
15751
- continue;
15752
- }
15753
- const base = mirror(yy, h) * w * 4;
15754
- for (let i = 0; i < w * 4; i++) {
15755
- col[i] += k[j + taps] * out[base + i];
15750
+ const horizontal = out.slice();
15751
+ const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15752
+ for (let row = vRow; row < endRow; row++) {
15753
+ const col = new Float64Array(w * 4);
15754
+ for (let j = -taps; j <= taps; j++) {
15755
+ const yy = row + j;
15756
+ if (vZeroAbove && yy < vRow) {
15757
+ continue;
15758
+ }
15759
+ const base = verticalIndex(yy) * w * 4;
15760
+ for (let i = 0; i < w * 4; i++) {
15761
+ col[i] += k[j + taps] * horizontal[base + i];
15762
+ }
15756
15763
  }
15764
+ out.set(col, row * w * 4);
15757
15765
  }
15758
- out.set(col, vRow * w * 4);
15759
15766
  }
15760
15767
  return out;
15761
15768
  }
15762
15769
  function applyBlur(src, w, h, radius) {
15763
15770
  return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15764
15771
  }
15765
- var SHARPEN_FALLOFF = Math.log2(96 / 25);
15766
- function sharpenGain(radius, amount) {
15767
- const x = amount / 100;
15768
- if (radius <= 3) {
15769
- return x * 2 * radius / 3;
15770
- }
15771
- return x * 2 * (3 / radius) ** SHARPEN_FALLOFF;
15772
+ function sharpenGain(_radius, amount) {
15773
+ return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15772
15774
  }
15773
15775
  function applySharpen(src, w, h, radius, amount) {
15774
15776
  const blurred = gdipBlur(src, w, h, radius);
@@ -28495,9 +28497,9 @@ function boxFrame(box) {
28495
28497
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28496
28498
  return { l: 0, t: 0, r: w, b: h };
28497
28499
  }
28498
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28500
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28499
28501
  const f = boxFrame(box);
28500
- const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
28502
+ const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch, compatible, clockwise);
28501
28503
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28502
28504
  const pts = [];
28503
28505
  for (let i = 0; i < q.length; i += 2) {
@@ -28505,9 +28507,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28505
28507
  }
28506
28508
  return pts;
28507
28509
  }
28508
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28510
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28509
28511
  if (rectangle) return rectRasterPath(box, clockwise);
28510
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28512
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28511
28513
  const path = new GdiRasterPath();
28512
28514
  for (let c = 0; c < 4; c++) {
28513
28515
  const o = c * 8;
@@ -30085,6 +30087,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30085
30087
  var CA_NEGATIVE = 1;
30086
30088
  var CA_LOG_FILTER = 2;
30087
30089
  var COLOR_ADJUSTMENT_SIZE = 24;
30090
+ function isChannelOnlyColorAdjustment(ca) {
30091
+ return !!ca && (ca.flags & ~CA_LOG_FILTER) === 0 && (ca.illuminant === 0 || ca.illuminant === 6) && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
30092
+ }
30088
30093
  function readColorAdjustment(view, off) {
30089
30094
  const ca = {
30090
30095
  flags: view.getUint16(off + 2, true),
@@ -30140,6 +30145,19 @@ function fromLightness(l) {
30140
30145
  }
30141
30146
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30142
30147
  function colorAdjustmentMapper(ca) {
30148
+ if (ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0) {
30149
+ const tables = [ca.redGamma, ca.greenGamma, ca.blueGamma].map((gamma) => {
30150
+ const table = new Uint8Array(256);
30151
+ for (let i = 0; i < 256; i++) {
30152
+ const input = (ca.flags & CA_NEGATIVE) !== 0 ? 255 - i : i;
30153
+ let value = (input / 255) ** (gamma / 1e4);
30154
+ if ((ca.flags & CA_LOG_FILTER) !== 0) value = Math.log2(1 + 7 * value) / 3;
30155
+ table[i] = Math.round(value * 255);
30156
+ }
30157
+ return table;
30158
+ });
30159
+ return (rgb2) => tables[0][rgb2 >> 16 & 255] << 16 | tables[1][rgb2 >> 8 & 255] << 8 | tables[2][rgb2 & 255];
30160
+ }
30143
30161
  const black = ca.referenceBlack / 1e4;
30144
30162
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30145
30163
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30163,7 +30181,7 @@ function colorAdjustmentMapper(ca) {
30163
30181
  const out = (v, c) => {
30164
30182
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30165
30183
  if (log) {
30166
- v = Math.log10(1 + 9 * v);
30184
+ v = Math.log2(1 + 7 * v) / 3;
30167
30185
  }
30168
30186
  return Math.round(v * 255);
30169
30187
  };
@@ -30387,7 +30405,7 @@ function halftoneSharpen(px, w, h) {
30387
30405
  }
30388
30406
  }
30389
30407
  }
30390
- function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30408
+ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false) {
30391
30409
  const W = Math.max(0, Math.round(Math.abs(dw)));
30392
30410
  const H = Math.max(0, Math.round(Math.abs(dh)));
30393
30411
  const SW = Math.round(Math.abs(sw));
@@ -30418,10 +30436,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30418
30436
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30419
30437
  const reducing = W < SW && H < SH;
30420
30438
  const mixed = W > SW && H < SH || W < SW && H > SH;
30421
- if (enlarging) {
30439
+ if (enlarging && !directDib) {
30422
30440
  halftoneDespeckle(rect, SW, SH);
30423
30441
  }
30424
- if (adjust) {
30442
+ if (adjust && !adjustAfterSampling) {
30425
30443
  adjust(rect);
30426
30444
  }
30427
30445
  const cols = halftoneAxis(0, SW, W, flipX);
@@ -30480,6 +30498,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30480
30498
  }
30481
30499
  }
30482
30500
  }
30501
+ if (adjust && adjustAfterSampling) adjust(out);
30483
30502
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30484
30503
  data[i * 4] = out[o];
30485
30504
  data[i * 4 + 1] = out[o + 1];
@@ -30511,7 +30530,18 @@ function patternOperand(rCtx, brush) {
30511
30530
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30512
30531
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30513
30532
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30514
- const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, decoded.adjust) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30533
+ const px = mode === HALFTONE ? stretchHalftone(
30534
+ decoded.pixels,
30535
+ req.sx,
30536
+ req.sy,
30537
+ req.sw,
30538
+ req.sh,
30539
+ req.dw,
30540
+ req.dh,
30541
+ decoded.adjust,
30542
+ decoded.adjustAfterSampling,
30543
+ req.dibOrigin === "bottom-left"
30544
+ ) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30515
30545
  if (px.width === 0 || px.height === 0) {
30516
30546
  return;
30517
30547
  }
@@ -30546,7 +30576,7 @@ function decodeSource(rCtx, req) {
30546
30576
  sy = image.height - sy - req.sh;
30547
30577
  }
30548
30578
  return {
30549
- decoded: { pixels: image, adjust },
30579
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30550
30580
  req: { ...req, sy }
30551
30581
  };
30552
30582
  }
@@ -37529,12 +37559,16 @@ function wmfRectangle(p, l, t, r, b) {
37529
37559
  axisRect: { interior: rectInterior(p, box) }
37530
37560
  });
37531
37561
  }
37532
- function compatCorner(p, w, h) {
37562
+ function compatCorner(p, w, h, box) {
37533
37563
  const m = gdiDeviceMatrix(p.rCtx);
37534
37564
  const cw = Math.round(Math.abs(w * m[0]) * 16);
37535
37565
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37536
37566
  const ux = 16 * p.kx;
37537
37567
  const uy = 16 * p.ky;
37568
+ if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37569
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37570
+ return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
37571
+ }
37538
37572
  if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
37539
37573
  return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
37540
37574
  }
@@ -37545,7 +37579,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37545
37579
  if (box.x1 < box.x0 || box.y1 < box.y0) {
37546
37580
  return;
37547
37581
  }
37548
- const [cw, ch] = compatCorner(p, w, h);
37582
+ const [cw, ch] = compatCorner(p, w, h, box);
37549
37583
  const cr = canvasRect(box);
37550
37584
  paintGdiShape(p.rCtx, {
37551
37585
  build: (c) => {
@@ -37570,7 +37604,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37570
37604
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37571
37605
  c.closePath();
37572
37606
  },
37573
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37607
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, !penIsNull(p) && penIsCosmetic(p.rCtx)),
37574
37608
  roundPen: true,
37575
37609
  fill: true,
37576
37610
  stroke: true
package/dist/index.js CHANGED
@@ -10986,7 +10986,7 @@ function flattenBezierPath(pts) {
10986
10986
  }
10987
10987
  return out;
10988
10988
  }
10989
- function roundRectCorners(l, t, r, b, cw, ch) {
10989
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = false) {
10990
10990
  const w = r - l;
10991
10991
  const h = b - t;
10992
10992
  let ew = Math.min(Math.abs(cw), w);
@@ -10995,10 +10995,12 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10995
10995
  ew = 0;
10996
10996
  eh = 0;
10997
10997
  }
10998
- const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10999
- const topX = Math.floor(r - ew / 2);
10998
+ const hy = compatible ? Math.round(eh / 2) : Math.floor(h / 2) - Math.floor((h - eh) / 2);
10999
+ const topX = compatible ? Math.round(r - ew / 2) : Math.floor(r - ew / 2);
11000
11000
  const hx = r - topX;
11001
- const q = [r, t + hy, r, t + hy - Math.floor(KAPPA * hy), r - hx + Math.ceil(KAPPA * hx), t, topX, t];
11001
+ const verticalControl = compatible && clockwise ? Math.ceil(KAPPA * hy) : Math.floor(KAPPA * hy);
11002
+ const horizontalControl = Math.ceil(KAPPA * hx);
11003
+ const q = [r, t + hy, r, t + hy - verticalControl, r - hx + horizontalControl, t, topX, t];
11002
11004
  const mx = (x) => l + r - x;
11003
11005
  const my = (y) => t + b - y;
11004
11006
  return [
@@ -15699,10 +15701,9 @@ function blurKernel(radius) {
15699
15701
  k[0] = 1;
15700
15702
  return k;
15701
15703
  }
15702
- const sigma = radius / 1.98;
15703
15704
  let sum = 0;
15704
15705
  for (let i = -taps; i <= taps; i++) {
15705
- const w = Math.exp(-(i * i) / (2 * sigma * sigma));
15706
+ const w = Math.exp(-1.96 * i * i / (radius * radius));
15706
15707
  k[i + taps] = w;
15707
15708
  sum += w;
15708
15709
  }
@@ -15723,6 +15724,8 @@ function mirror(p, n) {
15723
15724
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15724
15725
  const k = blurKernel(radius);
15725
15726
  const taps = (k.length - 1) / 2;
15727
+ const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15728
+ const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15726
15729
  const out = new Float64Array(w * h * 4);
15727
15730
  for (let y = 0; y < h; y++) {
15728
15731
  const row = y * w * 4;
@@ -15732,7 +15735,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15732
15735
  let s2 = 0;
15733
15736
  let s3 = 0;
15734
15737
  for (let j = -taps; j <= taps; j++) {
15735
- const p = row + mirror(x + j, w) * 4;
15738
+ const p = row + horizontalIndex(x + j) * 4;
15736
15739
  const kw = k[j + taps];
15737
15740
  s0 += kw * src[p];
15738
15741
  s1 += kw * src[p + 1];
@@ -15740,38 +15743,37 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15740
15743
  s3 += kw * src[p + 3];
15741
15744
  }
15742
15745
  const o = row + x * 4;
15743
- out[o] = s0;
15744
- out[o + 1] = s1;
15745
- out[o + 2] = s2;
15746
- out[o + 3] = s3;
15746
+ out[o] = Math.round(s0);
15747
+ out[o + 1] = Math.round(s1);
15748
+ out[o + 2] = Math.round(s2);
15749
+ out[o + 3] = Math.round(s3);
15747
15750
  }
15748
15751
  }
15749
15752
  if (taps > 0 && vRow >= 0 && vRow < h) {
15750
- const col = new Float64Array(w * 4);
15751
- for (let j = -taps; j <= taps; j++) {
15752
- const yy = vRow + j;
15753
- if (vZeroAbove && yy < vRow) {
15754
- continue;
15755
- }
15756
- const base = mirror(yy, h) * w * 4;
15757
- for (let i = 0; i < w * 4; i++) {
15758
- col[i] += k[j + taps] * out[base + i];
15753
+ const horizontal = out.slice();
15754
+ const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15755
+ for (let row = vRow; row < endRow; row++) {
15756
+ const col = new Float64Array(w * 4);
15757
+ for (let j = -taps; j <= taps; j++) {
15758
+ const yy = row + j;
15759
+ if (vZeroAbove && yy < vRow) {
15760
+ continue;
15761
+ }
15762
+ const base = verticalIndex(yy) * w * 4;
15763
+ for (let i = 0; i < w * 4; i++) {
15764
+ col[i] += k[j + taps] * horizontal[base + i];
15765
+ }
15759
15766
  }
15767
+ out.set(col, row * w * 4);
15760
15768
  }
15761
- out.set(col, vRow * w * 4);
15762
15769
  }
15763
15770
  return out;
15764
15771
  }
15765
15772
  function applyBlur(src, w, h, radius) {
15766
15773
  return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15767
15774
  }
15768
- var SHARPEN_FALLOFF = Math.log2(96 / 25);
15769
- function sharpenGain(radius, amount) {
15770
- const x = amount / 100;
15771
- if (radius <= 3) {
15772
- return x * 2 * radius / 3;
15773
- }
15774
- return x * 2 * (3 / radius) ** SHARPEN_FALLOFF;
15775
+ function sharpenGain(_radius, amount) {
15776
+ return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15775
15777
  }
15776
15778
  function applySharpen(src, w, h, radius, amount) {
15777
15779
  const blurred = gdipBlur(src, w, h, radius);
@@ -28498,9 +28500,9 @@ function boxFrame(box) {
28498
28500
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28499
28501
  return { l: 0, t: 0, r: w, b: h };
28500
28502
  }
28501
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28503
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28502
28504
  const f = boxFrame(box);
28503
- const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
28505
+ const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch, compatible, clockwise);
28504
28506
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28505
28507
  const pts = [];
28506
28508
  for (let i = 0; i < q.length; i += 2) {
@@ -28508,9 +28510,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28508
28510
  }
28509
28511
  return pts;
28510
28512
  }
28511
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28513
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28512
28514
  if (rectangle) return rectRasterPath(box, clockwise);
28513
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28515
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28514
28516
  const path = new GdiRasterPath();
28515
28517
  for (let c = 0; c < 4; c++) {
28516
28518
  const o = c * 8;
@@ -30088,6 +30090,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30088
30090
  var CA_NEGATIVE = 1;
30089
30091
  var CA_LOG_FILTER = 2;
30090
30092
  var COLOR_ADJUSTMENT_SIZE = 24;
30093
+ function isChannelOnlyColorAdjustment(ca) {
30094
+ return !!ca && (ca.flags & ~CA_LOG_FILTER) === 0 && (ca.illuminant === 0 || ca.illuminant === 6) && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
30095
+ }
30091
30096
  function readColorAdjustment(view, off) {
30092
30097
  const ca = {
30093
30098
  flags: view.getUint16(off + 2, true),
@@ -30143,6 +30148,19 @@ function fromLightness(l) {
30143
30148
  }
30144
30149
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30145
30150
  function colorAdjustmentMapper(ca) {
30151
+ if (ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0) {
30152
+ const tables = [ca.redGamma, ca.greenGamma, ca.blueGamma].map((gamma) => {
30153
+ const table = new Uint8Array(256);
30154
+ for (let i = 0; i < 256; i++) {
30155
+ const input = (ca.flags & CA_NEGATIVE) !== 0 ? 255 - i : i;
30156
+ let value = (input / 255) ** (gamma / 1e4);
30157
+ if ((ca.flags & CA_LOG_FILTER) !== 0) value = Math.log2(1 + 7 * value) / 3;
30158
+ table[i] = Math.round(value * 255);
30159
+ }
30160
+ return table;
30161
+ });
30162
+ return (rgb2) => tables[0][rgb2 >> 16 & 255] << 16 | tables[1][rgb2 >> 8 & 255] << 8 | tables[2][rgb2 & 255];
30163
+ }
30146
30164
  const black = ca.referenceBlack / 1e4;
30147
30165
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30148
30166
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30166,7 +30184,7 @@ function colorAdjustmentMapper(ca) {
30166
30184
  const out = (v, c) => {
30167
30185
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30168
30186
  if (log) {
30169
- v = Math.log10(1 + 9 * v);
30187
+ v = Math.log2(1 + 7 * v) / 3;
30170
30188
  }
30171
30189
  return Math.round(v * 255);
30172
30190
  };
@@ -30390,7 +30408,7 @@ function halftoneSharpen(px, w, h) {
30390
30408
  }
30391
30409
  }
30392
30410
  }
30393
- function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30411
+ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false) {
30394
30412
  const W = Math.max(0, Math.round(Math.abs(dw)));
30395
30413
  const H = Math.max(0, Math.round(Math.abs(dh)));
30396
30414
  const SW = Math.round(Math.abs(sw));
@@ -30421,10 +30439,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30421
30439
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30422
30440
  const reducing = W < SW && H < SH;
30423
30441
  const mixed = W > SW && H < SH || W < SW && H > SH;
30424
- if (enlarging) {
30442
+ if (enlarging && !directDib) {
30425
30443
  halftoneDespeckle(rect, SW, SH);
30426
30444
  }
30427
- if (adjust) {
30445
+ if (adjust && !adjustAfterSampling) {
30428
30446
  adjust(rect);
30429
30447
  }
30430
30448
  const cols = halftoneAxis(0, SW, W, flipX);
@@ -30483,6 +30501,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30483
30501
  }
30484
30502
  }
30485
30503
  }
30504
+ if (adjust && adjustAfterSampling) adjust(out);
30486
30505
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30487
30506
  data[i * 4] = out[o];
30488
30507
  data[i * 4 + 1] = out[o + 1];
@@ -30514,7 +30533,18 @@ function patternOperand(rCtx, brush) {
30514
30533
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30515
30534
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30516
30535
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30517
- const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, decoded.adjust) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30536
+ const px = mode === HALFTONE ? stretchHalftone(
30537
+ decoded.pixels,
30538
+ req.sx,
30539
+ req.sy,
30540
+ req.sw,
30541
+ req.sh,
30542
+ req.dw,
30543
+ req.dh,
30544
+ decoded.adjust,
30545
+ decoded.adjustAfterSampling,
30546
+ req.dibOrigin === "bottom-left"
30547
+ ) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30518
30548
  if (px.width === 0 || px.height === 0) {
30519
30549
  return;
30520
30550
  }
@@ -30549,7 +30579,7 @@ function decodeSource(rCtx, req) {
30549
30579
  sy = image.height - sy - req.sh;
30550
30580
  }
30551
30581
  return {
30552
- decoded: { pixels: image, adjust },
30582
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30553
30583
  req: { ...req, sy }
30554
30584
  };
30555
30585
  }
@@ -37532,12 +37562,16 @@ function wmfRectangle(p, l, t, r, b) {
37532
37562
  axisRect: { interior: rectInterior(p, box) }
37533
37563
  });
37534
37564
  }
37535
- function compatCorner(p, w, h) {
37565
+ function compatCorner(p, w, h, box) {
37536
37566
  const m = gdiDeviceMatrix(p.rCtx);
37537
37567
  const cw = Math.round(Math.abs(w * m[0]) * 16);
37538
37568
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37539
37569
  const ux = 16 * p.kx;
37540
37570
  const uy = 16 * p.ky;
37571
+ if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37572
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37573
+ return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
37574
+ }
37541
37575
  if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
37542
37576
  return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
37543
37577
  }
@@ -37548,7 +37582,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37548
37582
  if (box.x1 < box.x0 || box.y1 < box.y0) {
37549
37583
  return;
37550
37584
  }
37551
- const [cw, ch] = compatCorner(p, w, h);
37585
+ const [cw, ch] = compatCorner(p, w, h, box);
37552
37586
  const cr = canvasRect(box);
37553
37587
  paintGdiShape(p.rCtx, {
37554
37588
  build: (c) => {
@@ -37573,7 +37607,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37573
37607
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37574
37608
  c.closePath();
37575
37609
  },
37576
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37610
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, !penIsNull(p) && penIsCosmetic(p.rCtx)),
37577
37611
  roundPen: true,
37578
37612
  fill: true,
37579
37613
  stroke: true
package/dist/index.mjs CHANGED
@@ -10984,7 +10984,7 @@ function flattenBezierPath(pts) {
10984
10984
  }
10985
10985
  return out;
10986
10986
  }
10987
- function roundRectCorners(l, t, r, b, cw, ch) {
10987
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = false) {
10988
10988
  const w = r - l;
10989
10989
  const h = b - t;
10990
10990
  let ew = Math.min(Math.abs(cw), w);
@@ -10993,10 +10993,12 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10993
10993
  ew = 0;
10994
10994
  eh = 0;
10995
10995
  }
10996
- const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10997
- const topX = Math.floor(r - ew / 2);
10996
+ const hy = compatible ? Math.round(eh / 2) : Math.floor(h / 2) - Math.floor((h - eh) / 2);
10997
+ const topX = compatible ? Math.round(r - ew / 2) : Math.floor(r - ew / 2);
10998
10998
  const hx = r - topX;
10999
- const q = [r, t + hy, r, t + hy - Math.floor(KAPPA * hy), r - hx + Math.ceil(KAPPA * hx), t, topX, t];
10999
+ const verticalControl = compatible && clockwise ? Math.ceil(KAPPA * hy) : Math.floor(KAPPA * hy);
11000
+ const horizontalControl = Math.ceil(KAPPA * hx);
11001
+ const q = [r, t + hy, r, t + hy - verticalControl, r - hx + horizontalControl, t, topX, t];
11000
11002
  const mx = (x) => l + r - x;
11001
11003
  const my = (y) => t + b - y;
11002
11004
  return [
@@ -15697,10 +15699,9 @@ function blurKernel(radius) {
15697
15699
  k[0] = 1;
15698
15700
  return k;
15699
15701
  }
15700
- const sigma = radius / 1.98;
15701
15702
  let sum = 0;
15702
15703
  for (let i = -taps; i <= taps; i++) {
15703
- const w = Math.exp(-(i * i) / (2 * sigma * sigma));
15704
+ const w = Math.exp(-1.96 * i * i / (radius * radius));
15704
15705
  k[i + taps] = w;
15705
15706
  sum += w;
15706
15707
  }
@@ -15721,6 +15722,8 @@ function mirror(p, n) {
15721
15722
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15722
15723
  const k = blurKernel(radius);
15723
15724
  const taps = (k.length - 1) / 2;
15725
+ const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15726
+ const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15724
15727
  const out = new Float64Array(w * h * 4);
15725
15728
  for (let y = 0; y < h; y++) {
15726
15729
  const row = y * w * 4;
@@ -15730,7 +15733,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15730
15733
  let s2 = 0;
15731
15734
  let s3 = 0;
15732
15735
  for (let j = -taps; j <= taps; j++) {
15733
- const p = row + mirror(x + j, w) * 4;
15736
+ const p = row + horizontalIndex(x + j) * 4;
15734
15737
  const kw = k[j + taps];
15735
15738
  s0 += kw * src[p];
15736
15739
  s1 += kw * src[p + 1];
@@ -15738,38 +15741,37 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15738
15741
  s3 += kw * src[p + 3];
15739
15742
  }
15740
15743
  const o = row + x * 4;
15741
- out[o] = s0;
15742
- out[o + 1] = s1;
15743
- out[o + 2] = s2;
15744
- out[o + 3] = s3;
15744
+ out[o] = Math.round(s0);
15745
+ out[o + 1] = Math.round(s1);
15746
+ out[o + 2] = Math.round(s2);
15747
+ out[o + 3] = Math.round(s3);
15745
15748
  }
15746
15749
  }
15747
15750
  if (taps > 0 && vRow >= 0 && vRow < h) {
15748
- const col = new Float64Array(w * 4);
15749
- for (let j = -taps; j <= taps; j++) {
15750
- const yy = vRow + j;
15751
- if (vZeroAbove && yy < vRow) {
15752
- continue;
15753
- }
15754
- const base = mirror(yy, h) * w * 4;
15755
- for (let i = 0; i < w * 4; i++) {
15756
- col[i] += k[j + taps] * out[base + i];
15751
+ const horizontal = out.slice();
15752
+ const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15753
+ for (let row = vRow; row < endRow; row++) {
15754
+ const col = new Float64Array(w * 4);
15755
+ for (let j = -taps; j <= taps; j++) {
15756
+ const yy = row + j;
15757
+ if (vZeroAbove && yy < vRow) {
15758
+ continue;
15759
+ }
15760
+ const base = verticalIndex(yy) * w * 4;
15761
+ for (let i = 0; i < w * 4; i++) {
15762
+ col[i] += k[j + taps] * horizontal[base + i];
15763
+ }
15757
15764
  }
15765
+ out.set(col, row * w * 4);
15758
15766
  }
15759
- out.set(col, vRow * w * 4);
15760
15767
  }
15761
15768
  return out;
15762
15769
  }
15763
15770
  function applyBlur(src, w, h, radius) {
15764
15771
  return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15765
15772
  }
15766
- var SHARPEN_FALLOFF = Math.log2(96 / 25);
15767
- function sharpenGain(radius, amount) {
15768
- const x = amount / 100;
15769
- if (radius <= 3) {
15770
- return x * 2 * radius / 3;
15771
- }
15772
- return x * 2 * (3 / radius) ** SHARPEN_FALLOFF;
15773
+ function sharpenGain(_radius, amount) {
15774
+ return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15773
15775
  }
15774
15776
  function applySharpen(src, w, h, radius, amount) {
15775
15777
  const blurred = gdipBlur(src, w, h, radius);
@@ -28496,9 +28498,9 @@ function boxFrame(box) {
28496
28498
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28497
28499
  return { l: 0, t: 0, r: w, b: h };
28498
28500
  }
28499
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28501
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28500
28502
  const f = boxFrame(box);
28501
- const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch);
28503
+ const q = roundRectCorners(f.l, f.t, f.r, f.b, cw, ch, compatible, clockwise);
28502
28504
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28503
28505
  const pts = [];
28504
28506
  for (let i = 0; i < q.length; i += 2) {
@@ -28506,9 +28508,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28506
28508
  }
28507
28509
  return pts;
28508
28510
  }
28509
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28511
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28510
28512
  if (rectangle) return rectRasterPath(box, clockwise);
28511
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28513
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28512
28514
  const path = new GdiRasterPath();
28513
28515
  for (let c = 0; c < 4; c++) {
28514
28516
  const o = c * 8;
@@ -30086,6 +30088,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30086
30088
  var CA_NEGATIVE = 1;
30087
30089
  var CA_LOG_FILTER = 2;
30088
30090
  var COLOR_ADJUSTMENT_SIZE = 24;
30091
+ function isChannelOnlyColorAdjustment(ca) {
30092
+ return !!ca && (ca.flags & ~CA_LOG_FILTER) === 0 && (ca.illuminant === 0 || ca.illuminant === 6) && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
30093
+ }
30089
30094
  function readColorAdjustment(view, off) {
30090
30095
  const ca = {
30091
30096
  flags: view.getUint16(off + 2, true),
@@ -30141,6 +30146,19 @@ function fromLightness(l) {
30141
30146
  }
30142
30147
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30143
30148
  function colorAdjustmentMapper(ca) {
30149
+ if (ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0) {
30150
+ const tables = [ca.redGamma, ca.greenGamma, ca.blueGamma].map((gamma) => {
30151
+ const table = new Uint8Array(256);
30152
+ for (let i = 0; i < 256; i++) {
30153
+ const input = (ca.flags & CA_NEGATIVE) !== 0 ? 255 - i : i;
30154
+ let value = (input / 255) ** (gamma / 1e4);
30155
+ if ((ca.flags & CA_LOG_FILTER) !== 0) value = Math.log2(1 + 7 * value) / 3;
30156
+ table[i] = Math.round(value * 255);
30157
+ }
30158
+ return table;
30159
+ });
30160
+ return (rgb2) => tables[0][rgb2 >> 16 & 255] << 16 | tables[1][rgb2 >> 8 & 255] << 8 | tables[2][rgb2 & 255];
30161
+ }
30144
30162
  const black = ca.referenceBlack / 1e4;
30145
30163
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30146
30164
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30164,7 +30182,7 @@ function colorAdjustmentMapper(ca) {
30164
30182
  const out = (v, c) => {
30165
30183
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30166
30184
  if (log) {
30167
- v = Math.log10(1 + 9 * v);
30185
+ v = Math.log2(1 + 7 * v) / 3;
30168
30186
  }
30169
30187
  return Math.round(v * 255);
30170
30188
  };
@@ -30388,7 +30406,7 @@ function halftoneSharpen(px, w, h) {
30388
30406
  }
30389
30407
  }
30390
30408
  }
30391
- function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30409
+ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false) {
30392
30410
  const W = Math.max(0, Math.round(Math.abs(dw)));
30393
30411
  const H = Math.max(0, Math.round(Math.abs(dh)));
30394
30412
  const SW = Math.round(Math.abs(sw));
@@ -30419,10 +30437,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30419
30437
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30420
30438
  const reducing = W < SW && H < SH;
30421
30439
  const mixed = W > SW && H < SH || W < SW && H > SH;
30422
- if (enlarging) {
30440
+ if (enlarging && !directDib) {
30423
30441
  halftoneDespeckle(rect, SW, SH);
30424
30442
  }
30425
- if (adjust) {
30443
+ if (adjust && !adjustAfterSampling) {
30426
30444
  adjust(rect);
30427
30445
  }
30428
30446
  const cols = halftoneAxis(0, SW, W, flipX);
@@ -30481,6 +30499,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30481
30499
  }
30482
30500
  }
30483
30501
  }
30502
+ if (adjust && adjustAfterSampling) adjust(out);
30484
30503
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30485
30504
  data[i * 4] = out[o];
30486
30505
  data[i * 4 + 1] = out[o + 1];
@@ -30512,7 +30531,18 @@ function patternOperand(rCtx, brush) {
30512
30531
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30513
30532
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30514
30533
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30515
- const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, decoded.adjust) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30534
+ const px = mode === HALFTONE ? stretchHalftone(
30535
+ decoded.pixels,
30536
+ req.sx,
30537
+ req.sy,
30538
+ req.sw,
30539
+ req.sh,
30540
+ req.dw,
30541
+ req.dh,
30542
+ decoded.adjust,
30543
+ decoded.adjustAfterSampling,
30544
+ req.dibOrigin === "bottom-left"
30545
+ ) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
30516
30546
  if (px.width === 0 || px.height === 0) {
30517
30547
  return;
30518
30548
  }
@@ -30547,7 +30577,7 @@ function decodeSource(rCtx, req) {
30547
30577
  sy = image.height - sy - req.sh;
30548
30578
  }
30549
30579
  return {
30550
- decoded: { pixels: image, adjust },
30580
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30551
30581
  req: { ...req, sy }
30552
30582
  };
30553
30583
  }
@@ -37530,12 +37560,16 @@ function wmfRectangle(p, l, t, r, b) {
37530
37560
  axisRect: { interior: rectInterior(p, box) }
37531
37561
  });
37532
37562
  }
37533
- function compatCorner(p, w, h) {
37563
+ function compatCorner(p, w, h, box) {
37534
37564
  const m = gdiDeviceMatrix(p.rCtx);
37535
37565
  const cw = Math.round(Math.abs(w * m[0]) * 16);
37536
37566
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37537
37567
  const ux = 16 * p.kx;
37538
37568
  const uy = 16 * p.ky;
37569
+ if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37570
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37571
+ return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
37572
+ }
37539
37573
  if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
37540
37574
  return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
37541
37575
  }
@@ -37546,7 +37580,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37546
37580
  if (box.x1 < box.x0 || box.y1 < box.y0) {
37547
37581
  return;
37548
37582
  }
37549
- const [cw, ch] = compatCorner(p, w, h);
37583
+ const [cw, ch] = compatCorner(p, w, h, box);
37550
37584
  const cr = canvasRect(box);
37551
37585
  paintGdiShape(p.rCtx, {
37552
37586
  build: (c) => {
@@ -37571,7 +37605,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37571
37605
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37572
37606
  c.closePath();
37573
37607
  },
37574
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37608
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, !penIsNull(p) && penIsCosmetic(p.rCtx)),
37575
37609
  roundPen: true,
37576
37610
  fill: true,
37577
37611
  stroke: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.4.2",
3
+ "version": "4.4.4",
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",