emf-converter 4.6.6 → 4.6.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
@@ -28827,6 +28827,20 @@ var FLAT_SECTOR_BIAS = {
28827
28827
  function sectorBias(code) {
28828
28828
  return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
28829
28829
  }
28830
+ var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28831
+ var VERTICAL_STEP_FIRST = 7;
28832
+ function verticalSideOffset(width) {
28833
+ if (width < HOBBY_LIMIT || width > 1400) {
28834
+ return void 0;
28835
+ }
28836
+ let m = VERTICAL_STEP_FIRST;
28837
+ for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28838
+ if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28839
+ m = i + VERTICAL_STEP_FIRST;
28840
+ }
28841
+ }
28842
+ return 8 * m;
28843
+ }
28830
28844
  var penCache = /* @__PURE__ */ new Map();
28831
28845
  function penPolygon(width) {
28832
28846
  const cached = penCache.get(width);
@@ -28839,7 +28853,7 @@ function penPolygon(width) {
28839
28853
  pen = HOBBY[n - 1];
28840
28854
  } else {
28841
28855
  const rx = Math.ceil(width / 2);
28842
- const ry = 8 * Math.floor((width + 9) / 16);
28856
+ const ry = rx;
28843
28857
  const f = flattenBezierPath(ellipseBeziers(-rx, -ry, rx, ry));
28844
28858
  const half = [];
28845
28859
  for (let i = 0; i + 1 < f.length; i += 2) {
@@ -28917,7 +28931,8 @@ function perpendicularVectors(width, dx0, dy0) {
28917
28931
  cy = sectorBias(pair.charCodeAt(1));
28918
28932
  }
28919
28933
  }
28920
- const vx = r(x + cx), vy = r(y + cy);
28934
+ const side = dx === 0 ? verticalSideOffset(width) : void 0;
28935
+ const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
28921
28936
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28922
28937
  }
28923
28938
  function squareExtension(width, dx, dy) {
@@ -29571,6 +29586,14 @@ function brushPaint2(rCtx) {
29571
29586
  ...state.bkMode === 1 && state.brushPattern?.kind === "hatch" ? { skip: hatchBackgroundMask(state.brushPattern.hatch) } : {}
29572
29587
  };
29573
29588
  }
29589
+ function withoutCurveTangents(path) {
29590
+ if (!path.figures.some((f) => f.tangents)) {
29591
+ return path;
29592
+ }
29593
+ const plain = new GdiRasterPath();
29594
+ plain.figures = path.figures.map((f) => ({ pts: f.pts, closed: f.closed, roundWiden: f.roundWiden }));
29595
+ return plain;
29596
+ }
29574
29597
  function layoutRasterPath(rCtx, path, boxShape = false) {
29575
29598
  if (!rCtx.wmfLayoutRtl || boxShape) return path;
29576
29599
  const shift = Math.round(16 * (rCtx.wholeDevicePixels?.[0] ?? 1));
@@ -29581,9 +29604,11 @@ function layoutRasterPath(rCtx, path, boxShape = false) {
29581
29604
  function paintRasterPath(rCtx, path, opts) {
29582
29605
  path = layoutRasterPath(rCtx, path, opts.rectangle || opts.roundPen);
29583
29606
  const { ctx, state } = rCtx;
29607
+ let filled = !!opts.filled;
29584
29608
  if (opts.fill) {
29585
29609
  const paint = brushPaint2(rCtx);
29586
29610
  if (paint) {
29611
+ filled = true;
29587
29612
  const fillPath = opts.fillPath ? layoutRasterPath(rCtx, opts.fillPath, opts.rectangle || opts.roundPen) : path;
29588
29613
  const spans = fillPathSpans(fillPath, !!opts.winding);
29589
29614
  paintSpansDeferred(rCtx, spans, paint, state.rop2);
@@ -29596,7 +29621,7 @@ function paintRasterPath(rCtx, path, opts) {
29596
29621
  if (!penIsWidened(rCtx)) {
29597
29622
  return false;
29598
29623
  }
29599
- const polys = widenPath(path, penWidenOptions(rCtx, opts));
29624
+ const polys = widenPath(filled ? withoutCurveTangents(path) : path, penWidenOptions(rCtx, opts));
29600
29625
  paintSpansDeferred(rCtx, fillPolygonSpans(polys, true), { kind: "solid", rgb: rgbOf(state.penColor) }, state.rop2);
29601
29626
  return true;
29602
29627
  }
@@ -29913,7 +29938,7 @@ function paintGdiShape(rCtx, shape) {
29913
29938
  return;
29914
29939
  }
29915
29940
  if ((cosmetic || penIsWidened(rCtx)) && (aliased || bitwise)) {
29916
- paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen });
29941
+ paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen, filled: !!shape.fill && state.brushStyle !== 1 });
29917
29942
  return;
29918
29943
  }
29919
29944
  const pattern = cosmeticPattern(rCtx);
@@ -31435,28 +31460,92 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31435
31460
  if (reverse) taps.reverse();
31436
31461
  return taps;
31437
31462
  }
31438
- var SOFT_TENT_SIGMA = 0.25;
31439
- var SOFT_TENT_STEPS = 31;
31440
- var SOFT_TENT_SPAN = 3;
31441
- var softTentCache = /* @__PURE__ */ new Map();
31442
- function softTent(d) {
31443
- const key = Math.round(d * 1024);
31444
- const cached = softTentCache.get(key);
31445
- if (cached !== void 0) {
31446
- return cached;
31463
+ function halftoneReduceTaps(srcLen, dstLen, reverse) {
31464
+ if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31465
+ const taps = [];
31466
+ for (let i = 0; i < dstLen; i++) {
31467
+ const from = i * srcLen;
31468
+ const to = (i + 1) * srcLen;
31469
+ const run = [];
31470
+ let rest = 0;
31471
+ for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31472
+ const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31473
+ if (overlap <= 0) continue;
31474
+ const w = Math.ceil(overlap * FIX / srcLen);
31475
+ run.push([k, w]);
31476
+ if (run.length > 1) rest += w;
31477
+ }
31478
+ if (run.length > 1) run[0][1] = FIX - rest;
31479
+ taps.push(run);
31447
31480
  }
31448
- let sum = 0;
31481
+ if (reverse) taps.reverse();
31482
+ return taps;
31483
+ }
31484
+ var ENLARGE_KERNEL = [
31485
+ 0.7598,
31486
+ 0.7276,
31487
+ 0.6829,
31488
+ 0.6528,
31489
+ 0.6106,
31490
+ 0.5745,
31491
+ 0.5362,
31492
+ 0.4955,
31493
+ 0.457,
31494
+ 0.4121,
31495
+ 0.3762,
31496
+ 0.3233,
31497
+ 0.2741,
31498
+ 0.226,
31499
+ 0.1822,
31500
+ 0.1412,
31501
+ 0.1021,
31502
+ 0.0685,
31503
+ 0.0382,
31504
+ 0.0138,
31505
+ 0
31506
+ ];
31507
+ var ENLARGE_BOX = 0.2429;
31508
+ var ENLARGE_STEP = 0.05;
31509
+ function enlargeKernel(u) {
31510
+ const edge = u / ENLARGE_STEP;
31511
+ const i = Math.floor(edge);
31512
+ const smooth = i >= ENLARGE_KERNEL.length - 1 ? 0 : ENLARGE_KERNEL[i] + (ENLARGE_KERNEL[i + 1] - ENLARGE_KERNEL[i]) * (edge - i);
31513
+ return smooth + ENLARGE_BOX * (Math.abs(u - 0.5) < 1e-9 ? 0.5 : u < 0.5 ? 1 : 0);
31514
+ }
31515
+ function halftoneEnlargeTaps(src, dst) {
31516
+ const ratio = dst / src;
31517
+ const reach = Math.ceil((ENLARGE_KERNEL.length - 1) * ENLARGE_STEP * ratio);
31518
+ const fir = [];
31449
31519
  let total = 0;
31450
- const half = (SOFT_TENT_STEPS - 1) / 2;
31451
- for (let i = -half; i <= half; i++) {
31452
- const x = i / half * SOFT_TENT_SPAN * SOFT_TENT_SIGMA;
31453
- const g = Math.exp(-(x * x) / (2 * SOFT_TENT_SIGMA * SOFT_TENT_SIGMA));
31454
- total += g;
31455
- sum += g * Math.max(0, 1 - Math.abs(key / 1024 + x));
31520
+ for (let k = 0; k <= reach; k++) {
31521
+ const w = enlargeKernel(k / ratio);
31522
+ fir.push(w);
31523
+ total += k === 0 ? w : 2 * w;
31524
+ }
31525
+ const cover = [];
31526
+ for (let x = 0; x < dst; x++) {
31527
+ const lo = x / ratio;
31528
+ const hi = (x + 1) / ratio;
31529
+ const run = [];
31530
+ for (let j = Math.max(0, Math.floor(lo)); j < Math.min(src, hi); j++) {
31531
+ const overlap = Math.min(hi, j + 1) - Math.max(lo, j);
31532
+ if (overlap > 0) run.push([j, overlap * ratio]);
31533
+ }
31534
+ cover.push(run);
31456
31535
  }
31457
- const value = sum / total;
31458
- softTentCache.set(key, value);
31459
- return value;
31536
+ const taps = [];
31537
+ for (let x = 0; x < dst; x++) {
31538
+ const weights = /* @__PURE__ */ new Map();
31539
+ for (let k = -reach; k <= reach; k++) {
31540
+ const w = fir[Math.abs(k)] / total;
31541
+ if (w === 0) continue;
31542
+ for (const [j, v] of cover[Math.max(0, Math.min(dst - 1, x + k))]) {
31543
+ weights.set(j, (weights.get(j) ?? 0) + w * v);
31544
+ }
31545
+ }
31546
+ taps.push([...weights.entries()]);
31547
+ }
31548
+ return taps;
31460
31549
  }
31461
31550
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31462
31551
  for (let y = 0; y < h; y++) {
@@ -31598,7 +31687,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31598
31687
  const mixed = W > SW && H < SH || W < SW && H > SH;
31599
31688
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31600
31689
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31601
- const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31690
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31602
31691
  if ((enlarging || nearestMixed) && !directDib) {
31603
31692
  halftoneDespeckle(rect, SW, SH);
31604
31693
  }
@@ -31612,8 +31701,9 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31612
31701
  }
31613
31702
  const sampleW = nativeMixed ? Math.min(SW, W) : W;
31614
31703
  const sampleH = nativeMixed ? Math.min(SH, H) : H;
31615
- const cols = halftoneAxis(0, SW, sampleW, flipX);
31616
- const rows = halftoneAxis(0, SH, sampleH, flipY);
31704
+ const axis = nativeMixed ? (n, d, r) => halftoneReduceTaps(n, d, r) : (n, d, r) => halftoneAxis(0, n, d, r);
31705
+ const cols = axis(SW, sampleW, flipX);
31706
+ const rows = axis(SH, sampleH, flipY);
31617
31707
  if (nearestMixed) {
31618
31708
  const nearest = (taps, src2, dst, reverse) => {
31619
31709
  for (let i = 0; i < dst; i++) {
@@ -31685,29 +31775,15 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31685
31775
  const source = out;
31686
31776
  out = new Int32Array(W * H * 3);
31687
31777
  const axisTaps = (src2, dst) => {
31688
- const r = [];
31689
- for (let i = 0; i < dst; i++) {
31690
- if (src2 === dst) {
31691
- r.push([[i, 1]]);
31692
- continue;
31693
- }
31694
- const raw = (i + 0.5) * src2 / dst - 0.5;
31695
- const pos = Math.max(0, Math.min(src2 - 1, raw));
31696
- const i0 = Math.floor(pos);
31697
- if (dst === 2 * src2) {
31698
- r.push([[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]]);
31699
- continue;
31700
- }
31701
- const taps = [];
31702
- let total = 0;
31703
- for (let k = i0 - 2; k <= i0 + 3; k++) {
31704
- const w = softTent(raw - k);
31705
- taps.push([Math.max(0, Math.min(src2 - 1, k)), w]);
31706
- total += w;
31707
- }
31708
- r.push(taps.map(([k, w]) => [k, w / total]));
31778
+ if (src2 === dst) return Array.from({ length: dst }, (_, i) => [[i, 1]]);
31779
+ if (dst === 2 * src2) {
31780
+ return Array.from({ length: dst }, (_, i) => {
31781
+ const pos = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
31782
+ const i0 = Math.floor(pos);
31783
+ return [[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]];
31784
+ });
31709
31785
  }
31710
- return r;
31786
+ return halftoneEnlargeTaps(src2, dst);
31711
31787
  };
31712
31788
  const xt = axisTaps(sampleW, W), yt = axisTaps(sampleH, H);
31713
31789
  for (let y = 0; y < H; y++) {
@@ -31715,7 +31791,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31715
31791
  for (let c = 0; c < 3; c++) {
31716
31792
  let v = 0;
31717
31793
  for (const [yy, wy] of yt[y]) for (const [xx, wx] of xt[x]) v += source[(yy * sampleW + xx) * 3 + c] * wy * wx;
31718
- out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5)));
31794
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5 + 1e-7)));
31719
31795
  }
31720
31796
  }
31721
31797
  }
