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.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]);
@@ -16440,23 +16545,241 @@ function applyTint(src, hue, amount) {
16440
16545
  function applyRedEyeCorrection(src, width, height, areas) {
16441
16546
  const out = new Uint8ClampedArray(src);
16442
16547
  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);
16548
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16549
+ }
16550
+ return out;
16551
+ }
16552
+ var RED_EYE_FALLOFF = new Float64Array(
16553
+ [
16554
+ 24930,
16555
+ 24570,
16556
+ 24201,
16557
+ 23836,
16558
+ 23471,
16559
+ 23107,
16560
+ 22744,
16561
+ 22367,
16562
+ 21976,
16563
+ 21587,
16564
+ 21198,
16565
+ 20824,
16566
+ 20490,
16567
+ 20165,
16568
+ 19832,
16569
+ 19499,
16570
+ 19157,
16571
+ 18817,
16572
+ 18480,
16573
+ 18151,
16574
+ 17820,
16575
+ 17490,
16576
+ 17160,
16577
+ 16831,
16578
+ 16506,
16579
+ 16189,
16580
+ 15878,
16581
+ 15569,
16582
+ 15273,
16583
+ 14973,
16584
+ 14668,
16585
+ 14362,
16586
+ 14059,
16587
+ 13768,
16588
+ 13484,
16589
+ 13203,
16590
+ 12923,
16591
+ 12642,
16592
+ 12366,
16593
+ 12094,
16594
+ 11828,
16595
+ 11564,
16596
+ 11301,
16597
+ 11034,
16598
+ 10770,
16599
+ 10507,
16600
+ 10253,
16601
+ 10011,
16602
+ 9771,
16603
+ 9530,
16604
+ 9290,
16605
+ 9053,
16606
+ 8819,
16607
+ 8588,
16608
+ 8359,
16609
+ 8134,
16610
+ 7914,
16611
+ 7697,
16612
+ 7481,
16613
+ 7267,
16614
+ 7056,
16615
+ 6851,
16616
+ 6651,
16617
+ 6453,
16618
+ 6256,
16619
+ 6061,
16620
+ 5871,
16621
+ 5684,
16622
+ 5499,
16623
+ 5317,
16624
+ 5138,
16625
+ 4962,
16626
+ 4789,
16627
+ 4619,
16628
+ 4452,
16629
+ 4289,
16630
+ 4130,
16631
+ 3974,
16632
+ 3820,
16633
+ 3669,
16634
+ 3520,
16635
+ 3375,
16636
+ 3232,
16637
+ 3093,
16638
+ 2958,
16639
+ 2825,
16640
+ 2695,
16641
+ 2568,
16642
+ 2444,
16643
+ 2325,
16644
+ 2207,
16645
+ 2092,
16646
+ 1980,
16647
+ 1872,
16648
+ 1768,
16649
+ 1666,
16650
+ 1567,
16651
+ 1470,
16652
+ 1377,
16653
+ 1287,
16654
+ 1200,
16655
+ 1116,
16656
+ 1034,
16657
+ 956,
16658
+ 881,
16659
+ 808,
16660
+ 739,
16661
+ 674,
16662
+ 612,
16663
+ 541,
16664
+ 462,
16665
+ 381,
16666
+ 299,
16667
+ 220,
16668
+ 147,
16669
+ 85,
16670
+ 36,
16671
+ 3,
16672
+ 0
16673
+ ].map((v) => v / 1e5)
16674
+ );
16675
+ function redEyeFalloff(u) {
16676
+ const t = u * 128;
16677
+ const i = Math.floor(t);
16678
+ if (i >= RED_EYE_FALLOFF.length - 1) {
16679
+ return 0;
16680
+ }
16681
+ return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16682
+ }
16683
+ var RED_EYE_SECTORS = 60;
16684
+ var RED_EYE_BLUE_WEIGHT = 0.225;
16685
+ var RED_EYE_WEIGHT_CAP = 6;
16686
+ var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16687
+ var RED_EYE_DARK_STRENGTH = 0.49;
16688
+ var RED_EYE_DARK_RATIO = 0.6;
16689
+ var RED_EYE_GREEN_SHARE = 80 / 255;
16690
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16691
+ const w = x1 - x0;
16692
+ const h = y1 - y0;
16693
+ const count = w * h;
16694
+ const redness = new Float64Array(count);
16695
+ const dark = new Float64Array(count);
16696
+ let sx = 0;
16697
+ let sy = 0;
16698
+ let sw = 0;
16699
+ for (let y = 0; y < h; y++) {
16700
+ for (let x = 0; x < w; x++) {
16701
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16702
+ const r = out[i];
16703
+ const g = out[i + 1];
16704
+ const b = out[i + 2];
16705
+ const k = y * w + x;
16706
+ dark[k] = Math.min(g, b);
16707
+ const v = r - Math.max(g, b);
16708
+ if (v > 0) {
16709
+ redness[k] = v;
16710
+ const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16711
+ {
16712
+ const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16713
+ sx += weight * (x + 0.5);
16714
+ sy += weight * (y + 0.5);
16715
+ sw += weight;
16455
16716
  }
16456
16717
  }
16457
16718
  }
16458
16719
  }
16459
- return out;
16720
+ if (sw === 0) {
16721
+ if (!redness.some((v) => v > 0)) {
16722
+ return;
16723
+ }
16724
+ sx = w / 2;
16725
+ sy = h / 2;
16726
+ sw = 1;
16727
+ }
16728
+ const cx = sx / sw;
16729
+ const cy = sy / sw;
16730
+ const radius = Math.min(cx, cy) - 0.5;
16731
+ if (!(radius > 0)) {
16732
+ return;
16733
+ }
16734
+ const sector = new Uint8Array(count);
16735
+ const distance = new Float64Array(count);
16736
+ const sectorSum = new Float64Array(RED_EYE_SECTORS);
16737
+ const sectorDark = new Float64Array(RED_EYE_SECTORS);
16738
+ const sectorCount = new Float64Array(RED_EYE_SECTORS);
16739
+ const degrees = 180 / Math.PI;
16740
+ for (let y = 0; y < h; y++) {
16741
+ for (let x = 0; x < w; x++) {
16742
+ const dx = x + 0.5 - cx;
16743
+ const dy = y + 0.5 - cy;
16744
+ const k = y * w + x;
16745
+ const d = Math.hypot(dx, dy);
16746
+ distance[k] = d;
16747
+ let angle = Math.atan2(dy, dx) * degrees;
16748
+ if (angle < 0) {
16749
+ angle += 360;
16750
+ }
16751
+ const s = Math.min(RED_EYE_SECTORS - 1, Math.floor(angle / 6));
16752
+ sector[k] = s;
16753
+ if (d < radius) {
16754
+ sectorSum[s] += redness[k];
16755
+ sectorDark[s] += dark[k];
16756
+ sectorCount[s]++;
16757
+ }
16758
+ }
16759
+ }
16760
+ for (let y = 0; y < h; y++) {
16761
+ for (let x = 0; x < w; x++) {
16762
+ const k = y * w + x;
16763
+ const v = redness[k];
16764
+ if (v <= 0 || distance[k] >= radius) {
16765
+ continue;
16766
+ }
16767
+ const s = sector[k];
16768
+ const mean = sectorSum[s] / sectorCount[s];
16769
+ const meanDark = sectorDark[s] / sectorCount[s];
16770
+ const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16771
+ const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16772
+ const rest = (1 - falloff) * mean;
16773
+ if (rest >= v) {
16774
+ continue;
16775
+ }
16776
+ const removed = v - rest;
16777
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16778
+ out[i] = Math.round(out[i] - (1 - RED_EYE_GREEN_SHARE) * removed);
16779
+ out[i + 1] = Math.round(out[i + 1] + RED_EYE_GREEN_SHARE * removed);
16780
+ out[i + 2] = Math.round(out[i + 2] + RED_EYE_GREEN_SHARE * removed);
16781
+ }
16782
+ }
16460
16783
  }
