emf-converter 4.4.1 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -10332,6 +10332,16 @@ function decodeImageCodecFallback(bytes) {
10332
10332
  if (!f || !validSize(f.image.descriptor.width, f.image.descriptor.height)) return null;
10333
10333
  const frame2 = (0, import_gifuct_js.decompressFrame)(f, gif.gct, true);
10334
10334
  const data = new Uint8ClampedArray(width * height * 4);
10335
+ const transparent = frame2.transparentIndex !== void 0;
10336
+ const background = transparent ? frame2.colorTable[frame2.transparentIndex] : gif.gct?.[gif.lsd.backgroundColorIndex];
10337
+ if (background) {
10338
+ for (let i = 0; i < data.length; i += 4) {
10339
+ data[i] = background[0];
10340
+ data[i + 1] = background[1];
10341
+ data[i + 2] = background[2];
10342
+ data[i + 3] = transparent ? 0 : 255;
10343
+ }
10344
+ }
10335
10345
  const { top, left, width: fw, height: fh } = frame2.dims;
10336
10346
  for (let y = 0; y < fh && top + y < height; y++) {
10337
10347
  const n = Math.min(fw, width - left);
@@ -10975,7 +10985,7 @@ function flattenBezierPath(pts) {
10975
10985
  }
10976
10986
  return out;
10977
10987
  }
10978
- function roundRectCorners(l, t, r, b, cw, ch) {
10988
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false) {
10979
10989
  const w = r - l;
10980
10990
  const h = b - t;
10981
10991
  let ew = Math.min(Math.abs(cw), w);
@@ -10987,7 +10997,9 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10987
10997
  const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10988
10998
  const topX = Math.floor(r - ew / 2);
10989
10999
  const hx = r - topX;
10990
- 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 ? Math.round(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];
10991
11003
  const mx = (x) => l + r - x;
10992
11004
  const my = (y) => t + b - y;
10993
11005
  return [
@@ -28487,9 +28499,9 @@ function boxFrame(box) {
28487
28499
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28488
28500
  return { l: 0, t: 0, r: w, b: h };
28489
28501
  }
28490
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28502
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28491
28503
  const f = boxFrame(box);
28492
- 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);
28493
28505
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28494
28506
  const pts = [];
28495
28507
  for (let i = 0; i < q.length; i += 2) {
@@ -28497,9 +28509,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28497
28509
  }
28498
28510
  return pts;
28499
28511
  }
28500
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28512
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28501
28513
  if (rectangle) return rectRasterPath(box, clockwise);
28502
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28514
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28503
28515
  const path = new GdiRasterPath();
28504
28516
  for (let c = 0; c < 4; c++) {
28505
28517
  const o = c * 8;
@@ -30077,6 +30089,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30077
30089
  var CA_NEGATIVE = 1;
30078
30090
  var CA_LOG_FILTER = 2;
30079
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
+ }
30080
30095
  function readColorAdjustment(view, off) {
30081
30096
  const ca = {
30082
30097
  flags: view.getUint16(off + 2, true),
@@ -30132,6 +30147,19 @@ function fromLightness(l) {
30132
30147
  }
30133
30148
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30134
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
+ }
30135
30163
  const black = ca.referenceBlack / 1e4;
30136
30164
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30137
30165
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30155,7 +30183,7 @@ function colorAdjustmentMapper(ca) {
30155
30183
  const out = (v, c) => {
30156
30184
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30157
30185
  if (log) {
30158
- v = Math.log10(1 + 9 * v);
30186
+ v = Math.log2(1 + 7 * v) / 3;
30159
30187
  }
30160
30188
  return Math.round(v * 255);
30161
30189
  };
@@ -30379,7 +30407,7 @@ function halftoneSharpen(px, w, h) {
30379
30407
  }
30380
30408
  }
30381
30409
  }
30382
- 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) {
30383
30411
  const W = Math.max(0, Math.round(Math.abs(dw)));
30384
30412
  const H = Math.max(0, Math.round(Math.abs(dh)));
30385
30413
  const SW = Math.round(Math.abs(sw));
@@ -30409,14 +30437,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30409
30437
  }