@@ -38818,7 +38894,7 @@ function compatCorner(p, w, h, box) {
38818
38894
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38819
38895
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38820
38896
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
38821
- return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
38897
+ return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38822
38898
  }
38823
38899
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38824
38900
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
package/dist/browser.mjs CHANGED
@@ -28825,6 +28825,20 @@ var FLAT_SECTOR_BIAS = {
28825
28825
  function sectorBias(code) {
28826
28826
  return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
28827
28827
  }
28828
+ var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28829
+ var VERTICAL_STEP_FIRST = 7;
28830
+ function verticalSideOffset(width) {
28831
+ if (width < HOBBY_LIMIT || width > 1400) {
28832
+ return void 0;
28833
+ }
28834
+ let m = VERTICAL_STEP_FIRST;
28835
+ for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28836
+ if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28837
+ m = i + VERTICAL_STEP_FIRST;
28838
+ }
28839
+ }
28840
+ return 8 * m;
28841
+ }
28828
28842
  var penCache = /* @__PURE__ */ new Map();
28829
28843
  function penPolygon(width) {
28830
28844
  const cached = penCache.get(width);
@@ -28837,7 +28851,7 @@ function penPolygon(width) {
28837
28851
  pen = HOBBY[n - 1];
28838
28852
  } else {
28839
28853
  const rx = Math.ceil(width / 2);
28840
- const ry = 8 * Math.floor((width + 9) / 16);
28854
+ const ry = rx;
28841
28855
  const f = flattenBezierPath(ellipseBeziers(-rx, -ry, rx, ry));
28842
28856
  const half = [];
28843
28857
  for (let i = 0; i + 1 < f.length; i += 2) {
@@ -28915,7 +28929,8 @@ function perpendicularVectors(width, dx0, dy0) {
28915
28929
  cy = sectorBias(pair.charCodeAt(1));
28916
28930
  }
28917
28931
  }
28918
- const vx = r(x + cx), vy = r(y + cy);
28932
+ const side = dx === 0 ? verticalSideOffset(width) : void 0;
28933
+ const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
28919
28934
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28920
28935
  }
28921
28936
  function squareExtension(width, dx, dy) {
@@ -29569,6 +29584,14 @@ function brushPaint2(rCtx) {
29569
29584
  ...state.bkMode === 1 && state.brushPattern?.kind === "hatch" ? { skip: hatchBackgroundMask(state.brushPattern.hatch) } : {}
29570
29585
  };
29571
29586
  }
29587
+ function withoutCurveTangents(path) {
29588
+ if (!path.figures.some((f) => f.tangents)) {
29589
+ return path;
29590
+ }
29591
+ const plain = new GdiRasterPath();
29592
+ plain.figures = path.figures.map((f) => ({ pts: f.pts, closed: f.closed, roundWiden: f.roundWiden }));
29593
+ return plain;
29594
+ }
29572
29595
  function layoutRasterPath(rCtx, path, boxShape = false) {
29573
29596
  if (!rCtx.wmfLayoutRtl || boxShape) return path;
29574
29597
  const shift = Math.round(16 * (rCtx.wholeDevicePixels?.[0] ?? 1));
@@ -29579,9 +29602,11 @@ function layoutRasterPath(rCtx, path, boxShape = false) {
29579
29602
  function paintRasterPath(rCtx, path, opts) {
29580
29603
  path = layoutRasterPath(rCtx, path, opts.rectangle || opts.roundPen);
29581
29604
  const { ctx, state } = rCtx;
29605
+ let filled = !!opts.filled;
29582
29606
  if (opts.fill) {
29583
29607
  const paint = brushPaint2(rCtx);
29584
29608
  if (paint) {
29609
+ filled = true;
29585
29610
  const fillPath = opts.fillPath ? layoutRasterPath(rCtx, opts.fillPath, opts.rectangle || opts.roundPen) : path;
29586
29611
  const spans = fillPathSpans(fillPath, !!opts.winding);
29587
29612
  paintSpansDeferred(rCtx, spans, paint, state.rop2);
@@ -29594,7 +29619,7 @@ function paintRasterPath(rCtx, path, opts) {
29594
29619
  if (!penIsWidened(rCtx)) {
29595
29620
  return false;
29596
29621
  }
29597
- const polys = widenPath(path, penWidenOptions(rCtx, opts));
29622
+ const polys = widenPath(filled ? withoutCurveTangents(path) : path, penWidenOptions(rCtx, opts));
29598
29623
  paintSpansDeferred(rCtx, fillPolygonSpans(polys, true), { kind: "solid", rgb: rgbOf(state.penColor) }, state.rop2);
29599
29624
  return true;
29600
29625
  }
@@ -29911,7 +29936,7 @@ function paintGdiShape(rCtx, shape) {
29911
29936
  return;
29912
29937
  }
29913
29938
  if ((cosmetic || penIsWidened(rCtx)) && (aliased || bitwise)) {
29914
- paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen });
29939
+ paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen, filled: !!shape.fill && state.brushStyle !== 1 });
29915
29940
  return;
29916
29941
  }
29917
29942
  const pattern = cosmeticPattern(rCtx);
@@ -31433,28 +31458,92 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31433
31458
  if (reverse) taps.reverse();
31434
31459
  return taps;
31435
31460
  }
31436
- var SOFT_TENT_SIGMA = 0.25;
31437
- var SOFT_TENT_STEPS = 31;
31438
- var SOFT_TENT_SPAN = 3;
31439
- var softTentCache = /* @__PURE__ */ new Map();
31440
- function softTent(d) {
31441
- const key = Math.round(d * 1024);
31442
- const cached = softTentCache.get(key);
31443
- if (cached !== void 0) {
31444
- return cached;
31461
+ function halftoneReduceTaps(srcLen, dstLen, reverse) {
31462
+ if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31463
+ const taps = [];
31464
+ for (let i = 0; i < dstLen; i++) {
31465
+ const from = i * srcLen;
31466
+ const to = (i + 1) * srcLen;
31467
+ const run = [];
31468
+ let rest = 0;
31469
+ for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31470
+ const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31471
+ if (overlap <= 0) continue;
31472
+ const w = Math.ceil(overlap * FIX / srcLen);
31473
+ run.push([k, w]);
31474
+ if (run.length > 1) rest += w;
31475
+ }
31476
+ if (run.length > 1) run[0][1] = FIX - rest;
31477
+ taps.push(run);
31445
31478
  }
31446
- let sum = 0;
31479
+ if (reverse) taps.reverse();
31480
+ return taps;
31481
+ }
31482
+ var ENLARGE_KERNEL = [
31483
+ 0.7598,
31484
+ 0.7276,
31485
+ 0.6829,
31486
+ 0.6528,
31487
+ 0.6106,
31488
+ 0.5745,
31489
+ 0.5362,
31490
+ 0.4955,
31491
+ 0.457,
31492
+ 0.4121,
31493
+ 0.3762,
31494
+ 0.3233,
31495
+ 0.2741,
31496
+ 0.226,
31497
+ 0.1822,
31498
+ 0.1412,
31499
+ 0.1021,
31500
+ 0.0685,
31501
+ 0.0382,
31502
+ 0.0138,
31503
+ 0
31504
+ ];
31505
+ var ENLARGE_BOX = 0.2429;
31506
+ var ENLARGE_STEP = 0.05;
31507
+ function enlargeKernel(u) {
31508
+ const edge = u / ENLARGE_STEP;
31509
+ const i = Math.floor(edge);
31510
+ const smooth = i >= ENLARGE_KERNEL.length - 1 ? 0 : ENLARGE_KERNEL[i] + (ENLARGE_KERNEL[i + 1] - ENLARGE_KERNEL[i]) * (edge - i);
31511
+ return smooth + ENLARGE_BOX * (Math.abs(u - 0.5) < 1e-9 ? 0.5 : u < 0.5 ? 1 : 0);
31512
+ }
31513
+ function halftoneEnlargeTaps(src, dst) {
31514
+ const ratio = dst / src;
31515
+ const reach = Math.ceil((ENLARGE_KERNEL.length - 1) * ENLARGE_STEP * ratio);
31516
+ const fir = [];
31447
31517
  let total = 0;
31448
- const half = (SOFT_TENT_STEPS - 1) / 2;
31449
- for (let i = -half; i <= half; i++) {
31450
- const x = i / half * SOFT_TENT_SPAN * SOFT_TENT_SIGMA;
31451
- const g = Math.exp(-(x * x) / (2 * SOFT_TENT_SIGMA * SOFT_TENT_SIGMA));
31452
- total += g;
31453
- sum += g * Math.max(0, 1 - Math.abs(key / 1024 + x));
31518
+ for (let k = 0; k <= reach; k++) {
31519
+ const w = enlargeKernel(k / ratio);
31520
+ fir.push(w);
31521
+ total += k === 0 ? w : 2 * w;
31522
+ }
31523
+ const cover = [];
31524
+ for (let x = 0; x < dst; x++) {
31525
+ const lo = x / ratio;
31526
+ const hi = (x + 1) / ratio;
31527
+ const run = [];
31528
+ for (let j = Math.max(0, Math.floor(lo)); j < Math.min(src, hi); j++) {
31529
+ const overlap = Math.min(hi, j + 1) - Math.max(lo, j);
31530
+ if (overlap > 0) run.push([j, overlap * ratio]);
31531
+ }
31532
+ cover.push(run);
31454
31533
  }
31455
- const value = sum / total;
31456
- softTentCache.set(key, value);
31457
- return value;
31534
+ const taps = [];
31535
+ for (let x = 0; x < dst; x++) {
31536
+ const weights = /* @__PURE__ */ new Map();
31537
+ for (let k = -reach; k <= reach; k++) {
31538
+ const w = fir[Math.abs(k)] / total;
31539
+ if (w === 0) continue;
31540
+ for (const [j, v] of cover[Math.max(0, Math.min(dst - 1, x + k))]) {
31541
+ weights.set(j, (weights.get(j) ?? 0) + w * v);
31542
+ }
31543
+ }
31544
+ taps.push([...weights.entries()]);
31545
+ }
31546
+ return taps;
31458
31547
  }
31459
31548
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31460
31549
  for (let y = 0; y < h; y++) {
@@ -31596,7 +31685,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31596
31685
  const mixed = W > SW && H < SH || W < SW && H > SH;
31597
31686
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31598
31687
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31599
- const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31688
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31600
31689
  if ((enlarging || nearestMixed) && !directDib) {
31601
31690
  halftoneDespeckle(rect, SW, SH);
31602
31691
  }
@@ -31610,8 +31699,9 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31610
31699
  }
31611
31700
  const sampleW = nativeMixed ? Math.min(SW, W) : W;
31612
31701
  const sampleH = nativeMixed ? Math.min(SH, H) : H;
31613
- const cols = halftoneAxis(0, SW, sampleW, flipX);
31614
- const rows = halftoneAxis(0, SH, sampleH, flipY);
31702
+ const axis = nativeMixed ? (n, d, r) => halftoneReduceTaps(n, d, r) : (n, d, r) => halftoneAxis(0, n, d, r);
31703
+ const cols = axis(SW, sampleW, flipX);
31704
+ const rows = axis(SH, sampleH, flipY);
31615
31705
  if (nearestMixed) {
31616
31706
  const nearest = (taps, src2, dst, reverse) => {
31617
31707
  for (let i = 0; i < dst; i++) {
@@ -31683,29 +31773,15 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31683
31773
  const source = out;
31684
31774
  out = new Int32Array(W * H * 3);
31685
31775
  const axisTaps = (src2, dst) => {
31686
- const r = [];
31687
- for (let i = 0; i < dst; i++) {
31688
- if (src2 === dst) {
31689
- r.push([[i, 1]]);
31690
- continue;
31691
- }
31692
- const raw = (i + 0.5) * src2 / dst - 0.5;
31693
- const pos = Math.max(0, Math.min(src2 - 1, raw));
31694
- const i0 = Math.floor(pos);
31695
- if (dst === 2 * src2) {
31696
- r.push([[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]]);
31697
- continue;
31698
- }
31699
- const taps = [];
31700
- let total = 0;
31701
- for (let k = i0 - 2; k <= i0 + 3; k++) {
31702
- const w = softTent(raw - k);
31703
- taps.push([Math.max(0, Math.min(src2 - 1, k)), w]);
31704
- total += w;
31705
- }
31706
- r.push(taps.map(([k, w]) => [k, w / total]));
31776
+ if (src2 === dst) return Array.from({ length: dst }, (_, i) => [[i, 1]]);
31777
+ if (dst === 2 * src2) {
31778
+ return Array.from({ length: dst }, (_, i) => {
31779
+ const pos = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
31780
+ const i0 = Math.floor(pos);
31781
+ return [[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]];
31782
+ });
31707
31783
  }
31708
- return r;
31784
+ return halftoneEnlargeTaps(src2, dst);
31709
31785
  };
31710
31786
  const xt = axisTaps(sampleW, W), yt = axisTaps(sampleH, H);
31711
31787
  for (let y = 0; y < H; y++) {
@@ -31713,7 +31789,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31713
31789
  for (let c = 0; c < 3; c++) {
31714
31790
  let v = 0;
31715
31791
  for (const [yy, wy] of yt[y]) for (const [xx, wx] of xt[x]) v += source[(yy * sampleW + xx) * 3 + c] * wy * wx;
31716
- out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5)));
31792
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5 + 1e-7)));
31717
31793
  }
31718
31794
  }
31719
31795
  }