16461
16784
  function blurKernel(radius) {
16462
16785
  const taps = Math.ceil(radius);
@@ -16488,8 +16811,8 @@ function mirror(p, n) {
16488
16811
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows = Math.ceil(h / w)) {
16489
16812
  const k = blurKernel(radius);
16490
16813
  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);
16814
+ const horizontalIndex = (taps | 1) >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16815
+ const verticalIndex = (taps | 1) >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16493
16816
  const out = new Float64Array(w * h * 4);
16494
16817
  for (let y = 0; y < h; y++) {
16495
16818
  const row2 = y * w * 4;
@@ -16500,11 +16823,12 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16500
16823
  let s3 = 0;
16501
16824
  for (let j = -taps; j <= taps; j++) {
16502
16825
  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]);
16826
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16827
+ const times = j === 0 ? 2 : 1;
16828
+ s0 += times * Math.round(kw * src[p]);
16829
+ s1 += times * Math.round(kw * src[p + 1]);
16830
+ s2 += times * Math.round(kw * src[p + 2]);
16831
+ s3 += times * Math.round(kw * src[p + 3]);
16508
16832
  }
16509
16833
  const o = row2 + x * 4;
16510
16834
  out[o] = Math.floor((s0 + 128) / 256);
@@ -16524,8 +16848,10 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16524
16848
  continue;
16525
16849
  }
16526
16850
  const base = verticalIndex(yy) * w * 4;
16851
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16852
+ const times = j === 0 ? 2 : 1;
16527
16853
  for (let i = 0; i < w * 4; i++) {
16528
- col[i] += Math.round(k[j + taps] * horizontal[base + i] * 256) / 256;
16854
+ col[i] += times * Math.round(kw * horizontal[base + i]) / 256;
16529
16855
  }
16530
16856
  }
16531
16857
  out.set(col, row2 * w * 4);
@@ -16540,10 +16866,12 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16540
16866
  const inner = Math.ceil(span.size / factor);
16541
16867
  const n = inner + 2 * span.pad;
16542
16868
  const taps = (k.length - 1) / 2;
16543
- const index = taps >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16869
+ const index = (taps | 1) >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16544
16870
  const reduced = new Float64Array(n);
16545
16871
  const filtered = new Float64Array(n);
16546
16872
  const k256 = k.map((w) => w * 256);
16873
+ const half = k256[taps] / 2;
16874
+ k256[taps] = 0;
16547
16875
  for (let lane = 0; lane < lanes; lane++) for (let c = 0; c < 4; c++) {
16548
16876
  for (let b = span.pad; b < span.pad + inner; b++) {
16549
16877
  const from = span.start + (b - span.pad) * factor;
@@ -16557,33 +16885,48 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16557
16885
  if (b >= taps && b + taps < n) {
16558
16886
  for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[b - taps + j]);
16559
16887
  } else {
16560
- for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[index(b - taps + j)]);
16888
+ for (let j = 0; j < k256.length; j++) {
16889
+ const at = b - taps + j;
16890
+ sum += Math.round(k256[j] * (span.zeroOutside && (at < 0 || at >= n) ? 0 : reduced[index(at)]));
16891
+ }
16561
16892
  }
16893
+ sum += 2 * Math.round(half * reduced[b]);
16562
16894
  filtered[b] = Math.floor((sum + 128) / 256);
16563
16895
  }
16896
+ const lone = inner === 1 && span.pad > 0;
16897
+ const skipped2 = inner === 1 && span.size - Math.ceil(factor / 2) === 1 ? span.start + span.size - 1 : -1;
16564
16898
  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));
16899
+ if (i === skipped2) {
16900
+ if (span.pad > 0) put(i, lane, c, 0);
16901
+ continue;
16902
+ }
16903
+ const p = (i - span.start + 0.5) / factor - 0.5;
16904
+ if (lone) {
16905
+ const distance = Math.abs(i - span.start - (Math.min(span.size, factor / 2) - 0.5)) / factor;
16906
+ put(i, lane, c, clamp(Math.floor((1 + distance) * filtered[span.pad] + 1e-9), 0, 255));
16907
+ continue;
16908
+ }
16909
+ const a = clamp(Math.floor(p), 0, Math.max(0, inner - 2));
16567
16910
  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));
16911
+ 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
16912
  }
16570
16913
  }
16571
16914
  }
16572
- function effectBlur(src, width, height, radius, region) {
16915
+ function effectBlur(src, width, height, radius, region, expanded = false) {
16573
16916
  const factor = blurReduction(radius);
16574
16917
  if (factor === 1) {
16575
16918
  return gdipBlur(src, width, height, radius);
16576
16919
  }
16577
16920
  const k = blurKernel(radius / factor);
16578
16921
  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) => {
16922
+ const spanX = region ? { start: region.x, size: region.w, pad, zeroOutside: true } : expanded ? { start: 0, size: width, pad: 0, zeroOutside: true } : void 0;
16923
+ const spanY = region ? { start: region.y, size: region.h, pad, zeroOutside: true } : expanded ? { start: 0, size: height, pad: 0, zeroOutside: true } : void 0;
16924
+ const rows = new Uint8Array(src);
16925
+ blurAxis(width, height, factor, k, Math.ceil(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16583
16926
  rows[(lane * width + i) * 4 + c] = v;
16584
16927
  }, 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) => {
16928
+ const out = Float64Array.from(rows);
16929
+ blurAxis(height, width, factor, k, Math.ceil(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16587
16930
  out[(i * width + lane) * 4 + c] = v;
16588
16931
  }, spanY);
16589
16932
  return out;
@@ -16671,7 +17014,7 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
16671
17014
  const gy1 = Math.min(height, y1 + r);
16672
17015
  copyBlock(rgba, width, gx0, gy0, gx1 - gx0, gy1 - gy0, buf, bw, gx0 - (x0 - r), gy0 - (y0 - r));
16673
17016
  }
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)));
17017
+ 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
17018
  const w = x1 - x0;
