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