30410
30438
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30411
30439
  const reducing = W < SW && H < SH;
30412
- if (enlarging) {
30440
+ const mixed = W > SW && H < SH || W < SW && H > SH;
30441
+ if (enlarging && !directDib) {
30413
30442
  halftoneDespeckle(rect, SW, SH);
30414
30443
  }
30415
- if (adjust) {
30444
+ if (adjust && !adjustAfterSampling) {
30416
30445
  adjust(rect);
30417
30446
  }
30418
30447
  const cols = halftoneAxis(0, SW, W, flipX);
30419
30448
  const rows = halftoneAxis(0, SH, H, flipY);
30449
+ if (mixed) {
30450
+ const linear = (taps, src2, dst, reverse) => {
30451
+ if (dst <= src2) return;
30452
+ for (let i = 0; i < dst; i++) {
30453
+ const position = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
30454
+ const left = Math.floor(position), fraction = position - left;
30455
+ const at = (k) => reverse ? src2 - 1 - k : k;
30456
+ taps[i] = [[at(left), 1 - fraction], [at(Math.min(src2 - 1, left + 1)), fraction]];
30457
+ }
30458
+ };
30459
+ linear(cols, SW, W, flipX);
30460
+ linear(rows, SH, H, flipY);
30461
+ }
30420
30462
  const out = new Int32Array(W * H * 3);
30421
30463
  for (let y = 0; y < H; y++) {
30422
30464
  const ys = rows[y];
@@ -30437,14 +30479,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30437
30479
  }
30438
30480
  }
30439
30481
  const o = (y * W + x) * 3;
30440
- out[o] = Math.floor((2 * r + total) / (2 * total));
30441
- out[o + 1] = Math.floor((2 * g + total) / (2 * total));
30442
- out[o + 2] = Math.floor((2 * b + total) / (2 * total));
30482
+ out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30483
+ out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30484
+ out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30443
30485
  }
30444
30486
  }
30445
30487
  if (reducing) {
30446
30488
  halftoneSharpen(out, W, H);
30447
30489
  }
30490
+ if (mixed) {
30491
+ const source = out.slice();
30492
+ 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];
30493
+ for (let y = 0; y < H; y++) {
30494
+ for (let x = 0; x < W; x++) {
30495
+ for (let c = 0; c < 3; c++) {
30496
+ const v = at(x, y, c);
30497
+ const sum = W < SW ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30498
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, v + Math.floor((2 * v - sum) / 4)));
30499
+ }
30500
+ }
30501
+ }
30502
+ }
30503
+ if (adjust && adjustAfterSampling) adjust(out);
30448
30504
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30449
30505
  data[i * 4] = out[o];
30450
30506
  data[i * 4 + 1] = out[o + 1];
@@ -30476,7 +30532,18 @@ function patternOperand(rCtx, brush) {
30476
30532
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30477
30533
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30478
30534
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30479
- 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);
30480
30547
  if (px.width === 0 || px.height === 0) {
30481
30548
  return;
30482
30549
  }
@@ -30511,7 +30578,7 @@ function decodeSource(rCtx, req) {
30511
30578
  sy = image.height - sy - req.sh;
30512
30579
  }
30513
30580
  return {
30514
- decoded: { pixels: image, adjust },
30581
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30515
30582
  req: { ...req, sy }
30516
30583
  };
30517
30584
  }
@@ -37535,7 +37602,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37535
37602
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37536
37603
  c.closePath();
37537
37604
  },
37538
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37605
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, true),
37539
37606
  roundPen: true,
37540
37607
  fill: true,
37541
37608
  stroke: true