16676
17019
  const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
16677
17020
  for (let y = 0; y < y1 - y0; y++) {
@@ -28736,118 +29079,19 @@ var HOBBY = [
28736
29079
  [[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
29080
  ];
28738
29081
  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;
29082
+ function oddEdgeAdjustment(odd, firstEdge) {
29083
+ return odd ? firstEdge ? 0.5 : -0.5 : 0;
28837
29084
  }
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;
29085
+ function floorDivision(a, b) {
29086
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
29087
+ return Math.floor(a / b);
28843
29088
  }
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
- }
29089
+ const A = BigInt(Math.round(a)), B = BigInt(Math.round(b));
29090
+ let q = A / B;
29091
+ if (A % B !== 0n && A < 0n !== B < 0n) {
29092
+ q -= 1n;
28849
29093
  }
28850
- return 8 * m;
29094
+ return Number(q);
28851
29095
  }
28852
29096
  var penCache = /* @__PURE__ */ new Map();
28853
29097
  function penPolygon(width) {
@@ -28919,6 +29163,9 @@ function perpendicularVectors(width, dx0, dy0) {
28919
29163
  best = i;
28920
29164
  }
28921
29165
  }
29166
+ if (flip && best === 0 && nx * pen[n - 1][0] + ny * pen[n - 1][1] === bh) {
29167
+ best = n - 1;
29168
+ }
28922
29169
  const P = pen[(best + n - 1) % n];
28923
29170
  const N = pen[(best + 1) % n];
28924
29171
  const D = pen[best];
@@ -28927,20 +29174,19 @@ function perpendicularVectors(width, dx0, dy0) {
28927
29174
  const [B, hB, hS] = hP >= hN ? [P, hP, hN] : [N, hN, hP];
28928
29175
  const den = 2 * (bh - hB + (bh - hS));
28929
29176
  const w = den === 0 ? 0 : (hB - hS) / den;
28930
- const r = (v) => 8 * Math.floor((v + 4) / 8);
28931
29177
  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);
29178
+ const signs = [dx === 0 ? 0 : Math.sign(dy), Math.sign(dx)];
29179
+ const firstEdge = best === 0 && hP >= hN;
29180
+ const num = hB - hS;
29181
+ const rounded = [0, 1].map((c) => {
29182
+ const bias2 = signs[c] + 2 * oddEdgeAdjustment(Math.abs(B[c] - D[c]) % 2 === 1, firstEdge);
29183
+ if (den === 0) {
29184
+ return 8 * (c === 1 && dy < 0 ? Math.ceil((D[c] + bias2 / 2 + 4) / 8) - 1 : Math.floor((D[c] + bias2 / 2 + 4) / 8));
29185
+ }
29186
+ const top = D[c] * den + (B[c] - D[c]) * num;
29187
+ return 8 * floorDivision(2 * top + den * (bias2 + 8) - (c === 1 && dy < 0 ? 1 : 0), 16 * den);
29188
+ });
29189
+ const vx = rounded[0], vy = rounded[1];
28944
29190
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28945
29191
  }
