emf-converter 3.1.0 → 3.2.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 +10 -5
- package/dist/index.js +1623 -1112
- package/dist/index.mjs +1623 -1112
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,6 +52,428 @@ function invertCssColor(color) {
|
|
|
52
52
|
return color;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// src/emf-dib-rle-decoder.ts
|
|
56
|
+
function decodeRleBitmap(view, bitsOffset, bitsSize, width, height, _topDown, isRle4, colorTable, out, setPixel) {
|
|
57
|
+
let x = 0;
|
|
58
|
+
let y = height - 1;
|
|
59
|
+
let off = bitsOffset;
|
|
60
|
+
const endOff = bitsOffset + bitsSize;
|
|
61
|
+
while (off + 1 < endOff && y >= 0) {
|
|
62
|
+
const first = view.getUint8(off);
|
|
63
|
+
const second = view.getUint8(off + 1);
|
|
64
|
+
off += 2;
|
|
65
|
+
if (first === 0) {
|
|
66
|
+
if (second === 0) {
|
|
67
|
+
x = 0;
|
|
68
|
+
y--;
|
|
69
|
+
} else if (second === 1) {
|
|
70
|
+
break;
|
|
71
|
+
} else if (second === 2) {
|
|
72
|
+
if (off + 1 >= endOff) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
x += view.getUint8(off);
|
|
76
|
+
y -= view.getUint8(off + 1);
|
|
77
|
+
off += 2;
|
|
78
|
+
} else {
|
|
79
|
+
const count = second;
|
|
80
|
+
if (!isRle4) {
|
|
81
|
+
for (let i = 0; i < count && off < endOff && x < width; i++) {
|
|
82
|
+
const idx = view.getUint8(off++);
|
|
83
|
+
if (idx < colorTable.length) {
|
|
84
|
+
setPixel(
|
|
85
|
+
x,
|
|
86
|
+
height - 1 - y,
|
|
87
|
+
colorTable[idx][0],
|
|
88
|
+
colorTable[idx][1],
|
|
89
|
+
colorTable[idx][2],
|
|
90
|
+
255
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
x++;
|
|
94
|
+
}
|
|
95
|
+
if (count & 1) {
|
|
96
|
+
off++;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
const bytes = Math.ceil(count / 2);
|
|
100
|
+
let pi = 0;
|
|
101
|
+
for (let i = 0; i < bytes && off < endOff; i++) {
|
|
102
|
+
const byte = view.getUint8(off++);
|
|
103
|
+
for (let nibble = 0; nibble < 2 && pi < count; nibble++) {
|
|
104
|
+
const idx = nibble === 0 ? byte >> 4 & 15 : byte & 15;
|
|
105
|
+
if (idx < colorTable.length && x < width) {
|
|
106
|
+
setPixel(
|
|
107
|
+
x,
|
|
108
|
+
height - 1 - y,
|
|
109
|
+
colorTable[idx][0],
|
|
110
|
+
colorTable[idx][1],
|
|
111
|
+
colorTable[idx][2],
|
|
112
|
+
255
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
x++;
|
|
116
|
+
pi++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (bytes & 1) {
|
|
120
|
+
off++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else if (!isRle4) {
|
|
125
|
+
const idx = second;
|
|
126
|
+
const c = idx < colorTable.length ? colorTable[idx] : [0, 0, 0];
|
|
127
|
+
for (let i = 0; i < first && x < width; i++) {
|
|
128
|
+
setPixel(x, height - 1 - y, c[0], c[1], c[2], 255);
|
|
129
|
+
x++;
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
const hi = second >> 4 & 15;
|
|
133
|
+
const lo = second & 15;
|
|
134
|
+
for (let i = 0; i < first && x < width; i++) {
|
|
135
|
+
const idx = (i & 1) === 0 ? hi : lo;
|
|
136
|
+
if (idx < colorTable.length) {
|
|
137
|
+
setPixel(
|
|
138
|
+
x,
|
|
139
|
+
height - 1 - y,
|
|
140
|
+
colorTable[idx][0],
|
|
141
|
+
colorTable[idx][1],
|
|
142
|
+
colorTable[idx][2],
|
|
143
|
+
255
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
x++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return createImageDataCompat(
|
|
151
|
+
new Uint8ClampedArray(out.buffer, out.byteOffset, out.byteLength),
|
|
152
|
+
width,
|
|
153
|
+
height
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/emf-dib-uncompressed.ts
|
|
158
|
+
function countTrailingZeros(v) {
|
|
159
|
+
if (v === 0) {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
let c = 0;
|
|
163
|
+
let val = v;
|
|
164
|
+
while ((val & 1) === 0) {
|
|
165
|
+
val >>>= 1;
|
|
166
|
+
c++;
|
|
167
|
+
}
|
|
168
|
+
return c;
|
|
169
|
+
}
|
|
170
|
+
var BI_BITFIELDS = 3;
|
|
171
|
+
function parseBitfieldMasks(view, bmiOffset, headerSize, compression, bitCount) {
|
|
172
|
+
let rMask = 0, gMask = 0, bMask = 0;
|
|
173
|
+
let rShift = 0, gShift = 0, bShift = 0;
|
|
174
|
+
let rMax = 1, gMax = 1, bMax = 1;
|
|
175
|
+
if (compression === BI_BITFIELDS) {
|
|
176
|
+
const bfOff = bmiOffset + headerSize;
|
|
177
|
+
if (bfOff + 12 > view.byteLength) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
rMask = view.getUint32(bfOff, true);
|
|
181
|
+
gMask = view.getUint32(bfOff + 4, true);
|
|
182
|
+
bMask = view.getUint32(bfOff + 8, true);
|
|
183
|
+
rShift = countTrailingZeros(rMask);
|
|
184
|
+
gShift = countTrailingZeros(gMask);
|
|
185
|
+
bShift = countTrailingZeros(bMask);
|
|
186
|
+
rMax = rMask >>> rShift || 1;
|
|
187
|
+
gMax = gMask >>> gShift || 1;
|
|
188
|
+
bMax = bMask >>> bShift || 1;
|
|
189
|
+
} else if (bitCount === 16) {
|
|
190
|
+
rMask = 31744;
|
|
191
|
+
gMask = 992;
|
|
192
|
+
bMask = 31;
|
|
193
|
+
rShift = 10;
|
|
194
|
+
gShift = 5;
|
|
195
|
+
bShift = 0;
|
|
196
|
+
rMax = 31;
|
|
197
|
+
gMax = 31;
|
|
198
|
+
bMax = 31;
|
|
199
|
+
}
|
|
200
|
+
return { rMask, gMask, bMask, rShift, gShift, bShift, rMax, gMax, bMax };
|
|
201
|
+
}
|
|
202
|
+
function decodeUncompressedRows(view, bitsOffset, width, height, topDown, bitCount, colorTable, masks, out) {
|
|
203
|
+
const rowStride = Math.floor((bitCount * width + 31) / 32) * 4;
|
|
204
|
+
const { rMask, gMask, bMask, rShift, gShift, bShift, rMax, gMax, bMax } = masks;
|
|
205
|
+
for (let y = 0; y < height; y++) {
|
|
206
|
+
const srcY = topDown ? y : height - 1 - y;
|
|
207
|
+
const rowStart = bitsOffset + srcY * rowStride;
|
|
208
|
+
if (rowStart + rowStride > view.byteLength) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
for (let x = 0; x < width; x++) {
|
|
212
|
+
const dstPx = (y * width + x) * 4;
|
|
213
|
+
if (bitCount === 1) {
|
|
214
|
+
const byteIdx = rowStart + (x >> 3);
|
|
215
|
+
const bit = view.getUint8(byteIdx) >> 7 - (x & 7) & 1;
|
|
216
|
+
if (bit < colorTable.length) {
|
|
217
|
+
out[dstPx] = colorTable[bit][0];
|
|
218
|
+
out[dstPx + 1] = colorTable[bit][1];
|
|
219
|
+
out[dstPx + 2] = colorTable[bit][2];
|
|
220
|
+
}
|
|
221
|
+
out[dstPx + 3] = 255;
|
|
222
|
+
} else if (bitCount === 4) {
|
|
223
|
+
const byteIdx = rowStart + (x >> 1);
|
|
224
|
+
const nibble = (x & 1) === 0 ? view.getUint8(byteIdx) >> 4 & 15 : view.getUint8(byteIdx) & 15;
|
|
225
|
+
if (nibble < colorTable.length) {
|
|
226
|
+
out[dstPx] = colorTable[nibble][0];
|
|
227
|
+
out[dstPx + 1] = colorTable[nibble][1];
|
|
228
|
+
out[dstPx + 2] = colorTable[nibble][2];
|
|
229
|
+
}
|
|
230
|
+
out[dstPx + 3] = 255;
|
|
231
|
+
} else if (bitCount === 8) {
|
|
232
|
+
const idx = view.getUint8(rowStart + x);
|
|
233
|
+
if (idx < colorTable.length) {
|
|
234
|
+
out[dstPx] = colorTable[idx][0];
|
|
235
|
+
out[dstPx + 1] = colorTable[idx][1];
|
|
236
|
+
out[dstPx + 2] = colorTable[idx][2];
|
|
237
|
+
}
|
|
238
|
+
out[dstPx + 3] = 255;
|
|
239
|
+
} else if (bitCount === 16) {
|
|
240
|
+
const val = view.getUint16(rowStart + x * 2, true);
|
|
241
|
+
out[dstPx] = Math.round(((val & rMask) >>> rShift) * 255 / rMax);
|
|
242
|
+
out[dstPx + 1] = Math.round(((val & gMask) >>> gShift) * 255 / gMax);
|
|
243
|
+
out[dstPx + 2] = Math.round(((val & bMask) >>> bShift) * 255 / bMax);
|
|
244
|
+
out[dstPx + 3] = 255;
|
|
245
|
+
} else if (bitCount === 24) {
|
|
246
|
+
const srcPx = rowStart + x * 3;
|
|
247
|
+
out[dstPx] = view.getUint8(srcPx + 2);
|
|
248
|
+
out[dstPx + 1] = view.getUint8(srcPx + 1);
|
|
249
|
+
out[dstPx + 2] = view.getUint8(srcPx);
|
|
250
|
+
out[dstPx + 3] = 255;
|
|
251
|
+
} else {
|
|
252
|
+
const srcPx = rowStart + x * 4;
|
|
253
|
+
const bb = view.getUint8(srcPx);
|
|
254
|
+
const gg = view.getUint8(srcPx + 1);
|
|
255
|
+
const rr = view.getUint8(srcPx + 2);
|
|
256
|
+
const aa = view.getUint8(srcPx + 3);
|
|
257
|
+
out[dstPx] = rr;
|
|
258
|
+
out[dstPx + 1] = gg;
|
|
259
|
+
out[dstPx + 2] = bb;
|
|
260
|
+
out[dstPx + 3] = aa === 0 ? 255 : aa;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/emf-dib-decoder.ts
|
|
267
|
+
function decodeDibToImageData(view, bmiOffset, bitsOffset, bitsSize) {
|
|
268
|
+
if (bmiOffset < 0 || bitsOffset < 0 || bmiOffset + 40 > view.byteLength || bitsOffset + bitsSize > view.byteLength) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const headerSize = view.getUint32(bmiOffset, true);
|
|
272
|
+
if (headerSize < 40 || bmiOffset + headerSize > view.byteLength) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
const width = view.getInt32(bmiOffset + 4, true);
|
|
276
|
+
const heightRaw = view.getInt32(bmiOffset + 8, true);
|
|
277
|
+
const planes = view.getUint16(bmiOffset + 12, true);
|
|
278
|
+
const bitCount = view.getUint16(bmiOffset + 14, true);
|
|
279
|
+
const compression = view.getUint32(bmiOffset + 16, true);
|
|
280
|
+
if (planes !== 1 || width <= 0 || heightRaw === 0) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
if (width > 8192 || Math.abs(heightRaw) > 8192) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
const BI_RGB = 0;
|
|
287
|
+
const BI_RLE8 = 1;
|
|
288
|
+
const BI_RLE4 = 2;
|
|
289
|
+
const BI_BITFIELDS2 = 3;
|
|
290
|
+
if (bitCount !== 1 && bitCount !== 4 && bitCount !== 8 && bitCount !== 16 && bitCount !== 24 && bitCount !== 32) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (compression === BI_RLE8 && bitCount !== 8) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
if (compression === BI_RLE4 && bitCount !== 4) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
if (compression === BI_BITFIELDS2 && bitCount !== 16 && bitCount !== 32) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
if (compression !== BI_RGB && compression !== BI_RLE8 && compression !== BI_RLE4 && compression !== BI_BITFIELDS2) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
const height = Math.abs(heightRaw);
|
|
306
|
+
const topDown = heightRaw < 0;
|
|
307
|
+
const colorTable = [];
|
|
308
|
+
if (bitCount <= 8) {
|
|
309
|
+
const maxColors = 1 << bitCount;
|
|
310
|
+
const colorsUsed = view.getUint32(bmiOffset + 32, true) || maxColors;
|
|
311
|
+
const numColors = Math.min(colorsUsed, maxColors);
|
|
312
|
+
const ctOffset = bmiOffset + headerSize;
|
|
313
|
+
if (ctOffset + numColors * 4 > view.byteLength) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
for (let i = 0; i < numColors; i++) {
|
|
317
|
+
const b = view.getUint8(ctOffset + i * 4);
|
|
318
|
+
const g = view.getUint8(ctOffset + i * 4 + 1);
|
|
319
|
+
const r = view.getUint8(ctOffset + i * 4 + 2);
|
|
320
|
+
colorTable.push([r, g, b]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const masks = parseBitfieldMasks(view, bmiOffset, headerSize, compression, bitCount);
|
|
324
|
+
if (!masks) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
const out = new Uint8ClampedArray(width * height * 4);
|
|
328
|
+
if (compression === BI_RLE8 || compression === BI_RLE4) {
|
|
329
|
+
const setPixel = (x, y, r, g, b, a) => {
|
|
330
|
+
const dstPx = (y * width + x) * 4;
|
|
331
|
+
out[dstPx] = r;
|
|
332
|
+
out[dstPx + 1] = g;
|
|
333
|
+
out[dstPx + 2] = b;
|
|
334
|
+
out[dstPx + 3] = a;
|
|
335
|
+
};
|
|
336
|
+
return decodeRleBitmap(
|
|
337
|
+
view,
|
|
338
|
+
bitsOffset,
|
|
339
|
+
bitsSize,
|
|
340
|
+
width,
|
|
341
|
+
height,
|
|
342
|
+
topDown,
|
|
343
|
+
compression === BI_RLE4,
|
|
344
|
+
colorTable,
|
|
345
|
+
out,
|
|
346
|
+
setPixel
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
decodeUncompressedRows(
|
|
350
|
+
view,
|
|
351
|
+
bitsOffset,
|
|
352
|
+
width,
|
|
353
|
+
height,
|
|
354
|
+
topDown,
|
|
355
|
+
bitCount,
|
|
356
|
+
colorTable,
|
|
357
|
+
masks,
|
|
358
|
+
out
|
|
359
|
+
);
|
|
360
|
+
return createImageDataCompat(out, width, height);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/emf-gdi-brush-pattern.ts
|
|
364
|
+
var HATCH_ROWS = [
|
|
365
|
+
[0, 0, 0, 255, 0, 0, 0, 0],
|
|
366
|
+
// HS_HORIZONTAL
|
|
367
|
+
[8, 8, 8, 8, 8, 8, 8, 8],
|
|
368
|
+
// HS_VERTICAL
|
|
369
|
+
[128, 64, 32, 16, 8, 4, 2, 1],
|
|
370
|
+
// HS_FDIAGONAL
|
|
371
|
+
[1, 2, 4, 8, 16, 32, 64, 128],
|
|
372
|
+
// HS_BDIAGONAL
|
|
373
|
+
[8, 8, 8, 255, 8, 8, 8, 8],
|
|
374
|
+
// HS_CROSS
|
|
375
|
+
[129, 66, 36, 24, 24, 36, 66, 129]
|
|
376
|
+
// HS_DIAGCROSS
|
|
377
|
+
];
|
|
378
|
+
function hatchBit(hatch, x, y) {
|
|
379
|
+
const rows = HATCH_ROWS[hatch] ?? HATCH_ROWS[0];
|
|
380
|
+
return (rows[y & 7] >> 7 - (x & 7) & 1) === 1;
|
|
381
|
+
}
|
|
382
|
+
function cssHexToRgb(color) {
|
|
383
|
+
const m = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i.exec(color.trim());
|
|
384
|
+
if (!m) {
|
|
385
|
+
return 0;
|
|
386
|
+
}
|
|
387
|
+
const hex = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
|
|
388
|
+
return parseInt(hex, 16);
|
|
389
|
+
}
|
|
390
|
+
function decodeMonoBits(view, bmiOffset, bitsOffset) {
|
|
391
|
+
if (bmiOffset + 16 > view.byteLength) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
const width = view.getInt32(bmiOffset + 4, true);
|
|
395
|
+
const heightRaw = view.getInt32(bmiOffset + 8, true);
|
|
396
|
+
const bitCount = view.getUint16(bmiOffset + 14, true);
|
|
397
|
+
const height = Math.abs(heightRaw);
|
|
398
|
+
if (bitCount !== 1 || width <= 0 || height === 0 || width > 256 || height > 256) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
const stride = (width + 31 >> 5) * 4;
|
|
402
|
+
if (bitsOffset + stride * height > view.byteLength) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
const bits = new Uint8Array(width * height);
|
|
406
|
+
for (let row = 0; row < height; row++) {
|
|
407
|
+
const y = heightRaw > 0 ? height - 1 - row : row;
|
|
408
|
+
const rowOff = bitsOffset + row * stride;
|
|
409
|
+
for (let x = 0; x < width; x++) {
|
|
410
|
+
bits[y * width + x] = view.getUint8(rowOff + (x >> 3)) >> 7 - (x & 7) & 1;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return { width, height, bits };
|
|
414
|
+
}
|
|
415
|
+
function parsePatternBrush(view, offset, dataOff, mono) {
|
|
416
|
+
if (dataOff + 24 > view.byteLength) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
const offBmi = view.getUint32(dataOff + 8, true);
|
|
420
|
+
const offBits = view.getUint32(dataOff + 16, true);
|
|
421
|
+
const cbBits = view.getUint32(dataOff + 20, true);
|
|
422
|
+
const bmi = offset + offBmi;
|
|
423
|
+
const bitsAt = offset + offBits;
|
|
424
|
+
const bitCount = bmi + 16 <= view.byteLength ? view.getUint16(bmi + 14, true) : 0;
|
|
425
|
+
if (mono || bitCount === 1) {
|
|
426
|
+
const decoded = decodeMonoBits(view, bmi, bitsAt);
|
|
427
|
+
if (decoded && mono) {
|
|
428
|
+
return { kind: "mono", ...decoded };
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const image = decodeDibToImageData(view, bmi, bitsAt, cbBits);
|
|
432
|
+
if (!image) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
const rgb = new Uint32Array(image.width * image.height);
|
|
436
|
+
const d = image.data;
|
|
437
|
+
for (let i = 0; i < rgb.length; i++) {
|
|
438
|
+
rgb[i] = d[i * 4] << 16 | d[i * 4 + 1] << 8 | d[i * 4 + 2];
|
|
439
|
+
}
|
|
440
|
+
return { kind: "bitmap", width: image.width, height: image.height, rgb };
|
|
441
|
+
}
|
|
442
|
+
function realizeBrush(state) {
|
|
443
|
+
if (state.brushStyle === 1) {
|
|
444
|
+
return { kind: "none" };
|
|
445
|
+
}
|
|
446
|
+
const pattern = state.brushPattern;
|
|
447
|
+
if (!pattern) {
|
|
448
|
+
return { kind: "solid", rgb: cssHexToRgb(state.brushColor) };
|
|
449
|
+
}
|
|
450
|
+
if (pattern.kind === "bitmap") {
|
|
451
|
+
return { kind: "tile", width: pattern.width, height: pattern.height, rgb: pattern.rgb };
|
|
452
|
+
}
|
|
453
|
+
const bk = cssHexToRgb(state.bkColor);
|
|
454
|
+
if (pattern.kind === "hatch") {
|
|
455
|
+
const fg = cssHexToRgb(state.brushColor);
|
|
456
|
+
const rgb2 = new Uint32Array(64);
|
|
457
|
+
for (let y = 0; y < 8; y++) {
|
|
458
|
+
for (let x = 0; x < 8; x++) {
|
|
459
|
+
rgb2[y * 8 + x] = hatchBit(pattern.hatch, x, y) ? fg : bk;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return { kind: "tile", width: 8, height: 8, rgb: rgb2 };
|
|
463
|
+
}
|
|
464
|
+
const text = cssHexToRgb(state.textColor);
|
|
465
|
+
const rgb = new Uint32Array(pattern.width * pattern.height);
|
|
466
|
+
for (let i = 0; i < rgb.length; i++) {
|
|
467
|
+
rgb[i] = pattern.bits[i] ? bk : text;
|
|
468
|
+
}
|
|
469
|
+
return { kind: "tile", width: pattern.width, height: pattern.height, rgb };
|
|
470
|
+
}
|
|
471
|
+
function sampleTile(tile, devX, devY, orgX, orgY) {
|
|
472
|
+
const tx = ((devX - orgX) % tile.width + tile.width) % tile.width;
|
|
473
|
+
const ty = ((devY - orgY) % tile.height + tile.height) % tile.height;
|
|
474
|
+
return tile.rgb[ty * tile.width + tx];
|
|
475
|
+
}
|
|
476
|
+
|
|
55
477
|
// src/emf-gdi-text-layout.ts
|
|
56
478
|
function cumulativeGlyphOffsets(dx) {
|
|
57
479
|
const offsets = [];
|
|
@@ -237,6 +659,7 @@ var EMFPLUS_OBJECTTYPE_STRINGFORMAT = 7;
|
|
|
237
659
|
var EMFPLUS_OBJECTTYPE_REGION = 8;
|
|
238
660
|
var EMFPLUS_BRUSHTYPE_SOLID = 0;
|
|
239
661
|
var EMFPLUS_BRUSHTYPE_HATCHFILL = 1;
|
|
662
|
+
var EMFPLUS_BRUSHTYPE_TEXTUREFILL = 2;
|
|
240
663
|
var EMFPLUS_BRUSHTYPE_PATHGRADIENT = 3;
|
|
241
664
|
var EMFPLUS_BRUSHTYPE_LINEARGRADIENT = 4;
|
|
242
665
|
var META_EOF = 0;
|
|
@@ -487,7 +910,8 @@ function applyPen(ctx, state) {
|
|
|
487
910
|
function applyBrush(ctx, state) {
|
|
488
911
|
const paint = rop2Paint(state.rop2);
|
|
489
912
|
ctx.globalCompositeOperation = paint.gco;
|
|
490
|
-
|
|
913
|
+
const realized = realizeBrush(state);
|
|
914
|
+
if (realized.kind === "none") {
|
|
491
915
|
ctx.fillStyle = "rgba(0,0,0,0)";
|
|
492
916
|
return;
|
|
493
917
|
}
|
|
@@ -639,793 +1063,784 @@ async function exportCanvasToPngDataUrl(canvas) {
|
|
|
639
1063
|
emfLog(
|
|
640
1064
|
`exportCanvasToPngDataUrl: using HTMLCanvasElement.toDataURL (${canvas.width}\xD7${canvas.height})`
|
|
641
1065
|
);
|
|
642
|
-
return canvas.toDataURL("image/png");
|
|
643
|
-
}
|
|
644
|
-
if (nodeCanvasModule && canvas instanceof nodeCanvasModule.Canvas) {
|
|
645
|
-
emfLog(
|
|
646
|
-
`exportCanvasToPngDataUrl: using @napi-rs/canvas toDataURLAsync (${canvas.width}\xD7${canvas.height})`
|
|
647
|
-
);
|
|
648
|
-
return canvas.toDataURLAsync();
|
|
649
|
-
}
|
|
650
|
-
return null;
|
|
651
|
-
}
|
|
652
|
-
async function decodeDeferredImageBytes(bytes, mime) {
|
|
653
|
-
if (nodeCanvasModule) {
|
|
654
|
-
const image = await nodeCanvasModule.loadImage(new Uint8Array(bytes));
|
|
655
|
-
return { drawable: image, width: image.width, height: image.height, close: () => {
|
|
656
|
-
} };
|
|
657
|
-
}
|
|
658
|
-
if (typeof createImageBitmap !== "function") {
|
|
659
|
-
return null;
|
|
660
|
-
}
|
|
661
|
-
const blob = mime !== void 0 ? new Blob([bytes], { type: mime }) : new Blob([bytes]);
|
|
662
|
-
const bitmap = await createImageBitmap(blob);
|
|
663
|
-
return { drawable: bitmap, width: bitmap.width, height: bitmap.height, close: () => bitmap.close() };
|
|
664
|
-
}
|
|
665
|
-
function canvasDrawImage(ctx, image, dx, dy, dw, dh) {
|
|
666
|
-
const draw = ctx.drawImage;
|
|
667
|
-
draw.call(ctx, image, dx, dy, dw, dh);
|
|
668
|
-
}
|
|
669
|
-
function canvasGetImageData(ctx, x, y, w, h) {
|
|
670
|
-
const get = ctx.getImageData;
|
|
671
|
-
return get.call(ctx, x, y, w, h);
|
|
672
|
-
}
|
|
673
|
-
function canvasPutImageData(ctx, data, x, y) {
|
|
674
|
-
const put = ctx.putImageData;
|
|
675
|
-
put.call(ctx, data, x, y);
|
|
676
|
-
}
|
|
677
|
-
function canvasCreatePattern(ctx, image, repetition) {
|
|
678
|
-
const create = ctx.createPattern;
|
|
679
|
-
return create.call(ctx, image, repetition);
|
|
680
|
-
}
|
|
681
|
-
function createImageDataCompat(data, width, height) {
|
|
682
|
-
if (typeof ImageData !== "undefined") {
|
|
683
|
-
return new ImageData(data, width, height);
|
|
684
|
-
}
|
|
685
|
-
if (nodeCanvasModule) {
|
|
686
|
-
const Ctor = nodeCanvasModule.ImageData;
|
|
687
|
-
return new Ctor(data, width, height);
|
|
688
|
-
}
|
|
689
|
-
throw new ReferenceError("ImageData is not defined");
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// src/emf-header-parser.ts
|
|
693
|
-
function parseEmfHeader(view) {
|
|
694
|
-
emfLog("parseEmfHeader: byteLength =", view.byteLength);
|
|
695
|
-
if (view.byteLength < 88) {
|
|
696
|
-
return null;
|
|
697
|
-
}
|
|
698
|
-
const recordType = view.getUint32(0, true);
|
|
699
|
-
if (recordType !== EMR_HEADER) {
|
|
700
|
-
return null;
|
|
701
|
-
}
|
|
702
|
-
const boundsLeft = view.getInt32(8, true);
|
|
703
|
-
const boundsTop = view.getInt32(12, true);
|
|
704
|
-
const boundsRight = view.getInt32(16, true);
|
|
705
|
-
const boundsBottom = view.getInt32(20, true);
|
|
706
|
-
const frameLeft = view.getInt32(24, true);
|
|
707
|
-
const frameTop = view.getInt32(28, true);
|
|
708
|
-
const frameRight = view.getInt32(32, true);
|
|
709
|
-
const frameBottom = view.getInt32(36, true);
|
|
710
|
-
const frameW = frameRight - frameLeft;
|
|
711
|
-
const frameH = frameBottom - frameTop;
|
|
712
|
-
return {
|
|
713
|
-
bounds: {
|
|
714
|
-
left: boundsLeft,
|
|
715
|
-
top: boundsTop,
|
|
716
|
-
right: boundsRight,
|
|
717
|
-
bottom: boundsBottom
|
|
718
|
-
},
|
|
719
|
-
frameW,
|
|
720
|
-
frameH
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
function getRenderableEmfBounds(header) {
|
|
724
|
-
const boundsW = header.bounds.right - header.bounds.left;
|
|
725
|
-
const boundsH = header.bounds.bottom - header.bounds.top;
|
|
726
|
-
if (boundsW > 0 && boundsH > 0) {
|
|
727
|
-
return header.bounds;
|
|
728
|
-
}
|
|
729
|
-
if (header.frameW > 0 && header.frameH > 0) {
|
|
730
|
-
emfLog(
|
|
731
|
-
`getRenderableEmfBounds: bounds invalid (${boundsW}\xD7${boundsH}), falling back to frame ${header.frameW}\xD7${header.frameH}`
|
|
732
|
-
);
|
|
733
|
-
return { left: 0, top: 0, right: header.frameW, bottom: header.frameH };
|
|
734
|
-
}
|
|
735
|
-
return null;
|
|
736
|
-
}
|
|
737
|
-
function parseWmfHeader(view) {
|
|
738
|
-
if (view.byteLength < 22) {
|
|
739
|
-
return null;
|
|
740
|
-
}
|
|
741
|
-
const magic = view.getUint32(0, true);
|
|
742
|
-
let headerOffset = 0;
|
|
743
|
-
let boundsLeft = 0;
|
|
744
|
-
let boundsTop = 0;
|
|
745
|
-
let boundsRight = 800;
|
|
746
|
-
let boundsBottom = 600;
|
|
747
|
-
let unitsPerInch = 96;
|
|
748
|
-
if (magic === 2596720087) {
|
|
749
|
-
boundsLeft = view.getInt16(6, true);
|
|
750
|
-
boundsTop = view.getInt16(8, true);
|
|
751
|
-
boundsRight = view.getInt16(10, true);
|
|
752
|
-
boundsBottom = view.getInt16(12, true);
|
|
753
|
-
unitsPerInch = view.getUint16(14, true) || 96;
|
|
754
|
-
headerOffset = 22;
|
|
755
|
-
}
|
|
756
|
-
if (headerOffset + 18 > view.byteLength) {
|
|
757
|
-
return null;
|
|
758
|
-
}
|
|
759
|
-
const fileType = view.getUint16(headerOffset, true);
|
|
760
|
-
if (fileType !== 1 && fileType !== 2) {
|
|
761
|
-
return null;
|
|
762
|
-
}
|
|
763
|
-
const headerSize = view.getUint16(headerOffset + 2, true) * 2;
|
|
764
|
-
const maxRecordSize = view.getUint32(headerOffset + 8, true) * 2;
|
|
765
|
-
return {
|
|
766
|
-
headerSize: headerOffset + headerSize,
|
|
767
|
-
maxRecordSize,
|
|
768
|
-
boundsLeft,
|
|
769
|
-
boundsTop,
|
|
770
|
-
boundsRight,
|
|
771
|
-
boundsBottom,
|
|
772
|
-
unitsPerInch
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// src/emf-gdi-coord.ts
|
|
777
|
-
function gmx(r, x) {
|
|
778
|
-
const wt = r.state.worldTransform;
|
|
779
|
-
const px = wt[0] * x + wt[4];
|
|
780
|
-
if (r.useMappingMode) {
|
|
781
|
-
return (px - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
|
|
782
|
-
}
|
|
783
|
-
return (px - r.bounds.left) * r.sx;
|
|
784
|
-
}
|
|
785
|
-
function gmy(r, y) {
|
|
786
|
-
const wt = r.state.worldTransform;
|
|
787
|
-
const py = wt[3] * y + wt[5];
|
|
788
|
-
if (r.useMappingMode) {
|
|
789
|
-
return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
|
|
790
|
-
}
|
|
791
|
-
return (py - r.bounds.top) * r.sy;
|
|
792
|
-
}
|
|
793
|
-
function gmw(r, w) {
|
|
794
|
-
const pw = r.state.worldTransform[0] * w;
|
|
795
|
-
if (r.useMappingMode) {
|
|
796
|
-
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
|
|
797
|
-
}
|
|
798
|
-
return pw * r.sx;
|
|
799
|
-
}
|
|
800
|
-
function gmh(r, h) {
|
|
801
|
-
const ph = r.state.worldTransform[3] * h;
|
|
802
|
-
if (r.useMappingMode) {
|
|
803
|
-
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
|
|
804
|
-
}
|
|
805
|
-
return ph * r.sy;
|
|
806
|
-
}
|
|
807
|
-
function activateGdiMappingMode(r) {
|
|
808
|
-
r.useMappingMode = true;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
// src/emf-gdi-draw-shapes.ts
|
|
812
|
-
function handleSetPixelV(rCtx, dataOff, recSize) {
|
|
813
|
-
const { ctx, view } = rCtx;
|
|
814
|
-
if (recSize >= 20) {
|
|
815
|
-
const x = view.getInt32(dataOff, true);
|
|
816
|
-
const y = view.getInt32(dataOff + 4, true);
|
|
817
|
-
const color = readColorRef(view, dataOff + 8);
|
|
818
|
-
ctx.fillStyle = color;
|
|
819
|
-
ctx.fillRect(gmx(rCtx, x), gmy(rCtx, y), 1, 1);
|
|
820
|
-
}
|
|
821
|
-
return true;
|
|
822
|
-
}
|
|
823
|
-
function handleMoveToEx(rCtx, dataOff, recSize) {
|
|
824
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
825
|
-
if (recSize >= 16) {
|
|
826
|
-
state.curX = view.getInt32(dataOff, true);
|
|
827
|
-
state.curY = view.getInt32(dataOff + 4, true);
|
|
828
|
-
if (inPath) {
|
|
829
|
-
ctx.moveTo(gmx(rCtx, state.curX), gmy(rCtx, state.curY));
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
return true;
|
|
833
|
-
}
|
|
834
|
-
function handleLineTo(rCtx, dataOff, recSize) {
|
|
835
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
836
|
-
if (recSize >= 16) {
|
|
837
|
-
const lx = view.getInt32(dataOff, true);
|
|
838
|
-
const ly = view.getInt32(dataOff + 4, true);
|
|
839
|
-
if (inPath) {
|
|
840
|
-
ctx.lineTo(gmx(rCtx, lx), gmy(rCtx, ly));
|
|
841
|
-
} else {
|
|
842
|
-
applyPen(ctx, state);
|
|
843
|
-
ctx.beginPath();
|
|
844
|
-
ctx.moveTo(gmx(rCtx, state.curX), gmy(rCtx, state.curY));
|
|
845
|
-
ctx.lineTo(gmx(rCtx, lx), gmy(rCtx, ly));
|
|
846
|
-
ctx.stroke();
|
|
847
|
-
}
|
|
848
|
-
state.curX = lx;
|
|
849
|
-
state.curY = ly;
|
|
1066
|
+
return canvas.toDataURL("image/png");
|
|
850
1067
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const l = view.getInt32(dataOff, true);
|
|
857
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
858
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
859
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
860
|
-
if (inPath) {
|
|
861
|
-
ctx.rect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
862
|
-
} else {
|
|
863
|
-
applyBrush(ctx, state);
|
|
864
|
-
ctx.fillRect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
865
|
-
applyPen(ctx, state);
|
|
866
|
-
ctx.strokeRect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
867
|
-
}
|
|
1068
|
+
if (nodeCanvasModule && canvas instanceof nodeCanvasModule.Canvas) {
|
|
1069
|
+
emfLog(
|
|
1070
|
+
`exportCanvasToPngDataUrl: using @napi-rs/canvas toDataURLAsync (${canvas.width}\xD7${canvas.height})`
|
|
1071
|
+
);
|
|
1072
|
+
return canvas.toDataURLAsync();
|
|
868
1073
|
}
|
|
869
|
-
return
|
|
1074
|
+
return null;
|
|
870
1075
|
}
|
|
871
|
-
function
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
877
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
878
|
-
const rw = Math.abs(gmw(rCtx, view.getInt32(dataOff + 16, true))) / 2;
|
|
879
|
-
const rh = Math.abs(gmh(rCtx, view.getInt32(dataOff + 20, true))) / 2;
|
|
880
|
-
const x1 = gmx(rCtx, l);
|
|
881
|
-
const y1 = gmy(rCtx, t);
|
|
882
|
-
const w = gmw(rCtx, r - l);
|
|
883
|
-
const h = gmh(rCtx, b - t);
|
|
884
|
-
const drawRoundRect = () => {
|
|
885
|
-
const radius = Math.min(rw, rh, w / 2, h / 2);
|
|
886
|
-
ctx.moveTo(x1 + radius, y1);
|
|
887
|
-
ctx.lineTo(x1 + w - radius, y1);
|
|
888
|
-
ctx.arcTo(x1 + w, y1, x1 + w, y1 + radius, radius);
|
|
889
|
-
ctx.lineTo(x1 + w, y1 + h - radius);
|
|
890
|
-
ctx.arcTo(x1 + w, y1 + h, x1 + w - radius, y1 + h, radius);
|
|
891
|
-
ctx.lineTo(x1 + radius, y1 + h);
|
|
892
|
-
ctx.arcTo(x1, y1 + h, x1, y1 + h - radius, radius);
|
|
893
|
-
ctx.lineTo(x1, y1 + radius);
|
|
894
|
-
ctx.arcTo(x1, y1, x1 + radius, y1, radius);
|
|
895
|
-
ctx.closePath();
|
|
896
|
-
};
|
|
897
|
-
if (inPath) {
|
|
898
|
-
drawRoundRect();
|
|
899
|
-
} else {
|
|
900
|
-
ctx.beginPath();
|
|
901
|
-
drawRoundRect();
|
|
902
|
-
applyBrush(ctx, state);
|
|
903
|
-
ctx.fill();
|
|
904
|
-
applyPen(ctx, state);
|
|
905
|
-
ctx.stroke();
|
|
906
|
-
}
|
|
1076
|
+
async function decodeDeferredImageBytes(bytes, mime) {
|
|
1077
|
+
if (nodeCanvasModule) {
|
|
1078
|
+
const image = await nodeCanvasModule.loadImage(new Uint8Array(bytes));
|
|
1079
|
+
return { drawable: image, width: image.width, height: image.height, close: () => {
|
|
1080
|
+
} };
|
|
907
1081
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
function handleEllipse(rCtx, dataOff, recSize) {
|
|
911
|
-
const { ctx, view, state, inPath } = rCtx;
|
|
912
|
-
if (recSize >= 24) {
|
|
913
|
-
const l = view.getInt32(dataOff, true);
|
|
914
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
915
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
916
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
917
|
-
const cx = gmx(rCtx, (l + r) / 2);
|
|
918
|
-
const cy = gmy(rCtx, (t + b) / 2);
|
|
919
|
-
const rx = Math.abs(gmw(rCtx, r - l)) / 2;
|
|
920
|
-
const ry = Math.abs(gmh(rCtx, b - t)) / 2;
|
|
921
|
-
if (inPath) {
|
|
922
|
-
ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
923
|
-
} else {
|
|
924
|
-
ctx.beginPath();
|
|
925
|
-
ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
|
|
926
|
-
applyBrush(ctx, state);
|
|
927
|
-
ctx.fill();
|
|
928
|
-
applyPen(ctx, state);
|
|
929
|
-
ctx.stroke();
|
|
930
|
-
}
|
|
1082
|
+
if (typeof createImageBitmap !== "function") {
|
|
1083
|
+
return null;
|
|
931
1084
|
}
|
|
932
|
-
|
|
1085
|
+
const blob = mime !== void 0 ? new Blob([bytes], { type: mime }) : new Blob([bytes]);
|
|
1086
|
+
const bitmap = await createImageBitmap(blob);
|
|
1087
|
+
return { drawable: bitmap, width: bitmap.width, height: bitmap.height, close: () => bitmap.close() };
|
|
933
1088
|
}
|
|
934
|
-
function
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
const l = view.getInt32(dataOff, true);
|
|
938
|
-
const t = view.getInt32(dataOff + 4, true);
|
|
939
|
-
const r = view.getInt32(dataOff + 8, true);
|
|
940
|
-
const b = view.getInt32(dataOff + 12, true);
|
|
941
|
-
const startX = view.getInt32(dataOff + 16, true);
|
|
942
|
-
const startY = view.getInt32(dataOff + 20, true);
|
|
943
|
-
const endX = view.getInt32(dataOff + 24, true);
|
|
944
|
-
const endY = view.getInt32(dataOff + 28, true);
|
|
945
|
-
const cxA = (l + r) / 2;
|
|
946
|
-
const cyA = (t + b) / 2;
|
|
947
|
-
const rx = Math.abs(r - l) / 2;
|
|
948
|
-
const ry = Math.abs(b - t) / 2;
|
|
949
|
-
const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
|
|
950
|
-
const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
|
|
951
|
-
const mcx = gmx(rCtx, cxA);
|
|
952
|
-
const mcy = gmy(rCtx, cyA);
|
|
953
|
-
const mrx = Math.abs(gmw(rCtx, rx));
|
|
954
|
-
const mry = Math.abs(gmh(rCtx, ry));
|
|
955
|
-
const isArcTo = recType === EMR_ARCTO;
|
|
956
|
-
const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
|
|
957
|
-
if (!inPath) {
|
|
958
|
-
ctx.beginPath();
|
|
959
|
-
}
|
|
960
|
-
if (recType === EMR_PIE) {
|
|
961
|
-
ctx.moveTo(mcx, mcy);
|
|
962
|
-
}
|
|
963
|
-
if (isArcTo) {
|
|
964
|
-
ctx.lineTo(mcx + mrx * Math.cos(startAngle), mcy + mry * Math.sin(startAngle));
|
|
965
|
-
}
|
|
966
|
-
ctx.ellipse(mcx, mcy, mrx, mry, 0, startAngle, endAngle, false);
|
|
967
|
-
if (recType === EMR_PIE || recType === EMR_CHORD) {
|
|
968
|
-
ctx.closePath();
|
|
969
|
-
}
|
|
970
|
-
if (!inPath) {
|
|
971
|
-
if (needsFill) {
|
|
972
|
-
applyBrush(ctx, state);
|
|
973
|
-
ctx.fill();
|
|
974
|
-
}
|
|
975
|
-
applyPen(ctx, state);
|
|
976
|
-
ctx.stroke();
|
|
977
|
-
}
|
|
978
|
-
if (isArcTo) {
|
|
979
|
-
state.curX = endX;
|
|
980
|
-
state.curY = endY;
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
return true;
|
|
1089
|
+
function canvasDrawImage(ctx, image, dx, dy, dw, dh) {
|
|
1090
|
+
const draw = ctx.drawImage;
|
|
1091
|
+
draw.call(ctx, image, dx, dy, dw, dh);
|
|
984
1092
|
}
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
default:
|
|
1005
|
-
return false;
|
|
1093
|
+
function canvasGetImageData(ctx, x, y, w, h) {
|
|
1094
|
+
const get = ctx.getImageData;
|
|
1095
|
+
return get.call(ctx, x, y, w, h);
|
|
1096
|
+
}
|
|
1097
|
+
function canvasPutImageData(ctx, data, x, y) {
|
|
1098
|
+
const put = ctx.putImageData;
|
|
1099
|
+
put.call(ctx, data, x, y);
|
|
1100
|
+
}
|
|
1101
|
+
function canvasCreatePattern(ctx, image, repetition) {
|
|
1102
|
+
const create = ctx.createPattern;
|
|
1103
|
+
return create.call(ctx, image, repetition);
|
|
1104
|
+
}
|
|
1105
|
+
function createImageDataCompat(data, width, height) {
|
|
1106
|
+
if (typeof ImageData !== "undefined") {
|
|
1107
|
+
return new ImageData(data, width, height);
|
|
1108
|
+
}
|
|
1109
|
+
if (nodeCanvasModule) {
|
|
1110
|
+
const Ctor = nodeCanvasModule.ImageData;
|
|
1111
|
+
return new Ctor(data, width, height);
|
|
1006
1112
|
}
|
|
1113
|
+
throw new ReferenceError("ImageData is not defined");
|
|
1007
1114
|
}
|
|
1008
1115
|
|
|
1009
|
-
// src/emf-
|
|
1010
|
-
function
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
x++;
|
|
1048
|
-
}
|
|
1049
|
-
if (count & 1) {
|
|
1050
|
-
off++;
|
|
1051
|
-
}
|
|
1052
|
-
} else {
|
|
1053
|
-
const bytes = Math.ceil(count / 2);
|
|
1054
|
-
let pi = 0;
|
|
1055
|
-
for (let i = 0; i < bytes && off < endOff; i++) {
|
|
1056
|
-
const byte = view.getUint8(off++);
|
|
1057
|
-
for (let nibble = 0; nibble < 2 && pi < count; nibble++) {
|
|
1058
|
-
const idx = nibble === 0 ? byte >> 4 & 15 : byte & 15;
|
|
1059
|
-
if (idx < colorTable.length && x < width) {
|
|
1060
|
-
setPixel(
|
|
1061
|
-
x,
|
|
1062
|
-
height - 1 - y,
|
|
1063
|
-
colorTable[idx][0],
|
|
1064
|
-
colorTable[idx][1],
|
|
1065
|
-
colorTable[idx][2],
|
|
1066
|
-
255
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
x++;
|
|
1070
|
-
pi++;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
if (bytes & 1) {
|
|
1074
|
-
off++;
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
} else if (!isRle4) {
|
|
1079
|
-
const idx = second;
|
|
1080
|
-
const c = idx < colorTable.length ? colorTable[idx] : [0, 0, 0];
|
|
1081
|
-
for (let i = 0; i < first && x < width; i++) {
|
|
1082
|
-
setPixel(x, height - 1 - y, c[0], c[1], c[2], 255);
|
|
1083
|
-
x++;
|
|
1084
|
-
}
|
|
1085
|
-
} else {
|
|
1086
|
-
const hi = second >> 4 & 15;
|
|
1087
|
-
const lo = second & 15;
|
|
1088
|
-
for (let i = 0; i < first && x < width; i++) {
|
|
1089
|
-
const idx = (i & 1) === 0 ? hi : lo;
|
|
1090
|
-
if (idx < colorTable.length) {
|
|
1091
|
-
setPixel(
|
|
1092
|
-
x,
|
|
1093
|
-
height - 1 - y,
|
|
1094
|
-
colorTable[idx][0],
|
|
1095
|
-
colorTable[idx][1],
|
|
1096
|
-
colorTable[idx][2],
|
|
1097
|
-
255
|
|
1098
|
-
);
|
|
1099
|
-
}
|
|
1100
|
-
x++;
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1116
|
+
// src/emf-header-parser.ts
|
|
1117
|
+
function parseEmfHeader(view) {
|
|
1118
|
+
emfLog("parseEmfHeader: byteLength =", view.byteLength);
|
|
1119
|
+
if (view.byteLength < 88) {
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
const recordType = view.getUint32(0, true);
|
|
1123
|
+
if (recordType !== EMR_HEADER) {
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
const boundsLeft = view.getInt32(8, true);
|
|
1127
|
+
const boundsTop = view.getInt32(12, true);
|
|
1128
|
+
const boundsRight = view.getInt32(16, true);
|
|
1129
|
+
const boundsBottom = view.getInt32(20, true);
|
|
1130
|
+
const frameLeft = view.getInt32(24, true);
|
|
1131
|
+
const frameTop = view.getInt32(28, true);
|
|
1132
|
+
const frameRight = view.getInt32(32, true);
|
|
1133
|
+
const frameBottom = view.getInt32(36, true);
|
|
1134
|
+
const frameW = frameRight - frameLeft;
|
|
1135
|
+
const frameH = frameBottom - frameTop;
|
|
1136
|
+
return {
|
|
1137
|
+
bounds: {
|
|
1138
|
+
left: boundsLeft,
|
|
1139
|
+
top: boundsTop,
|
|
1140
|
+
right: boundsRight,
|
|
1141
|
+
bottom: boundsBottom
|
|
1142
|
+
},
|
|
1143
|
+
frameW,
|
|
1144
|
+
frameH
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
function getRenderableEmfBounds(header) {
|
|
1148
|
+
const boundsW = header.bounds.right - header.bounds.left;
|
|
1149
|
+
const boundsH = header.bounds.bottom - header.bounds.top;
|
|
1150
|
+
if (boundsW > 0 && boundsH > 0) {
|
|
1151
|
+
return header.bounds;
|
|
1103
1152
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1153
|
+
if (header.frameW > 0 && header.frameH > 0) {
|
|
1154
|
+
emfLog(
|
|
1155
|
+
`getRenderableEmfBounds: bounds invalid (${boundsW}\xD7${boundsH}), falling back to frame ${header.frameW}\xD7${header.frameH}`
|
|
1156
|
+
);
|
|
1157
|
+
return { left: 0, top: 0, right: header.frameW, bottom: header.frameH };
|
|
1158
|
+
}
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
function parseWmfHeader(view) {
|
|
1162
|
+
if (view.byteLength < 22) {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
const magic = view.getUint32(0, true);
|
|
1166
|
+
let headerOffset = 0;
|
|
1167
|
+
let boundsLeft = 0;
|
|
1168
|
+
let boundsTop = 0;
|
|
1169
|
+
let boundsRight = 800;
|
|
1170
|
+
let boundsBottom = 600;
|
|
1171
|
+
let unitsPerInch = 96;
|
|
1172
|
+
if (magic === 2596720087) {
|
|
1173
|
+
boundsLeft = view.getInt16(6, true);
|
|
1174
|
+
boundsTop = view.getInt16(8, true);
|
|
1175
|
+
boundsRight = view.getInt16(10, true);
|
|
1176
|
+
boundsBottom = view.getInt16(12, true);
|
|
1177
|
+
unitsPerInch = view.getUint16(14, true) || 96;
|
|
1178
|
+
headerOffset = 22;
|
|
1179
|
+
}
|
|
1180
|
+
if (headerOffset + 18 > view.byteLength) {
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
1183
|
+
const fileType = view.getUint16(headerOffset, true);
|
|
1184
|
+
if (fileType !== 1 && fileType !== 2) {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
const headerSize = view.getUint16(headerOffset + 2, true) * 2;
|
|
1188
|
+
const maxRecordSize = view.getUint32(headerOffset + 8, true) * 2;
|
|
1189
|
+
return {
|
|
1190
|
+
headerSize: headerOffset + headerSize,
|
|
1191
|
+
maxRecordSize,
|
|
1192
|
+
boundsLeft,
|
|
1193
|
+
boundsTop,
|
|
1194
|
+
boundsRight,
|
|
1195
|
+
boundsBottom,
|
|
1196
|
+
unitsPerInch
|
|
1197
|
+
};
|
|
1109
1198
|
}
|
|
1110
1199
|
|
|
1111
|
-
// src/emf-
|
|
1112
|
-
function
|
|
1113
|
-
|
|
1114
|
-
|
|
1200
|
+
// src/emf-gdi-coord.ts
|
|
1201
|
+
function gmx(r, x) {
|
|
1202
|
+
const wt = r.state.worldTransform;
|
|
1203
|
+
const px = wt[0] * x + wt[4];
|
|
1204
|
+
if (r.useMappingMode) {
|
|
1205
|
+
return (px - r.windowOrg.x) / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1) + r.viewportOrg.x;
|
|
1115
1206
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1207
|
+
return (px - r.bounds.left) * r.sx;
|
|
1208
|
+
}
|
|
1209
|
+
function gmy(r, y) {
|
|
1210
|
+
const wt = r.state.worldTransform;
|
|
1211
|
+
const py = wt[3] * y + wt[5];
|
|
1212
|
+
if (r.useMappingMode) {
|
|
1213
|
+
return (py - r.windowOrg.y) / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1) + r.viewportOrg.y;
|
|
1121
1214
|
}
|
|
1122
|
-
return
|
|
1215
|
+
return (py - r.bounds.top) * r.sy;
|
|
1123
1216
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
let rMax = 1, gMax = 1, bMax = 1;
|
|
1129
|
-
if (compression === BI_BITFIELDS) {
|
|
1130
|
-
const bfOff = bmiOffset + headerSize;
|
|
1131
|
-
if (bfOff + 12 > view.byteLength) {
|
|
1132
|
-
return null;
|
|
1133
|
-
}
|
|
1134
|
-
rMask = view.getUint32(bfOff, true);
|
|
1135
|
-
gMask = view.getUint32(bfOff + 4, true);
|
|
1136
|
-
bMask = view.getUint32(bfOff + 8, true);
|
|
1137
|
-
rShift = countTrailingZeros(rMask);
|
|
1138
|
-
gShift = countTrailingZeros(gMask);
|
|
1139
|
-
bShift = countTrailingZeros(bMask);
|
|
1140
|
-
rMax = rMask >>> rShift || 1;
|
|
1141
|
-
gMax = gMask >>> gShift || 1;
|
|
1142
|
-
bMax = bMask >>> bShift || 1;
|
|
1143
|
-
} else if (bitCount === 16) {
|
|
1144
|
-
rMask = 31744;
|
|
1145
|
-
gMask = 992;
|
|
1146
|
-
bMask = 31;
|
|
1147
|
-
rShift = 10;
|
|
1148
|
-
gShift = 5;
|
|
1149
|
-
bShift = 0;
|
|
1150
|
-
rMax = 31;
|
|
1151
|
-
gMax = 31;
|
|
1152
|
-
bMax = 31;
|
|
1217
|
+
function gmw(r, w) {
|
|
1218
|
+
const pw = r.state.worldTransform[0] * w;
|
|
1219
|
+
if (r.useMappingMode) {
|
|
1220
|
+
return pw / (r.windowExt.cx || 1) * (r.viewportExt.cx || 1);
|
|
1153
1221
|
}
|
|
1154
|
-
return
|
|
1222
|
+
return pw * r.sx;
|
|
1155
1223
|
}
|
|
1156
|
-
function
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1224
|
+
function gmh(r, h) {
|
|
1225
|
+
const ph = r.state.worldTransform[3] * h;
|
|
1226
|
+
if (r.useMappingMode) {
|
|
1227
|
+
return ph / (r.windowExt.cy || 1) * (r.viewportExt.cy || 1);
|
|
1228
|
+
}
|
|
1229
|
+
return ph * r.sy;
|
|
1230
|
+
}
|
|
1231
|
+
function activateGdiMappingMode(r) {
|
|
1232
|
+
r.useMappingMode = true;
|
|
1233
|
+
}
|
|
1234
|
+
function hasWorldRotation(r) {
|
|
1235
|
+
const wt = r.state.worldTransform;
|
|
1236
|
+
return wt[1] !== 0 || wt[2] !== 0;
|
|
1237
|
+
}
|
|
1238
|
+
function gdiDeviceMatrix(r) {
|
|
1239
|
+
const wt = r.state.worldTransform;
|
|
1240
|
+
if (r.useMappingMode) {
|
|
1241
|
+
const kx = (r.viewportExt.cx || 1) / (r.windowExt.cx || 1);
|
|
1242
|
+
const ky = (r.viewportExt.cy || 1) / (r.windowExt.cy || 1);
|
|
1243
|
+
return [
|
|
1244
|
+
wt[0] * kx,
|
|
1245
|
+
wt[1] * ky,
|
|
1246
|
+
wt[2] * kx,
|
|
1247
|
+
wt[3] * ky,
|
|
1248
|
+
(wt[4] - r.windowOrg.x) * kx + r.viewportOrg.x,
|
|
1249
|
+
(wt[5] - r.windowOrg.y) * ky + r.viewportOrg.y
|
|
1250
|
+
];
|
|
1251
|
+
}
|
|
1252
|
+
const sx = r.sx || 1;
|
|
1253
|
+
const sy = r.sy || 1;
|
|
1254
|
+
return [wt[0] * sx, wt[1] * sy, wt[2] * sx, wt[3] * sy, (wt[4] - r.bounds.left) * sx, (wt[5] - r.bounds.top) * sy];
|
|
1255
|
+
}
|
|
1256
|
+
function gmapPoint(r, x, y) {
|
|
1257
|
+
const m = gdiDeviceMatrix(r);
|
|
1258
|
+
return { x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5] };
|
|
1259
|
+
}
|
|
1260
|
+
function gdiEllipseParams(r, cx0, cy0, rx0, ry0) {
|
|
1261
|
+
const m = gdiDeviceMatrix(r);
|
|
1262
|
+
const center = gmapPoint(r, cx0, cy0);
|
|
1263
|
+
const a11 = m[0] * rx0;
|
|
1264
|
+
const a12 = m[2] * ry0;
|
|
1265
|
+
const a21 = m[1] * rx0;
|
|
1266
|
+
const a22 = m[3] * ry0;
|
|
1267
|
+
const p = a11 * a11 + a12 * a12;
|
|
1268
|
+
const q = a11 * a21 + a12 * a22;
|
|
1269
|
+
const s = a21 * a21 + a22 * a22;
|
|
1270
|
+
const mid = (p + s) / 2;
|
|
1271
|
+
const spread = Math.sqrt(Math.max(0, ((p - s) / 2) ** 2 + q * q));
|
|
1272
|
+
const lambda1 = Math.max(0, mid + spread);
|
|
1273
|
+
const lambda2 = Math.max(0, mid - spread);
|
|
1274
|
+
const rotation = 0.5 * Math.atan2(2 * q, p - s);
|
|
1275
|
+
return {
|
|
1276
|
+
cx: center.x,
|
|
1277
|
+
cy: center.y,
|
|
1278
|
+
rx: Math.sqrt(lambda1),
|
|
1279
|
+
ry: Math.sqrt(lambda2),
|
|
1280
|
+
rotation
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/emf-rop3.ts
|
|
1285
|
+
function rop3Index(rop) {
|
|
1286
|
+
return rop >>> 16 & 255;
|
|
1287
|
+
}
|
|
1288
|
+
function rop3Operands(index) {
|
|
1289
|
+
const t = index & 255;
|
|
1290
|
+
return {
|
|
1291
|
+
usesP: (t >> 4 & 15) !== (t & 15),
|
|
1292
|
+
usesS: (t >> 2 & 51) !== (t & 51),
|
|
1293
|
+
usesD: (t >> 1 & 85) !== (t & 85)
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function evalRop3(index, p, s, d, mask = 16777215) {
|
|
1297
|
+
let out = 0;
|
|
1298
|
+
const np = ~p;
|
|
1299
|
+
const ns = ~s;
|
|
1300
|
+
const nd = ~d;
|
|
1301
|
+
for (let i = 0; i < 8; i++) {
|
|
1302
|
+
if (index & 1 << i) {
|
|
1303
|
+
out |= (i & 4 ? p : np) & (i & 2 ? s : ns) & (i & 1 ? d : nd);
|
|
1164
1304
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
out[dstPx + 2] = view.getUint8(srcPx);
|
|
1204
|
-
out[dstPx + 3] = 255;
|
|
1205
|
-
} else {
|
|
1206
|
-
const srcPx = rowStart + x * 4;
|
|
1207
|
-
const bb = view.getUint8(srcPx);
|
|
1208
|
-
const gg = view.getUint8(srcPx + 1);
|
|
1209
|
-
const rr = view.getUint8(srcPx + 2);
|
|
1210
|
-
const aa = view.getUint8(srcPx + 3);
|
|
1211
|
-
out[dstPx] = rr;
|
|
1212
|
-
out[dstPx + 1] = gg;
|
|
1213
|
-
out[dstPx + 2] = bb;
|
|
1214
|
-
out[dstPx + 3] = aa === 0 ? 255 : aa;
|
|
1215
|
-
}
|
|
1305
|
+
}
|
|
1306
|
+
return out & mask;
|
|
1307
|
+
}
|
|
1308
|
+
function classifyRop3(rop) {
|
|
1309
|
+
const index = rop3Index(rop);
|
|
1310
|
+
switch (index) {
|
|
1311
|
+
case 204:
|
|
1312
|
+
return { kind: "copy" };
|
|
1313
|
+
case 0:
|
|
1314
|
+
return { kind: "solid", color: "black" };
|
|
1315
|
+
case 255:
|
|
1316
|
+
return { kind: "solid", color: "white" };
|
|
1317
|
+
case 170:
|
|
1318
|
+
return { kind: "noop" };
|
|
1319
|
+
case 85:
|
|
1320
|
+
return { kind: "invert-dest" };
|
|
1321
|
+
default:
|
|
1322
|
+
return { kind: "ternary", index, operands: rop3Operands(index) };
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
function applyRop3(dst, src, pattern, index, originX, originY) {
|
|
1326
|
+
const d = dst.data;
|
|
1327
|
+
const s = src ? src.data : null;
|
|
1328
|
+
const w = dst.width;
|
|
1329
|
+
const h = dst.height;
|
|
1330
|
+
const solidP = typeof pattern === "number" ? pattern : 0;
|
|
1331
|
+
const sampleP = typeof pattern === "function" ? pattern : null;
|
|
1332
|
+
for (let y = 0; y < h; y++) {
|
|
1333
|
+
for (let x = 0; x < w; x++) {
|
|
1334
|
+
const i = (y * w + x) * 4;
|
|
1335
|
+
const dv = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
|
|
1336
|
+
const sv = s ? s[i] << 16 | s[i + 1] << 8 | s[i + 2] : 0;
|
|
1337
|
+
const pv = sampleP ? sampleP(originX + x, originY + y) : solidP;
|
|
1338
|
+
const r = evalRop3(index, pv, sv, dv);
|
|
1339
|
+
d[i] = r >> 16 & 255;
|
|
1340
|
+
d[i + 1] = r >> 8 & 255;
|
|
1341
|
+
d[i + 2] = r & 255;
|
|
1342
|
+
d[i + 3] = 255;
|
|
1216
1343
|
}
|
|
1217
1344
|
}
|
|
1218
1345
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1346
|
+
function clampPositiveRect(x, y, w, h, canvasW, canvasH) {
|
|
1347
|
+
let left = w < 0 ? x + w : x;
|
|
1348
|
+
let top = h < 0 ? y + h : y;
|
|
1349
|
+
let width = Math.abs(w);
|
|
1350
|
+
let height = Math.abs(h);
|
|
1351
|
+
left = Math.round(left);
|
|
1352
|
+
top = Math.round(top);
|
|
1353
|
+
width = Math.round(width);
|
|
1354
|
+
height = Math.round(height);
|
|
1355
|
+
const right = Math.min(left + width, Math.round(canvasW));
|
|
1356
|
+
const bottom = Math.min(top + height, Math.round(canvasH));
|
|
1357
|
+
left = Math.max(left, 0);
|
|
1358
|
+
top = Math.max(top, 0);
|
|
1359
|
+
width = right - left;
|
|
1360
|
+
height = bottom - top;
|
|
1361
|
+
if (width <= 0 || height <= 0) {
|
|
1227
1362
|
return null;
|
|
1228
1363
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1364
|
+
return { x: left, y: top, w: width, h: height };
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/emf-rop2-exact.ts
|
|
1368
|
+
var ROP2_EXACT_INDEX = {
|
|
1369
|
+
[R2_MASKPEN]: 160,
|
|
1370
|
+
// P & D
|
|
1371
|
+
[R2_MERGEPEN]: 250,
|
|
1372
|
+
// P | D
|
|
1373
|
+
[R2_XORPEN]: 90,
|
|
1374
|
+
// P ^ D
|
|
1375
|
+
[R2_NOTXORPEN]: 165,
|
|
1376
|
+
// ~(P ^ D)
|
|
1377
|
+
[R2_MASKPENNOT]: 80,
|
|
1378
|
+
// P & ~D
|
|
1379
|
+
[R2_MERGEPENNOT]: 245,
|
|
1380
|
+
// P | ~D
|
|
1381
|
+
[R2_MASKNOTPEN]: 10,
|
|
1382
|
+
// ~P & D
|
|
1383
|
+
[R2_MERGENOTPEN]: 175,
|
|
1384
|
+
// ~P | D
|
|
1385
|
+
[R2_NOTMASKPEN]: 95,
|
|
1386
|
+
// ~(P & D)
|
|
1387
|
+
[R2_NOTMERGEPEN]: 5
|
|
1388
|
+
// ~(P | D)
|
|
1389
|
+
};
|
|
1390
|
+
function isExactRop2Bitwise(rop2) {
|
|
1391
|
+
return rop2 in ROP2_EXACT_INDEX;
|
|
1392
|
+
}
|
|
1393
|
+
function packRgb(r, g, b) {
|
|
1394
|
+
return r << 16 | g << 8 | b;
|
|
1395
|
+
}
|
|
1396
|
+
function surfaceSize(ctx) {
|
|
1397
|
+
const canvas = ctx.canvas;
|
|
1398
|
+
const w = canvas?.width;
|
|
1399
|
+
const h = canvas?.height;
|
|
1400
|
+
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
1401
|
+
}
|
|
1402
|
+
function paintWithExactRop2(ctx, rop2, color, draw) {
|
|
1403
|
+
const index = ROP2_EXACT_INDEX[rop2];
|
|
1404
|
+
if (index === void 0) {
|
|
1405
|
+
return false;
|
|
1236
1406
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1407
|
+
const size = surfaceSize(ctx);
|
|
1408
|
+
if (!size) {
|
|
1409
|
+
return false;
|
|
1239
1410
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1411
|
+
try {
|
|
1412
|
+
const temp = createTempCanvas(size.w, size.h);
|
|
1413
|
+
if (!temp) {
|
|
1414
|
+
return false;
|
|
1415
|
+
}
|
|
1416
|
+
temp.ctx.fillStyle = color;
|
|
1417
|
+
temp.ctx.strokeStyle = color;
|
|
1418
|
+
draw(temp.ctx);
|
|
1419
|
+
const paint = canvasGetImageData(temp.ctx, 0, 0, size.w, size.h);
|
|
1420
|
+
const dest = canvasGetImageData(ctx, 0, 0, size.w, size.h);
|
|
1421
|
+
const pd = paint.data;
|
|
1422
|
+
const dd = dest.data;
|
|
1423
|
+
for (let i = 0; i < dd.length; i += 4) {
|
|
1424
|
+
const coverage = pd[i + 3] / 255;
|
|
1425
|
+
if (coverage <= 0) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
const p = packRgb(pd[i], pd[i + 1], pd[i + 2]);
|
|
1429
|
+
const d = packRgb(dd[i], dd[i + 1], dd[i + 2]);
|
|
1430
|
+
const combined = evalRop3(index, p, d, d);
|
|
1431
|
+
const cr = combined >> 16 & 255;
|
|
1432
|
+
const cg = combined >> 8 & 255;
|
|
1433
|
+
const cb = combined & 255;
|
|
1434
|
+
dd[i] = Math.round(dd[i] + (cr - dd[i]) * coverage);
|
|
1435
|
+
dd[i + 1] = Math.round(dd[i + 1] + (cg - dd[i + 1]) * coverage);
|
|
1436
|
+
dd[i + 2] = Math.round(dd[i + 2] + (cb - dd[i + 2]) * coverage);
|
|
1437
|
+
dd[i + 3] = 255;
|
|
1438
|
+
}
|
|
1439
|
+
canvasPutImageData(ctx, dest, 0, 0);
|
|
1440
|
+
return true;
|
|
1441
|
+
} catch {
|
|
1442
|
+
return false;
|
|
1246
1443
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/emf-gdi-shape-paint.ts
|
|
1447
|
+
function surfaceSize2(ctx) {
|
|
1448
|
+
const canvas = ctx.canvas;
|
|
1449
|
+
const w = canvas?.width;
|
|
1450
|
+
const h = canvas?.height;
|
|
1451
|
+
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
1452
|
+
}
|
|
1453
|
+
function fillCurrentPathWithGdiPattern(rCtx, fillRule) {
|
|
1454
|
+
const { ctx, bounds, state } = rCtx;
|
|
1455
|
+
const realized = realizeBrush(state);
|
|
1456
|
+
if (realized.kind !== "tile") {
|
|
1457
|
+
return false;
|
|
1249
1458
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1459
|
+
const size = surfaceSize2(ctx);
|
|
1460
|
+
if (!size || typeof ctx.isPointInPath !== "function") {
|
|
1461
|
+
return false;
|
|
1252
1462
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1463
|
+
const sx = rCtx.sx || 1;
|
|
1464
|
+
const sy = rCtx.sy || 1;
|
|
1465
|
+
try {
|
|
1466
|
+
const img = canvasGetImageData(ctx, 0, 0, size.w, size.h);
|
|
1467
|
+
const d = img.data;
|
|
1468
|
+
for (let y = 0; y < size.h; y++) {
|
|
1469
|
+
for (let x = 0; x < size.w; x++) {
|
|
1470
|
+
if (!ctx.isPointInPath(x + 0.5, y + 0.5, fillRule)) {
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
const lx = Math.floor(bounds.left + (x + 0.5) / sx);
|
|
1474
|
+
const ly = Math.floor(bounds.top + (y + 0.5) / sy);
|
|
1475
|
+
const c = sampleTile(realized, lx, ly, state.brushOrgX, state.brushOrgY);
|
|
1476
|
+
const o = (y * size.w + x) * 4;
|
|
1477
|
+
d[o] = c >>> 16 & 255;
|
|
1478
|
+
d[o + 1] = c >>> 8 & 255;
|
|
1479
|
+
d[o + 2] = c & 255;
|
|
1480
|
+
d[o + 3] = 255;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
canvasPutImageData(ctx, img, 0, 0);
|
|
1484
|
+
return true;
|
|
1485
|
+
} catch {
|
|
1486
|
+
return false;
|
|
1255
1487
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1488
|
+
}
|
|
1489
|
+
function fillShapeExactOrFast(rCtx, buildPath, fillRule = "nonzero") {
|
|
1490
|
+
const { ctx, state } = rCtx;
|
|
1491
|
+
if (state.brushStyle === 1) {
|
|
1492
|
+
return;
|
|
1258
1493
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
if (bitCount <= 8) {
|
|
1263
|
-
const maxColors = 1 << bitCount;
|
|
1264
|
-
const colorsUsed = view.getUint32(bmiOffset + 32, true) || maxColors;
|
|
1265
|
-
const numColors = Math.min(colorsUsed, maxColors);
|
|
1266
|
-
const ctOffset = bmiOffset + headerSize;
|
|
1267
|
-
if (ctOffset + numColors * 4 > view.byteLength) {
|
|
1268
|
-
return null;
|
|
1494
|
+
if (realizeBrush(state).kind === "tile") {
|
|
1495
|
+
if (fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
1496
|
+
return;
|
|
1269
1497
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1498
|
+
} else {
|
|
1499
|
+
const paint = rop2Paint(state.rop2);
|
|
1500
|
+
if (!paint.exact && isExactRop2Bitwise(state.rop2)) {
|
|
1501
|
+
const handled = paintWithExactRop2(ctx, state.rop2, state.brushColor, (scratch) => {
|
|
1502
|
+
buildPath(scratch);
|
|
1503
|
+
scratch.fill(fillRule);
|
|
1504
|
+
});
|
|
1505
|
+
if (handled) {
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1275
1508
|
}
|
|
1276
1509
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1510
|
+
applyBrush(ctx, state);
|
|
1511
|
+
ctx.fill(fillRule);
|
|
1512
|
+
}
|
|
1513
|
+
function applyPenGeometry(ctx, state) {
|
|
1514
|
+
ctx.lineWidth = Math.max(state.penWidth, 1);
|
|
1515
|
+
switch (state.penStyle) {
|
|
1516
|
+
case 1:
|
|
1517
|
+
ctx.setLineDash([8, 4]);
|
|
1518
|
+
break;
|
|
1519
|
+
case 2:
|
|
1520
|
+
ctx.setLineDash([2, 2]);
|
|
1521
|
+
break;
|
|
1522
|
+
case 3:
|
|
1523
|
+
ctx.setLineDash([8, 4, 2, 4]);
|
|
1524
|
+
break;
|
|
1525
|
+
case 4:
|
|
1526
|
+
ctx.setLineDash([8, 4, 2, 4, 2, 4]);
|
|
1527
|
+
break;
|
|
1528
|
+
default:
|
|
1529
|
+
ctx.setLineDash([]);
|
|
1530
|
+
break;
|
|
1280
1531
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
width,
|
|
1295
|
-
height,
|
|
1296
|
-
topDown,
|
|
1297
|
-
compression === BI_RLE4,
|
|
1298
|
-
colorTable,
|
|
1299
|
-
out,
|
|
1300
|
-
setPixel
|
|
1301
|
-
);
|
|
1532
|
+
}
|
|
1533
|
+
function strokeShapeExactOrFast(rCtx, buildPath) {
|
|
1534
|
+
const { ctx, state } = rCtx;
|
|
1535
|
+
const paint = rop2Paint(state.rop2);
|
|
1536
|
+
if (!paint.exact && isExactRop2Bitwise(state.rop2) && state.penStyle !== 5) {
|
|
1537
|
+
const handled = paintWithExactRop2(ctx, state.rop2, state.penColor, (scratch) => {
|
|
1538
|
+
applyPenGeometry(scratch, state);
|
|
1539
|
+
buildPath(scratch);
|
|
1540
|
+
scratch.stroke();
|
|
1541
|
+
});
|
|
1542
|
+
if (handled) {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1302
1545
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
bitsOffset,
|
|
1306
|
-
width,
|
|
1307
|
-
height,
|
|
1308
|
-
topDown,
|
|
1309
|
-
bitCount,
|
|
1310
|
-
colorTable,
|
|
1311
|
-
masks,
|
|
1312
|
-
out
|
|
1313
|
-
);
|
|
1314
|
-
return createImageDataCompat(out, width, height);
|
|
1546
|
+
applyPen(ctx, state);
|
|
1547
|
+
ctx.stroke();
|
|
1315
1548
|
}
|
|
1316
1549
|
|
|
1317
|
-
// src/emf-gdi-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
// HS_VERTICAL
|
|
1323
|
-
[128, 64, 32, 16, 8, 4, 2, 1],
|
|
1324
|
-
// HS_FDIAGONAL
|
|
1325
|
-
[1, 2, 4, 8, 16, 32, 64, 128],
|
|
1326
|
-
// HS_BDIAGONAL
|
|
1327
|
-
[8, 8, 8, 255, 8, 8, 8, 8],
|
|
1328
|
-
// HS_CROSS
|
|
1329
|
-
[129, 66, 36, 24, 24, 36, 66, 129]
|
|
1330
|
-
// HS_DIAGCROSS
|
|
1331
|
-
];
|
|
1332
|
-
function hatchBit(hatch, x, y) {
|
|
1333
|
-
const rows = HATCH_ROWS[hatch] ?? HATCH_ROWS[0];
|
|
1334
|
-
return (rows[y & 7] >> 7 - (x & 7) & 1) === 1;
|
|
1550
|
+
// src/emf-gdi-draw-shapes.ts
|
|
1551
|
+
function needsPathBasedRectangle(rCtx) {
|
|
1552
|
+
const paint = rop2Paint(rCtx.state.rop2);
|
|
1553
|
+
const ropExact = !paint.exact && isExactRop2Bitwise(rCtx.state.rop2);
|
|
1554
|
+
return ropExact || realizeBrush(rCtx.state).kind === "tile";
|
|
1335
1555
|
}
|
|
1336
|
-
function
|
|
1337
|
-
const
|
|
1338
|
-
if (
|
|
1339
|
-
|
|
1556
|
+
function handleSetPixelV(rCtx, dataOff, recSize) {
|
|
1557
|
+
const { ctx, view } = rCtx;
|
|
1558
|
+
if (recSize >= 20) {
|
|
1559
|
+
const x = view.getInt32(dataOff, true);
|
|
1560
|
+
const y = view.getInt32(dataOff + 4, true);
|
|
1561
|
+
const color = readColorRef(view, dataOff + 8);
|
|
1562
|
+
const p = hasWorldRotation(rCtx) ? gmapPoint(rCtx, x, y) : { x: gmx(rCtx, x), y: gmy(rCtx, y) };
|
|
1563
|
+
ctx.fillStyle = color;
|
|
1564
|
+
ctx.fillRect(p.x, p.y, 1, 1);
|
|
1340
1565
|
}
|
|
1341
|
-
|
|
1342
|
-
return parseInt(hex, 16);
|
|
1566
|
+
return true;
|
|
1343
1567
|
}
|
|
1344
|
-
function
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
return null;
|
|
1568
|
+
function handleMoveToEx(rCtx, dataOff, recSize) {
|
|
1569
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1570
|
+
if (recSize >= 16) {
|
|
1571
|
+
state.curX = view.getInt32(dataOff, true);
|
|
1572
|
+
state.curY = view.getInt32(dataOff + 4, true);
|
|
1573
|
+
if (inPath) {
|
|
1574
|
+
const p = hasWorldRotation(rCtx) ? gmapPoint(rCtx, state.curX, state.curY) : { x: gmx(rCtx, state.curX), y: gmy(rCtx, state.curY) };
|
|
1575
|
+
ctx.moveTo(p.x, p.y);
|
|
1576
|
+
}
|
|
1354
1577
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
function handleLineTo(rCtx, dataOff, recSize) {
|
|
1581
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1582
|
+
if (recSize >= 16) {
|
|
1583
|
+
const lx = view.getInt32(dataOff, true);
|
|
1584
|
+
const ly = view.getInt32(dataOff + 4, true);
|
|
1585
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1586
|
+
const to = rotated ? gmapPoint(rCtx, lx, ly) : { x: gmx(rCtx, lx), y: gmy(rCtx, ly) };
|
|
1587
|
+
if (inPath) {
|
|
1588
|
+
ctx.lineTo(to.x, to.y);
|
|
1589
|
+
} else {
|
|
1590
|
+
const from = rotated ? gmapPoint(rCtx, state.curX, state.curY) : { x: gmx(rCtx, state.curX), y: gmy(rCtx, state.curY) };
|
|
1591
|
+
const build = (c) => {
|
|
1592
|
+
c.beginPath();
|
|
1593
|
+
c.moveTo(from.x, from.y);
|
|
1594
|
+
c.lineTo(to.x, to.y);
|
|
1595
|
+
};
|
|
1596
|
+
build(ctx);
|
|
1597
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1598
|
+
}
|
|
1599
|
+
state.curX = lx;
|
|
1600
|
+
state.curY = ly;
|
|
1358
1601
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
function handleRectangle(rCtx, dataOff, recSize) {
|
|
1605
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1606
|
+
if (recSize >= 24) {
|
|
1607
|
+
const l = view.getInt32(dataOff, true);
|
|
1608
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1609
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1610
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1611
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1612
|
+
if (rotated) {
|
|
1613
|
+
const p1 = gmapPoint(rCtx, l, t);
|
|
1614
|
+
const p2 = gmapPoint(rCtx, r, t);
|
|
1615
|
+
const p3 = gmapPoint(rCtx, r, b);
|
|
1616
|
+
const p4 = gmapPoint(rCtx, l, b);
|
|
1617
|
+
const build = (c) => {
|
|
1618
|
+
c.beginPath();
|
|
1619
|
+
c.moveTo(p1.x, p1.y);
|
|
1620
|
+
c.lineTo(p2.x, p2.y);
|
|
1621
|
+
c.lineTo(p3.x, p3.y);
|
|
1622
|
+
c.lineTo(p4.x, p4.y);
|
|
1623
|
+
c.closePath();
|
|
1624
|
+
};
|
|
1625
|
+
if (inPath) {
|
|
1626
|
+
build(ctx);
|
|
1627
|
+
} else {
|
|
1628
|
+
build(ctx);
|
|
1629
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1630
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1631
|
+
}
|
|
1632
|
+
return true;
|
|
1633
|
+
}
|
|
1634
|
+
if (inPath) {
|
|
1635
|
+
ctx.rect(gmx(rCtx, l), gmy(rCtx, t), gmw(rCtx, r - l), gmh(rCtx, b - t));
|
|
1636
|
+
} else {
|
|
1637
|
+
const x = gmx(rCtx, l);
|
|
1638
|
+
const y = gmy(rCtx, t);
|
|
1639
|
+
const w = gmw(rCtx, r - l);
|
|
1640
|
+
const h = gmh(rCtx, b - t);
|
|
1641
|
+
if (needsPathBasedRectangle(rCtx)) {
|
|
1642
|
+
const build = (c) => {
|
|
1643
|
+
c.beginPath();
|
|
1644
|
+
c.rect(x, y, w, h);
|
|
1645
|
+
};
|
|
1646
|
+
build(ctx);
|
|
1647
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1648
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1649
|
+
} else {
|
|
1650
|
+
applyBrush(ctx, state);
|
|
1651
|
+
ctx.fillRect(x, y, w, h);
|
|
1652
|
+
applyPen(ctx, state);
|
|
1653
|
+
ctx.strokeRect(x, y, w, h);
|
|
1654
|
+
}
|
|
1365
1655
|
}
|
|
1366
1656
|
}
|
|
1367
|
-
return
|
|
1657
|
+
return true;
|
|
1368
1658
|
}
|
|
1369
|
-
function
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
-
|
|
1659
|
+
function handleRoundRect(rCtx, dataOff, recSize) {
|
|
1660
|
+
const { ctx, view, inPath } = rCtx;
|
|
1661
|
+
if (recSize >= 32) {
|
|
1662
|
+
const l = view.getInt32(dataOff, true);
|
|
1663
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1664
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1665
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1666
|
+
const cornerW = view.getInt32(dataOff + 16, true);
|
|
1667
|
+
const cornerH = view.getInt32(dataOff + 20, true);
|
|
1668
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1669
|
+
const rw = Math.abs(rotated ? cornerW / 2 : gmw(rCtx, cornerW) / 2);
|
|
1670
|
+
const rh = Math.abs(rotated ? cornerH / 2 : gmh(rCtx, cornerH) / 2);
|
|
1671
|
+
const x1 = rotated ? l : gmx(rCtx, l);
|
|
1672
|
+
const y1 = rotated ? t : gmy(rCtx, t);
|
|
1673
|
+
const w = rotated ? r - l : gmw(rCtx, r - l);
|
|
1674
|
+
const h = rotated ? b - t : gmh(rCtx, b - t);
|
|
1675
|
+
const map = (x, y) => rotated ? gmapPoint(rCtx, x, y) : { x, y };
|
|
1676
|
+
const drawRoundRect = (c) => {
|
|
1677
|
+
const radius = Math.min(rw, rh, Math.abs(w) / 2, Math.abs(h) / 2);
|
|
1678
|
+
const start = map(x1 + radius, y1);
|
|
1679
|
+
c.moveTo(start.x, start.y);
|
|
1680
|
+
if (rotated) {
|
|
1681
|
+
const corners = [
|
|
1682
|
+
[x1 + w - radius, y1],
|
|
1683
|
+
[x1 + w, y1 + radius],
|
|
1684
|
+
[x1 + w, y1 + h - radius],
|
|
1685
|
+
[x1 + w - radius, y1 + h],
|
|
1686
|
+
[x1 + radius, y1 + h],
|
|
1687
|
+
[x1, y1 + h - radius],
|
|
1688
|
+
[x1, y1 + radius],
|
|
1689
|
+
[x1 + radius, y1]
|
|
1690
|
+
];
|
|
1691
|
+
for (const [px, py] of corners) {
|
|
1692
|
+
const q = map(px, py);
|
|
1693
|
+
c.lineTo(q.x, q.y);
|
|
1694
|
+
}
|
|
1695
|
+
} else {
|
|
1696
|
+
const corner2 = map(x1 + w - radius, y1);
|
|
1697
|
+
c.lineTo(corner2.x, corner2.y);
|
|
1698
|
+
const arc1a = map(x1 + w, y1);
|
|
1699
|
+
const arc1b = map(x1 + w, y1 + radius);
|
|
1700
|
+
c.arcTo(arc1a.x, arc1a.y, arc1b.x, arc1b.y, radius);
|
|
1701
|
+
const corner3 = map(x1 + w, y1 + h - radius);
|
|
1702
|
+
c.lineTo(corner3.x, corner3.y);
|
|
1703
|
+
const arc2a = map(x1 + w, y1 + h);
|
|
1704
|
+
const arc2b = map(x1 + w - radius, y1 + h);
|
|
1705
|
+
c.arcTo(arc2a.x, arc2a.y, arc2b.x, arc2b.y, radius);
|
|
1706
|
+
const corner4 = map(x1 + radius, y1 + h);
|
|
1707
|
+
c.lineTo(corner4.x, corner4.y);
|
|
1708
|
+
const arc3a = map(x1, y1 + h);
|
|
1709
|
+
const arc3b = map(x1, y1 + h - radius);
|
|
1710
|
+
c.arcTo(arc3a.x, arc3a.y, arc3b.x, arc3b.y, radius);
|
|
1711
|
+
const corner1 = map(x1, y1 + radius);
|
|
1712
|
+
c.lineTo(corner1.x, corner1.y);
|
|
1713
|
+
const arc4a = map(x1, y1);
|
|
1714
|
+
const arc4b = map(x1 + radius, y1);
|
|
1715
|
+
c.arcTo(arc4a.x, arc4a.y, arc4b.x, arc4b.y, radius);
|
|
1716
|
+
}
|
|
1717
|
+
c.closePath();
|
|
1718
|
+
};
|
|
1719
|
+
if (inPath) {
|
|
1720
|
+
drawRoundRect(ctx);
|
|
1721
|
+
} else {
|
|
1722
|
+
const build = (c) => {
|
|
1723
|
+
c.beginPath();
|
|
1724
|
+
drawRoundRect(c);
|
|
1725
|
+
};
|
|
1726
|
+
build(ctx);
|
|
1727
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1728
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1383
1729
|
}
|
|
1384
1730
|
}
|
|
1385
|
-
|
|
1386
|
-
if (!image) {
|
|
1387
|
-
return null;
|
|
1388
|
-
}
|
|
1389
|
-
const rgb = new Uint32Array(image.width * image.height);
|
|
1390
|
-
const d = image.data;
|
|
1391
|
-
for (let i = 0; i < rgb.length; i++) {
|
|
1392
|
-
rgb[i] = d[i * 4] << 16 | d[i * 4 + 1] << 8 | d[i * 4 + 2];
|
|
1393
|
-
}
|
|
1394
|
-
return { kind: "bitmap", width: image.width, height: image.height, rgb };
|
|
1731
|
+
return true;
|
|
1395
1732
|
}
|
|
1396
|
-
function
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1733
|
+
function handleEllipse(rCtx, dataOff, recSize) {
|
|
1734
|
+
const { ctx, view, inPath } = rCtx;
|
|
1735
|
+
if (recSize >= 24) {
|
|
1736
|
+
const l = view.getInt32(dataOff, true);
|
|
1737
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1738
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1739
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1740
|
+
const params = hasWorldRotation(rCtx) ? gdiEllipseParams(rCtx, (l + r) / 2, (t + b) / 2, Math.abs(r - l) / 2, Math.abs(b - t) / 2) : {
|
|
1741
|
+
cx: gmx(rCtx, (l + r) / 2),
|
|
1742
|
+
cy: gmy(rCtx, (t + b) / 2),
|
|
1743
|
+
rx: Math.abs(gmw(rCtx, r - l)) / 2,
|
|
1744
|
+
ry: Math.abs(gmh(rCtx, b - t)) / 2,
|
|
1745
|
+
rotation: 0
|
|
1746
|
+
};
|
|
1747
|
+
if (inPath) {
|
|
1748
|
+
ctx.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
1749
|
+
} else {
|
|
1750
|
+
const build = (c) => {
|
|
1751
|
+
c.beginPath();
|
|
1752
|
+
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, 0, Math.PI * 2);
|
|
1753
|
+
};
|
|
1754
|
+
build(ctx);
|
|
1755
|
+
fillShapeExactOrFast(rCtx, build);
|
|
1756
|
+
strokeShapeExactOrFast(rCtx, build);
|
|
1757
|
+
}
|
|
1406
1758
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1759
|
+
return true;
|
|
1760
|
+
}
|
|
1761
|
+
function handleArcFamily(rCtx, recType, dataOff, recSize) {
|
|
1762
|
+
const { ctx, view, state, inPath } = rCtx;
|
|
1763
|
+
if (recSize >= 40) {
|
|
1764
|
+
const l = view.getInt32(dataOff, true);
|
|
1765
|
+
const t = view.getInt32(dataOff + 4, true);
|
|
1766
|
+
const r = view.getInt32(dataOff + 8, true);
|
|
1767
|
+
const b = view.getInt32(dataOff + 12, true);
|
|
1768
|
+
const startX = view.getInt32(dataOff + 16, true);
|
|
1769
|
+
const startY = view.getInt32(dataOff + 20, true);
|
|
1770
|
+
const endX = view.getInt32(dataOff + 24, true);
|
|
1771
|
+
const endY = view.getInt32(dataOff + 28, true);
|
|
1772
|
+
const cxA = (l + r) / 2;
|
|
1773
|
+
const cyA = (t + b) / 2;
|
|
1774
|
+
const rx = Math.abs(r - l) / 2;
|
|
1775
|
+
const ry = Math.abs(b - t) / 2;
|
|
1776
|
+
const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
|
|
1777
|
+
const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
|
|
1778
|
+
const rotated = hasWorldRotation(rCtx);
|
|
1779
|
+
const params = rotated ? gdiEllipseParams(rCtx, cxA, cyA, rx, ry) : {
|
|
1780
|
+
cx: gmx(rCtx, cxA),
|
|
1781
|
+
cy: gmy(rCtx, cyA),
|
|
1782
|
+
rx: Math.abs(gmw(rCtx, rx)),
|
|
1783
|
+
ry: Math.abs(gmh(rCtx, ry)),
|
|
1784
|
+
rotation: 0
|
|
1785
|
+
};
|
|
1786
|
+
const isArcTo = recType === EMR_ARCTO;
|
|
1787
|
+
const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
|
|
1788
|
+
const startPoint = rotated ? gmapPoint(rCtx, cxA + rx * Math.cos(startAngle), cyA + ry * Math.sin(startAngle)) : { x: params.cx + params.rx * Math.cos(startAngle), y: params.cy + params.ry * Math.sin(startAngle) };
|
|
1789
|
+
const build = (c) => {
|
|
1790
|
+
if (recType === EMR_PIE) {
|
|
1791
|
+
c.moveTo(params.cx, params.cy);
|
|
1792
|
+
}
|
|
1793
|
+
if (isArcTo) {
|
|
1794
|
+
c.lineTo(startPoint.x, startPoint.y);
|
|
1795
|
+
}
|
|
1796
|
+
c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, startAngle, endAngle, false);
|
|
1797
|
+
if (needsFill) {
|
|
1798
|
+
c.closePath();
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
if (!inPath) {
|
|
1802
|
+
ctx.beginPath();
|
|
1803
|
+
}
|
|
1804
|
+
build(ctx);
|
|
1805
|
+
if (!inPath) {
|
|
1806
|
+
const buildWithPath = (c) => {
|
|
1807
|
+
c.beginPath();
|
|
1808
|
+
build(c);
|
|
1809
|
+
};
|
|
1810
|
+
if (needsFill) {
|
|
1811
|
+
fillShapeExactOrFast(rCtx, buildWithPath);
|
|
1414
1812
|
}
|
|
1813
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
1814
|
+
}
|
|
1815
|
+
if (isArcTo) {
|
|
1816
|
+
state.curX = endX;
|
|
1817
|
+
state.curY = endY;
|
|
1415
1818
|
}
|
|
1416
|
-
return { kind: "tile", width: 8, height: 8, rgb: rgb2 };
|
|
1417
|
-
}
|
|
1418
|
-
const text = cssHexToRgb(state.textColor);
|
|
1419
|
-
const rgb = new Uint32Array(pattern.width * pattern.height);
|
|
1420
|
-
for (let i = 0; i < rgb.length; i++) {
|
|
1421
|
-
rgb[i] = pattern.bits[i] ? bk : text;
|
|
1422
1819
|
}
|
|
1423
|
-
return
|
|
1820
|
+
return true;
|
|
1424
1821
|
}
|
|
1425
|
-
function
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1822
|
+
function handleEmfGdiShapeRecord(rCtx, recType, dataOff, recSize) {
|
|
1823
|
+
switch (recType) {
|
|
1824
|
+
case EMR_SETPIXELV:
|
|
1825
|
+
return handleSetPixelV(rCtx, dataOff, recSize);
|
|
1826
|
+
case EMR_MOVETOEX:
|
|
1827
|
+
return handleMoveToEx(rCtx, dataOff, recSize);
|
|
1828
|
+
case EMR_LINETO:
|
|
1829
|
+
return handleLineTo(rCtx, dataOff, recSize);
|
|
1830
|
+
case EMR_RECTANGLE:
|
|
1831
|
+
return handleRectangle(rCtx, dataOff, recSize);
|
|
1832
|
+
case EMR_ROUNDRECT:
|
|
1833
|
+
return handleRoundRect(rCtx, dataOff, recSize);
|
|
1834
|
+
case EMR_ELLIPSE:
|
|
1835
|
+
return handleEllipse(rCtx, dataOff, recSize);
|
|
1836
|
+
case EMR_ARC:
|
|
1837
|
+
case EMR_ARCTO:
|
|
1838
|
+
case EMR_CHORD:
|
|
1839
|
+
case EMR_PIE:
|
|
1840
|
+
return handleArcFamily(rCtx, recType, dataOff, recSize);
|
|
1841
|
+
default:
|
|
1842
|
+
return false;
|
|
1843
|
+
}
|
|
1429
1844
|
}
|
|
1430
1845
|
|
|
1431
1846
|
// src/emf-gdi-stretch.ts
|
|
@@ -1480,108 +1895,25 @@ function stretchGdi(src, sx, sy, sw, sh, dw, dh, mode) {
|
|
|
1480
1895
|
let first = true;
|
|
1481
1896
|
for (const yy of ys) {
|
|
1482
1897
|
for (const xx of xs) {
|
|
1483
|
-
const p = read(xx, yy);
|
|
1484
|
-
if (mode === BLACKONWHITE) {
|
|
1485
|
-
v &= p;
|
|
1486
|
-
} else if (mode === WHITEONBLACK) {
|
|
1487
|
-
v |= p;
|
|
1488
|
-
} else if (first) {
|
|
1489
|
-
v = p;
|
|
1490
|
-
}
|
|
1491
|
-
first = false;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
const o = (y * W + x) * 4;
|
|
1495
|
-
data[o] = v >> 16 & 255;
|
|
1496
|
-
data[o + 1] = v >> 8 & 255;
|
|
1497
|
-
data[o + 2] = v & 255;
|
|
1498
|
-
data[o + 3] = 255;
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
return { width: W, height: H, data };
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
// src/emf-rop3.ts
|
|
1505
|
-
function rop3Index(rop) {
|
|
1506
|
-
return rop >>> 16 & 255;
|
|
1507
|
-
}
|
|
1508
|
-
function rop3Operands(index) {
|
|
1509
|
-
const t = index & 255;
|
|
1510
|
-
return {
|
|
1511
|
-
usesP: (t >> 4 & 15) !== (t & 15),
|
|
1512
|
-
usesS: (t >> 2 & 51) !== (t & 51),
|
|
1513
|
-
usesD: (t >> 1 & 85) !== (t & 85)
|
|
1514
|
-
};
|
|
1515
|
-
}
|
|
1516
|
-
function evalRop3(index, p, s, d, mask = 16777215) {
|
|
1517
|
-
let out = 0;
|
|
1518
|
-
const np = ~p;
|
|
1519
|
-
const ns = ~s;
|
|
1520
|
-
const nd = ~d;
|
|
1521
|
-
for (let i = 0; i < 8; i++) {
|
|
1522
|
-
if (index & 1 << i) {
|
|
1523
|
-
out |= (i & 4 ? p : np) & (i & 2 ? s : ns) & (i & 1 ? d : nd);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
return out & mask;
|
|
1527
|
-
}
|
|
1528
|
-
function classifyRop3(rop) {
|
|
1529
|
-
const index = rop3Index(rop);
|
|
1530
|
-
switch (index) {
|
|
1531
|
-
case 204:
|
|
1532
|
-
return { kind: "copy" };
|
|
1533
|
-
case 0:
|
|
1534
|
-
return { kind: "solid", color: "black" };
|
|
1535
|
-
case 255:
|
|
1536
|
-
return { kind: "solid", color: "white" };
|
|
1537
|
-
case 170:
|
|
1538
|
-
return { kind: "noop" };
|
|
1539
|
-
case 85:
|
|
1540
|
-
return { kind: "invert-dest" };
|
|
1541
|
-
default:
|
|
1542
|
-
return { kind: "ternary", index, operands: rop3Operands(index) };
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
function applyRop3(dst, src, pattern, index, originX, originY) {
|
|
1546
|
-
const d = dst.data;
|
|
1547
|
-
const s = src ? src.data : null;
|
|
1548
|
-
const w = dst.width;
|
|
1549
|
-
const h = dst.height;
|
|
1550
|
-
const solidP = typeof pattern === "number" ? pattern : 0;
|
|
1551
|
-
const sampleP = typeof pattern === "function" ? pattern : null;
|
|
1552
|
-
for (let y = 0; y < h; y++) {
|
|
1553
|
-
for (let x = 0; x < w; x++) {
|
|
1554
|
-
const i = (y * w + x) * 4;
|
|
1555
|
-
const dv = d[i] << 16 | d[i + 1] << 8 | d[i + 2];
|
|
1556
|
-
const sv = s ? s[i] << 16 | s[i + 1] << 8 | s[i + 2] : 0;
|
|
1557
|
-
const pv = sampleP ? sampleP(originX + x, originY + y) : solidP;
|
|
1558
|
-
const r = evalRop3(index, pv, sv, dv);
|
|
1559
|
-
d[i] = r >> 16 & 255;
|
|
1560
|
-
d[i + 1] = r >> 8 & 255;
|
|
1561
|
-
d[i + 2] = r & 255;
|
|
1562
|
-
d[i + 3] = 255;
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
function clampPositiveRect(x, y, w, h, canvasW, canvasH) {
|
|
1567
|
-
let left = w < 0 ? x + w : x;
|
|
1568
|
-
let top = h < 0 ? y + h : y;
|
|
1569
|
-
let width = Math.abs(w);
|
|
1570
|
-
let height = Math.abs(h);
|
|
1571
|
-
left = Math.round(left);
|
|
1572
|
-
top = Math.round(top);
|
|
1573
|
-
width = Math.round(width);
|
|
1574
|
-
height = Math.round(height);
|
|
1575
|
-
const right = Math.min(left + width, Math.round(canvasW));
|
|
1576
|
-
const bottom = Math.min(top + height, Math.round(canvasH));
|
|
1577
|
-
left = Math.max(left, 0);
|
|
1578
|
-
top = Math.max(top, 0);
|
|
1579
|
-
width = right - left;
|
|
1580
|
-
height = bottom - top;
|
|
1581
|
-
if (width <= 0 || height <= 0) {
|
|
1582
|
-
return null;
|
|
1898
|
+
const p = read(xx, yy);
|
|
1899
|
+
if (mode === BLACKONWHITE) {
|
|
1900
|
+
v &= p;
|
|
1901
|
+
} else if (mode === WHITEONBLACK) {
|
|
1902
|
+
v |= p;
|
|
1903
|
+
} else if (first) {
|
|
1904
|
+
v = p;
|
|
1905
|
+
}
|
|
1906
|
+
first = false;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
const o = (y * W + x) * 4;
|
|
1910
|
+
data[o] = v >> 16 & 255;
|
|
1911
|
+
data[o + 1] = v >> 8 & 255;
|
|
1912
|
+
data[o + 2] = v & 255;
|
|
1913
|
+
data[o + 3] = 255;
|
|
1914
|
+
}
|
|
1583
1915
|
}
|
|
1584
|
-
return {
|
|
1916
|
+
return { width: W, height: H, data };
|
|
1585
1917
|
}
|
|
1586
1918
|
|
|
1587
1919
|
// src/emf-gdi-draw-bitmap.ts
|
|
@@ -2317,6 +2649,25 @@ function handleEmfGdiDrawRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
2317
2649
|
}
|
|
2318
2650
|
|
|
2319
2651
|
// src/emf-gdi-polypolygon-helpers.ts
|
|
2652
|
+
function buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, close) {
|
|
2653
|
+
const { view } = rCtx;
|
|
2654
|
+
let pIdx = 0;
|
|
2655
|
+
for (let p = 0; p < numPolys; p++) {
|
|
2656
|
+
const count = view.getUint32(countsOff + p * 4, true);
|
|
2657
|
+
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
2658
|
+
const pt = readPoint(pIdx);
|
|
2659
|
+
if (i === 0) {
|
|
2660
|
+
target.moveTo(pt.x, pt.y);
|
|
2661
|
+
} else {
|
|
2662
|
+
target.lineTo(pt.x, pt.y);
|
|
2663
|
+
}
|
|
2664
|
+
pIdx++;
|
|
2665
|
+
}
|
|
2666
|
+
if (close) {
|
|
2667
|
+
target.closePath();
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2320
2671
|
function handlePolyPolygon32(rCtx, offset, dataOff, recSize) {
|
|
2321
2672
|
const { ctx, view, state, inPath } = rCtx;
|
|
2322
2673
|
const numPolys = view.getUint32(dataOff + 16, true);
|
|
@@ -2329,33 +2680,25 @@ function handlePolyPolygon32(rCtx, offset, dataOff, recSize) {
|
|
|
2329
2680
|
if (ptOff + totalPoints * 8 > offset + recSize) {
|
|
2330
2681
|
return;
|
|
2331
2682
|
}
|
|
2683
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt32(ptOff + pIdx * 8, true), view.getInt32(ptOff + pIdx * 8 + 4, true));
|
|
2684
|
+
const build = (target) => {
|
|
2685
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, true);
|
|
2686
|
+
};
|
|
2332
2687
|
if (!inPath) {
|
|
2333
2688
|
ctx.beginPath();
|
|
2334
2689
|
}
|
|
2335
|
-
|
|
2336
|
-
for (let p = 0; p < numPolys; p++) {
|
|
2337
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
2338
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
2339
|
-
const px = view.getInt32(ptOff + pIdx * 8, true);
|
|
2340
|
-
const py = view.getInt32(ptOff + pIdx * 8 + 4, true);
|
|
2341
|
-
if (i === 0) {
|
|
2342
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2343
|
-
} else {
|
|
2344
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2345
|
-
}
|
|
2346
|
-
pIdx++;
|
|
2347
|
-
}
|
|
2348
|
-
ctx.closePath();
|
|
2349
|
-
}
|
|
2690
|
+
build(ctx);
|
|
2350
2691
|
if (!inPath) {
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2692
|
+
const buildWithPath = (target) => {
|
|
2693
|
+
target.beginPath();
|
|
2694
|
+
build(target);
|
|
2695
|
+
};
|
|
2696
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2697
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
2355
2698
|
}
|
|
2356
2699
|
}
|
|
2357
2700
|
function handlePolyPolyline32(rCtx, offset, dataOff, recSize) {
|
|
2358
|
-
const { ctx, view,
|
|
2701
|
+
const { ctx, view, inPath } = rCtx;
|
|
2359
2702
|
const numPolys = view.getUint32(dataOff + 16, true);
|
|
2360
2703
|
const totalPoints = view.getUint32(dataOff + 20, true);
|
|
2361
2704
|
if (numPolys === 0 || numPolys >= 1e4 || totalPoints >= 1e5) {
|
|
@@ -2366,26 +2709,20 @@ function handlePolyPolyline32(rCtx, offset, dataOff, recSize) {
|
|
|
2366
2709
|
if (ptOff + totalPoints * 8 > offset + recSize) {
|
|
2367
2710
|
return;
|
|
2368
2711
|
}
|
|
2712
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt32(ptOff + pIdx * 8, true), view.getInt32(ptOff + pIdx * 8 + 4, true));
|
|
2713
|
+
const build = (target) => {
|
|
2714
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, false);
|
|
2715
|
+
};
|
|
2369
2716
|
if (!inPath) {
|
|
2370
2717
|
ctx.beginPath();
|
|
2371
2718
|
}
|
|
2372
|
-
|
|
2373
|
-
for (let p = 0; p < numPolys; p++) {
|
|
2374
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
2375
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
2376
|
-
const px = view.getInt32(ptOff + pIdx * 8, true);
|
|
2377
|
-
const py = view.getInt32(ptOff + pIdx * 8 + 4, true);
|
|
2378
|
-
if (i === 0) {
|
|
2379
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2380
|
-
} else {
|
|
2381
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2382
|
-
}
|
|
2383
|
-
pIdx++;
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2719
|
+
build(ctx);
|
|
2386
2720
|
if (!inPath) {
|
|
2387
|
-
|
|
2388
|
-
|
|
2721
|
+
const buildWithPath = (target) => {
|
|
2722
|
+
target.beginPath();
|
|
2723
|
+
build(target);
|
|
2724
|
+
};
|
|
2725
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
2389
2726
|
}
|
|
2390
2727
|
}
|
|
2391
2728
|
function handlePolyPolygon16(rCtx, offset, dataOff, recSize) {
|
|
@@ -2400,29 +2737,21 @@ function handlePolyPolygon16(rCtx, offset, dataOff, recSize) {
|
|
|
2400
2737
|
if (ptOff + totalPoints * 4 > offset + recSize) {
|
|
2401
2738
|
return;
|
|
2402
2739
|
}
|
|
2740
|
+
const readPoint = (pIdx) => gmapPoint(rCtx, view.getInt16(ptOff + pIdx * 4, true), view.getInt16(ptOff + pIdx * 4 + 2, true));
|
|
2741
|
+
const build = (target) => {
|
|
2742
|
+
buildPolyPolygonPath(target, rCtx, readPoint, countsOff, numPolys, totalPoints, true);
|
|
2743
|
+
};
|
|
2403
2744
|
if (!inPath) {
|
|
2404
2745
|
ctx.beginPath();
|
|
2405
2746
|
}
|
|
2406
|
-
|
|
2407
|
-
for (let p = 0; p < numPolys; p++) {
|
|
2408
|
-
const count = view.getUint32(countsOff + p * 4, true);
|
|
2409
|
-
for (let i = 0; i < count && pIdx < totalPoints; i++) {
|
|
2410
|
-
const px = view.getInt16(ptOff + pIdx * 4, true);
|
|
2411
|
-
const py = view.getInt16(ptOff + pIdx * 4 + 2, true);
|
|
2412
|
-
if (i === 0) {
|
|
2413
|
-
ctx.moveTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2414
|
-
} else {
|
|
2415
|
-
ctx.lineTo(gmx(rCtx, px), gmy(rCtx, py));
|
|
2416
|
-
}
|
|
2417
|
-
pIdx++;
|
|
2418
|
-
}
|
|
2419
|
-
ctx.closePath();
|
|
2420
|
-
}
|
|
2747
|
+
build(ctx);
|
|
2421
2748
|
if (!inPath) {
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2749
|
+
const buildWithPath = (target) => {
|
|
2750
|
+
target.beginPath();
|
|
2751
|
+
build(target);
|
|
2752
|
+
};
|
|
2753
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2754
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
2426
2755
|
}
|
|
2427
2756
|
}
|
|
2428
2757
|
|
|
@@ -2440,43 +2769,44 @@ function handlePoly32(rCtx, recType, offset, dataOff, recSize) {
|
|
|
2440
2769
|
const isPolygon = recType === EMR_POLYGON;
|
|
2441
2770
|
const isBezier = recType === EMR_POLYBEZIER || recType === EMR_POLYBEZIERTO;
|
|
2442
2771
|
const isTo = recType === EMR_POLYBEZIERTO || recType === EMR_POLYLINETO;
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
)
|
|
2460
|
-
|
|
2772
|
+
const pt = (i) => gmapPoint(rCtx, view.getInt32(ptOff + i * 8, true), view.getInt32(ptOff + i * 8 + 4, true));
|
|
2773
|
+
const build = (target) => {
|
|
2774
|
+
if (!isTo) {
|
|
2775
|
+
const p0 = pt(0);
|
|
2776
|
+
target.moveTo(p0.x, p0.y);
|
|
2777
|
+
}
|
|
2778
|
+
let i = isTo ? 0 : 1;
|
|
2779
|
+
if (isBezier) {
|
|
2780
|
+
while (i + 2 < count) {
|
|
2781
|
+
const p1 = pt(i);
|
|
2782
|
+
const p2 = pt(i + 1);
|
|
2783
|
+
const p3 = pt(i + 2);
|
|
2784
|
+
target.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
2785
|
+
i += 3;
|
|
2786
|
+
}
|
|
2787
|
+
} else {
|
|
2788
|
+
for (; i < count; i++) {
|
|
2789
|
+
const p = pt(i);
|
|
2790
|
+
target.lineTo(p.x, p.y);
|
|
2791
|
+
}
|
|
2461
2792
|
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
ctx.lineTo(
|
|
2465
|
-
gmx(rCtx, view.getInt32(ptOff + i * 8, true)),
|
|
2466
|
-
gmy(rCtx, view.getInt32(ptOff + i * 8 + 4, true))
|
|
2467
|
-
);
|
|
2793
|
+
if (isPolygon) {
|
|
2794
|
+
target.closePath();
|
|
2468
2795
|
}
|
|
2796
|
+
};
|
|
2797
|
+
if (!inPath) {
|
|
2798
|
+
ctx.beginPath();
|
|
2469
2799
|
}
|
|
2470
|
-
|
|
2471
|
-
ctx.closePath();
|
|
2472
|
-
}
|
|
2800
|
+
build(ctx);
|
|
2473
2801
|
if (!inPath) {
|
|
2802
|
+
const buildWithPath = (target) => {
|
|
2803
|
+
target.beginPath();
|
|
2804
|
+
build(target);
|
|
2805
|
+
};
|
|
2474
2806
|
if (isPolygon) {
|
|
2475
|
-
|
|
2476
|
-
ctx.fill(state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2807
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2477
2808
|
}
|
|
2478
|
-
|
|
2479
|
-
ctx.stroke();
|
|
2809
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
2480
2810
|
}
|
|
2481
2811
|
if (count > 0) {
|
|
2482
2812
|
const last = count - 1;
|
|
@@ -2498,43 +2828,44 @@ function handlePoly16(rCtx, recType, offset, dataOff, recSize) {
|
|
|
2498
2828
|
const isPolygon = recType === EMR_POLYGON16;
|
|
2499
2829
|
const isBezier = recType === EMR_POLYBEZIER16 || recType === EMR_POLYBEZIERTO16;
|
|
2500
2830
|
const isTo = recType === EMR_POLYBEZIERTO16 || recType === EMR_POLYLINETO16;
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
)
|
|
2518
|
-
|
|
2831
|
+
const pt = (i) => gmapPoint(rCtx, view.getInt16(ptOff + i * 4, true), view.getInt16(ptOff + i * 4 + 2, true));
|
|
2832
|
+
const build = (target) => {
|
|
2833
|
+
if (!isTo) {
|
|
2834
|
+
const p0 = pt(0);
|
|
2835
|
+
target.moveTo(p0.x, p0.y);
|
|
2836
|
+
}
|
|
2837
|
+
let i = isTo ? 0 : 1;
|
|
2838
|
+
if (isBezier) {
|
|
2839
|
+
while (i + 2 < count) {
|
|
2840
|
+
const p1 = pt(i);
|
|
2841
|
+
const p2 = pt(i + 1);
|
|
2842
|
+
const p3 = pt(i + 2);
|
|
2843
|
+
target.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
|
2844
|
+
i += 3;
|
|
2845
|
+
}
|
|
2846
|
+
} else {
|
|
2847
|
+
for (; i < count; i++) {
|
|
2848
|
+
const p = pt(i);
|
|
2849
|
+
target.lineTo(p.x, p.y);
|
|
2850
|
+
}
|
|
2519
2851
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
ctx.lineTo(
|
|
2523
|
-
gmx(rCtx, view.getInt16(ptOff + i * 4, true)),
|
|
2524
|
-
gmy(rCtx, view.getInt16(ptOff + i * 4 + 2, true))
|
|
2525
|
-
);
|
|
2852
|
+
if (isPolygon) {
|
|
2853
|
+
target.closePath();
|
|
2526
2854
|
}
|
|
2855
|
+
};
|
|
2856
|
+
if (!inPath) {
|
|
2857
|
+
ctx.beginPath();
|
|
2527
2858
|
}
|
|
2528
|
-
|
|
2529
|
-
ctx.closePath();
|
|
2530
|
-
}
|
|
2859
|
+
build(ctx);
|
|
2531
2860
|
if (!inPath) {
|
|
2861
|
+
const buildWithPath = (target) => {
|
|
2862
|
+
target.beginPath();
|
|
2863
|
+
build(target);
|
|
2864
|
+
};
|
|
2532
2865
|
if (isPolygon) {
|
|
2533
|
-
|
|
2534
|
-
ctx.fill(state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2866
|
+
fillShapeExactOrFast(rCtx, buildWithPath, state.polyFillMode === 2 ? "nonzero" : "evenodd");
|
|
2535
2867
|
}
|
|
2536
|
-
|
|
2537
|
-
ctx.stroke();
|
|
2868
|
+
strokeShapeExactOrFast(rCtx, buildWithPath);
|
|
2538
2869
|
}
|
|
2539
2870
|
if (count > 0) {
|
|
2540
2871
|
const last = count - 1;
|
|
@@ -2587,16 +2918,24 @@ function handleEmfGdiPolyPathRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
2587
2918
|
case EMR_CLOSEFIGURE:
|
|
2588
2919
|
ctx.closePath();
|
|
2589
2920
|
return true;
|
|
2590
|
-
case EMR_FILLPATH:
|
|
2591
|
-
|
|
2592
|
-
|
|
2921
|
+
case EMR_FILLPATH: {
|
|
2922
|
+
const fillRule = state.polyFillMode === 2 ? "nonzero" : "evenodd";
|
|
2923
|
+
if (!fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
2924
|
+
applyBrush(ctx, state);
|
|
2925
|
+
ctx.fill(fillRule);
|
|
2926
|
+
}
|
|
2593
2927
|
return true;
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2928
|
+
}
|
|
2929
|
+
case EMR_STROKEANDFILLPATH: {
|
|
2930
|
+
const fillRule = state.polyFillMode === 2 ? "nonzero" : "evenodd";
|
|
2931
|
+
if (!fillCurrentPathWithGdiPattern(rCtx, fillRule)) {
|
|
2932
|
+
applyBrush(ctx, state);
|
|
2933
|
+
ctx.fill(fillRule);
|
|
2934
|
+
}
|
|
2597
2935
|
applyPen(ctx, state);
|
|
2598
2936
|
ctx.stroke();
|
|
2599
2937
|
return true;
|
|
2938
|
+
}
|
|
2600
2939
|
case EMR_STROKEPATH:
|
|
2601
2940
|
applyPen(ctx, state);
|
|
2602
2941
|
ctx.stroke();
|
|
@@ -3313,14 +3652,14 @@ function plainLinear(ctx, grad) {
|
|
|
3313
3652
|
}
|
|
3314
3653
|
var SEAM_BIAS = 1e-5;
|
|
3315
3654
|
var MAX_PERIODS = 2048;
|
|
3316
|
-
function
|
|
3655
|
+
function surfaceSize3(ctx) {
|
|
3317
3656
|
const canvas = ctx.canvas;
|
|
3318
3657
|
const w = canvas?.width;
|
|
3319
3658
|
const h = canvas?.height;
|
|
3320
3659
|
return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
|
|
3321
3660
|
}
|
|
3322
3661
|
function tiledLinear(ctx, grad, rect, device) {
|
|
3323
|
-
const size =
|
|
3662
|
+
const size = surfaceSize3(ctx);
|
|
3324
3663
|
const inv = invertLinear(device);
|
|
3325
3664
|
if (!size || !inv || typeof ctx.createLinearGradient !== "function") {
|
|
3326
3665
|
return null;
|
|
@@ -3462,6 +3801,42 @@ function createBrushGradient(ctx, grad, device = IDENTITY) {
|
|
|
3462
3801
|
}
|
|
3463
3802
|
}
|
|
3464
3803
|
|
|
3804
|
+
// src/emf-plus-brush-texture.ts
|
|
3805
|
+
var IDENTITY2 = [1, 0, 0, 1, 0, 0];
|
|
3806
|
+
function packArgb(rgba, i) {
|
|
3807
|
+
return (rgba[i + 3] << 24 | rgba[i] << 16 | rgba[i + 1] << 8 | rgba[i + 2]) >>> 0;
|
|
3808
|
+
}
|
|
3809
|
+
function createBrushTexture(ctx, texture, device = IDENTITY2) {
|
|
3810
|
+
const { width, height, rgba } = texture;
|
|
3811
|
+
if (width <= 0 || height <= 0) {
|
|
3812
|
+
return null;
|
|
3813
|
+
}
|
|
3814
|
+
try {
|
|
3815
|
+
const brush = texture.transform ?? IDENTITY2;
|
|
3816
|
+
const colorAt = (bx, by) => {
|
|
3817
|
+
const ix = Math.floor(bx);
|
|
3818
|
+
const iy = Math.floor(by);
|
|
3819
|
+
if (ix < 0 || iy < 0 || ix >= width || iy >= height) {
|
|
3820
|
+
return null;
|
|
3821
|
+
}
|
|
3822
|
+
return packArgb(rgba, (iy * width + ix) * 4);
|
|
3823
|
+
};
|
|
3824
|
+
return buildPattern(
|
|
3825
|
+
ctx,
|
|
3826
|
+
{ x: 0, y: 0, w: width, h: height },
|
|
3827
|
+
width,
|
|
3828
|
+
height,
|
|
3829
|
+
texture.wrapMode,
|
|
3830
|
+
brush,
|
|
3831
|
+
halfPixelDelta(device),
|
|
3832
|
+
{ phaseX: 0, lagX: 0, lagY: 0 },
|
|
3833
|
+
colorAt
|
|
3834
|
+
);
|
|
3835
|
+
} catch {
|
|
3836
|
+
return null;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3465
3840
|
// src/emf-plus-path.ts
|
|
3466
3841
|
function parseEmfPlusPath(data, off, maxLen) {
|
|
3467
3842
|
if (maxLen < 12) {
|
|
@@ -3608,6 +3983,12 @@ function resolveBrushPaint(rCtx, flags, brushIdOrColor) {
|
|
|
3608
3983
|
return g;
|
|
3609
3984
|
}
|
|
3610
3985
|
}
|
|
3986
|
+
if (obj.texture) {
|
|
3987
|
+
const p = createBrushTexture(rCtx.ctx, obj.texture, plusWorldMatrix(rCtx));
|
|
3988
|
+
if (p) {
|
|
3989
|
+
return p;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3611
3992
|
return obj.color;
|
|
3612
3993
|
}
|
|
3613
3994
|
return "rgba(0,0,0,1)";
|
|
@@ -4103,64 +4484,238 @@ function handleEmfPlusDrawRecord(rCtx, recType, recFlags, dataOff, recDataSize)
|
|
|
4103
4484
|
} else {
|
|
4104
4485
|
ctx.stroke();
|
|
4105
4486
|
}
|
|
4106
|
-
return true;
|
|
4487
|
+
return true;
|
|
4488
|
+
}
|
|
4489
|
+
case EMFPLUS_DRAWLINES: {
|
|
4490
|
+
if (recDataSize >= 4) {
|
|
4491
|
+
const penId = recFlags & 255;
|
|
4492
|
+
const pen = objectTable.get(penId);
|
|
4493
|
+
const count = view.getUint32(dataOff, true);
|
|
4494
|
+
const compressed = (recFlags & 16384) !== 0;
|
|
4495
|
+
const ptSize = compressed ? 4 : 8;
|
|
4496
|
+
if (pen && pen.kind === "plus-pen") {
|
|
4497
|
+
applyEmfPlusPen(ctx, pen);
|
|
4498
|
+
}
|
|
4499
|
+
applyPlusWorldTransform(rCtx);
|
|
4500
|
+
ctx.beginPath();
|
|
4501
|
+
let pOff = dataOff + 4;
|
|
4502
|
+
for (let i = 0; i < count && pOff + ptSize <= dataOff + recDataSize; i++) {
|
|
4503
|
+
const pt = readPointFromView(view, pOff, compressed);
|
|
4504
|
+
if (i === 0) {
|
|
4505
|
+
ctx.moveTo(pt.x, pt.y);
|
|
4506
|
+
} else {
|
|
4507
|
+
ctx.lineTo(pt.x, pt.y);
|
|
4508
|
+
}
|
|
4509
|
+
pOff += ptSize;
|
|
4510
|
+
}
|
|
4511
|
+
if (recFlags & 8192) {
|
|
4512
|
+
ctx.closePath();
|
|
4513
|
+
}
|
|
4514
|
+
ctx.stroke();
|
|
4515
|
+
}
|
|
4516
|
+
return true;
|
|
4517
|
+
}
|
|
4518
|
+
case EMFPLUS_FILLPOLYGON: {
|
|
4519
|
+
if (recDataSize >= 8) {
|
|
4520
|
+
const brushVal = view.getUint32(dataOff, true);
|
|
4521
|
+
const count = view.getUint32(dataOff + 4, true);
|
|
4522
|
+
const compressed = (recFlags & 16384) !== 0;
|
|
4523
|
+
const ptSize = compressed ? 4 : 8;
|
|
4524
|
+
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
4525
|
+
applyPlusWorldTransform(rCtx);
|
|
4526
|
+
ctx.beginPath();
|
|
4527
|
+
let pOff = dataOff + 8;
|
|
4528
|
+
for (let i = 0; i < count && pOff + ptSize <= dataOff + recDataSize; i++) {
|
|
4529
|
+
const pt = readPointFromView(view, pOff, compressed);
|
|
4530
|
+
if (i === 0) {
|
|
4531
|
+
ctx.moveTo(pt.x, pt.y);
|
|
4532
|
+
} else {
|
|
4533
|
+
ctx.lineTo(pt.x, pt.y);
|
|
4534
|
+
}
|
|
4535
|
+
pOff += ptSize;
|
|
4536
|
+
}
|
|
4537
|
+
ctx.closePath();
|
|
4538
|
+
ctx.fill();
|
|
4539
|
+
}
|
|
4540
|
+
return true;
|
|
4541
|
+
}
|
|
4542
|
+
default:
|
|
4543
|
+
return false;
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
|
|
4547
|
+
// src/emf-plus-bitmap-decoder.ts
|
|
4548
|
+
var PIXELFORMAT_24BPP_RGB = 137224;
|
|
4549
|
+
var PIXELFORMAT_32BPP_RGB = 139273;
|
|
4550
|
+
var PIXELFORMAT_32BPP_ARGB = 2498570;
|
|
4551
|
+
var PIXELFORMAT_32BPP_PARGB = 925707;
|
|
4552
|
+
function decodeEmfPlusBitmapPixels(view, pixelStart, width, height, stride, pixelFormat) {
|
|
4553
|
+
const absStride = Math.abs(stride);
|
|
4554
|
+
const topDown = stride > 0;
|
|
4555
|
+
const rowBytes = width * 4;
|
|
4556
|
+
const bmpRowStride = rowBytes + 3 & -4;
|
|
4557
|
+
const pixelDataSize = bmpRowStride * height;
|
|
4558
|
+
const bmpData = new Uint8Array(pixelDataSize);
|
|
4559
|
+
for (let y = 0; y < height; y++) {
|
|
4560
|
+
const srcRow = topDown ? y : height - 1 - y;
|
|
4561
|
+
const rowOff = pixelStart + srcRow * absStride;
|
|
4562
|
+
const dstRow = (height - 1 - y) * bmpRowStride;
|
|
4563
|
+
switch (pixelFormat) {
|
|
4564
|
+
case PIXELFORMAT_32BPP_ARGB:
|
|
4565
|
+
case PIXELFORMAT_32BPP_PARGB: {
|
|
4566
|
+
for (let x = 0; x < width; x++) {
|
|
4567
|
+
const off = rowOff + x * 4;
|
|
4568
|
+
if (off + 3 >= view.byteLength) {
|
|
4569
|
+
break;
|
|
4570
|
+
}
|
|
4571
|
+
let b = view.getUint8(off);
|
|
4572
|
+
let g = view.getUint8(off + 1);
|
|
4573
|
+
let r = view.getUint8(off + 2);
|
|
4574
|
+
const a = view.getUint8(off + 3);
|
|
4575
|
+
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
4576
|
+
r = Math.min(255, Math.round(r * 255 / a));
|
|
4577
|
+
g = Math.min(255, Math.round(g * 255 / a));
|
|
4578
|
+
b = Math.min(255, Math.round(b * 255 / a));
|
|
4579
|
+
}
|
|
4580
|
+
const di = dstRow + x * 4;
|
|
4581
|
+
bmpData[di] = b;
|
|
4582
|
+
bmpData[di + 1] = g;
|
|
4583
|
+
bmpData[di + 2] = r;
|
|
4584
|
+
bmpData[di + 3] = a;
|
|
4585
|
+
}
|
|
4586
|
+
break;
|
|
4587
|
+
}
|
|
4588
|
+
case PIXELFORMAT_32BPP_RGB: {
|
|
4589
|
+
for (let x = 0; x < width; x++) {
|
|
4590
|
+
const off = rowOff + x * 4;
|
|
4591
|
+
if (off + 3 >= view.byteLength) {
|
|
4592
|
+
break;
|
|
4593
|
+
}
|
|
4594
|
+
const di = dstRow + x * 4;
|
|
4595
|
+
bmpData[di] = view.getUint8(off);
|
|
4596
|
+
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4597
|
+
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4598
|
+
bmpData[di + 3] = 255;
|
|
4599
|
+
}
|
|
4600
|
+
break;
|
|
4601
|
+
}
|
|
4602
|
+
case PIXELFORMAT_24BPP_RGB: {
|
|
4603
|
+
for (let x = 0; x < width; x++) {
|
|
4604
|
+
const off = rowOff + x * 3;
|
|
4605
|
+
if (off + 2 >= view.byteLength) {
|
|
4606
|
+
break;
|
|
4607
|
+
}
|
|
4608
|
+
const di = dstRow + x * 4;
|
|
4609
|
+
bmpData[di] = view.getUint8(off);
|
|
4610
|
+
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4611
|
+
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4612
|
+
bmpData[di + 3] = 255;
|
|
4613
|
+
}
|
|
4614
|
+
break;
|
|
4615
|
+
}
|
|
4616
|
+
default:
|
|
4617
|
+
return null;
|
|
4107
4618
|
}
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
ctx.lineTo(pt.x, pt.y);
|
|
4619
|
+
}
|
|
4620
|
+
return wrapBmpFile(bmpData, bmpRowStride, width, height, pixelDataSize);
|
|
4621
|
+
}
|
|
4622
|
+
function decodeEmfPlusBitmapPixelsToRgba(view, pixelStart, width, height, stride, pixelFormat) {
|
|
4623
|
+
const absStride = Math.abs(stride);
|
|
4624
|
+
const topDown = stride > 0;
|
|
4625
|
+
const rgba = new Uint8ClampedArray(width * height * 4);
|
|
4626
|
+
for (let y = 0; y < height; y++) {
|
|
4627
|
+
const srcRow = topDown ? y : height - 1 - y;
|
|
4628
|
+
const rowOff = pixelStart + srcRow * absStride;
|
|
4629
|
+
const dstRow = y * width * 4;
|
|
4630
|
+
switch (pixelFormat) {
|
|
4631
|
+
case PIXELFORMAT_32BPP_ARGB:
|
|
4632
|
+
case PIXELFORMAT_32BPP_PARGB: {
|
|
4633
|
+
for (let x = 0; x < width; x++) {
|
|
4634
|
+
const off = rowOff + x * 4;
|
|
4635
|
+
if (off + 3 >= view.byteLength) {
|
|
4636
|
+
break;
|
|
4127
4637
|
}
|
|
4128
|
-
|
|
4638
|
+
let b = view.getUint8(off);
|
|
4639
|
+
let g = view.getUint8(off + 1);
|
|
4640
|
+
let r = view.getUint8(off + 2);
|
|
4641
|
+
const a = view.getUint8(off + 3);
|
|
4642
|
+
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
4643
|
+
r = Math.min(255, Math.round(r * 255 / a));
|
|
4644
|
+
g = Math.min(255, Math.round(g * 255 / a));
|
|
4645
|
+
b = Math.min(255, Math.round(b * 255 / a));
|
|
4646
|
+
}
|
|
4647
|
+
const di = dstRow + x * 4;
|
|
4648
|
+
rgba[di] = r;
|
|
4649
|
+
rgba[di + 1] = g;
|
|
4650
|
+
rgba[di + 2] = b;
|
|
4651
|
+
rgba[di + 3] = a;
|
|
4129
4652
|
}
|
|
4130
|
-
|
|
4131
|
-
|
|
4653
|
+
break;
|
|
4654
|
+
}
|
|
4655
|
+
case PIXELFORMAT_32BPP_RGB: {
|
|
4656
|
+
for (let x = 0; x < width; x++) {
|
|
4657
|
+
const off = rowOff + x * 4;
|
|
4658
|
+
if (off + 3 >= view.byteLength) {
|
|
4659
|
+
break;
|
|
4660
|
+
}
|
|
4661
|
+
const di = dstRow + x * 4;
|
|
4662
|
+
rgba[di] = view.getUint8(off + 2);
|
|
4663
|
+
rgba[di + 1] = view.getUint8(off + 1);
|
|
4664
|
+
rgba[di + 2] = view.getUint8(off);
|
|
4665
|
+
rgba[di + 3] = 255;
|
|
4132
4666
|
}
|
|
4133
|
-
|
|
4667
|
+
break;
|
|
4134
4668
|
}
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
const count = view.getUint32(dataOff + 4, true);
|
|
4141
|
-
const compressed = (recFlags & 16384) !== 0;
|
|
4142
|
-
const ptSize = compressed ? 4 : 8;
|
|
4143
|
-
ctx.fillStyle = resolveBrushPaint(rCtx, recFlags, brushVal);
|
|
4144
|
-
applyPlusWorldTransform(rCtx);
|
|
4145
|
-
ctx.beginPath();
|
|
4146
|
-
let pOff = dataOff + 8;
|
|
4147
|
-
for (let i = 0; i < count && pOff + ptSize <= dataOff + recDataSize; i++) {
|
|
4148
|
-
const pt = readPointFromView(view, pOff, compressed);
|
|
4149
|
-
if (i === 0) {
|
|
4150
|
-
ctx.moveTo(pt.x, pt.y);
|
|
4151
|
-
} else {
|
|
4152
|
-
ctx.lineTo(pt.x, pt.y);
|
|
4669
|
+
case PIXELFORMAT_24BPP_RGB: {
|
|
4670
|
+
for (let x = 0; x < width; x++) {
|
|
4671
|
+
const off = rowOff + x * 3;
|
|
4672
|
+
if (off + 2 >= view.byteLength) {
|
|
4673
|
+
break;
|
|
4153
4674
|
}
|
|
4154
|
-
|
|
4675
|
+
const di = dstRow + x * 4;
|
|
4676
|
+
rgba[di] = view.getUint8(off + 2);
|
|
4677
|
+
rgba[di + 1] = view.getUint8(off + 1);
|
|
4678
|
+
rgba[di + 2] = view.getUint8(off);
|
|
4679
|
+
rgba[di + 3] = 255;
|
|
4155
4680
|
}
|
|
4156
|
-
|
|
4157
|
-
ctx.fill();
|
|
4681
|
+
break;
|
|
4158
4682
|
}
|
|
4159
|
-
|
|
4683
|
+
default:
|
|
4684
|
+
return null;
|
|
4160
4685
|
}
|
|
4161
|
-
default:
|
|
4162
|
-
return false;
|
|
4163
4686
|
}
|
|
4687
|
+
return rgba;
|
|
4688
|
+
}
|
|
4689
|
+
function wrapBmpFile(bmpData, bmpRowStride, width, height, pixelDataSize) {
|
|
4690
|
+
const fileHeaderSize = 14;
|
|
4691
|
+
const dibHeaderSize = 108;
|
|
4692
|
+
const fileSize = fileHeaderSize + dibHeaderSize + pixelDataSize;
|
|
4693
|
+
const bmpFile = new ArrayBuffer(fileSize);
|
|
4694
|
+
const bmpView = new DataView(bmpFile);
|
|
4695
|
+
const bmpBytes = new Uint8Array(bmpFile);
|
|
4696
|
+
bmpView.setUint8(0, 66);
|
|
4697
|
+
bmpView.setUint8(1, 77);
|
|
4698
|
+
bmpView.setUint32(2, fileSize, true);
|
|
4699
|
+
bmpView.setUint32(6, 0, true);
|
|
4700
|
+
bmpView.setUint32(10, fileHeaderSize + dibHeaderSize, true);
|
|
4701
|
+
bmpView.setUint32(14, dibHeaderSize, true);
|
|
4702
|
+
bmpView.setInt32(18, width, true);
|
|
4703
|
+
bmpView.setInt32(22, height, true);
|
|
4704
|
+
bmpView.setUint16(26, 1, true);
|
|
4705
|
+
bmpView.setUint16(28, 32, true);
|
|
4706
|
+
bmpView.setUint32(30, 3, true);
|
|
4707
|
+
bmpView.setUint32(34, pixelDataSize, true);
|
|
4708
|
+
bmpView.setInt32(38, 2835, true);
|
|
4709
|
+
bmpView.setInt32(42, 2835, true);
|
|
4710
|
+
bmpView.setUint32(46, 0, true);
|
|
4711
|
+
bmpView.setUint32(50, 0, true);
|
|
4712
|
+
bmpView.setUint32(54, 16711680, true);
|
|
4713
|
+
bmpView.setUint32(58, 65280, true);
|
|
4714
|
+
bmpView.setUint32(62, 255, true);
|
|
4715
|
+
bmpView.setUint32(66, 4278190080, true);
|
|
4716
|
+
bmpView.setUint32(70, 1934772034, true);
|
|
4717
|
+
bmpBytes.set(bmpData, fileHeaderSize + dibHeaderSize);
|
|
4718
|
+
return bmpFile;
|
|
4164
4719
|
}
|
|
4165
4720
|
|
|
4166
4721
|
// src/emf-plus-brush-parser.ts
|
|
@@ -4316,6 +4871,62 @@ function parseLinearGradient(view, b, end) {
|
|
|
4316
4871
|
}
|
|
4317
4872
|
};
|
|
4318
4873
|
}
|
|
4874
|
+
function decodeTextureImage(view, off, end) {
|
|
4875
|
+
if (off + 28 > end) {
|
|
4876
|
+
return null;
|
|
4877
|
+
}
|
|
4878
|
+
const imageDataType = view.getUint32(off + 4, true);
|
|
4879
|
+
if (imageDataType !== 1) {
|
|
4880
|
+
return null;
|
|
4881
|
+
}
|
|
4882
|
+
const width = view.getInt32(off + 8, true);
|
|
4883
|
+
const height = view.getInt32(off + 12, true);
|
|
4884
|
+
const stride = view.getInt32(off + 16, true);
|
|
4885
|
+
const pixelFormat = view.getUint32(off + 20, true);
|
|
4886
|
+
const pixelStart = off + 28;
|
|
4887
|
+
const absStride = Math.abs(stride);
|
|
4888
|
+
if (width <= 0 || height <= 0 || width > 8192 || height > 8192 || pixelStart + absStride * height > end) {
|
|
4889
|
+
return null;
|
|
4890
|
+
}
|
|
4891
|
+
const rgba = decodeEmfPlusBitmapPixelsToRgba(view, pixelStart, width, height, stride, pixelFormat);
|
|
4892
|
+
return rgba ? { width, height, rgba } : null;
|
|
4893
|
+
}
|
|
4894
|
+
function parseTextureBrush(view, b, end) {
|
|
4895
|
+
if (b + 8 > end) {
|
|
4896
|
+
return null;
|
|
4897
|
+
}
|
|
4898
|
+
const flags = view.getUint32(b, true);
|
|
4899
|
+
const wrapMode = readWrapMode(view.getUint32(b + 4, true));
|
|
4900
|
+
let o = b + 8;
|
|
4901
|
+
let transform = null;
|
|
4902
|
+
if (flags & BRUSH_DATA_TRANSFORM && o + 24 <= end) {
|
|
4903
|
+
transform = readTransform(view, o);
|
|
4904
|
+
o += 24;
|
|
4905
|
+
}
|
|
4906
|
+
const image = decodeTextureImage(view, o, end);
|
|
4907
|
+
if (!image) {
|
|
4908
|
+
return null;
|
|
4909
|
+
}
|
|
4910
|
+
const texture = {
|
|
4911
|
+
width: image.width,
|
|
4912
|
+
height: image.height,
|
|
4913
|
+
rgba: image.rgba,
|
|
4914
|
+
wrapMode,
|
|
4915
|
+
transform
|
|
4916
|
+
};
|
|
4917
|
+
let r = 0;
|
|
4918
|
+
let g = 0;
|
|
4919
|
+
let bl = 0;
|
|
4920
|
+
const n = image.width * image.height;
|
|
4921
|
+
for (let i = 0; i < n; i++) {
|
|
4922
|
+
r += image.rgba[i * 4];
|
|
4923
|
+
g += image.rgba[i * 4 + 1];
|
|
4924
|
+
bl += image.rgba[i * 4 + 2];
|
|
4925
|
+
}
|
|
4926
|
+
const avg = n > 0 ? `rgba(${Math.round(r / n)},${Math.round(g / n)},${Math.round(bl / n)},1)` : "rgba(128,128,128,1)";
|
|
4927
|
+
emfLog(`parseEmfPlusBrushObject: texture fill ${image.width}x${image.height}, wrapMode=${wrapMode}`);
|
|
4928
|
+
return { kind: "plus-brush", color: avg, texture };
|
|
4929
|
+
}
|
|
4319
4930
|
function parsePathGradient(view, b, end) {
|
|
4320
4931
|
if (b + 24 > end) {
|
|
4321
4932
|
return null;
|
|
@@ -4523,6 +5134,10 @@ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
|
4523
5134
|
return { kind: "plus-brush", color: argbToRgba(view.getUint32(b + 4, true)) };
|
|
4524
5135
|
}
|
|
4525
5136
|
return { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
5137
|
+
case EMFPLUS_BRUSHTYPE_TEXTUREFILL: {
|
|
5138
|
+
const brush = parseTextureBrush(view, b, end);
|
|
5139
|
+
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
5140
|
+
}
|
|
4526
5141
|
case EMFPLUS_BRUSHTYPE_LINEARGRADIENT: {
|
|
4527
5142
|
const brush = parseLinearGradient(view, b, end);
|
|
4528
5143
|
return brush ?? { kind: "plus-brush", color: "rgba(0,0,0,1)" };
|
|
@@ -4536,110 +5151,6 @@ function parseEmfPlusBrushObject(view, dataOff, recDataSize) {
|
|
|
4536
5151
|
}
|
|
4537
5152
|
}
|
|
4538
5153
|
|
|
4539
|
-
// src/emf-plus-bitmap-decoder.ts
|
|
4540
|
-
var PIXELFORMAT_24BPP_RGB = 137224;
|
|
4541
|
-
var PIXELFORMAT_32BPP_RGB = 139273;
|
|
4542
|
-
var PIXELFORMAT_32BPP_ARGB = 2498570;
|
|
4543
|
-
var PIXELFORMAT_32BPP_PARGB = 925707;
|
|
4544
|
-
function decodeEmfPlusBitmapPixels(view, pixelStart, width, height, stride, pixelFormat) {
|
|
4545
|
-
const absStride = Math.abs(stride);
|
|
4546
|
-
const topDown = stride > 0;
|
|
4547
|
-
const rowBytes = width * 4;
|
|
4548
|
-
const bmpRowStride = rowBytes + 3 & -4;
|
|
4549
|
-
const pixelDataSize = bmpRowStride * height;
|
|
4550
|
-
const bmpData = new Uint8Array(pixelDataSize);
|
|
4551
|
-
for (let y = 0; y < height; y++) {
|
|
4552
|
-
const srcRow = topDown ? y : height - 1 - y;
|
|
4553
|
-
const rowOff = pixelStart + srcRow * absStride;
|
|
4554
|
-
const dstRow = (height - 1 - y) * bmpRowStride;
|
|
4555
|
-
switch (pixelFormat) {
|
|
4556
|
-
case PIXELFORMAT_32BPP_ARGB:
|
|
4557
|
-
case PIXELFORMAT_32BPP_PARGB: {
|
|
4558
|
-
for (let x = 0; x < width; x++) {
|
|
4559
|
-
const off = rowOff + x * 4;
|
|
4560
|
-
if (off + 3 >= view.byteLength) {
|
|
4561
|
-
break;
|
|
4562
|
-
}
|
|
4563
|
-
let b = view.getUint8(off);
|
|
4564
|
-
let g = view.getUint8(off + 1);
|
|
4565
|
-
let r = view.getUint8(off + 2);
|
|
4566
|
-
const a = view.getUint8(off + 3);
|
|
4567
|
-
if (pixelFormat === PIXELFORMAT_32BPP_PARGB && a > 0 && a < 255) {
|
|
4568
|
-
r = Math.min(255, Math.round(r * 255 / a));
|
|
4569
|
-
g = Math.min(255, Math.round(g * 255 / a));
|
|
4570
|
-
b = Math.min(255, Math.round(b * 255 / a));
|
|
4571
|
-
}
|
|
4572
|
-
const di = dstRow + x * 4;
|
|
4573
|
-
bmpData[di] = b;
|
|
4574
|
-
bmpData[di + 1] = g;
|
|
4575
|
-
bmpData[di + 2] = r;
|
|
4576
|
-
bmpData[di + 3] = a;
|
|
4577
|
-
}
|
|
4578
|
-
break;
|
|
4579
|
-
}
|
|
4580
|
-
case PIXELFORMAT_32BPP_RGB: {
|
|
4581
|
-
for (let x = 0; x < width; x++) {
|
|
4582
|
-
const off = rowOff + x * 4;
|
|
4583
|
-
if (off + 3 >= view.byteLength) {
|
|
4584
|
-
break;
|
|
4585
|
-
}
|
|
4586
|
-
const di = dstRow + x * 4;
|
|
4587
|
-
bmpData[di] = view.getUint8(off);
|
|
4588
|
-
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4589
|
-
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4590
|
-
bmpData[di + 3] = 255;
|
|
4591
|
-
}
|
|
4592
|
-
break;
|
|
4593
|
-
}
|
|
4594
|
-
case PIXELFORMAT_24BPP_RGB: {
|
|
4595
|
-
for (let x = 0; x < width; x++) {
|
|
4596
|
-
const off = rowOff + x * 3;
|
|
4597
|
-
if (off + 2 >= view.byteLength) {
|
|
4598
|
-
break;
|
|
4599
|
-
}
|
|
4600
|
-
const di = dstRow + x * 4;
|
|
4601
|
-
bmpData[di] = view.getUint8(off);
|
|
4602
|
-
bmpData[di + 1] = view.getUint8(off + 1);
|
|
4603
|
-
bmpData[di + 2] = view.getUint8(off + 2);
|
|
4604
|
-
bmpData[di + 3] = 255;
|
|
4605
|
-
}
|
|
4606
|
-
break;
|
|
4607
|
-
}
|
|
4608
|
-
default:
|
|
4609
|
-
return null;
|
|
4610
|
-
}
|
|
4611
|
-
}
|
|
4612
|
-
const fileHeaderSize = 14;
|
|
4613
|
-
const dibHeaderSize = 108;
|
|
4614
|
-
const fileSize = fileHeaderSize + dibHeaderSize + pixelDataSize;
|
|
4615
|
-
const bmpFile = new ArrayBuffer(fileSize);
|
|
4616
|
-
const bmpView = new DataView(bmpFile);
|
|
4617
|
-
const bmpBytes = new Uint8Array(bmpFile);
|
|
4618
|
-
bmpView.setUint8(0, 66);
|
|
4619
|
-
bmpView.setUint8(1, 77);
|
|
4620
|
-
bmpView.setUint32(2, fileSize, true);
|
|
4621
|
-
bmpView.setUint32(6, 0, true);
|
|
4622
|
-
bmpView.setUint32(10, fileHeaderSize + dibHeaderSize, true);
|
|
4623
|
-
bmpView.setUint32(14, dibHeaderSize, true);
|
|
4624
|
-
bmpView.setInt32(18, width, true);
|
|
4625
|
-
bmpView.setInt32(22, height, true);
|
|
4626
|
-
bmpView.setUint16(26, 1, true);
|
|
4627
|
-
bmpView.setUint16(28, 32, true);
|
|
4628
|
-
bmpView.setUint32(30, 3, true);
|
|
4629
|
-
bmpView.setUint32(34, pixelDataSize, true);
|
|
4630
|
-
bmpView.setInt32(38, 2835, true);
|
|
4631
|
-
bmpView.setInt32(42, 2835, true);
|
|
4632
|
-
bmpView.setUint32(46, 0, true);
|
|
4633
|
-
bmpView.setUint32(50, 0, true);
|
|
4634
|
-
bmpView.setUint32(54, 16711680, true);
|
|
4635
|
-
bmpView.setUint32(58, 65280, true);
|
|
4636
|
-
bmpView.setUint32(62, 255, true);
|
|
4637
|
-
bmpView.setUint32(66, 4278190080, true);
|
|
4638
|
-
bmpView.setUint32(70, 1934772034, true);
|
|
4639
|
-
bmpBytes.set(bmpData, fileHeaderSize + dibHeaderSize);
|
|
4640
|
-
return bmpFile;
|
|
4641
|
-
}
|
|
4642
|
-
|
|
4643
5154
|
// src/emf-plus-object-complex.ts
|
|
4644
5155
|
function parseEmfPlusPenObject(view, dataOff, recDataSize) {
|
|
4645
5156
|
if (recDataSize < 20) {
|