package/dist/browser.mjs CHANGED
@@ -10330,6 +10330,16 @@ function decodeImageCodecFallback(bytes) {
10330
10330
  if (!f || !validSize(f.image.descriptor.width, f.image.descriptor.height)) return null;
10331
10331
  const frame2 = (0, import_gifuct_js.decompressFrame)(f, gif.gct, true);
10332
10332
  const data = new Uint8ClampedArray(width * height * 4);
10333
+ const transparent = frame2.transparentIndex !== void 0;
10334
+ const background = transparent ? frame2.colorTable[frame2.transparentIndex] : gif.gct?.[gif.lsd.backgroundColorIndex];
10335
+ if (background) {
10336
+ for (let i = 0; i < data.length; i += 4) {
10337
+ data[i] = background[0];
10338
+ data[i + 1] = background[1];
10339
+ data[i + 2] = background[2];
10340
+ data[i + 3] = transparent ? 0 : 255;
10341
+ }
10342
+ }
10333
10343
  const { top, left, width: fw, height: fh } = frame2.dims;
10334
10344
  for (let y = 0; y < fh && top + y < height; y++) {
10335
10345
  const n = Math.min(fw, width - left);
@@ -10973,7 +10983,7 @@ function flattenBezierPath(pts) {
10973
10983
  }
10974
10984
  return out;
10975
10985
  }
10976
- function roundRectCorners(l, t, r, b, cw, ch) {
10986
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false) {
10977
10987
  const w = r - l;
10978
10988
  const h = b - t;
10979
10989
  let ew = Math.min(Math.abs(cw), w);
@@ -10985,7 +10995,9 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10985
10995
  const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10986
10996
  const topX = Math.floor(r - ew / 2);
10987
10997
  const hx = r - topX;
10988
- 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 ? Math.round(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];
10989
11001
  const mx = (x) => l + r - x;
10990
11002
  const my = (y) => t + b - y;
10991
11003
  return [
@@ -28485,9 +28497,9 @@ function boxFrame(box) {
28485
28497
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28486
28498
  return { l: 0, t: 0, r: w, b: h };
28487
28499
  }
28488
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28500
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28489
28501
  const f = boxFrame(box);
28490
- 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);
28491
28503
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28492
28504
  const pts = [];
28493
28505
  for (let i = 0; i < q.length; i += 2) {
@@ -28495,9 +28507,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28495
28507
  }
28496
28508
  return pts;
28497
28509
  }
28498
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28510
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28499
28511
  if (rectangle) return rectRasterPath(box, clockwise);
28500
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28512
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28501
28513
  const path = new GdiRasterPath();
28502
28514
  for (let c = 0; c < 4; c++) {
28503
28515
  const o = c * 8;
@@ -30075,6 +30087,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30075
30087
  var CA_NEGATIVE = 1;
30076
30088
  var CA_LOG_FILTER = 2;
30077
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
+ }
30078
30093
  function readColorAdjustment(view, off) {
30079
30094
  const ca = {
30080
30095
  flags: view.getUint16(off + 2, true),
@@ -30130,6 +30145,19 @@ function fromLightness(l) {
30130
30145
  }
30131
30146
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30132
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
+ }
30133
30161
  const black = ca.referenceBlack / 1e4;
30134
30162
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30135
30163
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30153,7 +30181,7 @@ function colorAdjustmentMapper(ca) {
30153
30181
  const out = (v, c) => {
30154
30182
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30155
30183
  if (log) {
30156
- v = Math.log10(1 + 9 * v);
30184
+ v = Math.log2(1 + 7 * v) / 3;
30157
30185
  }
30158
30186
  return Math.round(v * 255);
30159
30187
  };
@@ -30377,7 +30405,7 @@ function halftoneSharpen(px, w, h) {
30377
30405
  }
30378
30406
  }
30379
30407
  }
30380
- 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) {
30381
30409
  const W = Math.max(0, Math.round(Math.abs(dw)));
30382
30410
  const H = Math.max(0, Math.round(Math.abs(dh)));
30383
30411
  const SW = Math.round(Math.abs(sw));
@@ -30407,14 +30435,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30407
30435
  }
30408
30436
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30409
30437
  const reducing = W < SW && H < SH;
