emf-converter 4.7.0 → 4.8.1

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.mjs CHANGED
@@ -11773,19 +11773,73 @@ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = fa
11773
11773
  ];
11774
11774
  }
11775
11775
  var TRIG_STEP = 2 * Math.PI / 128;
11776
- function tableTrig(fn) {
11776
+ var TRIG_NODES = [
11777
+ 1,
11778
+ 0.99879566,
11779
+ 0.99518639,
11780
+ 0.98917961,
11781
+ 0.98079146,
11782
+ 0.97003981,
11783
+ 0.95695228,
11784
+ 0.94155978,
11785
+ 0.92389844,
11786
+ 0.90401145,
11787
+ 0.88194619,
11788
+ 0.85775575,
11789
+ 0.83149879,
11790
+ 0.80323775,
11791
+ 0.77303903,
11792
+ 0.74098864,
11793
+ 0.70710648,
11794
+ 0.67151859,
11795
+ 0.63435836,
11796
+ 0.59565849,
11797
+ 0.55552672,
11798
+ 0.51405765,
11799
+ 0.47135039,
11800
+ 0.42750875,
11801
+ 0.38263811,
11802
+ 0.33684632,
11803
+ 0.29024443,
11804
+ 0.24294646,
11805
+ 0.19505974,
11806
+ 0.14670808,
11807
+ 0.0980012,
11808
+ 0.04906089,
11809
+ 0
11810
+ ];
11811
+ var TRIG_TABLE = (() => {
11812
+ const cos = [];
11813
+ const sin = [];
11814
+ for (let k = 0; k <= 128; k++) {
11815
+ const q = k >> 5;
11816
+ const i = k & 31;
11817
+ const c = TRIG_NODES[i];
11818
+ const s = TRIG_NODES[32 - i];
11819
+ if (k === 128) {
11820
+ cos.push(1);
11821
+ sin.push(0);
11822
+ continue;
11823
+ }
11824
+ cos.push([c, -s, -c, s][q]);
11825
+ sin.push([s, c, -s, -c][q]);
11826
+ }
11827
+ return { cos, sin };
11828
+ })();
11829
+ function tableTrig(table) {
11777
11830
  return (a) => {
11778
11831
  let b = a % (2 * Math.PI);
11779
11832
  if (b < 0) {
11780
11833
  b += 2 * Math.PI;
11781
11834
  }
11782
- const i = Math.floor(b / TRIG_STEP);
11783
- const f = b / TRIG_STEP - i;
11784
- return fn(i * TRIG_STEP) * (1 - f) + fn((i + 1) * TRIG_STEP) * f;
11835
+ const x = b / TRIG_STEP;
11836
+ const i = Math.min(127, Math.floor(x));
11837
+ const f = x - i;
11838
+ return table[i] * (1 - f) + table[i + 1] * f;
11785
11839
  };
11786
11840
  }
11787
- var tableCos = tableTrig(Math.cos);
11788
- var tableSin = tableTrig(Math.sin);
11841
+ var tableCos = tableTrig(TRIG_TABLE.cos);
11842
+ var tableSin = tableTrig(TRIG_TABLE.sin);
11789
11843
  function angleArcPieces(startDeg, sweepDeg) {
11790
11844
  const out = [];
11791
11845
  const arc = (from, to) => {
@@ -11856,6 +11910,38 @@ function angleArcFix(box, startDeg, sweepDeg) {
11856
11910
  }
11857
11911
  return out;
11858
11912
  }
11913
+ var SMALL_SWEEP = 3 * Math.PI / 180;
11914
+ function anchoredPiece(boundary, free, rx, ry, cx, cy, smallArc) {
11915
+ const sweep = Math.abs(free - boundary);
11916
+ const small = smallArc;
11917
+ const cos = small ? Math.cos : tableCos;
11918
+ const sin = small ? Math.sin : tableSin;
11919
+ const anchor = [Math.round(cx + rx * Math.cos(boundary)), Math.round(cy - ry * Math.sin(boundary))];
11920
+ const point = [cx + rx * cos(free), cy - ry * sin(free)];
11921
+ let t;
11922
+ let kappa;
11923
+ if (small) {
11924
+ t = Math.tan(sweep / 2);
11925
+ kappa = 2 / 3 * (1 - Math.tan(sweep / 4) ** 2);
11926
+ } else {
11927
+ const c = tableCos(sweep);
11928
+ const sn = tableSin(sweep);
11929
+ t = (1 - c) / sn;
11930
+ const q = t / (1 + Math.sqrt(1 + t * t));
11931
+ kappa = 2 / 3 * (1 - q * q);
11932
+ }
11933
+ const dir = free > boundary ? 1 : -1;
11934
+ const tx = -Math.round(Math.sin(boundary));
11935
+ const ty = -Math.round(Math.cos(boundary));
11936
+ const length = tx !== 0 ? rx : ry;
11937
+ const X = [anchor[0] + dir * tx * length * t, anchor[1] + dir * ty * length * t];
11938
+ return {
11939
+ anchor,
11940
+ free: point,
11941
+ cAnchor: [anchor[0] + kappa * (X[0] - anchor[0]), anchor[1] + kappa * (X[1] - anchor[1])],
11942
+ cFree: [point[0] + kappa * (X[0] - point[0]), point[1] + kappa * (X[1] - point[1])]
11943
+ };
11944
+ }
11859
11945
  function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11860
11946
  const w = r - l;
11861
11947
  const h = b - t;
@@ -11894,6 +11980,21 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11894
11980
  continue;
11895
11981
  }
11896
11982
  const e = s > 0 ? Math.min(next, a1) : Math.max(next, a1);
11983
+ const endOnBoundary = Math.abs(e / Q - Math.round(e / Q)) < 1e-9;
11984
+ if (onBoundary !== endOnBoundary) {
11985
+ const piece = anchoredPiece(onBoundary ? a : e, onBoundary ? e : a, rx, ry, cx, cy, Math.abs(a1 - a0) <= SMALL_SWEEP);
11986
+ if (onBoundary) {
11987
+ out.push(...[piece.cAnchor, piece.cFree, piece.free].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11988
+ } else {
11989
+ if (out.length === 2) {
11990
+ out[0] = Math.round(piece.free[0]);
11991
+ out[1] = Math.round(piece.free[1]);
11992
+ }
11993
+ out.push(...[piece.cFree, piece.cAnchor, piece.anchor].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11994
+ }
11995
+ a = e;
11996
+ continue;
11997
+ }
11897
11998
  const k = 4 / 3 * Math.tan((e - a) / 4);
11898
11999
  out.push(
11899
12000
  Math.round(px(a) - k * rx * tableSin(a)),
@@ -11988,6 +12089,10 @@ var GdiRasterPath = class {
11988
12089
  };
11989
12090
  const t0 = tangent(x0, y0, c1x, c1y, c2x, c2y, x3, y3);
11990
12091
  const t1 = tangent(c2x, c2y, x3, y3, x3, y3, x3, y3) ?? tangent(c1x, c1y, x3, y3, x3, y3, x3, y3) ?? tangent(x0, y0, x3, y3, x3, y3, x3, y3);
12092
+ f.curveSegs ?? (f.curveSegs = /* @__PURE__ */ new Set());
12093
+ for (let k = first; k <= last; k++) {
12094
+ f.curveSegs.add(k);
12095
+ }
11991
12096
  f.tangents ?? (f.tangents = /* @__PURE__ */ new Map());
11992
12097
  if (t0) f.tangents.set(first, t0);
11993
12098
  if (t1) f.tangents.set(last, t1);
@@ -12021,7 +12126,7 @@ var GdiRasterPath = class {
12021
12126
  /** Appends every figure of `other`. */
12022
12127
  append(other) {
12023
12128
  for (const f of other.figures) {
12024
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
12129
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), curveSegs: f.curveSegs && new Set(f.curveSegs), roundWiden: f.roundWiden });
12025
12130
  }
12026
12131
  for (let i = 0; i < other.getPath.types.length; i++) {
12027
12132
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -16296,8 +16401,8 @@ function midtoneGamma(t) {
16296
16401
  low = high;
16297
16402
  high = (high + 100) / 2;
16298
16403
  }
16299
- gamma = Math.abs(t) === 100 ? 10 : Math.min(10, gamma + (Math.abs(t) - low) / (high - low));
16300
- return Math.fround(t < 0 ? gamma : 1 / gamma);
16404
+ gamma = Math.abs(t) === 100 ? 10 : Math.min(10, truncF32(gamma + truncF32(truncF32(Math.abs(t) - low) / truncF32(high - low))));
16405
+ return t < 0 ? truncF32(gamma) : truncF32(1 / truncF32(gamma));
16301
16406
  }
16302
16407
  function brightnessContrastLut(brightness, contrast) {
16303
16408
  const gain = contrast >= 100 ? 1e6 : contrast >= 0 ? 100 / (100 - contrast) : (100 + contrast) / 100;
@@ -16314,6 +16419,23 @@ function curveAdjustmentLut(adjustment, intensity) {
16314
16419
  return nativeCurveLookup(adjustment, clamp(Math.trunc(intensity), min, max)).slice();
16315
16420
  }
16316
16421
  function levelsLut(highlight, midtone, shadow) {
16422
+ if (highlight !== shadow) {
16423
+ const forward = highlight > shadow;
16424
+ const lo = truncF32((forward ? shadow : highlight) * 255 / 100);
16425
+ const hi = truncF32((forward ? highlight : shadow) * 255 / 100);
16426
+ const scale = truncF32(1 / truncF32(hi - lo));
16427
+ const exponent2 = midtoneGamma(midtone);
16428
+ const lut = new Uint8Array(256);
16429
+ for (let v = 0; v < 256; v++) {
16430
+ let level = clamp(truncF32(truncF32(v - lo) * scale), 0, 1);
16431
+ if (midtone !== 0) {
16432
+ level = truncF32(level ** exponent2);
16433
+ }
16434
+ const out = forward ? truncF32(255 * level) : truncF32(255 - truncF32(255 * level));
16435
+ lut[v] = clamp(Math.floor(out + 0.5), 0, 255);
16436
+ }
16437
+ return lut;
16438
+ }
16317
16439
  const base = shadow * 255 / 100 - (highlight === shadow && shadow < 50 ? 1 : 0);
16318
16440
  const span = highlight === shadow ? 1 : (highlight - shadow) * 255 / 100;
16319
16441
  const exponent = midtoneGamma(midtone);
@@ -16346,14 +16468,6 @@ function applyColorMatrix(src, m) {
16346
16468
  }
16347
16469
  return out;
16348
16470
  }
16349
- function hslToRgb(h, s, l) {
16350
- const c = (1 - Math.abs(2 * l - 1)) * s;
16351
- const hp = (h % 360 + 360) % 360 / 60;
16352
- const x = c * (1 - Math.abs(hp % 2 - 1));
16353
- const m = l - c / 2;
16354
- 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];
16355
- return [r + m, g + m, b + m];
16356
- }
16357
16471
  var HUE_SEXTANT = 43;
16358
16472
  var HUE_CIRCLE = 255;
16359
16473
  function gdipHue(r, g, b) {
@@ -16376,29 +16490,30 @@ function gdipHue(r, g, b) {
16376
16490
  function applyHueSaturationLightness(src, hue, saturation, lightness) {
16377
16491
  const out = new Uint8ClampedArray(src);
16378
16492
  const shift = Math.round(hue * HUE_CIRCLE / 360);
16379
- const sMul = 1 + saturation / 100;
16493
+ const satScale = 65536 + Math.floor(saturation * 65536 / 100);
16494
+ const lightOffset = Math.round(255 * Math.floor(lightness * 65536 / 100) / 65536);
16380
16495
  for (let i = 0; i < src.length; i += 4) {
16381
16496
  const r = src[i];
16382
16497
  const g = src[i + 1];
16383
16498
  const b = src[i + 2];
16384
16499
  const max = Math.max(r, g, b);
16385
16500
  const min = Math.min(r, g, b);
16386
- const l = clamp(Math.round(((max + min >> 1) * 100 + 255 * lightness) / 100), 0, 255);
16501
+ const l = clamp((max + min >> 1) + lightOffset, 0, 255);
16387
16502
  if (max === min) {
16388
16503
  out[i] = out[i + 1] = out[i + 2] = l;
16389
16504
  continue;
16390
16505
  }
16391
16506
  const sum = max + min;
16392
- const s = Math.fround(clamp((max - min) / (sum <= 255 ? sum : 510 - sum) * sMul, 0, 1));
16393
- const hi = l <= 127 ? l * (1 + s) : l + s * (255 - l);
16394
- const m1 = l <= 127 ? 2 * l - Math.trunc(hi) : Math.round(2 * l - hi);
16395
- const m2 = 2 * l - m1;
16507
+ const sat2 = clamp(Math.round(Math.floor((max - min) * 255 / (sum <= 255 ? sum : 510 - sum)) * satScale / 65536), 0, 255);
16508
+ const hi = l <= 127 ? Math.floor(l * (255 + sat2) / 255) : l + sat2 - Math.floor(l * sat2 / 255);
16509
+ const m1 = 2 * l - hi;
16510
+ const m2 = hi;
16396
16511
  const index = ((gdipHue(r, g, b) + shift) % HUE_CIRCLE + HUE_CIRCLE) % HUE_CIRCLE;
16397
16512
  const q = index + Math.floor((index + 41) / 85);
16398
16513
  const sextant = Math.min(5, Math.floor(q / HUE_SEXTANT));
16399
- const f = (q - sextant * HUE_SEXTANT) / HUE_SEXTANT;
16400
- const up = Math.trunc(m1 + (m2 - m1) * f);
16401
- const down = Math.trunc(m1 + (m2 - m1) * (1 - f));
16514
+ const position = q - sextant * HUE_SEXTANT;
16515
+ const up = m1 + Math.floor((m2 - m1) * position / HUE_SEXTANT);
16516
+ const down = m1 + Math.floor((m2 - m1) * (HUE_SEXTANT - position) / HUE_SEXTANT);
16402
16517
  const [nr, ng, nb] = [
16403
16518
  [m2, up, m1],
16404
16519
  [down, m2, m1],
@@ -16413,48 +16528,278 @@ function applyHueSaturationLightness(src, hue, saturation, lightness) {
16413
16528
  }
16414
16529
  return out;
16415
16530
  }
16416
- var LUMA_709 = [0.2126, 0.7152, 0.0722];
16417
- var TINT_CHROMA_SCALE = 0.985;
16418
16531
  function applyTint(src, hue, amount) {
16419
16532
  const index = (Math.round(hue * 255 / 360) + 256) % 256;
16420
16533
  const q = (index + Math.floor((index + 41) / 85)) % 258;
16421
- const tint = hslToRgb(q * 60 / 43, 1, 0.5);
16422
- const ty = tint[0] * LUMA_709[0] + tint[1] * LUMA_709[1] + tint[2] * LUMA_709[2];
16423
- const chroma = tint.map((c) => (c - ty) * TINT_CHROMA_SCALE);
16424
- const a = amount / 100;
16534
+ const sextant = Math.min(5, Math.floor(q / HUE_SEXTANT));
16535
+ const position = q - sextant * HUE_SEXTANT;
16536
+ const up = Math.floor(TINT_FULL * position / HUE_SEXTANT);
16537
+ const down = Math.floor(TINT_FULL * (HUE_SEXTANT - position) / HUE_SEXTANT);
16538
+ const [tr, tg, tb] = [
16539
+ [TINT_FULL, up, 0],
16540
+ [down, TINT_FULL, 0],
16541
+ [0, TINT_FULL, up],
16542
+ [0, down, TINT_FULL],
16543
+ [up, 0, TINT_FULL],
16544
+ [TINT_FULL, 0, down]
16545
+ ][sextant];
16546
+ const weight = Math.sign(amount) * Math.floor((255 * Math.abs(amount) + 49) / 100);
16547
+ const original = 255 - weight;
16425
16548
  const out = new Uint8ClampedArray(src);
16426
16549
  for (let i = 0; i < src.length; i += 4) {
16427
16550
  const r = src[i];
16428
16551
  const g = src[i + 1];
16429
16552
  const b = src[i + 2];
16430
- const y = r * LUMA_709[0] + g * LUMA_709[1] + b * LUMA_709[2];
16431
- const v = Math.max(r, g, b);
16432
- out[i] = Math.round((1 - a) * r + a * (y + v * chroma[0]));
16433
- out[i + 1] = Math.round((1 - a) * g + a * (y + v * chroma[1]));
16434
- out[i + 2] = Math.round((1 - a) * b + a * (y + v * chroma[2]));
16553
+ const u = weight * Math.max(r, g, b) >> 8;
16554
+ const x0 = original * r + tr * u;
16555
+ const x1 = original * g + tg * u;
16556
+ const x2 = original * b + tb * u;
16557
+ const base = 54 * r + 183 * g + 19 * b - (54 * (x0 >> 8) + 183 * (x1 >> 8) + 19 * (x2 >> 8));
16558
+ out[i] = base + x0 >> 8;
16559
+ out[i + 1] = base + x1 >> 8;
16560
+ out[i + 2] = base + x2 >> 8;
16435
16561
  }
16436
16562
  return out;
16437
16563
  }
16564
+ var TINT_FULL = 254;
16438
16565
  function applyRedEyeCorrection(src, width, height, areas) {
16439
16566
  const out = new Uint8ClampedArray(src);
16440
16567
  for (const a of areas) {
16441
- const x0 = clamp(a.left, 0, width);
16442
- const x1 = clamp(a.right, 0, width);
16443
- const y0 = clamp(a.top, 0, height);
16444
- const y1 = clamp(a.bottom, 0, height);
16445
- for (let y = y0; y < y1; y++) {
16446
- for (let x = x0; x < x1; x++) {
16447
- const i = (y * width + x) * 4;
16448
- const r = out[i];
16449
- const g = out[i + 1];
16450
- const b = out[i + 2];
16451
- if (r >= 2 * g && r >= 2 * b && r > 64) {
16452
- out[i] = Math.round((g + b) / 2);
16568
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16569
+ }
16570
+ return out;
16571
+ }
16572
+ var RED_EYE_FALLOFF = new Float64Array(
16573
+ [
16574
+ 24930,
16575
+ 24570,
16576
+ 24201,
16577
+ 23836,
16578
+ 23471,
16579
+ 23107,
16580
+ 22744,
16581
+ 22367,
16582
+ 21976,
16583
+ 21587,
16584
+ 21198,
16585
+ 20824,
16586
+ 20490,
16587
+ 20165,
16588
+ 19832,
16589
+ 19499,
16590
+ 19157,
16591
+ 18817,
16592
+ 18480,
16593
+ 18151,
16594
+ 17820,
16595
+ 17490,
16596
+ 17160,
16597
+ 16831,
16598
+ 16506,
16599
+ 16189,
16600
+ 15878,
16601
+ 15569,
16602
+ 15273,
16603
+ 14973,
16604
+ 14668,
16605
+ 14362,
16606
+ 14059,
16607
+ 13768,
16608
+ 13484,
16609
+ 13203,
16610
+ 12923,
16611
+ 12642,
16612
+ 12366,
16613
+ 12094,
16614
+ 11828,
16615
+ 11564,
16616
+ 11301,
16617
+ 11034,
16618
+ 10770,
16619
+ 10507,
16620
+ 10253,
16621
+ 10011,
16622
+ 9771,
16623
+ 9530,
16624
+ 9290,
16625
+ 9053,
16626
+ 8819,
16627
+ 8588,
16628
+ 8359,
16629
+ 8134,
16630
+ 7914,
16631
+ 7697,
16632
+ 7481,
16633
+ 7267,
16634
+ 7056,
16635
+ 6851,
16636
+ 6651,
16637
+ 6453,
16638
+ 6256,
16639
+ 6061,
16640
+ 5871,
16641
+ 5684,
16642
+ 5499,
16643
+ 5317,
16644
+ 5138,
16645
+ 4962,
16646
+ 4789,
16647
+ 4619,
16648
+ 4452,
16649
+ 4289,
16650
+ 4130,
16651
+ 3974,
16652
+ 3820,
16653
+ 3669,
16654
+ 3520,
16655
+ 3375,
16656
+ 3232,
16657
+ 3093,
16658
+ 2958,
16659
+ 2825,
16660
+ 2695,
16661
+ 2568,
16662
+ 2444,
16663
+ 2325,
16664
+ 2207,
16665
+ 2092,
16666
+ 1980,
16667
+ 1872,
16668
+ 1768,
16669
+ 1666,
16670
+ 1567,
16671
+ 1470,
16672
+ 1377,
16673
+ 1287,
16674
+ 1200,
16675
+ 1116,
16676
+ 1034,
16677
+ 956,
16678
+ 881,
16679
+ 808,
16680
+ 739,
16681
+ 674,
16682
+ 612,
16683
+ 541,
16684
+ 462,
16685
+ 381,
16686
+ 299,
16687
+ 220,
16688
+ 147,
16689
+ 85,
16690
+ 36,
16691
+ 3,
16692
+ 0
16693
+ ].map((v) => v / 1e5)
16694
+ );
16695
+ function redEyeFalloff(u) {
16696
+ const t = u * 128;
16697
+ const i = Math.floor(t);
16698
+ if (i >= RED_EYE_FALLOFF.length - 1) {
16699
+ return 0;
16700
+ }
16701
+ return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16702
+ }
16703
+ var RED_EYE_SECTORS = 60;
16704
+ var RED_EYE_BLUE_WEIGHT = 0.225;
16705
+ var RED_EYE_WEIGHT_CAP = 6;
16706
+ var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16707
+ var RED_EYE_DARK_STRENGTH = 0.49;
16708
+ var RED_EYE_DARK_RATIO = 0.6;
16709
+ var RED_EYE_GREEN_SHARE = 80 / 255;
16710
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16711
+ const w = x1 - x0;
16712
+ const h = y1 - y0;
16713
+ const count = w * h;
16714
+ const redness = new Float64Array(count);
16715
+ const dark = new Float64Array(count);
16716
+ let sx = 0;
16717
+ let sy = 0;
16718
+ let sw = 0;
16719
+ for (let y = 0; y < h; y++) {
16720
+ for (let x = 0; x < w; x++) {
16721
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16722
+ const r = out[i];
16723
+ const g = out[i + 1];
16724
+ const b = out[i + 2];
16725
+ const k = y * w + x;
16726
+ dark[k] = Math.min(g, b);
16727
+ const v = r - Math.max(g, b);
16728
+ if (v > 0) {
16729
+ redness[k] = v;
16730
+ const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16731
+ {
16732
+ const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16733
+ sx += weight * (x + 0.5);
16734
+ sy += weight * (y + 0.5);
16735
+ sw += weight;
16453
16736
  }
16454
16737
  }
16455
16738
  }
16456
16739
  }
16457
- return out;
16740
+ if (sw === 0) {
16741
+ if (!redness.some((v) => v > 0)) {
16742
+ return;
16743
+ }
16744
+ sx = w / 2;
16745
+ sy = h / 2;
16746
+ sw = 1;
16747
+ }
16748
+ const cx = sx / sw;
16749
+ const cy = sy / sw;
16750
+ const radius = Math.min(cx, cy) - 0.5;
16751
+ if (!(radius > 0)) {
16752
+ return;
16753
+ }
16754
+ const sector = new Uint8Array(count);
16755
+ const distance = new Float64Array(count);
16756
+ const sectorSum = new Float64Array(RED_EYE_SECTORS);
16757
+ const sectorDark = new Float64Array(RED_EYE_SECTORS);
16758
+ const sectorCount = new Float64Array(RED_EYE_SECTORS);
16759
+ const degrees = 180 / Math.PI;
16760
+ for (let y = 0; y < h; y++) {
16761
+ for (let x = 0; x < w; x++) {
16762
+ const dx = x + 0.5 - cx;
16763
+ const dy = y + 0.5 - cy;
16764
+ const k = y * w + x;
16765
+ const d = Math.hypot(dx, dy);
16766
+ distance[k] = d;
16767
+ let angle = Math.atan2(dy, dx) * degrees;
16768
+ if (angle < 0) {
16769
+ angle += 360;
16770
+ }
16771
+ const s = Math.min(RED_EYE_SECTORS - 1, Math.floor(angle / 6));
16772
+ sector[k] = s;
16773
+ if (d < radius) {
16774
+ sectorSum[s] += redness[k];
16775
+ sectorDark[s] += dark[k];
16776
+ sectorCount[s]++;
16777
+ }
16778
+ }
16779
+ }
16780
+ for (let y = 0; y < h; y++) {
16781
+ for (let x = 0; x < w; x++) {
16782
+ const k = y * w + x;
16783
+ const v = redness[k];
16784
+ if (v <= 0 || distance[k] >= radius) {
16785
+ continue;
16786
+ }
16787
+ const s = sector[k];
16788
+ const mean = sectorSum[s] / sectorCount[s];
16789
+ const meanDark = sectorDark[s] / sectorCount[s];
16790
+ const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16791
+ const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16792
+ const rest = (1 - falloff) * mean;
16793
+ if (rest >= v) {
16794
+ continue;
16795
+ }
16796
+ const removed = v - rest;
16797
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16798
+ out[i] = Math.round(out[i] - (1 - RED_EYE_GREEN_SHARE) * removed);
16799
+ out[i + 1] = Math.round(out[i + 1] + RED_EYE_GREEN_SHARE * removed);
16800
+ out[i + 2] = Math.round(out[i + 2] + RED_EYE_GREEN_SHARE * removed);
16801
+ }
16802
+ }
16458
16803
  }
16459
16804
  function blurKernel(radius) {
16460
16805
  const taps = Math.ceil(radius);
@@ -16486,8 +16831,8 @@ function mirror(p, n) {
16486
16831
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows = Math.ceil(h / w)) {
16487
16832
  const k = blurKernel(radius);
16488
16833
  const taps = (k.length - 1) / 2;
16489
- const horizontalIndex = taps >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16490
- const verticalIndex = taps >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16834
+ const horizontalIndex = (taps | 1) >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16835
+ const verticalIndex = (taps | 1) >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16491
16836
  const out = new Float64Array(w * h * 4);
16492
16837
  for (let y = 0; y < h; y++) {
16493
16838
  const row2 = y * w * 4;
@@ -16498,11 +16843,12 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16498
16843
  let s3 = 0;
16499
16844
  for (let j = -taps; j <= taps; j++) {
16500
16845
  const p = row2 + horizontalIndex(x + j) * 4;
16501
- const kw = k[j + taps] * 256;
16502
- s0 += Math.round(kw * src[p]);
16503
- s1 += Math.round(kw * src[p + 1]);
16504
- s2 += Math.round(kw * src[p + 2]);
16505
- s3 += Math.round(kw * src[p + 3]);
16846
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16847
+ const times = j === 0 ? 2 : 1;
16848
+ s0 += times * Math.round(kw * src[p]);
16849
+ s1 += times * Math.round(kw * src[p + 1]);
16850
+ s2 += times * Math.round(kw * src[p + 2]);
16851
+ s3 += times * Math.round(kw * src[p + 3]);
16506
16852
  }
16507
16853
  const o = row2 + x * 4;
16508
16854
  out[o] = Math.floor((s0 + 128) / 256);
@@ -16522,8 +16868,10 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16522
16868
  continue;
16523
16869
  }
16524
16870
  const base = verticalIndex(yy) * w * 4;
16871
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16872
+ const times = j === 0 ? 2 : 1;
16525
16873
  for (let i = 0; i < w * 4; i++) {
16526
- col[i] += Math.round(k[j + taps] * horizontal[base + i] * 256) / 256;
16874
+ col[i] += times * Math.round(kw * horizontal[base + i]) / 256;
16527
16875
  }
16528
16876
  }
16529
16877
  out.set(col, row2 * w * 4);
@@ -16538,10 +16886,12 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16538
16886
  const inner = Math.ceil(span.size / factor);
16539
16887
  const n = inner + 2 * span.pad;
16540
16888
  const taps = (k.length - 1) / 2;
16541
- const index = taps >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16889
+ const index = (taps | 1) >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16542
16890
  const reduced = new Float64Array(n);
16543
16891
  const filtered = new Float64Array(n);
16544
16892
  const k256 = k.map((w) => w * 256);
16893
+ const half = k256[taps] / 2;
16894
+ k256[taps] = 0;
16545
16895
  for (let lane = 0; lane < lanes; lane++) for (let c = 0; c < 4; c++) {
16546
16896
  for (let b = span.pad; b < span.pad + inner; b++) {
16547
16897
  const from = span.start + (b - span.pad) * factor;
@@ -16555,33 +16905,48 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16555
16905
  if (b >= taps && b + taps < n) {
16556
16906
  for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[b - taps + j]);
16557
16907
  } else {
16558
- for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[index(b - taps + j)]);
16908
+ for (let j = 0; j < k256.length; j++) {
16909
+ const at = b - taps + j;
16910
+ sum += Math.round(k256[j] * (span.zeroOutside && (at < 0 || at >= n) ? 0 : reduced[index(at)]));
16911
+ }
16559
16912
  }
16913
+ sum += 2 * Math.round(half * reduced[b]);
16560
16914
  filtered[b] = Math.floor((sum + 128) / 256);
16561
16915
  }
16916
+ const lone = inner === 1 && span.pad > 0;
16917
+ const skipped2 = inner === 1 && span.size - Math.ceil(factor / 2) === 1 ? span.start + span.size - 1 : -1;
16562
16918
  for (let i = 0; i < length; i++) {
16563
- const p = (i - span.start + 0.5) / factor - 0.5 + span.pad;
16564
- const a = clamp(Math.floor(p), 0, Math.max(0, n - 2));
16919
+ if (i === skipped2) {
16920
+ if (span.pad > 0) put(i, lane, c, 0);
16921
+ continue;
16922
+ }
16923
+ const p = (i - span.start + 0.5) / factor - 0.5;
16924
+ if (lone) {
16925
+ const distance = Math.abs(i - span.start - (Math.min(span.size, factor / 2) - 0.5)) / factor;
16926
+ put(i, lane, c, clamp(Math.floor((1 + distance) * filtered[span.pad] + 1e-9), 0, 255));
16927
+ continue;
16928
+ }
16929
+ const a = clamp(Math.floor(p), 0, Math.max(0, inner - 2));
16565
16930
  const t = p - a;
16566
- put(i, lane, c, clamp(Math.floor((1 - t) * filtered[a] + t * filtered[Math.min(n - 1, a + 1)] + 1e-9), 0, 255));
16931
+ put(i, lane, c, clamp(Math.floor((1 - t) * filtered[span.pad + a] + t * filtered[span.pad + Math.min(inner - 1, a + 1)] + 1e-9), 0, 255));
16567
16932
  }
16568
16933
  }
16569
16934
  }
16570
- function effectBlur(src, width, height, radius, region) {
16935
+ function effectBlur(src, width, height, radius, region, expanded = false) {
16571
16936
  const factor = blurReduction(radius);
16572
16937
  if (factor === 1) {
16573
16938
  return gdipBlur(src, width, height, radius);
16574
16939
  }
16575
16940
  const k = blurKernel(radius / factor);
16576
16941
  const pad = region ? Math.ceil(Math.ceil(radius) / factor) : 0;
16577
- const spanX = region ? { start: region.x, size: region.w, pad } : void 0;
16578
- const spanY = region ? { start: region.y, size: region.h, pad } : void 0;
16579
- const rows = new Uint8Array(src.length);
16580
- blurAxis(width, height, factor, k, Math.floor(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16942
+ const spanX = region ? { start: region.x, size: region.w, pad, zeroOutside: true } : expanded ? { start: 0, size: width, pad: 0, zeroOutside: true } : void 0;
16943
+ const spanY = region ? { start: region.y, size: region.h, pad, zeroOutside: true } : expanded ? { start: 0, size: height, pad: 0, zeroOutside: true } : void 0;
16944
+ const rows = new Uint8Array(src);
16945
+ blurAxis(width, height, factor, k, Math.ceil(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16581
16946
  rows[(lane * width + i) * 4 + c] = v;
16582
16947
  }, spanX);
16583
- const out = new Float64Array(src.length);
16584
- blurAxis(height, width, factor, k, Math.floor(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16948
+ const out = Float64Array.from(rows);
16949
+ blurAxis(height, width, factor, k, Math.ceil(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16585
16950
  out[(i * width + lane) * 4 + c] = v;
16586
16951
  }, spanY);
16587
16952
  return out;
@@ -16669,7 +17034,7 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
16669
17034
  const gy1 = Math.min(height, y1 + r);
16670
17035
  copyBlock(rgba, width, gx0, gy0, gx1 - gx0, gy1 - gy0, buf, bw, gx0 - (x0 - r), gy0 - (y0 - r));
16671
17036
  }
16672
- const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
17037
+ const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0, true) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
16673
17038
  const w = x1 - x0;
16674
17039
  const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
16675
17040
  for (let y = 0; y < y1 - y0; y++) {
@@ -20196,8 +20561,7 @@ function resampleImage(rgba, width, height, spec, surface) {
20196
20561
  const axisAligned = m[1] === 0 && m[2] === 0;
20197
20562
  if (hq && axisAligned) {
20198
20563
  const ox = m[0] * spec.srcX + m[4];
20199
- const oy = m[3] * spec.srcY + m[5];
20200
- m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5] + mirroredOrigin(oy) - oy];
20564
+ m = [m[0], 0, 0, m[3], m[4] + mirroredOrigin(ox) - ox, m[5]];
20201
20565
  }
20202
20566
  const inv2 = invert2(m);
20203
20567
  if (!inv2) {
@@ -23010,6 +23374,21 @@ var nestedReplayer = null;
23010
23374
  function registerNestedMetafileReplayer(fn) {
23011
23375
  nestedReplayer = fn;
23012
23376
  }
23377
+ function inclusiveNestedMapping(toDevice, srcX, srcY, srcW, srcH) {
23378
+ const [a, b, c, d, e, f] = toDevice;
23379
+ const lengthU = Math.hypot(a, b) * srcW;
23380
+ const lengthV = Math.hypot(c, d) * srcH;
23381
+ if (!(srcW > 1 && srcH > 1 && lengthU > 1 && lengthV > 1)) {
23382
+ return toDevice;
23383
+ }
23384
+ const ku = (lengthU - 1) / lengthU * (srcW / (srcW - 1));
23385
+ const kv = (lengthV - 1) / lengthV * (srcH / (srcH - 1));
23386
+ const na = a * ku;
23387
+ const nb = b * ku;
23388
+ const nc = c * kv;
23389
+ const nd = d * kv;
23390
+ return [na, nb, nc, nd, e + (a - na) * srcX + (c - nc) * srcY, f + (b - nb) * srcX + (d - nd) * srcY];
23391
+ }
23013
23392
  function drawNestedMetafile(rCtx, img, cached, source) {
23014
23393
  const depth = rCtx.nestingDepth ?? 0;
23015
23394
  if (!nestedReplayer || !source || !img.data || source.unit !== UNIT_PIXEL || depth >= MAX_NESTED_METAFILE_DEPTH) {
@@ -23023,9 +23402,12 @@ function drawNestedMetafile(rCtx, img, cached, source) {
23023
23402
  if (!size) {
23024
23403
  return false;
23025
23404
  }
23026
- const toCanvas = mulMatrix(
23027
- [1, 0, 0, 1, -0.5, -0.5],
23028
- mulMatrix(mulMatrix(plusWorldMatrix(rCtx), source.toWorld(srcX, srcY, srcW, srcH)), [1, 0, 0, 1, 0.5, 0.5])
23405
+ const toCanvas = inclusiveNestedMapping(
23406
+ mulMatrix(plusWorldMatrix(rCtx), source.toWorld(srcX, srcY, srcW, srcH)),
23407
+ srcX,
23408
+ srcY,
23409
+ srcW,
23410
+ srcH
23029
23411
  );
23030
23412
  const bytes = img.data.slice(0);
23031
23413
  const caches = cached && cached.kind === "metafile" ? cached.caches : void 0;
@@ -28734,118 +29116,19 @@ var HOBBY = [
28734
29116
  [[8, -48], [-8, -48], [-24, -40], [-40, -24], [-48, -8], [-48, 8], [-40, 24], [-24, 40], [-8, 48], [8, 48], [24, 40], [40, 24], [48, 8], [48, -8], [40, -24], [24, -40]]
28735
29117
  ];
28736
29118
  var HOBBY_LIMIT = 104;
28737
- var FLAT_SECTOR_BIAS = {
28738
- 7: [9, "cf..", "edededEdcDcdcd"],
28739
- 8: [9, "dFee", "ededededbdbdbd"],
28740
- 9: [9, "df..", "ddddddddbDbdbd"],
28741
- 10: [9, "de..", "dededeDebEbebE"],
28742
- 11: [9, "ce..", "eeeeeeEececece"],
28743
- 12: [13, "ce..", "eeeeeeeeeeeececEcecece"],
28744
- 13: [17, "ce..", "eeeeededdddddeDebebebdbdcdcdce"],
28745
- 14: [17, "df..", "ddedeeeededeeeedcDcebebecececd"],
28746
- 15: [17, "dF..", "ddedeeeeeeeedeDdbdbecececececd"],
28747
- 16: [17, "ce..", "eeeededededeeeeececebebebebece"],
28748
- 17: [17, "df..", "ddedededededdeddbdbebdbdbdbdcd"],
28749
- 18: [17, "de..", "deeededeededeeEdcDcecdcdbebece"],
28750
- 19: [17, "cf..", "ededeeeedddddeDebEbecdcdbebecd"],
28751
- 20: [17, "ce..", "eeeeddddeeeeddDdbdbdcecebdbdce"],
28752
- 21: [17, "dF..", "ddddededededddDdbdbdcdcdcdcdbd"],
28753
- 22: [17, "ce..", "eeeededeeeeedeDebebececebebece"],
28754
- 23: [17, "cf..", "edededededededEdcDcdcdcdcdcdcD"],
28755
- 24: [17, "dF..", "ddddddddddddddDdbdbdbdbdbdbdbd"],
28756
- 25: [17, "cE..", "eeeeededededeeEecEcecdcdcdcdce"],
28757
- 26: [17, "cf..", "ededdedeededdedebEbecdcdbebecd"],
28758
- 27: [17, "de..", "dedeededdedeededcDcdbebecdcdbe"],
28759
- 28: [17, "cf..", "ededddddeeeedeDebEbebebecdcdcd"],
28760
- 29: [17, "ce..", "eeeeeeeededededebebebebececece"],
28761
- 30: [17, "dF..", "dddddedeeeeeedEdcdcdcecebebebd"],
28762
- 31: [17, "de..", "dedeededededdeDebebecdcdcdcdbe"],
28763
- 32: [17, "cf..", "ededdededdddeeeecEcebdbdbebecd"],
28764
- 33: [17, "de..", "dedeeeeeededddDdbdbdcdcdcecebe"],
28765
- 34: [17, "de..", "dedeeeeeddddedEdcdcdbdbdcecebe"],
28766
- 35: [17, "cf..", "ededededdededeDebebebebecdcdcd"],
28767
- 36: [17, "deee", "eeeeeeeeddddddDdcdcdcdcDbebebe"],
28768
- 37: [17, "dF..", "dddddededededdDdbdbdcecececebd"],
28769
- 38: [17, "ce..", "eeeeddddddddeeEececebdbdbdbdce"],
28770
- 39: [17, "cF..", "ededddddddddededcdcdbdbDbdbdcd"],
28771
- 40: [17, "de..", "dededededdddddddbdbdbdbdbebebe"],
28772
- 41: [17, "ce..", "eeeeeeeeeeeeeeEececececececece"],
28773
- 42: [17, "cf..", "ededeeeedddddedebebebdbdcececd"],
28774
- 43: [17, "df..", "ddddeeeededeedEdcdcdbebececebd"],
28775
- 44: [17, "cF..", "ededdedeededdeDebebecdcdbebecd"],
28776
- 45: [17, "ce..", "eeeededededeeeeecEcebebebebece"],
28777
- 46: [17, "dF..", "ddddededddddedEdcDcdcdcdbdbdbd"],
28778
- 47: [17, "ce..", "dedededededededececececececece"],
28779
- 48: [25, "cf..", "ededeeeeeeeeededeeeeeeEecececececdcdcececececd"],
28780
- 49: [25, "cF..", "ededddddededeeeeededdeDebebecdcdcececdcdbdbdcd"],
28781
- 50: [25, "ce..", "eeeeddddededdededededeDebebebebebEbecdcdbdbdce"],
28782
- 51: [25, "ce..", "eeeeededddddeeeededeeeEececebebececebdbdcdcdce"],
28783
- 52: [25, "ce..", "eeeededededeededededeeEecececdcdcdcdbebebebece"],
28784
- 53: [25, "dF..", "ddedeeeedddddddddedeeeEdcdcebebebdbDbdbdcececd"],
28785
- 54: [25, "dF..", "ddedddddededededdddddEDdbdbebdbdcdcdcdcdbdbdcd"],
28786
- 55: [25, "dF..", "ddeddedeeeeeeeeedededeDdbdbebebebebebebebebecd"],
28787
- 56: [25, "dE..", "deeeeeeeededeeeededeeEedcdcebebecececdcdcecece"],
28788
- 57: [25, "de..", "deeedddddddddedeededeeEdcDcecDcdcececdcdbdbDce"],
28789
- 58: [25, "dF..", "ddedddddeeeedddddedeeeEdcdcebebebdbdcecebdbdcd"],
28790
- 59: [33, "cF..", "ededdedeededddddeeeeeeeedddddeDebebebdbdcecececebdbdcdcdbebecd"],
28791
- 60: [33, "cf..", "ededeeeeededededddddeeeeeeeedeDebebececececebdbdcdcdcdcdcececd"],
28792
- 61: [33, "ce..", "eeeeeeeeddddeeeeeeeeddddeeeeeeeececececebdbdcecececebdbdcecece"],
28793
- 62: [33, "ce..", "eeeeddddeeeeededededeeeeddddeeEecEcebdbdcececdcdcdcdcecebdbdce"],
28794
- 63: [33, "cf..", "ededededeeeededededeeeeeeeeeeeEececececececebebEbebecececdcdcd"],
28795
- 64: [33, "cF..", "ededeeeedddddedeeeeededeeeeedeDebebececebebececebebebdbdcececd"],
28796
- 65: [33, "df..", "ddedededededddddddddeeeeeeeedeDdbdbececececebdbdbdbdcdcdcdcdcd"],
28797
- 66: [33, "de..", "deeeededdedeeeeedddddededddddeDdbdbebdbdbebecdcdbebebebecdcdce"],
28798
- 67: [33, "de..", "deeeddddddddededdddddededdddeeEdcDcebdbdbebebdbdcdcdbdbdbdbdce"],
28799
- 68: [33, "dE..", "deeeeeeeeeeeeeeeddddeeeeeeeeeeEdcdcececececebdbdcecececececece"],
28800
- 69: [33, "df..", "ddedededdedeeeeeddddededededdeDdbdbecdcdcdcdbdbdcecebebecdcdcd"],
28801
- 70: [33, "dF..", "ddeddddddddddddddededededddddeDdbdbebdbdbebebebebdbdbdbdbdbDcd"],
28802
- 71: [33, "ce..", "eeeeddddeeeededeeeeededeededdeDebebecdcdbebececebebececebdbdce"],
28803
- 72: [33, "ce..", "eeeededededeededededddddddddeeEececebdbdbdbdcdcdcdcdbebebebece"],
28804
- 73: [33, "ce..", "eeeeededededddddededdedeeeeeeeEececececebebecdcdbdbdcdcDcdcdce"],
28805
- 74: [33, "cf..", "ededddddededededededdededddddeDebebebdbdbebecdcDcdcdcdcdbdbdcd"],
28806
- 75: [33, "cF..", "ededdededededdddddddeeeedddddeDebebebdbdcececdcdcdcdbebebebecd"],
28807
- 76: [33, "ce..", "eeeeeeeeeeeeeeeeeeeededeeeeedeDebebececebebecececececececececE"],
28808
- 77: [33, "ce..", "eeeeeeeedededddddededdddedededEdcDcdcdcdbdbdbebebdbdbebececece"],
28809
- 78: [33, "de..", "dedeededdedededeeeeeeeeededeedEdcdcdbebececececebebebebecdcdbe"],
28810
- 79: [33, "dF..", "ddddddddededdededdddeeeeedededEdcDcdcdcdcecebdbdbebecdcdbdbdbd"],
28811
- 80: [33, "dF..", "dddddededdddeeeeddddddddededddDdbdbdcdcdbdbdbdbDcecebdbdbebEbd"],
28812
- 81: [33, "de..", "dedeeeeeeeeededeededeeeedededdDdbdbdbebecececdcdbebececececebe"],
28813
- 82: [33, "de..", "dedeededdedeededdedededeedededEdcdcdcdcdbebebebecdcdbebecdcDbe"],
28814
- 83: [33, "de..", "dededededddddededdddeeeededeeeEececebebececebdbdbebebdbdbebebe"],
28815
- 84: [33, "cF..", "ededddddeeeeddddeeeeeeeeededeeEecececdcdcecebebecdcdcecebdbdcd"],
28816
- 85: [33, "cf..", "ededdddddededddddddddedeeeeedeDebebececebebebdbdbdbdbebebdbdcd"],
28817
- 86: [33, "ce..", "eeeeededddddeeeeeeeedededededeDebebebebebebebebebebebdbdcdcdce"],
28818
- 87: [33, "ce..", "eeeeddddeeeeddddddddeeeeddddeeeececebdbdcecebdbDbdbdcecebdbdce"],
28819
- 88: [33, "ce..", "eeeedededdddeeeeededededededeeeecececdcdcdcdcdcdcecebdbdbebece"],
28820
- 89: [33, "cF..", "ededeeeeddddddddededdedededeeeEecEcebebEbebecdcdbdbdbdbdcececd"],
28821
- 90: [33, "dF..", "ddddededeeeeededeeeedededededdDdbdbdbebebEbecececdcdcececdcdbd"],
28822
- 91: [33, "df..", "ddddeeeededededeededdddddededdDdbdbdbebebdbdcdcdbebebebececebd"],
28823
- 92: [33, "de..", "dedededeededdddddededededdddedEdcdcdbdbdbebebebebdbdcdcdbebebe"],
28824
- 93: [33, "de..", "dededddddddddedeededeeeeeeeeededcdcdcececececdcdbebebdbdbdbdbe"],
28825
- 94: [33, "dF..", "ddddeeeeddddeeeeddddeeeededeedEdcDcdbebececebdbDcecebdbdcecebd"],
28826
- 95: [33, "df..", "ddddddddededeeeedddddededdddddDdbdbDbdbdbebecdcdbebecdcdbdbdbd"],
28827
- 96: [33, "cf..", "ededeeeededeeeeedededdddeeeedeDebebececebdbdbebececebebecececd"],
28828
- 97: [33, "ce..", "eeeedededededededdddeeeedddddeDebebebdbdcEcebdbdbebebEbebebece"],
28829
- 98: [33, "ce..", "eeeeededededeeeeeeeededededeeeEececebebebebecececececdcdcdcdce"],
28830
- 99: [33, "cf..", "ededededddddddddddddededdedeeeEececebebecdcdbdbdbdbdbdbdcdcdcd"],
28831
- 100: [33, "cf..", "ededdedeeeeededededeeeeeededdeDebebecdcdcecebebebebececebebecd"]
28832
- };
28833
- function sectorBias(code) {
28834
- return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
29119
+ function oddEdgeAdjustment(odd, firstEdge) {
29120
+ return odd ? firstEdge ? 0.5 : -0.5 : 0;
28835
29121
  }
28836
- var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28837
- var VERTICAL_STEP_FIRST = 7;
28838
- function verticalSideOffset(width) {
28839
- if (width < HOBBY_LIMIT || width > 1400) {
28840
- return void 0;
29122
+ function floorDivision(a, b) {
29123
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
29124
+ return Math.floor(a / b);
28841
29125
  }
28842
- let m = VERTICAL_STEP_FIRST;
28843
- for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28844
- if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28845
- m = i + VERTICAL_STEP_FIRST;
28846
- }
29126
+ const A = BigInt(Math.round(a)), B = BigInt(Math.round(b));
29127
+ let q = A / B;
29128
+ if (A % B !== 0n && A < 0n !== B < 0n) {
29129
+ q -= 1n;
28847
29130
  }
28848
- return 8 * m;
29131
+ return Number(q);
28849
29132
  }
28850
29133
  var penCache = /* @__PURE__ */ new Map();
28851
29134
  function penPolygon(width) {
@@ -28917,6 +29200,9 @@ function perpendicularVectors(width, dx0, dy0) {
28917
29200
  best = i;
28918
29201
  }
28919
29202
  }
29203
+ if (flip && best === 0 && nx * pen[n - 1][0] + ny * pen[n - 1][1] === bh) {
29204
+ best = n - 1;
29205
+ }
28920
29206
  const P = pen[(best + n - 1) % n];
28921
29207
  const N = pen[(best + 1) % n];
28922
29208
  const D = pen[best];
@@ -28925,20 +29211,19 @@ function perpendicularVectors(width, dx0, dy0) {
28925
29211
  const [B, hB, hS] = hP >= hN ? [P, hP, hN] : [N, hN, hP];
28926
29212
  const den = 2 * (bh - hB + (bh - hS));
28927
29213
  const w = den === 0 ? 0 : (hB - hS) / den;
28928
- const r = (v) => 8 * Math.floor((v + 4) / 8);
28929
29214
  const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28930
- let cx = 0.5 * Math.sign(dy), cy = 0.5 * Math.sign(dx);
28931
- const table = width % 16 === 0 ? FLAT_SECTOR_BIAS[width / 16] : void 0;
28932
- if (table) {
28933
- const key = best * 2 + (hP >= hN ? 0 : 1);
28934
- const pair = key === 0 ? table[1].slice(0, 2) : key >= table[0] ? table[2].slice((key - table[0]) * 2, (key - table[0]) * 2 + 2) : "..";
28935
- if (pair[0] !== ".") {
28936
- cx = sectorBias(pair.charCodeAt(0));
28937
- cy = sectorBias(pair.charCodeAt(1));
28938
- }
28939
- }
28940
- const side = dx === 0 ? verticalSideOffset(width) : void 0;
28941
- const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
29215
+ const signs = [dx === 0 ? 0 : Math.sign(dy), Math.sign(dx)];
29216
+ const firstEdge = best === 0 && hP >= hN;
29217
+ const num = hB - hS;
29218
+ const rounded = [0, 1].map((c) => {
29219
+ const bias2 = signs[c] + 2 * oddEdgeAdjustment(Math.abs(B[c] - D[c]) % 2 === 1, firstEdge);
29220
+ if (den === 0) {
29221
+ return 8 * (c === 1 && dy < 0 ? Math.ceil((D[c] + bias2 / 2 + 4) / 8) - 1 : Math.floor((D[c] + bias2 / 2 + 4) / 8));
29222
+ }
29223
+ const top = D[c] * den + (B[c] - D[c]) * num;
29224
+ return 8 * floorDivision(2 * top + den * (bias2 + 8) - (c === 1 && dy < 0 ? 1 : 0), 16 * den);
29225
+ });
29226
+ const vx = rounded[0], vy = rounded[1];
28942
29227
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28943
29228
  }
28944
29229
  function squareExtension(width, dx, dy) {
@@ -28968,7 +29253,6 @@ var Outliner = class {
28968
29253
  this.n = this.pen.length;
28969
29254
  this.rr = opts.cap === "round" && opts.join === "round";
28970
29255
  this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28971
- this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28972
29256
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28973
29257
  }
28974
29258
  /**
@@ -28976,14 +29260,14 @@ var Outliner = class {
28976
29260
  * `drawDir` (a curve's end tangent) only picks the draw vertices, the
28977
29261
  * perpendicular following the chord (measured on Bezier ends).
28978
29262
  */
28979
- seg(a, b, dir, drawDir) {
29263
+ seg(a, b, dir, drawDir, curve = false) {
28980
29264
  const dx = dir ? dir[0] : b[0] - a[0];
28981
29265
  const dy = dir ? dir[1] : b[1] - a[1];
28982
29266
  const d = drawDir ?? [dx, dy];
28983
29267
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28984
- const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
29268
+ const perpendicular = curve && drawDir ? drawDir : [dx, dy];
28985
29269
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28986
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
29270
+ 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 };
28987
29271
  }
28988
29272
  push(p, v) {
28989
29273
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29018,7 +29302,7 @@ var Outliner = class {
29018
29302
  * vertex exactly on `A`'s ray is included when `startIncl`, one on `B`'s
29019
29303
  * ray when `endIncl`.
29020
29304
  */
29021
- wedge(p, A, B, startIncl, endIncl) {
29305
+ wedge(p, A, B, startIncl, endIncl, tail = -1) {
29022
29306
  const TWO = Math.PI * 2;
29023
29307
  const aA = Math.atan2(A[1], A[0]);
29024
29308
  const angleOf = (Q) => {
@@ -29049,13 +29333,21 @@ var Outliner = class {
29049
29333
  }
29050
29334
  }
29051
29335
  list.sort((x, y) => x[0] - y[0]);
29336
+ if (tail >= 0 && !list.some(([, k]) => k === tail)) {
29337
+ list.push([Infinity, tail]);
29338
+ }
29052
29339
  for (const [, k] of list) {
29053
29340
  this.penAt(p, k);
29054
29341
  }
29055
29342
  }
29343
+ /** The pen's extreme-x vertex `k` that a steep segment `s` rests on, or -1 when the rule does not apply. */
29344
+ extremeTail(applies, k, s) {
29345
+ const q = this.pen[k];
29346
+ return applies && Math.abs(s.dy) > Math.abs(s.dx) && Math.abs(q[0]) === this.maxX && q[1] === 0 ? k : -1;
29347
+ }
29056
29348
  /** Side offset of segment `s` at a join. */
29057
29349
  joinSide(s, side) {
29058
- if (this.roundJoinSides) {
29350
+ if (this.originalRoundJoinSides || s.curve) {
29059
29351
  const q = this.pen[side === "L" ? s.L : s.R];
29060
29352
  return [halfPixel(q[0]), halfPixel(q[1])];
29061
29353
  }
@@ -29103,32 +29395,36 @@ var Outliner = class {
29103
29395
  */
29104
29396
  join(p, a, b, side, outer) {
29105
29397
  const { cap, width, miterLimit } = this.opts;
29106
- const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
29398
+ const curveJoin = a.curve && b.curve;
29399
+ const join = curveJoin ? "round" : this.opts.join;
29400
+ const roundSides = this.originalRoundJoinSides || curveJoin;
29107
29401
  let sa = this.joinSide(a, side);
29108
29402
  let sb = this.joinSide(b, side);
29109
29403
  const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
29110
29404
  const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
29111
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29112
- sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
29113
- sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
29405
+ if (curveJoin && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29406
+ const t = a.tangentLength >= b.tangentLength ? a : b;
29407
+ sa = sb = side === "R" ? t.v : [-t.v[0], -t.v[1]];
29114
29408
  }
29115
29409
  const Da = side === "R" ? a.R : a.L;
29116
29410
  const Db = side === "R" ? b.R : b.L;
29117
- if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
29411
+ if (roundSides && (Da === Db || curveJoin && sa[0] === sb[0] && sa[1] === sb[1])) {
29118
29412
  this.push(p, sa);
29119
29413
  return;
29120
29414
  }
29121
29415
  this.push(p, sa);
29122
29416
  if (outer) {
29123
29417
  if (join === "round") {
29124
- if (this.roundJoinSides) {
29418
+ if (roundSides) {
29125
29419
  const q = this.pen[Db];
29126
29420
  const steep = Math.abs(b.dy) > Math.abs(b.dx);
29127
29421
  const incl = side === "L" && steep && Math.abs(q[0]) === this.maxX && q[0] * q[1] <= 0;
29128
29422
  this.walk(p, Da, Db, false, incl);
29129
29423
  } else {
29130
29424
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
29131
- this.wedge(p, rayA, rayB, !anti, !anti);
29425
+ if (Da !== Db || anti) {
29426
+ this.wedge(p, rayA, rayB, !anti, false, this.extremeTail(side === "L" && !anti, Db, b));
29427
+ }
29132
29428
  }
29133
29429
  } else if (join === "miter") {
29134
29430
  const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
@@ -29138,14 +29434,16 @@ var Outliner = class {
29138
29434
  }
29139
29435
  } else {
29140
29436
  this.pts.push([p[0], p[1]]);
29141
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
29437
+ if (curveJoin && !this.originalRoundJoinSides) {
29142
29438
  this.push(p, sb);
29143
29439
  this.push(p, sa);
29144
29440
  this.pts.push([p[0], p[1]]);
29145
29441
  }
29146
- if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
29442
+ if (join === "round" && cap === "flat" && !curveJoin) {
29147
29443
  this.push(p, sb);
29148
- this.wedge(p, rayB, rayA, false, false);
29444
+ if (Da !== Db) {
29445
+ this.wedge(p, rayB, rayA, false, false, this.extremeTail(side === "L", Da, a));
29446
+ }
29149
29447
  this.push(p, sa);
29150
29448
  this.pts.push([p[0], p[1]]);
29151
29449
  }
@@ -29178,17 +29476,17 @@ var Outliner = class {
29178
29476
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29179
29477
  * `draws` (curve end tangents) picks draw vertices only.
29180
29478
  */
29181
- open(P, dirs, draws) {
29479
+ open(P, dirs, draws, curves) {
29182
29480
  if (P.length < 2 && !dirs?.[0]) {
29183
29481
  this.dot(P[0]);
29184
29482
  return;
29185
29483
  }
29186
29484
  const segs2 = [];
29187
29485
  for (let i = 0; i + 1 < P.length; i++) {
29188
- segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i]));
29486
+ segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i], curves?.[i]));
29189
29487
  }
29190
29488
  if (segs2.length === 0) {
29191
- const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0]);
29489
+ const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29192
29490
  this.cap(P[0], s, true);
29193
29491
  this.cap(P[0], s, false);
29194
29492
  this.flush();
@@ -29219,11 +29517,11 @@ var Outliner = class {
29219
29517
  this.flush();
29220
29518
  }
29221
29519
  /** Outlines a closed polygon (distinct consecutive points, not repeating the first). */
29222
- closed(P, draws) {
29520
+ closed(P, draws, curves) {
29223
29521
  const m = P.length;
29224
29522
  const segs2 = [];
29225
29523
  for (let i = 0; i < m; i++) {
29226
- segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i]));
29524
+ segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i], curves?.[i]));
29227
29525
  }
29228
29526
  for (let j = 1; j <= m; j++) {
29229
29527
  const i = j % m;
@@ -29315,9 +29613,10 @@ function widenPath(path, opts) {
29315
29613
  const out = [];
29316
29614
  const dashed = !!opts.dashes && opts.dashes.length > 0;
29317
29615
  for (const fig of path.figures) {
29318
- const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
29616
+ const outliner = new Outliner(opts, out);
29319
29617
  let P = [];
29320
29618
  const dirs = [];
29619
+ const curves = [];
29321
29620
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
29322
29621
  const q = [fig.pts[i], fig.pts[i + 1]];
29323
29622
  const last = P[P.length - 1];
@@ -29326,6 +29625,7 @@ function widenPath(path, opts) {
29326
29625
  }
29327
29626
  if (last) {
29328
29627
  dirs.push(fig.tangents?.get(i / 2 - 1));
29628
+ curves.push(!!fig.roundWiden || !!fig.curveSegs?.has(i / 2 - 1));
29329
29629
  }
29330
29630
  P.push(q);
29331
29631
  }
@@ -29352,14 +29652,14 @@ function widenPath(path, opts) {
29352
29652
  pdraws.push(piece.draws[i - 1]);
29353
29653
  }
29354
29654
  }
29355
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]]);
29655
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29356
29656
  }
29357
29657
  } else if (closed && P.length >= 3) {
29358
- outliner.closed(P, dirs);
29658
+ outliner.closed(P, dirs, curves);
29359
29659
  } else if (closed && P.length === 2) {
29360
29660
  outliner.open([P[0], P[1], P[0]]);
29361
29661
  } else {
29362
- outliner.open(P, void 0, dirs);
29662
+ outliner.open(P, void 0, dirs, curves);
29363
29663
  }
29364
29664
  }
29365
29665
  return out;
@@ -31104,6 +31404,10 @@ function hasIlluminant(ca) {
31104
31404
  return illuminantCube(ca.illuminant) !== null;
31105
31405
  }
31106
31406
  var PALETTE = Array.from({ length: 32 }, (_, n) => Math.round(n * 255 / 31));
31407
+ function exactPaletteLevel(v) {
31408
+ const n = Math.round(v * 31 / 255);
31409
+ return PALETTE[n] === v ? 255 * n / 31 : v;
31410
+ }
31107
31411
  function paletteIndex(v) {
31108
31412
  const x = clamp012(v) * 255;
31109
31413
  let n = Math.min(30, Math.floor(x * 31 / 255));
@@ -31189,6 +31493,7 @@ function invert3(m) {
31189
31493
  }
31190
31494
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
31191
31495
  var clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
31496
+ var roundHalfUp = (v) => Math.floor(v + 0.5 + 1e-7);
31192
31497
  function adjustChroma(r, g, b, scale, angle) {
31193
31498
  const x = RGB_TO_XYZ[0][0] * r + RGB_TO_XYZ[0][1] * g + RGB_TO_XYZ[0][2] * b;
31194
31499
  const y = RGB_TO_XYZ[1][0] * r + RGB_TO_XYZ[1][1] * g + RGB_TO_XYZ[1][2] * b;
@@ -31240,7 +31545,7 @@ function colorAdjustmentMapper(ca) {
31240
31545
  if (negative) {
31241
31546
  v = 255 - clamp255(v);
31242
31547
  }
31243
- return Math.round(clamp255(v));
31548
+ return roundHalfUp(clamp255(v));
31244
31549
  };
31245
31550
  if (!illuminant && !chroma) {
31246
31551
  const tables = [0, 1, 2].map((channel) => Uint8Array.from({ length: 256 }, (_, i) => curve(i, channel)));
@@ -31253,7 +31558,11 @@ function colorAdjustmentMapper(ca) {
31253
31558
  c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31254
31559
  }
31255
31560
  if (chroma) {
31561
+ if (!illuminant) {
31562
+ c = c.map(exactPaletteLevel);
31563
+ }
31256
31564
  c = adjustChroma(clamp255(c[0]), clamp255(c[1]), clamp255(c[2]), scale, angle);
31565
+ c = c.map(roundHalfUp);
31257
31566
  }
31258
31567
  return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
31259
31568
  };
@@ -31287,27 +31596,6 @@ function colorAdjustRgb(rgbs, ca) {
31287
31596
  rgbs[i + 2] = v & 255;
31288
31597
  }
31289
31598
  }
31290
- function applyColorAdjustment(pixels, ca) {
31291
- if (!ca || isIdentityColorAdjustment(ca)) {
31292
- return;
31293
- }
31294
- const map = colorAdjustmentMapper(ca);
31295
- const d = pixels.data;
31296
- const cache2 = /* @__PURE__ */ new Map();
31297
- for (let i = 0; i + 3 < d.length; i += 4) {
31298
- const rgb2 = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
31299
- let v = cache2.get(rgb2);
31300
- if (v === void 0) {
31301
- v = map(rgb2);
31302
- if (cache2.size < 65536) {
31303
- cache2.set(rgb2, v);
31304
- }
31305
- }
31306
- d[i] = v >> 16 & 255;
31307
- d[i + 1] = v >> 8 & 255;
31308
- d[i + 2] = v & 255;
31309
- }
31310
- }
31311
31599
 
31312
31600
  // src/emf-gdi-halftone-dither.ts
31313
31601
  var THRESHOLDS = [
@@ -31466,22 +31754,22 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31466
31754
  if (reverse) taps.reverse();
31467
31755
  return taps;
31468
31756
  }
31757
+ var REDUCE_BITS = 13;
31469
31758
  function halftoneReduceTaps(srcLen, dstLen, reverse) {
31470
31759
  if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31760
+ const shares = 2 ** REDUCE_BITS;
31761
+ const scale = FIX / shares;
31471
31762
  const taps = [];
31472
31763
  for (let i = 0; i < dstLen; i++) {
31473
31764
  const from = i * srcLen;
31474
31765
  const to = (i + 1) * srcLen;
31475
31766
  const run = [];
31476
- let rest = 0;
31477
31767
  for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31478
- const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31479
- if (overlap <= 0) continue;
31480
- const w = Math.ceil(overlap * FIX / srcLen);
31481
- run.push([k, w]);
31482
- if (run.length > 1) rest += w;
31768
+ const lo = Math.max(from, k * dstLen) - from;
31769
+ const hi = Math.min(to, (k + 1) * dstLen) - from;
31770
+ if (hi <= lo) continue;
31771
+ run.push([k, (Math.floor(hi * shares / srcLen) - Math.floor(lo * shares / srcLen)) * scale]);
31483
31772
  }
31484
- if (run.length > 1) run[0][1] = FIX - rest;
31485
31773
  taps.push(run);
31486
31774
  }
31487
31775
  if (reverse) taps.reverse();
@@ -31555,7 +31843,7 @@ function halftoneEnlargeTaps(src, dst) {
31555
31843
  }
31556
31844
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31557
31845
  for (let y = 0; y < h; y++) {
31558
- const yy = phaseY ? phaseY[y] : flipY ? h - 1 - y : y;
31846
+ const yy = phaseY ? phaseY[y] : y;
31559
31847
  for (let x = 0; x < w; x++) {
31560
31848
  const xx = phaseX ? phaseX[x] : x;
31561
31849
  const threshold = ditherThreshold(dither.startX, dither.startY, xx, yy);
@@ -31642,11 +31930,33 @@ function halftoneSharpen(px, w, h) {
31642
31930
  }
31643
31931
  }
31644
31932
  }
31933
+ function halftonePairDespeckle(px, w, h, rows) {
31934
+ const n = rows ? w : h;
31935
+ const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
31936
+ const same = (i, j) => px[i] === px[j] && px[i + 1] === px[j + 1] && px[i + 2] === px[j + 2];
31937
+ const checker = (k) => k >= 0 && k + 1 < n && same(at(k, 0), at(k + 1, 1)) && same(at(k, 1), at(k + 1, 0)) && !same(at(k, 0), at(k, 1));
31938
+ const mean = Array.from({ length: n }, (_, k) => checker(k - 1) || checker(k));
31939
+ for (let k = 0; k < n; k++) {
31940
+ if (!mean[k]) continue;
31941
+ const first = at(k, 0);
31942
+ const last = at(k, 1);
31943
+ for (let c = 0; c < 3; c++) px[first + c] = px[last + c] = px[first + c] + px[last + c] + 1 >> 1;
31944
+ }
31945
+ }
31946
+ function halftoneKeepLast(px, w, h, rows) {
31947
+ const n = rows ? w : h;
31948
+ const out = new Int32Array(n * 3);
31949
+ for (let k = 0; k < n; k++) {
31950
+ const last = (rows ? w + k : k * w + 1) * 3;
31951
+ for (let c = 0; c < 3; c++) out[k * 3 + c] = px[last + c];
31952
+ }
31953
+ return out;
31954
+ }
31645
31955
  function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
31646
31956
  const W = Math.max(0, Math.round(Math.abs(dw)));
31647
31957
  const H = Math.max(0, Math.round(Math.abs(dh)));
31648
- const SW = Math.round(Math.abs(sw));
31649
- const SH = Math.round(Math.abs(sh));
31958
+ let SW = Math.round(Math.abs(sw));
31959
+ let SH = Math.round(Math.abs(sh));
31650
31960
  const x0 = Math.round(Math.min(sx, sx + sw));
31651
31961
  const y0 = Math.round(Math.min(sy, sy + sh));
31652
31962
  const flipX = dw < 0 !== sw < 0;
@@ -31655,7 +31965,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31655
31965
  if (W === 0 || H === 0 || SW === 0 || SH === 0) {
31656
31966
  return { width: W, height: H, data };
31657
31967
  }
31658
- const rect = new Int32Array(SW * SH * 3);
31968
+ let rect = new Int32Array(SW * SH * 3);
31659
31969
  for (let y = 0; y < SH; y++) {
31660
31970
  const yy = y0 + y;
31661
31971
  for (let x = 0; x < SW; x++) {
@@ -31670,19 +31980,40 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31670
31980
  rect[o + 2] = src.data[i + 2];
31671
31981
  }
31672
31982
  }
31983
+ const pairRows = SH === 2 && H === 1 && W > SW && (!adjust || adjustAfterSampling || !!dither);
31984
+ const pairColumns = SW === 2 && W === 1 && H > SH && (!adjust || adjustAfterSampling || !!dither);
31985
+ if (pairRows || pairColumns) {
31986
+ if (!directDib) halftonePairDespeckle(rect, SW, SH, pairRows);
31987
+ if (pairRows && W >= 2 * SW) {
31988
+ rect = halftoneKeepLast(rect, SW, SH, true);
31989
+ SH = 1;
31990
+ } else if (pairColumns && H >= 2 * SH) {
31991
+ rect = halftoneKeepLast(rect, SW, SH, false);
31992
+ SW = 1;
31993
+ }
31994
+ }
31673
31995
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
31674
31996
  const reducing = W < SW && H < SH;
31675
31997
  const mixed = W > SW && H < SH || W < SW && H > SH;
31676
31998
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31677
31999
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31678
32000
  const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31679
- if ((enlarging || nearestMixed) && !directDib) {
32001
+ if ((enlarging || nearestMixed) && !directDib && !pairRows && !pairColumns) {
31680
32002
  halftoneDespeckle(rect, SW, SH);
31681
32003
  }
31682
32004
  const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
31683
32005
  const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
31684
32006
  if (ditherAtSource) {
31685
- ditherPixels(rect, SW, SH, dither, false, flipY);
32007
+ ditherPixels(
32008
+ rect,
32009
+ SW,
32010
+ SH,
32011
+ dither,
32012
+ false,
32013
+ false,
32014
+ void 0,
32015
+ flipY ? Int32Array.from({ length: SH }, (_, j) => H - 1 - j) : void 0
32016
+ );
31686
32017
  }
31687
32018
  if (adjust && !adjustAfterSampling && (!dithered || ditherAtSource)) {
31688
32019
  adjust(rect);
@@ -31798,17 +32129,13 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31798
32129
  }
31799
32130
  }
31800
32131
  if (dithered && !ditherAtSource) {
31801
- const phase = (n, src2, dst, selectSource) => Int32Array.from({ length: n }, (_, i) => dst > src2 ? Math.floor(i * src2 / dst) : selectSource ? Math.max(0, Math.min(src2 - 1, Math.floor(((i + 1) * 2 * src2 - dst) / (2 * dst)))) : i);
31802
- ditherPixels(
31803
- out,
31804
- W,
31805
- H,
31806
- dither,
31807
- false,
31808
- false,
31809
- nearestMixed ? phase(W, SW, W, true) : void 0,
31810
- nearestMixed ? phase(H, SH, H, false) : void 0
31811
- );
32132
+ const phase = (n, src2, dst, selectSource) => Int32Array.from({ length: n }, (_, i) => dst > src2 ? Math.max(0, Math.ceil((2 * i + 1) * src2 / (2 * dst)) - 1) : selectSource ? Math.max(0, Math.min(src2 - 1, Math.floor(((i + 1) * 2 * src2 - dst) / (2 * dst)))) : i);
32133
+ const sourcePhase = nearestMixed && W * H > SW * SH;
32134
+ let phaseX = sourcePhase ? phase(W, SW, W, true) : void 0;
32135
+ let phaseY = sourcePhase ? phase(H, SH, H, false) : void 0;
32136
+ if (phaseX && flipX) phaseX = phaseX.slice().reverse();
32137
+ if (phaseY && flipY && H > SH) phaseY = phaseY.slice().reverse().map((v) => H - 1 - v);
32138
+ ditherPixels(out, W, H, dither, false, false, phaseX, phaseY);
31812
32139
  adjust(out);
31813
32140
  }
31814
32141
  if (adjust && adjustAfterSampling) adjust(out);
@@ -31871,7 +32198,7 @@ function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode, brushOrg
31871
32198
  canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
31872
32199
  target.restore();
31873
32200
  }
31874
- function decodeSource(rCtx, req) {
32201
+ function decodeSource(rCtx, req, rotated = false) {
31875
32202
  if (!req.source) {
31876
32203
  return null;
31877
32204
  }
@@ -31886,6 +32213,9 @@ function decodeSource(rCtx, req) {
31886
32213
  return null;
31887
32214
  }
31888
32215
  const ca = rCtx.state.colorAdjustment;
32216
+ if (req.stretch && rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32217
+ rCtx.halftoneRotatedAdjusts = !rotated && !!ca;
32218
+ }
31889
32219
  const halftone = req.stretch && rCtx.state.stretchBltMode === HALFTONE && ca;
31890
32220
  const channelOnly = isChannelOnlyColorAdjustment(ca);
31891
32221
  const split = halftone && !channelOnly ? splitColorAdjustment(ca) : void 0;
@@ -32033,6 +32363,7 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32033
32363
  let src = null;
32034
32364
  let srcX = sx;
32035
32365
  let srcY = sy;
32366
+ let adjustTexel;
32036
32367
  if (operands.usesS) {
32037
32368
  const decoded = decodeSource(rCtx, {
32038
32369
  plan: { kind: "ternary", index, operands },
@@ -32047,13 +32378,30 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32047
32378
  sh,
32048
32379
  dibOrigin,
32049
32380
  stretch
32050
- });
32381
+ }, true);
32051
32382
  if (!decoded) {
32052
32383
  return;
32053
32384
  }
32054
32385
  src = decoded.decoded.pixels;
32055
- if (decoded.decoded.adjust) {
32056
- applyColorAdjustment(src, rCtx.state.colorAdjustment);
32386
+ if (decoded.decoded.adjust && rCtx.halftoneRotatedAdjusts) {
32387
+ const { adjust, adjustAfterSampling, curves } = decoded.decoded;
32388
+ const startX = ditherStartX(0, rCtx.state.brushOrgX);
32389
+ const startY = ditherStartY(0, rCtx.state.brushOrgY);
32390
+ const triple = new Int32Array(3);
32391
+ adjustTexel = (rgb2, x, y) => {
32392
+ triple[0] = rgb2 >> 16 & 255;
32393
+ triple[1] = rgb2 >> 8 & 255;
32394
+ triple[2] = rgb2 & 255;
32395
+ if (!adjustAfterSampling) {
32396
+ const t = ditherThreshold(startX, startY, x, y);
32397
+ for (let c = 0; c < 3; c++) {
32398
+ triple[c] = ditherQuantize(triple[c], t);
32399
+ }
32400
+ }
32401
+ adjust(triple);
32402
+ curves?.(triple);
32403
+ return triple[0] << 16 | triple[1] << 8 | triple[2];
32404
+ };
32057
32405
  }
32058
32406
  srcX = decoded.req.sx;
32059
32407
  srcY = decoded.req.sy;
@@ -32077,7 +32425,10 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32077
32425
  sw,
32078
32426
  sh,
32079
32427
  (ix, iy, x, y, d) => {
32080
- const s = operands.usesS ? texel(ix, iy) : 0;
32428
+ let s = operands.usesS ? texel(ix, iy) : 0;
32429
+ if (adjustTexel && operands.usesS) {
32430
+ s = adjustTexel(s, x, y);
32431
+ }
32081
32432
  const p = typeof pattern === "function" ? pattern(x, y) : pattern;
32082
32433
  return evalRop3(index, p, s, d);
32083
32434
  }
@@ -32132,6 +32483,19 @@ function alignMirroredDest(rCtx, req) {
32132
32483
  dy: req.dh < 0 ? req.dy + pxY : req.dy
32133
32484
  };
32134
32485
  }
32486
+ function executePlgBltAsStretch(rCtx, dx, dy, dw, dh, source, r) {
32487
+ executeBlit(rCtx, {
32488
+ plan: classifyRop3(13369376),
32489
+ dx,
32490
+ dy,
32491
+ dw,
32492
+ dh,
32493
+ source,
32494
+ ...r,
32495
+ dibOrigin: "top-left",
32496
+ stretch: true
32497
+ });
32498
+ }
32135
32499
  function executeBlit(rCtx, request) {
32136
32500
  const { ctx } = rCtx;
32137
32501
  const req = alignMirroredDest(rCtx, request);
@@ -32634,6 +32998,18 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32634
32998
  if (b[1] === a[1] && q[0] === a[0]) {
32635
32999
  const W = Math.round(Math.abs(b[0] - a[0]) / 16);
32636
33000
  const H = Math.round(Math.abs(q[1] - a[1]) / 16);
33001
+ if (!mask && rCtx.state.stretchBltMode === HALFTONE && W > 0 && H > 0) {
33002
+ executePlgBltAsStretch(
33003
+ rCtx,
33004
+ Math.round(a[0] / 16),
33005
+ Math.round(a[1] / 16),
33006
+ b[0] < a[0] ? -W : W,
33007
+ q[1] < a[1] ? -H : H,
33008
+ sourceOf(offset, view.getUint32(dataOff + 88, true), view.getUint32(dataOff + 92, true), view.getUint32(dataOff + 96, true), view.getUint32(dataOff + 100, true), view.getUint32(dataOff + 84, true)),
33009
+ r
33010
+ );
33011
+ return;
33012
+ }
32637
33013
  const left = Math.round(a[0] / 16) + (b[0] < a[0] ? 1 - W : 0);
32638
33014
  const top = Math.round(a[1] / 16) + (q[1] < a[1] ? 1 - H : 0);
32639
33015
  const aw = Math.abs(r.sw);
@@ -32654,6 +33030,9 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32654
33030
  });
32655
33031
  return;
32656
33032
  }
33033
+ if (rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
33034
+ rCtx.halftoneRotatedAdjusts = false;
33035
+ }
32657
33036
  paintParallelogram(rCtx, a, b, q, r.sw, r.sh, (ix, iy) => texel(ix, iy));
32658
33037
  }
32659
33038
  function readBand(rCtx, offset, offBmi, cbBmi, offBits, cbBits, usage, scans) {
@@ -34782,6 +35161,7 @@ function flattenBracketPath(rCtx) {
34782
35161
  }
34783
35162
  for (const f of rCtx.rasterPath.figures) {
34784
35163
  delete f.tangents;
35164
+ delete f.curveSegs;
34785
35165
  delete f.roundWiden;
34786
35166
  }
34787
35167
  syncPathCmds(rCtx);
@@ -38873,7 +39253,7 @@ function wmfRectangle(p, l, t, r, b) {
38873
39253
  axisRect: { interior: rectInterior(p, box) }
38874
39254
  });
38875
39255
  }
38876
- function compatCorner(p, w, h, box) {
39256
+ function compatCorner(p, w, h, box, logical) {
38877
39257
  const m = gdiDeviceMatrix(p.rCtx);
38878
39258
  const cw = Math.round(Math.abs(w * m[0]) * 16);
38879
39259
  const ch = Math.round(Math.abs(h * m[3]) * 16);
@@ -38881,24 +39261,21 @@ function compatCorner(p, w, h, box) {
38881
39261
  const uy = 16 * p.ky;
38882
39262
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38883
39263
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38884
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38885
- return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38886
- }
38887
- if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38888
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38889
- return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
39264
+ const width2 = box.x1 - box.x0, height2 = box.y1 - box.y0;
39265
+ return [Math.floor(Math.min(cw, width2 + penWidth) * width2 / (width2 + penWidth)), Math.floor(Math.min(ch, height2 + penWidth) * height2 / (height2 + penWidth))];
38890
39266
  }
38891
- if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
38892
- return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
38893
- }
38894
- return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
39267
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
39268
+ const fullW = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width + ux;
39269
+ const fullH = Math.abs((logical[3] - logical[1]) * m[3]) * 16 || height + uy;
39270
+ const scaled2 = [Math.min(Math.abs(w * m[0]) * 16, fullW) * width / fullW, Math.min(Math.abs(h * m[3]) * 16, fullH) * height / fullH];
39271
+ return penIsCosmetic(p.rCtx) && !penIsNull(p) ? scaled2 : [Math.floor(scaled2[0]), Math.floor(scaled2[1])];
38895
39272
  }
38896
39273
  function wmfRoundRect(p, l, t, r, b, w, h) {
38897
39274
  const box = compatBox(p, l, t, r, b, { curved: true });
38898
39275
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38899
39276
  return;
38900
39277
  }
38901
- const [cw, ch] = compatCorner(p, w, h, box);
39278
+ const [cw, ch] = compatCorner(p, w, h, box, [l, t, r, b]);
38902
39279
  const cr = canvasRect(box);
38903
39280
  paintGdiShape(p.rCtx, {
38904
39281
  build: (c) => {
@@ -38951,10 +39328,14 @@ function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
38951
39328
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38952
39329
  return null;
38953
39330
  }
38954
- const raw = compatBox(p, l, t, r, b, { exclusive: false });
39331
+ const m = gdiDeviceMatrix(p.rCtx);
39332
+ const deviceFix = (x, y) => [(m[0] * x + m[2] * y + m[4]) * 16, (m[1] * x + m[3] * y + m[5]) * 16];
39333
+ const [ax, ay] = deviceFix(l, t);
39334
+ const [bx, by] = deviceFix(r, b);
39335
+ const raw = { x0: Math.min(ax, bx), x1: Math.max(ax, bx), y0: Math.min(ay, by), y1: Math.max(ay, by) };
38955
39336
  const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
38956
39337
  const radial = (x, y) => {
38957
- const [fx, fy] = fixPoint(p.rCtx, x, y);
39338
+ const [fx, fy] = deviceFix(x, y);
38958
39339
  return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
38959
39340
  };
38960
39341
  return { box, s: radial(xs, ys), e: radial(xe, ye) };