emf-converter 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +316 -106
- package/dist/index.mjs +316 -106
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -214,7 +214,7 @@ Everything below is verified against output painted by Windows itself; `src/gdi-
|
|
|
214
214
|
|
|
215
215
|
Everything is measured against output painted by Windows itself; `src/gdi-parity.fixture.test.ts` holds the exact per-fixture bounds.
|
|
216
216
|
|
|
217
|
-
- **Colour adjustment and image effects**: `
|
|
217
|
+
- **Colour adjustment and image effects**: The `HALFTONE` stretch mode resamples with an area-averaging box filter. Integer enlargements match Windows exactly; reductions do not, because Windows' halftone engine uses a narrower kernel and its own colour mapping (1.05% of pixels differ on the fixture). `EMR_SETCOLORADJUSTMENT` is applied to the source of `HALFTONE` `StretchBlt` and `StretchDIBits` calls, as in GDI. Windows does not publish its adjustment formulas, so gamma, reference black/white, contrast, brightness, colourfulness, red-green tint, negative and log filter use documented approximations, and Windows' dithering of the adjusted colours is not reproduced (6.6% of pixels differ by more than 24 on the fixture). The illuminant is ignored. EMF+ image effects (`SerializableObject`) are applied to the image's pixels before it is drawn. Colour matrix and lookup table effects follow GDI+'s definitions; MS-EMFPLUS does not specify the algorithms of the others (blur, sharpen, brightness/contrast, levels, colour balance, colour curves, hue/saturation/lightness, tint, red-eye), so those use documented approximations and have not been checked against GDI+ output. An effect is applied to the draw's source rectangle only, and a blur with `expandEdge` grows it by the blur radius, as GDI+ does. When the source rectangle is not in pixels, the effect is applied to the whole image without that growth. An effect is skipped when the image cannot be decoded to pixels: PNG and BMP always can be, while JPEG, GIF, TIFF and similar formats need a canvas backend (a browser or `@napi-rs/canvas`).
|
|
218
218
|
- **Pen transforms**: nonuniform and skewed EMF+ pen transforms are drawn with the transformed (elliptical or sheared) nib, but unlike uniform ones they are not yet verified against Windows output, and a singular pen transform is ignored.
|
|
219
219
|
- **Text**: ANSI, multi-string and small-text records now render; the Windows text fixtures retain a few glyph-edge differences and a `PolyTextOut` C1 control-glyph difference (under 0.1% of pixels; bounds in `src/emf-text-records.fixture.test.ts`). ANSI decoding uses the host's `TextDecoder` for common Windows code pages; unsupported encodings (including Johab and OEM on standard runtimes) fall back to Windows-1252. Vertical `ETO_PDY` advances require the `fonts` engine. Without `fonts`, SVG text measurements use deterministic estimates, so precise justification requires supplying the matching fonts.
|
|
220
220
|
- **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide Bezier curves follow `WidenPath` (which Windows' direct drawing does not quite match); at most 0.2% of pixels on the fixtures. `EMR_WIDENPATH` does not reproduce the extra inner join triangles GDI's own `WidenPath` emits (visible only when the widened outline is itself stroked). EMF+ 1-pixel antialiased lines can differ by one antialiasing sample at their ends, some closed widened outlines by one sample along an edge, and Inset or compound pens on closed figures are approximate.
|
package/dist/index.js
CHANGED
|
@@ -19177,7 +19177,7 @@ function paintBrushThroughMask(rCtx, sampler, box, drawCoverage, mode = "canvas"
|
|
|
19177
19177
|
const device = plusWorldMatrix(rCtx);
|
|
19178
19178
|
const m = mask.ctx;
|
|
19179
19179
|
const shift = aliased ? aliasedSampleShift(rCtx) : geometry ? plusCanvasShift(rCtx) : 0;
|
|
19180
|
-
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);
|
|
19181
19181
|
m.fillStyle = "#000";
|
|
19182
19182
|
m.strokeStyle = "#000";
|
|
19183
19183
|
drawCoverage(rasterMode !== "canvas" ? flatteningContext(m, device) : m);
|
|
@@ -19982,6 +19982,97 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
|
19982
19982
|
}
|
|
19983
19983
|
}
|
|
19984
19984
|
|
|
19985
|
+
// src/emf-gdi-color-adjust.ts
|
|
19986
|
+
var CA_NEGATIVE = 1;
|
|
19987
|
+
var CA_LOG_FILTER = 2;
|
|
19988
|
+
var COLOR_ADJUSTMENT_SIZE = 24;
|
|
19989
|
+
function readColorAdjustment(view, off) {
|
|
19990
|
+
const ca = {
|
|
19991
|
+
flags: view.getUint16(off + 2, true),
|
|
19992
|
+
illuminant: view.getUint16(off + 4, true),
|
|
19993
|
+
redGamma: view.getUint16(off + 6, true),
|
|
19994
|
+
greenGamma: view.getUint16(off + 8, true),
|
|
19995
|
+
blueGamma: view.getUint16(off + 10, true),
|
|
19996
|
+
referenceBlack: view.getUint16(off + 12, true),
|
|
19997
|
+
referenceWhite: view.getUint16(off + 14, true),
|
|
19998
|
+
contrast: view.getInt16(off + 16, true),
|
|
19999
|
+
brightness: view.getInt16(off + 18, true),
|
|
20000
|
+
colorfulness: view.getInt16(off + 20, true),
|
|
20001
|
+
redGreenTint: view.getInt16(off + 22, true)
|
|
20002
|
+
};
|
|
20003
|
+
const within = (v, lo, hi) => v >= lo && v <= hi;
|
|
20004
|
+
const valid = (ca.flags & -4) === 0 && within(ca.illuminant, 0, 8) && within(ca.redGamma, 2500, 65e3) && within(ca.greenGamma, 2500, 65e3) && within(ca.blueGamma, 2500, 65e3) && within(ca.referenceBlack, 0, 4e3) && within(ca.referenceWhite, 6e3, 1e4) && within(ca.contrast, -100, 100) && within(ca.brightness, -100, 100) && within(ca.colorfulness, -100, 100) && within(ca.redGreenTint, -100, 100);
|
|
20005
|
+
return valid ? ca : null;
|
|
20006
|
+
}
|
|
20007
|
+
function isIdentityColorAdjustment(ca) {
|
|
20008
|
+
return !ca || ca.flags === 0 && ca.redGamma === 1e4 && ca.greenGamma === 1e4 && ca.blueGamma === 1e4 && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
|
|
20009
|
+
}
|
|
20010
|
+
function colorfulnessScale(c) {
|
|
20011
|
+
return c > 0 ? 1 + 2 * c / 100 : 1 + c / 100;
|
|
20012
|
+
}
|
|
20013
|
+
var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
20014
|
+
function colorAdjustmentMapper(ca) {
|
|
20015
|
+
const black = ca.referenceBlack / 1e4;
|
|
20016
|
+
const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
|
|
20017
|
+
const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
|
|
20018
|
+
const curves = gammas.map((gamma) => {
|
|
20019
|
+
const table = new Float64Array(256);
|
|
20020
|
+
for (let i = 0; i < 256; i++) {
|
|
20021
|
+
let v = clamp012((i / 255 - black) / span);
|
|
20022
|
+
v = Math.pow(v, gamma);
|
|
20023
|
+
table[i] = ca.flags & CA_NEGATIVE ? 1 - v : v;
|
|
20024
|
+
}
|
|
20025
|
+
return table;
|
|
20026
|
+
});
|
|
20027
|
+
const chroma = colorfulnessScale(ca.colorfulness);
|
|
20028
|
+
const contrast = 1 + ca.contrast / 100;
|
|
20029
|
+
const brightness = 0.8 * ca.brightness / 100;
|
|
20030
|
+
const tint = 0.3 * ca.redGreenTint / 100;
|
|
20031
|
+
const log = (ca.flags & CA_LOG_FILTER) !== 0;
|
|
20032
|
+
const out = (v) => {
|
|
20033
|
+
v = clamp012(v);
|
|
20034
|
+
if (log) {
|
|
20035
|
+
v = Math.log10(1 + 9 * v);
|
|
20036
|
+
}
|
|
20037
|
+
return Math.round(v * 255);
|
|
20038
|
+
};
|
|
20039
|
+
return (rgb2) => {
|
|
20040
|
+
const r = curves[0][rgb2 >> 16 & 255];
|
|
20041
|
+
const g = curves[1][rgb2 >> 8 & 255];
|
|
20042
|
+
const b = curves[2][rgb2 & 255];
|
|
20043
|
+
let y = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
20044
|
+
let cr = (r - y) * chroma;
|
|
20045
|
+
const cb = (b - y) * chroma;
|
|
20046
|
+
y = (y - 0.5) * contrast + 0.5 + brightness;
|
|
20047
|
+
cr += tint;
|
|
20048
|
+
const r2 = y + cr;
|
|
20049
|
+
const b2 = y + cb;
|
|
20050
|
+
const g2 = (y - 0.299 * r2 - 0.114 * b2) / 0.587;
|
|
20051
|
+
return out(r2) << 16 | out(g2) << 8 | out(b2);
|
|
20052
|
+
};
|
|
20053
|
+
}
|
|
20054
|
+
function applyColorAdjustment(pixels, ca) {
|
|
20055
|
+
if (!ca || isIdentityColorAdjustment(ca)) {
|
|
20056
|
+
return;
|
|
20057
|
+
}
|
|
20058
|
+
const map = colorAdjustmentMapper(ca);
|
|
20059
|
+
const d = pixels.data;
|
|
20060
|
+
const cache = /* @__PURE__ */ new Map();
|
|
20061
|
+
for (let i = 0; i + 3 < d.length; i += 4) {
|
|
20062
|
+
const rgb2 = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
|
|
20063
|
+
let v = cache.get(rgb2);
|
|
20064
|
+
if (v === void 0) {
|
|
20065
|
+
v = map(rgb2);
|
|
20066
|
+
if (cache.size < 65536) {
|
|
20067
|
+
cache.set(rgb2, v);
|
|
20068
|
+
}
|
|
20069
|
+
}
|
|
20070
|
+
d[i] = v >> 16 & 255;
|
|
20071
|
+
d[i + 1] = v >> 8 & 255;
|
|
20072
|
+
d[i + 2] = v & 255;
|
|
20073
|
+
}
|
|
20074
|
+
}
|
|
20075
|
+
|
|
19985
20076
|
// src/emf-gdi-stretch.ts
|
|
19986
20077
|
var BLACKONWHITE = 1;
|
|
19987
20078
|
var WHITEONBLACK = 2;
|
|
@@ -20054,6 +20145,66 @@ function stretchGdi(src, sx, sy, sw, sh, dw, dh, mode) {
|
|
|
20054
20145
|
}
|
|
20055
20146
|
return { width: W, height: H, data };
|
|
20056
20147
|
}
|
|
20148
|
+
function axisBoxWeights(srcStart, srcLen, dstLen, reverse) {
|
|
20149
|
+
const out = [];
|
|
20150
|
+
if (dstLen <= 0 || srcLen <= 0) {
|
|
20151
|
+
return out;
|
|
20152
|
+
}
|
|
20153
|
+
const at = (k) => reverse ? srcStart + srcLen - 1 - k : srcStart + k;
|
|
20154
|
+
for (let i = 0; i < dstLen; i++) {
|
|
20155
|
+
const a = i * srcLen;
|
|
20156
|
+
const b = (i + 1) * srcLen;
|
|
20157
|
+
const taps = [];
|
|
20158
|
+
for (let k = Math.floor(a / dstLen); k * dstLen < b; k++) {
|
|
20159
|
+
const overlap = Math.min(b, (k + 1) * dstLen) - Math.max(a, k * dstLen);
|
|
20160
|
+
if (overlap > 0) {
|
|
20161
|
+
taps.push([at(k), overlap / srcLen]);
|
|
20162
|
+
}
|
|
20163
|
+
}
|
|
20164
|
+
out.push(taps);
|
|
20165
|
+
}
|
|
20166
|
+
return out;
|
|
20167
|
+
}
|
|
20168
|
+
function stretchHalftone(src, sx, sy, sw, sh, dw, dh) {
|
|
20169
|
+
const W = Math.max(0, Math.round(Math.abs(dw)));
|
|
20170
|
+
const H = Math.max(0, Math.round(Math.abs(dh)));
|
|
20171
|
+
const flipX = dw < 0 !== sw < 0;
|
|
20172
|
+
const flipY = dh < 0 !== sh < 0;
|
|
20173
|
+
const cols = axisBoxWeights(Math.round(Math.min(sx, sx + sw)), Math.round(Math.abs(sw)), W, flipX);
|
|
20174
|
+
const rows = axisBoxWeights(Math.round(Math.min(sy, sy + sh)), Math.round(Math.abs(sh)), H, flipY);
|
|
20175
|
+
const data = new Uint8ClampedArray(W * H * 4);
|
|
20176
|
+
const s = src.data;
|
|
20177
|
+
for (let y = 0; y < H; y++) {
|
|
20178
|
+
const ys = rows[y] ?? [];
|
|
20179
|
+
for (let x = 0; x < W; x++) {
|
|
20180
|
+
const xs = cols[x] ?? [];
|
|
20181
|
+
let r = 0;
|
|
20182
|
+
let g = 0;
|
|
20183
|
+
let b = 0;
|
|
20184
|
+
for (const [yy, wy] of ys) {
|
|
20185
|
+
if (yy < 0 || yy >= src.height) {
|
|
20186
|
+
continue;
|
|
20187
|
+
}
|
|
20188
|
+
for (const [xx, wx] of xs) {
|
|
20189
|
+
if (xx < 0 || xx >= src.width) {
|
|
20190
|
+
continue;
|
|
20191
|
+
}
|
|
20192
|
+
const w = wx * wy;
|
|
20193
|
+
const i = (yy * src.width + xx) * 4;
|
|
20194
|
+
r += s[i] * w;
|
|
20195
|
+
g += s[i + 1] * w;
|
|
20196
|
+
b += s[i + 2] * w;
|
|
20197
|
+
}
|
|
20198
|
+
}
|
|
20199
|
+
const o = (y * W + x) * 4;
|
|
20200
|
+
data[o] = Math.floor(r + 1e-6);
|
|
20201
|
+
data[o + 1] = Math.floor(g + 1e-6);
|
|
20202
|
+
data[o + 2] = Math.floor(b + 1e-6);
|
|
20203
|
+
data[o + 3] = 255;
|
|
20204
|
+
}
|
|
20205
|
+
}
|
|
20206
|
+
return { width: W, height: H, data };
|
|
20207
|
+
}
|
|
20057
20208
|
|
|
20058
20209
|
// src/emf-gdi-draw-bitmap.ts
|
|
20059
20210
|
function patternOperand(rCtx, brush) {
|
|
@@ -20077,46 +20228,18 @@ function patternOperand(rCtx, brush) {
|
|
|
20077
20228
|
function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
|
|
20078
20229
|
const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
|
|
20079
20230
|
const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
const tile = createTempCanvas(px.width, px.height);
|
|
20083
|
-
if (!tile) {
|
|
20084
|
-
return;
|
|
20085
|
-
}
|
|
20086
|
-
canvasPutImageData(tile.ctx, createImageDataCompat(px.data, px.width, px.height), 0, 0);
|
|
20087
|
-
target.save();
|
|
20088
|
-
target.globalCompositeOperation = "source-over";
|
|
20089
|
-
canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
|
|
20090
|
-
target.restore();
|
|
20231
|
+
const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
|
|
20232
|
+
if (px.width === 0 || px.height === 0) {
|
|
20091
20233
|
return;
|
|
20092
20234
|
}
|
|
20093
|
-
const
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20235
|
+
const tile = createTempCanvas(px.width, px.height);
|
|
20236
|
+
if (!tile) {
|
|
20237
|
+
return;
|
|
20238
|
+
}
|
|
20239
|
+
canvasPutImageData(tile.ctx, createImageDataCompat(px.data, px.width, px.height), 0, 0);
|
|
20097
20240
|
target.save();
|
|
20098
|
-
target.
|
|
20099
|
-
target.
|
|
20100
|
-
flipX ? -1 : 1,
|
|
20101
|
-
0,
|
|
20102
|
-
0,
|
|
20103
|
-
flipY ? -1 : 1,
|
|
20104
|
-
flipX ? dLeft * 2 + adw : 0,
|
|
20105
|
-
flipY ? dTop * 2 + adh : 0
|
|
20106
|
-
);
|
|
20107
|
-
const draw = target.drawImage;
|
|
20108
|
-
draw.call(
|
|
20109
|
-
target,
|
|
20110
|
-
decoded.canvas,
|
|
20111
|
-
Math.min(req.sx, req.sx + req.sw),
|
|
20112
|
-
Math.min(req.sy, req.sy + req.sh),
|
|
20113
|
-
Math.abs(req.sw),
|
|
20114
|
-
Math.abs(req.sh),
|
|
20115
|
-
dLeft,
|
|
20116
|
-
dTop,
|
|
20117
|
-
adw,
|
|
20118
|
-
adh
|
|
20119
|
-
);
|
|
20241
|
+
target.globalCompositeOperation = "source-over";
|
|
20242
|
+
canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
|
|
20120
20243
|
target.restore();
|
|
20121
20244
|
}
|
|
20122
20245
|
function decodeSource(rCtx, req) {
|
|
@@ -20133,17 +20256,15 @@ function decodeSource(rCtx, req) {
|
|
|
20133
20256
|
if (!image) {
|
|
20134
20257
|
return null;
|
|
20135
20258
|
}
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
return null;
|
|
20259
|
+
if (req.stretch && rCtx.state.stretchBltMode === HALFTONE) {
|
|
20260
|
+
applyColorAdjustment(image, rCtx.state.colorAdjustment);
|
|
20139
20261
|
}
|
|
20140
|
-
canvasPutImageData(temp.ctx, image, 0, 0);
|
|
20141
20262
|
let { sy } = req;
|
|
20142
20263
|
if (req.dibOrigin === "bottom-left" && rCtx.view.getInt32(req.source.bmi + 8, true) > 0) {
|
|
20143
20264
|
sy = image.height - sy - req.sh;
|
|
20144
20265
|
}
|
|
20145
20266
|
return {
|
|
20146
|
-
decoded: { pixels: image
|
|
20267
|
+
decoded: { pixels: image },
|
|
20147
20268
|
req: { ...req, sy }
|
|
20148
20269
|
};
|
|
20149
20270
|
}
|
|
@@ -20256,7 +20377,7 @@ function nudgedFloor(num, den, dx, dy) {
|
|
|
20256
20377
|
const q = Math.floor(num / den);
|
|
20257
20378
|
return num === q * den && nudgedSign(0, dx, dy) < 0 ? q - 1 : q;
|
|
20258
20379
|
}
|
|
20259
|
-
function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, sy, sw, sh, dibOrigin) {
|
|
20380
|
+
function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, sy, sw, sh, dibOrigin, stretch) {
|
|
20260
20381
|
const { ctx } = rCtx;
|
|
20261
20382
|
const index = rop3Index(rop);
|
|
20262
20383
|
if (index === 170) {
|
|
@@ -20292,7 +20413,8 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
|
|
|
20292
20413
|
sy,
|
|
20293
20414
|
sw,
|
|
20294
20415
|
sh,
|
|
20295
|
-
dibOrigin
|
|
20416
|
+
dibOrigin,
|
|
20417
|
+
stretch
|
|
20296
20418
|
});
|
|
20297
20419
|
if (!decoded) {
|
|
20298
20420
|
return;
|
|
@@ -20460,7 +20582,8 @@ function handleBlt(rCtx, offset, dataOff, recSize, stretch) {
|
|
|
20460
20582
|
srcRect.sy,
|
|
20461
20583
|
srcRect.sw,
|
|
20462
20584
|
srcRect.sh,
|
|
20463
|
-
"top-left"
|
|
20585
|
+
"top-left",
|
|
20586
|
+
stretch
|
|
20464
20587
|
);
|
|
20465
20588
|
return true;
|
|
20466
20589
|
}
|
|
@@ -20472,7 +20595,8 @@ function handleBlt(rCtx, offset, dataOff, recSize, stretch) {
|
|
|
20472
20595
|
dh: gmh(rCtx, cyDest),
|
|
20473
20596
|
source,
|
|
20474
20597
|
...srcRect,
|
|
20475
|
-
dibOrigin: "top-left"
|
|
20598
|
+
dibOrigin: "top-left",
|
|
20599
|
+
stretch
|
|
20476
20600
|
});
|
|
20477
20601
|
return true;
|
|
20478
20602
|
}
|
|
@@ -20499,7 +20623,7 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
|
20499
20623
|
const sw = view.getInt32(dataOff + 32, true);
|
|
20500
20624
|
const sh = view.getInt32(dataOff + 36, true);
|
|
20501
20625
|
if (hasWorldRotation(rCtx)) {
|
|
20502
|
-
executeRotatedBlit(rCtx, xDest, yDest, cxDest, cyDest, rop, source, sx, sy, sw, sh, "bottom-left");
|
|
20626
|
+
executeRotatedBlit(rCtx, xDest, yDest, cxDest, cyDest, rop, source, sx, sy, sw, sh, "bottom-left", true);
|
|
20503
20627
|
return true;
|
|
20504
20628
|
}
|
|
20505
20629
|
executeBlit(rCtx, {
|
|
@@ -20513,7 +20637,8 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
|
20513
20637
|
sy,
|
|
20514
20638
|
sw,
|
|
20515
20639
|
sh,
|
|
20516
|
-
dibOrigin: "bottom-left"
|
|
20640
|
+
dibOrigin: "bottom-left",
|
|
20641
|
+
stretch: true
|
|
20517
20642
|
});
|
|
20518
20643
|
return true;
|
|
20519
20644
|
}
|
|
@@ -23029,20 +23154,9 @@ function handleEmfGdiMiscRecord(rCtx, recType, dataOff, recSize) {
|
|
|
23029
23154
|
return true;
|
|
23030
23155
|
}
|
|
23031
23156
|
case EMR_SETCOLORADJUSTMENT: {
|
|
23032
|
-
|
|
23033
|
-
|
|
23034
|
-
|
|
23035
|
-
illuminant: view.getUint16(dataOff + 4, true),
|
|
23036
|
-
redGamma: view.getUint16(dataOff + 6, true),
|
|
23037
|
-
greenGamma: view.getUint16(dataOff + 8, true),
|
|
23038
|
-
blueGamma: view.getUint16(dataOff + 10, true),
|
|
23039
|
-
referenceBlack: view.getUint16(dataOff + 12, true),
|
|
23040
|
-
referenceWhite: view.getUint16(dataOff + 14, true),
|
|
23041
|
-
contrast: view.getInt16(dataOff + 16, true),
|
|
23042
|
-
brightness: view.getInt16(dataOff + 18, true),
|
|
23043
|
-
colorfulness: view.getInt16(dataOff + 20, true),
|
|
23044
|
-
redGreenTint: view.getInt16(dataOff + 22, true)
|
|
23045
|
-
};
|
|
23157
|
+
const ca = recSize >= 8 + COLOR_ADJUSTMENT_SIZE ? readColorAdjustment(view, dataOff) : null;
|
|
23158
|
+
if (ca) {
|
|
23159
|
+
state.colorAdjustment = ca;
|
|
23046
23160
|
}
|
|
23047
23161
|
return true;
|
|
23048
23162
|
}
|
|
@@ -23940,6 +24054,50 @@ function applyImageEffect(rgba, width, height, effect) {
|
|
|
23940
24054
|
return applyTint(rgba, effect.hue, effect.amount);
|
|
23941
24055
|
}
|
|
23942
24056
|
}
|
|
24057
|
+
function applyImageEffectToRect(rgba, width, height, effect, src) {
|
|
24058
|
+
if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
24059
|
+
return null;
|
|
24060
|
+
}
|
|
24061
|
+
let x0 = 0;
|
|
24062
|
+
let y0 = 0;
|
|
24063
|
+
let x1 = width;
|
|
24064
|
+
let y1 = height;
|
|
24065
|
+
if (src) {
|
|
24066
|
+
x0 = clamp(Math.floor(src.x), 0, width);
|
|
24067
|
+
y0 = clamp(Math.floor(src.y), 0, height);
|
|
24068
|
+
x1 = clamp(Math.ceil(src.x + src.w), 0, width);
|
|
24069
|
+
y1 = clamp(Math.ceil(src.y + src.h), 0, height);
|
|
24070
|
+
if (x1 <= x0 || y1 <= y0) {
|
|
24071
|
+
return null;
|
|
24072
|
+
}
|
|
24073
|
+
}
|
|
24074
|
+
const pad = src && effect.kind === "blur" && effect.expandEdge ? Math.ceil(effect.radius) : 0;
|
|
24075
|
+
const cw = x1 - x0;
|
|
24076
|
+
const ch = y1 - y0;
|
|
24077
|
+
const w = cw + 2 * pad;
|
|
24078
|
+
const h = ch + 2 * pad;
|
|
24079
|
+
let region = rgba;
|
|
24080
|
+
if (w !== width || h !== height || x0 !== 0 || y0 !== 0) {
|
|
24081
|
+
region = new Uint8ClampedArray(w * h * 4);
|
|
24082
|
+
for (let y = 0; y < ch; y++) {
|
|
24083
|
+
const from = ((y0 + y) * width + x0) * 4;
|
|
24084
|
+
region.set(rgba.subarray(from, from + cw * 4), ((pad + y) * w + pad) * 4);
|
|
24085
|
+
}
|
|
24086
|
+
}
|
|
24087
|
+
const ox = x0 - pad;
|
|
24088
|
+
const oy = y0 - pad;
|
|
24089
|
+
const local = effect.kind === "redEyeCorrection" ? {
|
|
24090
|
+
kind: effect.kind,
|
|
24091
|
+
areas: effect.areas.map((a) => ({
|
|
24092
|
+
left: a.left - ox,
|
|
24093
|
+
top: a.top - oy,
|
|
24094
|
+
right: a.right - ox,
|
|
24095
|
+
bottom: a.bottom - oy
|
|
24096
|
+
}))
|
|
24097
|
+
} : effect;
|
|
24098
|
+
const out = applyImageEffect(region, w, h, local);
|
|
24099
|
+
return out ? { rgba: out, x: ox, y: oy, width: w, height: h, pad } : null;
|
|
24100
|
+
}
|
|
23943
24101
|
|
|
23944
24102
|
// src/emf-plus-draw-image.ts
|
|
23945
24103
|
function surfaceSize4(ctx) {
|
|
@@ -24076,7 +24234,7 @@ function drawNestedMetafile(rCtx, img, cached, source) {
|
|
|
24076
24234
|
rCtx.deferredImages.push(...deferred);
|
|
24077
24235
|
return true;
|
|
24078
24236
|
}
|
|
24079
|
-
function
|
|
24237
|
+
function effectedDraw(img, bitmap, effect, draw, source) {
|
|
24080
24238
|
let src = bitmap;
|
|
24081
24239
|
if (!src && img.data) {
|
|
24082
24240
|
const bmp = decodeBmpFile(img.data.slice(0));
|
|
@@ -24087,21 +24245,61 @@ function effectedBitmap(img, bitmap, effect) {
|
|
|
24087
24245
|
if (!src) {
|
|
24088
24246
|
return null;
|
|
24089
24247
|
}
|
|
24090
|
-
const
|
|
24091
|
-
|
|
24248
|
+
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;
|
|
24249
|
+
const region = applyImageEffectToRect(
|
|
24250
|
+
src.rgba,
|
|
24251
|
+
src.width,
|
|
24252
|
+
src.height,
|
|
24253
|
+
effect,
|
|
24254
|
+
rect && { x: rect.srcX, y: rect.srcY, w: rect.srcW, h: rect.srcH }
|
|
24255
|
+
);
|
|
24256
|
+
if (!region) {
|
|
24257
|
+
return null;
|
|
24258
|
+
}
|
|
24259
|
+
const out = { kind: "bitmap", width: region.width, height: region.height, rgba: region.rgba };
|
|
24260
|
+
if (!rect) {
|
|
24261
|
+
return { bitmap: out, draw };
|
|
24262
|
+
}
|
|
24263
|
+
const grow = region.pad;
|
|
24264
|
+
const srcX = rect.srcX - grow - region.x;
|
|
24265
|
+
const srcY = rect.srcY - grow - region.y;
|
|
24266
|
+
const srcW = rect.srcW + 2 * grow;
|
|
24267
|
+
const srcH = rect.srcH + 2 * grow;
|
|
24268
|
+
const shift = [1, 0, 0, 1, region.x, region.y];
|
|
24269
|
+
const toWorld = mulMatrix(rect.toWorld(rect.srcX, rect.srcY, rect.srcW, rect.srcH), shift);
|
|
24270
|
+
return {
|
|
24271
|
+
bitmap: out,
|
|
24272
|
+
draw: {
|
|
24273
|
+
...draw,
|
|
24274
|
+
// The Canvas fallback scales the whole effected bitmap.
|
|
24275
|
+
transform: mulMatrix(draw.transform, toWorld),
|
|
24276
|
+
dx: 0,
|
|
24277
|
+
dy: 0,
|
|
24278
|
+
dw: region.width,
|
|
24279
|
+
dh: region.height,
|
|
24280
|
+
resample: draw.resample && {
|
|
24281
|
+
...draw.resample,
|
|
24282
|
+
srcX,
|
|
24283
|
+
srcY,
|
|
24284
|
+
srcW,
|
|
24285
|
+
srcH,
|
|
24286
|
+
toDevice: mulMatrix(draw.resample.toDevice, shift)
|
|
24287
|
+
}
|
|
24288
|
+
}
|
|
24289
|
+
};
|
|
24092
24290
|
}
|
|
24093
24291
|
function drawEmfPlusImageNow(rCtx, img, draw, source, effect = null) {
|
|
24094
24292
|
const cached = img.cacheKey !== void 0 ? rCtx.imageCache?.get(img.cacheKey) : void 0;
|
|
24095
24293
|
if (img.type === 2) {
|
|
24096
24294
|
return drawNestedMetafile(rCtx, img, cached, source);
|
|
24097
24295
|
}
|
|
24098
|
-
|
|
24099
|
-
const effected = effect ?
|
|
24296
|
+
const bitmap = cached && cached.kind === "bitmap" ? cached : null;
|
|
24297
|
+
const effected = effect ? effectedDraw(img, bitmap, effect, draw, source) : null;
|
|
24100
24298
|
if (effected) {
|
|
24101
|
-
bitmap
|
|
24299
|
+
return isSvgContext(rCtx.ctx) ? paintBitmapSvg(rCtx.ctx, img, effected.bitmap, effected.draw, true) : paintBitmapRaster(rCtx.ctx, effected.bitmap, effected.draw);
|
|
24102
24300
|
}
|
|
24103
24301
|
if (isSvgContext(rCtx.ctx)) {
|
|
24104
|
-
return paintBitmapSvg(rCtx.ctx, img, bitmap, draw
|
|
24302
|
+
return paintBitmapSvg(rCtx.ctx, img, bitmap, draw);
|
|
24105
24303
|
}
|
|
24106
24304
|
return bitmap ? paintBitmapRaster(rCtx.ctx, bitmap, draw) : false;
|
|
24107
24305
|
}
|
|
@@ -24393,28 +24591,32 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
|
|
|
24393
24591
|
if (mode !== "canvas" && size && strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, closedFigure)) {
|
|
24394
24592
|
return;
|
|
24395
24593
|
}
|
|
24396
|
-
const
|
|
24397
|
-
|
|
24398
|
-
|
|
24594
|
+
const outline = hasCustomCap(pen) ? penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) : null;
|
|
24595
|
+
let box = null;
|
|
24596
|
+
if (size && outline) {
|
|
24597
|
+
box = deviceBounds(outline.flat(), device, size, 2);
|
|
24598
|
+
} else if (size) {
|
|
24599
|
+
const miter = Math.max(1, ctx.miterLimit || 10);
|
|
24600
|
+
const margin = pen.width * scale * miter / 2 + 2;
|
|
24601
|
+
box = deviceBounds(points, device, size, margin);
|
|
24602
|
+
}
|
|
24399
24603
|
if (size && !box) {
|
|
24400
24604
|
return;
|
|
24401
24605
|
}
|
|
24402
|
-
|
|
24403
|
-
|
|
24404
|
-
|
|
24405
|
-
|
|
24406
|
-
(c
|
|
24407
|
-
|
|
24408
|
-
|
|
24409
|
-
|
|
24410
|
-
|
|
24411
|
-
|
|
24412
|
-
|
|
24413
|
-
|
|
24414
|
-
|
|
24415
|
-
|
|
24416
|
-
(c, x, y) => c.isPointInStroke(x, y) && (!inset || c.isPointInPath(x, y))
|
|
24417
|
-
)) {
|
|
24606
|
+
const drawMask = outline ? (c) => {
|
|
24607
|
+
traceOutline(c, outline);
|
|
24608
|
+
c.fill("nonzero");
|
|
24609
|
+
} : (c) => {
|
|
24610
|
+
applyPlusPenStyle(c, pen, widthScale);
|
|
24611
|
+
stroke(c);
|
|
24612
|
+
c.beginPath();
|
|
24613
|
+
buildPath(c);
|
|
24614
|
+
if (nib) {
|
|
24615
|
+
c.transform(nib[0], nib[1], nib[2], nib[3], 0, 0);
|
|
24616
|
+
}
|
|
24617
|
+
};
|
|
24618
|
+
const hitTest = outline ? (c, x, y) => c.isPointInPath(x, y, "nonzero") : (c, x, y) => c.isPointInStroke(x, y) && (!inset || c.isPointInPath(x, y));
|
|
24619
|
+
if (box && paintBrushThroughMask(rCtx, sampler, box, drawMask, mode, hitTest)) {
|
|
24418
24620
|
return;
|
|
24419
24621
|
}
|
|
24420
24622
|
}
|
|
@@ -24579,18 +24781,18 @@ function customCapOutline(figures, width, devicePen, pen) {
|
|
|
24579
24781
|
outline.push(...geo.polygons);
|
|
24580
24782
|
return outline;
|
|
24581
24783
|
}
|
|
24582
|
-
function
|
|
24784
|
+
function penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) {
|
|
24583
24785
|
const device = plusWorldMatrix(rCtx);
|
|
24584
24786
|
const frame2 = penFrame(device, nib);
|
|
24585
24787
|
const deviceInv = invertAffine(device);
|
|
24586
24788
|
if (!frame2 || !deviceInv || !(frame2.unit > 0)) {
|
|
24587
|
-
return
|
|
24789
|
+
return null;
|
|
24588
24790
|
}
|
|
24589
24791
|
let figures;
|
|
24590
24792
|
try {
|
|
24591
24793
|
figures = recordDeviceFigures(buildPath, frame2.record);
|
|
24592
24794
|
} catch {
|
|
24593
|
-
return
|
|
24795
|
+
return null;
|
|
24594
24796
|
}
|
|
24595
24797
|
if (closedFigure) {
|
|
24596
24798
|
figures = figures.map((f) => ({ ...f, closed: true }));
|
|
@@ -24598,25 +24800,33 @@ function fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure) {
|
|
|
24598
24800
|
const width = (pen.width || 1) * frame2.unit;
|
|
24599
24801
|
const devicePen = framePen(pen, frame2.unit);
|
|
24600
24802
|
const outline = hasCustomCap(pen) ? customCapOutline(figures, width, devicePen, pen) : widenFigures(figures, devicePen);
|
|
24601
|
-
const
|
|
24803
|
+
const m = mulMatrix(deviceInv, frame2.toDevice);
|
|
24804
|
+
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] })));
|
|
24805
|
+
}
|
|
24806
|
+
function traceOutline(c, outline) {
|
|
24807
|
+
c.beginPath();
|
|
24808
|
+
for (const poly of outline) {
|
|
24809
|
+
poly.forEach((p, i) => {
|
|
24810
|
+
if (i === 0) {
|
|
24811
|
+
c.moveTo(p.x, p.y);
|
|
24812
|
+
} else {
|
|
24813
|
+
c.lineTo(p.x, p.y);
|
|
24814
|
+
}
|
|
24815
|
+
});
|
|
24816
|
+
c.closePath();
|
|
24817
|
+
}
|
|
24818
|
+
}
|
|
24819
|
+
function fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure) {
|
|
24820
|
+
const outline = penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure);
|
|
24821
|
+
if (!outline) {
|
|
24822
|
+
return false;
|
|
24823
|
+
}
|
|
24602
24824
|
const { ctx } = rCtx;
|
|
24603
24825
|
ctx.save();
|
|
24604
24826
|
try {
|
|
24605
24827
|
ctx.fillStyle = paint;
|
|
24606
24828
|
applyPlusWorldTransform(rCtx);
|
|
24607
|
-
ctx
|
|
24608
|
-
for (const poly of outline) {
|
|
24609
|
-
poly.forEach((p, i) => {
|
|
24610
|
-
const x = toWorld[0] * p.x + toWorld[2] * p.y + toWorld[4];
|
|
24611
|
-
const y = toWorld[1] * p.x + toWorld[3] * p.y + toWorld[5];
|
|
24612
|
-
if (i === 0) {
|
|
24613
|
-
ctx.moveTo(x, y);
|
|
24614
|
-
} else {
|
|
24615
|
-
ctx.lineTo(x, y);
|
|
24616
|
-
}
|
|
24617
|
-
});
|
|
24618
|
-
ctx.closePath();
|
|
24619
|
-
}
|
|
24829
|
+
traceOutline(ctx, outline);
|
|
24620
24830
|
ctx.fill("nonzero");
|
|
24621
24831
|
} finally {
|
|
24622
24832
|
ctx.restore();
|
package/dist/index.mjs
CHANGED
|
@@ -19175,7 +19175,7 @@ function paintBrushThroughMask(rCtx, sampler, box, drawCoverage, mode = "canvas"
|
|
|
19175
19175
|
const device = plusWorldMatrix(rCtx);
|
|
19176
19176
|
const m = mask.ctx;
|
|
19177
19177
|
const shift = aliased ? aliasedSampleShift(rCtx) : geometry ? plusCanvasShift(rCtx) : 0;
|
|
19178
|
-
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);
|
|
19179
19179
|
m.fillStyle = "#000";
|
|
19180
19180
|
m.strokeStyle = "#000";
|
|
19181
19181
|
drawCoverage(rasterMode !== "canvas" ? flatteningContext(m, device) : m);
|
|
@@ -19980,6 +19980,97 @@ function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
|
19980
19980
|
}
|
|
19981
19981
|
}
|
|
19982
19982
|
|
|
19983
|
+
// src/emf-gdi-color-adjust.ts
|
|
19984
|
+
var CA_NEGATIVE = 1;
|
|
19985
|
+
var CA_LOG_FILTER = 2;
|
|
19986
|
+
var COLOR_ADJUSTMENT_SIZE = 24;
|
|
19987
|
+
function readColorAdjustment(view, off) {
|
|
19988
|
+
const ca = {
|
|
19989
|
+
flags: view.getUint16(off + 2, true),
|
|
19990
|
+
illuminant: view.getUint16(off + 4, true),
|
|
19991
|
+
redGamma: view.getUint16(off + 6, true),
|
|
19992
|
+
greenGamma: view.getUint16(off + 8, true),
|
|
19993
|
+
blueGamma: view.getUint16(off + 10, true),
|
|
19994
|
+
referenceBlack: view.getUint16(off + 12, true),
|
|
19995
|
+
referenceWhite: view.getUint16(off + 14, true),
|
|
19996
|
+
contrast: view.getInt16(off + 16, true),
|
|
19997
|
+
brightness: view.getInt16(off + 18, true),
|
|
19998
|
+
colorfulness: view.getInt16(off + 20, true),
|
|
19999
|
+
redGreenTint: view.getInt16(off + 22, true)
|
|
20000
|
+
};
|
|
20001
|
+
const within = (v, lo, hi) => v >= lo && v <= hi;
|
|
20002
|
+
const valid = (ca.flags & -4) === 0 && within(ca.illuminant, 0, 8) && within(ca.redGamma, 2500, 65e3) && within(ca.greenGamma, 2500, 65e3) && within(ca.blueGamma, 2500, 65e3) && within(ca.referenceBlack, 0, 4e3) && within(ca.referenceWhite, 6e3, 1e4) && within(ca.contrast, -100, 100) && within(ca.brightness, -100, 100) && within(ca.colorfulness, -100, 100) && within(ca.redGreenTint, -100, 100);
|
|
20003
|
+
return valid ? ca : null;
|
|
20004
|
+
}
|
|
20005
|
+
function isIdentityColorAdjustment(ca) {
|
|
20006
|
+
return !ca || ca.flags === 0 && ca.redGamma === 1e4 && ca.greenGamma === 1e4 && ca.blueGamma === 1e4 && ca.referenceBlack === 0 && ca.referenceWhite === 1e4 && ca.contrast === 0 && ca.brightness === 0 && ca.colorfulness === 0 && ca.redGreenTint === 0;
|
|
20007
|
+
}
|
|
20008
|
+
function colorfulnessScale(c) {
|
|
20009
|
+
return c > 0 ? 1 + 2 * c / 100 : 1 + c / 100;
|
|
20010
|
+
}
|
|
20011
|
+
var clamp012 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
20012
|
+
function colorAdjustmentMapper(ca) {
|
|
20013
|
+
const black = ca.referenceBlack / 1e4;
|
|
20014
|
+
const span = Math.max(1e-6, ca.referenceWhite / 1e4 - black);
|
|
20015
|
+
const gammas = [ca.redGamma / 1e4, ca.greenGamma / 1e4, ca.blueGamma / 1e4];
|
|
20016
|
+
const curves = gammas.map((gamma) => {
|
|
20017
|
+
const table = new Float64Array(256);
|
|
20018
|
+
for (let i = 0; i < 256; i++) {
|
|
20019
|
+
let v = clamp012((i / 255 - black) / span);
|
|
20020
|
+
v = Math.pow(v, gamma);
|
|
20021
|
+
table[i] = ca.flags & CA_NEGATIVE ? 1 - v : v;
|
|
20022
|
+
}
|
|
20023
|
+
return table;
|
|
20024
|
+
});
|
|
20025
|
+
const chroma = colorfulnessScale(ca.colorfulness);
|
|
20026
|
+
const contrast = 1 + ca.contrast / 100;
|
|
20027
|
+
const brightness = 0.8 * ca.brightness / 100;
|
|
20028
|
+
const tint = 0.3 * ca.redGreenTint / 100;
|
|
20029
|
+
const log = (ca.flags & CA_LOG_FILTER) !== 0;
|
|
20030
|
+
const out = (v) => {
|
|
20031
|
+
v = clamp012(v);
|
|
20032
|
+
if (log) {
|
|
20033
|
+
v = Math.log10(1 + 9 * v);
|
|
20034
|
+
}
|
|
20035
|
+
return Math.round(v * 255);
|
|
20036
|
+
};
|
|
20037
|
+
return (rgb2) => {
|
|
20038
|
+
const r = curves[0][rgb2 >> 16 & 255];
|
|
20039
|
+
const g = curves[1][rgb2 >> 8 & 255];
|
|
20040
|
+
const b = curves[2][rgb2 & 255];
|
|
20041
|
+
let y = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
20042
|
+
let cr = (r - y) * chroma;
|
|
20043
|
+
const cb = (b - y) * chroma;
|
|
20044
|
+
y = (y - 0.5) * contrast + 0.5 + brightness;
|
|
20045
|
+
cr += tint;
|
|
20046
|
+
const r2 = y + cr;
|
|
20047
|
+
const b2 = y + cb;
|
|
20048
|
+
const g2 = (y - 0.299 * r2 - 0.114 * b2) / 0.587;
|
|
20049
|
+
return out(r2) << 16 | out(g2) << 8 | out(b2);
|
|
20050
|
+
};
|
|
20051
|
+
}
|
|
20052
|
+
function applyColorAdjustment(pixels, ca) {
|
|
20053
|
+
if (!ca || isIdentityColorAdjustment(ca)) {
|
|
20054
|
+
return;
|
|
20055
|
+
}
|
|
20056
|
+
const map = colorAdjustmentMapper(ca);
|
|
20057
|
+
const d = pixels.data;
|
|
20058
|
+
const cache = /* @__PURE__ */ new Map();
|
|
20059
|
+
for (let i = 0; i + 3 < d.length; i += 4) {
|
|
20060
|
+
const rgb2 = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
|
|
20061
|
+
let v = cache.get(rgb2);
|
|
20062
|
+
if (v === void 0) {
|
|
20063
|
+
v = map(rgb2);
|
|
20064
|
+
if (cache.size < 65536) {
|
|
20065
|
+
cache.set(rgb2, v);
|
|
20066
|
+
}
|
|
20067
|
+
}
|
|
20068
|
+
d[i] = v >> 16 & 255;
|
|
20069
|
+
d[i + 1] = v >> 8 & 255;
|
|
20070
|
+
d[i + 2] = v & 255;
|
|
20071
|
+
}
|
|
20072
|
+
}
|
|
20073
|
+
|
|
19983
20074
|
// src/emf-gdi-stretch.ts
|
|
19984
20075
|
var BLACKONWHITE = 1;
|
|
19985
20076
|
var WHITEONBLACK = 2;
|
|
@@ -20052,6 +20143,66 @@ function stretchGdi(src, sx, sy, sw, sh, dw, dh, mode) {
|
|
|
20052
20143
|
}
|
|
20053
20144
|
return { width: W, height: H, data };
|
|
20054
20145
|
}
|
|
20146
|
+
function axisBoxWeights(srcStart, srcLen, dstLen, reverse) {
|
|
20147
|
+
const out = [];
|
|
20148
|
+
if (dstLen <= 0 || srcLen <= 0) {
|
|
20149
|
+
return out;
|
|
20150
|
+
}
|
|
20151
|
+
const at = (k) => reverse ? srcStart + srcLen - 1 - k : srcStart + k;
|
|
20152
|
+
for (let i = 0; i < dstLen; i++) {
|
|
20153
|
+
const a = i * srcLen;
|
|
20154
|
+
const b = (i + 1) * srcLen;
|
|
20155
|
+
const taps = [];
|
|
20156
|
+
for (let k = Math.floor(a / dstLen); k * dstLen < b; k++) {
|
|
20157
|
+
const overlap = Math.min(b, (k + 1) * dstLen) - Math.max(a, k * dstLen);
|
|
20158
|
+
if (overlap > 0) {
|
|
20159
|
+
taps.push([at(k), overlap / srcLen]);
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
out.push(taps);
|
|
20163
|
+
}
|
|
20164
|
+
return out;
|
|
20165
|
+
}
|
|
20166
|
+
function stretchHalftone(src, sx, sy, sw, sh, dw, dh) {
|
|
20167
|
+
const W = Math.max(0, Math.round(Math.abs(dw)));
|
|
20168
|
+
const H = Math.max(0, Math.round(Math.abs(dh)));
|
|
20169
|
+
const flipX = dw < 0 !== sw < 0;
|
|
20170
|
+
const flipY = dh < 0 !== sh < 0;
|
|
20171
|
+
const cols = axisBoxWeights(Math.round(Math.min(sx, sx + sw)), Math.round(Math.abs(sw)), W, flipX);
|
|
20172
|
+
const rows = axisBoxWeights(Math.round(Math.min(sy, sy + sh)), Math.round(Math.abs(sh)), H, flipY);
|
|
20173
|
+
const data = new Uint8ClampedArray(W * H * 4);
|
|
20174
|
+
const s = src.data;
|
|
20175
|
+
for (let y = 0; y < H; y++) {
|
|
20176
|
+
const ys = rows[y] ?? [];
|
|
20177
|
+
for (let x = 0; x < W; x++) {
|
|
20178
|
+
const xs = cols[x] ?? [];
|
|
20179
|
+
let r = 0;
|
|
20180
|
+
let g = 0;
|
|
20181
|
+
let b = 0;
|
|
20182
|
+
for (const [yy, wy] of ys) {
|
|
20183
|
+
if (yy < 0 || yy >= src.height) {
|
|
20184
|
+
continue;
|
|
20185
|
+
}
|
|
20186
|
+
for (const [xx, wx] of xs) {
|
|
20187
|
+
if (xx < 0 || xx >= src.width) {
|
|
20188
|
+
continue;
|
|
20189
|
+
}
|
|
20190
|
+
const w = wx * wy;
|
|
20191
|
+
const i = (yy * src.width + xx) * 4;
|
|
20192
|
+
r += s[i] * w;
|
|
20193
|
+
g += s[i + 1] * w;
|
|
20194
|
+
b += s[i + 2] * w;
|
|
20195
|
+
}
|
|
20196
|
+
}
|
|
20197
|
+
const o = (y * W + x) * 4;
|
|
20198
|
+
data[o] = Math.floor(r + 1e-6);
|
|
20199
|
+
data[o + 1] = Math.floor(g + 1e-6);
|
|
20200
|
+
data[o + 2] = Math.floor(b + 1e-6);
|
|
20201
|
+
data[o + 3] = 255;
|
|
20202
|
+
}
|
|
20203
|
+
}
|
|
20204
|
+
return { width: W, height: H, data };
|
|
20205
|
+
}
|
|
20055
20206
|
|
|
20056
20207
|
// src/emf-gdi-draw-bitmap.ts
|
|
20057
20208
|
function patternOperand(rCtx, brush) {
|
|
@@ -20075,46 +20226,18 @@ function patternOperand(rCtx, brush) {
|
|
|
20075
20226
|
function drawSourceMapped(target, decoded, req, offsetX, offsetY, mode) {
|
|
20076
20227
|
const dLeft = Math.min(req.dx, req.dx + req.dw) - offsetX;
|
|
20077
20228
|
const dTop = Math.min(req.dy, req.dy + req.dh) - offsetY;
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
const tile = createTempCanvas(px.width, px.height);
|
|
20081
|
-
if (!tile) {
|
|
20082
|
-
return;
|
|
20083
|
-
}
|
|
20084
|
-
canvasPutImageData(tile.ctx, createImageDataCompat(px.data, px.width, px.height), 0, 0);
|
|
20085
|
-
target.save();
|
|
20086
|
-
target.globalCompositeOperation = "source-over";
|
|
20087
|
-
canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
|
|
20088
|
-
target.restore();
|
|
20229
|
+
const px = mode === HALFTONE ? stretchHalftone(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh) : stretchGdi(decoded.pixels, req.sx, req.sy, req.sw, req.sh, req.dw, req.dh, mode);
|
|
20230
|
+
if (px.width === 0 || px.height === 0) {
|
|
20089
20231
|
return;
|
|
20090
20232
|
}
|
|
20091
|
-
const
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20233
|
+
const tile = createTempCanvas(px.width, px.height);
|
|
20234
|
+
if (!tile) {
|
|
20235
|
+
return;
|
|
20236
|
+
}
|
|
20237
|
+
canvasPutImageData(tile.ctx, createImageDataCompat(px.data, px.width, px.height), 0, 0);
|
|
20095
20238
|
target.save();
|
|
20096
|
-
target.
|
|
20097
|
-
target.
|
|
20098
|
-
flipX ? -1 : 1,
|
|
20099
|
-
0,
|
|
20100
|
-
0,
|
|
20101
|
-
flipY ? -1 : 1,
|
|
20102
|
-
flipX ? dLeft * 2 + adw : 0,
|
|
20103
|
-
flipY ? dTop * 2 + adh : 0
|
|
20104
|
-
);
|
|
20105
|
-
const draw = target.drawImage;
|
|
20106
|
-
draw.call(
|
|
20107
|
-
target,
|
|
20108
|
-
decoded.canvas,
|
|
20109
|
-
Math.min(req.sx, req.sx + req.sw),
|
|
20110
|
-
Math.min(req.sy, req.sy + req.sh),
|
|
20111
|
-
Math.abs(req.sw),
|
|
20112
|
-
Math.abs(req.sh),
|
|
20113
|
-
dLeft,
|
|
20114
|
-
dTop,
|
|
20115
|
-
adw,
|
|
20116
|
-
adh
|
|
20117
|
-
);
|
|
20239
|
+
target.globalCompositeOperation = "source-over";
|
|
20240
|
+
canvasDrawImage(target, tile.canvas, Math.round(dLeft), Math.round(dTop), px.width, px.height);
|
|
20118
20241
|
target.restore();
|
|
20119
20242
|
}
|
|
20120
20243
|
function decodeSource(rCtx, req) {
|
|
@@ -20131,17 +20254,15 @@ function decodeSource(rCtx, req) {
|
|
|
20131
20254
|
if (!image) {
|
|
20132
20255
|
return null;
|
|
20133
20256
|
}
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
return null;
|
|
20257
|
+
if (req.stretch && rCtx.state.stretchBltMode === HALFTONE) {
|
|
20258
|
+
applyColorAdjustment(image, rCtx.state.colorAdjustment);
|
|
20137
20259
|
}
|
|
20138
|
-
canvasPutImageData(temp.ctx, image, 0, 0);
|
|
20139
20260
|
let { sy } = req;
|
|
20140
20261
|
if (req.dibOrigin === "bottom-left" && rCtx.view.getInt32(req.source.bmi + 8, true) > 0) {
|
|
20141
20262
|
sy = image.height - sy - req.sh;
|
|
20142
20263
|
}
|
|
20143
20264
|
return {
|
|
20144
|
-
decoded: { pixels: image
|
|
20265
|
+
decoded: { pixels: image },
|
|
20145
20266
|
req: { ...req, sy }
|
|
20146
20267
|
};
|
|
20147
20268
|
}
|
|
@@ -20254,7 +20375,7 @@ function nudgedFloor(num, den, dx, dy) {
|
|
|
20254
20375
|
const q = Math.floor(num / den);
|
|
20255
20376
|
return num === q * den && nudgedSign(0, dx, dy) < 0 ? q - 1 : q;
|
|
20256
20377
|
}
|
|
20257
|
-
function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, sy, sw, sh, dibOrigin) {
|
|
20378
|
+
function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, sy, sw, sh, dibOrigin, stretch) {
|
|
20258
20379
|
const { ctx } = rCtx;
|
|
20259
20380
|
const index = rop3Index(rop);
|
|
20260
20381
|
if (index === 170) {
|
|
@@ -20290,7 +20411,8 @@ function executeRotatedBlit(rCtx, logDx, logDy, logDw, logDh, rop, source, sx, s
|
|
|
20290
20411
|
sy,
|
|
20291
20412
|
sw,
|
|
20292
20413
|
sh,
|
|
20293
|
-
dibOrigin
|
|
20414
|
+
dibOrigin,
|
|
20415
|
+
stretch
|
|
20294
20416
|
});
|
|
20295
20417
|
if (!decoded) {
|
|
20296
20418
|
return;
|
|
@@ -20458,7 +20580,8 @@ function handleBlt(rCtx, offset, dataOff, recSize, stretch) {
|
|
|
20458
20580
|
srcRect.sy,
|
|
20459
20581
|
srcRect.sw,
|
|
20460
20582
|
srcRect.sh,
|
|
20461
|
-
"top-left"
|
|
20583
|
+
"top-left",
|
|
20584
|
+
stretch
|
|
20462
20585
|
);
|
|
20463
20586
|
return true;
|
|
20464
20587
|
}
|
|
@@ -20470,7 +20593,8 @@ function handleBlt(rCtx, offset, dataOff, recSize, stretch) {
|
|
|
20470
20593
|
dh: gmh(rCtx, cyDest),
|
|
20471
20594
|
source,
|
|
20472
20595
|
...srcRect,
|
|
20473
|
-
dibOrigin: "top-left"
|
|
20596
|
+
dibOrigin: "top-left",
|
|
20597
|
+
stretch
|
|
20474
20598
|
});
|
|
20475
20599
|
return true;
|
|
20476
20600
|
}
|
|
@@ -20497,7 +20621,7 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
|
20497
20621
|
const sw = view.getInt32(dataOff + 32, true);
|
|
20498
20622
|
const sh = view.getInt32(dataOff + 36, true);
|
|
20499
20623
|
if (hasWorldRotation(rCtx)) {
|
|
20500
|
-
executeRotatedBlit(rCtx, xDest, yDest, cxDest, cyDest, rop, source, sx, sy, sw, sh, "bottom-left");
|
|
20624
|
+
executeRotatedBlit(rCtx, xDest, yDest, cxDest, cyDest, rop, source, sx, sy, sw, sh, "bottom-left", true);
|
|
20501
20625
|
return true;
|
|
20502
20626
|
}
|
|
20503
20627
|
executeBlit(rCtx, {
|
|
@@ -20511,7 +20635,8 @@ function handleStretchDibits(rCtx, offset, dataOff, recSize) {
|
|
|
20511
20635
|
sy,
|
|
20512
20636
|
sw,
|
|
20513
20637
|
sh,
|
|
20514
|
-
dibOrigin: "bottom-left"
|
|
20638
|
+
dibOrigin: "bottom-left",
|
|
20639
|
+
stretch: true
|
|
20515
20640
|
});
|
|
20516
20641
|
return true;
|
|
20517
20642
|
}
|
|
@@ -23027,20 +23152,9 @@ function handleEmfGdiMiscRecord(rCtx, recType, dataOff, recSize) {
|
|
|
23027
23152
|
return true;
|
|
23028
23153
|
}
|
|
23029
23154
|
case EMR_SETCOLORADJUSTMENT: {
|
|
23030
|
-
|
|
23031
|
-
|
|
23032
|
-
|
|
23033
|
-
illuminant: view.getUint16(dataOff + 4, true),
|
|
23034
|
-
redGamma: view.getUint16(dataOff + 6, true),
|
|
23035
|
-
greenGamma: view.getUint16(dataOff + 8, true),
|
|
23036
|
-
blueGamma: view.getUint16(dataOff + 10, true),
|
|
23037
|
-
referenceBlack: view.getUint16(dataOff + 12, true),
|
|
23038
|
-
referenceWhite: view.getUint16(dataOff + 14, true),
|
|
23039
|
-
contrast: view.getInt16(dataOff + 16, true),
|
|
23040
|
-
brightness: view.getInt16(dataOff + 18, true),
|
|
23041
|
-
colorfulness: view.getInt16(dataOff + 20, true),
|
|
23042
|
-
redGreenTint: view.getInt16(dataOff + 22, true)
|
|
23043
|
-
};
|
|
23155
|
+
const ca = recSize >= 8 + COLOR_ADJUSTMENT_SIZE ? readColorAdjustment(view, dataOff) : null;
|
|
23156
|
+
if (ca) {
|
|
23157
|
+
state.colorAdjustment = ca;
|
|
23044
23158
|
}
|
|
23045
23159
|
return true;
|
|
23046
23160
|
}
|
|
@@ -23938,6 +24052,50 @@ function applyImageEffect(rgba, width, height, effect) {
|
|
|
23938
24052
|
return applyTint(rgba, effect.hue, effect.amount);
|
|
23939
24053
|
}
|
|
23940
24054
|
}
|
|
24055
|
+
function applyImageEffectToRect(rgba, width, height, effect, src) {
|
|
24056
|
+
if (width <= 0 || height <= 0 || rgba.length !== width * height * 4) {
|
|
24057
|
+
return null;
|
|
24058
|
+
}
|
|
24059
|
+
let x0 = 0;
|
|
24060
|
+
let y0 = 0;
|
|
24061
|
+
let x1 = width;
|
|
24062
|
+
let y1 = height;
|
|
24063
|
+
if (src) {
|
|
24064
|
+
x0 = clamp(Math.floor(src.x), 0, width);
|
|
24065
|
+
y0 = clamp(Math.floor(src.y), 0, height);
|
|
24066
|
+
x1 = clamp(Math.ceil(src.x + src.w), 0, width);
|
|
24067
|
+
y1 = clamp(Math.ceil(src.y + src.h), 0, height);
|
|
24068
|
+
if (x1 <= x0 || y1 <= y0) {
|
|
24069
|
+
return null;
|
|
24070
|
+
}
|
|
24071
|
+
}
|
|
24072
|
+
const pad = src && effect.kind === "blur" && effect.expandEdge ? Math.ceil(effect.radius) : 0;
|
|
24073
|
+
const cw = x1 - x0;
|
|
24074
|
+
const ch = y1 - y0;
|
|
24075
|
+
const w = cw + 2 * pad;
|
|
24076
|
+
const h = ch + 2 * pad;
|
|
24077
|
+
let region = rgba;
|
|
24078
|
+
if (w !== width || h !== height || x0 !== 0 || y0 !== 0) {
|
|
24079
|
+
region = new Uint8ClampedArray(w * h * 4);
|
|
24080
|
+
for (let y = 0; y < ch; y++) {
|
|
24081
|
+
const from = ((y0 + y) * width + x0) * 4;
|
|
24082
|
+
region.set(rgba.subarray(from, from + cw * 4), ((pad + y) * w + pad) * 4);
|
|
24083
|
+
}
|
|
24084
|
+
}
|
|
24085
|
+
const ox = x0 - pad;
|
|
24086
|
+
const oy = y0 - pad;
|
|
24087
|
+
const local = effect.kind === "redEyeCorrection" ? {
|
|
24088
|
+
kind: effect.kind,
|
|
24089
|
+
areas: effect.areas.map((a) => ({
|
|
24090
|
+
left: a.left - ox,
|
|
24091
|
+
top: a.top - oy,
|
|
24092
|
+
right: a.right - ox,
|
|
24093
|
+
bottom: a.bottom - oy
|
|
24094
|
+
}))
|
|
24095
|
+
} : effect;
|
|
24096
|
+
const out = applyImageEffect(region, w, h, local);
|
|
24097
|
+
return out ? { rgba: out, x: ox, y: oy, width: w, height: h, pad } : null;
|
|
24098
|
+
}
|
|
23941
24099
|
|
|
23942
24100
|
// src/emf-plus-draw-image.ts
|
|
23943
24101
|
function surfaceSize4(ctx) {
|
|
@@ -24074,7 +24232,7 @@ function drawNestedMetafile(rCtx, img, cached, source) {
|
|
|
24074
24232
|
rCtx.deferredImages.push(...deferred);
|
|
24075
24233
|
return true;
|
|
24076
24234
|
}
|
|
24077
|
-
function
|
|
24235
|
+
function effectedDraw(img, bitmap, effect, draw, source) {
|
|
24078
24236
|
let src = bitmap;
|
|
24079
24237
|
if (!src && img.data) {
|
|
24080
24238
|
const bmp = decodeBmpFile(img.data.slice(0));
|
|
@@ -24085,21 +24243,61 @@ function effectedBitmap(img, bitmap, effect) {
|
|
|
24085
24243
|
if (!src) {
|
|
24086
24244
|
return null;
|
|
24087
24245
|
}
|
|
24088
|
-
const
|
|
24089
|
-
|
|
24246
|
+
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;
|
|
24247
|
+
const region = applyImageEffectToRect(
|
|
24248
|
+
src.rgba,
|
|
24249
|
+
src.width,
|
|
24250
|
+
src.height,
|
|
24251
|
+
effect,
|
|
24252
|
+
rect && { x: rect.srcX, y: rect.srcY, w: rect.srcW, h: rect.srcH }
|
|
24253
|
+
);
|
|
24254
|
+
if (!region) {
|
|
24255
|
+
return null;
|
|
24256
|
+
}
|
|
24257
|
+
const out = { kind: "bitmap", width: region.width, height: region.height, rgba: region.rgba };
|
|
24258
|
+
if (!rect) {
|
|
24259
|
+
return { bitmap: out, draw };
|
|
24260
|
+
}
|
|
24261
|
+
const grow = region.pad;
|
|
24262
|
+
const srcX = rect.srcX - grow - region.x;
|
|
24263
|
+
const srcY = rect.srcY - grow - region.y;
|
|
24264
|
+
const srcW = rect.srcW + 2 * grow;
|
|
24265
|
+
const srcH = rect.srcH + 2 * grow;
|
|
24266
|
+
const shift = [1, 0, 0, 1, region.x, region.y];
|
|
24267
|
+
const toWorld = mulMatrix(rect.toWorld(rect.srcX, rect.srcY, rect.srcW, rect.srcH), shift);
|
|
24268
|
+
return {
|
|
24269
|
+
bitmap: out,
|
|
24270
|
+
draw: {
|
|
24271
|
+
...draw,
|
|
24272
|
+
// The Canvas fallback scales the whole effected bitmap.
|
|
24273
|
+
transform: mulMatrix(draw.transform, toWorld),
|
|
24274
|
+
dx: 0,
|
|
24275
|
+
dy: 0,
|
|
24276
|
+
dw: region.width,
|
|
24277
|
+
dh: region.height,
|
|
24278
|
+
resample: draw.resample && {
|
|
24279
|
+
...draw.resample,
|
|
24280
|
+
srcX,
|
|
24281
|
+
srcY,
|
|
24282
|
+
srcW,
|
|
24283
|
+
srcH,
|
|
24284
|
+
toDevice: mulMatrix(draw.resample.toDevice, shift)
|
|
24285
|
+
}
|
|
24286
|
+
}
|
|
24287
|
+
};
|
|
24090
24288
|
}
|
|
24091
24289
|
function drawEmfPlusImageNow(rCtx, img, draw, source, effect = null) {
|
|
24092
24290
|
const cached = img.cacheKey !== void 0 ? rCtx.imageCache?.get(img.cacheKey) : void 0;
|
|
24093
24291
|
if (img.type === 2) {
|
|
24094
24292
|
return drawNestedMetafile(rCtx, img, cached, source);
|
|
24095
24293
|
}
|
|
24096
|
-
|
|
24097
|
-
const effected = effect ?
|
|
24294
|
+
const bitmap = cached && cached.kind === "bitmap" ? cached : null;
|
|
24295
|
+
const effected = effect ? effectedDraw(img, bitmap, effect, draw, source) : null;
|
|
24098
24296
|
if (effected) {
|
|
24099
|
-
bitmap
|
|
24297
|
+
return isSvgContext(rCtx.ctx) ? paintBitmapSvg(rCtx.ctx, img, effected.bitmap, effected.draw, true) : paintBitmapRaster(rCtx.ctx, effected.bitmap, effected.draw);
|
|
24100
24298
|
}
|
|
24101
24299
|
if (isSvgContext(rCtx.ctx)) {
|
|
24102
|
-
return paintBitmapSvg(rCtx.ctx, img, bitmap, draw
|
|
24300
|
+
return paintBitmapSvg(rCtx.ctx, img, bitmap, draw);
|
|
24103
24301
|
}
|
|
24104
24302
|
return bitmap ? paintBitmapRaster(rCtx.ctx, bitmap, draw) : false;
|
|
24105
24303
|
}
|
|
@@ -24391,28 +24589,32 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
|
|
|
24391
24589
|
if (mode !== "canvas" && size && strokeGdiplus(rCtx, pen, nib, sampler, buildPath, device, size, mode, closedFigure)) {
|
|
24392
24590
|
return;
|
|
24393
24591
|
}
|
|
24394
|
-
const
|
|
24395
|
-
|
|
24396
|
-
|
|
24592
|
+
const outline = hasCustomCap(pen) ? penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) : null;
|
|
24593
|
+
let box = null;
|
|
24594
|
+
if (size && outline) {
|
|
24595
|
+
box = deviceBounds(outline.flat(), device, size, 2);
|
|
24596
|
+
} else if (size) {
|
|
24597
|
+
const miter = Math.max(1, ctx.miterLimit || 10);
|
|
24598
|
+
const margin = pen.width * scale * miter / 2 + 2;
|
|
24599
|
+
box = deviceBounds(points, device, size, margin);
|
|
24600
|
+
}
|
|
24397
24601
|
if (size && !box) {
|
|
24398
24602
|
return;
|
|
24399
24603
|
}
|
|
24400
|
-
|
|
24401
|
-
|
|
24402
|
-
|
|
24403
|
-
|
|
24404
|
-
(c
|
|
24405
|
-
|
|
24406
|
-
|
|
24407
|
-
|
|
24408
|
-
|
|
24409
|
-
|
|
24410
|
-
|
|
24411
|
-
|
|
24412
|
-
|
|
24413
|
-
|
|
24414
|
-
(c, x, y) => c.isPointInStroke(x, y) && (!inset || c.isPointInPath(x, y))
|
|
24415
|
-
)) {
|
|
24604
|
+
const drawMask = outline ? (c) => {
|
|
24605
|
+
traceOutline(c, outline);
|
|
24606
|
+
c.fill("nonzero");
|
|
24607
|
+
} : (c) => {
|
|
24608
|
+
applyPlusPenStyle(c, pen, widthScale);
|
|
24609
|
+
stroke(c);
|
|
24610
|
+
c.beginPath();
|
|
24611
|
+
buildPath(c);
|
|
24612
|
+
if (nib) {
|
|
24613
|
+
c.transform(nib[0], nib[1], nib[2], nib[3], 0, 0);
|
|
24614
|
+
}
|
|
24615
|
+
};
|
|
24616
|
+
const hitTest = outline ? (c, x, y) => c.isPointInPath(x, y, "nonzero") : (c, x, y) => c.isPointInStroke(x, y) && (!inset || c.isPointInPath(x, y));
|
|
24617
|
+
if (box && paintBrushThroughMask(rCtx, sampler, box, drawMask, mode, hitTest)) {
|
|
24416
24618
|
return;
|
|
24417
24619
|
}
|
|
24418
24620
|
}
|
|
@@ -24577,18 +24779,18 @@ function customCapOutline(figures, width, devicePen, pen) {
|
|
|
24577
24779
|
outline.push(...geo.polygons);
|
|
24578
24780
|
return outline;
|
|
24579
24781
|
}
|
|
24580
|
-
function
|
|
24782
|
+
function penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure) {
|
|
24581
24783
|
const device = plusWorldMatrix(rCtx);
|
|
24582
24784
|
const frame2 = penFrame(device, nib);
|
|
24583
24785
|
const deviceInv = invertAffine(device);
|
|
24584
24786
|
if (!frame2 || !deviceInv || !(frame2.unit > 0)) {
|
|
24585
|
-
return
|
|
24787
|
+
return null;
|
|
24586
24788
|
}
|
|
24587
24789
|
let figures;
|
|
24588
24790
|
try {
|
|
24589
24791
|
figures = recordDeviceFigures(buildPath, frame2.record);
|
|
24590
24792
|
} catch {
|
|
24591
|
-
return
|
|
24793
|
+
return null;
|
|
24592
24794
|
}
|
|
24593
24795
|
if (closedFigure) {
|
|
24594
24796
|
figures = figures.map((f) => ({ ...f, closed: true }));
|
|
@@ -24596,25 +24798,33 @@ function fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure) {
|
|
|
24596
24798
|
const width = (pen.width || 1) * frame2.unit;
|
|
24597
24799
|
const devicePen = framePen(pen, frame2.unit);
|
|
24598
24800
|
const outline = hasCustomCap(pen) ? customCapOutline(figures, width, devicePen, pen) : widenFigures(figures, devicePen);
|
|
24599
|
-
const
|
|
24801
|
+
const m = mulMatrix(deviceInv, frame2.toDevice);
|
|
24802
|
+
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] })));
|
|
24803
|
+
}
|
|
24804
|
+
function traceOutline(c, outline) {
|
|
24805
|
+
c.beginPath();
|
|
24806
|
+
for (const poly of outline) {
|
|
24807
|
+
poly.forEach((p, i) => {
|
|
24808
|
+
if (i === 0) {
|
|
24809
|
+
c.moveTo(p.x, p.y);
|
|
24810
|
+
} else {
|
|
24811
|
+
c.lineTo(p.x, p.y);
|
|
24812
|
+
}
|
|
24813
|
+
});
|
|
24814
|
+
c.closePath();
|
|
24815
|
+
}
|
|
24816
|
+
}
|
|
24817
|
+
function fillPenOutline(rCtx, pen, nib, paint, buildPath, closedFigure) {
|
|
24818
|
+
const outline = penOutlineWorld(rCtx, pen, nib, buildPath, closedFigure);
|
|
24819
|
+
if (!outline) {
|
|
24820
|
+
return false;
|
|
24821
|
+
}
|
|
24600
24822
|
const { ctx } = rCtx;
|
|
24601
24823
|
ctx.save();
|
|
24602
24824
|
try {
|
|
24603
24825
|
ctx.fillStyle = paint;
|
|
24604
24826
|
applyPlusWorldTransform(rCtx);
|
|
24605
|
-
ctx
|
|
24606
|
-
for (const poly of outline) {
|
|
24607
|
-
poly.forEach((p, i) => {
|
|
24608
|
-
const x = toWorld[0] * p.x + toWorld[2] * p.y + toWorld[4];
|
|
24609
|
-
const y = toWorld[1] * p.x + toWorld[3] * p.y + toWorld[5];
|
|
24610
|
-
if (i === 0) {
|
|
24611
|
-
ctx.moveTo(x, y);
|
|
24612
|
-
} else {
|
|
24613
|
-
ctx.lineTo(x, y);
|
|
24614
|
-
}
|
|
24615
|
-
});
|
|
24616
|
-
ctx.closePath();
|
|
24617
|
-
}
|
|
24827
|
+
traceOutline(ctx, outline);
|
|
24618
24828
|
ctx.fill("nonzero");
|
|
24619
24829
|
} finally {
|
|
24620
24830
|
ctx.restore();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emf-converter",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"test": "vitest run",
|
|
46
46
|
"test:watch": "vitest",
|
|
47
47
|
"pack": "bun run build && bun pm pack",
|
|
48
|
+
"docs:typecheck": "vue-tsc --noEmit -p docs/tsconfig.json",
|
|
48
49
|
"docs:dev": "vitepress dev docs",
|
|
49
50
|
"docs:build": "vitepress build docs",
|
|
50
51
|
"docs:preview": "vitepress preview docs"
|
|
@@ -59,7 +60,8 @@
|
|
|
59
60
|
"tsup": "^8.5.1",
|
|
60
61
|
"typescript": "^6.0.3",
|
|
61
62
|
"vitepress": "^1.6.4",
|
|
62
|
-
"vitest": "^5.0.0"
|
|
63
|
+
"vitest": "^5.0.0",
|
|
64
|
+
"vue-tsc": "^3.3.11"
|
|
63
65
|
},
|
|
64
66
|
"peerDependencies": {
|
|
65
67
|
"@napi-rs/canvas": "^1.0.9"
|