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