28946
29192
  function squareExtension(width, dx, dy) {
@@ -28970,7 +29216,6 @@ var Outliner = class {
28970
29216
  this.n = this.pen.length;
28971
29217
  this.rr = opts.cap === "round" && opts.join === "round";
28972
29218
  this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28973
- this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28974
29219
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28975
29220
  }
28976
29221
  /**
@@ -28978,14 +29223,14 @@ var Outliner = class {
28978
29223
  * `drawDir` (a curve's end tangent) only picks the draw vertices, the
28979
29224
  * perpendicular following the chord (measured on Bezier ends).
28980
29225
  */
28981
- seg(a, b, dir, drawDir) {
29226
+ seg(a, b, dir, drawDir, curve = false) {
28982
29227
  const dx = dir ? dir[0] : b[0] - a[0];
28983
29228
  const dy = dir ? dir[1] : b[1] - a[1];
28984
29229
  const d = drawDir ?? [dx, dy];
28985
29230
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28986
- const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
29231
+ const perpendicular = curve && drawDir ? drawDir : [dx, dy];
28987
29232
  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 };
29233
+ 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
29234
  }
28990
29235
  push(p, v) {
28991
29236
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29020,7 +29265,7 @@ var Outliner = class {
29020
29265
  * vertex exactly on `A`'s ray is included when `startIncl`, one on `B`'s
29021
29266
  * ray when `endIncl`.
29022
29267
  */
29023
- wedge(p, A, B, startIncl, endIncl) {
29268
+ wedge(p, A, B, startIncl, endIncl, tail = -1) {
29024
29269
  const TWO = Math.PI * 2;
29025
29270
  const aA = Math.atan2(A[1], A[0]);
29026
29271
  const angleOf = (Q) => {
@@ -29051,13 +29296,21 @@ var Outliner = class {
29051
29296
  }
29052
29297
  }
29053
29298
  list.sort((x, y) => x[0] - y[0]);
29299
+ if (tail >= 0 && !list.some(([, k]) => k === tail)) {
29300
+ list.push([Infinity, tail]);
29301
+ }
29054
29302
  for (const [, k] of list) {
29055
29303
  this.penAt(p, k);
29056
29304
  }
29057
29305
  }
29306
+ /** The pen's extreme-x vertex `k` that a steep segment `s` rests on, or -1 when the rule does not apply. */
29307
+ extremeTail(applies, k, s) {
29308
+ const q = this.pen[k];
29309
+ return applies && Math.abs(s.dy) > Math.abs(s.dx) && Math.abs(q[0]) === this.maxX && q[1] === 0 ? k : -1;
29310
+ }
29058
29311
  /** Side offset of segment `s` at a join. */
29059
29312
  joinSide(s, side) {
29060
- if (this.roundJoinSides) {
29313
+ if (this.originalRoundJoinSides || s.curve) {
29061
29314
  const q = this.pen[side === "L" ? s.L : s.R];
29062
29315
  return [halfPixel(q[0]), halfPixel(q[1])];
29063
29316
  }
@@ -29105,32 +29358,36 @@ var Outliner = class {
29105
29358
  */
29106
29359
  join(p, a, b, side, outer) {
29107
29360
  const { cap, width, miterLimit } = this.opts;
29108
- const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
29361
+ const curveJoin = a.curve && b.curve;
29362
+ const join = curveJoin ? "round" : this.opts.join;
29363
+ const roundSides = this.originalRoundJoinSides || curveJoin;
29109
29364
  let sa = this.joinSide(a, side);
29110
29365
  let sb = this.joinSide(b, side);
29111
29366
  const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
29112
29367
  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]];
29368
+ if (curveJoin && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29369
+ const t = a.tangentLength >= b.tangentLength ? a : b;
29370
+ sa = sb = side === "R" ? t.v : [-t.v[0], -t.v[1]];
29116
29371
  }
29117
29372
  const Da = side === "R" ? a.R : a.L;
29118
29373
  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])) {
29374
+ if (roundSides && (Da === Db || curveJoin && sa[0] === sb[0] && sa[1] === sb[1])) {
29120
29375
  this.push(p, sa);
29121
29376
  return;
29122
29377
  }
29123
29378
  this.push(p, sa);
29124
29379
  if (outer) {
29125
29380
  if (join === "round") {
29126
- if (this.roundJoinSides) {
29381
+ if (roundSides) {
29127
29382
  const q = this.pen[Db];
29128
29383
  const steep = Math.abs(b.dy) > Math.abs(b.dx);
29129
29384
  const incl = side === "L" && steep && Math.abs(q[0]) === this.maxX && q[0] * q[1] <= 0;
29130
29385
  this.walk(p, Da, Db, false, incl);
29131
29386
  } else {
29132
29387
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
29133
- this.wedge(p, rayA, rayB, !anti, !anti);
29388
+ if (Da !== Db || anti) {
29389
+ this.wedge(p, rayA, rayB, !anti, false, this.extremeTail(side === "L" && !anti, Db, b));
29390
+ }
29134
29391
  }
29135
29392
  } else if (join === "miter") {
29136
29393
  const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
@@ -29140,14 +29397,16 @@ var Outliner = class {
29140
29397
  }
29141
29398
  } else {
29142
29399
  this.pts.push([p[0], p[1]]);
29143
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
29400
+ if (curveJoin && !this.originalRoundJoinSides) {
29144
29401
  this.push(p, sb);
29145
29402
  this.push(p, sa);
29146
29403
  this.pts.push([p[0], p[1]]);
29147
29404
  }
29148
- if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
29405
+ if (join === "round" && cap === "flat" && !curveJoin) {
29149
29406
  this.push(p, sb);
29150
- this.wedge(p, rayB, rayA, false, false);
29407
+ if (Da !== Db) {
29408
+ this.wedge(p, rayB, rayA, false, false, this.extremeTail(side === "L", Da, a));
29409
+ }
29151
29410
  this.push(p, sa);
29152
29411
  this.pts.push([p[0], p[1]]);
29153
29412
  }
@@ -29180,17 +29439,17 @@ var Outliner = class {
29180
29439
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29181
29440
  * `draws` (curve end tangents) picks draw vertices only.
29182
29441
  */
29183
- open(P, dirs, draws) {
29442
+ open(P, dirs, draws, curves) {
29184
29443
  if (P.length < 2 && !dirs?.[0]) {
29185
29444
  this.dot(P[0]);
29186
29445
  return;
29187
29446
  }
29188
29447
  const segs2 = [];
29189
29448
  for (let i = 0; i + 1 < P.length; i++) {
29190
- segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i]));
29449
+ segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i], curves?.[i]));
29191
29450
  }
29192
29451
  if (segs2.length === 0) {
29193
- const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0]);
29452
+ const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29194
29453
  this.cap(P[0], s, true);
29195
29454
  this.cap(P[0], s, false);
29196
29455
  this.flush();
@@ -29221,11 +29480,11 @@ var Outliner = class {
29221
29480
  this.flush();
29222
29481
  }
29223
29482
  /** Outlines a closed polygon (distinct consecutive points, not repeating the first). */
29224
- closed(P, draws) {
29483
+ closed(P, draws, curves) {
29225
29484
  const m = P.length;
29226
29485
  const segs2 = [];
29227
29486
  for (let i = 0; i < m; i++) {
29228
- segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i]));
29487
+ segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i], curves?.[i]));
29229
29488
  }
29230
29489
  for (let j = 1; j <= m; j++) {
29231
29490
  const i = j % m;
@@ -29317,9 +29576,10 @@ function widenPath(path, opts) {
29317
29576
  const out = [];
29318
29577
  const dashed = !!opts.dashes && opts.dashes.length > 0;
29319
29578
  for (const fig of path.figures) {
29320
- const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
29579
+ const outliner = new Outliner(opts, out);
29321
29580
  let P = [];
29322
29581
  const dirs = [];
29582
+ const curves = [];
29323
29583
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
29324
29584
  const q = [fig.pts[i], fig.pts[i + 1]];
29325
29585
  const last = P[P.length - 1];
@@ -29328,6 +29588,7 @@ function widenPath(path, opts) {
29328
29588
  }
29329
29589
  if (last) {
29330
29590
  dirs.push(fig.tangents?.get(i / 2 - 1));
29591
+ curves.push(!!fig.roundWiden || !!fig.curveSegs?.has(i / 2 - 1));
29331
29592
  }
29332
29593
  P.push(q);
29333
29594
  }
@@ -29354,14 +29615,14 @@ function widenPath(path, opts) {
29354
29615
  pdraws.push(piece.draws[i - 1]);
29355
29616
  }
29356
29617
  }
29357
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]]);
29618
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29358
29619
  }
29359
29620
  } else if (closed && P.length >= 3) {
29360
- outliner.closed(P, dirs);
29621
+ outliner.closed(P, dirs, curves);
29361
29622
  } else if (closed && P.length === 2) {
29362
29623
  outliner.open([P[0], P[1], P[0]]);
29363
29624
  } else {
29364
- outliner.open(P, void 0, dirs);
29625
+ outliner.open(P, void 0, dirs, curves);
29365
29626
  }
29366
29627
  }
29367
29628
  return out;
@@ -31106,6 +31367,10 @@ function hasIlluminant(ca) {
31106
31367
  return illuminantCube(ca.illuminant) !== null;
31107
31368
  }
31108
31369
  var PALETTE = Array.from({ length: 32 }, (_, n) => Math.round(n * 255 / 31));
