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