30410
- if (enlarging) {
30438
+ const mixed = W > SW && H < SH || W < SW && H > SH;
30439
+ if (enlarging && !directDib) {
30411
30440
  halftoneDespeckle(rect, SW, SH);
30412
30441
  }
30413
- if (adjust) {
30442
+ if (adjust && !adjustAfterSampling) {
30414
30443
  adjust(rect);
30415
30444
  }
30416
30445
  const cols = halftoneAxis(0, SW, W, flipX);
30417
30446
  const rows = halftoneAxis(0, SH, H, flipY);
30447
+ if (mixed) {
30448
+ const linear = (taps, src2, dst, reverse) => {
30449
+ if (dst <= src2) return;
30450
+ for (let i = 0; i < dst; i++) {
30451
+ const position = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
30452
+ const left = Math.floor(position), fraction = position - left;
30453
+ const at = (k) => reverse ? src2 - 1 - k : k;
30454
+ taps[i] = [[at(left), 1 - fraction], [at(Math.min(src2 - 1, left + 1)), fraction]];
30455
+ }
30456
+ };
30457
+ linear(cols, SW, W, flipX);
30458
+ linear(rows, SH, H, flipY);
30459
+ }
30418
30460
  const out = new Int32Array(W * H * 3);
30419
30461
  for (let y = 0; y < H; y++) {
30420
30462
  const ys = rows[y];
@@ -30435,14 +30477,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30435
30477
  }
30436
30478
  }
30437
30479
  const o = (y * W + x) * 3;
30438
- out[o] = Math.floor((2 * r + total) / (2 * total));
30439
- out[o + 1] = Math.floor((2 * g + total) / (2 * total));
30440
- out[o + 2] = Math.floor((2 * b + total) / (2 * total));
30480
+ out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30481
+ out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30482
+ out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30441
30483
  }
30442
30484
  }
30443
30485
  if (reducing) {
30444
30486
  halftoneSharpen(out, W, H);
30445
30487
  }
30488
+ if (mixed) {
30489
+ const source = out.slice();
30490
+ 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];
30491
+ for (let y = 0; y < H; y++) {
30492
+ for (let x = 0; x < W; x++) {
30493
+ for (let c = 0; c < 3; c++) {
30494
+ const v = at(x, y, c);
30495
+ const sum = W < SW ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30496
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, v + Math.floor((2 * v - sum) / 4)));
30497
+ }
30498
+ }
30499
+ }
30500
+ }
30501
+ if (adjust && adjustAfterSampling) adjust(out);
30446
30502
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30447
30503
  data[i * 4] = out[o];
30448
30504
  data[i * 4 + 1] = out[o + 1];
@@ -30474,7 +30530,18 @@ function patternOperand(rCtx, brush) {
30474
30530
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30475
30531
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30476
30532
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30477
- 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);
30478
30545
  if (px.width === 0 || px.height === 0) {
30479
30546
  return;
30480
30547
  }
@@ -30509,7 +30576,7 @@ function decodeSource(rCtx, req) {
30509
30576
  sy = image.height - sy - req.sh;
30510
30577
  }
30511
30578
  return {
30512
- decoded: { pixels: image, adjust },
30579
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30513
30580
  req: { ...req, sy }
30514
30581
  };
30515
30582
  }
@@ -37533,7 +37600,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37533
37600
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37534
37601
  c.closePath();
37535
37602
  },
37536
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37603
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, true),
37537
37604
  roundPen: true,
37538
37605
  fill: true,
37539
37606
  stroke: true