31370
+ function exactPaletteLevel(v) {
31371
+ const n = Math.round(v * 31 / 255);
31372
+ return PALETTE[n] === v ? 255 * n / 31 : v;
31373
+ }
31109
31374
  function paletteIndex(v) {
31110
31375
  const x = clamp012(v) * 255;
31111
31376
  let n = Math.min(30, Math.floor(x * 31 / 255));
@@ -31191,6 +31456,7 @@ function invert3(m) {
31191
31456
  }
31192
31457
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
31193
31458
  var clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
31459
+ var roundHalfUp = (v) => Math.floor(v + 0.5 + 1e-7);
31194
31460
  function adjustChroma(r, g, b, scale, angle) {
31195
31461
  const x = RGB_TO_XYZ[0][0] * r + RGB_TO_XYZ[0][1] * g + RGB_TO_XYZ[0][2] * b;
31196
31462
  const y = RGB_TO_XYZ[1][0] * r + RGB_TO_XYZ[1][1] * g + RGB_TO_XYZ[1][2] * b;
@@ -31242,7 +31508,7 @@ function colorAdjustmentMapper(ca) {
31242
31508
  if (negative) {
31243
31509
  v = 255 - clamp255(v);
31244
31510
  }
31245
- return Math.round(clamp255(v));
31511
+ return roundHalfUp(clamp255(v));
31246
31512
  };
31247
31513
  if (!illuminant && !chroma) {
31248
31514
  const tables = [0, 1, 2].map((channel) => Uint8Array.from({ length: 256 }, (_, i) => curve(i, channel)));
@@ -31255,7 +31521,11 @@ function colorAdjustmentMapper(ca) {
31255
31521
  c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31256
31522
  }
31257
31523
  if (chroma) {
31524
+ if (!illuminant) {
31525
+ c = c.map(exactPaletteLevel);
31526
+ }
31258
31527
  c = adjustChroma(clamp255(c[0]), clamp255(c[1]), clamp255(c[2]), scale, angle);
31528
+ c = c.map(roundHalfUp);
31259
31529
  }
31260
31530
  return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
31261
31531
  };
@@ -31289,27 +31559,6 @@ function colorAdjustRgb(rgbs, ca) {
31289
31559
  rgbs[i + 2] = v & 255;
31290
31560
  }
31291
31561
  }
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
31562
 
31314
31563
  // src/emf-gdi-halftone-dither.ts
31315
31564
  var THRESHOLDS = [
@@ -31468,22 +31717,22 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31468
31717
  if (reverse) taps.reverse();
31469
31718
  return taps;
31470
31719
  }
31720
+ var REDUCE_BITS = 13;
31471
31721
  function halftoneReduceTaps(srcLen, dstLen, reverse) {
31472
31722
  if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31723
+ const shares = 2 ** REDUCE_BITS;
31724
+ const scale = FIX / shares;
31473
31725
  const taps = [];
31474
31726
  for (let i = 0; i < dstLen; i++) {
31475
31727
  const from = i * srcLen;
31476
31728
  const to = (i + 1) * srcLen;
31477
31729
  const run = [];
31478
- let rest = 0;
31479
31730
  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;
31731
+ const lo = Math.max(from, k * dstLen) - from;
31732
+ const hi = Math.min(to, (k + 1) * dstLen) - from;
31733
+ if (hi <= lo) continue;
31734
+ run.push([k, (Math.floor(hi * shares / srcLen) - Math.floor(lo * shares / srcLen)) * scale]);
31485
31735
  }
31486
- if (run.length > 1) run[0][1] = FIX - rest;
31487
31736
  taps.push(run);
31488
31737
  }
31489
31738
  if (reverse) taps.reverse();
@@ -31557,7 +31806,7 @@ function halftoneEnlargeTaps(src, dst) {
31557
31806
  }
31558
31807
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31559
31808
  for (let y = 0; y < h; y++) {
31560
- const yy = phaseY ? phaseY[y] : flipY ? h - 1 - y : y;
31809
+ const yy = phaseY ? phaseY[y] : y;
31561
31810
  for (let x = 0; x < w; x++) {
31562
31811
  const xx = phaseX ? phaseX[x] : x;
31563
31812
  const threshold = ditherThreshold(dither.startX, dither.startY, xx, yy);
@@ -31568,82 +31817,64 @@ function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31568
31817
  }
31569
31818
  }
31570
31819
  }