@@ -38816,7 +38892,7 @@ function compatCorner(p, w, h, box) {
38816
38892
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38817
38893
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38818
38894
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
38819
- return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
38895
+ return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38820
38896
  }
38821
38897
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38822
38898
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
package/dist/index.js CHANGED
@@ -28828,6 +28828,20 @@ var FLAT_SECTOR_BIAS = {
28828
28828
  function sectorBias(code) {
28829
28829
  return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
28830
28830
  }
28831
+ var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28832
+ var VERTICAL_STEP_FIRST = 7;
28833
+ function verticalSideOffset(width) {
28834
+ if (width < HOBBY_LIMIT || width > 1400) {
28835
+ return void 0;
28836
+ }
28837
+ let m = VERTICAL_STEP_FIRST;
28838
+ for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28839
+ if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28840
+ m = i + VERTICAL_STEP_FIRST;
28841
+ }
28842
+ }
28843
+ return 8 * m;
28844
+ }
28831
28845
  var penCache = /* @__PURE__ */ new Map();
28832
28846
  function penPolygon(width) {
28833
28847
  const cached = penCache.get(width);
@@ -28840,7 +28854,7 @@ function penPolygon(width) {
28840
28854
  pen = HOBBY[n - 1];
28841
28855
  } else {
28842
28856
  const rx = Math.ceil(width / 2);
28843
- const ry = 8 * Math.floor((width + 9) / 16);
28857
+ const ry = rx;
28844
28858
  const f = flattenBezierPath(ellipseBeziers(-rx, -ry, rx, ry));
28845
28859
  const half = [];
28846
28860
  for (let i = 0; i + 1 < f.length; i += 2) {
@@ -28918,7 +28932,8 @@ function perpendicularVectors(width, dx0, dy0) {
28918
28932
  cy = sectorBias(pair.charCodeAt(1));
28919
28933
  }
28920
28934
  }
28921
- const vx = r(x + cx), vy = r(y + cy);
28935
+ const side = dx === 0 ? verticalSideOffset(width) : void 0;
28936
+ const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
28922
28937
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28923
28938
  }
28924
28939
  function squareExtension(width, dx, dy) {
@@ -29572,6 +29587,14 @@ function brushPaint2(rCtx) {
29572
29587
  ...state.bkMode === 1 && state.brushPattern?.kind === "hatch" ? { skip: hatchBackgroundMask(state.brushPattern.hatch) } : {}
29573
29588
  };
29574
29589
  }
29590
+ function withoutCurveTangents(path) {
29591
+ if (!path.figures.some((f) => f.tangents)) {
29592
+ return path;
29593
+ }
29594
+ const plain = new GdiRasterPath();
29595
+ plain.figures = path.figures.map((f) => ({ pts: f.pts, closed: f.closed, roundWiden: f.roundWiden }));
29596
+ return plain;
29597
+ }
29575
29598
  function layoutRasterPath(rCtx, path, boxShape = false) {
29576
29599
  if (!rCtx.wmfLayoutRtl || boxShape) return path;
29577
29600
  const shift = Math.round(16 * (rCtx.wholeDevicePixels?.[0] ?? 1));
@@ -29582,9 +29605,11 @@ function layoutRasterPath(rCtx, path, boxShape = false) {
29582
29605
  function paintRasterPath(rCtx, path, opts) {
29583
29606
  path = layoutRasterPath(rCtx, path, opts.rectangle || opts.roundPen);
29584
29607
  const { ctx, state } = rCtx;
29608
+ let filled = !!opts.filled;
29585
29609
  if (opts.fill) {
29586
29610
  const paint = brushPaint2(rCtx);
29587
29611
  if (paint) {
29612
+ filled = true;
29588
29613
  const fillPath = opts.fillPath ? layoutRasterPath(rCtx, opts.fillPath, opts.rectangle || opts.roundPen) : path;
29589
29614
  const spans = fillPathSpans(fillPath, !!opts.winding);
29590
29615
  paintSpansDeferred(rCtx, spans, paint, state.rop2);
@@ -29597,7 +29622,7 @@ function paintRasterPath(rCtx, path, opts) {
29597
29622
  if (!penIsWidened(rCtx)) {
29598
29623
  return false;
29599
29624
  }
29600
- const polys = widenPath(path, penWidenOptions(rCtx, opts));
29625
+ const polys = widenPath(filled ? withoutCurveTangents(path) : path, penWidenOptions(rCtx, opts));
29601
29626
  paintSpansDeferred(rCtx, fillPolygonSpans(polys, true), { kind: "solid", rgb: rgbOf(state.penColor) }, state.rop2);
29602
29627
  return true;
29603
29628
  }
@@ -29914,7 +29939,7 @@ function paintGdiShape(rCtx, shape) {
29914
29939
  return;
29915
29940
  }
29916
29941
  if ((cosmetic || penIsWidened(rCtx)) && (aliased || bitwise)) {
29917
- paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen });
29942
+ paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen, filled: !!shape.fill && state.brushStyle !== 1 });
29918
29943
  return;
29919
29944
  }
29920
29945
  const pattern = cosmeticPattern(rCtx);
@@ -31436,28 +31461,92 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31436
31461
  if (reverse) taps.reverse();
31437
31462
  return taps;
31438
31463
  }
31439
- var SOFT_TENT_SIGMA = 0.25;
31440
- var SOFT_TENT_STEPS = 31;
31441
- var SOFT_TENT_SPAN = 3;
31442
- var softTentCache = /* @__PURE__ */ new Map();
31443
- function softTent(d) {
31444
- const key = Math.round(d * 1024);
31445
- const cached = softTentCache.get(key);
31446
- if (cached !== void 0) {
31447
- return cached;
31464
+ function halftoneReduceTaps(srcLen, dstLen, reverse) {
31465
+ if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31466
+ const taps = [];
31467
+ for (let i = 0; i < dstLen; i++) {
31468
+ const from = i * srcLen;
31469
+ const to = (i + 1) * srcLen;
31470
+ const run = [];
31471
+ let rest = 0;
31472
+ for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31473
+ const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31474
+ if (overlap <= 0) continue;
31475
+ const w = Math.ceil(overlap * FIX / srcLen);
31476
+ run.push([k, w]);
31477
+ if (run.length > 1) rest += w;
31478
+ }
31479
+ if (run.length > 1) run[0][1] = FIX - rest;
31480
+ taps.push(run);
31448
31481
  }
31449
- let sum = 0;
31482
+ if (reverse) taps.reverse();
31483
+ return taps;
31484
+ }
31485
+ var ENLARGE_KERNEL = [
31486
+ 0.7598,
31487
+ 0.7276,
31488
+ 0.6829,
31489
+ 0.6528,
31490
+ 0.6106,
31491
+ 0.5745,
31492
+ 0.5362,
31493
+ 0.4955,
31494
+ 0.457,
31495
+ 0.4121,
31496
+ 0.3762,
31497
+ 0.3233,
31498
+ 0.2741,
31499
+ 0.226,
31500
+ 0.1822,
31501
+ 0.1412,
31502
+ 0.1021,
31503
+ 0.0685,
31504
+ 0.0382,
31505
+ 0.0138,
31506
+ 0
31507
+ ];
31508
+ var ENLARGE_BOX = 0.2429;
31509
+ var ENLARGE_STEP = 0.05;
31510
+ function enlargeKernel(u) {
31511
+ const edge = u / ENLARGE_STEP;
31512
+ const i = Math.floor(edge);
31513
+ const smooth = i >= ENLARGE_KERNEL.length - 1 ? 0 : ENLARGE_KERNEL[i] + (ENLARGE_KERNEL[i + 1] - ENLARGE_KERNEL[i]) * (edge - i);
31514
+ return smooth + ENLARGE_BOX * (Math.abs(u - 0.5) < 1e-9 ? 0.5 : u < 0.5 ? 1 : 0);
31515
+ }
31516
+ function halftoneEnlargeTaps(src, dst) {
31517
+ const ratio = dst / src;
31518
+ const reach = Math.ceil((ENLARGE_KERNEL.length - 1) * ENLARGE_STEP * ratio);
31519
+ const fir = [];
31450
31520
  let total = 0;
31451
- const half = (SOFT_TENT_STEPS - 1) / 2;
31452
- for (let i = -half; i <= half; i++) {
31453
- const x = i / half * SOFT_TENT_SPAN * SOFT_TENT_SIGMA;
31454
- const g = Math.exp(-(x * x) / (2 * SOFT_TENT_SIGMA * SOFT_TENT_SIGMA));
31455
- total += g;
31456
- sum += g * Math.max(0, 1 - Math.abs(key / 1024 + x));
31521
+ for (let k = 0; k <= reach; k++) {
31522
+ const w = enlargeKernel(k / ratio);
31523
+ fir.push(w);
31524
+ total += k === 0 ? w : 2 * w;
31525
+ }
31526
+ const cover = [];
31527
+ for (let x = 0; x < dst; x++) {
31528
+ const lo = x / ratio;
31529
+ const hi = (x + 1) / ratio;
31530
+ const run = [];
31531
+ for (let j = Math.max(0, Math.floor(lo)); j < Math.min(src, hi); j++) {
31532
+ const overlap = Math.min(hi, j + 1) - Math.max(lo, j);
31533
+ if (overlap > 0) run.push([j, overlap * ratio]);
31534
+ }
31535
+ cover.push(run);
31457
31536
  }
31458
- const value = sum / total;
31459
- softTentCache.set(key, value);
31460
- return value;
31537
+ const taps = [];
31538
+ for (let x = 0; x < dst; x++) {
31539
+ const weights = /* @__PURE__ */ new Map();
31540
+ for (let k = -reach; k <= reach; k++) {
31541
+ const w = fir[Math.abs(k)] / total;
31542
+ if (w === 0) continue;
31543
+ for (const [j, v] of cover[Math.max(0, Math.min(dst - 1, x + k))]) {
31544
+ weights.set(j, (weights.get(j) ?? 0) + w * v);
31545
+ }
31546
+ }
31547
+ taps.push([...weights.entries()]);
31548
+ }
31549
+ return taps;
31461
31550
  }
31462
31551
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31463
31552
  for (let y = 0; y < h; y++) {
@@ -31599,7 +31688,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31599
31688
  const mixed = W > SW && H < SH || W < SW && H > SH;
31600
31689
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31601
31690
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31602
- const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31691
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31603
31692
  if ((enlarging || nearestMixed) && !directDib) {
31604
31693
  halftoneDespeckle(rect, SW, SH);
31605
31694
  }
@@ -31613,8 +31702,9 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31613
31702
  }
31614
31703
  const sampleW = nativeMixed ? Math.min(SW, W) : W;
31615
31704
  const sampleH = nativeMixed ? Math.min(SH, H) : H;
31616
- const cols = halftoneAxis(0, SW, sampleW, flipX);
31617
- const rows = halftoneAxis(0, SH, sampleH, flipY);
31705
+ const axis = nativeMixed ? (n, d, r) => halftoneReduceTaps(n, d, r) : (n, d, r) => halftoneAxis(0, n, d, r);
31706
+ const cols = axis(SW, sampleW, flipX);
31707
+ const rows = axis(SH, sampleH, flipY);
31618
31708
  if (nearestMixed) {
31619
31709
  const nearest = (taps, src2, dst, reverse) => {
31620
31710
  for (let i = 0; i < dst; i++) {
@@ -31686,29 +31776,15 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31686
31776
  const source = out;
31687
31777
  out = new Int32Array(W * H * 3);
31688
31778
  const axisTaps = (src2, dst) => {
31689
- const r = [];
31690
- for (let i = 0; i < dst; i++) {
31691
- if (src2 === dst) {
31692
- r.push([[i, 1]]);
31693
- continue;
31694
- }
31695
- const raw = (i + 0.5) * src2 / dst - 0.5;
31696
- const pos = Math.max(0, Math.min(src2 - 1, raw));
31697
- const i0 = Math.floor(pos);
31698
- if (dst === 2 * src2) {
31699
- r.push([[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]]);
31700
- continue;
31701
- }
31702
- const taps = [];
31703
- let total = 0;
31704
- for (let k = i0 - 2; k <= i0 + 3; k++) {
31705
- const w = softTent(raw - k);
31706
- taps.push([Math.max(0, Math.min(src2 - 1, k)), w]);
31707
- total += w;
31708
- }
31709
- r.push(taps.map(([k, w]) => [k, w / total]));
31779
+ if (src2 === dst) return Array.from({ length: dst }, (_, i) => [[i, 1]]);
31780
+ if (dst === 2 * src2) {
31781
+ return Array.from({ length: dst }, (_, i) => {
31782
+ const pos = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
31783
+ const i0 = Math.floor(pos);
31784
+ return [[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]];
31785
+ });
31710
31786
  }
31711
- return r;
31787
+ return halftoneEnlargeTaps(src2, dst);
31712
31788
  };
31713
31789
  const xt = axisTaps(sampleW, W), yt = axisTaps(sampleH, H);
31714
31790
  for (let y = 0; y < H; y++) {
@@ -31716,7 +31792,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31716
31792
  for (let c = 0; c < 3; c++) {
31717
31793
  let v = 0;
31718
31794
  for (const [yy, wy] of yt[y]) for (const [xx, wx] of xt[x]) v += source[(yy * sampleW + xx) * 3 + c] * wy * wx;
31719
- out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5)));
31795
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5 + 1e-7)));
31720
31796
  }
31721
31797
  }
31722
31798
  }