package/dist/index.js CHANGED
@@ -10332,6 +10332,16 @@ function decodeImageCodecFallback(bytes) {
10332
10332
  if (!f || !validSize(f.image.descriptor.width, f.image.descriptor.height)) return null;
10333
10333
  const frame2 = (0, import_gifuct_js.decompressFrame)(f, gif.gct, true);
10334
10334
  const data = new Uint8ClampedArray(width * height * 4);
10335
+ const transparent = frame2.transparentIndex !== void 0;
10336
+ const background = transparent ? frame2.colorTable[frame2.transparentIndex] : gif.gct?.[gif.lsd.backgroundColorIndex];
10337
+ if (background) {
10338
+ for (let i = 0; i < data.length; i += 4) {
10339
+ data[i] = background[0];
10340
+ data[i + 1] = background[1];
10341
+ data[i + 2] = background[2];
10342
+ data[i + 3] = transparent ? 0 : 255;
10343
+ }
10344
+ }
10335
10345
  const { top, left, width: fw, height: fh } = frame2.dims;
10336
10346
  for (let y = 0; y < fh && top + y < height; y++) {
10337
10347
  const n = Math.min(fw, width - left);
@@ -10976,7 +10986,7 @@ function flattenBezierPath(pts) {
10976
10986
  }
10977
10987
  return out;
10978
10988
  }
10979
- function roundRectCorners(l, t, r, b, cw, ch) {
10989
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false) {
10980
10990
  const w = r - l;
10981
10991
  const h = b - t;
10982
10992
  let ew = Math.min(Math.abs(cw), w);
@@ -10988,7 +10998,9 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10988
10998
  const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10989
10999
  const topX = Math.floor(r - ew / 2);
10990
11000
  const hx = r - topX;
10991
- 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 ? Math.round(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];
10992
11004
  const mx = (x) => l + r - x;
10993
11005
  const my = (y) => t + b - y;
10994
11006
  return [
@@ -28488,9 +28500,9 @@ function boxFrame(box) {
28488
28500
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28489
28501
  return { l: 0, t: 0, r: w, b: h };
28490
28502
  }
28491
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28503
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28492
28504
  const f = boxFrame(box);
28493
- 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);
28494
28506
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28495
28507
  const pts = [];
28496
28508
  for (let i = 0; i < q.length; i += 2) {
@@ -28498,9 +28510,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28498
28510
  }
28499
28511
  return pts;
28500
28512
  }
28501
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28513
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28502
28514
  if (rectangle) return rectRasterPath(box, clockwise);
28503
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28515
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28504
28516
  const path = new GdiRasterPath();
28505
28517
  for (let c = 0; c < 4; c++) {
28506
28518
  const o = c * 8;
@@ -30078,6 +30090,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30078
30090
  var CA_NEGATIVE = 1;
30079
30091
  var CA_LOG_FILTER = 2;
30080
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
+ }
30081
30096
  function readColorAdjustment(view, off) {
30082
30097
  const ca = {
30083
30098
  flags: view.getUint16(off + 2, true),
@@ -30133,6 +30148,19 @@ function fromLightness(l) {
30133
30148
  }
30134
30149
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30135
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
+ }
30136
30164
  const black = ca.referenceBlack / 1e4;
30137
30165
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30138
30166
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30156,7 +30184,7 @@ function colorAdjustmentMapper(ca) {
30156
30184
  const out = (v, c) => {
30157
30185
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30158
30186
  if (log) {
30159
- v = Math.log10(1 + 9 * v);
30187
+ v = Math.log2(1 + 7 * v) / 3;
30160
30188
  }
30161
30189
  return Math.round(v * 255);
30162
30190
  };
@@ -30380,7 +30408,7 @@ function halftoneSharpen(px, w, h) {
30380
30408
  }
30381
30409
  }
30382
30410
  }
30383
- 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) {
30384
30412
  const W = Math.max(0, Math.round(Math.abs(dw)));
30385
30413
  const H = Math.max(0, Math.round(Math.abs(dh)));
30386
30414
  const SW = Math.round(Math.abs(sw));
@@ -30410,14 +30438,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30410
30438
  }
30411
30439
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30412
30440
  const reducing = W < SW && H < SH;
30413
- if (enlarging) {
30441
+ const mixed = W > SW && H < SH || W < SW && H > SH;
30442
+ if (enlarging && !directDib) {
30414
30443
  halftoneDespeckle(rect, SW, SH);
30415
30444
  }
30416
- if (adjust) {
30445
+ if (adjust && !adjustAfterSampling) {
30417
30446
  adjust(rect);
30418
30447
  }
30419
30448
  const cols = halftoneAxis(0, SW, W, flipX);
30420
30449
  const rows = halftoneAxis(0, SH, H, flipY);
30450
+ if (mixed) {
30451
+ const linear = (taps, src2, dst, reverse) => {
30452
+ if (dst <= src2) return;
30453
+ for (let i = 0; i < dst; i++) {
30454
+ const position = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
30455
+ const left = Math.floor(position), fraction = position - left;
30456
+ const at = (k) => reverse ? src2 - 1 - k : k;
30457
+ taps[i] = [[at(left), 1 - fraction], [at(Math.min(src2 - 1, left + 1)), fraction]];
30458
+ }
30459
+ };
30460
+ linear(cols, SW, W, flipX);
30461
+ linear(rows, SH, H, flipY);
30462
+ }
30421
30463
  const out = new Int32Array(W * H * 3);
30422
30464
  for (let y = 0; y < H; y++) {
30423
30465
  const ys = rows[y];
@@ -30438,14 +30480,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30438
30480
  }
30439
30481
  }
30440
30482
  const o = (y * W + x) * 3;
30441
- out[o] = Math.floor((2 * r + total) / (2 * total));
30442
- out[o + 1] = Math.floor((2 * g + total) / (2 * total));
30443
- out[o + 2] = Math.floor((2 * b + total) / (2 * total));
30483
+ out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30484
+ out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30485
+ out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30444
30486
  }
