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