31571
- var SPECKLE_OVERSHOOT = 4;
31572
31820
  function halftoneDespeckle(px, w, h) {
31821
+ if (w < 2 || h < 2) {
31822
+ return;
31823
+ }
31573
31824
  const src = px.slice();
31574
- const at = (x, y, c) => src[(y * w + x) * 3 + c];
31575
- const over = (v) => v < -SPECKLE_OVERSHOOT * 4 || v > (255 + SPECKLE_OVERSHOOT) * 4;
31576
- const loneX = w > 2 && w % 2 === 1 ? w - 1 : -1;
31577
- const loneY = h > 2 && h % 2 === 1 ? h - 1 : -1;
31578
- const mirror2 = (i, n, d) => {
31579
- const j = i + d;
31580
- return j < 0 || j >= n ? i - d : j;
31825
+ const index = (x, y) => {
31826
+ const mx = x < 0 ? -x : x >= w ? 2 * (w - 1) - x : x;
31827
+ const my = y < 0 ? -y : y >= h ? 2 * (h - 1) - y : y;
31828
+ return (Math.max(0, Math.min(h - 1, my)) * w + Math.max(0, Math.min(w - 1, mx))) * 3;
31581
31829
  };
31582
- 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;
31583
- 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;
31584
- 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);
31585
- const loneColumn = (y) => loneX > 0 && differs(loneX, y, loneX - 1, y);
31586
- const loneRow = (x) => loneY > 0 && differs(x, loneY, x, loneY - 1);
31587
- const loneColumnValue = (y, c) => y === loneY && loneRow(loneX) ? at(loneX, y - 1, c) + 3 * at(loneX, y, c) + 2 >> 2 : vBlur(loneX, y, c);
31588
- const loneRowValue = (x, c) => x === loneX && loneColumn(loneY) ? at(x - 1, loneY, c) + 3 * at(x, loneY, c) + 2 >> 2 : hBlur(x, loneY, c);
31589
- const speckled = (x, y) => {
31590
- const up = mirror2(y, h, -1);
31591
- const down = mirror2(y, h, 1);
31592
- const left = mirror2(x, w, -1);
31593
- const right = mirror2(x, w, 1);
31594
- let hClip = 0;
31595
- let vClip = 0;
31596
- let alternating = 0;
31597
- for (let c = 0; c < 3; c++) {
31598
- const v = at(x, y, c);
31599
- const axial = Math.max(
31600
- Math.abs(v - at(left, y, c)),
31601
- Math.abs(v - at(right, y, c)),
31602
- Math.abs(v - at(x, up, c)),
31603
- Math.abs(v - at(x, down, c))
31604
- );
31605
- const diagonal = Math.min(
31606
- Math.abs(v - at(left, up, c)),
31607
- Math.abs(v - at(right, up, c)),
31608
- Math.abs(v - at(left, down, c)),
31609
- Math.abs(v - at(right, down, c))
31610
- );
31611
- if (diagonal < axial) alternating++;
31612
- if (over(6 * v - at(left, y, c) - at(right, y, c))) {
31613
- hClip++;
31614
- }
31615
- if (over(6 * v - at(x, up, c) - at(x, down, c))) {
31616
- vClip++;
31830
+ const same = (i, j) => src[i] === src[j] && src[i + 1] === src[j + 1] && src[i + 2] === src[j + 2];
31831
+ const stride = w + 1;
31832
+ const flags = new Uint8Array(stride * (h + 1));
31833
+ for (let y = -1; y < h; y++) {
31834
+ for (let x = -1; x < w; x++) {
31835
+ const a = index(x, y);
31836
+ const b = index(x + 1, y);
31837
+ const c = index(x, y + 1);
31838
+ const d = index(x + 1, y + 1);
31839
+ flags[(y + 1) * stride + x + 1] = same(a, d) && same(b, c) && !same(a, b) ? 1 : 0;
31840
+ }
31841
+ }
31842
+ const checker = (x, y) => flags[(y + 1) * stride + x + 1] === 1;
31843
+ for (let y = 0; y < h - 1; y++) {
31844
+ for (let x = 0; x < w - 1; x++) {
31845
+ if (!checker(x, y)) {
31846
+ continue;
31617
31847
  }
31618
- }
31619
- return hClip >= 2 && vClip >= 2 && alternating >= 2;
31620
- };
31621
- for (let y = 0; y < h; y++) {
31622
- for (let x = 0; x < w; x++) {
31623
- if (!speckled(x, y)) {
31848
+ const ia = index(x, y);
31849
+ const ib = index(x + 1, y);
31850
+ const members = [
31851
+ [x, y, 1, 1, true],
31852
+ [x + 1, y + 1, -1, -1, true],
31853
+ [x + 1, y, -1, 1, false],
31854
+ [x, y + 1, 1, -1, false]
31855
+ ];
31856
+ if (checker(x - 1, y) && checker(x + 1, y) || checker(x, y - 1) && checker(x, y + 1)) {
31857
+ for (const [mx, my] of members) {
31858
+ const o = (my * w + mx) * 3;
31859
+ for (let c = 0; c < 3; c++) {
31860
+ px[o + c] = src[ia + c] + src[ib + c] + 1 >> 1;
31861
+ }
31862
+ }
31624
31863
  continue;
31625
31864
  }
31626
- const left = mirror2(x, w, -1);
31627
- const right = mirror2(x, w, 1);
31628
- const up = mirror2(y, h, -1);
31629
- const down = mirror2(y, h, 1);
31630
- const lonelyColumn = x === loneX && loneColumn(y);
31631
- const lonelyRow = y === loneY && loneRow(x);
31632
- const nearLoneX = x === loneX - 1 && loneX > 0 && loneColumn(y) && !speckled(loneX, y);
31633
- const nearLoneY = y === loneY - 1 && loneY > 0 && loneRow(x) && !speckled(x, loneY);
31634
- const cornerRepeats = lonelyColumn && lonelyRow && (!speckled(x, y - 1) || !speckled(x - 1, y));
31635
- for (let c = 0; c < 3; c++) {
31636
- const v = at(x, y, c);
31637
- let r;
31638
- if (lonelyColumn && lonelyRow) {
31639
- r = cornerRepeats ? at(left, y, c) + 3 * v + 2 >> 2 : hBlur(x, y, c);
31640
- } else {
31641
- let sum = lonelyColumn ? at(x, up, c) + 2 * v + at(x, down, c) : at(left, y, c) + 2 * v + at(right, y, c);
31642
- if (nearLoneX) sum += loneColumnValue(y, c) - at(loneX, y, c);
31643
- if (nearLoneY) sum += loneRowValue(x, c) - at(x, loneY, c);
31644
- r = sum + 2 >> 2;
31865
+ const brighter = 4 * (src[ia] - src[ib]) + 8 * (src[ia + 1] - src[ib + 1]) + (src[ia + 2] - src[ib + 2]);
31866
+ for (const [mx, my, sx, sy, isA] of members) {
31867
+ if (isA ? brighter < 0 : brighter >= 0) {
31868
+ continue;
31869
+ }
31870
+ const o = (my * w + mx) * 3;
31871
+ const t1 = index(mx + sx, my);
31872
+ const t2 = index(mx, my + sy);
31873
+ const t3 = index(mx + 2 * sx, my);
31874
+ const t4 = index(mx, my + 2 * sy);
31875
+ for (let c = 0; c < 3; c++) {
31876
+ px[o + c] = 12 * px[o + c] + src[t1 + c] + src[t2 + c] + src[t3 + c] + src[t4 + c] + 8 >> 4;
31645
31877
  }
31646
- px[(y * w + x) * 3 + c] = r;
31647
31878
  }
31648
31879
  }
31649
31880
  }
@@ -31662,11 +31893,33 @@ function halftoneSharpen(px, w, h) {
31662
31893
  }
31663
31894
  }
31664
31895
  }
31896
+ function halftonePairDespeckle(px, w, h, rows) {
31897
+ const n = rows ? w : h;
31898
+ const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
31899
+ const same = (i, j) => px[i] === px[j] && px[i + 1] === px[j + 1] && px[i + 2] === px[j + 2];
31900
+ 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));
31901
+ const mean = Array.from({ length: n }, (_, k) => checker(k - 1) || checker(k));
31902
+ for (let k = 0; k < n; k++) {
31903
+ if (!mean[k]) continue;
31904
+ const first = at(k, 0);
31905
+ const last = at(k, 1);
31906
+ for (let c = 0; c < 3; c++) px[first + c] = px[last + c] = px[first + c] + px[last + c] + 1 >> 1;
31907
+ }
31908
+ }
31909
+ function halftoneKeepLast(px, w, h, rows) {
31910
+ const n = rows ? w : h;
31911
+ const out = new Int32Array(n * 3);
31912
+ for (let k = 0; k < n; k++) {
31913
+ const last = (rows ? w + k : k * w + 1) * 3;
31914
+ for (let c = 0; c < 3; c++) out[k * 3 + c] = px[last + c];
31915
+ }
31916
+ return out;
31917
+ }
31665
31918
  function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
31666
31919
  const W = Math.max(0, Math.round(Math.abs(dw)));
31667
31920
  const H = Math.max(0, Math.round(Math.abs(dh)));
31668
- const SW = Math.round(Math.abs(sw));
31669
- const SH = Math.round(Math.abs(sh));
31921
+ let SW = Math.round(Math.abs(sw));
31922
+ let SH = Math.round(Math.abs(sh));
31670
31923
  const x0 = Math.round(Math.min(sx, sx + sw));
31671
31924
  const y0 = Math.round(Math.min(sy, sy + sh));
31672
31925
  const flipX = dw < 0 !== sw < 0;
@@ -31675,7 +31928,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31675
31928
  if (W === 0 || H === 0 || SW === 0 || SH === 0) {
31676
31929
  return { width: W, height: H, data };
31677
31930
  }
31678
- const rect = new Int32Array(SW * SH * 3);
31931
+ let rect = new Int32Array(SW * SH * 3);
31679
31932
  for (let y = 0; y < SH; y++) {
31680
31933
  const yy = y0 + y;
31681
31934
  for (let x = 0; x < SW; x++) {
@@ -31690,19 +31943,40 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31690
31943
  rect[o + 2] = src.data[i + 2];
31691
31944
  }
31692
31945
  }
31946
+ const pairRows = SH === 2 && H === 1 && W > SW && (!adjust || adjustAfterSampling || !!dither);
31947
+ const pairColumns = SW === 2 && W === 1 && H > SH && (!adjust || adjustAfterSampling || !!dither);
31948
+ if (pairRows || pairColumns) {
31949
+ if (!directDib) halftonePairDespeckle(rect, SW, SH, pairRows);
31950
+ if (pairRows && W >= 2 * SW) {
31951
+ rect = halftoneKeepLast(rect, SW, SH, true);
31952
+ SH = 1;
31953
+ } else if (pairColumns && H >= 2 * SH) {
31954
+ rect = halftoneKeepLast(rect, SW, SH, false);
31955
+ SW = 1;
31956
+ }
31957
+ }
31693
31958
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
31694
31959
  const reducing = W < SW && H < SH;
31695
31960
  const mixed = W > SW && H < SH || W < SW && H > SH;
31696
31961
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31697
31962
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31698
31963
  const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31699
- if ((enlarging || nearestMixed) && !directDib) {
31964
+ if ((enlarging || nearestMixed) && !directDib && !pairRows && !pairColumns) {
31700
31965
  halftoneDespeckle(rect, SW, SH);
31701
31966
  }
31702
31967
  const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
31703
31968
  const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
31704
31969
  if (ditherAtSource) {
31705
- ditherPixels(rect, SW, SH, dither, false, flipY);
31970
+ ditherPixels(
31971
+ rect,
31972
+ SW,
31973
+ SH,
31974
+ dither,
31975
+ false,
31976
+ false,
31977
+ void 0,
31978
+ flipY ? Int32Array.from({ length: SH }, (_, j) => H - 1 - j) : void 0
31979
+ );
31706
31980
  }
31707
31981
  if (adjust && !adjustAfterSampling && (!dithered || ditherAtSource)) {
31708
31982
  adjust(rect);
@@ -31818,17 +32092,13 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31818
32092
  }
31819
32093
  }
31820
32094
  if (dithered && !ditherAtSource) {
31821
- 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);
31822
- ditherPixels(
31823
- out,
31824
- W,
31825
- H,
31826
- dither,
31827
- false,
31828
- false,
31829
- nearestMixed ? phase(W, SW, W, true) : void 0,
31830
- nearestMixed ? phase(H, SH, H, false) : void 0
31831
- );
32095
+ 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);
32096
+ const sourcePhase = nearestMixed && W * H > SW * SH;
32097
+ let phaseX = sourcePhase ? phase(W, SW, W, true) : void 0;
32098
+ let phaseY = sourcePhase ? phase(H, SH, H, false) : void 0;
32099
+ if (phaseX && flipX) phaseX = phaseX.slice().reverse();
32100
+ if (phaseY && flipY && H > SH) phaseY = phaseY.slice().reverse().map((v) => H - 1 - v);
32101
+ ditherPixels(out, W, H, dither, false, false, phaseX, phaseY);
31832
32102
  adjust(out);
31833
32103
  }
31834
32104
  if (adjust && adjustAfterSampling) adjust(out);
@@ -31891,7 +32161,7 @@ function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode, brushOrg
31891
32161
  canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
31892
32162
  target.restore();
31893
32163
  }
