emf-converter 4.4.6 → 4.4.8

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
@@ -11333,7 +11333,7 @@ var GdiRasterPath = class {
11333
11333
  /** Appends every figure of `other`. */
11334
11334
  append(other) {
11335
11335
  for (const f of other.figures) {
11336
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents) });
11336
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
11337
11337
  }
11338
11338
  for (let i = 0; i < other.getPath.types.length; i++) {
11339
11339
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -15763,11 +15763,11 @@ function mirror(p, n) {
15763
15763
  }
15764
15764
  return p;
15765
15765
  }
15766
- function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15766
+ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, fullVertical = false, edgeWidth = w, edgeHeight = h) {
15767
15767
  const k = blurKernel(radius);
15768
15768
  const taps = (k.length - 1) / 2;
15769
- const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15770
- const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15769
+ const horizontalIndex = taps >= edgeWidth ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15770
+ const verticalIndex = taps >= edgeHeight ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15771
15771
  const out = new Float64Array(w * h * 4);
15772
15772
  for (let y = 0; y < h; y++) {
15773
15773
  const row = y * w * 4;
@@ -15793,7 +15793,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15793
15793
  }
15794
15794
  if (taps > 0 && vRow >= 0 && vRow < h) {
15795
15795
  const horizontal = out.slice();
15796
- const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15796
+ const endRow = fullVertical ? h : Math.min(h, vRow + Math.ceil(h / w));
15797
15797
  for (let row = vRow; row < endRow; row++) {
15798
15798
  const col = new Float64Array(w * 4);
15799
15799
  for (let j = -taps; j <= taps; j++) {
@@ -15811,14 +15811,46 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15811
15811
  }
15812
15812
  return out;
15813
15813
  }
15814
+ function blurReduction(radius) {
15815
+ return radius < 20 ? 1 : radius < 40 ? 2 : radius <= 80 ? 4 : radius < 160 ? 8 : 16;
15816
+ }
15817
+ function effectBlur(src, width, height, radius) {
15818
+ const factor = blurReduction(radius);
15819
+ if (factor === 1 || (width < 4 || height < 4) && (width % factor !== 0 || height % factor !== 0)) {
15820
+ return gdipBlur(src, width, height, radius);
15821
+ }
15822
+ const w = Math.ceil(width / factor);
15823
+ const h = Math.ceil(height / factor);
15824
+ const reduced = new Uint8ClampedArray(w * h * 4);
15825
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
15826
+ const x1 = Math.min(width, (x + 1) * factor), y1 = Math.min(height, (y + 1) * factor);
15827
+ const count = (x1 - x * factor) * (y1 - y * factor);
15828
+ for (let c = 0; c < 4; c++) {
15829
+ let sum = 0;
15830
+ for (let yy = y * factor; yy < y1; yy++) for (let xx = x * factor; xx < x1; xx++) sum += src[(yy * width + xx) * 4 + c];
15831
+ reduced[(y * w + x) * 4 + c] = Math.floor(sum / count);
15832
+ }
15833
+ }
15834
+ const filtered = gdipBlur(reduced, w, h, radius / factor, 0, false, true, Math.floor(width / factor), Math.floor(height / factor));
15835
+ for (let i = 0; i < filtered.length; i++) filtered[i] = Math.round(filtered[i]);
15836
+ const out = new Float64Array(src.length);
15837
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
15838
+ const px = clamp((x + 0.5) / factor - 0.5, 0, w - 1), py = clamp((y + 0.5) / factor - 0.5, 0, h - 1);
15839
+ const ix = Math.floor(px), iy = Math.floor(py), fx = px - ix, fy = py - iy;
15840
+ const a = (iy * w + ix) * 4, b = (iy * w + Math.min(w - 1, ix + 1)) * 4;
15841
+ const d = (Math.min(h - 1, iy + 1) * w + ix) * 4, e = (Math.min(h - 1, iy + 1) * w + Math.min(w - 1, ix + 1)) * 4;
15842
+ for (let c = 0; c < 4; c++) out[(y * width + x) * 4 + c] = Math.round((1 - fy) * ((1 - fx) * filtered[a + c] + fx * filtered[b + c]) + fy * ((1 - fx) * filtered[d + c] + fx * filtered[e + c]));
15843
+ }
15844
+ return out;
15845
+ }
15814
15846
  function applyBlur(src, w, h, radius) {
15815
- return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15847
+ return Uint8ClampedArray.from(effectBlur(src, w, h, radius), Math.round);
15816
15848
  }
15817
15849
  function sharpenGain(_radius, amount) {
15818
15850
  return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15819
15851
  }