30445
30487
  }
30446
30488
  if (reducing) {
30447
30489
  halftoneSharpen(out, W, H);
30448
30490
  }
30491
+ if (mixed) {
30492
+ const source = out.slice();
30493
+ 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];
30494
+ for (let y = 0; y < H; y++) {
30495
+ for (let x = 0; x < W; x++) {
30496
+ for (let c = 0; c < 3; c++) {
30497
+ const v = at(x, y, c);
30498
+ const sum = W < SW ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30499
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, v + Math.floor((2 * v - sum) / 4)));
30500
+ }
30501
+ }
30502
+ }
30503
+ }
30504
+ if (adjust && adjustAfterSampling) adjust(out);
30449
30505
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30450
30506
  data[i * 4] = out[o];
30451
30507
  data[i * 4 + 1] = out[o + 1];
@@ -30477,7 +30533,18 @@ function patternOperand(rCtx, brush) {
30477
30533
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30478
30534
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30479
30535
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30480
- 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);
30481
30548
  if (px.width === 0 || px.height === 0) {
30482
30549
  return;
30483
30550
  }
@@ -30512,7 +30579,7 @@ function decodeSource(rCtx, req) {
30512
30579
  sy = image.height - sy - req.sh;
30513
30580
  }
30514
30581
  return {
30515
- decoded: { pixels: image, adjust },
30582
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30516
30583
  req: { ...req, sy }
30517
30584
  };
30518
30585
  }
@@ -37536,7 +37603,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37536
37603
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37537
37604
  c.closePath();
37538
37605
  },
37539
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37606
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, true),
37540
37607
  roundPen: true,
37541
37608
  fill: true,
37542
37609
  stroke: true