31894
- function decodeSource(rCtx, req) {
32164
+ function decodeSource(rCtx, req, rotated = false) {
31895
32165
  if (!req.source) {
31896
32166
  return null;
31897
32167
  }
@@ -31906,6 +32176,9 @@ function decodeSource(rCtx, req) {
31906
32176
  return null;
31907
32177
  }
31908
32178
  const ca = rCtx.state.colorAdjustment;
32179
+ if (req.stretch && rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32180
+ rCtx.halftoneRotatedAdjusts = !rotated && !!ca;
32181
+ }
31909
32182
  const halftone = req.stretch && rCtx.state.stretchBltMode === HALFTONE && ca;
31910
32183
  const channelOnly = isChannelOnlyColorAdjustment(ca);
31911
32184
  const split = halftone && !channelOnly ? splitColorAdjustment(ca) : void 0;
@@ -32053,6 +32326,7 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32053
32326
  let src = null;
32054
32327
  let srcX = sx;
32055
32328
  let srcY = sy;
32329
+ let adjustTexel;
32056
32330
  if (operands.usesS) {
32057
32331
  const decoded = decodeSource(rCtx, {
32058
32332
  plan: { kind: "ternary", index, operands },
@@ -32067,13 +32341,30 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32067
32341
  sh,
32068
32342
  dibOrigin,
32069
32343
  stretch
32070
- });
32344
+ }, true);
32071
32345
  if (!decoded) {
32072
32346
  return;
32073
32347
  }
32074
32348
  src = decoded.decoded.pixels;
32075
- if (decoded.decoded.adjust) {
32076
- applyColorAdjustment(src, rCtx.state.colorAdjustment);
32349
+ if (decoded.decoded.adjust && rCtx.halftoneRotatedAdjusts) {
32350
+ const { adjust, adjustAfterSampling, curves } = decoded.decoded;
32351
+ const startX = ditherStartX(0, rCtx.state.brushOrgX);
32352
+ const startY = ditherStartY(0, rCtx.state.brushOrgY);
32353
+ const triple = new Int32Array(3);
32354
+ adjustTexel = (rgb2, x, y) => {
32355
+ triple[0] = rgb2 >> 16 & 255;
32356
+ triple[1] = rgb2 >> 8 & 255;
32357
+ triple[2] = rgb2 & 255;
32358
+ if (!adjustAfterSampling) {
32359
+ const t = ditherThreshold(startX, startY, x, y);
32360
+ for (let c = 0; c < 3; c++) {
32361
+ triple[c] = ditherQuantize(triple[c], t);
32362
+ }
32363
+ }
32364
+ adjust(triple);
32365
+ curves?.(triple);
32366
+ return triple[0] << 16 | triple[1] << 8 | triple[2];
32367
+ };
32077
32368
  }
32078
32369
  srcX = decoded.req.sx;
32079
32370
  srcY = decoded.req.sy;
@@ -32097,7 +32388,10 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32097
32388
  sw,
32098
32389
  sh,
32099
32390
  (ix, iy, x, y, d) => {
32100
- const s = operands.usesS ? texel(ix, iy) : 0;
32391
+ let s = operands.usesS ? texel(ix, iy) : 0;
32392
+ if (adjustTexel && operands.usesS) {
32393
+ s = adjustTexel(s, x, y);
32394
+ }
32101
32395
  const p = typeof pattern === "function" ? pattern(x, y) : pattern;
32102
32396
  return evalRop3(index, p, s, d);
32103
32397
  }
@@ -32152,6 +32446,19 @@ function alignMirroredDest(rCtx, req) {
32152
32446
  dy: req.dh < 0 ? req.dy + pxY : req.dy
32153
32447
  };
32154
32448
  }
32449
+ function executePlgBltAsStretch(rCtx, dx, dy, dw, dh, source, r) {
32450
+ executeBlit(rCtx, {
32451
+ plan: classifyRop3(13369376),
32452
+ dx,
32453
+ dy,
32454
+ dw,
32455
+ dh,
32456
+ source,
32457
+ ...r,
32458
+ dibOrigin: "top-left",
32459
+ stretch: true
32460
+ });
32461
+ }
32155
32462
  function executeBlit(rCtx, request) {
32156
32463
  const { ctx } = rCtx;
32157
32464
  const req = alignMirroredDest(rCtx, request);
@@ -32654,6 +32961,18 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32654
32961
  if (b[1] === a[1] && q[0] === a[0]) {
32655
32962
  const W = Math.round(Math.abs(b[0] - a[0]) / 16);
32656
32963
  const H = Math.round(Math.abs(q[1] - a[1]) / 16);
32964
+ if (!mask && rCtx.state.stretchBltMode === HALFTONE && W > 0 && H > 0) {
32965
+ executePlgBltAsStretch(
32966
+ rCtx,
32967
+ Math.round(a[0] / 16),
32968
+ Math.round(a[1] / 16),
32969
+ b[0] < a[0] ? -W : W,
32970
+ q[1] < a[1] ? -H : H,
32971
+ 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)),
32972
+ r
32973
+ );
32974
+ return;
32975
+ }
32657
32976
  const left = Math.round(a[0] / 16) + (b[0] < a[0] ? 1 - W : 0);
32658
32977
  const top = Math.round(a[1] / 16) + (q[1] < a[1] ? 1 - H : 0);
32659
32978
  const aw = Math.abs(r.sw);
@@ -32674,6 +32993,9 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32674
32993
  });