15820
15852
  function applySharpen(src, w, h, radius, amount) {
15821
- const blurred = gdipBlur(src, w, h, radius);
15853
+ const blurred = effectBlur(src, w, h, radius);
15822
15854
  const gain = sharpenGain(radius, amount);
15823
15855
  const out = new Uint8ClampedArray(src);
15824
15856
  for (let i = 0; i < src.length; i += 4) {
@@ -28073,7 +28105,8 @@ var Outliner = class {
28073
28105
  this.pen = penPolygon(opts.width);
28074
28106
  this.n = this.pen.length;
28075
28107
  this.rr = opts.cap === "round" && opts.join === "round";
28076
- this.roundJoinSides = opts.join === "round" && opts.cap !== "flat";
28108
+ this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28109
+ this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28077
28110
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28078
28111
  }
28079
28112
  /**
@@ -28086,7 +28119,8 @@ var Outliner = class {
28086
28119
  const dy = dir ? dir[1] : b[1] - a[1];
28087
28120
  const d = drawDir ?? [dx, dy];
28088
28121
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28089
- return { dx, dy, L, R, v: flatVector(this.opts.width, dx, dy), e: squareExtension(this.opts.width, dx, dy) };
28122
+ const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28123
+ return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28090
28124
  }
28091
28125
  push(p, v) {
28092
28126
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28205,12 +28239,17 @@ var Outliner = class {
28205
28239
  * segment).
28206
28240
  */
28207
28241
  join(p, a, b, side, outer) {
28208
- const { join, cap, width, miterLimit } = this.opts;
28209
- const sa = this.joinSide(a, side);
28210
- const sb = this.joinSide(b, side);
28242
+ const { cap, width, miterLimit } = this.opts;
28243
+ const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28244
+ let sa = this.joinSide(a, side);
28245
+ let sb = this.joinSide(b, side);
28246
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28247
+ sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28248
+ sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
28249
+ }
28211
28250
  const Da = side === "R" ? a.R : a.L;
28212
28251
  const Db = side === "R" ? b.R : b.L;
28213
- if (this.roundJoinSides && Da === Db) {
28252
+ if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
28214
28253
  this.push(p, sa);
28215
28254
  return;
28216
28255
  }
@@ -28234,7 +28273,12 @@ var Outliner = class {
28234
28273
  }
28235
28274
  } else {
28236
28275
  this.pts.push([p[0], p[1]]);
28237
- if (join === "round" && cap === "flat") {
28276
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
28277
+ this.push(p, sb);
28278
+ this.push(p, sa);
28279
+ this.pts.push([p[0], p[1]]);
28280
+ }
28281
+ if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28238
28282
  this.push(p, sb);
28239
28283
  this.wedge(p, sb, sa, false, false);
28240
28284
  this.push(p, sa);
@@ -28404,9 +28448,9 @@ function dashPieces(P, tangents, pattern, shorten) {
28404
28448
  }
28405
28449
  function widenPath(path, opts) {
28406
28450
  const out = [];
28407
- const outliner = new Outliner(opts, out);
28408
28451
  const dashed = !!opts.dashes && opts.dashes.length > 0;
28409
28452
  for (const fig of path.figures) {
28453
+ const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
28410
28454
  let P = [];
28411
28455
  const dirs = [];
28412
28456
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
@@ -28518,6 +28562,7 @@ function ellipseRasterPath(box, clockwise = false) {
28518
28562
  const path = new GdiRasterPath();
28519
28563
  path.addBeziers(ellipseBeziersBox(box, clockwise), true);
28520
28564
  path.closeFigure();
28565
+ path.figures[0].roundWiden = true;
28521
28566
  return path;
28522
28567
  }
28523
28568
  function frameMapper(box, l, t, w, h) {
@@ -28637,7 +28682,7 @@ function penWidenOptions(rCtx, opts = {}) {
28637
28682
  const { state } = rCtx;
28638
28683
  const flags = state.penFlags ?? state.penStyle;
28639
28684
  const matrix = gdiDeviceMatrix(rCtx);
28640
- const widthPx = opts.rectangle && rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28685
+ const widthPx = rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28641
28686
  const capBits = flags & 3840;
28642
28687
  const joinBits = flags & 61440;
28643
28688
  const dashes = state.penExtended ? geometricStyle(flags, widthPx, state.penUserStyle, widthPx / (state.penWidth || 1)) : null;
@@ -33507,6 +33552,7 @@ function flattenBracketPath(rCtx) {
33507
33552
  }
33508
33553
  for (const f of rCtx.rasterPath.figures) {
33509
33554
  delete f.tangents;
33555
+ delete f.roundWiden;
33510
33556
  }
33511
33557
  syncPathCmds(rCtx);
33512
33558
  }
@@ -37534,19 +37580,12 @@ function compatBox(p, l, t, r, b, opts = {}) {
37534
37580
  if (w > 1) {
37535
37581
  const matrix = gdiDeviceMatrix(p.rCtx);
37536
37582
  const rawWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) / p.kx;
37537
- if (!opts.curved) {
37538
- box.x1 += Math.round(ux);
37539
- box.y1 += Math.round(uy);
37540
- box.x0 += Math.round(rawWidth * ux / 2);
37541
- box.y0 += Math.round(rawWidth * uy / 2);
37542
- box.x1 -= Math.round(rawWidth * ux / 2);
37543
- box.y1 -= Math.round(rawWidth * uy / 2);
37544
- return box;
37545
- }
37546
- box.x0 += Math.round(Math.floor(w / 2) * ux);
37547
- box.y0 += Math.round(Math.floor(w / 2) * uy);
37548
- box.x1 -= Math.round(Math.floor((w - 1) / 2) * ux);
37549
- box.y1 -= Math.round(Math.floor((w - 1) / 2) * uy);
37583
+ box.x1 += Math.round(ux);
37584
+ box.y1 += Math.round(uy);
37585
+ box.x0 += Math.round(rawWidth * ux / 2);
37586
+ box.y0 += Math.round(rawWidth * uy / 2);
37587
+ box.x1 -= Math.round(rawWidth * ux / 2);
37588
+ box.y1 -= Math.round(rawWidth * uy / 2);
37550
37589
  }
37551
37590
  }
37552
37591
  return box;
@@ -37610,6 +37649,11 @@ function compatCorner(p, w, h, box) {
37610
37649
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37611
37650
  const ux = 16 * p.kx;
37612
37651
  const uy = 16 * p.ky;
37652
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
37653
+ const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
37654
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37655
+ return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
37656
+ }
37613
37657
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37614
37658
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
37615
37659
  return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
package/dist/browser.mjs CHANGED
@@ -11331,7 +11331,7 @@ var GdiRasterPath = class {
11331
11331
  /** Appends every figure of `other`. */
11332
11332
  append(other) {
11333
11333
  for (const f of other.figures) {
11334
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents) });
11334
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
11335
11335
  }
11336
11336
  for (let i = 0; i < other.getPath.types.length; i++) {
11337
11337
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -15761,11 +15761,11 @@ function mirror(p, n) {
15761
15761
  }
15762
15762
  return p;
15763
15763
  }
15764
- function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15764
+ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, fullVertical = false, edgeWidth = w, edgeHeight = h) {
15765
15765
  const k = blurKernel(radius);
15766
15766
  const taps = (k.length - 1) / 2;
15767
- const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15768
- const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15767
+ const horizontalIndex = taps >= edgeWidth ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15768
+ const verticalIndex = taps >= edgeHeight ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15769
15769
  const out = new Float64Array(w * h * 4);
15770
15770
  for (let y = 0; y < h; y++) {
15771
15771
  const row = y * w * 4;
@@ -15791,7 +15791,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15791
15791
  }
15792
15792
  if (taps > 0 && vRow >= 0 && vRow < h) {
15793
15793
  const horizontal = out.slice();
15794
- const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15794
+ const endRow = fullVertical ? h : Math.min(h, vRow + Math.ceil(h / w));
15795
15795
  for (let row = vRow; row < endRow; row++) {
15796
15796
  const col = new Float64Array(w * 4);
15797
15797
  for (let j = -taps; j <= taps; j++) {
@@ -15809,14 +15809,46 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15809
15809
  }
15810
15810
  return out;
15811
15811
  }
15812
+ function blurReduction(radius) {
15813
+ return radius < 20 ? 1 : radius < 40 ? 2 : radius <= 80 ? 4 : radius < 160 ? 8 : 16;
15814
+ }
15815
+ function effectBlur(src, width, height, radius) {
15816
+ const factor = blurReduction(radius);
15817
+ if (factor === 1 || (width < 4 || height < 4) && (width % factor !== 0 || height % factor !== 0)) {
15818
+ return gdipBlur(src, width, height, radius);
15819
+ }
15820
+ const w = Math.ceil(width / factor);
15821
+ const h = Math.ceil(height / factor);
15822
+ const reduced = new Uint8ClampedArray(w * h * 4);
15823
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
15824
+ const x1 = Math.min(width, (x + 1) * factor), y1 = Math.min(height, (y + 1) * factor);
15825
+ const count = (x1 - x * factor) * (y1 - y * factor);
15826
+ for (let c = 0; c < 4; c++) {
15827
+ let sum = 0;
15828
+ for (let yy = y * factor; yy < y1; yy++) for (let xx = x * factor; xx < x1; xx++) sum += src[(yy * width + xx) * 4 + c];
15829
+ reduced[(y * w + x) * 4 + c] = Math.floor(sum / count);
15830
+ }
15831
+ }
15832
+ const filtered = gdipBlur(reduced, w, h, radius / factor, 0, false, true, Math.floor(width / factor), Math.floor(height / factor));
15833
+ for (let i = 0; i < filtered.length; i++) filtered[i] = Math.round(filtered[i]);
15834
+ const out = new Float64Array(src.length);
15835
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
15836
+ const px = clamp((x + 0.5) / factor - 0.5, 0, w - 1), py = clamp((y + 0.5) / factor - 0.5, 0, h - 1);
15837
+ const ix = Math.floor(px), iy = Math.floor(py), fx = px - ix, fy = py - iy;
15838
+ const a = (iy * w + ix) * 4, b = (iy * w + Math.min(w - 1, ix + 1)) * 4;
15839
+ const d = (Math.min(h - 1, iy + 1) * w + ix) * 4, e = (Math.min(h - 1, iy + 1) * w + Math.min(w - 1, ix + 1)) * 4;
15840
+ for (let c = 0; c < 4; c++) out[(y * width + x) * 4 + c] = Math.round((1 - fy) * ((1 - fx) * filtered[a + c] + fx * filtered[b + c]) + fy * ((1 - fx) * filtered[d + c] + fx * filtered[e + c]));
15841
+ }
15842
+ return out;
15843
+ }
15812
15844
  function applyBlur(src, w, h, radius) {
15813
- return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15845
+ return Uint8ClampedArray.from(effectBlur(src, w, h, radius), Math.round);
15814
15846
  }
15815
15847
  function sharpenGain(_radius, amount) {
15816
15848
  return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15817
15849
  }
15818
15850
  function applySharpen(src, w, h, radius, amount) {
15819
- const blurred = gdipBlur(src, w, h, radius);
15851
+ const blurred = effectBlur(src, w, h, radius);
15820
15852
  const gain = sharpenGain(radius, amount);
15821
15853
  const out = new Uint8ClampedArray(src);
15822
15854
  for (let i = 0; i < src.length; i += 4) {
@@ -28071,7 +28103,8 @@ var Outliner = class {
28071
28103
  this.pen = penPolygon(opts.width);
28072
28104
  this.n = this.pen.length;
28073
28105
  this.rr = opts.cap === "round" && opts.join === "round";
28074
- this.roundJoinSides = opts.join === "round" && opts.cap !== "flat";
28106
+ this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28107
+ this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28075
28108
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28076
28109
  }
28077
28110
  /**
@@ -28084,7 +28117,8 @@ var Outliner = class {
28084
28117
  const dy = dir ? dir[1] : b[1] - a[1];
28085
28118
  const d = drawDir ?? [dx, dy];
28086
28119
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28087
- return { dx, dy, L, R, v: flatVector(this.opts.width, dx, dy), e: squareExtension(this.opts.width, dx, dy) };
28120
+ const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28121
+ return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28088
28122
  }
28089
28123
  push(p, v) {
28090
28124
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28203,12 +28237,17 @@ var Outliner = class {
28203
28237
  * segment).
28204
28238
  */
28205
28239
  join(p, a, b, side, outer) {
28206
- const { join, cap, width, miterLimit } = this.opts;
28207
- const sa = this.joinSide(a, side);
28208
- const sb = this.joinSide(b, side);
28240
+ const { cap, width, miterLimit } = this.opts;
28241
+ const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28242
+ let sa = this.joinSide(a, side);
28243
+ let sb = this.joinSide(b, side);
28244
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28245
+ sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28246
+ sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
28247
+ }
28209
28248
  const Da = side === "R" ? a.R : a.L;
28210
28249
  const Db = side === "R" ? b.R : b.L;
28211
- if (this.roundJoinSides && Da === Db) {
28250
+ if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
28212
28251
  this.push(p, sa);
28213
28252
  return;
28214
28253
  }
@@ -28232,7 +28271,12 @@ var Outliner = class {
28232
28271
  }
28233
28272
  } else {
28234
28273
  this.pts.push([p[0], p[1]]);
28235
- if (join === "round" && cap === "flat") {
28274
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
28275
+ this.push(p, sb);
28276
+ this.push(p, sa);
28277
+ this.pts.push([p[0], p[1]]);
28278
+ }
28279
+ if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28236
28280
  this.push(p, sb);
28237
28281
  this.wedge(p, sb, sa, false, false);
28238
28282
  this.push(p, sa);
@@ -28402,9 +28446,9 @@ function dashPieces(P, tangents, pattern, shorten) {
28402
28446
  }
28403
28447
  function widenPath(path, opts) {
28404
28448
  const out = [];
28405
- const outliner = new Outliner(opts, out);
28406
28449
  const dashed = !!opts.dashes && opts.dashes.length > 0;
28407
28450
  for (const fig of path.figures) {
28451
+ const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
28408
28452
  let P = [];
28409
28453
  const dirs = [];
28410
28454
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
@@ -28516,6 +28560,7 @@ function ellipseRasterPath(box, clockwise = false) {
28516
28560
  const path = new GdiRasterPath();
28517
28561
  path.addBeziers(ellipseBeziersBox(box, clockwise), true);
28518
28562
  path.closeFigure();
28563
+ path.figures[0].roundWiden = true;
28519
28564
  return path;
28520
28565
  }
28521
28566
  function frameMapper(box, l, t, w, h) {
@@ -28635,7 +28680,7 @@ function penWidenOptions(rCtx, opts = {}) {
28635
28680
  const { state } = rCtx;
28636
28681
  const flags = state.penFlags ?? state.penStyle;
28637
28682
  const matrix = gdiDeviceMatrix(rCtx);
28638
- const widthPx = opts.rectangle && rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28683
+ const widthPx = rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28639
28684
  const capBits = flags & 3840;
28640
28685
  const joinBits = flags & 61440;
28641
28686
  const dashes = state.penExtended ? geometricStyle(flags, widthPx, state.penUserStyle, widthPx / (state.penWidth || 1)) : null;
@@ -33505,6 +33550,7 @@ function flattenBracketPath(rCtx) {
33505
33550
  }
33506
33551
  for (const f of rCtx.rasterPath.figures) {
33507
33552
  delete f.tangents;
33553
+ delete f.roundWiden;
33508
33554
  }
33509
33555
  syncPathCmds(rCtx);
33510
33556
  }
@@ -37532,19 +37578,12 @@ function compatBox(p, l, t, r, b, opts = {}) {
37532
37578
  if (w > 1) {
37533
37579
  const matrix = gdiDeviceMatrix(p.rCtx);
37534
37580
  const rawWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) / p.kx;
37535
- if (!opts.curved) {
37536
- box.x1 += Math.round(ux);
37537
- box.y1 += Math.round(uy);
37538
- box.x0 += Math.round(rawWidth * ux / 2);
37539
- box.y0 += Math.round(rawWidth * uy / 2);
37540
- box.x1 -= Math.round(rawWidth * ux / 2);
37541
- box.y1 -= Math.round(rawWidth * uy / 2);
37542
- return box;
37543
- }
37544
- box.x0 += Math.round(Math.floor(w / 2) * ux);
37545
- box.y0 += Math.round(Math.floor(w / 2) * uy);
37546
- box.x1 -= Math.round(Math.floor((w - 1) / 2) * ux);
37547
- box.y1 -= Math.round(Math.floor((w - 1) / 2) * uy);
37581
+ box.x1 += Math.round(ux);
37582
+ box.y1 += Math.round(uy);
37583
+ box.x0 += Math.round(rawWidth * ux / 2);
37584
+ box.y0 += Math.round(rawWidth * uy / 2);
37585
+ box.x1 -= Math.round(rawWidth * ux / 2);
37586
+ box.y1 -= Math.round(rawWidth * uy / 2);
37548
37587
  }
37549
37588
  }
37550
37589
  return box;
@@ -37608,6 +37647,11 @@ function compatCorner(p, w, h, box) {
37608
37647
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37609
37648
  const ux = 16 * p.kx;
37610
37649
  const uy = 16 * p.ky;
37650
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
37651
+ const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
37652
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37653
+ return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
37654
+ }
37611
37655
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37612
37656
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
37613
37657
  return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
package/dist/index.js CHANGED
@@ -11334,7 +11334,7 @@ var GdiRasterPath = class {
11334
11334
  /** Appends every figure of `other`. */
11335
11335
  append(other) {
11336
11336
  for (const f of other.figures) {
11337
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents) });
11337
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
11338
11338
  }
11339
11339
  for (let i = 0; i < other.getPath.types.length; i++) {
11340
11340
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -15764,11 +15764,11 @@ function mirror(p, n) {
15764
15764
  }
15765
15765
  return p;
15766
15766
  }
15767
- function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15767
+ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, fullVertical = false, edgeWidth = w, edgeHeight = h) {
15768
15768
  const k = blurKernel(radius);
15769
15769
  const taps = (k.length - 1) / 2;
15770
- const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15771
- const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15770
+ const horizontalIndex = taps >= edgeWidth ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15771
+ const verticalIndex = taps >= edgeHeight ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15772
15772
  const out = new Float64Array(w * h * 4);
15773
15773
  for (let y = 0; y < h; y++) {
15774
15774
  const row = y * w * 4;
@@ -15794,7 +15794,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15794
15794
  }
15795
15795
  if (taps > 0 && vRow >= 0 && vRow < h) {
15796
15796
  const horizontal = out.slice();
15797
- const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15797
+ const endRow = fullVertical ? h : Math.min(h, vRow + Math.ceil(h / w));
15798
15798
  for (let row = vRow; row < endRow; row++) {
15799
15799
  const col = new Float64Array(w * 4);
15800
15800
  for (let j = -taps; j <= taps; j++) {
@@ -15812,14 +15812,46 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15812
15812
  }
15813
15813
  return out;
15814
15814
  }
15815
+ function blurReduction(radius) {
15816
+ return radius < 20 ? 1 : radius < 40 ? 2 : radius <= 80 ? 4 : radius < 160 ? 8 : 16;
15817
+ }
15818
+ function effectBlur(src, width, height, radius) {
15819
+ const factor = blurReduction(radius);
15820
+ if (factor === 1 || (width < 4 || height < 4) && (width % factor !== 0 || height % factor !== 0)) {
15821
+ return gdipBlur(src, width, height, radius);
15822
+ }
15823
+ const w = Math.ceil(width / factor);
15824
+ const h = Math.ceil(height / factor);
15825
+ const reduced = new Uint8ClampedArray(w * h * 4);
15826
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
15827
+ const x1 = Math.min(width, (x + 1) * factor), y1 = Math.min(height, (y + 1) * factor);
15828
+ const count = (x1 - x * factor) * (y1 - y * factor);
15829
+ for (let c = 0; c < 4; c++) {
15830
+ let sum = 0;
15831
+ for (let yy = y * factor; yy < y1; yy++) for (let xx = x * factor; xx < x1; xx++) sum += src[(yy * width + xx) * 4 + c];
15832
+ reduced[(y * w + x) * 4 + c] = Math.floor(sum / count);
15833
+ }
15834
+ }
15835
+ const filtered = gdipBlur(reduced, w, h, radius / factor, 0, false, true, Math.floor(width / factor), Math.floor(height / factor));
15836
+ for (let i = 0; i < filtered.length; i++) filtered[i] = Math.round(filtered[i]);
15837
+ const out = new Float64Array(src.length);
15838
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
15839
+ const px = clamp((x + 0.5) / factor - 0.5, 0, w - 1), py = clamp((y + 0.5) / factor - 0.5, 0, h - 1);
15840
+ const ix = Math.floor(px), iy = Math.floor(py), fx = px - ix, fy = py - iy;
15841
+ const a = (iy * w + ix) * 4, b = (iy * w + Math.min(w - 1, ix + 1)) * 4;
15842
+ const d = (Math.min(h - 1, iy + 1) * w + ix) * 4, e = (Math.min(h - 1, iy + 1) * w + Math.min(w - 1, ix + 1)) * 4;
15843
+ for (let c = 0; c < 4; c++) out[(y * width + x) * 4 + c] = Math.round((1 - fy) * ((1 - fx) * filtered[a + c] + fx * filtered[b + c]) + fy * ((1 - fx) * filtered[d + c] + fx * filtered[e + c]));
15844
+ }
15845
+ return out;
15846
+ }
15815
15847
  function applyBlur(src, w, h, radius) {
15816
- return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15848
+ return Uint8ClampedArray.from(effectBlur(src, w, h, radius), Math.round);
15817
15849
  }
15818
15850
  function sharpenGain(_radius, amount) {
15819
15851
  return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15820
15852
  }
15821
15853
  function applySharpen(src, w, h, radius, amount) {
15822
- const blurred = gdipBlur(src, w, h, radius);
15854
+ const blurred = effectBlur(src, w, h, radius);
15823
15855
  const gain = sharpenGain(radius, amount);
15824
15856
  const out = new Uint8ClampedArray(src);
15825
15857
  for (let i = 0; i < src.length; i += 4) {
@@ -28074,7 +28106,8 @@ var Outliner = class {
28074
28106
  this.pen = penPolygon(opts.width);
28075
28107
  this.n = this.pen.length;
28076
28108
  this.rr = opts.cap === "round" && opts.join === "round";
28077
- this.roundJoinSides = opts.join === "round" && opts.cap !== "flat";
28109
+ this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28110
+ this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28078
28111
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28079
28112
  }
28080
28113
  /**
@@ -28087,7 +28120,8 @@ var Outliner = class {
28087
28120
  const dy = dir ? dir[1] : b[1] - a[1];
28088
28121
  const d = drawDir ?? [dx, dy];
28089
28122
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28090
- return { dx, dy, L, R, v: flatVector(this.opts.width, dx, dy), e: squareExtension(this.opts.width, dx, dy) };
28123
+ const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28124
+ return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28091
28125
  }
28092
28126
  push(p, v) {
28093
28127
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28206,12 +28240,17 @@ var Outliner = class {
28206
28240
  * segment).
28207
28241
  */
28208
28242
  join(p, a, b, side, outer) {
28209
- const { join, cap, width, miterLimit } = this.opts;
28210
- const sa = this.joinSide(a, side);
28211
- const sb = this.joinSide(b, side);
28243
+ const { cap, width, miterLimit } = this.opts;
28244
+ const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28245
+ let sa = this.joinSide(a, side);
28246
+ let sb = this.joinSide(b, side);
28247
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28248
+ sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28249
+ sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
28250
+ }
28212
28251
  const Da = side === "R" ? a.R : a.L;
28213
28252
  const Db = side === "R" ? b.R : b.L;
28214
- if (this.roundJoinSides && Da === Db) {
28253
+ if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
28215
28254
  this.push(p, sa);
28216
28255
  return;
28217
28256
  }
@@ -28235,7 +28274,12 @@ var Outliner = class {
28235
28274
  }
28236
28275
  } else {
28237
28276
  this.pts.push([p[0], p[1]]);
28238
- if (join === "round" && cap === "flat") {
28277
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
28278
+ this.push(p, sb);
28279
+ this.push(p, sa);
28280
+ this.pts.push([p[0], p[1]]);
28281
+ }
28282
+ if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28239
28283
  this.push(p, sb);
28240
28284
  this.wedge(p, sb, sa, false, false);
28241
28285
  this.push(p, sa);
@@ -28405,9 +28449,9 @@ function dashPieces(P, tangents, pattern, shorten) {
28405
28449
  }
28406
28450
  function widenPath(path, opts) {
28407
28451
  const out = [];
28408
- const outliner = new Outliner(opts, out);
28409
28452
  const dashed = !!opts.dashes && opts.dashes.length > 0;
28410
28453
  for (const fig of path.figures) {
28454
+ const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
28411
28455
  let P = [];
28412
28456
  const dirs = [];
28413
28457
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
@@ -28519,6 +28563,7 @@ function ellipseRasterPath(box, clockwise = false) {
28519
28563
  const path = new GdiRasterPath();
28520
28564
  path.addBeziers(ellipseBeziersBox(box, clockwise), true);
28521
28565
  path.closeFigure();
28566
+ path.figures[0].roundWiden = true;
28522
28567
  return path;
28523
28568
  }
28524
28569
  function frameMapper(box, l, t, w, h) {
@@ -28638,7 +28683,7 @@ function penWidenOptions(rCtx, opts = {}) {
28638
28683
  const { state } = rCtx;
28639
28684
  const flags = state.penFlags ?? state.penStyle;
28640
28685
  const matrix = gdiDeviceMatrix(rCtx);
28641
- const widthPx = opts.rectangle && rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28686
+ const widthPx = rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28642
28687
  const capBits = flags & 3840;
28643
28688
  const joinBits = flags & 61440;
28644
28689
  const dashes = state.penExtended ? geometricStyle(flags, widthPx, state.penUserStyle, widthPx / (state.penWidth || 1)) : null;
@@ -33508,6 +33553,7 @@ function flattenBracketPath(rCtx) {
33508
33553
  }
33509
33554
  for (const f of rCtx.rasterPath.figures) {
33510
33555
  delete f.tangents;
33556
+ delete f.roundWiden;
33511
33557
  }
33512
33558
  syncPathCmds(rCtx);
33513
33559
  }
@@ -37535,19 +37581,12 @@ function compatBox(p, l, t, r, b, opts = {}) {
37535
37581
  if (w > 1) {
37536
37582
  const matrix = gdiDeviceMatrix(p.rCtx);
37537
37583
  const rawWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) / p.kx;
37538
- if (!opts.curved) {
37539
- box.x1 += Math.round(ux);
37540
- box.y1 += Math.round(uy);
37541
- box.x0 += Math.round(rawWidth * ux / 2);
37542
- box.y0 += Math.round(rawWidth * uy / 2);
37543
- box.x1 -= Math.round(rawWidth * ux / 2);
37544
- box.y1 -= Math.round(rawWidth * uy / 2);
37545
- return box;
37546
- }
37547
- box.x0 += Math.round(Math.floor(w / 2) * ux);
37548
- box.y0 += Math.round(Math.floor(w / 2) * uy);
37549
- box.x1 -= Math.round(Math.floor((w - 1) / 2) * ux);
37550
- box.y1 -= Math.round(Math.floor((w - 1) / 2) * uy);
37584
+ box.x1 += Math.round(ux);
37585
+ box.y1 += Math.round(uy);
37586
+ box.x0 += Math.round(rawWidth * ux / 2);
37587
+ box.y0 += Math.round(rawWidth * uy / 2);
37588
+ box.x1 -= Math.round(rawWidth * ux / 2);
37589
+ box.y1 -= Math.round(rawWidth * uy / 2);
37551
37590
  }
37552
37591
  }
37553
37592
  return box;
@@ -37611,6 +37650,11 @@ function compatCorner(p, w, h, box) {
37611
37650
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37612
37651
  const ux = 16 * p.kx;
37613
37652
  const uy = 16 * p.ky;
37653
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
37654
+ const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
37655
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37656
+ return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
37657
+ }
37614
37658
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37615
37659
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
37616
37660
  return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
package/dist/index.mjs CHANGED
@@ -11332,7 +11332,7 @@ var GdiRasterPath = class {
11332
11332
  /** Appends every figure of `other`. */
11333
11333
  append(other) {
11334
11334
  for (const f of other.figures) {
11335
- this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents) });
11335
+ this.figures.push({ pts: f.pts.slice(), closed: f.closed, tangents: f.tangents && new Map(f.tangents), roundWiden: f.roundWiden });
11336
11336
  }
11337
11337
  for (let i = 0; i < other.getPath.types.length; i++) {
11338
11338
  this.log(other.getPath.pts[2 * i], other.getPath.pts[2 * i + 1], other.getPath.types[i]);
@@ -15762,11 +15762,11 @@ function mirror(p, n) {
15762
15762
  }
15763
15763
  return p;
15764
15764
  }
15765
- function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15765
+ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false, fullVertical = false, edgeWidth = w, edgeHeight = h) {
15766
15766
  const k = blurKernel(radius);
15767
15767
  const taps = (k.length - 1) / 2;
15768
- const horizontalIndex = radius <= 16 && radius >= w ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15769
- const verticalIndex = radius <= 16 && radius >= h ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15768
+ const horizontalIndex = taps >= edgeWidth ? (x) => Math.max(0, Math.min(w - 1, x)) : (x) => mirror(x, w);
15769
+ const verticalIndex = taps >= edgeHeight ? (y) => Math.max(0, Math.min(h - 1, y)) : (y) => mirror(y, h);
15770
15770
  const out = new Float64Array(w * h * 4);
15771
15771
  for (let y = 0; y < h; y++) {
15772
15772
  const row = y * w * 4;
@@ -15792,7 +15792,7 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15792
15792
  }
15793
15793
  if (taps > 0 && vRow >= 0 && vRow < h) {
15794
15794
  const horizontal = out.slice();
15795
- const endRow = Math.min(h, vRow + (radius <= 16 ? Math.ceil(h / w) : 1));
15795
+ const endRow = fullVertical ? h : Math.min(h, vRow + Math.ceil(h / w));
15796
15796
  for (let row = vRow; row < endRow; row++) {
15797
15797
  const col = new Float64Array(w * 4);
15798
15798
  for (let j = -taps; j <= taps; j++) {
@@ -15810,14 +15810,46 @@ function gdipBlur(src, w, h, radius, vRow = 0, vZeroAbove = false) {
15810
15810
  }
15811
15811
  return out;
15812
15812
  }
15813
+ function blurReduction(radius) {
15814
+ return radius < 20 ? 1 : radius < 40 ? 2 : radius <= 80 ? 4 : radius < 160 ? 8 : 16;
15815
+ }
15816
+ function effectBlur(src, width, height, radius) {
15817
+ const factor = blurReduction(radius);
15818
+ if (factor === 1 || (width < 4 || height < 4) && (width % factor !== 0 || height % factor !== 0)) {
15819
+ return gdipBlur(src, width, height, radius);
15820
+ }
15821
+ const w = Math.ceil(width / factor);
15822
+ const h = Math.ceil(height / factor);
15823
+ const reduced = new Uint8ClampedArray(w * h * 4);
15824
+ for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
15825
+ const x1 = Math.min(width, (x + 1) * factor), y1 = Math.min(height, (y + 1) * factor);
15826
+ const count = (x1 - x * factor) * (y1 - y * factor);
15827
+ for (let c = 0; c < 4; c++) {
15828
+ let sum = 0;
15829
+ for (let yy = y * factor; yy < y1; yy++) for (let xx = x * factor; xx < x1; xx++) sum += src[(yy * width + xx) * 4 + c];
15830
+ reduced[(y * w + x) * 4 + c] = Math.floor(sum / count);
15831
+ }
15832
+ }
15833
+ const filtered = gdipBlur(reduced, w, h, radius / factor, 0, false, true, Math.floor(width / factor), Math.floor(height / factor));
15834
+ for (let i = 0; i < filtered.length; i++) filtered[i] = Math.round(filtered[i]);
15835
+ const out = new Float64Array(src.length);
15836
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
15837
+ const px = clamp((x + 0.5) / factor - 0.5, 0, w - 1), py = clamp((y + 0.5) / factor - 0.5, 0, h - 1);
15838
+ const ix = Math.floor(px), iy = Math.floor(py), fx = px - ix, fy = py - iy;
15839
+ const a = (iy * w + ix) * 4, b = (iy * w + Math.min(w - 1, ix + 1)) * 4;
15840
+ const d = (Math.min(h - 1, iy + 1) * w + ix) * 4, e = (Math.min(h - 1, iy + 1) * w + Math.min(w - 1, ix + 1)) * 4;
15841
+ for (let c = 0; c < 4; c++) out[(y * width + x) * 4 + c] = Math.round((1 - fy) * ((1 - fx) * filtered[a + c] + fx * filtered[b + c]) + fy * ((1 - fx) * filtered[d + c] + fx * filtered[e + c]));
15842
+ }
15843
+ return out;
15844
+ }
15813
15845
  function applyBlur(src, w, h, radius) {
15814
- return Uint8ClampedArray.from(gdipBlur(src, w, h, radius), Math.round);
15846
+ return Uint8ClampedArray.from(effectBlur(src, w, h, radius), Math.round);
15815
15847
  }
15816
15848
  function sharpenGain(_radius, amount) {
15817
15849
  return roundHalfDown(64 * amount / (250 - 2 * amount)) / 64;
15818
15850
  }
15819
15851
  function applySharpen(src, w, h, radius, amount) {
15820
- const blurred = gdipBlur(src, w, h, radius);
15852
+ const blurred = effectBlur(src, w, h, radius);
15821
15853
  const gain = sharpenGain(radius, amount);
15822
15854
  const out = new Uint8ClampedArray(src);
15823
15855
  for (let i = 0; i < src.length; i += 4) {
@@ -28072,7 +28104,8 @@ var Outliner = class {
28072
28104
  this.pen = penPolygon(opts.width);
28073
28105
  this.n = this.pen.length;
28074
28106
  this.rr = opts.cap === "round" && opts.join === "round";
28075
- this.roundJoinSides = opts.join === "round" && opts.cap !== "flat";
28107
+ this.originalRoundJoinSides = opts.join === "round" && opts.cap !== "flat";
28108
+ this.roundJoinSides = !!opts.roundCurveJoins || this.originalRoundJoinSides;
28076
28109
  this.maxX = Math.max(...this.pen.map((q) => Math.abs(q[0])));
28077
28110
  }
28078
28111
  /**
@@ -28085,7 +28118,8 @@ var Outliner = class {
28085
28118
  const dy = dir ? dir[1] : b[1] - a[1];
28086
28119
  const d = drawDir ?? [dx, dy];
28087
28120
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
28088
- return { dx, dy, L, R, v: flatVector(this.opts.width, dx, dy), e: squareExtension(this.opts.width, dx, dy) };
28121
+ const perpendicular = this.opts.roundCurveJoins && drawDir ? drawDir : [dx, dy];
28122
+ return { dx, dy, L, R, v: flatVector(this.opts.width, perpendicular[0], perpendicular[1]), e: squareExtension(this.opts.width, dx, dy), curveEnd: !!drawDir };
28089
28123
  }
28090
28124
  push(p, v) {
28091
28125
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -28204,12 +28238,17 @@ var Outliner = class {
28204
28238
  * segment).
28205
28239
  */
28206
28240
  join(p, a, b, side, outer) {
28207
- const { join, cap, width, miterLimit } = this.opts;
28208
- const sa = this.joinSide(a, side);
28209
- const sb = this.joinSide(b, side);
28241
+ const { cap, width, miterLimit } = this.opts;
28242
+ const join = this.opts.roundCurveJoins ? "round" : this.opts.join;
28243
+ let sa = this.joinSide(a, side);
28244
+ let sb = this.joinSide(b, side);
28245
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides && a.curveEnd && b.curveEnd) {
28246
+ sa = side === "R" ? a.v : [-a.v[0], -a.v[1]];
28247
+ sb = side === "R" ? b.v : [-b.v[0], -b.v[1]];
28248
+ }
28210
28249
  const Da = side === "R" ? a.R : a.L;
28211
28250
  const Db = side === "R" ? b.R : b.L;
28212
- if (this.roundJoinSides && Da === Db) {
28251
+ if (this.roundJoinSides && (Da === Db || this.opts.roundCurveJoins && sa[0] === sb[0] && sa[1] === sb[1])) {
28213
28252
  this.push(p, sa);
28214
28253
  return;
28215
28254
  }
@@ -28233,7 +28272,12 @@ var Outliner = class {
28233
28272
  }
28234
28273
  } else {
28235
28274
  this.pts.push([p[0], p[1]]);
28236
- if (join === "round" && cap === "flat") {
28275
+ if (this.opts.roundCurveJoins && !this.originalRoundJoinSides) {
28276
+ this.push(p, sb);
28277
+ this.push(p, sa);
28278
+ this.pts.push([p[0], p[1]]);
28279
+ }
28280
+ if (join === "round" && cap === "flat" && !this.opts.roundCurveJoins) {
28237
28281
  this.push(p, sb);
28238
28282
  this.wedge(p, sb, sa, false, false);
28239
28283
  this.push(p, sa);
@@ -28403,9 +28447,9 @@ function dashPieces(P, tangents, pattern, shorten) {
28403
28447
  }
28404
28448
  function widenPath(path, opts) {
28405
28449
  const out = [];
28406
- const outliner = new Outliner(opts, out);
28407
28450
  const dashed = !!opts.dashes && opts.dashes.length > 0;
28408
28451
  for (const fig of path.figures) {
28452
+ const outliner = new Outliner(fig.roundWiden ? { ...opts, roundCurveJoins: true } : opts, out);
28409
28453
  let P = [];
28410
28454
  const dirs = [];
28411
28455
  for (let i = 0; i + 1 < fig.pts.length; i += 2) {
@@ -28517,6 +28561,7 @@ function ellipseRasterPath(box, clockwise = false) {
28517
28561
  const path = new GdiRasterPath();
28518
28562
  path.addBeziers(ellipseBeziersBox(box, clockwise), true);
28519
28563
  path.closeFigure();
28564
+ path.figures[0].roundWiden = true;
28520
28565
  return path;
28521
28566
  }
28522
28567
  function frameMapper(box, l, t, w, h) {
@@ -28636,7 +28681,7 @@ function penWidenOptions(rCtx, opts = {}) {
28636
28681
  const { state } = rCtx;
28637
28682
  const flags = state.penFlags ?? state.penStyle;
28638
28683
  const matrix = gdiDeviceMatrix(rCtx);
28639
- const widthPx = opts.rectangle && rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28684
+ const widthPx = rCtx.wholeDevicePixels && (flags & 15) === 6 ? state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) : penDeviceWidth(rCtx);
28640
28685
  const capBits = flags & 3840;
28641
28686
  const joinBits = flags & 61440;
28642
28687
  const dashes = state.penExtended ? geometricStyle(flags, widthPx, state.penUserStyle, widthPx / (state.penWidth || 1)) : null;
@@ -33506,6 +33551,7 @@ function flattenBracketPath(rCtx) {
33506
33551
  }
33507
33552
  for (const f of rCtx.rasterPath.figures) {
33508
33553
  delete f.tangents;
33554
+ delete f.roundWiden;
33509
33555
  }
33510
33556
  syncPathCmds(rCtx);
33511
33557
  }
@@ -37533,19 +37579,12 @@ function compatBox(p, l, t, r, b, opts = {}) {
37533
37579
  if (w > 1) {
37534
37580
  const matrix = gdiDeviceMatrix(p.rCtx);
37535
37581
  const rawWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(matrix[0] * matrix[3] - matrix[1] * matrix[2])) / p.kx;
37536
- if (!opts.curved) {
37537
- box.x1 += Math.round(ux);
37538
- box.y1 += Math.round(uy);
37539
- box.x0 += Math.round(rawWidth * ux / 2);
37540
- box.y0 += Math.round(rawWidth * uy / 2);
37541
- box.x1 -= Math.round(rawWidth * ux / 2);
37542
- box.y1 -= Math.round(rawWidth * uy / 2);
37543
- return box;
37544
- }
37545
- box.x0 += Math.round(Math.floor(w / 2) * ux);
37546
- box.y0 += Math.round(Math.floor(w / 2) * uy);
37547
- box.x1 -= Math.round(Math.floor((w - 1) / 2) * ux);
37548
- box.y1 -= Math.round(Math.floor((w - 1) / 2) * uy);
37582
+ box.x1 += Math.round(ux);
37583
+ box.y1 += Math.round(uy);
37584
+ box.x0 += Math.round(rawWidth * ux / 2);
37585
+ box.y0 += Math.round(rawWidth * uy / 2);
37586
+ box.x1 -= Math.round(rawWidth * ux / 2);
37587
+ box.y1 -= Math.round(rawWidth * uy / 2);
37549
37588
  }
37550
37589
  }
37551
37590
  return box;
@@ -37609,6 +37648,11 @@ function compatCorner(p, w, h, box) {
37609
37648
  const ch = Math.round(Math.abs(h * m[3]) * 16);
37610
37649
  const ux = 16 * p.kx;
37611
37650
  const uy = 16 * p.ky;
37651
+ if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
37652
+ const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
37653
+ const width = box.x1 - box.x0, height = box.y1 - box.y0;
37654
+ return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
37655
+ }
37612
37656
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
37613
37657
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
37614
37658
  return [Math.min(cw, width + ux) * width / (width + ux), Math.min(ch, height + uy) * height / (height + uy)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.4.6",
3
+ "version": "4.4.8",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",