emf-converter 4.7.0 → 4.8.1

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