emf-converter 4.8.0 → 4.8.2

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
@@ -16404,8 +16404,8 @@ function midtoneGamma(t) {
16404
16404
  low = high;
16405
16405
  high = (high + 100) / 2;
16406
16406
  }
16407
- gamma = Math.abs(t) === 100 ? 10 : Math.min(10, gamma + (Math.abs(t) - low) / (high - low));
16408
- return Math.fround(t < 0 ? gamma : 1 / gamma);
16407
+ gamma = Math.abs(t) === 100 ? 10 : Math.min(10, truncF32(gamma + truncF32(truncF32(Math.abs(t) - low) / truncF32(high - low))));
16408
+ return t < 0 ? truncF32(gamma) : truncF32(1 / truncF32(gamma));
16409
16409
  }
16410
16410
  function brightnessContrastLut(brightness, contrast) {
16411
16411
  const gain = contrast >= 100 ? 1e6 : contrast >= 0 ? 100 / (100 - contrast) : (100 + contrast) / 100;
@@ -16422,6 +16422,23 @@ function curveAdjustmentLut(adjustment, intensity) {
16422
16422
  return nativeCurveLookup(adjustment, clamp(Math.trunc(intensity), min, max)).slice();
16423
16423
  }
16424
16424
  function levelsLut(highlight, midtone, shadow) {
16425
+ if (highlight !== shadow) {
16426
+ const forward = highlight > shadow;
16427
+ const lo = truncF32((forward ? shadow : highlight) * 255 / 100);
16428
+ const hi = truncF32((forward ? highlight : shadow) * 255 / 100);
16429
+ const scale = truncF32(1 / truncF32(hi - lo));
16430
+ const exponent2 = midtoneGamma(midtone);
16431
+ const lut = new Uint8Array(256);
16432
+ for (let v = 0; v < 256; v++) {
16433
+ let level = clamp(truncF32(truncF32(v - lo) * scale), 0, 1);
16434
+ if (midtone !== 0) {
16435
+ level = truncF32(level ** exponent2);
16436
+ }
16437
+ const out = forward ? truncF32(255 * level) : truncF32(255 - truncF32(255 * level));
16438
+ lut[v] = clamp(Math.floor(out + 0.5), 0, 255);
16439
+ }
16440
+ return lut;
16441
+ }
16425
16442
  const base = shadow * 255 / 100 - (highlight === shadow && shadow < 50 ? 1 : 0);
16426
16443
  const span = highlight === shadow ? 1 : (highlight - shadow) * 255 / 100;
16427
16444
  const exponent = midtoneGamma(midtone);
@@ -16454,14 +16471,6 @@ function applyColorMatrix(src, m) {
16454
16471
  }
16455
16472
  return out;
16456
16473
  }
16457
- function hslToRgb(h, s, l) {
16458
- const c = (1 - Math.abs(2 * l - 1)) * s;
16459
- const hp = (h % 360 + 360) % 360 / 60;
16460
- const x = c * (1 - Math.abs(hp % 2 - 1));
16461
- const m = l - c / 2;
16462
- const [r, g, b] = hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];
16463
- return [r + m, g + m, b + m];
16464
- }
16465
16474
  var HUE_SEXTANT = 43;
16466
16475
  var HUE_CIRCLE = 255;
