emf-converter 4.15.2 → 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -21043,6 +21043,7 @@ function invert2(m) {
21043
21043
  }
21044
21044
  var COVERAGE_NUDGE_X = 1e-6;
21045
21045
  var COVERAGE_NUDGE_Y = 1e-9;
21046
+ var AXIS_ALIGNED_SHEAR = 5e-4;
21046
21047
  var SUBPIXEL_GRID = 16;
21047
21048
  function snapToDeviceGrid(spec) {
21048
21049
  const m = spec.toDevice;
@@ -21092,8 +21093,11 @@ function fadeAt(d, bilinear) {
21092
21093
  const i = Math.floor(x);
21093
21094
  return FAR_FADE[i] + (FAR_FADE[i + 1] - FAR_FADE[i]) * (x - i);
21094
21095
  }
21096
+ function ceilExtent(len) {
21097
+ return Math.ceil(len * (1 - 15e-8));
21098
+ }
21095
21099
  function nearSourceExtent(deviceLen, srcLen) {
21096
- const d = Math.ceil(deviceLen - 1e-6) - srcLen;
21100
+ const d = ceilExtent(deviceLen) - srcLen;
21097
21101
  return d >= -1 && d <= 1;
21098
21102
  }
21099
21103
  var BICUBIC_A = -0.5;
@@ -21122,7 +21126,7 @@ function axisFilter(kernel, scale, copy) {
21122
21126
  return { radius: 0.5, weight: (i, c) => Math.floor(c + 0.5) === i ? 1 : 0 };
21123
21127
  }
21124
21128
  }
21125
- function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge = { wrap: void 0, clamp: null }, integerBicubic = false) {
21129
+ function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge = { wrap: void 0, clamp: null }, integerBicubic = false, premult = false) {
21126
21130
  const [outer0, outerW, inner0, innerW] = verticalFirst ? [iu0, wu, iv0, wv] : [iv0, wv, iu0, wu];
21127
21131
  const [oMin, oMax, iMin, iMax] = verticalFirst ? [box.x0, box.x1, box.y0, box.y1] : [box.y0, box.y1, box.x0, box.x1];
21128
21132
  let r = 0;
@@ -21163,9 +21167,9 @@ function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge
21163
21167
  }
21164
21168
  const alpha = px[s + 3];
21165
21169
  const wa = wi * alpha / 255;
21166
- lr += integerBicubic ? wi * Math.round(alpha * px[s] / 255) : wa * px[s];
21167
- lg += integerBicubic ? wi * Math.round(alpha * px[s + 1] / 255) : wa * px[s + 1];
21168
- lb += integerBicubic ? wi * Math.round(alpha * px[s + 2] / 255) : wa * px[s + 2];
21170
+ lr += premult ? wi * px[s] : integerBicubic ? wi * Math.round(alpha * px[s] / 255) : wa * px[s];
21171
+ lg += premult ? wi * px[s + 1] : integerBicubic ? wi * Math.round(alpha * px[s + 1] / 255) : wa * px[s + 1];
21172
+ lb += premult ? wi * px[s + 2] : integerBicubic ? wi * Math.round(alpha * px[s + 2] / 255) : wa * px[s + 2];
21169
21173
  la += wi * alpha;
21170
21174
  }