@@ -38819,7 +38895,7 @@ function compatCorner(p, w, h, box) {
38819
38895
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38820
38896
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38821
38897
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
38822
- return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
38898
+ return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38823
38899
  }
38824
38900
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38825
38901
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
package/dist/index.mjs CHANGED
@@ -28826,6 +28826,20 @@ var FLAT_SECTOR_BIAS = {
28826
28826
  function sectorBias(code) {
28827
28827
  return code >= 97 ? (code - 97) / 2 - 1.5 : (code - 65) / 2 - 1.5 - 1 / 64;
28828
28828
  }
28829
+ var VERTICAL_STEP_EARLY = "120002002002002002202200200200200220220020000022222220000002222220000002222220000";
28830
+ var VERTICAL_STEP_FIRST = 7;
28831
+ function verticalSideOffset(width) {
28832
+ if (width < HOBBY_LIMIT || width > 1400) {
28833
+ return void 0;
28834
+ }
28835
+ let m = VERTICAL_STEP_FIRST;
28836
+ for (let i = 1; i < VERTICAL_STEP_EARLY.length; i++) {
28837
+ if (width >= 16 * (i + VERTICAL_STEP_FIRST) - 7 - +VERTICAL_STEP_EARLY[i]) {
28838
+ m = i + VERTICAL_STEP_FIRST;
28839
+ }
28840
+ }
28841
+ return 8 * m;
28842
+ }
28829
28843
  var penCache = /* @__PURE__ */ new Map();
28830
28844
  function penPolygon(width) {
28831
28845
  const cached = penCache.get(width);
@@ -28838,7 +28852,7 @@ function penPolygon(width) {
28838
28852
  pen = HOBBY[n - 1];
28839
28853
  } else {
28840
28854
  const rx = Math.ceil(width / 2);
28841
- const ry = 8 * Math.floor((width + 9) / 16);
28855
+ const ry = rx;
28842
28856
  const f = flattenBezierPath(ellipseBeziers(-rx, -ry, rx, ry));
28843
28857
  const half = [];
28844
28858
  for (let i = 0; i + 1 < f.length; i += 2) {
@@ -28916,7 +28930,8 @@ function perpendicularVectors(width, dx0, dy0) {
28916
28930
  cy = sectorBias(pair.charCodeAt(1));
28917
28931
  }
28918
28932
  }
28919
- const vx = r(x + cx), vy = r(y + cy);
28933
+ const side = dx === 0 ? verticalSideOffset(width) : void 0;
28934
+ const vx = side === void 0 ? r(x + cx) : -side, vy = r(y + cy);
28920
28935
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
28921
28936
  }
28922
28937
  function squareExtension(width, dx, dy) {
@@ -29570,6 +29585,14 @@ function brushPaint2(rCtx) {
29570
29585
  ...state.bkMode === 1 && state.brushPattern?.kind === "hatch" ? { skip: hatchBackgroundMask(state.brushPattern.hatch) } : {}
29571
29586
  };
29572
29587
  }
29588
+ function withoutCurveTangents(path) {
29589
+ if (!path.figures.some((f) => f.tangents)) {
29590
+ return path;
29591
+ }
29592
+ const plain = new GdiRasterPath();
29593
+ plain.figures = path.figures.map((f) => ({ pts: f.pts, closed: f.closed, roundWiden: f.roundWiden }));
29594
+ return plain;
29595
+ }
29573
29596
  function layoutRasterPath(rCtx, path, boxShape = false) {
29574
29597
  if (!rCtx.wmfLayoutRtl || boxShape) return path;
29575
29598
  const shift = Math.round(16 * (rCtx.wholeDevicePixels?.[0] ?? 1));
@@ -29580,9 +29603,11 @@ function layoutRasterPath(rCtx, path, boxShape = false) {
29580
29603
  function paintRasterPath(rCtx, path, opts) {
29581
29604
  path = layoutRasterPath(rCtx, path, opts.rectangle || opts.roundPen);
29582
29605
  const { ctx, state } = rCtx;
29606
+ let filled = !!opts.filled;
29583
29607
  if (opts.fill) {
29584
29608
  const paint = brushPaint2(rCtx);
29585
29609
  if (paint) {
29610
+ filled = true;
29586
29611
  const fillPath = opts.fillPath ? layoutRasterPath(rCtx, opts.fillPath, opts.rectangle || opts.roundPen) : path;
29587
29612
  const spans = fillPathSpans(fillPath, !!opts.winding);
29588
29613
  paintSpansDeferred(rCtx, spans, paint, state.rop2);
@@ -29595,7 +29620,7 @@ function paintRasterPath(rCtx, path, opts) {
29595
29620
  if (!penIsWidened(rCtx)) {
29596
29621
  return false;
29597
29622
  }
29598
- const polys = widenPath(path, penWidenOptions(rCtx, opts));
29623
+ const polys = widenPath(filled ? withoutCurveTangents(path) : path, penWidenOptions(rCtx, opts));
29599
29624
  paintSpansDeferred(rCtx, fillPolygonSpans(polys, true), { kind: "solid", rgb: rgbOf(state.penColor) }, state.rop2);
29600
29625
  return true;
29601
29626
  }
@@ -29912,7 +29937,7 @@ function paintGdiShape(rCtx, shape) {
29912
29937
  return;
29913
29938
  }
29914
29939
  if ((cosmetic || penIsWidened(rCtx)) && (aliased || bitwise)) {
29915
- paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen });
29940
+ paintRasterPath(rCtx, getPath(), { fill: false, stroke: true, style: shape.style, rectangle: shape.rectangle, roundPen: shape.roundPen, filled: !!shape.fill && state.brushStyle !== 1 });
29916
29941
  return;
29917
29942
  }
29918
29943
  const pattern = cosmeticPattern(rCtx);
@@ -31434,28 +31459,92 @@ function halftoneAxis(start, srcLen, dstLen, reverse) {
31434
31459
  if (reverse) taps.reverse();
31435
31460
  return taps;
31436
31461
  }
31437
- var SOFT_TENT_SIGMA = 0.25;
31438
- var SOFT_TENT_STEPS = 31;
31439
- var SOFT_TENT_SPAN = 3;
31440
- var softTentCache = /* @__PURE__ */ new Map();
31441
- function softTent(d) {
31442
- const key = Math.round(d * 1024);
31443
- const cached = softTentCache.get(key);
31444
- if (cached !== void 0) {
31445
- return cached;
31462
+ function halftoneReduceTaps(srcLen, dstLen, reverse) {
31463
+ if (dstLen >= srcLen) return halftoneAxis(0, srcLen, dstLen, reverse);
31464
+ const taps = [];
31465
+ for (let i = 0; i < dstLen; i++) {
31466
+ const from = i * srcLen;
31467
+ const to = (i + 1) * srcLen;
31468
+ const run = [];
31469
+ let rest = 0;
31470
+ for (let k = Math.floor(from / dstLen); k * dstLen < to; k++) {
31471
+ const overlap = Math.min(to, (k + 1) * dstLen) - Math.max(from, k * dstLen);
31472
+ if (overlap <= 0) continue;
31473
+ const w = Math.ceil(overlap * FIX / srcLen);
31474
+ run.push([k, w]);
31475
+ if (run.length > 1) rest += w;
31476
+ }
31477
+ if (run.length > 1) run[0][1] = FIX - rest;
31478
+ taps.push(run);
31446
31479
  }
31447
- let sum = 0;
31480
+ if (reverse) taps.reverse();
31481
+ return taps;
31482
+ }
31483
+ var ENLARGE_KERNEL = [
31484
+ 0.7598,
31485
+ 0.7276,
31486
+ 0.6829,
31487
+ 0.6528,
31488
+ 0.6106,
31489
+ 0.5745,
31490
+ 0.5362,
31491
+ 0.4955,
31492
+ 0.457,
31493
+ 0.4121,
31494
+ 0.3762,
31495
+ 0.3233,
31496
+ 0.2741,
31497
+ 0.226,
31498
+ 0.1822,
31499
+ 0.1412,
31500
+ 0.1021,
31501
+ 0.0685,
31502
+ 0.0382,
31503
+ 0.0138,
31504
+ 0
31505
+ ];
31506
+ var ENLARGE_BOX = 0.2429;
31507
+ var ENLARGE_STEP = 0.05;
31508
+ function enlargeKernel(u) {
31509
+ const edge = u / ENLARGE_STEP;
31510
+ const i = Math.floor(edge);
31511
+ const smooth = i >= ENLARGE_KERNEL.length - 1 ? 0 : ENLARGE_KERNEL[i] + (ENLARGE_KERNEL[i + 1] - ENLARGE_KERNEL[i]) * (edge - i);
31512
+ return smooth + ENLARGE_BOX * (Math.abs(u - 0.5) < 1e-9 ? 0.5 : u < 0.5 ? 1 : 0);
31513
+ }
31514
+ function halftoneEnlargeTaps(src, dst) {
31515
+ const ratio = dst / src;
31516
+ const reach = Math.ceil((ENLARGE_KERNEL.length - 1) * ENLARGE_STEP * ratio);
31517
+ const fir = [];
31448
31518
  let total = 0;
31449
- const half = (SOFT_TENT_STEPS - 1) / 2;
31450
- for (let i = -half; i <= half; i++) {
31451
- const x = i / half * SOFT_TENT_SPAN * SOFT_TENT_SIGMA;
31452
- const g = Math.exp(-(x * x) / (2 * SOFT_TENT_SIGMA * SOFT_TENT_SIGMA));
31453
- total += g;
31454
- sum += g * Math.max(0, 1 - Math.abs(key / 1024 + x));
31519
+ for (let k = 0; k <= reach; k++) {
31520
+ const w = enlargeKernel(k / ratio);
31521
+ fir.push(w);
31522
+ total += k === 0 ? w : 2 * w;
31523
+ }
31524
+ const cover = [];
31525
+ for (let x = 0; x < dst; x++) {
31526
+ const lo = x / ratio;
31527
+ const hi = (x + 1) / ratio;
31528
+ const run = [];
31529
+ for (let j = Math.max(0, Math.floor(lo)); j < Math.min(src, hi); j++) {
31530
+ const overlap = Math.min(hi, j + 1) - Math.max(lo, j);
31531
+ if (overlap > 0) run.push([j, overlap * ratio]);
31532
+ }
31533
+ cover.push(run);
31455
31534
  }
31456
- const value = sum / total;
31457
- softTentCache.set(key, value);
31458
- return value;
31535
+ const taps = [];
31536
+ for (let x = 0; x < dst; x++) {
31537
+ const weights = /* @__PURE__ */ new Map();
31538
+ for (let k = -reach; k <= reach; k++) {
31539
+ const w = fir[Math.abs(k)] / total;
31540
+ if (w === 0) continue;
31541
+ for (const [j, v] of cover[Math.max(0, Math.min(dst - 1, x + k))]) {
31542
+ weights.set(j, (weights.get(j) ?? 0) + w * v);
31543
+ }
31544
+ }
31545
+ taps.push([...weights.entries()]);
31546
+ }
31547
+ return taps;
31459
31548
  }
31460
31549
  function ditherPixels(px, w, h, dither, flipX, flipY, phaseX, phaseY) {
31461
31550
  for (let y = 0; y < h; y++) {
@@ -31597,7 +31686,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31597
31686
  const mixed = W > SW && H < SH || W < SW && H > SH;
31598
31687
  const dithered = !!dither && !!adjust && !adjustAfterSampling;
31599
31688
  const nearestMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W < SW && W * 2 > SW || H < SH && H * 2 > SH);
31600
- const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && Math.min(SW, W) >= 2 && Math.min(SH, H) >= 2 && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31689
+ const nativeMixed = mixed && (!adjust || adjustAfterSampling || dithered) && (W > SW && H * 2 <= SH || H > SH && W * 2 <= SW);
31601
31690
  if ((enlarging || nearestMixed) && !directDib) {
31602
31691
  halftoneDespeckle(rect, SW, SH);
31603
31692
  }
@@ -31611,8 +31700,9 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31611
31700
  }
31612
31701
  const sampleW = nativeMixed ? Math.min(SW, W) : W;
31613
31702
  const sampleH = nativeMixed ? Math.min(SH, H) : H;
31614
- const cols = halftoneAxis(0, SW, sampleW, flipX);
31615
- const rows = halftoneAxis(0, SH, sampleH, flipY);
31703
+ const axis = nativeMixed ? (n, d, r) => halftoneReduceTaps(n, d, r) : (n, d, r) => halftoneAxis(0, n, d, r);
31704
+ const cols = axis(SW, sampleW, flipX);
31705
+ const rows = axis(SH, sampleH, flipY);
31616
31706
  if (nearestMixed) {
31617
31707
  const nearest = (taps, src2, dst, reverse) => {
31618
31708
  for (let i = 0; i < dst; i++) {
@@ -31684,29 +31774,15 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31684
31774
  const source = out;
31685
31775
  out = new Int32Array(W * H * 3);
31686
31776
  const axisTaps = (src2, dst) => {
31687
- const r = [];
31688
- for (let i = 0; i < dst; i++) {
31689
- if (src2 === dst) {
31690
- r.push([[i, 1]]);
31691
- continue;
31692
- }
31693
- const raw = (i + 0.5) * src2 / dst - 0.5;
31694
- const pos = Math.max(0, Math.min(src2 - 1, raw));
31695
- const i0 = Math.floor(pos);
31696
- if (dst === 2 * src2) {
31697
- r.push([[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]]);
31698
- continue;
31699
- }
31700
- const taps = [];
31701
- let total = 0;
31702
- for (let k = i0 - 2; k <= i0 + 3; k++) {
31703
- const w = softTent(raw - k);
31704
- taps.push([Math.max(0, Math.min(src2 - 1, k)), w]);
31705
- total += w;
31706
- }
31707
- r.push(taps.map(([k, w]) => [k, w / total]));
31777
+ if (src2 === dst) return Array.from({ length: dst }, (_, i) => [[i, 1]]);
31778
+ if (dst === 2 * src2) {
31779
+ return Array.from({ length: dst }, (_, i) => {
31780
+ const pos = Math.max(0, Math.min(src2 - 1, (i + 0.5) * src2 / dst - 0.5));
31781
+ const i0 = Math.floor(pos);
31782
+ return [[i0, 1 - (pos - i0)], [Math.min(src2 - 1, i0 + 1), pos - i0]];
31783
+ });
31708
31784
  }
31709
- return r;
31785
+ return halftoneEnlargeTaps(src2, dst);
31710
31786
  };
31711
31787
  const xt = axisTaps(sampleW, W), yt = axisTaps(sampleH, H);
31712
31788
  for (let y = 0; y < H; y++) {
@@ -31714,7 +31790,7 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
31714
31790
  for (let c = 0; c < 3; c++) {
31715
31791
  let v = 0;
31716
31792
  for (const [yy, wy] of yt[y]) for (const [xx, wx] of xt[x]) v += source[(yy * sampleW + xx) * 3 + c] * wy * wx;
31717
- out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5)));
31793
+ out[(y * W + x) * 3 + c] = Math.max(0, Math.min(255, Math.floor(v + 0.5 + 1e-7)));
31718
31794
  }
31719
31795
  }
31720
31796
  }
@@ -38817,7 +38893,7 @@ function compatCorner(p, w, h, box) {
38817
38893
  if ((p.rCtx.state.penStyle & 15) === PS_INSIDEFRAME && !penIsCosmetic(p.rCtx)) {
38818
38894
  const penWidth = p.rCtx.state.penWidth * Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) * 16;
38819
38895
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
38820
- return [Math.min(cw, width + penWidth) * width / (width + penWidth), Math.min(ch, height + penWidth) * height / (height + penWidth)];
38896
+ return [Math.floor(Math.min(cw, width + penWidth) * width / (width + penWidth)), Math.floor(Math.min(ch, height + penWidth) * height / (height + penWidth))];
38821
38897
  }
38822
38898
  if (!penIsNull(p) && penIsCosmetic(p.rCtx)) {
38823
38899
  const width = box.x1 - box.x0, height = box.y1 - box.y0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.6.6",
3
+ "version": "4.6.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",