16467
16476
  function gdipHue(r, g, b) {
@@ -16484,29 +16493,30 @@ function gdipHue(r, g, b) {
16484
16493
  function applyHueSaturationLightness(src, hue, saturation, lightness) {
16485
16494
  const out = new Uint8ClampedArray(src);
16486
16495
  const shift = Math.round(hue * HUE_CIRCLE / 360);
16487
- const sMul = 1 + saturation / 100;
16496
+ const satScale = 65536 + Math.floor(saturation * 65536 / 100);
16497
+ const lightOffset = Math.round(255 * Math.floor(lightness * 65536 / 100) / 65536);
16488
16498
  for (let i = 0; i < src.length; i += 4) {
16489
16499
  const r = src[i];
16490
16500
  const g = src[i + 1];
16491
16501
  const b = src[i + 2];
16492
16502
  const max = Math.max(r, g, b);
16493
16503
  const min = Math.min(r, g, b);
16494
- const l = clamp(Math.round(((max + min >> 1) * 100 + 255 * lightness) / 100), 0, 255);
16504
+ const l = clamp((max + min >> 1) + lightOffset, 0, 255);
16495
16505
  if (max === min) {
16496
16506
  out[i] = out[i + 1] = out[i + 2] = l;
16497
16507
  continue;
16498
16508
  }
16499
16509
  const sum = max + min;
16500
- const s = Math.fround(clamp((max - min) / (sum <= 255 ? sum : 510 - sum) * sMul, 0, 1));
16501
- const hi = l <= 127 ? l * (1 + s) : l + s * (255 - l);
16502
- const m1 = l <= 127 ? 2 * l - Math.trunc(hi) : Math.round(2 * l - hi);
16503
- const m2 = 2 * l - m1;
16510
+ const sat2 = clamp(Math.round(Math.floor((max - min) * 255 / (sum <= 255 ? sum : 510 - sum)) * satScale / 65536), 0, 255);
16511
+ const hi = l <= 127 ? Math.floor(l * (255 + sat2) / 255) : l + sat2 - Math.floor(l * sat2 / 255);
16512
+ const m1 = 2 * l - hi;
16513
+ const m2 = hi;
16504
16514
  const index = ((gdipHue(r, g, b) + shift) % HUE_CIRCLE + HUE_CIRCLE) % HUE_CIRCLE;
16505
16515
  const q = index + Math.floor((index + 41) / 85);
16506
16516
  const sextant = Math.min(5, Math.floor(q / HUE_SEXTANT));
16507
- const f = (q - sextant * HUE_SEXTANT) / HUE_SEXTANT;
16508
- const up = Math.trunc(m1 + (m2 - m1) * f);
16509
- const down = Math.trunc(m1 + (m2 - m1) * (1 - f));
16517
+ const position = q - sextant * HUE_SEXTANT;
16518
+ const up = m1 + Math.floor((m2 - m1) * position / HUE_SEXTANT);
16519
+ const down = m1 + Math.floor((m2 - m1) * (HUE_SEXTANT - position) / HUE_SEXTANT);
16510
16520
  const [nr, ng, nb] = [
16511
16521
  [m2, up, m1],
16512
16522
  [down, m2, m1],
@@ -16521,28 +16531,40 @@ function applyHueSaturationLightness(src, hue, saturation, lightness) {
16521
16531
  }
16522
16532
  return out;
16523
16533
  }
16524
- var LUMA_709 = [0.2126, 0.7152, 0.0722];
16525
- var TINT_CHROMA_SCALE = 0.985;
16526
16534
  function applyTint(src, hue, amount) {
16527
16535
  const index = (Math.round(hue * 255 / 360) + 256) % 256;
16528
16536
  const q = (index + Math.floor((index + 41) / 85)) % 258;
16529
- const tint = hslToRgb(q * 60 / 43, 1, 0.5);
16530
- const ty = tint[0] * LUMA_709[0] + tint[1] * LUMA_709[1] + tint[2] * LUMA_709[2];
16531
- const chroma = tint.map((c) => (c - ty) * TINT_CHROMA_SCALE);
16532
- const a = amount / 100;
16537
+ const sextant = Math.min(5, Math.floor(q / HUE_SEXTANT));
16538
+ const position = q - sextant * HUE_SEXTANT;
16539
+ const up = Math.floor(TINT_FULL * position / HUE_SEXTANT);
16540
+ const down = Math.floor(TINT_FULL * (HUE_SEXTANT - position) / HUE_SEXTANT);
16541
+ const [tr, tg, tb] = [
16542
+ [TINT_FULL, up, 0],
16543
+ [down, TINT_FULL, 0],
16544
+ [0, TINT_FULL, up],
16545
+ [0, down, TINT_FULL],
16546
+ [up, 0, TINT_FULL],
16547
+ [TINT_FULL, 0, down]
16548
+ ][sextant];
16549
+ const weight = Math.sign(amount) * Math.floor((255 * Math.abs(amount) + 49) / 100);
16550
+ const original = 255 - weight;
16533
16551
  const out = new Uint8ClampedArray(src);
16534
16552
  for (let i = 0; i < src.length; i += 4) {
16535
16553
  const r = src[i];
16536
16554
  const g = src[i + 1];
16537
16555
  const b = src[i + 2];
16538
- const y = r * LUMA_709[0] + g * LUMA_709[1] + b * LUMA_709[2];
16539
- const v = Math.max(r, g, b);
16540
- out[i] = Math.round((1 - a) * r + a * (y + v * chroma[0]));
16541
- out[i + 1] = Math.round((1 - a) * g + a * (y + v * chroma[1]));
16542
- out[i + 2] = Math.round((1 - a) * b + a * (y + v * chroma[2]));
16556
+ const u = weight * Math.max(r, g, b) >> 8;
16557
+ const x0 = original * r + tr * u;
16558
+ const x1 = original * g + tg * u;
16559
+ const x2 = original * b + tb * u;
16560
+ const base = 54 * r + 183 * g + 19 * b - (54 * (x0 >> 8) + 183 * (x1 >> 8) + 19 * (x2 >> 8));
16561
+ out[i] = base + x0 >> 8;
16562
+ out[i + 1] = base + x1 >> 8;
16563
+ out[i + 2] = base + x2 >> 8;
16543
16564
  }
16544
16565
  return out;
16545
16566
  }
16567
+ var TINT_FULL = 254;
16546
16568
  function applyRedEyeCorrection(src, width, height, areas) {
16547
16569
  const out = new Uint8ClampedArray(src);
16548
16570
  for (const a of areas) {
@@ -20542,8 +20564,7 @@ function resampleImage(rgba, width, height, spec, surface) {
20542
20564
  const axisAligned = m[1] === 0 && m[2] === 0;
20543
20565
  if (hq && axisAligned) {
20544
20566
  const ox = m[0] * spec.srcX + m[4];
20545
- const oy = m[3] * spec.srcY + m[5];
20546
- m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5] + mirroredOrigin(oy) - oy];
20567
+ m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5]];
20547
20568
  }
20548
20569
  const inv2 = invert2(m);
20549
20570
  if (!inv2) {
@@ -23356,6 +23377,21 @@ var nestedReplayer = null;
23356
23377
  function registerNestedMetafileReplayer(fn) {
23357
23378
  nestedReplayer = fn;
23358
23379
  }
23380
+ function inclusiveNestedMapping(toDevice, srcX, srcY, srcW, srcH) {
23381
+ const [a, b, c, d, e, f] = toDevice;
23382
+ const lengthU = Math.hypot(a, b) * srcW;
23383
+ const lengthV = Math.hypot(c, d) * srcH;
23384
+ if (!(srcW > 1 && srcH > 1 && lengthU > 1 && lengthV > 1)) {
23385
+ return toDevice;
23386
+ }
23387
+ const ku = (lengthU - 1) / lengthU * (srcW / (srcW - 1));
23388
+ const kv = (lengthV - 1) / lengthV * (srcH / (srcH - 1));
23389
+ const na = a * ku;
23390
+ const nb = b * ku;
23391
+ const nc = c * kv;
23392
+ const nd = d * kv;
23393
+ return [na, nb, nc, nd, e + (a - na) * srcX + (c - nc) * srcY, f + (b - nb) * srcX + (d - nd) * srcY];
23394
+ }
23359
23395
  function drawNestedMetafile(rCtx, img, cached, source) {
23360
23396
  const depth = rCtx.nestingDepth ?? 0;
23361
23397
  if (!nestedReplayer || !source || !img.data || source.unit !== UNIT_PIXEL || depth >= MAX_NESTED_METAFILE_DEPTH) {
@@ -23369,9 +23405,12 @@ function drawNestedMetafile(rCtx, img, cached, source) {
23369
23405
  if (!size) {
23370
23406
  return false;
23371
23407
  }
23372
- const toCanvas = mulMatrix(
23373
- [1, 0, 0, 1, -0.5, -0.5],
23374
- mulMatrix(mulMatrix(plusWorldMatrix(rCtx), source.toWorld(srcX, srcY, srcW, srcH)), [1, 0, 0, 1, 0.5, 0.5])
23408
+ const toCanvas = inclusiveNestedMapping(
23409
+ mulMatrix(plusWorldMatrix(rCtx), source.toWorld(srcX, srcY, srcW, srcH)),
23410
+ srcX,
23411
+ srcY,
23412
+ srcW,
23413
+ srcH
23375
23414
  );
23376
23415
  const bytes = img.data.slice(0);
23377
23416
  const caches = cached && cached.kind === "metafile" ? cached.caches : void 0;
@@ -29190,9 +29229,9 @@ function perpendicularVectors(width, dx0, dy0) {
29190
29229
  const vx = rounded[0], vy = rounded[1];
29191
29230
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29192
29231
  }
29193
- function squareExtension(width, dx, dy) {
29232
+ function squareExtension(width, dx, dy, scale = 1) {
29194
29233
  const len = Math.hypot(dx, dy);
29195
- const r = width / 2;
29234
+ const r = width / 2 * scale;
29196
29235
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29197
29236
  }
29198
29237
  function turnSign(ax, ay, bx, by) {
@@ -29231,7 +29270,7 @@ var Outliner = class {
29231
29270
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29232
29271
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29233
29272
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29234
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29273
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29235
29274
  }
29236
29275
  push(p, v) {
29237
29276
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29326,7 +29365,7 @@ var Outliner = class {
29326
29365
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29327
29366
  }
29328
29367
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29329
- cap(p, s, start) {
29368
+ cap(p, s, start, scale = 1) {
29330
29369
  const from = start ? "L" : "R";
29331
29370
  const to = start ? "R" : "L";
29332
29371
  const { cap } = this.opts;
@@ -29344,7 +29383,8 @@ var Outliner = class {
29344
29383
  const sv = this.capSide(s, from);
29345
29384
  const ev = this.capSide(s, to);
29346
29385
  if (cap === "square") {
29347
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29386
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29387
+ const e = start ? [-e0[0], -e0[1]] : e0;
29348
29388
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29349
29389
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29350
29390
  } else {
@@ -29440,7 +29480,7 @@ var Outliner = class {
29440
29480
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29441
29481
  * `draws` (curve end tangents) picks draw vertices only.
29442
29482
  */
29443
- open(P, dirs, draws, curves) {
29483
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29444
29484
  if (P.length < 2 && !dirs?.[0]) {
29445
29485
  this.dot(P[0]);
29446
29486
  return;
@@ -29451,12 +29491,12 @@ var Outliner = class {
29451
29491
  }
29452
29492
  if (segs2.length === 0) {
29453
29493
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29454
- this.cap(P[0], s, true);
29455
- this.cap(P[0], s, false);
29494
+ this.cap(P[0], s, true, startScale);
29495
+ this.cap(P[0], s, false, endScale);
29456
29496
  this.flush();
29457
29497
  return;
29458
29498
  }
29459
- this.cap(P[0], segs2[0], true);
29499
+ this.cap(P[0], segs2[0], true, startScale);
29460
29500
  for (let i = 0; i + 1 < segs2.length; i++) {
29461
29501
  const s = segs2[i];
29462
29502
  const t = segs2[i + 1];
@@ -29467,7 +29507,7 @@ var Outliner = class {
29467
29507
  this.join(P[i + 1], s, t, "R", tr < 0);
29468
29508
  }
29469
29509
  }
29470
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29510
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29471
29511
  for (let i = segs2.length - 1; i > 0; i--) {
29472
29512
  const s = segs2[i];
29473
29513
  const t = segs2[i - 1];
@@ -29523,7 +29563,7 @@ var Outliner = class {
29523
29563
  this.flush();
29524
29564
  }
29525
29565
  };
29526
- function dashPieces(P, tangents, pattern, shorten) {
29566
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29527
29567
  const out = [];
29528
29568
  const dashes = [];
29529
29569
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29538,12 +29578,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29538
29578
  let idx = 0;
29539
29579
  let left = dashes[0];
29540
29580
  let on = true;
29541
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29581
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29542
29582
  for (let i = 0; i + 1 < P.length; i++) {
29543
29583
  const [x0, y0] = P[i];
29544
29584
  const [x1, y1] = P[i + 1];
29545
29585
  const dir = [x1 - x0, y1 - y0];
29546
- const len = Math.hypot(dir[0], dir[1]);
29586
+ const real = Math.hypot(dir[0], dir[1]);
29587
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29588
+ if (len === 0) {
29589
+ if (on && cur) {
29590
+ cur.pts.push(P[i + 1]);
29591
+ cur.dirs.push(dir);
29592
+ cur.draws.push(tangents[i]);
29593
+ cur.curves.push(curveFlags[i]);
29594
+ }
29595
+ continue;
29596
+ }
29547
29597
  let t = 0;
29548
29598
  while (len - t > left || left === 0 && on) {
29549
29599
  t += left;
@@ -29552,10 +29602,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29552
29602
  cur.pts.push(q);
29553
29603
  cur.dirs.push(dir);
29554
29604
  cur.draws.push(tangents[i]);
29605
+ cur.curves.push(curveFlags[i]);
29606
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29555
29607
  out.push(cur);
29556
29608
  cur = null;
29557
29609
  } else {
29558
- cur = { pts: [q], dirs: [], draws: [] };
29610
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29559
29611
  }
29560
29612
  on = !on;
29561
29613
  idx = (idx + 1) % dashes.length;
@@ -29566,9 +29618,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29566
29618
  cur.pts.push(P[i + 1]);
29567
29619
  cur.dirs.push(dir);
29568
29620
  cur.draws.push(tangents[i]);
29621
+ cur.curves.push(curveFlags[i]);
29622
+ cur.endScale = real / len;
29569
29623
  }
29570
29624
  }
29571
29625
  if (on && cur && cur.dirs.length > 0) {
29626
+ cur.endScale = 1;
29572
29627
  out.push(cur);
29573
29628
  }
29574
29629
  return out;
@@ -29603,10 +29658,13 @@ function widenPath(path, opts) {
29603
29658
  if (dashed) {
29604
29659
  const run = closed ? [...P, P[0]] : P;
29605
29660
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29606
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29661
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29662
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29663
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29607
29664
  const pts = [piece.pts[0]];
29608
29665
  const pdirs = [];
29609
29666
  const pdraws = [];
29667
+ const pcurves = [];
29610
29668
  for (let i = 1; i < piece.pts.length; i++) {
29611
29669
  const q = piece.pts[i];
29612
29670
  const last = pts[pts.length - 1];
@@ -29614,9 +29672,10 @@ function widenPath(path, opts) {
29614
29672
  pts.push(q);
29615
29673
  pdirs.push(piece.dirs[i - 1]);
29616
29674
  pdraws.push(piece.draws[i - 1]);
29675
+ pcurves.push(piece.curves[i - 1]);
29617
29676
  }
29618
29677
  }
29619
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29678
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
29620
29679
  }
29621
29680
  } else if (closed && P.length >= 3) {
29622
29681
  outliner.closed(P, dirs, curves);
@@ -29824,7 +29883,10 @@ function penWidenOptions(rCtx, opts = {}) {
29824
29883
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29825
29884
  miterLimit: state.miterLimit ?? 10,
29826
29885
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29827
- shortenDashes: (flags & 15) !== 7
29886
+ shortenDashes: (flags & 15) !== 7,
29887
+ // One logical unit per device pixel, unrotated: GDI measures dash
29888
+ // segments in whole pixels.
29889
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
29828
29890
  };
29829
29891
  }
29830
29892
  function hatchBackgroundMask(hatch) {