21171
21175
  if (integerBicubic) {
@@ -21205,7 +21209,8 @@ function mirroredOrigin(o) {
21205
21209
  const f = o - Math.floor(o);
21206
21210
  return f === 0 ? o : Math.floor(o) + 1 - f;
21207
21211
  }
21208
- function resampleImage(rgba, width, height, spec, surface) {
21212
+ function resampleImage(rgba, width, height, spec, surface, options2 = {}) {
21213
+ const { plainRotated: plainRotatedPass = false, keepOrigin = false, premultSource = false, premultOut = false } = typeof options2 === "boolean" ? { plainRotated: options2 } : options2;
21209
21214
  if (spec.rightHalo && spec.rightHalo.length === height * 4) {
21210
21215
  const grown = new Uint8ClampedArray((width + 1) * height * 4);
21211
21216
  for (let y = 0; y < height; y++) {
@@ -21217,20 +21222,27 @@ function resampleImage(rgba, width, height, spec, surface) {
21217
21222
  if (spec.kernel === "nearest" && !spec.wrap) {
21218
21223
  return resampleNearest(rgba, width, height, spec, surface);
21219
21224
  }
21220
- let m = snapToDeviceGrid(spec);
21225
+ let m = keepOrigin ? spec.toDevice : snapToDeviceGrid(spec);
21221
21226
  let kernel = spec.kernel;
21222
21227
  const hq = kernel === "hq-bilinear" || kernel === "hq-bicubic";
21228
+ if (hq && (m[1] !== 0 || m[2] !== 0) && Math.abs(m[1]) <= AXIS_ALIGNED_SHEAR * Math.abs(m[0]) && Math.abs(m[2]) <= AXIS_ALIGNED_SHEAR * Math.abs(m[3])) {
21229
+ m = [m[0], 0, 0, m[3], m[4], m[5]];
21230
+ }
21223
21231
  const axisAligned = m[1] === 0 && m[2] === 0;
21224
- const integerBicubic = kernel === "bicubic" && axisAligned;
21225
- const unitBicubicCopy = integerBicubic && !spec.halfPixelOffset && m[0] === 1 && m[3] === 1 && m[4] - Math.round(m[4]) > -1 / 64 && m[4] - Math.round(m[4]) <= 1 / 64 && m[5] - Math.round(m[5]) > -1 / 64 && m[5] - Math.round(m[5]) <= 1 / 64;
21232
+ let plainRotated = plainRotatedPass;
21233
+ const unitBicubicCopy = kernel === "bicubic" && axisAligned && !spec.halfPixelOffset && m[0] === 1 && m[3] === 1 && m[4] - Math.round(m[4]) > -1 / 64 && m[4] - Math.round(m[4]) <= 1 / 64 && m[5] - Math.round(m[5]) > -1 / 64 && m[5] - Math.round(m[5]) <= 1 / 64;
21226
21234
  let farFade = false;
21227
21235
  let shiftX = 0;
21228
21236
  let shiftY = 0;
21229
21237
  if (hq && !axisAligned) {
21230
21238
  const extentU = Math.hypot(m[0], m[1]) * spec.srcW;
21231
21239
  const extentV = Math.hypot(m[2], m[3]) * spec.srcH;
21232
- if (nearSourceExtent(extentU, spec.srcW) || nearSourceExtent(extentV, spec.srcH)) {
21240
+ const near = nearSourceExtent(extentU, spec.srcW) || nearSourceExtent(extentV, spec.srcH);
21241
+ if (!spec.wrap) {
21242
+ return resampleRotatedTwoStage(rgba, width, height, spec, surface, m, kernel, near);
21243
+ } else if (near) {
21233
21244
  kernel = kernel === "hq-bicubic" ? "bicubic" : "bilinear";
21245
+ plainRotated = true;
21234
21246
  } else {
21235
21247
  farFade = true;
21236
21248
  if (spec.halfPixelOffset) {
@@ -21241,6 +21253,13 @@ function resampleImage(rgba, width, height, spec, surface) {
21241
21253
  }
21242
21254
  }
21243
21255
  }
21256
+ const integerBicubic = kernel === "bicubic" && (axisAligned || plainRotated);
21257
+ if (hq && axisAligned && !spec.wrap && !premultSource) {
21258
+ const phased = resampleHqAxisAligned(rgba, width, height, spec, surface, m, premultOut);
21259
+ if (phased !== void 0) {
21260
+ return phased;
21261
+ }
21262
+ }
21244
21263
  if (hq && axisAligned) {
21245
21264
  const ox = m[0] * spec.srcX + m[4];
21246
21265
  m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5]];
@@ -21350,7 +21369,7 @@ function resampleImage(rgba, width, height, spec, surface) {
21350
21369
  for (let t = iv0; t <= iv1; t++) {
21351
21370
  wv.push(integerBicubic ? Math.round(fv.weight(t, cv) * 65536) / 65536 : fv.weight(t, cv));
21352
21371
  }
21353
- return blendSeparable(rgba, width, box, iu0, wu, iv0, wv, kernel === "bicubic", edge, integerBicubic);
21372
+ return blendSeparable(rgba, width, box, iu0, wu, iv0, wv, kernel === "bicubic", edge, integerBicubic, premultSource);
21354
21373
  };
21355
21374
  let [r, g, b, a] = sampleAt(u, v);
21356
21375
  if (farFade && !spec.wrap && a > 0) {
@@ -21374,6 +21393,12 @@ function resampleImage(rgba, width, height, spec, surface) {
21374
21393
  b = Math.trunc(b);
21375
21394
  a = Math.trunc(a);
21376
21395
  if (a <= 0) continue;
21396
+ } else if (premultSource) {
21397
+ r = Math.round(r);
21398
+ g = Math.round(g);
21399
+ b = Math.round(b);
21400
+ a = Math.round(a);
21401
+ if (a <= 0) continue;
21377
21402
  }
21378
21403
  const alpha = Math.min(255, a);
21379
21404
  const dst = (j * w + i) * 4;
@@ -21385,6 +21410,244 @@ function resampleImage(rgba, width, height, spec, surface) {
21385
21410
  }
21386
21411
  return { x: bx0, y: by0, w, h, rgba: out };
21387
21412
  }
21413
+ var HQ_PHASES = 128;
21414
+ function resampleHqAxisAligned(rgba, width, height, spec, surface, m, premultOut) {
21415
+ const kernel = spec.kernel;
21416
+ const scaleU = m[0];
21417
+ const scaleV = m[3];
21418
+ if (!(scaleU >= 1 && scaleV >= 1) || spec.rightHalo) {
21419
+ return void 0;
21420
+ }
21421
+ const integral = (v) => Math.abs(v - Math.round(v)) < 1e-9;
21422
+ if (scaleU === 1 && scaleV === 1 && integral(m[4]) && integral(m[5])) {
21423
+ return void 0;
21424
+ }
21425
+ const shift = spec.halfPixelOffset ? 0.5 : 0;
21426
+ const left = m[0] * spec.srcX + m[4] - shift;
21427
+ const top = m[3] * spec.srcY + m[5] - shift;
21428
+ const right = left + m[0] * spec.srcW;
21429
+ const bottom = top + m[3] * spec.srcH;
21430
+ const xFirst = Math.ceil(left - COVERAGE_NUDGE_X);
21431
+ const xLast = Math.ceil(right - COVERAGE_NUDGE_X) - 1;
21432
+ const yFirst = Math.ceil(top - COVERAGE_NUDGE_Y);
21433
+ const yLast = Math.ceil(bottom - COVERAGE_NUDGE_Y) - 1;
21434
+ const cx0 = Math.max(0, xFirst);
21435
+ const cy0 = Math.max(0, yFirst);
21436
+ const cx1 = Math.min(surface.w - 1, xLast);
21437
+ const cy1 = Math.min(surface.h - 1, yLast);
21438
+ if (cx1 < cx0 || cy1 < cy0) {
21439
+ return null;
21440
+ }
21441
+ const w = cx1 - cx0 + 1;
21442
+ const h = cy1 - cy0 + 1;
21443
+ if (w * h > MAX_RESAMPLE_PIXELS) {
21444
+ return null;
21445
+ }
21446
+ const cubic = kernel === "hq-bicubic";
21447
+ const bin = cubic ? 0.5 : 1;
21448
+ const stepU = Math.round(65536 / scaleU);
21449
+ const stepV = Math.round(65536 / scaleV);
21450
+ const dx = Math.max(0, xFirst - left);
21451
+ const dy = Math.max(0, yFirst - top);
21452
+ const offsetU = dx > 1e-9 ? Math.round((1 - dx) * stepU) : 0;
21453
+ const offsetV = dy > 1e-9 ? Math.round(dy * stepV) : 0;
21454
+ const filterU = axisFilter(kernel, scaleU, false);
21455
+ const filterV = axisFilter(kernel, scaleV, false);
21456
+ const tapsFor = (filter, step, offset, origin, from, to) => {
21457
+ const taps = [];
21458
+ for (let k = from; k <= to; k++) {
21459
+ const phase = Math.floor((k * step + offset - 1) / (65536 / HQ_PHASES));
21460
+ const c = origin + (phase + bin) / HQ_PHASES;
21461
+ const first = Math.ceil(c - filter.radius);
21462
+ const last = Math.floor(c + filter.radius);
21463
+ const weights = [];
21464
+ for (let t = first; t <= last; t++) {
21465
+ weights.push(filter.weight(t, c));
21466
+ }
21467
+ taps.push({ first, weights });
21468
+ }
21469
+ return taps;
21470
+ };
21471
+ const colTaps = tapsFor(filterU, stepU, offsetU, spec.srcX, cx0 - xFirst, cx1 - xFirst);
21472
+ const rowTaps = tapsFor(filterV, stepV, offsetV, spec.srcY, cy0 - yFirst, cy1 - yFirst);
21473
+ let rowMin = Infinity;
21474
+ let rowMax = -Infinity;
21475
+ for (const t of rowTaps) {
21476
+ rowMin = Math.min(rowMin, t.first);
21477
+ rowMax = Math.max(rowMax, t.first + t.weights.length - 1);
21478
+ }
21479
+ rowMin = Math.max(0, rowMin);
21480
+ rowMax = Math.min(height - 1, rowMax);
21481
+ const rows = Math.max(0, rowMax - rowMin + 1);
21482
+ const inter = new Uint8Array(rows * w * 4);
21483
+ for (let r = 0; r < rows; r++) {
21484
+ const srcRow = (rowMin + r) * width;
21485
+ for (let i = 0; i < w; i++) {
21486
+ const t = colTaps[i];
21487
+ let pr = 0;
21488
+ let pg = 0;
21489
+ let pb = 0;
21490
+ let pa = 0;
21491
+ for (let q = 0; q < t.weights.length; q++) {
21492
+ const tx = t.first + q;
21493
+ if (tx < 0 || tx >= width) {
21494
+ continue;
21495
+ }
21496
+ const o = (srcRow + tx) * 4;
21497
+ const a = rgba[o + 3];
21498
+ if (a === 0) {
21499
+ continue;
21500
+ }
21501
+ const wq = t.weights[q];
21502
+ pr += wq * Math.round(rgba[o] * a / 255);
21503
+ pg += wq * Math.round(rgba[o + 1] * a / 255);
21504
+ pb += wq * Math.round(rgba[o + 2] * a / 255);
21505
+ pa += wq * a;
21506
+ }
21507
+ const alpha = Math.min(255, Math.max(0, Math.round(pa)));
21508
+ const d = (r * w + i) * 4;
21509
+ inter[d] = Math.min(alpha, Math.max(0, Math.round(pr)));
21510
+ inter[d + 1] = Math.min(alpha, Math.max(0, Math.round(pg)));
21511
+ inter[d + 2] = Math.min(alpha, Math.max(0, Math.round(pb)));
21512
+ inter[d + 3] = alpha;
21513
+ }
21514
+ }
21515
+ const out = new Uint8ClampedArray(w * h * 4);
21516
+ for (let j = 0; j < h; j++) {
21517
+ const t = rowTaps[j];
21518
+ for (let i = 0; i < w; i++) {
21519
+ let pr = 0;
21520
+ let pg = 0;
21521
+ let pb = 0;
21522
+ let pa = 0;
21523
+ for (let q = 0; q < t.weights.length; q++) {
21524
+ const ty = t.first + q - rowMin;
21525
+ if (ty < 0 || ty >= rows) {
21526
+ continue;
21527
+ }
21528
+ const o = (ty * w + i) * 4;
21529
+ const wq = t.weights[q];
21530
+ pr += wq * inter[o];
21531
+ pg += wq * inter[o + 1];
21532
+ pb += wq * inter[o + 2];
21533
+ pa += wq * inter[o + 3];
21534
+ }
21535
+ const alpha = Math.min(255, Math.max(0, Math.round(pa)));
21536
+ if (alpha === 0) {
21537
+ continue;
21538
+ }
21539
+ const d = (j * w + i) * 4;
21540
+ const k = premultOut ? 1 : 255 / alpha;
21541
+ out[d] = Math.min(alpha, Math.max(0, Math.round(pr))) * k;
21542
+ out[d + 1] = Math.min(alpha, Math.max(0, Math.round(pg))) * k;
21543
+ out[d + 2] = Math.min(alpha, Math.max(0, Math.round(pb))) * k;
21544
+ out[d + 3] = alpha;
21545
+ }
21546
+ }
21547
+ return { x: cx0, y: cy0, w, h, rgba: out, premultiplied: premultOut };
21548
+ }
21549
+ function resampleRotatedTwoStage(rgba, width, height, spec, surface, m, kernel, near) {
21550
+ const pad = 2;
21551
+ let img;
21552
+ let pw;
21553
+ let ph;
21554
+ let a;
21555
+ let b;
21556
+ let c;
21557
+ let d;
21558
+ let ex;
21559
+ let ey;
21560
+ const half = spec.halfPixelOffset;
21561
+ if (near) {
21562
+ pw = width + 2 * pad;
21563
+ ph = height + 2 * pad;
21564
+ img = new Uint8ClampedArray(pw * ph * 4);
21565
+ for (let y = 0; y < height; y++) {
21566
+ for (let x = 0; x < width; x++) {
21567
+ const s = (y * width + x) * 4;
21568
+ const al = rgba[s + 3];
21569
+ const o = ((y + pad) * pw + x + pad) * 4;
21570
+ img[o] = Math.round(rgba[s] * al / 255);
21571
+ img[o + 1] = Math.round(rgba[s + 1] * al / 255);
21572
+ img[o + 2] = Math.round(rgba[s + 2] * al / 255);
21573
+ img[o + 3] = al;
21574
+ }
21575
+ }
21576
+ [a, b, c, d] = m;
21577
+ ex = m[4] - (half ? 0.5 : 0);
21578
+ ey = m[5] - (half ? 0.5 : 0);
21579
+ } else {
21580
+ const W = ceilExtent(Math.hypot(m[0], m[1]) * spec.srcW);
21581
+ const H = ceilExtent(Math.hypot(m[2], m[3]) * spec.srcH);
21582
+ const kx = W / spec.srcW;
21583
+ const ky = H / spec.srcH;
21584
+ const pre = resampleImage(rgba, width, height, {
21585
+ srcX: spec.srcX,
21586
+ srcY: spec.srcY,
21587
+ srcW: spec.srcW,
21588
+ srcH: spec.srcH,
21589
+ toDevice: [kx, 0, 0, ky, -spec.srcX * kx, -spec.srcY * ky],
21590
+ kernel,
21591
+ halfPixelOffset: false
21592
+ }, { w: W, h: H }, { premultOut: true });
21593
+ if (!pre) {
21594
+ return null;
21595
+ }
21596
+ if (!pre.premultiplied) {
21597
+ for (let i = 0; i < pre.rgba.length; i += 4) {
21598
+ const al = pre.rgba[i + 3];
21599
+ pre.rgba[i] = Math.round(pre.rgba[i] * al / 255);
21600
+ pre.rgba[i + 1] = Math.round(pre.rgba[i + 1] * al / 255);
21601
+ pre.rgba[i + 2] = Math.round(pre.rgba[i + 2] * al / 255);
21602
+ }
21603
+ }
21604
+ pw = W + 2 * pad;
21605
+ ph = H + 2 * pad;
21606
+ img = new Uint8ClampedArray(pw * ph * 4);
21607
+ for (let y = 0; y < pre.h; y++) {
21608
+ for (let x = 0; x < pre.w; x++) {
21609
+ const dx = x + pre.x;
21610
+ const dy = y + pre.y;
21611
+ if (dx < W && dy < H) {
21612
+ img.set(pre.rgba.subarray((y * pre.w + x) * 4, (y * pre.w + x) * 4 + 4), ((dy + pad) * pw + dx + pad) * 4);
21613
+ }
21614
+ }
21615
+ }
21616
+ a = m[0] / kx;
21617
+ b = m[1] / kx;
21618
+ c = m[2] / ky;
21619
+ d = m[3] / ky;
21620
+ const ox = m[0] * spec.srcX + m[2] * spec.srcY + m[4];
21621
+ const oy = m[1] * spec.srcX + m[3] * spec.srcY + m[5];
21622
+ ex = half ? ox - 0.5 + 0.5 * (a + c) : ox;
21623
+ ey = half ? oy - 0.5 + 0.5 * (b + d) : oy;
21624
+ }
21625
+ const block = resampleImage(img, pw, ph, {
21626
+ srcX: 0,
21627
+ srcY: 0,
21628
+ srcW: pw,
21629
+ srcH: ph,
21630
+ toDevice: [a, b, c, d, ex - a * pad - c * pad, ey - b * pad - d * pad],
21631
+ kernel: kernel === "hq-bicubic" ? "bicubic" : "bilinear",
21632
+ halfPixelOffset: false
21633
+ }, surface, { plainRotated: true, keepOrigin: true, premultSource: true });
21634
+ if (!block) {
21635
+ return block;
21636
+ }
21637
+ const corners = [
21638
+ [spec.srcX, spec.srcY],
21639
+ [spec.srcX + spec.srcW, spec.srcY],
21640
+ [spec.srcX + spec.srcW, spec.srcY + spec.srcH],
21641
+ [spec.srcX, spec.srcY + spec.srcH]
21642
+ ].flatMap(([u, v]) => [toPlusFix(m[0] * u + m[2] * v + m[4]), toPlusFix(m[1] * u + m[3] * v + m[5])]);
21643
+ const coverage = rasterizePlusFill([corners], false, false, half, { x: block.x, y: block.y, w: block.w, h: block.h });
21644
+ for (let i = 0; i < block.w * block.h; i++) {
21645
+ if (!coverage[i]) {
21646
+ block.rgba.fill(0, i * 4, i * 4 + 4);
21647
+ }
21648
+ }
21649
+ return block;
21650
+ }
21388
21651
  var FIX16 = 65536;
21389
21652
  function resampleNearest(rgba, width, height, spec, surface) {
21390
21653
  const m = spec.toDevice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.15.2",
3
+ "version": "4.16.0",
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",