package/dist/index.mjs CHANGED
@@ -10330,6 +10330,16 @@ function decodeImageCodecFallback(bytes) {
10330
10330
  if (!f || !validSize(f.image.descriptor.width, f.image.descriptor.height)) return null;
10331
10331
  const frame2 = (0, import_gifuct_js.decompressFrame)(f, gif.gct, true);
10332
10332
  const data = new Uint8ClampedArray(width * height * 4);
10333
+ const transparent = frame2.transparentIndex !== void 0;
10334
+ const background = transparent ? frame2.colorTable[frame2.transparentIndex] : gif.gct?.[gif.lsd.backgroundColorIndex];
10335
+ if (background) {
10336
+ for (let i = 0; i < data.length; i += 4) {
10337
+ data[i] = background[0];
10338
+ data[i + 1] = background[1];
10339
+ data[i + 2] = background[2];
10340
+ data[i + 3] = transparent ? 0 : 255;
10341
+ }
10342
+ }
10333
10343
  const { top, left, width: fw, height: fh } = frame2.dims;
10334
10344
  for (let y = 0; y < fh && top + y < height; y++) {
10335
10345
  const n = Math.min(fw, width - left);
@@ -10974,7 +10984,7 @@ function flattenBezierPath(pts) {
10974
10984
  }
10975
10985
  return out;
10976
10986
  }
10977
- function roundRectCorners(l, t, r, b, cw, ch) {
10987
+ function roundRectCorners(l, t, r, b, cw, ch, compatible = false) {
10978
10988
  const w = r - l;
10979
10989
  const h = b - t;
10980
10990
  let ew = Math.min(Math.abs(cw), w);
@@ -10986,7 +10996,9 @@ function roundRectCorners(l, t, r, b, cw, ch) {
10986
10996
  const hy = Math.floor(h / 2) - Math.floor((h - eh) / 2);
10987
10997
  const topX = Math.floor(r - ew / 2);
10988
10998
  const hx = r - topX;
10989
- 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 ? Math.round(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];
10990
11002
  const mx = (x) => l + r - x;
10991
11003
  const my = (y) => t + b - y;
10992
11004
  return [
@@ -28486,9 +28498,9 @@ function boxFrame(box) {
28486
28498
  const h = Math.round(Math.hypot(box.eyx, box.eyy));
28487
28499
  return { l: 0, t: 0, r: w, b: h };
28488
28500
  }
28489
- function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28501
+ function roundRectDeviceBeziers(box, cw, ch, clockwise = false, compatible = false) {
28490
28502
  const f = boxFrame(box);
28491
- 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);
28492
28504
  const map = frameMapper(box, f.l, f.t, f.r - f.l, f.b - f.t);
28493
28505
  const pts = [];
28494
28506
  for (let i = 0; i < q.length; i += 2) {
@@ -28496,9 +28508,9 @@ function roundRectDeviceBeziers(box, cw, ch, clockwise = false) {
28496
28508
  }
28497
28509
  return pts;
28498
28510
  }
28499
- function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false) {
28511
+ function roundRectRasterPath(box, cw, ch, clockwise = false, rectangle = false, compatible = false) {
28500
28512
  if (rectangle) return rectRasterPath(box, clockwise);
28501
- const pts = roundRectDeviceBeziers(box, cw, ch, clockwise);
28513
+ const pts = roundRectDeviceBeziers(box, cw, ch, clockwise, compatible);
28502
28514
  const path = new GdiRasterPath();
28503
28515
  for (let c = 0; c < 4; c++) {
28504
28516
  const o = c * 8;
@@ -30076,6 +30088,9 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
30076
30088
  var CA_NEGATIVE = 1;
30077
30089
  var CA_LOG_FILTER = 2;
30078
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
+ }
30079
30094
  function readColorAdjustment(view, off) {
30080
30095
  const ca = {
30081
30096
  flags: view.getUint16(off + 2, true),
@@ -30131,6 +30146,19 @@ function fromLightness(l) {
30131
30146
  }
30132
30147
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
30133
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
+ }
30134
30162
  const black = ca.referenceBlack / 1e4;
30135
30163
  const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
30136
30164
  const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
@@ -30154,7 +30182,7 @@ function colorAdjustmentMapper(ca) {
30154
30182
  const out = (v, c) => {
30155
30183
  v = clamp012(fromLightness(slope * lightness(clamp012(v)) + offset)) ** (1 / gammas[c]);
30156
30184
  if (log) {
30157
- v = Math.log10(1 + 9 * v);
30185
+ v = Math.log2(1 + 7 * v) / 3;
30158
30186
  }
30159
30187
  return Math.round(v * 255);
30160
30188
  };
@@ -30378,7 +30406,7 @@ function halftoneSharpen(px, w, h) {
30378
30406
  }
30379
30407
  }
30380
30408
  }
30381
- 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) {
30382
30410
  const W = Math.max(0, Math.round(Math.abs(dw)));
30383
30411
  const H = Math.max(0, Math.round(Math.abs(dh)));
30384
30412
  const SW = Math.round(Math.abs(sw));
@@ -30408,14 +30436,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30408
30436
  }
30409
30437
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
30410
30438
  const reducing = W < SW && H < SH;
30411
- if (enlarging) {
30439
+ const mixed = W > SW && H < SH || W < SW && H > SH;
30440
+ if (enlarging && !directDib) {
30412
30441
  halftoneDespeckle(rect, SW, SH);
30413
30442
  }
30414
- if (adjust) {
30443
+ if (adjust && !adjustAfterSampling) {
30415
30444
  adjust(rect);
30416
30445
  }
30417
30446
  const cols = halftoneAxis(0, SW, W, flipX);
30418
30447
  const rows = halftoneAxis(0, SH, H, flipY);
30448
+ if (mixed) {
30449
+ const linear = (taps, src2, dst, reverse) => {
30450
+ if (dst <= src2) return;
30451
+ for (let i = 0; i < dst; i++) {
30452
+ const position = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
30453
+ const left = Math.floor(position), fraction = position - left;
30454
+ const at = (k) => reverse ? src2 - 1 - k : k;
30455
+ taps[i] = [[at(left), 1 - fraction], [at(Math.min(src2 - 1, left + 1)), fraction]];
30456
+ }
30457
+ };
30458
+ linear(cols, SW, W, flipX);
30459
+ linear(rows, SH, H, flipY);
30460
+ }
30419
30461
  const out = new Int32Array(W * H * 3);
30420
30462
  for (let y = 0; y < H; y++) {
30421
30463
  const ys = rows[y];
@@ -30436,14 +30478,28 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust) {
30436
30478
  }
30437
30479
  }
30438
30480
  const o = (y * W + x) * 3;
30439
- out[o] = Math.floor((2 * r + total) / (2 * total));
30440
- out[o + 1] = Math.floor((2 * g + total) / (2 * total));
30441
- out[o + 2] = Math.floor((2 * b + total) / (2 * total));
30481
+ out[o] = mixed ? Math.ceil(r / total - 0.5) : Math.floor((2 * r + total) / (2 * total));
30482
+ out[o + 1] = mixed ? Math.ceil(g / total - 0.5) : Math.floor((2 * g + total) / (2 * total));
30483
+ out[o + 2] = mixed ? Math.ceil(b / total - 0.5) : Math.floor((2 * b + total) / (2 * total));
30442
30484
  }
30443
30485
  }
30444
30486
  if (reducing) {
30445
30487
  halftoneSharpen(out, W, H);
30446
30488
  }
30489
+ if (mixed) {
30490
+ const source = out.slice();
30491
+ 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];
30492
+ for (let y = 0; y < H; y++) {
30493
+ for (let x = 0; x < W; x++) {
30494
+ for (let c = 0; c < 3; c++) {
30495
+ const v = at(x, y, c);
30496
+ const sum = W < SW ? at(x - 1, y, c) + at(x + 1, y, c) : at(x, y - 1, c) + at(x, y + 1, c);
30497
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, v + Math.floor((2 * v - sum) / 4)));
30498
+ }
30499
+ }
30500
+ }
30501
+ }
30502
+ if (adjust && adjustAfterSampling) adjust(out);
30447
30503
  for (let i = 0, o = 0; i < W * H; i++, o += 3) {
30448
30504
  data[i * 4] = out[o];
30449
30505
  data[i * 4 + 1] = out[o + 1];
@@ -30475,7 +30531,18 @@ function patternOperand(rCtx, brush) {
30475
30531
  function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
30476
30532
  const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
30477
30533
  const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
30478
- 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);
30479
30546
  if (px.width === 0 || px.height === 0) {
30480
30547
  return;
30481
30548
  }
@@ -30510,7 +30577,7 @@ function decodeSource(rCtx, req) {
30510
30577
  sy = image.height - sy - req.sh;
30511
30578
  }
30512
30579
  return {
30513
- decoded: { pixels: image, adjust },
30580
+ decoded: { pixels: image, adjust, adjustAfterSampling: isChannelOnlyColorAdjustment(ca) },
30514
30581
  req: { ...req, sy }
30515
30582
  };
30516
30583
  }
@@ -37534,7 +37601,7 @@ function wmfRoundRect(p, l, t, r, b, w, h) {
37534
37601
  c.ellipse(cr.x + ex, cr.y + ey, ex, ey, 0, 2 * q, 3 * q);
37535
37602
  c.closePath();
37536
37603
  },
37537
- raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0),
37604
+ raster: () => roundRectRasterPath(fixBoxOf(box), cw, ch, clockwiseOf(p), w === 0 || h === 0, true),
37538
37605
  roundPen: true,
37539
37606
  fill: true,
37540
37607
  stroke: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.4.1",
3
+ "version": "4.4.3",
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",