emf-converter 4.6.10 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.mjs CHANGED
@@ -11773,19 +11773,73 @@ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = fa
11773
11773
  ];
11774
11774
  }
11775
11775
  var TRIG_STEP = 2 * Math.PI / 128;
11776
- function tableTrig(fn) {
11776
+ var TRIG_NODES = [
11777
+ 1,
11778
+ 0.99879566,
11779
+ 0.99518639,
11780
+ 0.98917961,
11781
+ 0.98079146,
11782
+ 0.97003981,
11783
+ 0.95695228,
11784
+ 0.94155978,
11785
+ 0.92389844,
11786
+ 0.90401145,
11787
+ 0.88194619,
11788
+ 0.85775575,
11789
+ 0.83149879,
11790
+ 0.80323775,
11791
+ 0.77303903,
11792
+ 0.74098864,
11793
+ 0.70710648,
11794
+ 0.67151859,
11795
+ 0.63435836,
11796
+ 0.59565849,
11797
+ 0.55552672,
11798
+ 0.51405765,
11799
+ 0.47135039,
11800
+ 0.42750875,
11801
+ 0.38263811,
11802
+ 0.33684632,
11803
+ 0.29024443,
11804
+ 0.24294646,
11805
+ 0.19505974,
11806
+ 0.14670808,
11807
+ 0.0980012,
11808
+ 0.04906089,
11809
+ 0
11810
+ ];
11811
+ var TRIG_TABLE = (() => {
11812
+ const cos = [];
11813
+ const sin = [];
11814
+ for (let k = 0; k <= 128; k++) {
11815
+ const q = k >> 5;
11816
+ const i = k & 31;
11817
+ const c = TRIG_NODES[i];
11818
+ const s = TRIG_NODES[32 - i];
11819
+ if (k === 128) {
11820
+ cos.push(1);
11821
+ sin.push(0);
11822
+ continue;
11823
+ }
11824
+ cos.push([c, -s, -c, s][q]);
11825
+ sin.push([s, c, -s, -c][q]);
11826
+ }
11827
+ return { cos, sin };
11828
+ })();
11829
+ function tableTrig(table) {
11777
11830
  return (a) => {
11778
11831
  let b = a % (2 * Math.PI);
11779
11832
  if (b < 0) {
11780
11833
  b += 2 * Math.PI;
11781
11834
  }
11782
- const i = Math.floor(b / TRIG_STEP);
11783
- const f = b / TRIG_STEP - i;
11784
- return fn(i * TRIG_STEP) * (1 - f) + fn((i + 1) * TRIG_STEP) * f;
11835
+ const x = b / TRIG_STEP;
11836
+ const i = Math.min(127, Math.floor(x));
11837
+ const f = x - i;
11838
+ return table[i] * (1 - f) + table[i + 1] * f;
11785
11839
  };
11786
11840
  }
11787
- var tableCos = tableTrig(Math.cos);
11788
- var tableSin = tableTrig(Math.sin);
11841
+ var tableCos = tableTrig(TRIG_TABLE.cos);
11842
+ var tableSin = tableTrig(TRIG_TABLE.sin);
11789
11843
  function angleArcPieces(startDeg, sweepDeg) {
11790
11844
  const out = [];
11791
11845
  const arc = (from, to) => {
@@ -11856,6 +11910,38 @@ function angleArcFix(box, startDeg, sweepDeg) {
11856
11910
  }
11857
11911
  return out;
11858
11912
  }
11913
+ var SMALL_SWEEP = 3 * Math.PI / 180;
11914
+ function anchoredPiece(boundary, free, rx, ry, cx, cy, smallArc) {
11915
+ const sweep = Math.abs(free - boundary);
11916
+ const small = smallArc;
11917
+ const cos = small ? Math.cos : tableCos;
11918
+ const sin = small ? Math.sin : tableSin;
11919
+ const anchor = [Math.round(cx + rx * Math.cos(boundary)), Math.round(cy - ry * Math.sin(boundary))];
11920
+ const point = [cx + rx * cos(free), cy - ry * sin(free)];
11921
+ let t;
11922
+ let kappa;
11923
+ if (small) {
11924
+ t = Math.tan(sweep / 2);
11925
+ kappa = 2 / 3 * (1 - Math.tan(sweep / 4) ** 2);
11926
+ } else {
11927
+ const c = tableCos(sweep);
11928
+ const sn = tableSin(sweep);
11929
+ t = (1 - c) / sn;
11930
+ const q = t / (1 + Math.sqrt(1 + t * t));
11931
+ kappa = 2 / 3 * (1 - q * q);
11932
+ }
11933
+ const dir = free > boundary ? 1 : -1;
11934
+ const tx = -Math.round(Math.sin(boundary));
11935
+ const ty = -Math.round(Math.cos(boundary));
11936
+ const length = tx !== 0 ? rx : ry;
11937
+ const X = [anchor[0] + dir * tx * length * t, anchor[1] + dir * ty * length * t];
11938
+ return {
11939
+ anchor,
11940
+ free: point,
11941
+ cAnchor: [anchor[0] + kappa * (X[0] - anchor[0]), anchor[1] + kappa * (X[1] - anchor[1])],
11942
+ cFree: [point[0] + kappa * (X[0] - point[0]), point[1] + kappa * (X[1] - point[1])]
11943
+ };
11944
+ }
11859
11945
  function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11860
11946
  const w = r - l;
11861
11947
  const h = b - t;
@@ -11894,6 +11980,21 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11894
11980
  continue;
11895
11981
  }
11896
11982
  const e = s > 0 ? Math.min(next, a1) : Math.max(next, a1);
11983
+ const endOnBoundary = Math.abs(e / Q - Math.round(e / Q)) < 1e-9;
11984
+ if (onBoundary !== endOnBoundary) {
11985
+ const piece = anchoredPiece(onBoundary ? a : e, onBoundary ? e : a, rx, ry, cx, cy, Math.abs(a1 - a0) <= SMALL_SWEEP);
11986
+ if (onBoundary) {
11987
+ out.push(...[piece.cAnchor, piece.cFree, piece.free].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11988
+ } else {
11989
+ if (out.length === 2) {
11990
+ out[0] = Math.round(piece.free[0]);
11991
+ out[1] = Math.round(piece.free[1]);
11992
+ }
11993
+ out.push(...[piece.cFree, piece.cAnchor, piece.anchor].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11994
+ }
11995
+ a = e;
11996
+ continue;
11997
+ }
11897
11998
  const k = 4 / 3 * Math.tan((e - a) / 4);
11898
11999
  out.push(
11899
12000
  Math.round(px(a) - k * rx * tableSin(a)),
@@ -11988,6 +12089,10 @@ var GdiRasterPath = class {
11988
12089
  };
11989
12090
  const t0 = tangent(x0, y0, c1x, c1y, c2x, c2y, x3, y3);
11990
12091
  const t1 = tangent(c2x, c2y, x3, y3, x3, y3, x3, y3) ?? tangent(c1x, c1y, x3, y3, x3, y3, x3, y3) ?? tangent(x0, y0, x3, y3, x3, y3, x3, y3);
12092
+ f.curveSegs ?? (f.curveSegs = /* @__PURE__ */ new Set());
12093
+ for (let k = first; k <= last; k++) {
12094
+ f.curveSegs.add(k);
12095
+ }
11991
12096
  f.tangents ?? (f.tangents = /* @__PURE__ */ new Map());
11992
12097
  if (t0) f.tangents.set(first, t0);
11993
12098
  if (t1) f.tangents.set(last, t1);
@@ -12021,7 +12126,7 @@ var GdiRasterPath = class {
12021
12126
  /** Appends every figure of `other`. */
12022
12127
  append(other) {
12023
12128
  for (const f of other.figures) {
12024
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
12129
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), curveSegs: f.curveSegs && new Set(f.curveSegs), roundWiden: f.roundWiden });
12025
12130
  }
12026
12131
  for (let i = 0; i < other.getPath.types.length; i++) {
12027
12132
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -16438,23 +16543,241 @@ function applyTint(src, hue, amount) {
16438
16543
  function applyRedEyeCorrection(src, width, height, areas) {
16439
16544
  const out = new Uint8ClampedArray(src);
16440
16545
  for (const a of areas) {
16441
- const x0 = clamp(a.left, 0, width);
16442
- const x1 = clamp(a.right, 0, width);
16443
- const y0 = clamp(a.top, 0, height);
16444
- const y1 = clamp(a.bottom, 0, height);
16445
- for (let y = y0; y < y1; y++) {
16446
- for (let x = x0; x < x1; x++) {
16447
- const i = (y * width + x) * 4;
16448
- const r = out[i];
16449
- const g = out[i + 1];
16450
- const b = out[i + 2];
16451
- if (r >= 2 * g && r >= 2 * b && r > 64) {
16452
- out[i] = Math.round((g + b) / 2);
16546
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16547
+ }
16548
+ return out;
16549
+ }
16550
+ var RED_EYE_FALLOFF = new Float64Array(
16551
+ [
16552
+ 24930,
16553
+ 24570,
16554
+ 24201,
16555
+ 23836,
16556
+ 23471,
16557
+ 23107,
16558
+ 22744,
16559
+ 22367,
16560
+ 21976,
16561
+ 21587,
16562
+ 21198,
16563
+ 20824,
16564
+ 20490,
16565
+ 20165,
16566
+ 19832,
16567
+ 19499,
16568
+ 19157,
16569
+ 18817,
16570
+ 18480,
16571
+ 18151,
16572
+ 17820,
16573
+ 17490,
16574
+ 17160,
16575
+ 16831,
16576
+ 16506,
16577
+ 16189,
16578
+ 15878,
16579
+ 15569,
16580
+ 15273,
16581
+ 14973,
16582
+ 14668,
16583
+ 14362,
16584
+ 14059,
16585
+ 13768,
16586
+ 13484,
16587
+ 13203,
16588
+ 12923,
16589
+ 12642,
16590
+ 12366,
16591
+ 12094,
16592
+ 11828,
16593
+ 11564,
16594
+ 11301,
16595
+ 11034,
16596
+ 10770,
16597
+ 10507,
16598
+ 10253,
16599
+ 10011,
16600
+ 9771,
16601
+ 9530,
16602
+ 9290,
16603
+ 9053,
16604
+ 8819,
16605
+ 8588,
16606
+ 8359,
16607
+ 8134,
16608
+ 7914,
16609
+ 7697,
16610
+ 7481,
16611
+ 7267,
16612
+ 7056,
16613
+ 6851,
16614
+ 6651,
16615
+ 6453,
16616
+ 6256,
16617
+ 6061,
16618
+ 5871,
16619
+ 5684,
16620
+ 5499,
16621
+ 5317,
16622
+ 5138,
16623
+ 4962,
16624
+ 4789,
16625
+ 4619,
16626
+ 4452,
16627
+ 4289,
16628
+ 4130,
16629
+ 3974,
16630
+ 3820,
16631
+ 3669,
16632
+ 3520,
16633
+ 3375,
16634
+ 3232,
16635
+ 3093,
16636
+ 2958,
16637
+ 2825,
16638
+ 2695,
16639
+ 2568,
16640
+ 2444,
16641
+ 2325,
16642
+ 2207,
16643
+ 2092,
16644
+ 1980,
16645
+ 1872,
16646
+ 1768,
16647
+ 1666,
16648
+ 1567,
16649
+ 1470,
16650
+ 1377,
16651
+ 1287,
16652
+ 1200,
16653
+ 1116,
16654
+ 1034,
16655
+ 956,
16656
+ 881,
16657
+ 808,
16658
+ 739,
16659
+ 674,
16660
+ 612,
16661
+ 541,
16662
+ 462,
16663
+ 381,
16664
+ 299,
16665
+ 220,
16666
+ 147,
16667
+ 85,
16668
+ 36,
16669
+ 3,
16670
+ 0
16671
+ ].map((v) => v / 1e5)
16672
+ );
16673
+ function redEyeFalloff(u) {
16674
+ const t = u * 128;
16675
+ const i = Math.floor(t);
16676
+ if (i >= RED_EYE_FALLOFF.length - 1) {
16677
+ return 0;
16678
+ }
16679
+ return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16680
+ }
16681
+ var RED_EYE_SECTORS = 60;
16682
+ var RED_EYE_BLUE_WEIGHT = 0.225;
16683
+ var RED_EYE_WEIGHT_CAP = 6;
16684
+ var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16685
+ var RED_EYE_DARK_STRENGTH = 0.49;
16686
+ var RED_EYE_DARK_RATIO = 0.6;
16687
+ var RED_EYE_GREEN_SHARE = 80 / 255;
16688
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16689
+ const w = x1 - x0;
16690
+ const h = y1 - y0;
16691
+ const count = w * h;
16692
+ const redness = new Float64Array(count);
16693
+ const dark = new Float64Array(count);
16694
+ let sx = 0;
16695
+ let sy = 0;
16696
+ let sw = 0;
16697
+ for (let y = 0; y < h; y++) {
16698
+ for (let x = 0; x < w; x++) {
16699
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16700
+ const r = out[i];
16701
+ const g = out[i + 1];
16702
+ const b = out[i + 2];
16703
+ const k = y * w + x;
16704
+ dark[k] = Math.min(g, b);
16705
+ const v = r - Math.max(g, b);
16706
+ if (v > 0) {
16707
+ redness[k] = v;
16708
+ const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16709
+ {
16710
+ const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16711
+ sx += weight * (x + 0.5);
16712
+ sy += weight * (y + 0.5);
16713
+ sw += weight;
16453
16714
  }
16454
16715
  }
16455
16716
  }
16456
16717
  }
16457
- return out;
16718
+ if (sw === 0) {
16719
+ if (!redness.some((v) => v > 0)) {
16720
+ return;
16721
+ }
16722
+ sx = w / 2;
16723
+ sy = h / 2;
16724
+ sw = 1;
16725
+ }
16726
+ const cx = sx / sw;
16727
+ const cy = sy / sw;
16728
+ const radius = Math.min(cx, cy) - 0.5;
16729
+ if (!(radius > 0)) {
16730
+ return;
16731
+ }
16732
+ const sector = new Uint8Array(count);
16733
+ const distance = new Float64Array(count);
16734
+ const sectorSum = new Float64Array(RED_EYE_SECTORS);
16735
+ const sectorDark = new Float64Array(RED_EYE_SECTORS);
16736
+ const sectorCount = new Float64Array(RED_EYE_SECTORS);
16737
+ const degrees = 180 / Math.PI;
16738
+ for (let y = 0; y < h; y++) {
16739
+ for (let x = 0; x < w; x++) {
16740
+ const dx = x + 0.5 - cx;
16741
+ const dy = y + 0.5 - cy;
16742
+ const k = y * w + x;
16743
+ const d = Math.hypot(dx, dy);
16744
+ distance[k] = d;
16745
+ let angle = Math.atan2(dy, dx) * degrees;
16746
+ if (angle < 0) {
16747
+ angle += 360;
16748
+ }
16749
+ const s = Math.min(RED_EYE_SECTORS - 1, Math.floor(angle / 6));
16750
+ sector[k] = s;
16751
+ if (d < radius) {
16752
+ sectorSum[s] += redness[k];
16753
+ sectorDark[s] += dark[k];
16754
+ sectorCount[s]++;
16755
+ }
16756
+ }
16757
+ }
16758
+ for (let y = 0; y < h; y++) {
16759
+ for (let x = 0; x < w; x++) {
16760
+ const k = y * w + x;
16761
+ const v = redness[k];
16762
+ if (v <= 0 || distance[k] >= radius) {
16763
+ continue;
16764
+ }
16765
+ const s = sector[k];
16766
+ const mean = sectorSum[s] / sectorCount[s];
16767
+ const meanDark = sectorDark[s] / sectorCount[s];
16768
+ const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16769
+ const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16770
+ const rest = (1 - falloff) * mean;
16771
+ if (rest >= v) {
16772
+ continue;
16773
+ }
16774
+ const removed = v - rest;
16775
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16776
+ out[i] = Math.round(out[i] - (1 - RED_EYE_GREEN_SHARE) * removed);
16777
+ out[i + 1] = Math.round(out[i + 1] + RED_EYE_GREEN_SHARE * removed);
16778
+ out[i + 2] = Math.round(out[i + 2] + RED_EYE_GREEN_SHARE * removed);
16779
+ }
16780
+ }
16458
16781
  }
16459
16782
  function blurKernel(radius) {
16460
16783
  const taps = Math.ceil(radius);
@@ -16486,8 +16809,8 @@ function mirror(p, n) {
16486
16809
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows = Math.ceil(h / w)) {
16487
16810
  const k = blurKernel(radius);
16488
16811
  const taps = (k.length - 1) / 2;
16489
- const horizontalIndex = taps >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16490
- const verticalIndex = taps >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16812
+ const horizontalIndex = (taps | 1) >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16813
+ const verticalIndex = (taps | 1) >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16491
16814
  const out = new Float64Array(w * h * 4);
16492
16815
  for (let y = 0; y < h; y++) {
16493
16816
  const row2 = y * w * 4;
@@ -16498,11 +16821,12 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16498
16821
  let s3 = 0;
16499
16822
  for (let j = -taps; j <= taps; j++) {
16500
16823
  const p = row2 + horizontalIndex(x + j) * 4;
16501
- const kw = k[j + taps] * 256;
16502
- s0 += Math.round(kw * src[p]);
16503
- s1 += Math.round(kw * src[p + 1]);
16504
- s2 += Math.round(kw * src[p + 2]);
16505
- s3 += Math.round(kw * src[p + 3]);
16824
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16825
+ const times = j === 0 ? 2 : 1;
16826
+ s0 += times * Math.round(kw * src[p]);
16827
+ s1 += times * Math.round(kw * src[p + 1]);
16828
+ s2 += times * Math.round(kw * src[p + 2]);
16829
+ s3 += times * Math.round(kw * src[p + 3]);
16506
16830
  }
16507
16831
  const o = row2 + x * 4;
16508
16832
  out[o] = Math.floor((s0 + 128) / 256);
@@ -16522,8 +16846,10 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16522
16846
  continue;
16523
16847
  }
16524
16848
  const base = verticalIndex(yy) * w * 4;
16849
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16850
+ const times = j === 0 ? 2 : 1;
16525
16851
  for (let i = 0; i < w * 4; i++) {
16526
- col[i] += Math.round(k[j + taps] * horizontal[base + i] * 256) / 256;
16852
+ col[i] += times * Math.round(kw * horizontal[base + i]) / 256;
16527
16853
  }
16528
16854
  }
16529
16855
  out.set(col, row2 * w * 4);
@@ -16538,10 +16864,12 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16538
16864
  const inner = Math.ceil(span.size / factor);
16539
16865
  const n = inner + 2 * span.pad;
16540
16866
  const taps = (k.length - 1) / 2;
16541
- const index = taps >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16867
+ const index = (taps | 1) >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16542
16868
  const reduced = new Float64Array(n);
16543
16869
  const filtered = new Float64Array(n);
16544
16870
  const k256 = k.map((w) => w * 256);
16871
+ const half = k256[taps] / 2;
16872
+ k256[taps] = 0;
16545
16873
  for (let lane = 0; lane < lanes; lane++) for (let c = 0; c < 4; c++) {
16546
16874
  for (let b = span.pad; b < span.pad + inner; b++) {
16547
16875
  const from = span.start + (b - span.pad) * factor;
@@ -16555,33 +16883,48 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16555
16883
  if (b >= taps && b + taps < n) {
16556
16884
  for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[b - taps + j]);
16557
16885
  } else {
16558
- for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[index(b - taps + j)]);
16886
+ for (let j = 0; j < k256.length; j++) {
16887
+ const at = b - taps + j;
16888
+ sum += Math.round(k256[j] * (span.zeroOutside && (at < 0 || at >= n) ? 0 : reduced[index(at)]));
16889
+ }
16559
16890
  }
16891
+ sum += 2 * Math.round(half * reduced[b]);
16560
16892
  filtered[b] = Math.floor((sum + 128) / 256);
16561
16893
  }
16894
+ const lone = inner === 1 && span.pad > 0;
16895
+ const skipped2 = inner === 1 && span.size - Math.ceil(factor / 2) === 1 ? span.start + span.size - 1 : -1;
16562
16896
  for (let i = 0; i < length; i++) {
16563
- const p = (i - span.start + 0.5) / factor - 0.5 + span.pad;
16564
- const a = clamp(Math.floor(p), 0, Math.max(0, n - 2));
16897
+ if (i === skipped2) {
16898
+ if (span.pad > 0) put(i, lane, c, 0);
16899
+ continue;
16900
+ }
16901
+ const p = (i - span.start + 0.5) / factor - 0.5;
16902
+ if (lone) {
16903
+ const distance = Math.abs(i - span.start - (Math.min(span.size, factor / 2) - 0.5)) / factor;
16904
+ put(i, lane, c, clamp(Math.floor((1 + distance) * filtered[span.pad] + 1e-9), 0, 255));
16905
+ continue;
16906
+ }
16907
+ const a = clamp(Math.floor(p), 0, Math.max(0, inner - 2));
16565
16908
  const t = p - a;
16566
- put(i, lane, c, clamp(Math.floor((1 - t) * filtered[a] + t * filtered[Math.min(n - 1, a + 1)] + 1e-9), 0, 255));
16909
+ put(i, lane, c, clamp(Math.floor((1 - t) * filtered[span.pad + a] + t * filtered[span.pad + Math.min(inner - 1, a + 1)] + 1e-9), 0, 255));
16567
16910
  }
16568
16911
  }
16569
16912
  }
16570
- function effectBlur(src, width, height, radius, region) {
16913
+ function effectBlur(src, width, height, radius, region, expanded = false) {
16571
16914
  const factor = blurReduction(radius);
16572
16915
  if (factor === 1) {
16573
16916
  return gdipBlur(src, width, height, radius);
16574
16917
  }
16575
16918
  const k = blurKernel(radius / factor);
16576
16919
  const pad = region ? Math.ceil(Math.ceil(radius) / factor) : 0;
16577
- const spanX = region ? { start: region.x, size: region.w, pad } : void 0;
16578
- const spanY = region ? { start: region.y, size: region.h, pad } : void 0;
16579
- const rows = new Uint8Array(src.length);
16580
- blurAxis(width, height, factor, k, Math.floor(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16920
+ const spanX = region ? { start: region.x, size: region.w, pad, zeroOutside: true } : expanded ? { start: 0, size: width, pad: 0, zeroOutside: true } : void 0;
16921
+ const spanY = region ? { start: region.y, size: region.h, pad, zeroOutside: true } : expanded ? { start: 0, size: height, pad: 0, zeroOutside: true } : void 0;
16922
+ const rows = new Uint8Array(src);
16923
+ blurAxis(width, height, factor, k, Math.ceil(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16581
16924
  rows[(lane * width + i) * 4 + c] = v;
16582
16925
  }, spanX);
16583
- const out = new Float64Array(src.length);
16584
- blurAxis(height, width, factor, k, Math.floor(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16926
+ const out = Float64Array.from(rows);
16927
+ blurAxis(height, width, factor, k, Math.ceil(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16585
16928
  out[(i * width + lane) * 4 + c] = v;
16586
16929
  }, spanY);
16587
16930
  return out;
@@ -16669,7 +17012,7 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
16669
17012
  const gy1 = Math.min(height, y1 + r);
16670
17013
  copyBlock(rgba, width, gx0, gy0, gx1 - gx0, gy1 - gy0, buf, bw, gx0 - (x0 - r), gy0 - (y0 - r));
16671
17014
  }
16672
- const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
17015
+ const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0, true) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
16673
17016
  const w = x1 - x0;
16674
17017
  const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
16675
17018
  for (let y = 0; y < y1 - y0; y++) {
@@ -28734,118 +29077,19 @@ var HOBBY = [
28734
29077
  [[8, -48], [-8, -48], [-24, -40], [-40, -24], [-48, -8], [-48, 8], [-40, 24], [-24, 40], [-8, 48], [8, 48], [24, 40], [40, 24], [48, 8], [48, -8], [40, -24], [24, -40]]
28735
29078
  ];
28736
29079
  var HOBBY_LIMIT = 104;
28737
- var FLAT_SECTOR_BIAS = {
28738
- 7: [9, "cf..", "edededEdcDcdcd"],
28739
- 8: [9, "dFee", "ededededbdbdbd"],
28740
- 9: [9, "df..", "ddddddddbDbdbd"],
28741
- 10: [9, "de..", "dededeDebEbebE"],
28742
- 11: [9, "ce..", "eeeeeeEececece"],
28743
- 12: [13, "ce..", "eeeeeeeeeeeececEcecece"],
28744
- 13: [17, "ce..", "eeeeededdddddeDebebebdbdcdcdce"],
28745
- 14: [17, "df..", "ddedeeeededeeeedcDcebebecececd"],
28746
- 15: [17, "dF..", "ddedeeeeeeeedeDdbdbecececececd"],
28747
- 16: [17, "ce..", "eeeededededeeeeececebebebebece"],
28748
- 17: [17, "df..", "ddedededededdeddbdbebdbdbdbdcd"],
28749
- 18: [17, "de..", "deeededeededeeEdcDcecdcdbebece"],
28750
- 19: [17, "cf..", "ededeeeedddddeDebEbecdcdbebecd"],
28751
- 20: [17, "ce..", "eeeeddddeeeeddDdbdbdcecebdbdce"],
28752
- 21: [17, "dF..", "ddddededededddDdbdbdcdcdcdcdbd"],
28753
- 22: [17, "ce..", "eeeededeeeeedeDebebececebebece"],
28754
- 23: [17, "cf..", "edededededededEdcDcdcdcdcdcdcD"],
28755
- 24: [17, "dF..", "ddddddddddddddDdbdbdbdbdbdbdbd"],
28756
- 25: [17, "cE..", "eeeeededededeeEecEcecdcdcdcdce"],
28757
- 26: [17, "cf..", "ededdedeededdedebEbecdcdbebecd"],
28758
- 27: [17, "de..", "dedeededdedeededcDcdbebecdcdbe"],
28759
- 28: [17, "cf..", "ededddddeeeedeDebEbebebecdcdcd"],
28760
- 29: [17, "ce..", "eeeeeeeededededebebebebececece"],
28761
- 30: [17, "dF..", "dddddedeeeeeedEdcdcdcecebebebd"],
28762
- 31: [17, "de..", "dedeededededdeDebebecdcdcdcdbe"],
28763
- 32: [17, "cf..", "ededdededdddeeeecEcebdbdbebecd"],
28764
- 33: [17, "de..", "dedeeeeeededddDdbdbdcdcdcecebe"],
28765
- 34: [17, "de..", "dedeeeeeddddedEdcdcdbdbdcecebe"],
28766
- 35: [17, "cf..", "ededededdededeDebebebebecdcdcd"],
28767
- 36: [17, "deee", "eeeeeeeeddddddDdcdcdcdcDbebebe"],
28768
- 37: [17, "dF..", "dddddededededdDdbdbdcecececebd"],
28769
- 38: [17, "ce..", "eeeeddddddddeeEececebdbdbdbdce"],
28770
- 39: [17, "cF..", "ededddddddddededcdcdbdbDbdbdcd"],
28771
- 40: [17, "de..", "dededededdddddddbdbdbdbdbebebe"],
28772
- 41: [17, "ce..", "eeeeeeeeeeeeeeEececececececece"],
28773
- 42: [17, "cf..", "ededeeeedddddedebebebdbdcececd"],
28774
- 43: [17, "df..", "ddddeeeededeedEdcdcdbebececebd"],
28775
- 44: [17, "cF..", "ededdedeededdeDebebecdcdbebecd"],
28776
- 45: [17, "ce..", "eeeededededeeeeecEcebebebebece"],
28777
- 46: [17, "dF..", "ddddededddddedEdcDcdcdcdbdbdbd"],
28778
- 47: [17, "ce..", "dedededededededececececececece"],
28779
- 48: [25, "cf..", "ededeeeeeeeeededeeeeeeEecececececdcdcececececd"],
28780
- 49: [25, "cF..", "ededddddededeeeeededdeDebebecdcdcececdcdbdbdcd"],
28781
- 50: [25, "ce..", "eeeeddddededdededededeDebebebebebEbecdcdbdbdce"],
28782
- 51: [25, "ce..", "eeeeededddddeeeededeeeEececebebececebdbdcdcdce"],
28783
- 52: [25, "ce..", "eeeededededeededededeeEecececdcdcdcdbebebebece"],
28784
- 53: [25, "dF..", "ddedeeeedddddddddedeeeEdcdcebebebdbDbdbdcececd"],
28785
- 54: [25, "dF..", "ddedddddededededdddddEDdbdbebdbdcdcdcdcdbdbdcd"],
28786
- 55: [25, "dF..", "ddeddedeeeeeeeeedededeDdbdbebebebebebebebebecd"],
28787
- 56: [25, "dE..", "deeeeeeeededeeeededeeEedcdcebebecececdcdcecece"],
28788
- 57: [25, "de..", "deeedddddddddedeededeeEdcDcecDcdcececdcdbdbDce"],
28789
- 58: [25, "dF..", "ddedddddeeeedddddedeeeEdcdcebebebdbdcecebdbdcd"],
28790
- 59: [33, "cF..", "ededdedeededddddeeeeeeeedddddeDebebebdbdcecececebdbdcdcdbebecd"],
28791
- 60: [33, "cf..", "ededeeeeededededddddeeeeeeeedeDebebececececebdbdcdcdcdcdcececd"],
28792
- 61: [33, "ce..", "eeeeeeeeddddeeeeeeeeddddeeeeeeeececececebdbdcecececebdbdcecece"],
28793
- 62: [33, "ce..", "eeeeddddeeeeededededeeeeddddeeEecEcebdbdcececdcdcdcdcecebdbdce"],
28794
- 63: [33, "cf..", "ededededeeeededededeeeeeeeeeeeEececececececebebEbebecececdcdcd"],
28795
- 64: [33, "cF..", "ededeeeedddddedeeeeededeeeeedeDebebececebebececebebebdbdcececd"],
28796
- 65: [33, "df..", "ddedededededddddddddeeeeeeeedeDdbdbececececebdbdbdbdcdcdcdcdcd"],
28797
- 66: [33, "de..", "deeeededdedeeeeedddddededddddeDdbdbebdbdbebecdcdbebebebecdcdce"],
28798
- 67: [33, "de..", "deeeddddddddededdddddededdddeeEdcDcebdbdbebebdbdcdcdbdbdbdbdce"],
28799
- 68: [33, "dE..", "deeeeeeeeeeeeeeeddddeeeeeeeeeeEdcdcececececebdbdcecececececece"],
28800
- 69: [33, "df..", "ddedededdedeeeeeddddededededdeDdbdbecdcdcdcdbdbdcecebebecdcdcd"],
28801
- 70: [33, "dF..", "ddeddddddddddddddededededddddeDdbdbebdbdbebebebebdbdbdbdbdbDcd"],
28802
- 71: [33, "ce..", "eeeeddddeeeededeeeeededeededdeDebebecdcdbebececebebececebdbdce"],
28803
- 72: [33, "ce..", "eeeededededeededededddddddddeeEececebdbdbdbdcdcdcdcdbebebebece"],
28804
- 73: [33, "ce..", "eeeeededededddddededdedeeeeeeeEececececebebecdcdbdbdcdcDcdcdce"],
28805
- 74: [33, "cf..", "ededddddededededededdededddddeDebebebdbdbebecdcDcdcdcdcdbdbdcd"],
28806
- 75: [33, "cF..", "ededdededededdddddddeeeedddddeDebebebdbdcececdcdcdcdbebebebecd"],
28807
- 76: [33, "ce..", "eeeeeeeeeeeeeeeeeeeededeeeeedeDebebececebebecececececececececE"],
28808
- 77: [33, "ce..", "eeeeeeeedededddddededdddedededEdcDcdcdcdbdbdbebebdbdbebececece"],
28809
- 78: [33, "de..", "dedeededdedededeeeeeeeeededeedEdcdcdbebececececebebebebecdcdbe"],
28810
- 79: [33, "dF..", "ddddddddededdededdddeeeeedededEdcDcdcdcdcecebdbdbebecdcdbdbdbd"],
28811
- 80: [33, "dF..", "dddddededdddeeeeddddddddededddDdbdbdcdcdbdbdbdbDcecebdbdbebEbd"],
28812
- 81: [33, "de..", "dedeeeeeeeeededeededeeeedededdDdbdbdbebecececdcdbebececececebe"],
28813
- 82: [33, "de..", "dedeededdedeededdedededeedededEdcdcdcdcdbebebebecdcdbebecdcDbe"],
28814
- 83: [33, "de..", "dededededddddededdddeeeededeeeEececebebececebdbdbebebdbdbebebe"],
28815
- 84: [33, "cF..", "ededddddeeeeddddeeeeeeeeededeeEecececdcdcecebebecdcdcecebdbdcd"],
28816
- 85: [33, "cf..", "ededdddddededddddddddedeeeeedeDebebececebebebdbdbdbdbebebdbdcd"],
28817
- 86: [33, "ce..", "eeeeededddddeeeeeeeedededededeDebebebebebebebebebebebdbdcdcdce"],
28818
- 87: [33, "ce..", "eeeeddddeeeeddddddddeeeeddddeeeececebdbdcecebdbDbdbdcecebdbdce"],
28819
- 88: [33, "ce..", "eeeedededdddeeeeededededededeeeecececdcdcdcdcdcdcecebdbdbebece"],
28820
- 89: [33, "cF..", "ededeeeeddddddddededdedededeeeEecEcebebEbebecdcdbdbdbdbdcececd"],
28821
- 90: [33, "dF..", "ddddededeeeeededeeeedededededdDdbdbdbebebEbecececdcdcececdcdbd"],
28822
- 91: [33, "df..", "ddddeeeededededeededdddddededdDdbdbdbebebdbdcdcdbebebebececebd"],
28823
- 92: [33, "de..", "dedededeededdddddededededdddedEdcdcdbdbdbebebebebdbdcdcdbebebe"],
28824
- 93: [33, "de..", "dededddddddddedeededeeeeeeeeededcdcdcececececdcdbebebdbdbdbdbe"],
28825
- 94: [33, "dF..", "ddddeeeeddddeeeeddddeeeededeedEdcDcdbebececebdbDcecebdbdcecebd"],
28826
- 95: [33, "df..", "ddddddddededeeeedddddededdddddDdbdbDbdbdbebecdcdbebecdcdbdbdbd"],
28827
- 96: [33, "cf..", "ededeeeededeeeeedededdddeeeedeDebebececebdbdbebececebebecececd"],
28828
- 97: [33, "ce..", "eeeedededededededdddeeeedddddeDebebebdbdcEcebdbdbebebEbebebece"],
28829
- 98: [33, "ce..", "eeeeededededeeeeeeeededededeeeEececebebebebecececececdcdcdcdce"],
28830
- 99: [33, "cf..", "ededededddddddddddddededdedeeeEececebebecdcdbdbdbdbdbdbdcdcdcd"],
28831
- 100: [33, "cf..", "ededdedeeeeededededeeeeeededdeDebebecdcdcecebebebebececebebecd"]
28832
- };
28833
- function sectorBias(code) {
28834
- return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
29080
+ function oddEdgeAdjustment(odd, firstEdge) {
29081
+ return odd ? firstEdge ? 0.5 : -0.5 : 0;
28835
29082
  }
28836
- var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28837
- var VERTICAL_STEP_FIRST = 7;
28838
- function verticalSideOffset(width) {
28839
- if (width < HOBBY_LIMIT || width > 1400) {
28840
- return void 0;
29083
+ function floorDivision(a, b) {
29084
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
29085
+ return Math.floor(a / b);
28841
29086
  }
28842
- let m = VERTICAL_STEP_FIRST;
28843
- for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28844
- if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28845
- m = i + VERTICAL_STEP_FIRST;
28846
- }
29087
+ const A = BigInt(Math.round(a)), B = BigInt(Math.round(b));
29088
+ let q = A / B;
29089
+ if (A % B !== 0n && A < 0n !== B < 0n) {
29090
+ q -= 1n;
28847
29091
  }
28848
- return 8 * m;
29092
+ return Number(q);
28849
29093
  }
28850
29094
  var penCache = /* @__PURE__ */ new Map();
28851
29095
  function penPolygon(width) {
@@ -28917,6 +29161,9 @@ function perpendicularVectors(width, dx0, dy0) {
28917
29161
  best = i;
28918
29162
  }
28919
29163
  }
29164
+ if (flip && best === 0 && nx * pen[n - 1][0] + ny * pen[n - 1][1] === bh) {
29165
+ best = n - 1;
29166
+ }
28920
29167
  const P = pen[(best + n - 1) % n];
28921
29168
  const N = pen[(best + 1) % n];
28922
29169
  const D = pen[best];
@@ -28925,20 +29172,19 @@ function perpendicularVectors(width, dx0, dy0) {
28925
29172
  const [B, hB, hS] = hP >= hN ? [P, hP, hN] : [N, hN, hP];
28926
29173
  const den = 2 * (bh - hB + (bh - hS));
28927
29174
  const w = den === 0 ? 0 : (hB - hS) / den;
28928
- const r = (v) => 8 * Math.floor((v + 4) / 8);
28929
29175
  const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28930
- let cx = 0.5 * Math.sign(dy), cy = 0.5 * Math.sign(dx);
28931
- const table = width % 16 === 0 ? FLAT_SECTOR_BIAS[width / 16] : void 0;
28932
- if (table) {
28933
- const key = best * 2 + (hP >= hN ? 0 : 1);
28934
- const pair = key === 0 ? table[1].slice(0, 2) : key >= table[0] ? table[2].slice((key - table[0]) * 2, (key - table[0]) * 2 + 2) : "..";
28935
- if (pair[0] !== ".") {
28936
- cx = sectorBias(pair.charCodeAt(0));
28937
- cy = sectorBias(pair.charCodeAt(1));
28938
- }
28939
- }
28940
- const side = dx === 0 ? verticalSideOffset(width) : void 0;
28941
- const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
29176
+ const signs = [dx === 0 ? 0 : Math.sign(dy), Math.sign(dx)];
29177
+ const firstEdge = best === 0 && hP >= hN;
29178
+ const num = hB - hS;
29179
+ const rounded = [0, 1].map((c) => {
29180
+ const bias2 = signs[c] + 2 * oddEdgeAdjustment(Math.abs(B[c] - D[c]) % 2 === 1, firstEdge);
29181
+ if (den === 0) {
29182
+ return 8 * (c === 1 && dy < 0 ? Math.ceil((D[c] + bias2 / 2 + 4) / 8) - 1 : Math.floor((D[c] + bias2 / 2 + 4) / 8));
29183
+ }
29184
+ const top = D[c] * den + (B[c] - D[c]) * num;
29185
+ return 8 * floorDivision(2 * top + den * (bias2 + 8) - (c === 1 && dy < 0 ? 1 : 0), 16 * den);
29186
+ });
29187
+ const vx = rounded[0], vy = rounded[1];
28942
29188
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28943
29189
  }
28944
29190
  function squareExtension(width, dx, dy) {
@@ -28968,7 +29214,6 @@ var Outliner = class {
28968
29214
  this.n = this.pen.length;
28969
29215
  this.rr = opts.cap === "round" && opts.join === "round";
28970
29216
  this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28971
- this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28972
29217
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28973
29218
  }
28974
29219
  /**
@@ -28976,14 +29221,14 @@ var Outliner = class {
28976
29221
  * `drawDir` (a curve's end tangent) only picks the draw vertices, the
28977
29222
  * perpendicular following the chord (measured on Bezier ends).
28978
29223
  */
28979
- seg(a, b, dir, drawDir) {
29224
+ seg(a, b, dir, drawDir, curve = false) {
28980
29225
  const dx = dir ? dir[0] : b[0] - a[0];
28981
29226
  const dy = dir ? dir[1] : b[1] - a[1];
28982
29227
  const d = drawDir ?? [dx, dy];
28983
29228
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28984
- const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
29229
+ const perpendicular = curve && drawDir ? drawDir : [dx, dy];
28985
29230
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28986
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
29231
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
28987
29232
  }
28988
29233
  push(p, v) {
28989
29234
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29018,7 +29263,7 @@ var Outliner = class {
29018
29263
  * vertex exactly on `A`'s ray is included when `startIncl`, one on `B`'s
29019
29264
  * ray when `endIncl`.
29020
29265
  */
29021
- wedge(p, A, B, startIncl, endIncl) {
29266
+ wedge(p, A, B, startIncl, endIncl, tail = -1) {
29022
29267
  const TWO = Math.PI * 2;
29023
29268
  const aA = Math.atan2(A[1], A[0]);
29024
29269
  const angleOf = (Q) => {
@@ -29049,13 +29294,21 @@ var Outliner = class {
29049
29294
  }
29050
29295
  }
29051
29296
  list.sort((x, y) => x[0] - y[0]);
29297
+ if (tail >= 0 && !list.some(([, k]) => k === tail)) {
29298
+ list.push([Infinity, tail]);
29299
+ }
29052
29300
  for (const [, k] of list) {
29053
29301
  this.penAt(p, k);
29054
29302
  }
29055
29303
  }
29304
+ /** The pen's extreme-x vertex `k` that a steep segment `s` rests on, or -1 when the rule does not apply. */
29305
+ extremeTail(applies, k, s) {
29306
+ const q = this.pen[k];
29307
+ return applies && Math.abs(s.dy) > Math.abs(s.dx) && Math.abs(q[0]) === this.maxX && q[1] === 0 ? k : -1;
29308
+ }
29056
29309
  /** Side offset of segment `s` at a join. */
29057
29310
  joinSide(s, side) {
29058
- if (this.roundJoinSides) {
29311
+ if (this.originalRoundJoinSides || s.curve) {
29059
29312
  const q = this.pen[side === "L" ? s.L : s.R];
29060
29313
  return [halfPixel(q[0]), halfPixel(q[1])];
29061
29314
  }
@@ -29103,32 +29356,36 @@ var Outliner = class {
29103
29356
  */
29104
29357
  join(p, a, b, side, outer) {
29105
29358
  const { cap, width, miterLimit } = this.opts;
29106
- const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
29359
+ const curveJoin = a.curve && b.curve;
29360
+ const join = curveJoin ? "round" : this.opts.join;
29361
+ const roundSides = this.originalRoundJoinSides || curveJoin;
29107
29362
  let sa = this.joinSide(a, side);
29108
29363
  let sb = this.joinSide(b, side);
29109
29364
  const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
29110
29365
  const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
29111
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29112
- sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
29113
- sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
29366
+ if (curveJoin && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29367
+ const t = a.tangentLength >= b.tangentLength ? a : b;
29368
+ sa = sb = side === "R" ? t.v : [-t.v[0], -t.v[1]];
29114
29369
  }
29115
29370
  const Da = side === "R" ? a.R : a.L;
29116
29371
  const Db = side === "R" ? b.R : b.L;
29117
- if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
29372
+ if (roundSides && (Da === Db || curveJoin && sa[0] === sb[0] && sa[1] === sb[1])) {
29118
29373
  this.push(p, sa);
29119
29374
  return;
29120
29375
  }
29121
29376
  this.push(p, sa);
29122
29377
  if (outer) {
29123
29378
  if (join === "round") {
29124
- if (this.roundJoinSides) {
29379
+ if (roundSides) {
29125
29380
  const q = this.pen[Db];
29126
29381
  const steep = Math.abs(b.dy) > Math.abs(b.dx);
29127
29382
  const incl = side === "L" && steep && Math.abs(q[0]) === this.maxX && q[0] * q[1] <= 0;
29128
29383
  this.walk(p, Da, Db, false, incl);
29129
29384
  } else {
29130
29385
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
29131
- this.wedge(p, rayA, rayB, !anti, !anti);
29386
+ if (Da !== Db || anti) {
29387
+ this.wedge(p, rayA, rayB, !anti, false, this.extremeTail(side === "L" && !anti, Db, b));
29388
+ }
29132
29389
  }
29133
29390
  } else if (join === "miter") {
29134
29391
  const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
@@ -29138,14 +29395,16 @@ var Outliner = class {
29138
29395
  }
29139
29396
  } else {
29140
29397
  this.pts.push([p[0], p[1]]);
29141
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
29398
+ if (curveJoin && !this.originalRoundJoinSides) {
29142
29399
  this.push(p, sb);
29143
29400
  this.push(p, sa);
29144
29401
  this.pts.push([p[0], p[1]]);
29145
29402
  }
29146
- if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
29403
+ if (join === "round" && cap === "flat" && !curveJoin) {
29147
29404
  this.push(p, sb);
29148
- this.wedge(p, rayB, rayA, false, false);
29405
+ if (Da !== Db) {
29406
+ this.wedge(p, rayB, rayA, false, false, this.extremeTail(side === "L", Da, a));
29407
+ }
29149
29408
  this.push(p, sa);
29150
29409
  this.pts.push([p[0], p[1]]);
29151
29410
  }
@@ -29178,17 +29437,17 @@ var Outliner = class {
29178
29437
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29179
29438
  * `draws` (curve end tangents) picks draw vertices only.
29180
29439
  */
29181
- open(P, dirs, draws) {
29440
+ open(P, dirs, draws, curves) {
29182
29441
  if (P.length < 2 && !dirs?.[0]) {
29183
29442
  this.dot(P[0]);
29184
29443
  return;
29185
29444
  }
29186
29445
  const segs2 = [];
29187
29446
  for (let i = 0; i + 1 < P.length; i++) {
29188
- segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i]));
29447
+ segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i], curves?.[i]));
29189
29448
  }
29190
29449
  if (segs2.length === 0) {
29191
- const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0]);
29450
+ const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29192
29451
  this.cap(P[0], s, true);
29193
29452
  this.cap(P[0], s, false);
29194
29453
  this.flush();
@@ -29219,11 +29478,11 @@ var Outliner = class {
29219
29478
  this.flush();
29220
29479
  }
29221
29480
  /** Outlines a closed polygon (distinct consecutive points, not repeating the first). */
29222
- closed(P, draws) {
29481
+ closed(P, draws, curves) {
29223
29482
  const m = P.length;
29224
29483
  const segs2 = [];
29225
29484
  for (let i = 0; i < m; i++) {
29226
- segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i]));
29485
+ segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i], curves?.[i]));
29227
29486
  }
29228
29487
  for (let j = 1; j <= m; j++) {
29229
29488
  const i = j % m;
@@ -29315,9 +29574,10 @@ function widenPath(path, opts) {
29315
29574
  const out = [];
29316
29575
  const dashed = !!opts.dashes && opts.dashes.length > 0;
29317
29576
  for (const fig of path.figures) {
29318
- const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
29577
+ const outliner = new Outliner(opts, out);
29319
29578
  let P = [];
29320
29579
  const dirs = [];
29580
+ const curves = [];
29321
29581
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
29322
29582
  const q = [fig.pts[i], fig.pts[i + 1]];
29323
29583
  const last = P[P.length - 1];
@@ -29326,6 +29586,7 @@ function widenPath(path, opts) {
29326
29586
  }
29327
29587
  if (last) {
29328
29588
  dirs.push(fig.tangents?.get(i / 2 - 1));
29589
+ curves.push(!!fig.roundWiden || !!fig.curveSegs?.has(i / 2 - 1));
29329
29590
  }
29330
29591
  P.push(q);
29331
29592
  }
@@ -29352,14 +29613,14 @@ function widenPath(path, opts) {
29352
29613
  pdraws.push(piece.draws[i - 1]);
29353
29614
  }
29354
29615
  }
29355
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]]);
29616
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29356
29617
  }
29357
29618
  } else if (closed && P.length >= 3) {
29358
- outliner.closed(P, dirs);
29619
+ outliner.closed(P, dirs, curves);
29359
29620
  } else if (closed && P.length === 2) {
29360
29621
  outliner.open([P[0], P[1], P[0]]);
29361
29622
  } else {
29362
- outliner.open(P, void 0, dirs);
29623
+ outliner.open(P, void 0, dirs, curves);
29363
29624
  }
29364
29625
  }
29365
29626
  return out;
@@ -31104,6 +31365,10 @@ function hasIlluminant(ca) {
31104
31365
  return illuminantCube(ca.illuminant) !== null;
31105
31366
  }
31106
31367
  var PALETTE = Array.from({ length: 32 }, (_, n) => Math.round(n * 255 / 31));
31368
+ function exactPaletteLevel(v) {
31369
+ const n = Math.round(v * 31 / 255);
31370
+ return PALETTE[n] === v ? 255 * n / 31 : v;
31371
+ }
31107
31372
  function paletteIndex(v) {
31108
31373
  const x = clamp012(v) * 255;
31109
31374
  let n = Math.min(30, Math.floor(x * 31 / 255));
@@ -31189,6 +31454,7 @@ function invert3(m) {
31189
31454
  }
31190
31455
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
31191
31456
  var clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
31457
+ var roundHalfUp = (v) => Math.floor(v + 0.5 + 1e-7);
31192
31458
  function adjustChroma(r, g, b, scale, angle) {
31193
31459
  const x = RGB_TO_XYZ[0][0] * r + RGB_TO_XYZ[0][1] * g + RGB_TO_XYZ[0][2] * b;
31194
31460
  const y = RGB_TO_XYZ[1][0] * r + RGB_TO_XYZ[1][1] * g + RGB_TO_XYZ[1][2] * b;
@@ -31240,7 +31506,7 @@ function colorAdjustmentMapper(ca) {
31240
31506
  if (negative) {
31241
31507
  v = 255 - clamp255(v);
31242
31508
  }
31243
- return Math.round(clamp255(v));
31509
+ return roundHalfUp(clamp255(v));
31244
31510
  };
31245
31511
  if (!illuminant && !chroma) {
31246
31512
  const tables = [0, 1, 2].map((channel) => Uint8Array.from({ length: 256 }, (_, i) => curve(i, channel)));
@@ -31253,7 +31519,11 @@ function colorAdjustmentMapper(ca) {
31253
31519
  c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31254
31520
  }
31255
31521
  if (chroma) {
31522
+ if (!illuminant) {
31523
+ c = c.map(exactPaletteLevel);
31524
+ }
31256
31525
  c = adjustChroma(clamp255(c[0]), clamp255(c[1]), clamp255(c[2]), scale, angle);
31526
+ c = c.map(roundHalfUp);
31257
31527
  }
31258
31528
  return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
31259
31529
  };
@@ -31287,27 +31557,6 @@ function colorAdjustRgb(rgbs, ca) {
31287
31557
  rgbs[i + 2] = v & 255;
31288
31558
  }
31289
31559
  }
31290
- function applyColorAdjustment(pixels, ca) {
31291
- if (!ca || isIdentityColorAdjustment(ca)) {
31292
- return;
31293
- }
31294
- const map = colorAdjustmentMapper(ca);
31295
- const d = pixels.data;
31296
- const cache2 = /* @__PURE__ */ new Map();
31297
- for (let i = 0; i + 3 < d.length; i += 4) {
31298
- const rgb2 = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
31299
- let v = cache2.get(rgb2);
31300
- if (v === void 0) {
31301
- v = map(rgb2);
31302
- if (cache2.size < 65536) {
31303
- cache2.set(rgb2, v);
31304
- }
31305
- }
31306
- d[i] = v >> 16 & 255;
31307
- d[i + 1] = v >> 8 & 255;
31308
- d[i + 2] = v & 255;
31309
- }
31310
- }
31311
31560
 
31312
31561
  // src/emf-gdi-halftone-dither.ts
31313
31562
  var THRESHOLDS = [
@@ -31466,22 +31715,22 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31466
31715
  if (reverse) taps.reverse();
31467
31716
  return taps;
31468
31717
  }
31718
+ var REDUCE_BITS = 13;
31469
31719
  function halftoneReduceTaps(srcLen, dstLen, reverse) {
31470
31720
  if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31721
+ const shares = 2 ** REDUCE_BITS;
31722
+ const scale = FIX / shares;
31471
31723
  const taps = [];
31472
31724
  for (let i = 0; i < dstLen; i++) {
31473
31725
  const from = i * srcLen;
31474
31726
  const to = (i + 1) * srcLen;
31475
31727
  const run = [];
31476
- let rest = 0;
31477
31728
  for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31478
- const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31479
- if (overlap <= 0) continue;
31480
- const w = Math.ceil(overlap * FIX / srcLen);
31481
- run.push([k, w]);
31482
- if (run.length > 1) rest += w;
31729
+ const lo = Math.max(from, k * dstLen) - from;
31730
+ const hi = Math.min(to, (k + 1) * dstLen) - from;
31731
+ if (hi <= lo) continue;
31732
+ run.push([k, (Math.floor(hi * shares / srcLen) - Math.floor(lo * shares / srcLen)) * scale]);
31483
31733
  }
31484
- if (run.length > 1) run[0][1] = FIX - rest;
31485
31734
  taps.push(run);
31486
31735
  }
31487
31736
  if (reverse) taps.reverse();
@@ -31555,7 +31804,7 @@ function halftoneEnlargeTaps(src, dst) {
31555
31804
  }
31556
31805
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31557
31806
  for (let y = 0; y < h; y++) {
31558
- const yy = phaseY ? phaseY[y] : flipY ? h - 1 - y : y;
31807
+ const yy = phaseY ? phaseY[y] : y;
31559
31808
  for (let x = 0; x < w; x++) {
31560
31809
  const xx = phaseX ? phaseX[x] : x;
31561
31810
  const threshold = ditherThreshold(dither.startX, dither.startY, xx, yy);
@@ -31566,82 +31815,64 @@ function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31566
31815
  }
31567
31816
  }
31568
31817
  }
31569
- var SPECKLE_OVERSHOOT = 4;
31570
31818
  function halftoneDespeckle(px, w, h) {
31819
+ if (w < 2 || h < 2) {
31820
+ return;
31821
+ }
31571
31822
  const src = px.slice();
31572
- const at = (x, y, c) => src[(y * w + x) * 3 + c];
31573
- const over = (v) => v < -SPECKLE_OVERSHOOT * 4 || v > (255 + SPECKLE_OVERSHOOT) * 4;
31574
- const loneX = w > 2 && w % 2 === 1 ? w - 1 : -1;
31575
- const loneY = h > 2 && h % 2 === 1 ? h - 1 : -1;
31576
- const mirror2 = (i, n, d) => {
31577
- const j = i + d;
31578
- return j < 0 || j >= n ? i - d : j;
31823
+ const index = (x, y) => {
31824
+ const mx = x < 0 ? -x : x >= w ? 2 * (w - 1) - x : x;
31825
+ const my = y < 0 ? -y : y >= h ? 2 * (h - 1) - y : y;
31826
+ return (Math.max(0, Math.min(h - 1, my)) * w + Math.max(0, Math.min(w - 1, mx))) * 3;
31579
31827
  };
31580
- const vBlur = (x, y, c) => at(x, mirror2(y, h, -1), c) + 2 * at(x, y, c) + at(x, mirror2(y, h, 1), c) + 2 >> 2;
31581
- const hBlur = (x, y, c) => at(mirror2(x, w, -1), y, c) + 2 * at(x, y, c) + at(mirror2(x, w, 1), y, c) + 2 >> 2;
31582
- const differs = (x0, y0, x1, y1) => at(x0, y0, 0) !== at(x1, y1, 0) || at(x0, y0, 1) !== at(x1, y1, 1) || at(x0, y0, 2) !== at(x1, y1, 2);
31583
- const loneColumn = (y) => loneX > 0 && differs(loneX, y, loneX - 1, y);
31584
- const loneRow = (x) => loneY > 0 && differs(x, loneY, x, loneY - 1);
31585
- const loneColumnValue = (y, c) => y === loneY && loneRow(loneX) ? at(loneX, y - 1, c) + 3 * at(loneX, y, c) + 2 >> 2 : vBlur(loneX, y, c);
31586
- const loneRowValue = (x, c) => x === loneX && loneColumn(loneY) ? at(x - 1, loneY, c) + 3 * at(x, loneY, c) + 2 >> 2 : hBlur(x, loneY, c);
31587
- const speckled = (x, y) => {
31588
- const up = mirror2(y, h, -1);
31589
- const down = mirror2(y, h, 1);
31590
- const left = mirror2(x, w, -1);
31591
- const right = mirror2(x, w, 1);
31592
- let hClip = 0;
31593
- let vClip = 0;
31594
- let alternating = 0;
31595
- for (let c = 0; c < 3; c++) {
31596
- const v = at(x, y, c);
31597
- const axial = Math.max(
31598
- Math.abs(v - at(left, y, c)),
31599
- Math.abs(v - at(right, y, c)),
31600
- Math.abs(v - at(x, up, c)),
31601
- Math.abs(v - at(x, down, c))
31602
- );
31603
- const diagonal = Math.min(
31604
- Math.abs(v - at(left, up, c)),
31605
- Math.abs(v - at(right, up, c)),
31606
- Math.abs(v - at(left, down, c)),
31607
- Math.abs(v - at(right, down, c))
31608
- );
31609
- if (diagonal < axial) alternating++;
31610
- if (over(6 * v - at(left, y, c) - at(right, y, c))) {
31611
- hClip++;
31612
- }
31613
- if (over(6 * v - at(x, up, c) - at(x, down, c))) {
31614
- vClip++;
31828
+ const same = (i, j) => src[i] === src[j] && src[i + 1] === src[j + 1] && src[i + 2] === src[j + 2];
31829
+ const stride = w + 1;
31830
+ const flags = new Uint8Array(stride * (h + 1));
31831
+ for (let y = -1; y < h; y++) {
31832
+ for (let x = -1; x < w; x++) {
31833
+ const a = index(x, y);
31834
+ const b = index(x + 1, y);
31835
+ const c = index(x, y + 1);
31836
+ const d = index(x + 1, y + 1);
31837
+ flags[(y + 1) * stride + x + 1] = same(a, d) && same(b, c) && !same(a, b) ? 1 : 0;
31838
+ }
31839
+ }
31840
+ const checker = (x, y) => flags[(y + 1) * stride + x + 1] === 1;
31841
+ for (let y = 0; y < h - 1; y++) {
31842
+ for (let x = 0; x < w - 1; x++) {
31843
+ if (!checker(x, y)) {
31844
+ continue;
31615
31845
  }
31616
- }
31617
- return hClip >= 2 && vClip >= 2 && alternating >= 2;
31618
- };
31619
- for (let y = 0; y < h; y++) {
31620
- for (let x = 0; x < w; x++) {
31621
- if (!speckled(x, y)) {
31846
+ const ia = index(x, y);
31847
+ const ib = index(x + 1, y);
31848
+ const members = [
31849
+ [x, y, 1, 1, true],
31850
+ [x + 1, y + 1, -1, -1, true],
31851
+ [x + 1, y, -1, 1, false],
31852
+ [x, y + 1, 1, -1, false]
31853
+ ];
31854
+ if (checker(x - 1, y) && checker(x + 1, y) || checker(x, y - 1) && checker(x, y + 1)) {
31855
+ for (const [mx, my] of members) {
31856
+ const o = (my * w + mx) * 3;
31857
+ for (let c = 0; c < 3; c++) {
31858
+ px[o + c] = src[ia + c] + src[ib + c] + 1 >> 1;
31859
+ }
31860
+ }
31622
31861
  continue;
31623
31862
  }
31624
- const left = mirror2(x, w, -1);
31625
- const right = mirror2(x, w, 1);
31626
- const up = mirror2(y, h, -1);
31627
- const down = mirror2(y, h, 1);
31628
- const lonelyColumn = x === loneX && loneColumn(y);
31629
- const lonelyRow = y === loneY && loneRow(x);
31630
- const nearLoneX = x === loneX - 1 && loneX > 0 && loneColumn(y) && !speckled(loneX, y);
31631
- const nearLoneY = y === loneY - 1 && loneY > 0 && loneRow(x) && !speckled(x, loneY);
31632
- const cornerRepeats = lonelyColumn && lonelyRow && (!speckled(x, y - 1) || !speckled(x - 1, y));
31633
- for (let c = 0; c < 3; c++) {
31634
- const v = at(x, y, c);
31635
- let r;
31636
- if (lonelyColumn && lonelyRow) {
31637
- r = cornerRepeats ? at(left, y, c) + 3 * v + 2 >> 2 : hBlur(x, y, c);
31638
- } else {
31639
- let sum = lonelyColumn ? at(x, up, c) + 2 * v + at(x, down, c) : at(left, y, c) + 2 * v + at(right, y, c);
31640
- if (nearLoneX) sum += loneColumnValue(y, c) - at(loneX, y, c);
31641
- if (nearLoneY) sum += loneRowValue(x, c) - at(x, loneY, c);
31642
- r = sum + 2 >> 2;
31863
+ const brighter = 4 * (src[ia] - src[ib]) + 8 * (src[ia + 1] - src[ib + 1]) + (src[ia + 2] - src[ib + 2]);
31864
+ for (const [mx, my, sx, sy, isA] of members) {
31865
+ if (isA ? brighter < 0 : brighter >= 0) {
31866
+ continue;
31867
+ }
31868
+ const o = (my * w + mx) * 3;
31869
+ const t1 = index(mx + sx, my);
31870
+ const t2 = index(mx, my + sy);
31871
+ const t3 = index(mx + 2 * sx, my);
31872
+ const t4 = index(mx, my + 2 * sy);
31873
+ for (let c = 0; c < 3; c++) {
31874
+ px[o + c] = 12 * px[o + c] + src[t1 + c] + src[t2 + c] + src[t3 + c] + src[t4 + c] + 8 >> 4;
31643
31875
  }
31644
- px[(y * w + x) * 3 + c] = r;
31645
31876
  }
31646
31877
  }
31647
31878
  }
@@ -31660,11 +31891,33 @@ function halftoneSharpen(px, w, h) {
31660
31891
  }
31661
31892
  }
31662
31893
  }
31894
+ function halftonePairDespeckle(px, w, h, rows) {
31895
+ const n = rows ? w : h;
31896
+ const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
31897
+ const same = (i, j) => px[i] === px[j] && px[i + 1] === px[j + 1] && px[i + 2] === px[j + 2];
31898
+ 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));
31899
+ const mean = Array.from({ length: n }, (_, k) => checker(k - 1) || checker(k));
31900
+ for (let k = 0; k < n; k++) {
31901
+ if (!mean[k]) continue;
31902
+ const first = at(k, 0);
31903
+ const last = at(k, 1);
31904
+ for (let c = 0; c < 3; c++) px[first + c] = px[last + c] = px[first + c] + px[last + c] + 1 >> 1;
31905
+ }
31906
+ }
31907
+ function halftoneKeepLast(px, w, h, rows) {
31908
+ const n = rows ? w : h;
31909
+ const out = new Int32Array(n * 3);
31910
+ for (let k = 0; k < n; k++) {
31911
+ const last = (rows ? w + k : k * w + 1) * 3;
31912
+ for (let c = 0; c < 3; c++) out[k * 3 + c] = px[last + c];
31913
+ }
31914
+ return out;
31915
+ }
31663
31916
  function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
31664
31917
  const W = Math.max(0, Math.round(Math.abs(dw)));
31665
31918
  const H = Math.max(0, Math.round(Math.abs(dh)));
31666
- const SW = Math.round(Math.abs(sw));
31667
- const SH = Math.round(Math.abs(sh));
31919
+ let SW = Math.round(Math.abs(sw));
31920
+ let SH = Math.round(Math.abs(sh));
31668
31921
  const x0 = Math.round(Math.min(sx, sx + sw));
31669
31922
  const y0 = Math.round(Math.min(sy, sy + sh));
31670
31923
  const flipX = dw < 0 !== sw < 0;
@@ -31673,7 +31926,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31673
31926
  if (W === 0 || H === 0 || SW === 0 || SH === 0) {
31674
31927
  return { width: W, height: H, data };
31675
31928
  }
31676
- const rect = new Int32Array(SW * SH * 3);
31929
+ let rect = new Int32Array(SW * SH * 3);
31677
31930
  for (let y = 0; y < SH; y++) {
31678
31931
  const yy = y0 + y;
31679
31932
  for (let x = 0; x < SW; x++) {
@@ -31688,19 +31941,40 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31688
31941
  rect[o + 2] = src.data[i + 2];
31689
31942
  }
31690
31943
  }
31944
+ const pairRows = SH === 2 && H === 1 && W > SW && (!adjust || adjustAfterSampling || !!dither);
31945
+ const pairColumns = SW === 2 && W === 1 && H > SH && (!adjust || adjustAfterSampling || !!dither);
31946
+ if (pairRows || pairColumns) {
31947
+ if (!directDib) halftonePairDespeckle(rect, SW, SH, pairRows);
31948
+ if (pairRows && W >= 2 * SW) {
31949
+ rect = halftoneKeepLast(rect, SW, SH, true);
31950
+ SH = 1;
31951
+ } else if (pairColumns && H >= 2 * SH) {
31952
+ rect = halftoneKeepLast(rect, SW, SH, false);
31953
+ SW = 1;
31954
+ }
31955
+ }
31691
31956
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
31692
31957
  const reducing = W < SW && H < SH;
31693
31958
  const mixed = W > SW && H < SH || W < SW && H > SH;
31694
31959
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31695
31960
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31696
31961
  const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31697
- if ((enlarging || nearestMixed) && !directDib) {
31962
+ if ((enlarging || nearestMixed) && !directDib && !pairRows && !pairColumns) {
31698
31963
  halftoneDespeckle(rect, SW, SH);
31699
31964
  }
31700
31965
  const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
31701
31966
  const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
31702
31967
  if (ditherAtSource) {
31703
- ditherPixels(rect, SW, SH, dither, false, flipY);
31968
+ ditherPixels(
31969
+ rect,
31970
+ SW,
31971
+ SH,
31972
+ dither,
31973
+ false,
31974
+ false,
31975
+ void 0,
31976
+ flipY ? Int32Array.from({ length: SH }, (_, j) => H - 1 - j) : void 0
31977
+ );
31704
31978
  }
31705
31979
  if (adjust && !adjustAfterSampling && (!dithered || ditherAtSource)) {
31706
31980
  adjust(rect);
@@ -31816,17 +32090,13 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31816
32090
  }
31817
32091
  }
31818
32092
  if (dithered && !ditherAtSource) {
31819
- 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);
31820
- ditherPixels(
31821
- out,
31822
- W,
31823
- H,
31824
- dither,
31825
- false,
31826
- false,
31827
- nearestMixed ? phase(W, SW, W, true) : void 0,
31828
- nearestMixed ? phase(H, SH, H, false) : void 0
31829
- );
32093
+ 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);
32094
+ const sourcePhase = nearestMixed && W * H > SW * SH;
32095
+ let phaseX = sourcePhase ? phase(W, SW, W, true) : void 0;
32096
+ let phaseY = sourcePhase ? phase(H, SH, H, false) : void 0;
32097
+ if (phaseX && flipX) phaseX = phaseX.slice().reverse();
32098
+ if (phaseY && flipY && H > SH) phaseY = phaseY.slice().reverse().map((v) => H - 1 - v);
32099
+ ditherPixels(out, W, H, dither, false, false, phaseX, phaseY);
31830
32100
  adjust(out);
31831
32101
  }
31832
32102
  if (adjust && adjustAfterSampling) adjust(out);
@@ -31889,7 +32159,7 @@ function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode, brushOrg
31889
32159
  canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
31890
32160
  target.restore();
31891
32161
  }
31892
- function decodeSource(rCtx, req) {
32162
+ function decodeSource(rCtx, req, rotated = false) {
31893
32163
  if (!req.source) {
31894
32164
  return null;
31895
32165
  }
@@ -31904,6 +32174,9 @@ function decodeSource(rCtx, req) {
31904
32174
  return null;
31905
32175
  }
31906
32176
  const ca = rCtx.state.colorAdjustment;
32177
+ if (req.stretch && rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32178
+ rCtx.halftoneRotatedAdjusts = !rotated && !!ca;
32179
+ }
31907
32180
  const halftone = req.stretch && rCtx.state.stretchBltMode === HALFTONE && ca;
31908
32181
  const channelOnly = isChannelOnlyColorAdjustment(ca);
31909
32182
  const split = halftone && !channelOnly ? splitColorAdjustment(ca) : void 0;
@@ -32051,6 +32324,7 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32051
32324
  let src = null;
32052
32325
  let srcX = sx;
32053
32326
  let srcY = sy;
32327
+ let adjustTexel;
32054
32328
  if (operands.usesS) {
32055
32329
  const decoded = decodeSource(rCtx, {
32056
32330
  plan: { kind: "ternary", index, operands },
@@ -32065,13 +32339,30 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32065
32339
  sh,
32066
32340
  dibOrigin,
32067
32341
  stretch
32068
- });
32342
+ }, true);
32069
32343
  if (!decoded) {
32070
32344
  return;
32071
32345
  }
32072
32346
  src = decoded.decoded.pixels;
32073
- if (decoded.decoded.adjust) {
32074
- applyColorAdjustment(src, rCtx.state.colorAdjustment);
32347
+ if (decoded.decoded.adjust && rCtx.halftoneRotatedAdjusts) {
32348
+ const { adjust, adjustAfterSampling, curves } = decoded.decoded;
32349
+ const startX = ditherStartX(0, rCtx.state.brushOrgX);
32350
+ const startY = ditherStartY(0, rCtx.state.brushOrgY);
32351
+ const triple = new Int32Array(3);
32352
+ adjustTexel = (rgb2, x, y) => {
32353
+ triple[0] = rgb2 >> 16 & 255;
32354
+ triple[1] = rgb2 >> 8 & 255;
32355
+ triple[2] = rgb2 & 255;
32356
+ if (!adjustAfterSampling) {
32357
+ const t = ditherThreshold(startX, startY, x, y);
32358
+ for (let c = 0; c < 3; c++) {
32359
+ triple[c] = ditherQuantize(triple[c], t);
32360
+ }
32361
+ }
32362
+ adjust(triple);
32363
+ curves?.(triple);
32364
+ return triple[0] << 16 | triple[1] << 8 | triple[2];
32365
+ };
32075
32366
  }
32076
32367
  srcX = decoded.req.sx;
32077
32368
  srcY = decoded.req.sy;
@@ -32095,7 +32386,10 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32095
32386
  sw,
32096
32387
  sh,
32097
32388
  (ix, iy, x, y, d) => {
32098
- const s = operands.usesS ? texel(ix, iy) : 0;
32389
+ let s = operands.usesS ? texel(ix, iy) : 0;
32390
+ if (adjustTexel && operands.usesS) {
32391
+ s = adjustTexel(s, x, y);
32392
+ }
32099
32393
  const p = typeof pattern === "function" ? pattern(x, y) : pattern;
32100
32394
  return evalRop3(index, p, s, d);
32101
32395
  }
@@ -32150,6 +32444,19 @@ function alignMirroredDest(rCtx, req) {
32150
32444
  dy: req.dh < 0 ? req.dy + pxY : req.dy
32151
32445
  };
32152
32446
  }
32447
+ function executePlgBltAsStretch(rCtx, dx, dy, dw, dh, source, r) {
32448
+ executeBlit(rCtx, {
32449
+ plan: classifyRop3(13369376),
32450
+ dx,
32451
+ dy,
32452
+ dw,
32453
+ dh,
32454
+ source,
32455
+ ...r,
32456
+ dibOrigin: "top-left",
32457
+ stretch: true
32458
+ });
32459
+ }
32153
32460
  function executeBlit(rCtx, request) {
32154
32461
  const { ctx } = rCtx;
32155
32462
  const req = alignMirroredDest(rCtx, request);
@@ -32652,6 +32959,18 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32652
32959
  if (b[1] === a[1] && q[0] === a[0]) {
32653
32960
  const W = Math.round(Math.abs(b[0] - a[0]) / 16);
32654
32961
  const H = Math.round(Math.abs(q[1] - a[1]) / 16);
32962
+ if (!mask && rCtx.state.stretchBltMode === HALFTONE && W > 0 && H > 0) {
32963
+ executePlgBltAsStretch(
32964
+ rCtx,
32965
+ Math.round(a[0] / 16),
32966
+ Math.round(a[1] / 16),
32967
+ b[0] < a[0] ? -W : W,
32968
+ q[1] < a[1] ? -H : H,
32969
+ 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)),
32970
+ r
32971
+ );
32972
+ return;
32973
+ }
32655
32974
  const left = Math.round(a[0] / 16) + (b[0] < a[0] ? 1 - W : 0);
32656
32975
  const top = Math.round(a[1] / 16) + (q[1] < a[1] ? 1 - H : 0);
32657
32976
  const aw = Math.abs(r.sw);
@@ -32672,6 +32991,9 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32672
32991
  });
32673
32992
  return;
32674
32993
  }
32994
+ if (rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32995
+ rCtx.halftoneRotatedAdjusts = false;
32996
+ }
32675
32997
  paintParallelogram(rCtx, a, b, q, r.sw, r.sh, (ix, iy) => texel(ix, iy));
32676
32998
  }
32677
32999
  function readBand(rCtx, offset, offBmi, cbBmi, offBits, cbBits, usage, scans) {
@@ -34800,6 +35122,7 @@ function flattenBracketPath(rCtx) {
34800
35122
  }
34801
35123
  for (const f of rCtx.rasterPath.figures) {
34802
35124
  delete f.tangents;
35125
+ delete f.curveSegs;
34803
35126
  delete f.roundWiden;
34804
35127
  }
34805
35128
  syncPathCmds(rCtx);
@@ -38891,7 +39214,7 @@ function wmfRectangle(p, l, t, r, b) {
38891
39214
  axisRect: { interior: rectInterior(p, box) }
38892
39215
  });
38893
39216
  }
38894
- function compatCorner(p, w, h, box) {
39217
+ function compatCorner(p, w, h, box, logical) {
38895
39218
  const m = gdiDeviceMatrix(p.rCtx);
38896
39219
  const cw = Math.round(Math.abs(w * m[0]) * 16);
38897
39220
  const ch = Math.round(Math.abs(h * m[3]) * 16);
@@ -38899,24 +39222,21 @@ function compatCorner(p, w, h, box) {
38899
39222
  const uy = 16 * p.ky;
38900
39223
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38901
39224
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38902
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38903
- return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38904
- }
38905
- if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38906
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38907
- return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
39225
+ const width2 = box.x1 - box.x0, height2 = box.y1 - box.y0;
39226
+ return [Math.floor(Math.min(cw, width2 + penWidth) * width2 / (width2 + penWidth)), Math.floor(Math.min(ch, height2 + penWidth) * height2 / (height2 + penWidth))];
38908
39227
  }
38909
- if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
38910
- return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
38911
- }
38912
- return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
39228
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
39229
+ const fullW = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width + ux;
39230
+ const fullH = Math.abs((logical[3] - logical[1]) * m[3]) * 16 || height + uy;
39231
+ const scaled2 = [Math.min(Math.abs(w * m[0]) * 16, fullW) * width / fullW, Math.min(Math.abs(h * m[3]) * 16, fullH) * height / fullH];
39232
+ return penIsCosmetic(p.rCtx) && !penIsNull(p) ? scaled2 : [Math.floor(scaled2[0]), Math.floor(scaled2[1])];
38913
39233
  }
38914
39234
  function wmfRoundRect(p, l, t, r, b, w, h) {
38915
39235
  const box = compatBox(p, l, t, r, b, { curved: true });
38916
39236
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38917
39237
  return;
38918
39238
  }
38919
- const [cw, ch] = compatCorner(p, w, h, box);
39239
+ const [cw, ch] = compatCorner(p, w, h, box, [l, t, r, b]);
38920
39240
  const cr = canvasRect(box);
38921
39241
  paintGdiShape(p.rCtx, {
38922
39242
  build: (c) => {
@@ -38969,10 +39289,14 @@ function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
38969
39289
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38970
39290
  return null;
38971
39291
  }
38972
- const raw = compatBox(p, l, t, r, b, { exclusive: false });
39292
+ const m = gdiDeviceMatrix(p.rCtx);
39293
+ const deviceFix = (x, y) => [(m[0] * x + m[2] * y + m[4]) * 16, (m[1] * x + m[3] * y + m[5]) * 16];
39294
+ const [ax, ay] = deviceFix(l, t);
39295
+ const [bx, by] = deviceFix(r, b);
39296
+ const raw = { x0: Math.min(ax, bx), x1: Math.max(ax, bx), y0: Math.min(ay, by), y1: Math.max(ay, by) };
38973
39297
  const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
38974
39298
  const radial = (x, y) => {
38975
- const [fx, fy] = fixPoint(p.rCtx, x, y);
39299
+ const [fx, fy] = deviceFix(x, y);
38976
39300
  return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
38977
39301
  };
38978
39302
  return { box, s: radial(xs, ys), e: radial(xe, ye) };