emf-converter 4.7.0 → 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/index.js CHANGED
@@ -11776,19 +11776,73 @@ function roundRectCorners(l, t, r, b, cw, ch, compatible = false, clockwise = fa
11776
11776
  ];
11777
11777
  }
11778
11778
  var TRIG_STEP = 2 * Math.PI / 128;
11779
- function tableTrig(fn) {
11779
+ var TRIG_NODES = [
11780
+ 1,
11781
+ 0.99879566,
11782
+ 0.99518639,
11783
+ 0.98917961,
11784
+ 0.98079146,
11785
+ 0.97003981,
11786
+ 0.95695228,
11787
+ 0.94155978,
11788
+ 0.92389844,
11789
+ 0.90401145,
11790
+ 0.88194619,
11791
+ 0.85775575,
11792
+ 0.83149879,
11793
+ 0.80323775,
11794
+ 0.77303903,
11795
+ 0.74098864,
11796
+ 0.70710648,
11797
+ 0.67151859,
11798
+ 0.63435836,
11799
+ 0.59565849,
11800
+ 0.55552672,
11801
+ 0.51405765,
11802
+ 0.47135039,
11803
+ 0.42750875,
11804
+ 0.38263811,
11805
+ 0.33684632,
11806
+ 0.29024443,
11807
+ 0.24294646,
11808
+ 0.19505974,
11809
+ 0.14670808,
11810
+ 0.0980012,
11811
+ 0.04906089,
11812
+ 0
11813
+ ];
11814
+ var TRIG_TABLE = (() => {
11815
+ const cos = [];
11816
+ const sin = [];
11817
+ for (let k = 0; k <= 128; k++) {
11818
+ const q = k >> 5;
11819
+ const i = k & 31;
11820
+ const c = TRIG_NODES[i];
11821
+ const s = TRIG_NODES[32 - i];
11822
+ if (k === 128) {
11823
+ cos.push(1);
11824
+ sin.push(0);
11825
+ continue;
11826
+ }
11827
+ cos.push([c, -s, -c, s][q]);
11828
+ sin.push([s, c, -s, -c][q]);
11829
+ }
11830
+ return { cos, sin };
11831
+ })();
11832
+ function tableTrig(table) {
11780
11833
  return (a) => {
11781
11834
  let b = a % (2 * Math.PI);
11782
11835
  if (b < 0) {
11783
11836
  b += 2 * Math.PI;
11784
11837
  }
11785
- const i = Math.floor(b / TRIG_STEP);
11786
- const f = b / TRIG_STEP - i;
11787
- return fn(i * TRIG_STEP) * (1 - f) + fn((i + 1) * TRIG_STEP) * f;
11838
+ const x = b / TRIG_STEP;
11839
+ const i = Math.min(127, Math.floor(x));
11840
+ const f = x - i;
11841
+ return table[i] * (1 - f) + table[i + 1] * f;
11788
11842
  };
11789
11843
  }
11790
- var tableCos = tableTrig(Math.cos);
11791
- var tableSin = tableTrig(Math.sin);
11844
+ var tableCos = tableTrig(TRIG_TABLE.cos);
11845
+ var tableSin = tableTrig(TRIG_TABLE.sin);
11792
11846
  function angleArcPieces(startDeg, sweepDeg) {
11793
11847
  const out = [];
11794
11848
  const arc = (from, to) => {
@@ -11859,6 +11913,38 @@ function angleArcFix(box, startDeg, sweepDeg) {
11859
11913
  }
11860
11914
  return out;
11861
11915
  }
11916
+ var SMALL_SWEEP = 3 * Math.PI / 180;
11917
+ function anchoredPiece(boundary, free, rx, ry, cx, cy, smallArc) {
11918
+ const sweep = Math.abs(free - boundary);
11919
+ const small = smallArc;
11920
+ const cos = small ? Math.cos : tableCos;
11921
+ const sin = small ? Math.sin : tableSin;
11922
+ const anchor = [Math.round(cx + rx * Math.cos(boundary)), Math.round(cy - ry * Math.sin(boundary))];
11923
+ const point = [cx + rx * cos(free), cy - ry * sin(free)];
11924
+ let t;
11925
+ let kappa;
11926
+ if (small) {
11927
+ t = Math.tan(sweep / 2);
11928
+ kappa = 2 / 3 * (1 - Math.tan(sweep / 4) ** 2);
11929
+ } else {
11930
+ const c = tableCos(sweep);
11931
+ const sn = tableSin(sweep);
11932
+ t = (1 - c) / sn;
11933
+ const q = t / (1 + Math.sqrt(1 + t * t));
11934
+ kappa = 2 / 3 * (1 - q * q);
11935
+ }
11936
+ const dir = free > boundary ? 1 : -1;
11937
+ const tx = -Math.round(Math.sin(boundary));
11938
+ const ty = -Math.round(Math.cos(boundary));
11939
+ const length = tx !== 0 ? rx : ry;
11940
+ const X = [anchor[0] + dir * tx * length * t, anchor[1] + dir * ty * length * t];
11941
+ return {
11942
+ anchor,
11943
+ free: point,
11944
+ cAnchor: [anchor[0] + kappa * (X[0] - anchor[0]), anchor[1] + kappa * (X[1] - anchor[1])],
11945
+ cFree: [point[0] + kappa * (X[0] - point[0]), point[1] + kappa * (X[1] - point[1])]
11946
+ };
11947
+ }
11862
11948
  function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11863
11949
  const w = r - l;
11864
11950
  const h = b - t;
@@ -11897,6 +11983,21 @@ function arcBeziers(l, t, r, b, xs, ys, xe, ye, clockwise) {
11897
11983
  continue;
11898
11984
  }
11899
11985
  const e = s > 0 ? Math.min(next, a1) : Math.max(next, a1);
11986
+ const endOnBoundary = Math.abs(e / Q - Math.round(e / Q)) < 1e-9;
11987
+ if (onBoundary !== endOnBoundary) {
11988
+ const piece = anchoredPiece(onBoundary ? a : e, onBoundary ? e : a, rx, ry, cx, cy, Math.abs(a1 - a0) <= SMALL_SWEEP);
11989
+ if (onBoundary) {
11990
+ out.push(...[piece.cAnchor, piece.cFree, piece.free].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11991
+ } else {
11992
+ if (out.length === 2) {
11993
+ out[0] = Math.round(piece.free[0]);
11994
+ out[1] = Math.round(piece.free[1]);
11995
+ }
11996
+ out.push(...[piece.cFree, piece.cAnchor, piece.anchor].flatMap((q) => [Math.round(q[0]), Math.round(q[1])]));
11997
+ }
11998
+ a = e;
11999
+ continue;
12000
+ }
11900
12001
  const k = 4 / 3 * Math.tan((e - a) / 4);
11901
12002
  out.push(
11902
12003
  Math.round(px(a) - k * rx * tableSin(a)),
@@ -11991,6 +12092,10 @@ var GdiRasterPath = class {
11991
12092
  };
11992
12093
  const t0 = tangent(x0, y0, c1x, c1y, c2x, c2y, x3, y3);
11993
12094
  const t1 = tangent(c2x, c2y, x3, y3, x3, y3, x3, y3) ?? tangent(c1x, c1y, x3, y3, x3, y3, x3, y3) ?? tangent(x0, y0, x3, y3, x3, y3, x3, y3);
12095
+ f.curveSegs ?? (f.curveSegs = /* @__PURE__ */ new Set());
12096
+ for (let k = first; k <= last; k++) {
12097
+ f.curveSegs.add(k);
12098
+ }
11994
12099
  f.tangents ?? (f.tangents = /* @__PURE__ */ new Map());
11995
12100
  if (t0) f.tangents.set(first, t0);
11996
12101
  if (t1) f.tangents.set(last, t1);
@@ -12024,7 +12129,7 @@ var GdiRasterPath = class {
12024
12129
  /** Appends every figure of `other`. */
12025
12130
  append(other) {
12026
12131
  for (const f of other.figures) {
12027
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
12132
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), curveSegs: f.curveSegs && new Set(f.curveSegs), roundWiden: f.roundWiden });
12028
12133
  }
12029
12134
  for (let i = 0; i < other.getPath.types.length; i++) {
12030
12135
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -16441,23 +16546,241 @@ function applyTint(src, hue, amount) {
16441
16546
  function applyRedEyeCorrection(src, width, height, areas) {
16442
16547
  const out = new Uint8ClampedArray(src);
16443
16548
  for (const a of areas) {
16444
- const x0 = clamp(a.left, 0, width);
16445
- const x1 = clamp(a.right, 0, width);
16446
- const y0 = clamp(a.top, 0, height);
16447
- const y1 = clamp(a.bottom, 0, height);
16448
- for (let y = y0; y < y1; y++) {
16449
- for (let x = x0; x < x1; x++) {
16450
- const i = (y * width + x) * 4;
16451
- const r = out[i];
16452
- const g = out[i + 1];
16453
- const b = out[i + 2];
16454
- if (r >= 2 * g && r >= 2 * b && r > 64) {
16455
- out[i] = Math.round((g + b) / 2);
16549
+ correctRedEyeArea(out, width, clamp(a.left, 0, width), clamp(a.top, 0, height), clamp(a.right, 0, width), clamp(a.bottom, 0, height));
16550
+ }
16551
+ return out;
16552
+ }
16553
+ var RED_EYE_FALLOFF = new Float64Array(
16554
+ [
16555
+ 24930,
16556
+ 24570,
16557
+ 24201,
16558
+ 23836,
16559
+ 23471,
16560
+ 23107,
16561
+ 22744,
16562
+ 22367,
16563
+ 21976,
16564
+ 21587,
16565
+ 21198,
16566
+ 20824,
16567
+ 20490,
16568
+ 20165,
16569
+ 19832,
16570
+ 19499,
16571
+ 19157,
16572
+ 18817,
16573
+ 18480,
16574
+ 18151,
16575
+ 17820,
16576
+ 17490,
16577
+ 17160,
16578
+ 16831,
16579
+ 16506,
16580
+ 16189,
16581
+ 15878,
16582
+ 15569,
16583
+ 15273,
16584
+ 14973,
16585
+ 14668,
16586
+ 14362,
16587
+ 14059,
16588
+ 13768,
16589
+ 13484,
16590
+ 13203,
16591
+ 12923,
16592
+ 12642,
16593
+ 12366,
16594
+ 12094,
16595
+ 11828,
16596
+ 11564,
16597
+ 11301,
16598
+ 11034,
16599
+ 10770,
16600
+ 10507,
16601
+ 10253,
16602
+ 10011,
16603
+ 9771,
16604
+ 9530,
16605
+ 9290,
16606
+ 9053,
16607
+ 8819,
16608
+ 8588,
16609
+ 8359,
16610
+ 8134,
16611
+ 7914,
16612
+ 7697,
16613
+ 7481,
16614
+ 7267,
16615
+ 7056,
16616
+ 6851,
16617
+ 6651,
16618
+ 6453,
16619
+ 6256,
16620
+ 6061,
16621
+ 5871,
16622
+ 5684,
16623
+ 5499,
16624
+ 5317,
16625
+ 5138,
16626
+ 4962,
16627
+ 4789,
16628
+ 4619,
16629
+ 4452,
16630
+ 4289,
16631
+ 4130,
16632
+ 3974,
16633
+ 3820,
16634
+ 3669,
16635
+ 3520,
16636
+ 3375,
16637
+ 3232,
16638
+ 3093,
16639
+ 2958,
16640
+ 2825,
16641
+ 2695,
16642
+ 2568,
16643
+ 2444,
16644
+ 2325,
16645
+ 2207,
16646
+ 2092,
16647
+ 1980,
16648
+ 1872,
16649
+ 1768,
16650
+ 1666,
16651
+ 1567,
16652
+ 1470,
16653
+ 1377,
16654
+ 1287,
16655
+ 1200,
16656
+ 1116,
16657
+ 1034,
16658
+ 956,
16659
+ 881,
16660
+ 808,
16661
+ 739,
16662
+ 674,
16663
+ 612,
16664
+ 541,
16665
+ 462,
16666
+ 381,
16667
+ 299,
16668
+ 220,
16669
+ 147,
16670
+ 85,
16671
+ 36,
16672
+ 3,
16673
+ 0
16674
+ ].map((v) => v / 1e5)
16675
+ );
16676
+ function redEyeFalloff(u) {
16677
+ const t = u * 128;
16678
+ const i = Math.floor(t);
16679
+ if (i >= RED_EYE_FALLOFF.length - 1) {
16680
+ return 0;
16681
+ }
16682
+ return RED_EYE_FALLOFF[i] + (t - i) * (RED_EYE_FALLOFF[i + 1] - RED_EYE_FALLOFF[i]);
16683
+ }
16684
+ var RED_EYE_SECTORS = 60;
16685
+ var RED_EYE_BLUE_WEIGHT = 0.225;
16686
+ var RED_EYE_WEIGHT_CAP = 6;
16687
+ var RED_EYE_PURE_WEIGHT = RED_EYE_WEIGHT_CAP;
16688
+ var RED_EYE_DARK_STRENGTH = 0.49;
16689
+ var RED_EYE_DARK_RATIO = 0.6;
16690
+ var RED_EYE_GREEN_SHARE = 80 / 255;
16691
+ function correctRedEyeArea(out, stride, x0, y0, x1, y1) {
16692
+ const w = x1 - x0;
16693
+ const h = y1 - y0;
16694
+ const count = w * h;
16695
+ const redness = new Float64Array(count);
16696
+ const dark = new Float64Array(count);
16697
+ let sx = 0;
16698
+ let sy = 0;
16699
+ let sw = 0;
16700
+ for (let y = 0; y < h; y++) {
16701
+ for (let x = 0; x < w; x++) {
16702
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16703
+ const r = out[i];
16704
+ const g = out[i + 1];
16705
+ const b = out[i + 2];
16706
+ const k = y * w + x;
16707
+ dark[k] = Math.min(g, b);
16708
+ const v = r - Math.max(g, b);
16709
+ if (v > 0) {
16710
+ redness[k] = v;
16711
+ const denominator = g + RED_EYE_BLUE_WEIGHT * b;
16712
+ {
16713
+ const weight = denominator >= 1 ? Math.min(RED_EYE_WEIGHT_CAP, v / denominator) : RED_EYE_PURE_WEIGHT;
16714
+ sx += weight * (x + 0.5);
16715
+ sy += weight * (y + 0.5);
16716
+ sw += weight;
16456
16717
  }
16457
16718
  }
16458
16719
  }
16459
16720
  }
16460
- return out;
16721
+ if (sw === 0) {
16722
+ if (!redness.some((v) => v > 0)) {
16723
+ return;
16724
+ }
16725
+ sx = w / 2;
16726
+ sy = h / 2;
16727
+ sw = 1;
16728
+ }
16729
+ const cx = sx / sw;
16730
+ const cy = sy / sw;
16731
+ const radius = Math.min(cx, cy) - 0.5;
16732
+ if (!(radius > 0)) {
16733
+ return;
16734
+ }
16735
+ const sector = new Uint8Array(count);
16736
+ const distance = new Float64Array(count);
16737
+ const sectorSum = new Float64Array(RED_EYE_SECTORS);
16738
+ const sectorDark = new Float64Array(RED_EYE_SECTORS);
16739
+ const sectorCount = new Float64Array(RED_EYE_SECTORS);
16740
+ const degrees = 180 / Math.PI;
16741
+ for (let y = 0; y < h; y++) {
16742
+ for (let x = 0; x < w; x++) {
16743
+ const dx = x + 0.5 - cx;
16744
+ const dy = y + 0.5 - cy;
16745
+ const k = y * w + x;
16746
+ const d = Math.hypot(dx, dy);
16747
+ distance[k] = d;
16748
+ let angle = Math.atan2(dy, dx) * degrees;
16749
+ if (angle < 0) {
16750
+ angle += 360;
16751
+ }
16752
+ const s = Math.min(RED_EYE_SECTORS - 1, Math.floor(angle / 6));
16753
+ sector[k] = s;
16754
+ if (d < radius) {
16755
+ sectorSum[s] += redness[k];
16756
+ sectorDark[s] += dark[k];
16757
+ sectorCount[s]++;
16758
+ }
16759
+ }
16760
+ }
16761
+ for (let y = 0; y < h; y++) {
16762
+ for (let x = 0; x < w; x++) {
16763
+ const k = y * w + x;
16764
+ const v = redness[k];
16765
+ if (v <= 0 || distance[k] >= radius) {
16766
+ continue;
16767
+ }
16768
+ const s = sector[k];
16769
+ const mean = sectorSum[s] / sectorCount[s];
16770
+ const meanDark = sectorDark[s] / sectorCount[s];
16771
+ const ratio = meanDark > 0 ? dark[k] / meanDark : 1;
16772
+ const falloff = Math.max(redEyeFalloff(distance[k] / radius), RED_EYE_DARK_STRENGTH * Math.max(0, 1 - ratio / RED_EYE_DARK_RATIO));
16773
+ const rest = (1 - falloff) * mean;
16774
+ if (rest >= v) {
16775
+ continue;
16776
+ }
16777
+ const removed = v - rest;
16778
+ const i = ((y0 + y) * stride + x0 + x) * 4;
16779
+ out[i] = Math.round(out[i] - (1 - RED_EYE_GREEN_SHARE) * removed);
16780
+ out[i + 1] = Math.round(out[i + 1] + RED_EYE_GREEN_SHARE * removed);
16781
+ out[i + 2] = Math.round(out[i + 2] + RED_EYE_GREEN_SHARE * removed);
16782
+ }
16783
+ }
16461
16784
  }
16462
16785
  function blurKernel(radius) {
16463
16786
  const taps = Math.ceil(radius);
@@ -16489,8 +16812,8 @@ function mirror(p, n) {
16489
16812
  function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows = Math.ceil(h / w)) {
16490
16813
  const k = blurKernel(radius);
16491
16814
  const taps = (k.length - 1) / 2;
16492
- const horizontalIndex = taps >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16493
- const verticalIndex = taps >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16815
+ const horizontalIndex = (taps | 1) >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
16816
+ const verticalIndex = (taps | 1) >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
16494
16817
  const out = new Float64Array(w * h * 4);
16495
16818
  for (let y = 0; y < h; y++) {
16496
16819
  const row2 = y * w * 4;
@@ -16501,11 +16824,12 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16501
16824
  let s3 = 0;
16502
16825
  for (let j = -taps; j <= taps; j++) {
16503
16826
  const p = row2 + horizontalIndex(x + j) * 4;
16504
- const kw = k[j + taps] * 256;
16505
- s0 += Math.round(kw * src[p]);
16506
- s1 += Math.round(kw * src[p + 1]);
16507
- s2 += Math.round(kw * src[p + 2]);
16508
- s3 += Math.round(kw * src[p + 3]);
16827
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16828
+ const times = j === 0 ? 2 : 1;
16829
+ s0 += times * Math.round(kw * src[p]);
16830
+ s1 += times * Math.round(kw * src[p + 1]);
16831
+ s2 += times * Math.round(kw * src[p + 2]);
16832
+ s3 += times * Math.round(kw * src[p + 3]);
16509
16833
  }
16510
16834
  const o = row2 + x * 4;
16511
16835
  out[o] = Math.floor((s0 + 128) / 256);
@@ -16525,8 +16849,10 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, verticalRows
16525
16849
  continue;
16526
16850
  }
16527
16851
  const base = verticalIndex(yy) * w * 4;
16852
+ const kw = j === 0 ? k[taps] * 128 : k[j + taps] * 256;
16853
+ const times = j === 0 ? 2 : 1;
16528
16854
  for (let i = 0; i < w * 4; i++) {
16529
- col[i] += Math.round(k[j + taps] * horizontal[base + i] * 256) / 256;
16855
+ col[i] += times * Math.round(kw * horizontal[base + i]) / 256;
16530
16856
  }
16531
16857
  }
16532
16858
  out.set(col, row2 * w * 4);
@@ -16541,10 +16867,12 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16541
16867
  const inner = Math.ceil(span.size / factor);
16542
16868
  const n = inner + 2 * span.pad;
16543
16869
  const taps = (k.length - 1) / 2;
16544
- const index = taps >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16870
+ const index = (taps | 1) >= edgeLength ? (i) => clamp(i, 0, n - 1) : (i) => mirror(i, n);
16545
16871
  const reduced = new Float64Array(n);
16546
16872
  const filtered = new Float64Array(n);
16547
16873
  const k256 = k.map((w) => w * 256);
16874
+ const half = k256[taps] / 2;
16875
+ k256[taps] = 0;
16548
16876
  for (let lane = 0; lane < lanes; lane++) for (let c = 0; c < 4; c++) {
16549
16877
  for (let b = span.pad; b < span.pad + inner; b++) {
16550
16878
  const from = span.start + (b - span.pad) * factor;
@@ -16558,33 +16886,48 @@ function blurAxis(length, lanes, factor, k, edgeLength, get, put, span = { start
16558
16886
  if (b >= taps && b + taps < n) {
16559
16887
  for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[b - taps + j]);
16560
16888
  } else {
16561
- for (let j = 0; j < k256.length; j++) sum += Math.round(k256[j] * reduced[index(b - taps + j)]);
16889
+ for (let j = 0; j < k256.length; j++) {
16890
+ const at = b - taps + j;
16891
+ sum += Math.round(k256[j] * (span.zeroOutside && (at < 0 || at >= n) ? 0 : reduced[index(at)]));
16892
+ }
16562
16893
  }
16894
+ sum += 2 * Math.round(half * reduced[b]);
16563
16895
  filtered[b] = Math.floor((sum + 128) / 256);
16564
16896
  }
16897
+ const lone = inner === 1 && span.pad > 0;
16898
+ const skipped2 = inner === 1 && span.size - Math.ceil(factor / 2) === 1 ? span.start + span.size - 1 : -1;
16565
16899
  for (let i = 0; i < length; i++) {
16566
- const p = (i - span.start + 0.5) / factor - 0.5 + span.pad;
16567
- const a = clamp(Math.floor(p), 0, Math.max(0, n - 2));
16900
+ if (i === skipped2) {
16901
+ if (span.pad > 0) put(i, lane, c, 0);
16902
+ continue;
16903
+ }
16904
+ const p = (i - span.start + 0.5) / factor - 0.5;
16905
+ if (lone) {
16906
+ const distance = Math.abs(i - span.start - (Math.min(span.size, factor / 2) - 0.5)) / factor;
16907
+ put(i, lane, c, clamp(Math.floor((1 + distance) * filtered[span.pad] + 1e-9), 0, 255));
16908
+ continue;
16909
+ }
16910
+ const a = clamp(Math.floor(p), 0, Math.max(0, inner - 2));
16568
16911
  const t = p - a;
16569
- put(i, lane, c, clamp(Math.floor((1 - t) * filtered[a] + t * filtered[Math.min(n - 1, a + 1)] + 1e-9), 0, 255));
16912
+ put(i, lane, c, clamp(Math.floor((1 - t) * filtered[span.pad + a] + t * filtered[span.pad + Math.min(inner - 1, a + 1)] + 1e-9), 0, 255));
16570
16913
  }
16571
16914
  }
16572
16915
  }
16573
- function effectBlur(src, width, height, radius, region) {
16916
+ function effectBlur(src, width, height, radius, region, expanded = false) {
16574
16917
  const factor = blurReduction(radius);
16575
16918
  if (factor === 1) {
16576
16919
  return gdipBlur(src, width, height, radius);
16577
16920
  }
16578
16921
  const k = blurKernel(radius / factor);
16579
16922
  const pad = region ? Math.ceil(Math.ceil(radius) / factor) : 0;
16580
- const spanX = region ? { start: region.x, size: region.w, pad } : void 0;
16581
- const spanY = region ? { start: region.y, size: region.h, pad } : void 0;
16582
- const rows = new Uint8Array(src.length);
16583
- blurAxis(width, height, factor, k, Math.floor(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16923
+ const spanX = region ? { start: region.x, size: region.w, pad, zeroOutside: true } : expanded ? { start: 0, size: width, pad: 0, zeroOutside: true } : void 0;
16924
+ const spanY = region ? { start: region.y, size: region.h, pad, zeroOutside: true } : expanded ? { start: 0, size: height, pad: 0, zeroOutside: true } : void 0;
16925
+ const rows = new Uint8Array(src);
16926
+ blurAxis(width, height, factor, k, Math.ceil(width / factor), (i, lane, c) => src[(lane * width + i) * 4 + c], (i, lane, c, v) => {
16584
16927
  rows[(lane * width + i) * 4 + c] = v;
16585
16928
  }, spanX);
16586
- const out = new Float64Array(src.length);
16587
- blurAxis(height, width, factor, k, Math.floor(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16929
+ const out = Float64Array.from(rows);
16930
+ blurAxis(height, width, factor, k, Math.ceil(height / factor), (i, lane, c) => rows[(i * width + lane) * 4 + c], (i, lane, c, v) => {
16588
16931
  out[(i * width + lane) * 4 + c] = v;
16589
16932
  }, spanY);
16590
16933
  return out;
@@ -16672,7 +17015,7 @@ function expandedBlur(rgba, width, height, radius, x0, y0, x1, y1, cropped) {
16672
17015
  const gy1 = Math.min(height, y1 + r);
16673
17016
  copyBlock(rgba, width, gx0, gy0, gx1 - gx0, gy1 - gy0, buf, bw, gx0 - (x0 - r), gy0 - (y0 - r));
16674
17017
  }
16675
- const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
17018
+ const blurred = blurReduction(radius) > 1 ? effectBlur(buf, bw, bh, radius, cropped ? { x: r, y: r, w: x1 - x0, h: y1 - y0 } : void 0, true) : gdipBlur(buf, bw, bh, radius, cropped ? r : 0, cropped, Math.ceil((y1 - y0) / (x1 - x0)));
16676
17019
  const w = x1 - x0;
16677
17020
  const out = new Uint8ClampedArray(w * (y1 - y0) * 4);
16678
17021
  for (let y = 0; y < y1 - y0; y++) {
@@ -28737,118 +29080,19 @@ var HOBBY = [
28737
29080
  [[8, -48], [-8, -48], [-24, -40], [-40, -24], [-48, -8], [-48, 8], [-40, 24], [-24, 40], [-8, 48], [8, 48], [24, 40], [40, 24], [48, 8], [48, -8], [40, -24], [24, -40]]
28738
29081
  ];
28739
29082
  var HOBBY_LIMIT = 104;
28740
- var FLAT_SECTOR_BIAS = {
28741
- 7: [9, "cf..", "edededEdcDcdcd"],
28742
- 8: [9, "dFee", "ededededbdbdbd"],
28743
- 9: [9, "df..", "ddddddddbDbdbd"],
28744
- 10: [9, "de..", "dededeDebEbebE"],
28745
- 11: [9, "ce..", "eeeeeeEececece"],
28746
- 12: [13, "ce..", "eeeeeeeeeeeececEcecece"],
28747
- 13: [17, "ce..", "eeeeededdddddeDebebebdbdcdcdce"],
28748
- 14: [17, "df..", "ddedeeeededeeeedcDcebebecececd"],
28749
- 15: [17, "dF..", "ddedeeeeeeeedeDdbdbecececececd"],
28750
- 16: [17, "ce..", "eeeededededeeeeececebebebebece"],
28751
- 17: [17, "df..", "ddedededededdeddbdbebdbdbdbdcd"],
28752
- 18: [17, "de..", "deeededeededeeEdcDcecdcdbebece"],
28753
- 19: [17, "cf..", "ededeeeedddddeDebEbecdcdbebecd"],
28754
- 20: [17, "ce..", "eeeeddddeeeeddDdbdbdcecebdbdce"],
28755
- 21: [17, "dF..", "ddddededededddDdbdbdcdcdcdcdbd"],
28756
- 22: [17, "ce..", "eeeededeeeeedeDebebececebebece"],
28757
- 23: [17, "cf..", "edededededededEdcDcdcdcdcdcdcD"],
28758
- 24: [17, "dF..", "ddddddddddddddDdbdbdbdbdbdbdbd"],
28759
- 25: [17, "cE..", "eeeeededededeeEecEcecdcdcdcdce"],
28760
- 26: [17, "cf..", "ededdedeededdedebEbecdcdbebecd"],
28761
- 27: [17, "de..", "dedeededdedeededcDcdbebecdcdbe"],
28762
- 28: [17, "cf..", "ededddddeeeedeDebEbebebecdcdcd"],
28763
- 29: [17, "ce..", "eeeeeeeededededebebebebececece"],
28764
- 30: [17, "dF..", "dddddedeeeeeedEdcdcdcecebebebd"],
28765
- 31: [17, "de..", "dedeededededdeDebebecdcdcdcdbe"],
28766
- 32: [17, "cf..", "ededdededdddeeeecEcebdbdbebecd"],
28767
- 33: [17, "de..", "dedeeeeeededddDdbdbdcdcdcecebe"],
28768
- 34: [17, "de..", "dedeeeeeddddedEdcdcdbdbdcecebe"],
28769
- 35: [17, "cf..", "ededededdededeDebebebebecdcdcd"],
28770
- 36: [17, "deee", "eeeeeeeeddddddDdcdcdcdcDbebebe"],
28771
- 37: [17, "dF..", "dddddededededdDdbdbdcecececebd"],
28772
- 38: [17, "ce..", "eeeeddddddddeeEececebdbdbdbdce"],
28773
- 39: [17, "cF..", "ededddddddddededcdcdbdbDbdbdcd"],
28774
- 40: [17, "de..", "dededededdddddddbdbdbdbdbebebe"],
28775
- 41: [17, "ce..", "eeeeeeeeeeeeeeEececececececece"],
28776
- 42: [17, "cf..", "ededeeeedddddedebebebdbdcececd"],
28777
- 43: [17, "df..", "ddddeeeededeedEdcdcdbebececebd"],
28778
- 44: [17, "cF..", "ededdedeededdeDebebecdcdbebecd"],
28779
- 45: [17, "ce..", "eeeededededeeeeecEcebebebebece"],
28780
- 46: [17, "dF..", "ddddededddddedEdcDcdcdcdbdbdbd"],
28781
- 47: [17, "ce..", "dedededededededececececececece"],
28782
- 48: [25, "cf..", "ededeeeeeeeeededeeeeeeEecececececdcdcececececd"],
28783
- 49: [25, "cF..", "ededddddededeeeeededdeDebebecdcdcececdcdbdbdcd"],
28784
- 50: [25, "ce..", "eeeeddddededdededededeDebebebebebEbecdcdbdbdce"],
28785
- 51: [25, "ce..", "eeeeededddddeeeededeeeEececebebececebdbdcdcdce"],
28786
- 52: [25, "ce..", "eeeededededeededededeeEecececdcdcdcdbebebebece"],
28787
- 53: [25, "dF..", "ddedeeeedddddddddedeeeEdcdcebebebdbDbdbdcececd"],
28788
- 54: [25, "dF..", "ddedddddededededdddddEDdbdbebdbdcdcdcdcdbdbdcd"],
28789
- 55: [25, "dF..", "ddeddedeeeeeeeeedededeDdbdbebebebebebebebebecd"],
28790
- 56: [25, "dE..", "deeeeeeeededeeeededeeEedcdcebebecececdcdcecece"],
28791
- 57: [25, "de..", "deeedddddddddedeededeeEdcDcecDcdcececdcdbdbDce"],
28792
- 58: [25, "dF..", "ddedddddeeeedddddedeeeEdcdcebebebdbdcecebdbdcd"],
28793
- 59: [33, "cF..", "ededdedeededddddeeeeeeeedddddeDebebebdbdcecececebdbdcdcdbebecd"],
28794
- 60: [33, "cf..", "ededeeeeededededddddeeeeeeeedeDebebececececebdbdcdcdcdcdcececd"],
28795
- 61: [33, "ce..", "eeeeeeeeddddeeeeeeeeddddeeeeeeeececececebdbdcecececebdbdcecece"],
28796
- 62: [33, "ce..", "eeeeddddeeeeededededeeeeddddeeEecEcebdbdcececdcdcdcdcecebdbdce"],
28797
- 63: [33, "cf..", "ededededeeeededededeeeeeeeeeeeEececececececebebEbebecececdcdcd"],
28798
- 64: [33, "cF..", "ededeeeedddddedeeeeededeeeeedeDebebececebebececebebebdbdcececd"],
28799
- 65: [33, "df..", "ddedededededddddddddeeeeeeeedeDdbdbececececebdbdbdbdcdcdcdcdcd"],
28800
- 66: [33, "de..", "deeeededdedeeeeedddddededddddeDdbdbebdbdbebecdcdbebebebecdcdce"],
28801
- 67: [33, "de..", "deeeddddddddededdddddededdddeeEdcDcebdbdbebebdbdcdcdbdbdbdbdce"],
28802
- 68: [33, "dE..", "deeeeeeeeeeeeeeeddddeeeeeeeeeeEdcdcececececebdbdcecececececece"],
28803
- 69: [33, "df..", "ddedededdedeeeeeddddededededdeDdbdbecdcdcdcdbdbdcecebebecdcdcd"],
28804
- 70: [33, "dF..", "ddeddddddddddddddededededddddeDdbdbebdbdbebebebebdbdbdbdbdbDcd"],
28805
- 71: [33, "ce..", "eeeeddddeeeededeeeeededeededdeDebebecdcdbebececebebececebdbdce"],
28806
- 72: [33, "ce..", "eeeededededeededededddddddddeeEececebdbdbdbdcdcdcdcdbebebebece"],
28807
- 73: [33, "ce..", "eeeeededededddddededdedeeeeeeeEececececebebecdcdbdbdcdcDcdcdce"],
28808
- 74: [33, "cf..", "ededddddededededededdededddddeDebebebdbdbebecdcDcdcdcdcdbdbdcd"],
28809
- 75: [33, "cF..", "ededdededededdddddddeeeedddddeDebebebdbdcececdcdcdcdbebebebecd"],
28810
- 76: [33, "ce..", "eeeeeeeeeeeeeeeeeeeededeeeeedeDebebececebebecececececececececE"],
28811
- 77: [33, "ce..", "eeeeeeeedededddddededdddedededEdcDcdcdcdbdbdbebebdbdbebececece"],
28812
- 78: [33, "de..", "dedeededdedededeeeeeeeeededeedEdcdcdbebececececebebebebecdcdbe"],
28813
- 79: [33, "dF..", "ddddddddededdededdddeeeeedededEdcDcdcdcdcecebdbdbebecdcdbdbdbd"],
28814
- 80: [33, "dF..", "dddddededdddeeeeddddddddededddDdbdbdcdcdbdbdbdbDcecebdbdbebEbd"],
28815
- 81: [33, "de..", "dedeeeeeeeeededeededeeeedededdDdbdbdbebecececdcdbebececececebe"],
28816
- 82: [33, "de..", "dedeededdedeededdedededeedededEdcdcdcdcdbebebebecdcdbebecdcDbe"],
28817
- 83: [33, "de..", "dededededddddededdddeeeededeeeEececebebececebdbdbebebdbdbebebe"],
28818
- 84: [33, "cF..", "ededddddeeeeddddeeeeeeeeededeeEecececdcdcecebebecdcdcecebdbdcd"],
28819
- 85: [33, "cf..", "ededdddddededddddddddedeeeeedeDebebececebebebdbdbdbdbebebdbdcd"],
28820
- 86: [33, "ce..", "eeeeededddddeeeeeeeedededededeDebebebebebebebebebebebdbdcdcdce"],
28821
- 87: [33, "ce..", "eeeeddddeeeeddddddddeeeeddddeeeececebdbdcecebdbDbdbdcecebdbdce"],
28822
- 88: [33, "ce..", "eeeedededdddeeeeededededededeeeecececdcdcdcdcdcdcecebdbdbebece"],
28823
- 89: [33, "cF..", "ededeeeeddddddddededdedededeeeEecEcebebEbebecdcdbdbdbdbdcececd"],
28824
- 90: [33, "dF..", "ddddededeeeeededeeeedededededdDdbdbdbebebEbecececdcdcececdcdbd"],
28825
- 91: [33, "df..", "ddddeeeededededeededdddddededdDdbdbdbebebdbdcdcdbebebebececebd"],
28826
- 92: [33, "de..", "dedededeededdddddededededdddedEdcdcdbdbdbebebebebdbdcdcdbebebe"],
28827
- 93: [33, "de..", "dededddddddddedeededeeeeeeeeededcdcdcececececdcdbebebdbdbdbdbe"],
28828
- 94: [33, "dF..", "ddddeeeeddddeeeeddddeeeededeedEdcDcdbebececebdbDcecebdbdcecebd"],
28829
- 95: [33, "df..", "ddddddddededeeeedddddededdddddDdbdbDbdbdbebecdcdbebecdcdbdbdbd"],
28830
- 96: [33, "cf..", "ededeeeededeeeeedededdddeeeedeDebebececebdbdbebececebebecececd"],
28831
- 97: [33, "ce..", "eeeedededededededdddeeeedddddeDebebebdbdcEcebdbdbebebEbebebece"],
28832
- 98: [33, "ce..", "eeeeededededeeeeeeeededededeeeEececebebebebecececececdcdcdcdce"],
28833
- 99: [33, "cf..", "ededededddddddddddddededdedeeeEececebebecdcdbdbdbdbdbdbdcdcdcd"],
28834
- 100: [33, "cf..", "ededdedeeeeededededeeeeeededdeDebebecdcdcecebebebebececebebecd"]
28835
- };
28836
- function sectorBias(code) {
28837
- return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
29083
+ function oddEdgeAdjustment(odd, firstEdge) {
29084
+ return odd ? firstEdge ? 0.5 : -0.5 : 0;
28838
29085
  }
28839
- var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28840
- var VERTICAL_STEP_FIRST = 7;
28841
- function verticalSideOffset(width) {
28842
- if (width < HOBBY_LIMIT || width > 1400) {
28843
- return void 0;
29086
+ function floorDivision(a, b) {
29087
+ if (Number.isSafeInteger(a) && Number.isSafeInteger(b)) {
29088
+ return Math.floor(a / b);
28844
29089
  }
28845
- let m = VERTICAL_STEP_FIRST;
28846
- for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28847
- if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28848
- m = i + VERTICAL_STEP_FIRST;
28849
- }
29090
+ const A = BigInt(Math.round(a)), B = BigInt(Math.round(b));
29091
+ let q = A / B;
29092
+ if (A % B !== 0n && A < 0n !== B < 0n) {
29093
+ q -= 1n;
28850
29094
  }
28851
- return 8 * m;
29095
+ return Number(q);
28852
29096
  }
28853
29097
  var penCache = /* @__PURE__ */ new Map();
28854
29098
  function penPolygon(width) {
@@ -28920,6 +29164,9 @@ function perpendicularVectors(width, dx0, dy0) {
28920
29164
  best = i;
28921
29165
  }
28922
29166
  }
29167
+ if (flip && best === 0 && nx * pen[n - 1][0] + ny * pen[n - 1][1] === bh) {
29168
+ best = n - 1;
29169
+ }
28923
29170
  const P = pen[(best + n - 1) % n];
28924
29171
  const N = pen[(best + 1) % n];
28925
29172
  const D = pen[best];
@@ -28928,20 +29175,19 @@ function perpendicularVectors(width, dx0, dy0) {
28928
29175
  const [B, hB, hS] = hP >= hN ? [P, hP, hN] : [N, hN, hP];
28929
29176
  const den = 2 * (bh - hB + (bh - hS));
28930
29177
  const w = den === 0 ? 0 : (hB - hS) / den;
28931
- const r = (v) => 8 * Math.floor((v + 4) / 8);
28932
29178
  const x = D[0] + (B[0] - D[0]) * w, y = D[1] + (B[1] - D[1]) * w;
28933
- let cx = 0.5 * Math.sign(dy), cy = 0.5 * Math.sign(dx);
28934
- const table = width % 16 === 0 ? FLAT_SECTOR_BIAS[width / 16] : void 0;
28935
- if (table) {
28936
- const key = best * 2 + (hP >= hN ? 0 : 1);
28937
- const pair = key === 0 ? table[1].slice(0, 2) : key >= table[0] ? table[2].slice((key - table[0]) * 2, (key - table[0]) * 2 + 2) : "..";
28938
- if (pair[0] !== ".") {
28939
- cx = sectorBias(pair.charCodeAt(0));
28940
- cy = sectorBias(pair.charCodeAt(1));
28941
- }
28942
- }
28943
- const side = dx === 0 ? verticalSideOffset(width) : void 0;
28944
- const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
29179
+ const signs = [dx === 0 ? 0 : Math.sign(dy), Math.sign(dx)];
29180
+ const firstEdge = best === 0 && hP >= hN;
29181
+ const num = hB - hS;
29182
+ const rounded = [0, 1].map((c) => {
29183
+ const bias2 = signs[c] + 2 * oddEdgeAdjustment(Math.abs(B[c] - D[c]) % 2 === 1, firstEdge);
29184
+ if (den === 0) {
29185
+ return 8 * (c === 1 && dy < 0 ? Math.ceil((D[c] + bias2 / 2 + 4) / 8) - 1 : Math.floor((D[c] + bias2 / 2 + 4) / 8));
29186
+ }
29187
+ const top = D[c] * den + (B[c] - D[c]) * num;
29188
+ return 8 * floorDivision(2 * top + den * (bias2 + 8) - (c === 1 && dy < 0 ? 1 : 0), 16 * den);
29189
+ });
29190
+ const vx = rounded[0], vy = rounded[1];
28945
29191
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28946
29192
  }
28947
29193
  function squareExtension(width, dx, dy) {
@@ -28971,7 +29217,6 @@ var Outliner = class {
28971
29217
  this.n = this.pen.length;
28972
29218
  this.rr = opts.cap === "round" && opts.join === "round";
28973
29219
  this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28974
- this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28975
29220
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28976
29221
  }
28977
29222
  /**
@@ -28979,14 +29224,14 @@ var Outliner = class {
28979
29224
  * `drawDir` (a curve's end tangent) only picks the draw vertices, the
28980
29225
  * perpendicular following the chord (measured on Bezier ends).
28981
29226
  */
28982
- seg(a, b, dir, drawDir) {
29227
+ seg(a, b, dir, drawDir, curve = false) {
28983
29228
  const dx = dir ? dir[0] : b[0] - a[0];
28984
29229
  const dy = dir ? dir[1] : b[1] - a[1];
28985
29230
  const d = drawDir ?? [dx, dy];
28986
29231
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28987
- const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
29232
+ const perpendicular = curve && drawDir ? drawDir : [dx, dy];
28988
29233
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
28989
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
29234
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
28990
29235
  }
28991
29236
  push(p, v) {
28992
29237
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29021,7 +29266,7 @@ var Outliner = class {
29021
29266
  * vertex exactly on `A`'s ray is included when `startIncl`, one on `B`'s
29022
29267
  * ray when `endIncl`.
29023
29268
  */
29024
- wedge(p, A, B, startIncl, endIncl) {
29269
+ wedge(p, A, B, startIncl, endIncl, tail = -1) {
29025
29270
  const TWO = Math.PI * 2;
29026
29271
  const aA = Math.atan2(A[1], A[0]);
29027
29272
  const angleOf = (Q) => {
@@ -29052,13 +29297,21 @@ var Outliner = class {
29052
29297
  }
29053
29298
  }
29054
29299
  list.sort((x, y) => x[0] - y[0]);
29300
+ if (tail >= 0 && !list.some(([, k]) => k === tail)) {
29301
+ list.push([Infinity, tail]);
29302
+ }
29055
29303
  for (const [, k] of list) {
29056
29304
  this.penAt(p, k);
29057
29305
  }
29058
29306
  }
29307
+ /** The pen's extreme-x vertex `k` that a steep segment `s` rests on, or -1 when the rule does not apply. */
29308
+ extremeTail(applies, k, s) {
29309
+ const q = this.pen[k];
29310
+ return applies && Math.abs(s.dy) > Math.abs(s.dx) && Math.abs(q[0]) === this.maxX && q[1] === 0 ? k : -1;
29311
+ }
29059
29312
  /** Side offset of segment `s` at a join. */
29060
29313
  joinSide(s, side) {
29061
- if (this.roundJoinSides) {
29314
+ if (this.originalRoundJoinSides || s.curve) {
29062
29315
  const q = this.pen[side === "L" ? s.L : s.R];
29063
29316
  return [halfPixel(q[0]), halfPixel(q[1])];
29064
29317
  }
@@ -29106,32 +29359,36 @@ var Outliner = class {
29106
29359
  */
29107
29360
  join(p, a, b, side, outer) {
29108
29361
  const { cap, width, miterLimit } = this.opts;
29109
- const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
29362
+ const curveJoin = a.curve && b.curve;
29363
+ const join = curveJoin ? "round" : this.opts.join;
29364
+ const roundSides = this.originalRoundJoinSides || curveJoin;
29110
29365
  let sa = this.joinSide(a, side);
29111
29366
  let sb = this.joinSide(b, side);
29112
29367
  const rayA = side === "R" ? a.vRaw : [-a.vRaw[0], -a.vRaw[1]];
29113
29368
  const rayB = side === "R" ? b.vRaw : [-b.vRaw[0], -b.vRaw[1]];
29114
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29115
- sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
29116
- sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
29369
+ if (curveJoin && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
29370
+ const t = a.tangentLength >= b.tangentLength ? a : b;
29371
+ sa = sb = side === "R" ? t.v : [-t.v[0], -t.v[1]];
29117
29372
  }
29118
29373
  const Da = side === "R" ? a.R : a.L;
29119
29374
  const Db = side === "R" ? b.R : b.L;
29120
- if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
29375
+ if (roundSides && (Da === Db || curveJoin && sa[0] === sb[0] && sa[1] === sb[1])) {
29121
29376
  this.push(p, sa);
29122
29377
  return;
29123
29378
  }
29124
29379
  this.push(p, sa);
29125
29380
  if (outer) {
29126
29381
  if (join === "round") {
29127
- if (this.roundJoinSides) {
29382
+ if (roundSides) {
29128
29383
  const q = this.pen[Db];
29129
29384
  const steep = Math.abs(b.dy) > Math.abs(b.dx);
29130
29385
  const incl = side === "L" && steep && Math.abs(q[0]) === this.maxX && q[0] * q[1] <= 0;
29131
29386
  this.walk(p, Da, Db, false, incl);
29132
29387
  } else {
29133
29388
  const anti = sa[0] === -sb[0] && sa[1] === -sb[1];
29134
- this.wedge(p, rayA, rayB, !anti, !anti);
29389
+ if (Da !== Db || anti) {
29390
+ this.wedge(p, rayA, rayB, !anti, false, this.extremeTail(side === "L" && !anti, Db, b));
29391
+ }
29135
29392
  }
29136
29393
  } else if (join === "miter") {
29137
29394
  const m = miterPoint(sa, [a.dx, a.dy], sb, [b.dx, b.dy], width, miterLimit, side);
@@ -29141,14 +29398,16 @@ var Outliner = class {
29141
29398
  }
29142
29399
  } else {
29143
29400
  this.pts.push([p[0], p[1]]);
29144
- if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
29401
+ if (curveJoin && !this.originalRoundJoinSides) {
29145
29402
  this.push(p, sb);
29146
29403
  this.push(p, sa);
29147
29404
  this.pts.push([p[0], p[1]]);
29148
29405
  }
29149
- if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
29406
+ if (join === "round" && cap === "flat" && !curveJoin) {
29150
29407
  this.push(p, sb);
29151
- this.wedge(p, rayB, rayA, false, false);
29408
+ if (Da !== Db) {
29409
+ this.wedge(p, rayB, rayA, false, false, this.extremeTail(side === "L", Da, a));
29410
+ }
29152
29411
  this.push(p, sa);
29153
29412
  this.pts.push([p[0], p[1]]);
29154
29413
  }
@@ -29181,17 +29440,17 @@ var Outliner = class {
29181
29440
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29182
29441
  * `draws` (curve end tangents) picks draw vertices only.
29183
29442
  */
29184
- open(P, dirs, draws) {
29443
+ open(P, dirs, draws, curves) {
29185
29444
  if (P.length < 2 && !dirs?.[0]) {
29186
29445
  this.dot(P[0]);
29187
29446
  return;
29188
29447
  }
29189
29448
  const segs2 = [];
29190
29449
  for (let i = 0; i + 1 < P.length; i++) {
29191
- segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i]));
29450
+ segs2.push(this.seg(P[i], P[i + 1], dirs?.[i], draws?.[i], curves?.[i]));
29192
29451
  }
29193
29452
  if (segs2.length === 0) {
29194
- const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0]);
29453
+ const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29195
29454
  this.cap(P[0], s, true);
29196
29455
  this.cap(P[0], s, false);
29197
29456
  this.flush();
@@ -29222,11 +29481,11 @@ var Outliner = class {
29222
29481
  this.flush();
29223
29482
  }
29224
29483
  /** Outlines a closed polygon (distinct consecutive points, not repeating the first). */
29225
- closed(P, draws) {
29484
+ closed(P, draws, curves) {
29226
29485
  const m = P.length;
29227
29486
  const segs2 = [];
29228
29487
  for (let i = 0; i < m; i++) {
29229
- segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i]));
29488
+ segs2.push(this.seg(P[i], P[(i + 1) % m], void 0, draws?.[i], curves?.[i]));
29230
29489
  }
29231
29490
  for (let j = 1; j <= m; j++) {
29232
29491
  const i = j % m;
@@ -29318,9 +29577,10 @@ function widenPath(path, opts) {
29318
29577
  const out = [];
29319
29578
  const dashed = !!opts.dashes && opts.dashes.length > 0;
29320
29579
  for (const fig of path.figures) {
29321
- const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
29580
+ const outliner = new Outliner(opts, out);
29322
29581
  let P = [];
29323
29582
  const dirs = [];
29583
+ const curves = [];
29324
29584
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
29325
29585
  const q = [fig.pts[i], fig.pts[i + 1]];
29326
29586
  const last = P[P.length - 1];
@@ -29329,6 +29589,7 @@ function widenPath(path, opts) {
29329
29589
  }
29330
29590
  if (last) {
29331
29591
  dirs.push(fig.tangents?.get(i / 2 - 1));
29592
+ curves.push(!!fig.roundWiden || !!fig.curveSegs?.has(i / 2 - 1));
29332
29593
  }
29333
29594
  P.push(q);
29334
29595
  }
@@ -29355,14 +29616,14 @@ function widenPath(path, opts) {
29355
29616
  pdraws.push(piece.draws[i - 1]);
29356
29617
  }
29357
29618
  }
29358
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]]);
29619
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29359
29620
  }
29360
29621
  } else if (closed && P.length >= 3) {
29361
- outliner.closed(P, dirs);
29622
+ outliner.closed(P, dirs, curves);
29362
29623
  } else if (closed && P.length === 2) {
29363
29624
  outliner.open([P[0], P[1], P[0]]);
29364
29625
  } else {
29365
- outliner.open(P, void 0, dirs);
29626
+ outliner.open(P, void 0, dirs, curves);
29366
29627
  }
29367
29628
  }
29368
29629
  return out;
@@ -31107,6 +31368,10 @@ function hasIlluminant(ca) {
31107
31368
  return illuminantCube(ca.illuminant) !== null;
31108
31369
  }
31109
31370
  var PALETTE = Array.from({ length: 32 }, (_, n) => Math.round(n * 255 / 31));
31371
+ function exactPaletteLevel(v) {
31372
+ const n = Math.round(v * 31 / 255);
31373
+ return PALETTE[n] === v ? 255 * n / 31 : v;
31374
+ }
31110
31375
  function paletteIndex(v) {
31111
31376
  const x = clamp012(v) * 255;
31112
31377
  let n = Math.min(30, Math.floor(x * 31 / 255));
@@ -31192,6 +31457,7 @@ function invert3(m) {
31192
31457
  }
31193
31458
  var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
31194
31459
  var clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
31460
+ var roundHalfUp = (v) => Math.floor(v + 0.5 + 1e-7);
31195
31461
  function adjustChroma(r, g, b, scale, angle) {
31196
31462
  const x = RGB_TO_XYZ[0][0] * r + RGB_TO_XYZ[0][1] * g + RGB_TO_XYZ[0][2] * b;
31197
31463
  const y = RGB_TO_XYZ[1][0] * r + RGB_TO_XYZ[1][1] * g + RGB_TO_XYZ[1][2] * b;
@@ -31243,7 +31509,7 @@ function colorAdjustmentMapper(ca) {
31243
31509
  if (negative) {
31244
31510
  v = 255 - clamp255(v);
31245
31511
  }
31246
- return Math.round(clamp255(v));
31512
+ return roundHalfUp(clamp255(v));
31247
31513
  };
31248
31514
  if (!illuminant && !chroma) {
31249
31515
  const tables = [0, 1, 2].map((channel) => Uint8Array.from({ length: 256 }, (_, i) => curve(i, channel)));
@@ -31256,7 +31522,11 @@ function colorAdjustmentMapper(ca) {
31256
31522
  c = [mapped[0] * 255, mapped[1] * 255, mapped[2] * 255];
31257
31523
  }
31258
31524
  if (chroma) {
31525
+ if (!illuminant) {
31526
+ c = c.map(exactPaletteLevel);
31527
+ }
31259
31528
  c = adjustChroma(clamp255(c[0]), clamp255(c[1]), clamp255(c[2]), scale, angle);
31529
+ c = c.map(roundHalfUp);
31260
31530
  }
31261
31531
  return curve(c[0], 0) << 16 | curve(c[1], 1) << 8 | curve(c[2], 2);
31262
31532
  };
@@ -31290,27 +31560,6 @@ function colorAdjustRgb(rgbs, ca) {
31290
31560
  rgbs[i + 2] = v & 255;
31291
31561
  }
31292
31562
  }
31293
- function applyColorAdjustment(pixels, ca) {
31294
- if (!ca || isIdentityColorAdjustment(ca)) {
31295
- return;
31296
- }
31297
- const map = colorAdjustmentMapper(ca);
31298
- const d = pixels.data;
31299
- const cache2 = /* @__PURE__ */ new Map();
31300
- for (let i = 0; i + 3 < d.length; i += 4) {
31301
- const rgb2 = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
31302
- let v = cache2.get(rgb2);
31303
- if (v === void 0) {
31304
- v = map(rgb2);
31305
- if (cache2.size < 65536) {
31306
- cache2.set(rgb2, v);
31307
- }
31308
- }
31309
- d[i] = v >> 16 & 255;
31310
- d[i + 1] = v >> 8 & 255;
31311
- d[i + 2] = v & 255;
31312
- }
31313
- }
31314
31563
 
31315
31564
  // src/emf-gdi-halftone-dither.ts
31316
31565
  var THRESHOLDS = [
@@ -31469,22 +31718,22 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31469
31718
  if (reverse) taps.reverse();
31470
31719
  return taps;
31471
31720
  }
31721
+ var REDUCE_BITS = 13;
31472
31722
  function halftoneReduceTaps(srcLen, dstLen, reverse) {
31473
31723
  if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31724
+ const shares = 2 ** REDUCE_BITS;
31725
+ const scale = FIX / shares;
31474
31726
  const taps = [];
31475
31727
  for (let i = 0; i < dstLen; i++) {
31476
31728
  const from = i * srcLen;
31477
31729
  const to = (i + 1) * srcLen;
31478
31730
  const run = [];
31479
- let rest = 0;
31480
31731
  for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31481
- const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31482
- if (overlap <= 0) continue;
31483
- const w = Math.ceil(overlap * FIX / srcLen);
31484
- run.push([k, w]);
31485
- if (run.length > 1) rest += w;
31732
+ const lo = Math.max(from, k * dstLen) - from;
31733
+ const hi = Math.min(to, (k + 1) * dstLen) - from;
31734
+ if (hi <= lo) continue;
31735
+ run.push([k, (Math.floor(hi * shares / srcLen) - Math.floor(lo * shares / srcLen)) * scale]);
31486
31736
  }
31487
- if (run.length > 1) run[0][1] = FIX - rest;
31488
31737
  taps.push(run);
31489
31738
  }
31490
31739
  if (reverse) taps.reverse();
@@ -31558,7 +31807,7 @@ function halftoneEnlargeTaps(src, dst) {
31558
31807
  }
31559
31808
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31560
31809
  for (let y = 0; y < h; y++) {
31561
- const yy = phaseY ? phaseY[y] : flipY ? h - 1 - y : y;
31810
+ const yy = phaseY ? phaseY[y] : y;
31562
31811
  for (let x = 0; x < w; x++) {
31563
31812
  const xx = phaseX ? phaseX[x] : x;
31564
31813
  const threshold = ditherThreshold(dither.startX, dither.startY, xx, yy);
@@ -31645,11 +31894,33 @@ function halftoneSharpen(px, w, h) {
31645
31894
  }
31646
31895
  }
31647
31896
  }
31897
+ function halftonePairDespeckle(px, w, h, rows) {
31898
+ const n = rows ? w : h;
31899
+ const at = (k, side) => (rows ? side * w + k : k * w + side) * 3;
31900
+ const same = (i, j) => px[i] === px[j] && px[i + 1] === px[j + 1] && px[i + 2] === px[j + 2];
31901
+ 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));
31902
+ const mean = Array.from({ length: n }, (_, k) => checker(k - 1) || checker(k));
31903
+ for (let k = 0; k < n; k++) {
31904
+ if (!mean[k]) continue;
31905
+ const first = at(k, 0);
31906
+ const last = at(k, 1);
31907
+ for (let c = 0; c < 3; c++) px[first + c] = px[last + c] = px[first + c] + px[last + c] + 1 >> 1;
31908
+ }
31909
+ }
31910
+ function halftoneKeepLast(px, w, h, rows) {
31911
+ const n = rows ? w : h;
31912
+ const out = new Int32Array(n * 3);
31913
+ for (let k = 0; k < n; k++) {
31914
+ const last = (rows ? w + k : k * w + 1) * 3;
31915
+ for (let c = 0; c < 3; c++) out[k * 3 + c] = px[last + c];
31916
+ }
31917
+ return out;
31918
+ }
31648
31919
  function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSampling = false, directDib = false, dither, curves) {
31649
31920
  const W = Math.max(0, Math.round(Math.abs(dw)));
31650
31921
  const H = Math.max(0, Math.round(Math.abs(dh)));
31651
- const SW = Math.round(Math.abs(sw));
31652
- const SH = Math.round(Math.abs(sh));
31922
+ let SW = Math.round(Math.abs(sw));
31923
+ let SH = Math.round(Math.abs(sh));
31653
31924
  const x0 = Math.round(Math.min(sx, sx + sw));
31654
31925
  const y0 = Math.round(Math.min(sy, sy + sh));
31655
31926
  const flipX = dw < 0 !== sw < 0;
@@ -31658,7 +31929,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31658
31929
  if (W === 0 || H === 0 || SW === 0 || SH === 0) {
31659
31930
  return { width: W, height: H, data };
31660
31931
  }
31661
- const rect = new Int32Array(SW * SH * 3);
31932
+ let rect = new Int32Array(SW * SH * 3);
31662
31933
  for (let y = 0; y < SH; y++) {
31663
31934
  const yy = y0 + y;
31664
31935
  for (let x = 0; x < SW; x++) {
@@ -31673,19 +31944,40 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31673
31944
  rect[o + 2] = src.data[i + 2];
31674
31945
  }
31675
31946
  }
31947
+ const pairRows = SH === 2 && H === 1 && W > SW && (!adjust || adjustAfterSampling || !!dither);
31948
+ const pairColumns = SW === 2 && W === 1 && H > SH && (!adjust || adjustAfterSampling || !!dither);
31949
+ if (pairRows || pairColumns) {
31950
+ if (!directDib) halftonePairDespeckle(rect, SW, SH, pairRows);
31951
+ if (pairRows && W >= 2 * SW) {
31952
+ rect = halftoneKeepLast(rect, SW, SH, true);
31953
+ SH = 1;
31954
+ } else if (pairColumns && H >= 2 * SH) {
31955
+ rect = halftoneKeepLast(rect, SW, SH, false);
31956
+ SW = 1;
31957
+ }
31958
+ }
31676
31959
  const enlarging = W >= SW && H >= SH && (W > SW || H > SH);
31677
31960
  const reducing = W < SW && H < SH;
31678
31961
  const mixed = W > SW && H < SH || W < SW && H > SH;
31679
31962
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31680
31963
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31681
31964
  const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31682
- if ((enlarging || nearestMixed) && !directDib) {
31965
+ if ((enlarging || nearestMixed) && !directDib && !pairRows && !pairColumns) {
31683
31966
  halftoneDespeckle(rect, SW, SH);
31684
31967
  }
31685
31968
  const reducedEven = W < SW ? SW % 2 === 0 && H % SH === 0 : SH % 2 === 0 && W % SW === 0;
31686
31969
  const ditherAtSource = dithered && (W >= SW && H >= SH || nativeMixed && reducedEven);
31687
31970
  if (ditherAtSource) {
31688
- ditherPixels(rect, SW, SH, dither, false, flipY);
31971
+ ditherPixels(
31972
+ rect,
31973
+ SW,
31974
+ SH,
31975
+ dither,
31976
+ false,
31977
+ false,
31978
+ void 0,
31979
+ flipY ? Int32Array.from({ length: SH }, (_, j) => H - 1 - j) : void 0
31980
+ );
31689
31981
  }
31690
31982
  if (adjust && !adjustAfterSampling && (!dithered || ditherAtSource)) {
31691
31983
  adjust(rect);
@@ -31801,17 +32093,13 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31801
32093
  }
31802
32094
  }
31803
32095
  if (dithered && !ditherAtSource) {
31804
- const phase = (n, src2, dst, selectSource) => Int32Array.from({ length: n }, (_, i) => dst > src2 ? Math.floor(i * src2 / dst) : selectSource ? Math.max(0, Math.min(src2 - 1, Math.floor(((i + 1) * 2 * src2 - dst) / (2 * dst)))) : i);
31805
- ditherPixels(
31806
- out,
31807
- W,
31808
- H,
31809
- dither,
31810
- false,
31811
- false,
31812
- nearestMixed ? phase(W, SW, W, true) : void 0,
31813
- nearestMixed ? phase(H, SH, H, false) : void 0
31814
- );
32096
+ 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);
32097
+ const sourcePhase = nearestMixed && W * H > SW * SH;
32098
+ let phaseX = sourcePhase ? phase(W, SW, W, true) : void 0;
32099
+ let phaseY = sourcePhase ? phase(H, SH, H, false) : void 0;
32100
+ if (phaseX && flipX) phaseX = phaseX.slice().reverse();
32101
+ if (phaseY && flipY && H > SH) phaseY = phaseY.slice().reverse().map((v) => H - 1 - v);
32102
+ ditherPixels(out, W, H, dither, false, false, phaseX, phaseY);
31815
32103
  adjust(out);
31816
32104
  }
31817
32105
  if (adjust && adjustAfterSampling) adjust(out);
@@ -31874,7 +32162,7 @@ function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode, brushOrg
31874
32162
  canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
31875
32163
  target.restore();
31876
32164
  }
31877
- function decodeSource(rCtx, req) {
32165
+ function decodeSource(rCtx, req, rotated = false) {
31878
32166
  if (!req.source) {
31879
32167
  return null;
31880
32168
  }
@@ -31889,6 +32177,9 @@ function decodeSource(rCtx, req) {
31889
32177
  return null;
31890
32178
  }
31891
32179
  const ca = rCtx.state.colorAdjustment;
32180
+ if (req.stretch && rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32181
+ rCtx.halftoneRotatedAdjusts = !rotated && !!ca;
32182
+ }
31892
32183
  const halftone = req.stretch && rCtx.state.stretchBltMode === HALFTONE && ca;
31893
32184
  const channelOnly = isChannelOnlyColorAdjustment(ca);
31894
32185
  const split = halftone && !channelOnly ? splitColorAdjustment(ca) : void 0;
@@ -32036,6 +32327,7 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32036
32327
  let src = null;
32037
32328
  let srcX = sx;
32038
32329
  let srcY = sy;
32330
+ let adjustTexel;
32039
32331
  if (operands.usesS) {
32040
32332
  const decoded = decodeSource(rCtx, {
32041
32333
  plan: { kind: "ternary", index, operands },
@@ -32050,13 +32342,30 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32050
32342
  sh,
32051
32343
  dibOrigin,
32052
32344
  stretch
32053
- });
32345
+ }, true);
32054
32346
  if (!decoded) {
32055
32347
  return;
32056
32348
  }
32057
32349
  src = decoded.decoded.pixels;
32058
- if (decoded.decoded.adjust) {
32059
- applyColorAdjustment(src, rCtx.state.colorAdjustment);
32350
+ if (decoded.decoded.adjust && rCtx.halftoneRotatedAdjusts) {
32351
+ const { adjust, adjustAfterSampling, curves } = decoded.decoded;
32352
+ const startX = ditherStartX(0, rCtx.state.brushOrgX);
32353
+ const startY = ditherStartY(0, rCtx.state.brushOrgY);
32354
+ const triple = new Int32Array(3);
32355
+ adjustTexel = (rgb2, x, y) => {
32356
+ triple[0] = rgb2 >> 16 & 255;
32357
+ triple[1] = rgb2 >> 8 & 255;
32358
+ triple[2] = rgb2 & 255;
32359
+ if (!adjustAfterSampling) {
32360
+ const t = ditherThreshold(startX, startY, x, y);
32361
+ for (let c = 0; c < 3; c++) {
32362
+ triple[c] = ditherQuantize(triple[c], t);
32363
+ }
32364
+ }
32365
+ adjust(triple);
32366
+ curves?.(triple);
32367
+ return triple[0] << 16 | triple[1] << 8 | triple[2];
32368
+ };
32060
32369
  }
32061
32370
  srcX = decoded.req.sx;
32062
32371
  srcY = decoded.req.sy;
@@ -32080,7 +32389,10 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
32080
32389
  sw,
32081
32390
  sh,
32082
32391
  (ix, iy, x, y, d) => {
32083
- const s = operands.usesS ? texel(ix, iy) : 0;
32392
+ let s = operands.usesS ? texel(ix, iy) : 0;
32393
+ if (adjustTexel && operands.usesS) {
32394
+ s = adjustTexel(s, x, y);
32395
+ }
32084
32396
  const p = typeof pattern === "function" ? pattern(x, y) : pattern;
32085
32397
  return evalRop3(index, p, s, d);
32086
32398
  }
@@ -32135,6 +32447,19 @@ function alignMirroredDest(rCtx, req) {
32135
32447
  dy: req.dh < 0 ? req.dy + pxY : req.dy
32136
32448
  };
32137
32449
  }
32450
+ function executePlgBltAsStretch(rCtx, dx, dy, dw, dh, source, r) {
32451
+ executeBlit(rCtx, {
32452
+ plan: classifyRop3(13369376),
32453
+ dx,
32454
+ dy,
32455
+ dw,
32456
+ dh,
32457
+ source,
32458
+ ...r,
32459
+ dibOrigin: "top-left",
32460
+ stretch: true
32461
+ });
32462
+ }
32138
32463
  function executeBlit(rCtx, request) {
32139
32464
  const { ctx } = rCtx;
32140
32465
  const req = alignMirroredDest(rCtx, request);
@@ -32637,6 +32962,18 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32637
32962
  if (b[1] === a[1] && q[0] === a[0]) {
32638
32963
  const W = Math.round(Math.abs(b[0] - a[0]) / 16);
32639
32964
  const H = Math.round(Math.abs(q[1] - a[1]) / 16);
32965
+ if (!mask && rCtx.state.stretchBltMode === HALFTONE && W > 0 && H > 0) {
32966
+ executePlgBltAsStretch(
32967
+ rCtx,
32968
+ Math.round(a[0] / 16),
32969
+ Math.round(a[1] / 16),
32970
+ b[0] < a[0] ? -W : W,
32971
+ q[1] < a[1] ? -H : H,
32972
+ 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)),
32973
+ r
32974
+ );
32975
+ return;
32976
+ }
32640
32977
  const left = Math.round(a[0] / 16) + (b[0] < a[0] ? 1 - W : 0);
32641
32978
  const top = Math.round(a[1] / 16) + (q[1] < a[1] ? 1 - H : 0);
32642
32979
  const aw = Math.abs(r.sw);
@@ -32657,6 +32994,9 @@ function handlePlgBlt(rCtx, offset, dataOff, recSize) {
32657
32994
  });
32658
32995
  return;
32659
32996
  }
32997
+ if (rCtx.state.stretchBltMode === HALFTONE && rCtx.halftoneRotatedAdjusts === void 0) {
32998
+ rCtx.halftoneRotatedAdjusts = false;
32999
+ }
32660
33000
  paintParallelogram(rCtx, a, b, q, r.sw, r.sh, (ix, iy) => texel(ix, iy));
32661
33001
  }
32662
33002
  function readBand(rCtx, offset, offBmi, cbBmi, offBits, cbBits, usage, scans) {
@@ -34785,6 +35125,7 @@ function flattenBracketPath(rCtx) {
34785
35125
  }
34786
35126
  for (const f of rCtx.rasterPath.figures) {
34787
35127
  delete f.tangents;
35128
+ delete f.curveSegs;
34788
35129
  delete f.roundWiden;
34789
35130
  }
34790
35131
  syncPathCmds(rCtx);
@@ -38876,7 +39217,7 @@ function wmfRectangle(p, l, t, r, b) {
38876
39217
  axisRect: { interior: rectInterior(p, box) }
38877
39218
  });
38878
39219
  }
38879
- function compatCorner(p, w, h, box) {
39220
+ function compatCorner(p, w, h, box, logical) {
38880
39221
  const m = gdiDeviceMatrix(p.rCtx);
38881
39222
  const cw = Math.round(Math.abs(w * m[0]) * 16);
38882
39223
  const ch = Math.round(Math.abs(h * m[3]) * 16);
@@ -38884,24 +39225,21 @@ function compatCorner(p, w, h, box) {
38884
39225
  const uy = 16 * p.ky;
38885
39226
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38886
39227
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38887
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38888
- return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
39228
+ const width2 = box.x1 - box.x0, height2 = box.y1 - box.y0;
39229
+ return [Math.floor(Math.min(cw, width2 + penWidth) * width2 / (width2 + penWidth)), Math.floor(Math.min(ch, height2 + penWidth) * height2 / (height2 + penWidth))];
38889
39230
  }
38890
- if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38891
- const width = box.x1 - box.x0, height = box.y1 - box.y0;
38892
- return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
38893
- }
38894
- if (!penIsNull(p) && !penIsCosmetic(p.rCtx)) {
38895
- return [Math.round(Math.floor(cw / (2 * ux)) * 2 * ux), Math.round(Math.floor(ch / (2 * uy)) * 2 * uy)];
38896
- }
38897
- return [Math.round(Math.floor(Math.round(cw / ux) / 2) * 2 * ux), Math.round(Math.floor(Math.round(ch / uy) / 2) * 2 * uy)];
39231
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
39232
+ const fullW = Math.abs((logical[2] - logical[0]) * m[0]) * 16 || width + ux;
39233
+ const fullH = Math.abs((logical[3] - logical[1]) * m[3]) * 16 || height + uy;
39234
+ const scaled2 = [Math.min(Math.abs(w * m[0]) * 16, fullW) * width / fullW, Math.min(Math.abs(h * m[3]) * 16, fullH) * height / fullH];
39235
+ return penIsCosmetic(p.rCtx) && !penIsNull(p) ? scaled2 : [Math.floor(scaled2[0]), Math.floor(scaled2[1])];
38898
39236
  }
38899
39237
  function wmfRoundRect(p, l, t, r, b, w, h) {
38900
39238
  const box = compatBox(p, l, t, r, b, { curved: true });
38901
39239
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38902
39240
  return;
38903
39241
  }
38904
- const [cw, ch] = compatCorner(p, w, h, box);
39242
+ const [cw, ch] = compatCorner(p, w, h, box, [l, t, r, b]);
38905
39243
  const cr = canvasRect(box);
38906
39244
  paintGdiShape(p.rCtx, {
38907
39245
  build: (c) => {
@@ -38954,10 +39292,14 @@ function compatArc(p, kind, l, t, r, b, xs, ys, xe, ye) {
38954
39292
  if (box.x1 < box.x0 || box.y1 < box.y0) {
38955
39293
  return null;
38956
39294
  }
38957
- const raw = compatBox(p, l, t, r, b, { exclusive: false });
39295
+ const m = gdiDeviceMatrix(p.rCtx);
39296
+ const deviceFix = (x, y) => [(m[0] * x + m[2] * y + m[4]) * 16, (m[1] * x + m[3] * y + m[5]) * 16];
39297
+ const [ax, ay] = deviceFix(l, t);
39298
+ const [bx, by] = deviceFix(r, b);
39299
+ const raw = { x0: Math.min(ax, bx), x1: Math.max(ax, bx), y0: Math.min(ay, by), y1: Math.max(ay, by) };
38958
39300
  const onBox = (v, r0, r1, b0, b1) => (b0 + b1) / 2 + (r1 !== r0 ? (v - (r0 + r1) / 2) * (b1 - b0) / (r1 - r0) : v - (r0 + r1) / 2);
38959
39301
  const radial = (x, y) => {
38960
- const [fx, fy] = fixPoint(p.rCtx, x, y);
39302
+ const [fx, fy] = deviceFix(x, y);
38961
39303
  return [onBox(fx, raw.x0, raw.x1, box.x0, box.x1), onBox(fy, raw.y0, raw.y1, box.y0, box.y1)];
38962
39304
  };
38963
39305
  return { box, s: radial(xs, ys), e: radial(xe, ye) };