32675
32994
  return;
32676
32995
  }
32996
+ if (rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32997
+ rCtx.halftoneRotatedAdjusts = false;
32998
+ }
32677
32999
  paintParallelogram(rCtx, a, b, q, r.sw, r.sh, (ix, iy) => texel(ix, iy));
32678
33000
  }
32679
33001
  function readBand(rCtx, offset, offBmi, cbBmi, offBits, cbBits, usage, scans) {
@@ -34802,6 +35124,7 @@ function flattenBracketPath(rCtx) {
34802
35124
  }
34803
35125
  for (const f of rCtx.rasterPath.figures) {
34804
35126
  delete f.tangents;
35127
+ delete f.curveSegs;
34805
35128
  delete f.roundWiden;
34806
35129
  }
34807
35130
  syncPathCmds(rCtx);
@@ -38893,7 +39216,7 @@ function wmfRectangle(p, l, t, r, b) {
38893
39216
  axisRect: { interior: rectInterior(p, box) }
38894
39217
  });
38895
39218
  }
38896
- function compatCorner(p, w, h, box) {
39219
+ function compatCorner(p, w, h, box, logical) {
38897
39220
  const m = gdiDeviceMatrix(p.rCtx);
38898
39221
  const cw = Math.round(Math.abs(w * m[0]) * 16);
38899
39222
  const ch = Math.round(Math.abs(h * m[3]) * 16);
@@ -38901,24 +39224,21 @@ function compatCorner(p, w, h, box) {
38901
39224
  const uy = 16 * p.ky;
38902
39225
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38903
39226
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38904
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38905
- return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38906
- }
38907
- if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38908
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38909
- return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
39227
+ const width2 = box.x1 - box.x0, height2 = box.y1 - box.y0;
39228
+ return [Math.floor(Math.min(cw, width2 + penWidth) * width2 / (width2 + penWidth)), Math.floor(Math.min(ch, height2 + penWidth) * height2 / (height2 + penWidth))];
38910
39229
  }
38911
- if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
38912
- return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
38913
- }
38914
- return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
39230
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
39231
+ const fullW = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width + ux;
39232
+ const fullH = Math.abs((logical[3] - logical[1]) * m[3]) * 16 || height + uy;
39233
+ const scaled2 = [Math.min(Math.abs(w * m[0]) * 16, fullW) * width / fullW, Math.min(Math.abs(h * m[3]) * 16, fullH) * height / fullH];
39234
+ return penIsCosmetic(p.rCtx) && !penIsNull(p) ? scaled2 : [Math.floor(scaled2[0]), Math.floor(scaled2[1])];
38915
39235
  }
38916
39236
  function wmfRoundRect(p, l, t, r, b, w, h) {
38917
39237
  const box = compatBox(p, l, t, r, b, { curved: true });
38918
39238
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38919
39239
  return;
38920
39240
  }
38921
- const [cw, ch] = compatCorner(p, w, h, box);
39241
+ const [cw, ch] = compatCorner(p, w, h, box, [l, t, r, b]);
38922
39242
  const cr = canvasRect(box);
38923
39243
  paintGdiShape(p.rCtx, {
38924
39244
  build: (c) => {
@@ -38971,10 +39291,14 @@ function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
38971
39291
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38972
39292
  return null;
38973
39293
  }
38974
- const raw = compatBox(p, l, t, r, b, { exclusive: false });
39294
+ const m = gdiDeviceMatrix(p.rCtx);
39295
+ const deviceFix = (x, y) => [(m[0] * x + m[2] * y + m[4]) * 16, (m[1] * x + m[3] * y + m[5]) * 16];
39296
+ const [ax, ay] = deviceFix(l, t);
39297
+ const [bx, by] = deviceFix(r, b);
39298
+ const raw = { x0: Math.min(ax, bx), x1: Math.max(ax, bx), y0: Math.min(ay, by), y1: Math.max(ay, by) };
38975
39299
  const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
38976
39300
  const radial = (x, y) => {
38977
- const [fx, fy] = fixPoint(p.rCtx, x, y);
39301
+ const [fx, fy] = deviceFix(x, y);
38978
39302
  return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
38979
39303
  };
38980
39304
  return { box, s: radial(xs, ys), e: radial(xe, ye) };