emf-converter 4.1.0 → 4.2.1
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/README.md +6 -6
- package/dist/index.js +711 -130
- package/dist/index.mjs +711 -130
- package/package.json +9 -3
package/dist/index.mjs
CHANGED
|
@@ -22,9 +22,9 @@ function lerpArgbToRgba(argbA, argbB, t) {
|
|
|
22
22
|
return `rgba(${r},${g},${b},${a.toFixed(3)})`;
|
|
23
23
|
}
|
|
24
24
|
function invertCssColor(color) {
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
const v = parseInt(
|
|
25
|
+
const hex5 = /^#([0-9a-f]{6})$/i.exec(color);
|
|
26
|
+
if (hex5) {
|
|
27
|
+
const v = parseInt(hex5[1], 16);
|
|
28
28
|
const inv2 = 16777215 ^ v;
|
|
29
29
|
return `#${inv2.toString(16).padStart(6, "0")}`;
|
|
30
30
|
}
|
|
@@ -384,8 +384,8 @@ function cssHexToRgb(color) {
|
|
|
384
384
|
if (!m) {
|
|
385
385
|
return 0;
|
|
386
386
|
}
|
|
387
|
-
const
|
|
388
|
-
return parseInt(
|
|
387
|
+
const hex5 = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
|
|
388
|
+
return parseInt(hex5, 16);
|
|
389
389
|
}
|
|
390
390
|
function decodeMonoBits(view, bmiOffset, bitsOffset) {
|
|
391
391
|
if (bmiOffset + 16 > view.byteLength) {
|
|
@@ -728,6 +728,7 @@ var EMFPLUS_SETRENDERINGORIGIN = 16413;
|
|
|
728
728
|
var EMFPLUS_SETTEXTCONTRAST = 16416;
|
|
729
729
|
var EMFPLUS_SETCOMPOSITINGMODE = 16419;
|
|
730
730
|
var EMFPLUS_STROKEFILLPATH = 16439;
|
|
731
|
+
var EMFPLUS_SERIALIZABLEOBJECT = 16440;
|
|
731
732
|
var EMFPLUS_SETTSGRAPHICS = 16441;
|
|
732
733
|
var EMFPLUS_SETTSCLIP = 16442;
|
|
733
734
|
var EMFPLUS_OBJECTTYPE_BRUSH = 1;
|
|
@@ -13738,7 +13739,8 @@ function gmx(r, x) {
|
|
|
13738
13739
|
const wt = r.state.worldTransform;
|
|
13739
13740
|
const px = wt[0] * x + wt[4];
|
|
13740
13741
|
if (r.useMappingMode) {
|
|
13741
|
-
|
|
13742
|
+
const dx = (px - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
|
|
13743
|
+
return r.deviceToCanvas ? (dx - r.bounds.left) * r.sx : dx;
|
|
13742
13744
|
}
|
|
13743
13745
|
return (px - r.bounds.left) * r.sx;
|
|
13744
13746
|
}
|
|
@@ -13746,24 +13748,31 @@ function gmy(r, y) {
|
|
|
13746
13748
|
const wt = r.state.worldTransform;
|
|
13747
13749
|
const py = wt[3] * y + wt[5];
|
|
13748
13750
|
if (r.useMappingMode) {
|
|
13749
|
-
|
|
13751
|
+
const dy = (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
|
|
13752
|
+
return r.deviceToCanvas ? (dy - r.bounds.top) * r.sy : dy;
|
|
13750
13753
|
}
|
|
13751
13754
|
return (py - r.bounds.top) * r.sy;
|
|
13752
13755
|
}
|
|
13753
13756
|
function gmw(r, w) {
|
|
13754
13757
|
const pw = r.state.worldTransform[0] * w;
|
|
13755
13758
|
if (r.useMappingMode) {
|
|
13756
|
-
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
|
|
13759
|
+
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) * (r.deviceToCanvas ? r.sx : 1);
|
|
13757
13760
|
}
|
|
13758
13761
|
return pw * r.sx;
|
|
13759
13762
|
}
|
|
13760
13763
|
function gmh(r, h) {
|
|
13761
13764
|
const ph = r.state.worldTransform[3] * h;
|
|
13762
13765
|
if (r.useMappingMode) {
|
|
13763
|
-
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
|
|
13766
|
+
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) * (r.deviceToCanvas ? r.sy : 1);
|
|
13764
13767
|
}
|
|
13765
13768
|
return ph * r.sy;
|
|
13766
13769
|
}
|
|
13770
|
+
function gdiDevicePixelX(r) {
|
|
13771
|
+
return !r.useMappingMode || r.deviceToCanvas ? r.sx : 1;
|
|
13772
|
+
}
|
|
13773
|
+
function gdiDevicePixelY(r) {
|
|
13774
|
+
return !r.useMappingMode || r.deviceToCanvas ? r.sy : 1;
|
|
13775
|
+
}
|
|
13767
13776
|
function activateGdiMappingMode(r) {
|
|
13768
13777
|
r.useMappingMode = true;
|
|
13769
13778
|
}
|
|
@@ -13776,13 +13785,14 @@ function gdiDeviceMatrix(r) {
|
|
|
13776
13785
|
if (r.useMappingMode) {
|
|
13777
13786
|
const kx = (r.viewportExt.cx || 1) / (r.windowExt.cx || 1);
|
|
13778
13787
|
const ky = (r.viewportExt.cy || 1) / (r.windowExt.cy || 1);
|
|
13788
|
+
const [cx, cy, ox, oy] = r.deviceToCanvas ? [r.sx, r.sy, r.bounds.left, r.bounds.top] : [1, 1, 0, 0];
|
|
13779
13789
|
return [
|
|
13780
|
-
wt[0] * kx,
|
|
13781
|
-
wt[1] * ky,
|
|
13782
|
-
wt[2] * kx,
|
|
13783
|
-
wt[3] * ky,
|
|
13784
|
-
(wt[4] - r.windowOrg.x) * kx + r.viewportOrg.x,
|
|
13785
|
-
(wt[5] - r.windowOrg.y) * ky + r.viewportOrg.y
|
|
13790
|
+
wt[0] * kx * cx,
|
|
13791
|
+
wt[1] * ky * cy,
|
|
13792
|
+
wt[2] * kx * cx,
|
|
13793
|
+
wt[3] * ky * cy,
|
|
13794
|
+
((wt[4] - r.windowOrg.x) * kx + r.viewportOrg.x - ox) * cx,
|
|
13795
|
+
((wt[5] - r.windowOrg.y) * ky + r.viewportOrg.y - oy) * cy
|
|
13786
13796
|
];
|
|
13787
13797
|
}
|
|
13788
13798
|
const sx = r.sx || 1;
|
|
@@ -15657,8 +15667,8 @@ function paintWithRop2PerPixel(ctx, rop2, color, draw, pad = 2, fillRule) {
|
|
|
15657
15667
|
sc.restore();
|
|
15658
15668
|
}
|
|
15659
15669
|
const pointTest = fillRule !== void 0 && typeof sc.isPointInPath === "function";
|
|
15660
|
-
const
|
|
15661
|
-
const fixedP =
|
|
15670
|
+
const hex5 = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
15671
|
+
const fixedP = hex5 ? parseInt(hex5[1], 16) : -1;
|
|
15662
15672
|
const paint = canvasGetImageData(sc, 0, 0, box.w, box.h);
|
|
15663
15673
|
const pd = paint.data;
|
|
15664
15674
|
const dd = index === 240 ? null : canvasGetImageData(ctx, box.x, box.y, box.w, box.h).data;
|
|
@@ -17957,7 +17967,7 @@ function writeTextureColor(inv2, width, height, rgba, wrap, halfPixel2, dx, dy,
|
|
|
17957
17967
|
const iv = Math.floor(cv + 1e-9);
|
|
17958
17968
|
const fu = Math.max(0, cu - iu);
|
|
17959
17969
|
const fv = Math.max(0, cv - iv);
|
|
17960
|
-
const
|
|
17970
|
+
const clamp2 = wrap === "clamp";
|
|
17961
17971
|
const mirrorX = wrap === "tile-flip-x" || wrap === "tile-flip-xy";
|
|
17962
17972
|
const mirrorY = wrap === "tile-flip-y" || wrap === "tile-flip-xy";
|
|
17963
17973
|
let r = 0;
|
|
@@ -17973,7 +17983,7 @@ function writeTextureColor(inv2, width, height, rgba, wrap, halfPixel2, dx, dy,
|
|
|
17973
17983
|
}
|
|
17974
17984
|
let x;
|
|
17975
17985
|
let y;
|
|
17976
|
-
if (
|
|
17986
|
+
if (clamp2) {
|
|
17977
17987
|
if (tu < 0 || tv < 0 || tu >= width || tv >= height) {
|
|
17978
17988
|
continue;
|
|
17979
17989
|
}
|
|
@@ -19165,7 +19175,7 @@ function paintBrushThroughMask(rCtx, sampler, box, drawCoverage, mode = "canvas"
|
|
|
19165
19175
|
const device = plusWorldMatrix(rCtx);
|
|
19166
19176
|
const m = mask.ctx;
|
|
19167
19177
|
const shift = aliased ? aliasedSampleShift(rCtx) : geometry ? plusCanvasShift(rCtx) : 0;
|
|
19168
|
-
m.setTransform(device[0], device[1], device[2], device[3], device[4] - box.x + shift
|
|
19178
|
+
m.setTransform(device[0], device[1], device[2], device[3], device[4] - box.x + shift, device[5] - box.y + shift);
|
|
19169
19179
|
m.fillStyle = "#000";
|
|
19170
19180
|
m.strokeStyle = "#000";
|
|
19171
19181
|
drawCoverage(rasterMode !== "canvas" ? flatteningContext(m, device) : m);
|
|
@@ -20355,8 +20365,8 @@ function paintParallelogram(rCtx, a, b, q, sw, sh, pixel) {
|
|
|
20355
20365
|
});
|
|
20356
20366
|
}
|
|
20357
20367
|
function alignMirroredDest(rCtx, req) {
|
|
20358
|
-
const pxX = rCtx
|
|
20359
|
-
const pxY = rCtx
|
|
20368
|
+
const pxX = gdiDevicePixelX(rCtx);
|
|
20369
|
+
const pxY = gdiDevicePixelY(rCtx);
|
|
20360
20370
|
return {
|
|
20361
20371
|
...req,
|
|
20362
20372
|
dx: req.dw < 0 ? req.dx + pxX : req.dx,
|
|
@@ -20591,10 +20601,10 @@ function axisMap(rCtx, xDest, yDest, cxDest, cyDest, sx, sy, sw, sh) {
|
|
|
20591
20601
|
const dw = gmw(rCtx, cxDest);
|
|
20592
20602
|
const dh = gmh(rCtx, cyDest);
|
|
20593
20603
|
if (dw < 0) {
|
|
20594
|
-
dx += rCtx
|
|
20604
|
+
dx += gdiDevicePixelX(rCtx);
|
|
20595
20605
|
}
|
|
20596
20606
|
if (dh < 0) {
|
|
20597
|
-
dy += rCtx
|
|
20607
|
+
dy += gdiDevicePixelY(rCtx);
|
|
20598
20608
|
}
|
|
20599
20609
|
const W = Math.round(Math.abs(dw));
|
|
20600
20610
|
const H = Math.round(Math.abs(dh));
|
|
@@ -23523,6 +23533,456 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
|
|
|
23523
23533
|
}
|
|
23524
23534
|
}
|
|
23525
23535
|
|
|
23536
|
+
// src/emf-plus-image-effects.ts
|
|
23537
|
+
var EFFECT_GUIDS = {
|
|
23538
|
+
"633C80A4-1843-482B-9EF2-BE2834C5FDD4": "blur",
|
|
23539
|
+
"D3A1DBE1-8EC4-4C17-9F4C-EA97AD1C343D": "brightnessContrast",
|
|
23540
|
+
"537E597D-251E-48DA-9664-29CA496B70F8": "colorBalance",
|
|
23541
|
+
"DD6A0022-58E4-4A67-9D9B-D48EB881A53D": "colorCurve",
|
|
23542
|
+
"A7CE72A9-0F7F-40D7-B3CC-D0C02D5C3212": "colorLookupTable",
|
|
23543
|
+
"718F2615-7933-40E3-A511-5F68FE14DD74": "colorMatrix",
|
|
23544
|
+
"8B2DD6C3-EB07-4D87-A5F0-7108E26A9C5F": "hueSaturationLightness",
|
|
23545
|
+
"99C354EC-2A31-4F3A-8C34-17A803B33A25": "levels",
|
|
23546
|
+
"74D29D05-69A4-4266-9549-3CC52836B632": "redEyeCorrection",
|
|
23547
|
+
"63CBF3EE-C526-402C-8F71-62C540BF5142": "sharpen",
|
|
23548
|
+
"1077AF00-2848-4441-9489-44AD4C2D7A2C": "tint"
|
|
23549
|
+
};
|
|
23550
|
+
var DRAWIMAGE_EFFECT_FLAG = 8192;
|
|
23551
|
+
var CurveAdjustment = {
|
|
23552
|
+
Exposure: 0,
|
|
23553
|
+
Density: 1,
|
|
23554
|
+
Contrast: 2,
|
|
23555
|
+
Highlight: 3,
|
|
23556
|
+
Shadow: 4,
|
|
23557
|
+
Midtone: 5,
|
|
23558
|
+
WhiteSaturation: 6,
|
|
23559
|
+
BlackSaturation: 7
|
|
23560
|
+
};
|
|
23561
|
+
var CurveChannel = { All: 0, Red: 1, Green: 2, Blue: 3 };
|
|
23562
|
+
var hex4 = (v, digits) => v.toString(16).toUpperCase().padStart(digits, "0");
|
|
23563
|
+
function readGuid(view, off) {
|
|
23564
|
+
const tail = [];
|
|
23565
|
+
for (let i = 0; i < 8; i++) {
|
|
23566
|
+
tail.push(hex4(view.getUint8(off + 8 + i), 2));
|
|
23567
|
+
}
|
|
23568
|
+
return [
|
|
23569
|
+
hex4(view.getUint32(off, true), 8),
|
|
23570
|
+
hex4(view.getUint16(off + 4, true), 4),
|
|
23571
|
+
hex4(view.getUint16(off + 6, true), 4),
|
|
23572
|
+
tail.slice(0, 2).join(""),
|
|
23573
|
+
tail.slice(2).join("")
|
|
23574
|
+
].join("-");
|
|
23575
|
+
}
|
|
23576
|
+
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
23577
|
+
function parseSerializableObject(view, dataOff, dataSize) {
|
|
23578
|
+
if (dataSize < 20) {
|
|
23579
|
+
return null;
|
|
23580
|
+
}
|
|
23581
|
+
const kind = EFFECT_GUIDS[readGuid(view, dataOff)];
|
|
23582
|
+
const size = view.getUint32(dataOff + 16, true);
|
|
23583
|
+
if (!kind || 20 + size > dataSize) {
|
|
23584
|
+
return null;
|
|
23585
|
+
}
|
|
23586
|
+
const p = dataOff + 20;
|
|
23587
|
+
const i32 = (k, lo, hi) => clamp(view.getInt32(p + 4 * k, true), lo, hi);
|
|
23588
|
+
const f323 = (k, lo, hi) => {
|
|
23589
|
+
const v = view.getFloat32(p + 4 * k, true);
|
|
23590
|
+
return Number.isFinite(v) ? clamp(v, lo, hi) : 0;
|
|
23591
|
+
};
|
|
23592
|
+
const need = (n) => size >= n;
|
|
23593
|
+
switch (kind) {
|
|
23594
|
+
case "blur":
|
|
23595
|
+
return need(8) ? { kind, radius: f323(0, 0, 255), expandEdge: view.getUint32(p + 4, true) !== 0 } : null;
|
|
23596
|
+
case "brightnessContrast":
|
|
23597
|
+
return need(8) ? { kind, brightness: i32(0, -255, 255), contrast: i32(1, -100, 100) } : null;
|
|
23598
|
+
case "colorBalance":
|
|
23599
|
+
return need(12) ? { kind, cyanRed: i32(0, -100, 100), magentaGreen: i32(1, -100, 100), yellowBlue: i32(2, -100, 100) } : null;
|
|
23600
|
+
case "colorCurve":
|
|
23601
|
+
return need(12) ? {
|
|
23602
|
+
kind,
|
|
23603
|
+
adjustment: view.getInt32(p, true),
|
|
23604
|
+
channel: view.getInt32(p + 4, true),
|
|
23605
|
+
intensity: i32(2, -255, 255)
|
|
23606
|
+
} : null;
|
|
23607
|
+
case "colorLookupTable": {
|
|
23608
|
+
if (!need(1024)) {
|
|
23609
|
+
return null;
|
|
23610
|
+
}
|
|
23611
|
+
const lut = (k) => new Uint8Array(view.buffer.slice(view.byteOffset + p + 256 * k, view.byteOffset + p + 256 * (k + 1)));
|
|
23612
|
+
return { kind, b: lut(0), g: lut(1), r: lut(2), a: lut(3) };
|
|
23613
|
+
}
|
|
23614
|
+
case "colorMatrix": {
|
|
23615
|
+
if (!need(100)) {
|
|
23616
|
+
return null;
|
|
23617
|
+
}
|
|
23618
|
+
const matrix = [];
|
|
23619
|
+
for (let k = 0; k < 25; k++) {
|
|
23620
|
+
const v = view.getFloat32(p + 4 * k, true);
|
|
23621
|
+
matrix.push(Number.isFinite(v) ? v : 0);
|
|
23622
|
+
}
|
|
23623
|
+
return { kind, matrix };
|
|
23624
|
+
}
|
|
23625
|
+
case "hueSaturationLightness":
|
|
23626
|
+
return need(12) ? { kind, hue: i32(0, -180, 180), saturation: i32(1, -100, 100), lightness: i32(2, -100, 100) } : null;
|
|
23627
|
+
case "levels":
|
|
23628
|
+
return need(12) ? { kind, highlight: i32(0, 0, 100), midtone: i32(1, -100, 100), shadow: i32(2, 0, 100) } : null;
|
|
23629
|
+
case "redEyeCorrection": {
|
|
23630
|
+
if (!need(4)) {
|
|
23631
|
+
return null;
|
|
23632
|
+
}
|
|
23633
|
+
const count = view.getInt32(p, true);
|
|
23634
|
+
if (count < 0 || 4 + 16 * count > size) {
|
|
23635
|
+
return null;
|
|
23636
|
+
}
|
|
23637
|
+
const areas = [];
|
|
23638
|
+
for (let k = 0; k < count; k++) {
|
|
23639
|
+
const o = p + 4 + 16 * k;
|
|
23640
|
+
areas.push({
|
|
23641
|
+
left: view.getInt32(o, true),
|
|
23642
|
+
top: view.getInt32(o + 4, true),
|
|
23643
|
+
right: view.getInt32(o + 8, true),
|
|
23644
|
+
bottom: view.getInt32(o + 12, true)
|
|
23645
|
+
});
|
|
23646
|
+
}
|
|
23647
|
+
return { kind, areas };
|
|
23648
|
+
}
|
|
23649
|
+
case "sharpen":
|
|
23650
|
+
return need(8) ? { kind, radius: f323(0, 0, 255), amount: f323(1, 0, 100) } : null;
|
|
23651
|
+
case "tint":
|
|
23652
|
+
return need(8) ? { kind, hue: i32(0, -180, 180), amount: i32(1, -100, 100) } : null;
|
|
23653
|
+
}
|
|
23654
|
+
}
|
|
23655
|
+
function buildLut(f) {
|
|
23656
|
+
const lut = new Uint8Array(256);
|
|
23657
|
+
for (let v = 0; v < 256; v++) {
|
|
23658
|
+
lut[v] = clamp(Math.round(f(v)), 0, 255);
|
|
23659
|
+
}
|
|
23660
|
+
return lut;
|
|
23661
|
+
}
|
|
23662
|
+
function contrastCurve(c) {
|
|
23663
|
+
const gain = c >= 100 ? 1e6 : c >= 0 ? 100 / (100 - c) : (100 + c) / 100;
|
|
23664
|
+
return (v) => (v - 127.5) * gain + 127.5;
|
|
23665
|
+
}
|
|
23666
|
+
function balanceCurve(t) {
|
|
23667
|
+
const f = t / 100;
|
|
23668
|
+
return (v) => f >= 0 ? v + (255 - v) * f : v * (1 + f);
|
|
23669
|
+
}
|
|
23670
|
+
function midtoneCurve(t) {
|
|
23671
|
+
const gamma = 2 ** (-t / 100);
|
|
23672
|
+
return (v) => 255 * (v / 255) ** gamma;
|
|
23673
|
+
}
|
|
23674
|
+
function curveAdjustmentLut(adjustment, intensity) {
|
|
23675
|
+
const t = intensity;
|
|
23676
|
+
switch (adjustment) {
|
|
23677
|
+
case CurveAdjustment.Exposure:
|
|
23678
|
+
return buildLut((v) => v * (1 + t / 255));
|
|
23679
|
+
case CurveAdjustment.Density:
|
|
23680
|
+
return buildLut((v) => v * (1 - t / 255));
|
|
23681
|
+
case CurveAdjustment.Contrast:
|
|
23682
|
+
return buildLut(contrastCurve(clamp(t, -100, 100)));
|
|
23683
|
+
case CurveAdjustment.Highlight:
|
|
23684
|
+
case CurveAdjustment.Shadow: {
|
|
23685
|
+
const high = adjustment === CurveAdjustment.Highlight;
|
|
23686
|
+
const amp = clamp(t, -100, 100) * 0.32;
|
|
23687
|
+
return buildLut((v) => {
|
|
23688
|
+
const inside = high ? v > 128 : v < 128;
|
|
23689
|
+
if (!inside) {
|
|
23690
|
+
return v;
|
|
23691
|
+
}
|
|
23692
|
+
const u = high ? (v - 128) / 127 : v / 128;
|
|
23693
|
+
return v + amp * Math.sin(Math.PI * u);
|
|
23694
|
+
});
|
|
23695
|
+
}
|
|
23696
|
+
case CurveAdjustment.Midtone:
|
|
23697
|
+
return buildLut(midtoneCurve(clamp(t, -100, 100)));
|
|
23698
|
+
case CurveAdjustment.WhiteSaturation: {
|
|
23699
|
+
const w = Math.max(1, clamp(t, 0, 255));
|
|
23700
|
+
return buildLut((v) => v * 255 / w);
|
|
23701
|
+
}
|
|
23702
|
+
case CurveAdjustment.BlackSaturation: {
|
|
23703
|
+
const b = Math.min(254, clamp(t, 0, 255));
|
|
23704
|
+
return buildLut((v) => (v - b) * 255 / (255 - b));
|
|
23705
|
+
}
|
|
23706
|
+
default:
|
|
23707
|
+
return null;
|
|
23708
|
+
}
|
|
23709
|
+
}
|
|
23710
|
+
function levelsLut(highlight, midtone, shadow) {
|
|
23711
|
+
const black = shadow / 100 * 255;
|
|
23712
|
+
const white = Math.max(black + 1, highlight / 100 * 255);
|
|
23713
|
+
const gamma = midtoneCurve(midtone);
|
|
23714
|
+
return buildLut((v) => gamma(clamp((v - black) / (white - black), 0, 1) * 255));
|
|
23715
|
+
}
|
|
23716
|
+
function applyLuts(src, r, g, b, a = null) {
|
|
23717
|
+
const out = new Uint8ClampedArray(src);
|
|
23718
|
+
for (let i = 0; i < out.length; i += 4) {
|
|
23719
|
+
if (r) out[i] = r[src[i]];
|
|
23720
|
+
if (g) out[i + 1] = g[src[i + 1]];
|
|
23721
|
+
if (b) out[i + 2] = b[src[i + 2]];
|
|
23722
|
+
if (a) out[i + 3] = a[src[i + 3]];
|
|
23723
|
+
}
|
|
23724
|
+
return out;
|
|
23725
|
+
}
|
|
23726
|
+
function applyColorMatrix(src, m) {
|
|
23727
|
+
const out = new Uint8ClampedArray(src.length);
|
|
23728
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
23729
|
+
const r = src[i] / 255;
|
|
23730
|
+
const g = src[i + 1] / 255;
|
|
23731
|
+
const b = src[i + 2] / 255;
|
|
23732
|
+
const a = src[i + 3] / 255;
|
|
23733
|
+
for (let j = 0; j < 4; j++) {
|
|
23734
|
+
const v = r * m[j] + g * m[5 + j] + b * m[10 + j] + a * m[15 + j] + m[20 + j];
|
|
23735
|
+
out[i + j] = Math.round(clamp(v, 0, 1) * 255);
|
|
23736
|
+
}
|
|
23737
|
+
}
|
|
23738
|
+
return out;
|
|
23739
|
+
}
|
|
23740
|
+
function rgbToHsl(r, g, b) {
|
|
23741
|
+
const max = Math.max(r, g, b);
|
|
23742
|
+
const min = Math.min(r, g, b);
|
|
23743
|
+
const l = (max + min) / 2;
|
|
23744
|
+
const d = max - min;
|
|
23745
|
+
if (d === 0) {
|
|
23746
|
+
return [0, 0, l];
|
|
23747
|
+
}
|
|
23748
|
+
const s = d / (1 - Math.abs(2 * l - 1));
|
|
23749
|
+
let h;
|
|
23750
|
+
if (max === r) {
|
|
23751
|
+
h = (g - b) / d % 6;
|
|
23752
|
+
} else if (max === g) {
|
|
23753
|
+
h = (b - r) / d + 2;
|
|
23754
|
+
} else {
|
|
23755
|
+
h = (r - g) / d + 4;
|
|
23756
|
+
}
|
|
23757
|
+
h *= 60;
|
|
23758
|
+
return [h < 0 ? h + 360 : h, s, l];
|
|
23759
|
+
}
|
|
23760
|
+
function hslToRgb(h, s, l) {
|
|
23761
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
23762
|
+
const hp = (h % 360 + 360) % 360 / 60;
|
|
23763
|
+
const x = c * (1 - Math.abs(hp % 2 - 1));
|
|
23764
|
+
const m = l - c / 2;
|
|
23765
|
+
const [r, g, b] = hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];
|
|
23766
|
+
return [r + m, g + m, b + m];
|
|
23767
|
+
}
|
|
23768
|
+
function applyHueSaturationLightness(src, hue, saturation, lightness) {
|
|
23769
|
+
const out = new Uint8ClampedArray(src);
|
|
23770
|
+
const push = (v, t) => t >= 0 ? v + (1 - v) * (t / 100) : v * (1 + t / 100);
|
|
23771
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
23772
|
+
const [h, s, l] = rgbToHsl(src[i] / 255, src[i + 1] / 255, src[i + 2] / 255);
|
|
23773
|
+
const [r, g, b] = hslToRgb(h + hue, push(s, saturation), push(l, lightness));
|
|
23774
|
+
out[i] = Math.round(r * 255);
|
|
23775
|
+
out[i + 1] = Math.round(g * 255);
|
|
23776
|
+
out[i + 2] = Math.round(b * 255);
|
|
23777
|
+
}
|
|
23778
|
+
return out;
|
|
23779
|
+
}
|
|
23780
|
+
var LUMA = [0.299, 0.587, 0.114];
|
|
23781
|
+
function applyTint(src, hue, amount) {
|
|
23782
|
+
const tint = hslToRgb(hue, 1, 0.5);
|
|
23783
|
+
const y = tint[0] * LUMA[0] + tint[1] * LUMA[1] + tint[2] * LUMA[2];
|
|
23784
|
+
const k = amount / 100 * 255;
|
|
23785
|
+
const add = tint.map((c) => (c - y) * k);
|
|
23786
|
+
const out = new Uint8ClampedArray(src);
|
|
23787
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
23788
|
+
out[i] = Math.round(src[i] + add[0]);
|
|
23789
|
+
out[i + 1] = Math.round(src[i + 1] + add[1]);
|
|
23790
|
+
out[i + 2] = Math.round(src[i + 2] + add[2]);
|
|
23791
|
+
}
|
|
23792
|
+
return out;
|
|
23793
|
+
}
|
|
23794
|
+
function applyRedEyeCorrection(src, width, height, areas) {
|
|
23795
|
+
const out = new Uint8ClampedArray(src);
|
|
23796
|
+
for (const a of areas) {
|
|
23797
|
+
const x0 = clamp(a.left, 0, width);
|
|
23798
|
+
const x1 = clamp(a.right, 0, width);
|
|
23799
|
+
const y0 = clamp(a.top, 0, height);
|
|
23800
|
+
const y1 = clamp(a.bottom, 0, height);
|
|
23801
|
+
for (let y = y0; y < y1; y++) {
|
|
23802
|
+
for (let x = x0; x < x1; x++) {
|
|
23803
|
+
const i = (y * width + x) * 4;
|
|
23804
|
+
const r = out[i];
|
|
23805
|
+
const g = out[i + 1];
|
|
23806
|
+
const b = out[i + 2];
|
|
23807
|
+
if (r > g && r > b) {
|
|
23808
|
+
out[i] = Math.round((g + b) / 2);
|
|
23809
|
+
}
|
|
23810
|
+
}
|
|
23811
|
+
}
|
|
23812
|
+
}
|
|
23813
|
+
return out;
|
|
23814
|
+
}
|
|
23815
|
+
function boxRadiiForSigma(sigma) {
|
|
23816
|
+
const target = sigma * sigma;
|
|
23817
|
+
let k = 0;
|
|
23818
|
+
while ((k + 1) * (k + 2) <= target) {
|
|
23819
|
+
k++;
|
|
23820
|
+
}
|
|
23821
|
+
const v = (n) => n * (n + 1) / 3;
|
|
23822
|
+
let best = 0;
|
|
23823
|
+
for (let m = 1; m <= 3; m++) {
|
|
23824
|
+
if (Math.abs((3 - m) * v(k) + m * v(k + 1) - target) < Math.abs((3 - best) * v(k) + best * v(k + 1) - target)) {
|
|
23825
|
+
best = m;
|
|
23826
|
+
}
|
|
23827
|
+
}
|
|
23828
|
+
return [0, 1, 2].map((i) => i < best ? k + 1 : k);
|
|
23829
|
+
}
|
|
23830
|
+
function boxPass(src, w, h, r, horizontal) {
|
|
23831
|
+
if (r <= 0) {
|
|
23832
|
+
return src;
|
|
23833
|
+
}
|
|
23834
|
+
const out = new Float64Array(src.length);
|
|
23835
|
+
const lines = horizontal ? h : w;
|
|
23836
|
+
const len = horizontal ? w : h;
|
|
23837
|
+
const step = horizontal ? 4 : w * 4;
|
|
23838
|
+
const norm = 1 / (2 * r + 1);
|
|
23839
|
+
for (let line = 0; line < lines; line++) {
|
|
23840
|
+
const base = horizontal ? line * w * 4 : line * 4;
|
|
23841
|
+
for (let c = 0; c < 4; c++) {
|
|
23842
|
+
const at = (p) => src[base + clamp(p, 0, len - 1) * step + c];
|
|
23843
|
+
let sum = 0;
|
|
23844
|
+
for (let p = -r; p <= r; p++) {
|
|
23845
|
+
sum += at(p);
|
|
23846
|
+
}
|
|
23847
|
+
for (let p = 0; p < len; p++) {
|
|
23848
|
+
out[base + p * step + c] = sum * norm;
|
|
23849
|
+
sum += at(p + r + 1) - at(p - r);
|
|
23850
|
+
}
|
|
23851
|
+
}
|
|
23852
|
+
}
|
|
23853
|
+
return out;
|
|
23854
|
+
}
|
|
23855
|
+
function blurFloat(src, w, h, radius) {
|
|
23856
|
+
let buf = new Float64Array(src.length);
|
|
23857
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
23858
|
+
const a = src[i + 3] / 255;
|
|
23859
|
+
buf[i] = src[i] * a;
|
|
23860
|
+
buf[i + 1] = src[i + 1] * a;
|
|
23861
|
+
buf[i + 2] = src[i + 2] * a;
|
|
23862
|
+
buf[i + 3] = src[i + 3];
|
|
23863
|
+
}
|
|
23864
|
+
for (const r of boxRadiiForSigma(radius / 2)) {
|
|
23865
|
+
buf = boxPass(buf, w, h, r, true);
|
|
23866
|
+
buf = boxPass(buf, w, h, r, false);
|
|
23867
|
+
}
|
|
23868
|
+
for (let i = 0; i < buf.length; i += 4) {
|
|
23869
|
+
const a = buf[i + 3] / 255;
|
|
23870
|
+
for (let c = 0; c < 3; c++) {
|
|
23871
|
+
buf[i + c] = a > 0 ? buf[i + c] / a : 0;
|
|
23872
|
+
}
|
|
23873
|
+
}
|
|
23874
|
+
return buf;
|
|
23875
|
+
}
|
|
23876
|
+
function applyBlur(src, w, h, radius) {
|
|
23877
|
+
const blurred = blurFloat(src, w, h, radius);
|
|
23878
|
+
const out = new Uint8ClampedArray(src.length);
|
|
23879
|
+
for (let i = 0; i < src.length; i++) {
|
|
23880
|
+
out[i] = Math.round(blurred[i]);
|
|
23881
|
+
}
|
|
23882
|
+
return out;
|
|
23883
|
+
}
|
|
23884
|
+
function applySharpen(src, w, h, radius, amount) {
|
|
23885
|
+
const blurred = blurFloat(src, w, h, radius);
|
|
23886
|
+
const k = amount / 100;
|
|
23887
|
+
const out = new Uint8ClampedArray(src);
|
|
23888
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
23889
|
+
for (let c = 0; c < 3; c++) {
|
|
23890
|
+
out[i + c] = Math.round(src[i + c] + k * (src[i + c] - blurred[i + c]));
|
|
23891
|
+
}
|
|
23892
|
+
}
|
|
23893
|
+
return out;
|
|
23894
|
+
}
|
|
23895
|
+
function applyImageEffect(rgba, width, height, effect) {
|
|
23896
|
+
if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
23897
|
+
return null;
|
|
23898
|
+
}
|
|
23899
|
+
switch (effect.kind) {
|
|
23900
|
+
case "blur":
|
|
23901
|
+
return applyBlur(rgba, width, height, effect.radius);
|
|
23902
|
+
case "sharpen":
|
|
23903
|
+
return applySharpen(rgba, width, height, effect.radius, effect.amount);
|
|
23904
|
+
case "brightnessContrast": {
|
|
23905
|
+
const contrast = contrastCurve(effect.contrast);
|
|
23906
|
+
const lut = buildLut((v) => contrast(v) + effect.brightness);
|
|
23907
|
+
return applyLuts(rgba, lut, lut, lut);
|
|
23908
|
+
}
|
|
23909
|
+
case "colorBalance":
|
|
23910
|
+
return applyLuts(
|
|
23911
|
+
rgba,
|
|
23912
|
+
buildLut(balanceCurve(effect.cyanRed)),
|
|
23913
|
+
buildLut(balanceCurve(effect.magentaGreen)),
|
|
23914
|
+
buildLut(balanceCurve(effect.yellowBlue))
|
|
23915
|
+
);
|
|
23916
|
+
case "colorCurve": {
|
|
23917
|
+
const lut = curveAdjustmentLut(effect.adjustment, effect.intensity);
|
|
23918
|
+
const ch = effect.channel;
|
|
23919
|
+
if (!lut || ch < CurveChannel.All || ch > CurveChannel.Blue) {
|
|
23920
|
+
return null;
|
|
23921
|
+
}
|
|
23922
|
+
const pick = (c) => ch === CurveChannel.All || ch === c ? lut : null;
|
|
23923
|
+
return applyLuts(rgba, pick(CurveChannel.Red), pick(CurveChannel.Green), pick(CurveChannel.Blue));
|
|
23924
|
+
}
|
|
23925
|
+
case "colorLookupTable":
|
|
23926
|
+
return applyLuts(rgba, effect.r, effect.g, effect.b, effect.a);
|
|
23927
|
+
case "colorMatrix":
|
|
23928
|
+
return applyColorMatrix(rgba, effect.matrix);
|
|
23929
|
+
case "hueSaturationLightness":
|
|
23930
|
+
return applyHueSaturationLightness(rgba, effect.hue, effect.saturation, effect.lightness);
|
|
23931
|
+
case "levels": {
|
|
23932
|
+
const lut = levelsLut(effect.highlight, effect.midtone, effect.shadow);
|
|
23933
|
+
return applyLuts(rgba, lut, lut, lut);
|
|
23934
|
+
}
|
|
23935
|
+
case "redEyeCorrection":
|
|
23936
|
+
return applyRedEyeCorrection(rgba, width, height, effect.areas);
|
|
23937
|
+
case "tint":
|
|
23938
|
+
return applyTint(rgba, effect.hue, effect.amount);
|
|
23939
|
+
}
|
|
23940
|
+
}
|
|
23941
|
+
function applyImageEffectToRect(rgba, width, height, effect, src) {
|
|
23942
|
+
if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
23943
|
+
return null;
|
|
23944
|
+
}
|
|
23945
|
+
let x0 = 0;
|
|
23946
|
+
let y0 = 0;
|
|
23947
|
+
let x1 = width;
|
|
23948
|
+
let y1 = height;
|
|
23949
|
+
if (src) {
|
|
23950
|
+
x0 = clamp(Math.floor(src.x), 0, width);
|
|
23951
|
+
y0 = clamp(Math.floor(src.y), 0, height);
|
|
23952
|
+
x1 = clamp(Math.ceil(src.x + src.w), 0, width);
|
|
23953
|
+
y1 = clamp(Math.ceil(src.y + src.h), 0, height);
|
|
23954
|
+
if (x1 <= x0 || y1 <= y0) {
|
|
23955
|
+
return null;
|
|
23956
|
+
}
|
|
23957
|
+
}
|
|
23958
|
+
const pad = src && effect.kind === "blur" && effect.expandEdge ? Math.ceil(effect.radius) : 0;
|
|
23959
|
+
const cw = x1 - x0;
|
|
23960
|
+
const ch = y1 - y0;
|
|
23961
|
+
const w = cw + 2 * pad;
|
|
23962
|
+
const h = ch + 2 * pad;
|
|
23963
|
+
let region = rgba;
|
|
23964
|
+
if (w !== width || h !== height || x0 !== 0 || y0 !== 0) {
|
|
23965
|
+
region = new Uint8ClampedArray(w * h * 4);
|
|
23966
|
+
for (let y = 0; y < ch; y++) {
|
|
23967
|
+
const from = ((y0 + y) * width + x0) * 4;
|
|
23968
|
+
region.set(rgba.subarray(from, from + cw * 4), ((pad + y) * w + pad) * 4);
|
|
23969
|
+
}
|
|
23970
|
+
}
|
|
23971
|
+
const ox = x0 - pad;
|
|
23972
|
+
const oy = y0 - pad;
|
|
23973
|
+
const local = effect.kind === "redEyeCorrection" ? {
|
|
23974
|
+
kind: effect.kind,
|
|
23975
|
+
areas: effect.areas.map((a) => ({
|
|
23976
|
+
left: a.left - ox,
|
|
23977
|
+
top: a.top - oy,
|
|
23978
|
+
right: a.right - ox,
|
|
23979
|
+
bottom: a.bottom - oy
|
|
23980
|
+
}))
|
|
23981
|
+
} : effect;
|
|
23982
|
+
const out = applyImageEffect(region, w, h, local);
|
|
23983
|
+
return out ? { rgba: out, x: ox, y: oy, width: w, height: h, pad } : null;
|
|
23984
|
+
}
|
|
23985
|
+
|
|
23526
23986
|
// src/emf-plus-draw-image.ts
|
|
23527
23987
|
function surfaceSize4(ctx) {
|
|
23528
23988
|
const canvas = ctx.canvas;
|
|
@@ -23585,8 +24045,8 @@ function svgImagePayload(img, bitmap) {
|
|
|
23585
24045
|
}
|
|
23586
24046
|
return decodeBmpFile(bytes.buffer);
|
|
23587
24047
|
}
|
|
23588
|
-
function paintBitmapSvg(svg, img, bitmap, draw) {
|
|
23589
|
-
const payload = svgImagePayload(img, bitmap);
|
|
24048
|
+
function paintBitmapSvg(svg, img, bitmap, draw, pixelsOnly = false) {
|
|
24049
|
+
const payload = pixelsOnly && bitmap ? { kind: "rgba", data: bitmap.rgba, width: bitmap.width, height: bitmap.height } : svgImagePayload(img, bitmap);
|
|
23590
24050
|
if (!payload) {
|
|
23591
24051
|
return false;
|
|
23592
24052
|
}
|
|
@@ -23658,12 +24118,70 @@ function drawNestedMetafile(rCtx, img, cached, source) {
|
|
|
23658
24118
|
rCtx.deferredImages.push(...deferred);
|
|
23659
24119
|
return true;
|
|
23660
24120
|
}
|
|
23661
|
-
function
|
|
24121
|
+
function effectedDraw(img, bitmap, effect, draw, source) {
|
|
24122
|
+
let src = bitmap;
|
|
24123
|
+
if (!src && img.data) {
|
|
24124
|
+
const bmp = decodeBmpFile(img.data.slice(0));
|
|
24125
|
+
if (bmp && bmp.kind === "rgba") {
|
|
24126
|
+
src = { rgba: bmp.data, width: bmp.width, height: bmp.height };
|
|
24127
|
+
}
|
|
24128
|
+
}
|
|
24129
|
+
if (!src) {
|
|
24130
|
+
return null;
|
|
24131
|
+
}
|
|
24132
|
+
const rect = source && source.unit === UNIT_PIXEL && source.srcW > 0 && source.srcH > 0 && [source.srcX, source.srcY, source.srcW, source.srcH].every(Number.isFinite) ? source : null;
|
|
24133
|
+
const region = applyImageEffectToRect(
|
|
24134
|
+
src.rgba,
|
|
24135
|
+
src.width,
|
|
24136
|
+
src.height,
|
|
24137
|
+
effect,
|
|
24138
|
+
rect && { x: rect.srcX, y: rect.srcY, w: rect.srcW, h: rect.srcH }
|
|
24139
|
+
);
|
|
24140
|
+
if (!region) {
|
|
24141
|
+
return null;
|
|
24142
|
+
}
|
|
24143
|
+
const out = { kind: "bitmap", width: region.width, height: region.height, rgba: region.rgba };
|
|
24144
|
+
if (!rect) {
|
|
24145
|
+
return { bitmap: out, draw };
|
|
24146
|
+
}
|
|
24147
|
+
const grow = region.pad;
|
|
24148
|
+
const srcX = rect.srcX - grow - region.x;
|
|
24149
|
+
const srcY = rect.srcY - grow - region.y;
|
|
24150
|
+
const srcW = rect.srcW + 2 * grow;
|
|
24151
|
+
const srcH = rect.srcH + 2 * grow;
|
|
24152
|
+
const shift = [1, 0, 0, 1, region.x, region.y];
|
|
24153
|
+
const toWorld = mulMatrix(rect.toWorld(rect.srcX, rect.srcY, rect.srcW, rect.srcH), shift);
|
|
24154
|
+
return {
|
|
24155
|
+
bitmap: out,
|
|
24156
|
+
draw: {
|
|
24157
|
+
...draw,
|
|
24158
|
+
// The Canvas fallback scales the whole effected bitmap.
|
|
24159
|
+
transform: mulMatrix(draw.transform, toWorld),
|
|
24160
|
+
dx: 0,
|
|
24161
|
+
dy: 0,
|
|
24162
|
+
dw: region.width,
|
|
24163
|
+
dh: region.height,
|
|
24164
|
+
resample: draw.resample && {
|
|
24165
|
+
...draw.resample,
|
|
24166
|
+
srcX,
|
|
24167
|
+
srcY,
|
|
24168
|
+
srcW,
|
|
24169
|
+
srcH,
|
|
24170
|
+
toDevice: mulMatrix(draw.resample.toDevice, shift)
|
|
24171
|
+
}
|
|
24172
|
+
}
|
|
24173
|
+
};
|
|
24174
|
+
}
|
|
24175
|
+
function drawEmfPlusImageNow(rCtx, img, draw, source, effect = null) {
|
|
23662
24176
|
const cached = img.cacheKey !== void 0 ? rCtx.imageCache?.get(img.cacheKey) : void 0;
|
|
23663
24177
|
if (img.type === 2) {
|
|
23664
24178
|
return drawNestedMetafile(rCtx, img, cached, source);
|
|
23665
24179
|
}
|
|
23666
24180
|
const bitmap = cached && cached.kind === "bitmap" ? cached : null;
|
|
24181
|
+
const effected = effect ? effectedDraw(img, bitmap, effect, draw, source) : null;
|
|
24182
|
+
if (effected) {
|
|
24183
|
+
return isSvgContext(rCtx.ctx) ? paintBitmapSvg(rCtx.ctx, img, effected.bitmap, effected.draw, true) : paintBitmapRaster(rCtx.ctx, effected.bitmap, effected.draw);
|
|
24184
|
+
}
|
|
23667
24185
|
if (isSvgContext(rCtx.ctx)) {
|
|
23668
24186
|
return paintBitmapSvg(rCtx.ctx, img, bitmap, draw);
|
|
23669
24187
|
}
|
|
@@ -23918,20 +24436,27 @@ function applyPlusPenStyle(ctx, pen, widthScale = 1) {
|
|
|
23918
24436
|
}
|
|
23919
24437
|
function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false) {
|
|
23920
24438
|
const { ctx } = rCtx;
|
|
23921
|
-
|
|
24439
|
+
const resolved = resolvePenTransform(pen);
|
|
24440
|
+
pen = resolved.pen;
|
|
24441
|
+
const nib = resolved.nib;
|
|
23922
24442
|
const inset = !!pen && pen.alignment === 1 && closedFigure;
|
|
23923
24443
|
const widthScale = inset ? 2 : 1;
|
|
23924
24444
|
const stroke = (c) => {
|
|
23925
|
-
if (inset) {
|
|
24445
|
+
if (inset || nib) {
|
|
23926
24446
|
c.save();
|
|
24447
|
+
}
|
|
24448
|
+
if (inset) {
|
|
23927
24449
|
c.beginPath();
|
|
23928
24450
|
buildPath(c);
|
|
23929
24451
|
c.clip();
|
|
23930
24452
|
}
|
|
23931
24453
|
c.beginPath();
|
|
23932
24454
|
buildPath(c);
|
|
24455
|
+
if (nib) {
|
|
24456
|
+
c.transform(nib[0], nib[1], nib[2], nib[3], 0, 0);
|
|
24457
|
+
}
|
|
23933
24458
|
c.stroke();
|
|
23934
|
-
if (inset) {
|
|
24459
|
+
if (inset || nib) {
|
|
23935
24460
|
c.restore();
|
|
23936
24461
|
}
|
|
23937
24462
|
};
|
|
@@ -23946,97 +24471,138 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
|
|
|
23946
24471
|
if (sampler) {
|
|
23947
24472
|
const size = surfaceSize5(ctx);
|
|
23948
24473
|
const device = plusWorldMatrix(rCtx);
|
|
23949
|
-
const scale =
|
|
23950
|
-
if (mode !== "canvas" && size && strokeGdiplus(rCtx, pen, sampler, buildPath, device, size, mode, closedFigure)) {
|
|
24474
|
+
const scale = maxStretch(nib ? mulMatrix(device, nib) : device);
|
|
24475
|
+
if (mode !== "canvas" && size && strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, closedFigure)) {
|
|
23951
24476
|
return;
|
|
23952
24477
|
}
|
|
23953
|
-
const
|
|
23954
|
-
|
|
23955
|
-
|
|
24478
|
+
const outline = hasCustomCap(pen) ? penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) : null;
|
|
24479
|
+
let box = null;
|
|
24480
|
+
if (size && outline) {
|
|
24481
|
+
box = deviceBounds(outline.flat(), device, size, 2);
|
|
24482
|
+
} else if (size) {
|
|
24483
|
+
const miter = Math.max(1, ctx.miterLimit || 10);
|
|
24484
|
+
const margin = pen.width * scale * miter / 2 + 2;
|
|
24485
|
+
box = deviceBounds(points, device, size, margin);
|
|
24486
|
+
}
|
|
23956
24487
|
if (size && !box) {
|
|
23957
24488
|
return;
|
|
23958
24489
|
}
|
|
23959
|
-
|
|
23960
|
-
|
|
23961
|
-
|
|
23962
|
-
|
|
23963
|
-
(c
|
|
23964
|
-
|
|
23965
|
-
|
|
23966
|
-
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
|
|
23971
|
-
))
|
|
24490
|
+
const drawMask = outline ? (c) => {
|
|
24491
|
+
traceOutline(c, outline);
|
|
24492
|
+
c.fill("nonzero");
|
|
24493
|
+
} : (c) => {
|
|
24494
|
+
applyPlusPenStyle(c, pen, widthScale);
|
|
24495
|
+
stroke(c);
|
|
24496
|
+
c.beginPath();
|
|
24497
|
+
buildPath(c);
|
|
24498
|
+
if (nib) {
|
|
24499
|
+
c.transform(nib[0], nib[1], nib[2], nib[3], 0, 0);
|
|
24500
|
+
}
|
|
24501
|
+
};
|
|
24502
|
+
const hitTest = outline ? (c, x, y) => c.isPointInPath(x, y, "nonzero") : (c, x, y) => c.isPointInStroke(x, y) && (!inset || c.isPointInPath(x, y));
|
|
24503
|
+
if (box && paintBrushThroughMask(rCtx, sampler, box, drawMask, mode, hitTest)) {
|
|
23972
24504
|
return;
|
|
23973
24505
|
}
|
|
23974
24506
|
}
|
|
23975
|
-
|
|
24507
|
+
const paint = pen.brush ? brushPaint2(rCtx, pen.brush) : pen.color;
|
|
24508
|
+
if ((hasCustomCap(pen) || nib && typeof paint !== "string") && fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure)) {
|
|
23976
24509
|
return;
|
|
23977
24510
|
}
|
|
23978
|
-
ctx.strokeStyle =
|
|
24511
|
+
ctx.strokeStyle = paint;
|
|
23979
24512
|
}
|
|
23980
24513
|
applyPlusWorldTransform(rCtx);
|
|
23981
24514
|
stroke(ctx);
|
|
23982
24515
|
}
|
|
23983
|
-
function
|
|
24516
|
+
function resolvePenTransform(pen) {
|
|
23984
24517
|
if (!pen?.transform) {
|
|
23985
|
-
return pen;
|
|
23986
|
-
}
|
|
23987
|
-
if (isIdentity2(pen.transform)) {
|
|
23988
|
-
return { ...pen, transform: null };
|
|
24518
|
+
return { pen, nib: null };
|
|
23989
24519
|
}
|
|
23990
24520
|
const [a, b, c, d] = pen.transform;
|
|
23991
24521
|
const sx = Math.hypot(a, b);
|
|
23992
24522
|
const sy = Math.hypot(c, d);
|
|
23993
24523
|
const dot = a * c + b * d;
|
|
23994
|
-
|
|
23995
|
-
|
|
24524
|
+
const det = a * d - b * c;
|
|
24525
|
+
const plain = { ...pen, transform: null };
|
|
24526
|
+
if (pen.width === 0 || isIdentity2(pen.transform) || !Number.isFinite(sx + sy + dot + det) || Math.abs(det) < 1e-12) {
|
|
24527
|
+
return { pen: { ...plain, width: pen.width === 0 ? 1 : pen.width }, nib: null };
|
|
23996
24528
|
}
|
|
23997
|
-
|
|
23998
|
-
}
|
|
23999
|
-
function strokeGdiplus(rCtx, pen, sampler, buildPath, device, size, mode, closedFigure) {
|
|
24000
|
-
if (pen.transform && !isIdentity2(pen.transform)) {
|
|
24001
|
-
return false;
|
|
24529
|
+
if (Math.abs(sx - sy) <= 1e-6 * sx && Math.abs(dot) <= 1e-6 * sx * sy) {
|
|
24530
|
+
return { pen: { ...plain, width: pen.width * sx }, nib: null };
|
|
24002
24531
|
}
|
|
24003
|
-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
|
|
24013
|
-
return false;
|
|
24532
|
+
return { pen: plain, nib: [a, b, c, d, 0, 0] };
|
|
24533
|
+
}
|
|
24534
|
+
function maxStretch(m) {
|
|
24535
|
+
const s = m[0] * m[0] + m[1] * m[1] + m[2] * m[2] + m[3] * m[3];
|
|
24536
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
24537
|
+
return Math.sqrt((s + Math.sqrt(Math.max(0, s * s - 4 * det * det))) / 2);
|
|
24538
|
+
}
|
|
24539
|
+
function penFrame(device, nib) {
|
|
24540
|
+
if (!nib) {
|
|
24541
|
+
return { record: device, unit: Math.hypot(device[0], device[1]), toDevice: IDENTITY5 };
|
|
24014
24542
|
}
|
|
24015
|
-
|
|
24016
|
-
|
|
24543
|
+
const full = mulMatrix(device, nib);
|
|
24544
|
+
const unit = maxStretch(full);
|
|
24545
|
+
const inv2 = invertAffine(nib);
|
|
24546
|
+
if (!inv2 || !(unit > 0) || !Number.isFinite(unit)) {
|
|
24547
|
+
return null;
|
|
24017
24548
|
}
|
|
24018
|
-
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24549
|
+
return {
|
|
24550
|
+
record: [inv2[0] * unit, inv2[1] * unit, inv2[2] * unit, inv2[3] * unit, 0, 0],
|
|
24551
|
+
unit,
|
|
24552
|
+
toDevice: mulMatrix(full, [1 / unit, 0, 0, 1 / unit, 0, 0])
|
|
24553
|
+
};
|
|
24554
|
+
}
|
|
24555
|
+
function framePen(pen, unit) {
|
|
24556
|
+
const dash = penDashArray(pen).map((v) => v * unit);
|
|
24557
|
+
return {
|
|
24558
|
+
half: (pen.width || 1) * unit / 2,
|
|
24022
24559
|
join: pen.lineJoin ?? 0,
|
|
24023
24560
|
miterLimit: pen.miterLimit && pen.miterLimit >= 1 ? pen.miterLimit : 10,
|
|
24024
24561
|
startCap: pen.startCap ?? 0,
|
|
24025
24562
|
endCap: pen.endCap ?? 0,
|
|
24026
24563
|
dashCap: pen.dashCap ?? 0,
|
|
24027
24564
|
dash: dash.length > 0 ? dash : null,
|
|
24028
|
-
dashOffset: (pen.dashOffset ?? 0) * (pen.width || 1) *
|
|
24565
|
+
dashOffset: (pen.dashOffset ?? 0) * (pen.width || 1) * unit,
|
|
24029
24566
|
compound: pen.compound ?? null,
|
|
24030
24567
|
inset: pen.alignment === 1
|
|
24031
24568
|
};
|
|
24569
|
+
}
|
|
24570
|
+
function strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, closedFigure) {
|
|
24571
|
+
if (!nib) {
|
|
24572
|
+
const sx = Math.hypot(device[0], device[1]);
|
|
24573
|
+
const sy = Math.hypot(device[2], device[3]);
|
|
24574
|
+
const dot = device[0] * device[2] + device[1] * device[3];
|
|
24575
|
+
if (!(sx > 0) || Math.abs(sx - sy) > 1e-6 * sx || Math.abs(dot) > 1e-6 * sx * sy) {
|
|
24576
|
+
return false;
|
|
24577
|
+
}
|
|
24578
|
+
}
|
|
24579
|
+
const frame2 = penFrame(device, nib);
|
|
24580
|
+
if (!frame2) {
|
|
24581
|
+
return false;
|
|
24582
|
+
}
|
|
24583
|
+
let figures;
|
|
24584
|
+
try {
|
|
24585
|
+
figures = recordDeviceFigures(buildPath, frame2.record);
|
|
24586
|
+
} catch {
|
|
24587
|
+
return false;
|
|
24588
|
+
}
|
|
24589
|
+
if (closedFigure) {
|
|
24590
|
+
figures = figures.map((f) => ({ ...f, closed: true }));
|
|
24591
|
+
}
|
|
24592
|
+
const width = (pen.width || 1) * frame2.unit;
|
|
24593
|
+
const devicePen = framePen(pen, frame2.unit);
|
|
24032
24594
|
const antialias = mode === "gdiplus-aa";
|
|
24595
|
+
const toFix = (poly) => {
|
|
24596
|
+
const m = frame2.toDevice;
|
|
24597
|
+
return poly.flatMap((p) => [toPlusFix(m[0] * p.x + m[2] * p.y + m[4]), toPlusFix(m[1] * p.x + m[3] * p.y + m[5])]);
|
|
24598
|
+
};
|
|
24033
24599
|
let fix;
|
|
24034
24600
|
if (hasCustomCap(pen)) {
|
|
24035
|
-
fix = customCapOutline(figures, width, devicePen, pen).map(
|
|
24601
|
+
fix = customCapOutline(figures, width, devicePen, pen).map(toFix);
|
|
24036
24602
|
} else if (width <= NOMINAL_PEN_MAX && !devicePen.dash && !(((pen.startCap ?? 0) | (pen.endCap ?? 0) | (pen.dashCap ?? 0)) & 240)) {
|
|
24037
24603
|
return strokeNominal(rCtx, pen, sampler, buildPath, device, size, antialias, closedFigure);
|
|
24038
24604
|
} else {
|
|
24039
|
-
fix = widenFigures(figures, devicePen).map(
|
|
24605
|
+
fix = widenFigures(figures, devicePen).map(toFix);
|
|
24040
24606
|
}
|
|
24041
24607
|
const box = figuresBox(fix, size);
|
|
24042
24608
|
if (!box) {
|
|
@@ -24099,68 +24665,59 @@ function customCapOutline(figures, width, devicePen, pen) {
|
|
|
24099
24665
|
outline.push(...geo.polygons);
|
|
24100
24666
|
return outline;
|
|
24101
24667
|
}
|
|
24102
|
-
function
|
|
24668
|
+
function penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) {
|
|
24103
24669
|
const device = plusWorldMatrix(rCtx);
|
|
24104
|
-
const
|
|
24105
|
-
const
|
|
24106
|
-
if (!
|
|
24107
|
-
return
|
|
24670
|
+
const frame2 = penFrame(device, nib);
|
|
24671
|
+
const deviceInv = invertAffine(device);
|
|
24672
|
+
if (!frame2 || !deviceInv || !(frame2.unit > 0)) {
|
|
24673
|
+
return null;
|
|
24108
24674
|
}
|
|
24109
24675
|
let figures;
|
|
24110
24676
|
try {
|
|
24111
|
-
figures = recordDeviceFigures(buildPath,
|
|
24677
|
+
figures = recordDeviceFigures(buildPath, frame2.record);
|
|
24112
24678
|
} catch {
|
|
24113
|
-
return
|
|
24679
|
+
return null;
|
|
24114
24680
|
}
|
|
24115
24681
|
if (closedFigure) {
|
|
24116
24682
|
figures = figures.map((f) => ({ ...f, closed: true }));
|
|
24117
24683
|
}
|
|
24118
|
-
const width = (pen.width || 1) *
|
|
24119
|
-
const
|
|
24120
|
-
const
|
|
24121
|
-
|
|
24122
|
-
|
|
24123
|
-
|
|
24124
|
-
|
|
24125
|
-
|
|
24126
|
-
|
|
24127
|
-
|
|
24128
|
-
|
|
24129
|
-
|
|
24130
|
-
|
|
24131
|
-
|
|
24132
|
-
|
|
24133
|
-
|
|
24134
|
-
|
|
24135
|
-
|
|
24136
|
-
|
|
24137
|
-
|
|
24138
|
-
|
|
24139
|
-
|
|
24684
|
+
const width = (pen.width || 1) * frame2.unit;
|
|
24685
|
+
const devicePen = framePen(pen, frame2.unit);
|
|
24686
|
+
const outline = hasCustomCap(pen) ? customCapOutline(figures, width, devicePen, pen) : widenFigures(figures, devicePen);
|
|
24687
|
+
const m = mulMatrix(deviceInv, frame2.toDevice);
|
|
24688
|
+
return outline.map((poly) => poly.map((p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] })));
|
|
24689
|
+
}
|
|
24690
|
+
function traceOutline(c, outline) {
|
|
24691
|
+
c.beginPath();
|
|
24692
|
+
for (const poly of outline) {
|
|
24693
|
+
poly.forEach((p, i) => {
|
|
24694
|
+
if (i === 0) {
|
|
24695
|
+
c.moveTo(p.x, p.y);
|
|
24696
|
+
} else {
|
|
24697
|
+
c.lineTo(p.x, p.y);
|
|
24698
|
+
}
|
|
24699
|
+
});
|
|
24700
|
+
c.closePath();
|
|
24701
|
+
}
|
|
24702
|
+
}
|
|
24703
|
+
function fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure) {
|
|
24704
|
+
const outline = penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure);
|
|
24705
|
+
if (!outline) {
|
|
24706
|
+
return false;
|
|
24707
|
+
}
|
|
24140
24708
|
const { ctx } = rCtx;
|
|
24141
24709
|
ctx.save();
|
|
24142
24710
|
try {
|
|
24143
|
-
ctx.fillStyle =
|
|
24711
|
+
ctx.fillStyle = paint;
|
|
24144
24712
|
applyPlusWorldTransform(rCtx);
|
|
24145
|
-
ctx
|
|
24146
|
-
for (const poly of customCapOutline(figures, width, devicePen, pen)) {
|
|
24147
|
-
poly.forEach((p, i) => {
|
|
24148
|
-
const x = inv2[0] * p.x + inv2[2] * p.y + inv2[4];
|
|
24149
|
-
const y = inv2[1] * p.x + inv2[3] * p.y + inv2[5];
|
|
24150
|
-
if (i === 0) {
|
|
24151
|
-
ctx.moveTo(x, y);
|
|
24152
|
-
} else {
|
|
24153
|
-
ctx.lineTo(x, y);
|
|
24154
|
-
}
|
|
24155
|
-
});
|
|
24156
|
-
ctx.closePath();
|
|
24157
|
-
}
|
|
24713
|
+
traceOutline(ctx, outline);
|
|
24158
24714
|
ctx.fill("nonzero");
|
|
24159
24715
|
} finally {
|
|
24160
24716
|
ctx.restore();
|
|
24161
24717
|
}
|
|
24162
24718
|
return true;
|
|
24163
24719
|
}
|
|
24720
|
+
var IDENTITY5 = [1, 0, 0, 1, 0, 0];
|
|
24164
24721
|
function isIdentity2(m) {
|
|
24165
24722
|
return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
|
|
24166
24723
|
}
|
|
@@ -24213,7 +24770,7 @@ function imageResampleSpec(rCtx, dataOff, isMetafile, toWorld) {
|
|
|
24213
24770
|
...imageAttributesWrap(rCtx, view.getUint32(dataOff, true))
|
|
24214
24771
|
};
|
|
24215
24772
|
}
|
|
24216
|
-
function drawOrDeferImage(rCtx, imgObj, dataOff, dx, dy, dw, dh, toWorld) {
|
|
24773
|
+
function drawOrDeferImage(rCtx, imgObj, dataOff, dx, dy, dw, dh, toWorld, effect = null) {
|
|
24217
24774
|
if (!imgObj.data) {
|
|
24218
24775
|
return;
|
|
24219
24776
|
}
|
|
@@ -24237,7 +24794,7 @@ function drawOrDeferImage(rCtx, imgObj, dataOff, dx, dy, dw, dh, toWorld) {
|
|
|
24237
24794
|
srcH: view.getFloat32(dataOff + 20, true),
|
|
24238
24795
|
toWorld
|
|
24239
24796
|
};
|
|
24240
|
-
if (drawEmfPlusImageNow(rCtx, imgObj, draw, source)) {
|
|
24797
|
+
if (drawEmfPlusImageNow(rCtx, imgObj, draw, source, effect)) {
|
|
24241
24798
|
return;
|
|
24242
24799
|
}
|
|
24243
24800
|
if (isSvgContext(rCtx.ctx)) {
|
|
@@ -24601,6 +25158,10 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
|
|
|
24601
25158
|
const imgObj = objectTable.get(imgId);
|
|
24602
25159
|
const count = view.getUint32(dataOff + 24, true);
|
|
24603
25160
|
const ptOff = dataOff + 28;
|
|
25161
|
+
const effect = (recFlags & DRAWIMAGE_EFFECT_FLAG) !== 0 ? rCtx.ext?.pendingEffect ?? null : null;
|
|
25162
|
+
if (rCtx.ext) {
|
|
25163
|
+
rCtx.ext.pendingEffect = null;
|
|
25164
|
+
}
|
|
24604
25165
|
if (count >= 3 && imgObj && imgObj.kind === "plus-image" && imgObj.data) {
|
|
24605
25166
|
const pts = readPlusPoints(view, ptOff, dataOff + recDataSize, 3, recFlags);
|
|
24606
25167
|
if (!pts) {
|
|
@@ -24624,7 +25185,7 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
|
|
|
24624
25185
|
const c = (p3x - p1x) / sh;
|
|
24625
25186
|
const d = (p3y - p1y) / sh;
|
|
24626
25187
|
return [a, b, c, d, p1x - a * sx - c * sy, p1y - b * sx - d * sy];
|
|
24627
|
-
});
|
|
25188
|
+
}, effect);
|
|
24628
25189
|
} else {
|
|
24629
25190
|
imgObj && imgObj.kind === "plus-image" && imgObj.data;
|
|
24630
25191
|
}
|
|
@@ -25454,12 +26015,14 @@ function replayEmfPlusRecords(view, offset, length, ctx, canvasW, canvasH, state
|
|
|
25454
26015
|
recordCount++;
|
|
25455
26016
|
emfPlusRecordTypes.set(recType, (emfPlusRecordTypes.get(recType) ?? 0) + 1);
|
|
25456
26017
|
const dataOff = offset + 12;
|
|
26018
|
+
s.gdiPassthrough = recType === EMFPLUS_GETDC;
|
|
25457
26019
|
if (rCtx.ext?.multiFormatSkip && recType !== EMFPLUS_ENDOFFILE) {
|
|
25458
26020
|
offset += recSize;
|
|
25459
26021
|
continue;
|
|
25460
26022
|
}
|
|
25461
26023
|
switch (recType) {
|
|
25462
26024
|
case EMFPLUS_HEADER: {
|
|
26025
|
+
s.dualMode = (recFlags & 1) !== 0;
|
|
25463
26026
|
if (recDataSize >= 16) {
|
|
25464
26027
|
view.getFloat32(dataOff + 8, true);
|
|
25465
26028
|
view.getFloat32(dataOff + 12, true);
|
|
@@ -25478,6 +26041,12 @@ function replayEmfPlusRecords(view, offset, length, ctx, canvasW, canvasH, state
|
|
|
25478
26041
|
case EMFPLUS_MULTIFORMATSECTION:
|
|
25479
26042
|
case EMFPLUS_MULTIFORMATEND:
|
|
25480
26043
|
break;
|
|
26044
|
+
// An image effect for the next DrawImagePoints with flag E
|
|
26045
|
+
// (`emf-plus-image-effects.ts`); an unknown or malformed effect
|
|
26046
|
+
// replaces any earlier one with none.
|
|
26047
|
+
case EMFPLUS_SERIALIZABLEOBJECT:
|
|
26048
|
+
(rCtx.ext ?? (rCtx.ext = {})).pendingEffect = parseSerializableObject(view, dataOff, recDataSize);
|
|
26049
|
+
break;
|
|
25481
26050
|
case EMFPLUS_CLEAR: {
|
|
25482
26051
|
if (recDataSize >= 4) {
|
|
25483
26052
|
const argb = view.getUint32(dataOff, true);
|
|
@@ -25624,11 +26193,16 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, rep
|
|
|
25624
26193
|
state: { ...defaultState(), fontFamilyMap: replayOptions.fontFamilyMap },
|
|
25625
26194
|
stateStack: [],
|
|
25626
26195
|
inPath: false,
|
|
25627
|
-
|
|
26196
|
+
// GDI's defaults: both origins at 0 and a 1:1 window/viewport ratio.
|
|
26197
|
+
// The window/viewport mapping yields device units, placed on the
|
|
26198
|
+
// canvas like every other device coordinate (bounds' top-left to the
|
|
26199
|
+
// canvas origin, scaled by sx/sy); see `deviceToCanvas`.
|
|
26200
|
+
windowOrg: { x: 0, y: 0 },
|
|
25628
26201
|
windowExt: { cx: logicalW, cy: logicalH },
|
|
25629
26202
|
viewportOrg: { x: 0, y: 0 },
|
|
25630
|
-
viewportExt: { cx:
|
|
26203
|
+
viewportExt: { cx: logicalW, cy: logicalH },
|
|
25631
26204
|
useMappingMode: false,
|
|
26205
|
+
deviceToCanvas: true,
|
|
25632
26206
|
clipSaveDepth: 0,
|
|
25633
26207
|
bounds,
|
|
25634
26208
|
canvasW,
|
|
@@ -25715,6 +26289,13 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, rep
|
|
|
25715
26289
|
offset += recSize;
|
|
25716
26290
|
continue;
|
|
25717
26291
|
}
|
|
26292
|
+
if (emfPlusState.dualMode && !emfPlusState.gdiPassthrough) {
|
|
26293
|
+
if (recType !== EMR_SAVEDC && recType !== EMR_RESTOREDC) {
|
|
26294
|
+
handleEmfGdiStateRecord(rCtx, recType, offset, dataOff, recSize);
|
|
26295
|
+
}
|
|
26296
|
+
offset += recSize;
|
|
26297
|
+
continue;
|
|
26298
|
+
}
|
|
25718
26299
|
const handled = handleEmfGdiStateRecord(rCtx, recType, offset, dataOff, recSize) || handleEmfGdiDrawRecord(rCtx, recType, offset, dataOff, recSize) || handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize);
|
|
25719
26300
|
if (!handled) {
|
|
25720
26301
|
console.warn(`[emf-converter] Unhandled EMR record type: ${recType}`);
|