@pi-r/jimp 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +528 -373
- package/package.json +9 -7
- package/types/index.d.ts +53 -9
- package/util.d.ts +2 -3
- package/util.js +55 -52
- package/worker/jimp.js +64 -0
package/index.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@e-mc/core');
|
|
4
|
+
var types = require('@e-mc/types');
|
|
5
|
+
|
|
2
6
|
const path = require("node:path");
|
|
3
7
|
const fs = require("node:fs");
|
|
8
|
+
const crypto = require("node:crypto");
|
|
4
9
|
const child_process = require("node:child_process");
|
|
5
10
|
const jimp = require("jimp");
|
|
11
|
+
const jimp_utils = require("@jimp/utils");
|
|
6
12
|
const gifwrap = require("gifwrap");
|
|
7
13
|
const bmp = require("bmp-js");
|
|
8
|
-
const node_crypto_1 = require("node:crypto");
|
|
9
|
-
const types_1 = require("@e-mc/types");
|
|
10
|
-
const util_1 = require("@pi-r/jimp/util");
|
|
11
14
|
const Image = require('@e-mc/image');
|
|
15
|
+
const util = require("@pi-r/jimp/util");
|
|
12
16
|
const kJimp = Symbol.for('jimp:constructor');
|
|
13
17
|
let WEBPMUX = null, WEBPMUX_INIT = false;
|
|
14
18
|
try {
|
|
@@ -18,6 +22,8 @@ try {
|
|
|
18
22
|
catch {
|
|
19
23
|
}
|
|
20
24
|
const CACHE_TRANSFORM = {};
|
|
25
|
+
const FONT_DATA = {};
|
|
26
|
+
const WORKER_JIMP = core.WorkerChannel.create(path.join(__dirname, 'worker', 'jimp.js'), 'PIR_JIMP');
|
|
21
27
|
let CACHE_INIT = false;
|
|
22
28
|
let TEMP_DIR = '';
|
|
23
29
|
const METHOD_ALIAS = {
|
|
@@ -34,14 +40,11 @@ const METHOD_ALIAS = {
|
|
|
34
40
|
convolute: 'cl',
|
|
35
41
|
convolution: 'cu',
|
|
36
42
|
flip: 'fl',
|
|
37
|
-
mirror: 'mi',
|
|
38
43
|
rotate: 'ro',
|
|
39
44
|
brightness: 'br',
|
|
40
45
|
contrast: 'cn',
|
|
41
|
-
|
|
42
|
-
dither16: 'dt',
|
|
46
|
+
dither: 'dt',
|
|
43
47
|
greyscale: 'gr',
|
|
44
|
-
grayscale: 'gr',
|
|
45
48
|
invert: 'in',
|
|
46
49
|
normalize: 'no',
|
|
47
50
|
fade: 'fa',
|
|
@@ -55,42 +58,59 @@ const METHOD_ALIAS = {
|
|
|
55
58
|
pixelate: 'px',
|
|
56
59
|
displace: 'dp',
|
|
57
60
|
color: 'co',
|
|
58
|
-
colour: 'co',
|
|
59
|
-
backgroundQuiet: 'bq',
|
|
60
61
|
circle: 'ci',
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fishEye: 'fe',
|
|
66
|
-
filterType: 'ft',
|
|
67
|
-
rgba: 'rg',
|
|
68
|
-
shadow: 'sh',
|
|
69
|
-
threshold: 'th'
|
|
62
|
+
fisheye: 'fe',
|
|
63
|
+
threshold: 'th',
|
|
64
|
+
quantize: 'qu',
|
|
65
|
+
print: 'pr'
|
|
70
66
|
};
|
|
67
|
+
{
|
|
68
|
+
const fonts = require('jimp/fonts');
|
|
69
|
+
const tasks = [];
|
|
70
|
+
const fontName = [];
|
|
71
|
+
for (const font in fonts) {
|
|
72
|
+
fontName.push(font);
|
|
73
|
+
tasks.push(jimp.loadFont(fonts[font]));
|
|
74
|
+
}
|
|
75
|
+
void Promise.allSettled(tasks)
|
|
76
|
+
.then(result => {
|
|
77
|
+
for (let i = 0; i < result.length; ++i) {
|
|
78
|
+
const font = result[i];
|
|
79
|
+
const name = fontName[i];
|
|
80
|
+
if (font.status === 'fulfilled') {
|
|
81
|
+
FONT_DATA[name] = font.value;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error(font.reason);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
71
89
|
function getMethodName(value) {
|
|
72
|
-
if (value
|
|
73
|
-
value
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
90
|
+
if (types.isString(value)) {
|
|
91
|
+
if (value.length === 2) {
|
|
92
|
+
value = value.toLowerCase();
|
|
93
|
+
for (const alias in METHOD_ALIAS) {
|
|
94
|
+
if (METHOD_ALIAS[alias] === value) {
|
|
95
|
+
return alias;
|
|
96
|
+
}
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
99
|
+
else if (METHOD_ALIAS[value] || METHOD_ALIAS[value = value.toLowerCase()]) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
82
102
|
}
|
|
83
103
|
}
|
|
84
|
-
async function performCommand(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
104
|
+
async function performCommand(instance, buffer, command, outputType, outputAs, output, { host, file } = {}) {
|
|
105
|
+
const decodeMap = instance.settings.jimp?.options?.decode;
|
|
106
|
+
return jimp.Jimp.read(buffer, decodeMap).then(async (img) => {
|
|
107
|
+
return transformCommand(output, new JimpHandler(img, instance, host), command, outputType, outputAs, file);
|
|
88
108
|
});
|
|
89
109
|
}
|
|
90
110
|
function execOptions(settings) {
|
|
91
111
|
const exec = settings.jimp?.exec;
|
|
92
112
|
let uid, gid;
|
|
93
|
-
if (
|
|
113
|
+
if (types.isPlainObject(exec)) {
|
|
94
114
|
let { uid: u, gid: g } = exec;
|
|
95
115
|
if ((u = parseInt(u)) >= 0) {
|
|
96
116
|
uid = u;
|
|
@@ -101,34 +121,36 @@ function execOptions(settings) {
|
|
|
101
121
|
}
|
|
102
122
|
return { uid, gid };
|
|
103
123
|
}
|
|
104
|
-
async function transformCommand(
|
|
124
|
+
async function transformCommand(output, handler, command, outputType, outputAs, file) {
|
|
105
125
|
if (command) {
|
|
106
126
|
handler.instance.setCommand(command, outputAs);
|
|
107
127
|
}
|
|
128
|
+
handler.instance.outputType = outputType;
|
|
108
129
|
await handler.method();
|
|
109
130
|
handler.resize();
|
|
110
131
|
handler.crop();
|
|
111
|
-
if (outputType !==
|
|
132
|
+
if (outputType !== Image.MIME_JPEG) {
|
|
112
133
|
handler.opacity();
|
|
113
134
|
}
|
|
114
|
-
else if (!outputAs) {
|
|
115
|
-
handler.quality();
|
|
116
|
-
}
|
|
117
135
|
switch (handler.rotateCount) {
|
|
118
136
|
case 0:
|
|
119
|
-
|
|
137
|
+
break;
|
|
120
138
|
case 1:
|
|
121
|
-
|
|
139
|
+
await handler.rotate();
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
await handler.rotate(output, (err, result) => {
|
|
143
|
+
if (!err) {
|
|
144
|
+
try {
|
|
145
|
+
handler.host?.add(result, file);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
122
152
|
}
|
|
123
|
-
return handler
|
|
124
|
-
if (!err) {
|
|
125
|
-
try {
|
|
126
|
-
handler.host?.add(result, parent);
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
153
|
+
return handler;
|
|
132
154
|
}
|
|
133
155
|
async function setImageCache(instance, tempKey, tempFile, output, localFile) {
|
|
134
156
|
try {
|
|
@@ -174,14 +196,26 @@ function getImageCache(instance, tempKey) {
|
|
|
174
196
|
removeFile(tempFile);
|
|
175
197
|
delete stored[tempKey];
|
|
176
198
|
}
|
|
177
|
-
|
|
178
|
-
return [null,
|
|
199
|
+
setTempDir(instance);
|
|
200
|
+
return [null, path.join(TEMP_DIR, crypto.randomUUID())];
|
|
179
201
|
}
|
|
180
202
|
function getCacheData(instance) {
|
|
181
203
|
if (!CACHE_INIT) {
|
|
182
|
-
|
|
204
|
+
setTempDir(instance);
|
|
183
205
|
const settings = instance.settings.jimp ||= {};
|
|
184
|
-
const expires =
|
|
206
|
+
const expires = types.parseExpires(settings.cache_expires || 0);
|
|
207
|
+
if (settings.worker) {
|
|
208
|
+
let { min = -1, max = -1, expires: idleTimeout = 0 } = settings.worker;
|
|
209
|
+
if ((min = Math.trunc(+min)) >= 0) {
|
|
210
|
+
WORKER_JIMP.min = min;
|
|
211
|
+
}
|
|
212
|
+
if ((max = Math.trunc(+max)) >= 0) {
|
|
213
|
+
WORKER_JIMP.max = max;
|
|
214
|
+
}
|
|
215
|
+
if ((idleTimeout = types.parseExpires(idleTimeout)) > 0) {
|
|
216
|
+
WORKER_JIMP.idleTimeout = idleTimeout;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
185
219
|
if (expires === 0) {
|
|
186
220
|
settings.cache_expires = 0;
|
|
187
221
|
}
|
|
@@ -197,7 +231,7 @@ function getCacheData(instance) {
|
|
|
197
231
|
const pathname = path.join(TEMP_DIR, item.name);
|
|
198
232
|
try {
|
|
199
233
|
const data = JSON.parse(fs.readFileSync(pathname, 'utf8'));
|
|
200
|
-
if (
|
|
234
|
+
if (types.isPlainObject(data) && fs.existsSync(data.tempFile)) {
|
|
201
235
|
if (data.ctimeMs + expires > current) {
|
|
202
236
|
CACHE_TRANSFORM[data.tempKey] = data;
|
|
203
237
|
return;
|
|
@@ -219,17 +253,20 @@ function getCacheData(instance) {
|
|
|
219
253
|
}
|
|
220
254
|
return CACHE_TRANSFORM;
|
|
221
255
|
}
|
|
222
|
-
function formatMessage(instance, value, startTime, failed, cTimeMs) {
|
|
256
|
+
function formatMessage(instance, value, startTime, failed, worker = false, cTimeMs) {
|
|
223
257
|
if (cTimeMs) {
|
|
224
258
|
instance.formatMessage(2048, "jimp", [value, 'cache'], new Date(cTimeMs).toLocaleString(), { ...Image.LOG_STYLE_NOTICE, hintBold: true });
|
|
225
259
|
}
|
|
226
260
|
else if (startTime) {
|
|
261
|
+
if (worker) {
|
|
262
|
+
value += ' (worker)';
|
|
263
|
+
}
|
|
227
264
|
instance.writeTimeProcess("jimp", value, startTime, { type: 2048, failed });
|
|
228
265
|
}
|
|
229
266
|
}
|
|
230
267
|
function getTempPath(instance, ext) {
|
|
231
|
-
|
|
232
|
-
return path.join(
|
|
268
|
+
setTempDir(instance);
|
|
269
|
+
return path.join(TEMP_DIR, crypto.randomUUID() + '.' + ext);
|
|
233
270
|
}
|
|
234
271
|
function rotateAnim(cmd) {
|
|
235
272
|
const rotate = cmd.rotate;
|
|
@@ -237,10 +274,20 @@ function rotateAnim(cmd) {
|
|
|
237
274
|
rotate.values = [rotate.values.pop()];
|
|
238
275
|
}
|
|
239
276
|
}
|
|
277
|
+
function setBackground(instance, args) {
|
|
278
|
+
instance.background = jimp_utils.rgbaToInt(...args);
|
|
279
|
+
}
|
|
240
280
|
function removeFile(pathname) {
|
|
241
281
|
fs.unlink(pathname, () => { });
|
|
242
282
|
}
|
|
283
|
+
function errorParameters(alias, value) {
|
|
284
|
+
throw types.errorMessage(alias, "Invalid parameters", JSON.stringify(value));
|
|
285
|
+
}
|
|
286
|
+
function setTempDir(instance) {
|
|
287
|
+
TEMP_DIR ||= instance.getTempDir({ moduleDir: true, createDir: true }) || types.getTempDir(true, "jimp");
|
|
288
|
+
}
|
|
243
289
|
const hasTransform = (cmd) => !!(cmd.rotate || cmd.resize || cmd.crop || cmd.method || typeof cmd.opacity === 'number' && cmd.opacity >= 0 && cmd.opacity < 1);
|
|
290
|
+
const isUnsupported = (value) => value === Image.MIME_GIF || value === Image.MIME_WEBP;
|
|
244
291
|
const emptyResult = (options) => (options.tempFile ? '' : null);
|
|
245
292
|
class JimpHandler {
|
|
246
293
|
handler;
|
|
@@ -251,43 +298,38 @@ class JimpHandler {
|
|
|
251
298
|
this.instance = instance;
|
|
252
299
|
this._host = _host;
|
|
253
300
|
}
|
|
254
|
-
async rotate(
|
|
301
|
+
async rotate(output, callback) {
|
|
255
302
|
if (this.aborted) {
|
|
256
|
-
return
|
|
303
|
+
return;
|
|
257
304
|
}
|
|
258
305
|
const data = this.instance.rotateData;
|
|
259
306
|
if (data) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
307
|
+
if (!output) {
|
|
308
|
+
Jimp.applyRotate(this.handler, data);
|
|
309
|
+
return;
|
|
263
310
|
}
|
|
311
|
+
Jimp.applyBackground(this.handler, data);
|
|
312
|
+
const leading = output.substring(0, output.lastIndexOf('.') + 1);
|
|
313
|
+
const ext = path.extname(output);
|
|
264
314
|
const tasks = [];
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
for (let i = 1; i < length; ++i) {
|
|
271
|
-
const value = values[i];
|
|
272
|
-
const img = this.handler.clone().rotate(value);
|
|
273
|
-
const output = leading + value + ext;
|
|
274
|
-
tasks.push(img.writeAsync(output)
|
|
275
|
-
.then(() => {
|
|
276
|
-
void this.finalize(output, callback, false);
|
|
277
|
-
})
|
|
278
|
-
.catch((err) => {
|
|
279
|
-
this.instance.writeFail(["Unable to rotate image", "jimp"], err, 2048);
|
|
280
|
-
}));
|
|
315
|
+
for (let i = 0; i < data.values.length; i++) {
|
|
316
|
+
const value = data.values[i];
|
|
317
|
+
if (i === 0) {
|
|
318
|
+
this.handler.rotate(value);
|
|
319
|
+
continue;
|
|
281
320
|
}
|
|
321
|
+
const img = this.handler.clone().rotate(value);
|
|
322
|
+
const target = leading + value + ext;
|
|
323
|
+
tasks.push(img.write(target, this.instance.getEncodeOptions())
|
|
324
|
+
.then(() => {
|
|
325
|
+
void this.finalize(target, callback, false);
|
|
326
|
+
})
|
|
327
|
+
.catch((err) => {
|
|
328
|
+
this.instance.writeFail(["Unable to rotate image", "jimp"], err, 2048);
|
|
329
|
+
}));
|
|
282
330
|
}
|
|
283
|
-
|
|
284
|
-
this.handler.rotate(deg);
|
|
285
|
-
}
|
|
286
|
-
if (tasks.length > 0) {
|
|
287
|
-
return Promise.all(tasks).then(() => this);
|
|
288
|
-
}
|
|
331
|
+
await Promise.all(tasks);
|
|
289
332
|
}
|
|
290
|
-
return this;
|
|
291
333
|
}
|
|
292
334
|
async method() {
|
|
293
335
|
if (this.aborted) {
|
|
@@ -298,20 +340,23 @@ class JimpHandler {
|
|
|
298
340
|
for (const [name, args = []] of data) {
|
|
299
341
|
try {
|
|
300
342
|
const alias = getMethodName(name);
|
|
301
|
-
if (!alias) {
|
|
302
|
-
throw (0, types_1.errorValue)("Invalid method name", name);
|
|
303
|
-
}
|
|
304
343
|
if (alias === 'composite') {
|
|
305
344
|
const [src, x, y, opts] = args;
|
|
306
|
-
if (
|
|
307
|
-
this.handler.composite(await jimp.read(src), x, y, opts);
|
|
345
|
+
if (types.isString(src) && typeof x === 'number' && typeof y === 'number') {
|
|
346
|
+
this.handler.composite(await jimp.Jimp.read(src), x, y, opts);
|
|
308
347
|
}
|
|
309
348
|
else {
|
|
310
|
-
|
|
349
|
+
errorParameters(alias, args);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else if (alias) {
|
|
353
|
+
const result = Jimp.applyMethod(this.handler, alias, ...args);
|
|
354
|
+
if (result.length > 0) {
|
|
355
|
+
this.instance.addLog(this.instance.statusType.WARN, "Invalid parameters" + `: ${result.join(', ')}`, alias);
|
|
311
356
|
}
|
|
312
357
|
}
|
|
313
358
|
else {
|
|
314
|
-
|
|
359
|
+
throw types.errorValue("Invalid method name", name);
|
|
315
360
|
}
|
|
316
361
|
}
|
|
317
362
|
catch (err) {
|
|
@@ -326,86 +371,23 @@ class JimpHandler {
|
|
|
326
371
|
}
|
|
327
372
|
const data = this.instance.resizeData;
|
|
328
373
|
if (data) {
|
|
329
|
-
|
|
330
|
-
if (!isNaN(color)) {
|
|
331
|
-
this.handler.background(color);
|
|
332
|
-
}
|
|
333
|
-
let flags = 0;
|
|
334
|
-
switch (align[0]) {
|
|
335
|
-
case 'left':
|
|
336
|
-
flags |= jimp.HORIZONTAL_ALIGN_LEFT;
|
|
337
|
-
break;
|
|
338
|
-
case 'center':
|
|
339
|
-
flags |= jimp.HORIZONTAL_ALIGN_CENTER;
|
|
340
|
-
break;
|
|
341
|
-
case 'right':
|
|
342
|
-
flags |= jimp.HORIZONTAL_ALIGN_RIGHT;
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
switch (align[1]) {
|
|
346
|
-
case 'top':
|
|
347
|
-
flags |= jimp.VERTICAL_ALIGN_TOP;
|
|
348
|
-
break;
|
|
349
|
-
case 'middle':
|
|
350
|
-
flags |= jimp.VERTICAL_ALIGN_MIDDLE;
|
|
351
|
-
break;
|
|
352
|
-
case 'bottom':
|
|
353
|
-
flags |= jimp.VERTICAL_ALIGN_BOTTOM;
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
switch (mode) {
|
|
357
|
-
case 'contain':
|
|
358
|
-
this.handler.contain(width, height, flags);
|
|
359
|
-
break;
|
|
360
|
-
case 'cover':
|
|
361
|
-
this.handler.cover(width, height, flags);
|
|
362
|
-
break;
|
|
363
|
-
case 'scale':
|
|
364
|
-
this.handler.scaleToFit(width, height);
|
|
365
|
-
break;
|
|
366
|
-
default: {
|
|
367
|
-
let resizeMode = jimp.RESIZE_NEAREST_NEIGHBOR;
|
|
368
|
-
switch (algorithm) {
|
|
369
|
-
case 'bilinear':
|
|
370
|
-
resizeMode = jimp.RESIZE_BILINEAR;
|
|
371
|
-
break;
|
|
372
|
-
case 'bicubic':
|
|
373
|
-
resizeMode = jimp.RESIZE_BICUBIC;
|
|
374
|
-
break;
|
|
375
|
-
case 'hermite':
|
|
376
|
-
resizeMode = jimp.RESIZE_HERMITE;
|
|
377
|
-
break;
|
|
378
|
-
case 'bezier':
|
|
379
|
-
resizeMode = jimp.RESIZE_BEZIER;
|
|
380
|
-
break;
|
|
381
|
-
case 'none':
|
|
382
|
-
resizeMode = undefined;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
this.handler.resize(width === Infinity ? jimp.AUTO : width, height === Infinity ? jimp.AUTO : height, resizeMode);
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
374
|
+
Jimp.applyResize(this.handler, data);
|
|
389
375
|
}
|
|
390
376
|
}
|
|
391
377
|
background(value) {
|
|
392
|
-
|
|
393
|
-
value = parseInt('0x' + value.map(item => item.toString(16)).join(''), 16);
|
|
394
|
-
}
|
|
395
|
-
this.handler.background(value);
|
|
378
|
+
this.handler.background = Array.isArray(value) ? jimp_utils.rgbaToInt(...value) : value;
|
|
396
379
|
}
|
|
397
380
|
async finalize(output, callback, replace) {
|
|
398
381
|
if (this.aborted) {
|
|
399
382
|
return;
|
|
400
383
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const settings = instance.settings;
|
|
384
|
+
if (this.instance.outputAs === 'webp' && path.extname(output).toLowerCase() !== '.webp') {
|
|
385
|
+
const settings = this.instance.settings;
|
|
404
386
|
const webp = settings.webp ||= {};
|
|
405
|
-
const data = instance.qualityData;
|
|
406
|
-
replace ??= instance.getCommand().includes('@');
|
|
407
|
-
const
|
|
408
|
-
const args = [
|
|
387
|
+
const data = this.instance.qualityData;
|
|
388
|
+
replace ??= this.instance.getCommand().includes('@');
|
|
389
|
+
const outFile = util.renameExt(output, 'webp', replace);
|
|
390
|
+
const args = [util.normalizePath(output)];
|
|
409
391
|
if (data) {
|
|
410
392
|
const { value, preset, nearLossless } = data;
|
|
411
393
|
if (preset) {
|
|
@@ -446,13 +428,13 @@ class JimpHandler {
|
|
|
446
428
|
}
|
|
447
429
|
}
|
|
448
430
|
}
|
|
449
|
-
args.push('-o',
|
|
431
|
+
args.push('-o', util.normalizePath(outFile));
|
|
450
432
|
try {
|
|
451
|
-
child_process.execFile(
|
|
433
|
+
child_process.execFile(types.sanitizeCmd(await util.importBinary('cwebp', webp.path), args), { shell: true, signal: this.instance.signal, ...execOptions(settings) }, err => {
|
|
452
434
|
if (err) {
|
|
453
|
-
this.instance.writeFail(["Unable to convert file", path.basename(
|
|
435
|
+
this.instance.writeFail(["Unable to convert file", path.basename(outFile)], err, 2048);
|
|
454
436
|
}
|
|
455
|
-
else if (
|
|
437
|
+
else if (output !== outFile) {
|
|
456
438
|
const tempFile = output;
|
|
457
439
|
queueMicrotask(() => {
|
|
458
440
|
fs.unlink(tempFile, error => {
|
|
@@ -461,7 +443,7 @@ class JimpHandler {
|
|
|
461
443
|
}
|
|
462
444
|
});
|
|
463
445
|
});
|
|
464
|
-
output =
|
|
446
|
+
output = outFile;
|
|
465
447
|
}
|
|
466
448
|
if (callback) {
|
|
467
449
|
callback(err, output);
|
|
@@ -469,7 +451,7 @@ class JimpHandler {
|
|
|
469
451
|
});
|
|
470
452
|
}
|
|
471
453
|
catch (err) {
|
|
472
|
-
this.instance.checkPackage(err, 'cwebp-bin',
|
|
454
|
+
this.instance.checkPackage(err, 'cwebp-bin', 2048);
|
|
473
455
|
if (callback) {
|
|
474
456
|
callback(err, '');
|
|
475
457
|
}
|
|
@@ -481,16 +463,13 @@ class JimpHandler {
|
|
|
481
463
|
}
|
|
482
464
|
async getBuffer(tempFile, saveAs) {
|
|
483
465
|
const emptyData = () => tempFile ? '' : null;
|
|
484
|
-
const output = getTempPath(this.instance, saveAs &&
|
|
466
|
+
const output = getTempPath(this.instance, saveAs && util.MIME_OUTPUT.has('image/' + (saveAs === 'jpg' ? 'jpeg' : saveAs)) ? saveAs : (this.handler.mime || this.instance.outputType).split('/').pop());
|
|
485
467
|
if (!output) {
|
|
486
468
|
return emptyData();
|
|
487
469
|
}
|
|
488
470
|
return new Promise(resolve => {
|
|
489
|
-
this.handler.write(output,
|
|
490
|
-
|
|
491
|
-
resolve(emptyData());
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
471
|
+
this.handler.write(output, this.instance.getEncodeOptions())
|
|
472
|
+
.then(() => {
|
|
494
473
|
void this.finalize(output, (error, result) => {
|
|
495
474
|
if (error) {
|
|
496
475
|
resolve(emptyData());
|
|
@@ -515,6 +494,9 @@ class JimpHandler {
|
|
|
515
494
|
}
|
|
516
495
|
}
|
|
517
496
|
});
|
|
497
|
+
})
|
|
498
|
+
.catch(() => {
|
|
499
|
+
resolve(emptyData());
|
|
518
500
|
});
|
|
519
501
|
});
|
|
520
502
|
}
|
|
@@ -524,51 +506,23 @@ class JimpHandler {
|
|
|
524
506
|
}
|
|
525
507
|
const data = this.instance.cropData;
|
|
526
508
|
if (data) {
|
|
527
|
-
this.handler
|
|
509
|
+
Jimp.applyCrop(this.handler, data);
|
|
528
510
|
}
|
|
529
511
|
}
|
|
530
512
|
opacity() {
|
|
531
|
-
if (this.aborted) {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
513
|
const value = this.instance.opacityValue ?? NaN;
|
|
535
|
-
if (value >= 0) {
|
|
514
|
+
if (value >= 0 && !this.aborted) {
|
|
536
515
|
this.handler.opacity(value);
|
|
537
516
|
}
|
|
538
517
|
}
|
|
539
|
-
|
|
540
|
-
if (this.aborted) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
const data = this.instance.qualityData;
|
|
544
|
-
if (data && !isNaN(data.value)) {
|
|
545
|
-
this.handler.quality(data.value);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
write(output, callback) {
|
|
549
|
-
if (this.aborted) {
|
|
550
|
-
if (callback) {
|
|
551
|
-
callback((0, types_1.createAbortError)(), '');
|
|
552
|
-
}
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
this.handler.write(output, err => {
|
|
556
|
-
if (!err) {
|
|
557
|
-
void this.finalize(output, callback);
|
|
558
|
-
}
|
|
559
|
-
else if (callback) {
|
|
560
|
-
callback(err, '');
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
async writeAsync(output, callback) {
|
|
518
|
+
async write(output, callback) {
|
|
565
519
|
if (this.aborted) {
|
|
566
520
|
if (callback) {
|
|
567
|
-
callback(
|
|
521
|
+
callback(types.createAbortError(), '');
|
|
568
522
|
}
|
|
569
523
|
return;
|
|
570
524
|
}
|
|
571
|
-
return this.handler.
|
|
525
|
+
return this.handler.write(output, this.instance.getEncodeOptions())
|
|
572
526
|
.then(() => {
|
|
573
527
|
void this.finalize(output, callback);
|
|
574
528
|
})
|
|
@@ -594,31 +548,34 @@ class JimpHandler {
|
|
|
594
548
|
class Jimp extends Image {
|
|
595
549
|
static [kJimp] = true;
|
|
596
550
|
static async transform(file, command, options = {}) {
|
|
597
|
-
const [outputType, saveAs, outputAs] = (0, util_1.parseFormat)(command = command.trim(), options.mimeType);
|
|
598
|
-
if (!outputType) {
|
|
599
|
-
return emptyResult(options);
|
|
600
|
-
}
|
|
601
551
|
const instance = new Jimp(options.module);
|
|
602
552
|
let buffer = null;
|
|
603
553
|
if (Buffer.isBuffer(file)) {
|
|
604
|
-
const tempDir = TEMP_DIR || instance.getTempDir();
|
|
605
|
-
if (!this.createDir(tempDir)) {
|
|
606
|
-
return emptyResult(options);
|
|
607
|
-
}
|
|
608
554
|
try {
|
|
609
|
-
|
|
555
|
+
setTempDir(instance);
|
|
556
|
+
const { mime, ext } = await this.resolveMime(file) || { ext: 'unknown' };
|
|
610
557
|
buffer = file;
|
|
611
|
-
fs.writeFileSync(file = path.join(
|
|
558
|
+
fs.writeFileSync(file = path.join(TEMP_DIR, crypto.randomUUID() + '.' + ext), buffer);
|
|
559
|
+
if (mime) {
|
|
560
|
+
options.mimeType = mime;
|
|
561
|
+
}
|
|
612
562
|
}
|
|
613
563
|
catch {
|
|
614
564
|
return emptyResult(options);
|
|
615
565
|
}
|
|
616
566
|
options.cache = false;
|
|
617
567
|
}
|
|
568
|
+
else {
|
|
569
|
+
options.mimeType ||= Image.lookupMime(file);
|
|
570
|
+
}
|
|
571
|
+
const [outputType, saveAs, outputAs] = util.parseFormat(command = command.trim(), options.mimeType);
|
|
572
|
+
if (!outputType) {
|
|
573
|
+
return emptyResult(options);
|
|
574
|
+
}
|
|
618
575
|
const filename = path.basename(file);
|
|
619
576
|
const broadcastId = options.broadcastId;
|
|
620
577
|
if (broadcastId) {
|
|
621
|
-
if (
|
|
578
|
+
if (types.isPlainObject(broadcastId)) {
|
|
622
579
|
instance.broadcastId = broadcastId.value;
|
|
623
580
|
if (broadcastId.stripAnsi === false) {
|
|
624
581
|
instance.supports('stripAnsi', false);
|
|
@@ -630,7 +587,7 @@ class Jimp extends Image {
|
|
|
630
587
|
}
|
|
631
588
|
const writeMessage = (failed, cTimeMs) => {
|
|
632
589
|
if (cTimeMs || options.startTime) {
|
|
633
|
-
formatMessage(instance, filename +
|
|
590
|
+
formatMessage(instance, filename + util.showOutputType(options.mimeType, outputType, outputAs), options.startTime, failed, false, cTimeMs);
|
|
634
591
|
}
|
|
635
592
|
};
|
|
636
593
|
let tempKey, tempFile;
|
|
@@ -644,9 +601,9 @@ class Jimp extends Image {
|
|
|
644
601
|
}
|
|
645
602
|
instance.formatMessage(Image.LOG_TYPE.IMAGE, "jimp", ["Transforming image...", filename], command);
|
|
646
603
|
Image.initCpuUsage(instance);
|
|
647
|
-
return performCommand(
|
|
648
|
-
.then(async (
|
|
649
|
-
const result = await
|
|
604
|
+
return performCommand(instance, file, command, outputType, outputAs, file)
|
|
605
|
+
.then(async (img) => {
|
|
606
|
+
const result = await img.getBuffer(options.tempFile, saveAs);
|
|
650
607
|
instance.flushLog();
|
|
651
608
|
writeMessage(!result || instance.errors.length > 0);
|
|
652
609
|
if (result && tempKey && tempFile) {
|
|
@@ -654,15 +611,165 @@ class Jimp extends Image {
|
|
|
654
611
|
}
|
|
655
612
|
return result;
|
|
656
613
|
})
|
|
657
|
-
.catch(() =>
|
|
658
|
-
return emptyResult(options);
|
|
659
|
-
})
|
|
614
|
+
.catch(() => emptyResult(options))
|
|
660
615
|
.finally(() => {
|
|
661
616
|
if (buffer && !options.cache) {
|
|
662
617
|
removeFile(file);
|
|
663
618
|
}
|
|
664
619
|
});
|
|
665
620
|
}
|
|
621
|
+
static applyBackground(instance, data) {
|
|
622
|
+
if (!isNaN(data.color)) {
|
|
623
|
+
instance.background = data.color;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
static applyResize(instance, data) {
|
|
627
|
+
this.applyBackground(instance, data);
|
|
628
|
+
const { width: w, height: h } = data;
|
|
629
|
+
let align = 0;
|
|
630
|
+
switch (data.align[0]) {
|
|
631
|
+
case 'left':
|
|
632
|
+
align |= jimp.HorizontalAlign.LEFT;
|
|
633
|
+
break;
|
|
634
|
+
case 'center':
|
|
635
|
+
align |= jimp.HorizontalAlign.CENTER;
|
|
636
|
+
break;
|
|
637
|
+
case 'right':
|
|
638
|
+
align |= jimp.HorizontalAlign.RIGHT;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
switch (data.align[1]) {
|
|
642
|
+
case 'top':
|
|
643
|
+
align |= jimp.VerticalAlign.TOP;
|
|
644
|
+
break;
|
|
645
|
+
case 'middle':
|
|
646
|
+
align |= jimp.VerticalAlign.MIDDLE;
|
|
647
|
+
break;
|
|
648
|
+
case 'bottom':
|
|
649
|
+
align |= jimp.VerticalAlign.BOTTOM;
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
switch (data.mode) {
|
|
653
|
+
case 'contain':
|
|
654
|
+
instance.contain({ w, h, align: align > 0 ? align : undefined });
|
|
655
|
+
break;
|
|
656
|
+
case 'cover':
|
|
657
|
+
instance.cover({ w, h, align: align > 0 ? align : undefined });
|
|
658
|
+
break;
|
|
659
|
+
case 'scale':
|
|
660
|
+
instance.scaleToFit({ w, h });
|
|
661
|
+
break;
|
|
662
|
+
default: {
|
|
663
|
+
let mode;
|
|
664
|
+
switch (data.algorithm) {
|
|
665
|
+
case 'bilinear':
|
|
666
|
+
mode = jimp.ResizeStrategy.BILINEAR;
|
|
667
|
+
break;
|
|
668
|
+
case 'bicubic':
|
|
669
|
+
mode = jimp.ResizeStrategy.BICUBIC;
|
|
670
|
+
break;
|
|
671
|
+
case 'hermite':
|
|
672
|
+
mode = jimp.ResizeStrategy.HERMITE;
|
|
673
|
+
break;
|
|
674
|
+
case 'bezier':
|
|
675
|
+
mode = jimp.ResizeStrategy.BEZIER;
|
|
676
|
+
break;
|
|
677
|
+
case 'nearest':
|
|
678
|
+
case 'nearest_neighbor':
|
|
679
|
+
mode = jimp.ResizeStrategy.NEAREST_NEIGHBOR;
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
const options = { mode };
|
|
683
|
+
if (w < Infinity) {
|
|
684
|
+
options.w = w;
|
|
685
|
+
}
|
|
686
|
+
if (h < Infinity) {
|
|
687
|
+
options.h = h;
|
|
688
|
+
}
|
|
689
|
+
instance.resize(options);
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
static applyCrop(instance, data) {
|
|
695
|
+
instance.crop({ x: data.x, y: data.y, w: data.width, h: data.height });
|
|
696
|
+
}
|
|
697
|
+
static applyRotate(instance, data) {
|
|
698
|
+
this.applyBackground(instance, data);
|
|
699
|
+
instance.rotate(data.values[0]);
|
|
700
|
+
}
|
|
701
|
+
static applyMethod(instance, name, ...args) {
|
|
702
|
+
const alias = getMethodName(name);
|
|
703
|
+
if (!alias) {
|
|
704
|
+
throw types.errorValue("Invalid method name", name);
|
|
705
|
+
}
|
|
706
|
+
switch (alias) {
|
|
707
|
+
case 'background':
|
|
708
|
+
if (args.length === 4) {
|
|
709
|
+
setBackground(instance, args);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
if (args.length === 1) {
|
|
713
|
+
if (typeof args[0] === 'number') {
|
|
714
|
+
instance.background = args[0];
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
if (Array.isArray(args[0])) {
|
|
718
|
+
setBackground(instance, args[0]);
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
errorParameters(alias, args);
|
|
723
|
+
break;
|
|
724
|
+
case 'sepia':
|
|
725
|
+
case 'normalize':
|
|
726
|
+
case 'invert':
|
|
727
|
+
case 'greyscale':
|
|
728
|
+
case 'dither':
|
|
729
|
+
instance[alias]();
|
|
730
|
+
break;
|
|
731
|
+
default: {
|
|
732
|
+
const arg = args.shift();
|
|
733
|
+
switch (alias) {
|
|
734
|
+
case 'blur':
|
|
735
|
+
case 'gaussian':
|
|
736
|
+
case 'brightness':
|
|
737
|
+
case 'contrast':
|
|
738
|
+
case 'posterize':
|
|
739
|
+
case 'opacity':
|
|
740
|
+
case 'fade':
|
|
741
|
+
if (types.isPlainObject(arg)) {
|
|
742
|
+
errorParameters(alias, args);
|
|
743
|
+
}
|
|
744
|
+
instance[alias](+arg);
|
|
745
|
+
break;
|
|
746
|
+
case 'pixelate':
|
|
747
|
+
case 'convolute':
|
|
748
|
+
instance[alias](arg);
|
|
749
|
+
break;
|
|
750
|
+
case 'print':
|
|
751
|
+
if (types.isPlainObject(arg)) {
|
|
752
|
+
if (types.isString(arg.font)) {
|
|
753
|
+
arg.font = FONT_DATA[arg.font];
|
|
754
|
+
}
|
|
755
|
+
instance.print(arg);
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
errorParameters(alias, arg);
|
|
759
|
+
}
|
|
760
|
+
break;
|
|
761
|
+
default:
|
|
762
|
+
if (!types.isPlainObject(arg)) {
|
|
763
|
+
errorParameters(alias, arg);
|
|
764
|
+
}
|
|
765
|
+
instance[alias](arg);
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return args;
|
|
772
|
+
}
|
|
666
773
|
_moduleName = "jimp";
|
|
667
774
|
_threadable = true;
|
|
668
775
|
resizeData = null;
|
|
@@ -671,6 +778,7 @@ class Jimp extends Image {
|
|
|
671
778
|
qualityData = null;
|
|
672
779
|
methodData = null;
|
|
673
780
|
opacityValue = NaN;
|
|
781
|
+
outputType = '';
|
|
674
782
|
parseRotate(value) {
|
|
675
783
|
const data = super.parseRotate(value);
|
|
676
784
|
if (data && this.settings.jimp?.rotate_clockwise) {
|
|
@@ -681,43 +789,61 @@ class Jimp extends Image {
|
|
|
681
789
|
}
|
|
682
790
|
return data;
|
|
683
791
|
}
|
|
792
|
+
parseWorker(command, outputType) {
|
|
793
|
+
if (typeof command === 'string') {
|
|
794
|
+
command = this.parseCommand(command);
|
|
795
|
+
}
|
|
796
|
+
if (!isUnsupported(outputType)) {
|
|
797
|
+
const { method, rotate } = command;
|
|
798
|
+
if (rotate && rotate.values.length > 1) {
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
if (method) {
|
|
802
|
+
const values = method.map(item => [getMethodName(item[0]) || item[0], item[1]]);
|
|
803
|
+
if (values.find(item => !item[0])) {
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
command.method = values;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
684
811
|
async using(data, command) {
|
|
685
812
|
if (this.aborted) {
|
|
686
|
-
return
|
|
813
|
+
return types.createAbortError(true);
|
|
687
814
|
}
|
|
688
815
|
return new Promise(async (resolve, reject) => {
|
|
689
816
|
const { host, file } = data;
|
|
690
817
|
const localUri = host.getLocalUri(data);
|
|
691
818
|
const mimeType = host.getMimeType(data);
|
|
692
|
-
if (!localUri || !
|
|
693
|
-
reject(
|
|
819
|
+
if (!localUri || !util.MIME_INPUT.has(mimeType)) {
|
|
820
|
+
reject(types.errorValue("Unknown", !localUri ? 'URI' : 'MIME'));
|
|
694
821
|
return;
|
|
695
822
|
}
|
|
696
823
|
if (!this.canRead(localUri, { ownPermissionOnly: true })) {
|
|
697
|
-
reject(
|
|
824
|
+
reject(types.errorValue("Not permitted to read file", localUri));
|
|
698
825
|
return;
|
|
699
826
|
}
|
|
700
|
-
const [outputType, saveAs, outputAs] =
|
|
827
|
+
const [outputType, saveAs, outputAs] = util.parseFormat(command = command.trim(), mimeType);
|
|
701
828
|
if (!outputType) {
|
|
702
|
-
reject(
|
|
829
|
+
reject(types.errorValue("Invalid format", /^\w+/.exec(command)?.[0] || "Unknown"));
|
|
703
830
|
return;
|
|
704
831
|
}
|
|
705
832
|
const replace = command.includes('@');
|
|
706
833
|
const output = host.addCopy(data.getObject({ command, outputType }), saveAs, replace);
|
|
707
834
|
if (!output) {
|
|
708
|
-
reject(
|
|
835
|
+
reject(types.errorValue("Not able to copy file", outputType));
|
|
709
836
|
return;
|
|
710
837
|
}
|
|
711
838
|
if (!this.canWrite(output, { ownPermissionOnly: true })) {
|
|
712
|
-
reject(
|
|
839
|
+
reject(types.errorValue("Not permitted to write file", output));
|
|
713
840
|
return;
|
|
714
841
|
}
|
|
715
842
|
const startTime = process.hrtime();
|
|
716
|
-
const success = (result, ctimeMs) => {
|
|
843
|
+
const success = (result, worker, ctimeMs) => {
|
|
717
844
|
const filename = path.basename(result);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
host.writeImage(document, data.getObject({ command, output: result }));
|
|
845
|
+
if (file.document) {
|
|
846
|
+
host.writeImage(file.document, data.getObject({ command, output: result }));
|
|
721
847
|
}
|
|
722
848
|
if (host.getLocalUri(data) !== result) {
|
|
723
849
|
if (command.includes('%')) {
|
|
@@ -739,7 +865,7 @@ class Jimp extends Image {
|
|
|
739
865
|
if (replace && file.localUri !== output && !host.assets.find(item => item.localUri === output && !item.invalid)) {
|
|
740
866
|
host.filesToRemove.add(output);
|
|
741
867
|
}
|
|
742
|
-
formatMessage(this,
|
|
868
|
+
formatMessage(this, util.showInputType(mimeType, outputType, outputAs) + filename, startTime, false, worker, ctimeMs);
|
|
743
869
|
resolve();
|
|
744
870
|
};
|
|
745
871
|
let tempKey, tempFile;
|
|
@@ -747,33 +873,30 @@ class Jimp extends Image {
|
|
|
747
873
|
let buffer, ctimeMs;
|
|
748
874
|
[buffer, tempFile] = getImageCache(this, tempKey = (file.etag || Image.asHash(file.buffer)) + command + mimeType);
|
|
749
875
|
if (buffer) {
|
|
750
|
-
const result = outputAs === 'webp' ?
|
|
876
|
+
const result = outputAs === 'webp' ? util.renameExt(output, 'webp', replace) : output;
|
|
751
877
|
fs.writeFileSync(result, file.buffer = buffer);
|
|
752
|
-
success(result, ctimeMs);
|
|
878
|
+
success(result, false, ctimeMs);
|
|
753
879
|
return;
|
|
754
880
|
}
|
|
755
881
|
}
|
|
756
|
-
const
|
|
882
|
+
const outputData = this.parseCommand(command);
|
|
883
|
+
const finalize = (value, worker) => {
|
|
757
884
|
if (tempFile && tempKey) {
|
|
758
885
|
void setImageCache(this, tempKey, tempFile, value);
|
|
759
886
|
}
|
|
760
887
|
if (replace) {
|
|
761
888
|
delete file.buffer;
|
|
762
889
|
}
|
|
763
|
-
success(value);
|
|
890
|
+
success(value, worker);
|
|
764
891
|
};
|
|
765
892
|
const transformBuffer = (bmpFile) => {
|
|
766
893
|
startMessage();
|
|
767
|
-
performCommand(
|
|
894
|
+
performCommand(this, bmpFile || file.buffer || localUri, outputData, outputType, outputAs, output, { host, file })
|
|
768
895
|
.then(img => {
|
|
769
896
|
if (typeof bmpFile === 'string') {
|
|
770
897
|
removeFile(bmpFile);
|
|
771
898
|
}
|
|
772
|
-
|
|
773
|
-
this.writeFail(["Unable to finalize image", "jimp"], err, { type: 2048, startTime });
|
|
774
|
-
resolve();
|
|
775
|
-
};
|
|
776
|
-
if (outputType === jimp.MIME_GIF) {
|
|
899
|
+
if (outputType === Image.MIME_GIF) {
|
|
777
900
|
try {
|
|
778
901
|
const { GifUtil, GifFrame } = gifwrap;
|
|
779
902
|
const frame = new GifFrame(img.handler.bitmap);
|
|
@@ -782,160 +905,153 @@ class Jimp extends Image {
|
|
|
782
905
|
.then(() => {
|
|
783
906
|
finalize(output);
|
|
784
907
|
})
|
|
785
|
-
.catch(
|
|
908
|
+
.catch(reject);
|
|
786
909
|
}
|
|
787
910
|
catch (err) {
|
|
788
|
-
|
|
911
|
+
reject(err);
|
|
789
912
|
}
|
|
790
913
|
}
|
|
791
914
|
else {
|
|
792
|
-
img.write(output, (err, result) => {
|
|
915
|
+
void img.write(output, (err, result) => {
|
|
793
916
|
if (!err && result) {
|
|
794
917
|
finalize(result);
|
|
795
918
|
}
|
|
796
|
-
else if (
|
|
919
|
+
else if (types.isErrorCode(err, types.ERR_CODE.MODULE_NOT_FOUND)) {
|
|
797
920
|
resolve();
|
|
798
921
|
}
|
|
799
922
|
else {
|
|
800
|
-
|
|
923
|
+
reject(err || new Error("Unknown"));
|
|
801
924
|
}
|
|
802
925
|
});
|
|
803
926
|
}
|
|
804
927
|
})
|
|
805
|
-
.catch(
|
|
806
|
-
this.writeFail(["Unable to read buffer", path.basename(localUri)], err, { type: 2048, startTime });
|
|
807
|
-
resolve();
|
|
808
|
-
});
|
|
928
|
+
.catch(reject);
|
|
809
929
|
};
|
|
810
930
|
const startMessage = () => {
|
|
811
931
|
host.formatMessage(2048, "jimp", ["Transforming image...", path.basename(localUri)], command);
|
|
812
932
|
};
|
|
813
|
-
if (mimeType ===
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
933
|
+
if (mimeType === Image.MIME_GIF && isUnsupported(outputType)) {
|
|
934
|
+
const transformWebP = async (target, modified) => {
|
|
935
|
+
if (outputAs === 'webp') {
|
|
936
|
+
if (!modified) {
|
|
937
|
+
startMessage();
|
|
938
|
+
}
|
|
939
|
+
const { path: webp_path, gif2webp } = this.settings.webp ||= {};
|
|
940
|
+
const webp = util.renameExt(output, 'webp', replace);
|
|
941
|
+
const args = [util.normalizePath(target)];
|
|
942
|
+
const quality = outputData.quality;
|
|
943
|
+
if (quality) {
|
|
944
|
+
const { value, method } = quality;
|
|
945
|
+
if (!isNaN(value)) {
|
|
946
|
+
args.push('-q', value.toString());
|
|
820
947
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
const args = [(0, util_1.normalizePath)(target)];
|
|
824
|
-
const quality = cmd.quality;
|
|
825
|
-
if (quality) {
|
|
826
|
-
const { value, method } = quality;
|
|
827
|
-
if (!isNaN(value)) {
|
|
828
|
-
args.push('-q', value.toString());
|
|
829
|
-
}
|
|
830
|
-
if (!isNaN(method)) {
|
|
831
|
-
args.push('-m', method.toString());
|
|
832
|
-
}
|
|
948
|
+
if (!isNaN(method)) {
|
|
949
|
+
args.push('-m', method.toString());
|
|
833
950
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
951
|
+
}
|
|
952
|
+
if (Array.isArray(gif2webp)) {
|
|
953
|
+
for (let i = 0, length = gif2webp.length; i < length; ++i) {
|
|
954
|
+
const arg = gif2webp[i];
|
|
955
|
+
switch (arg) {
|
|
956
|
+
case '-o':
|
|
957
|
+
++i;
|
|
958
|
+
case '-h':
|
|
959
|
+
case '-version':
|
|
960
|
+
continue;
|
|
961
|
+
case '-q':
|
|
962
|
+
case '-m':
|
|
963
|
+
if (args.includes(arg)) {
|
|
839
964
|
++i;
|
|
840
|
-
case '-h':
|
|
841
|
-
case '-version':
|
|
842
965
|
continue;
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
break;
|
|
850
|
-
case '--':
|
|
851
|
-
i = length;
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
|
-
args.push(arg);
|
|
966
|
+
}
|
|
967
|
+
break;
|
|
968
|
+
case '--':
|
|
969
|
+
i = length;
|
|
970
|
+
continue;
|
|
855
971
|
}
|
|
972
|
+
args.push(arg);
|
|
856
973
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
else {
|
|
865
|
-
reject(err);
|
|
866
|
-
}
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
catch (err) {
|
|
870
|
-
if (this.checkPackage(err, 'gif2webp-bin', 2048)) {
|
|
871
|
-
resolve();
|
|
974
|
+
}
|
|
975
|
+
args.push('-o', util.normalizePath(webp));
|
|
976
|
+
try {
|
|
977
|
+
child_process.execFile(types.sanitizeCmd(await util.importBinary('gif2webp', webp_path), args), { shell: true, signal: this.signal, ...execOptions(this.settings) }, (err, stdout) => {
|
|
978
|
+
if (!err) {
|
|
979
|
+
this.addLog(4, stdout);
|
|
980
|
+
finalize(webp);
|
|
872
981
|
}
|
|
873
982
|
else {
|
|
874
983
|
reject(err);
|
|
875
984
|
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
else if (modified) {
|
|
879
|
-
finalize(output);
|
|
985
|
+
});
|
|
880
986
|
}
|
|
881
|
-
|
|
882
|
-
|
|
987
|
+
catch (err) {
|
|
988
|
+
if (this.checkPackage(err, 'gif2webp-bin', 2048)) {
|
|
989
|
+
resolve();
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
reject(err);
|
|
993
|
+
}
|
|
883
994
|
}
|
|
884
|
-
}
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
995
|
+
}
|
|
996
|
+
else if (modified) {
|
|
997
|
+
finalize(output);
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
resolve();
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
if (hasTransform(outputData)) {
|
|
1004
|
+
try {
|
|
1005
|
+
startMessage();
|
|
1006
|
+
const { GifUtil, BitmapImage } = gifwrap;
|
|
1007
|
+
GifUtil.read(file.buffer || localUri)
|
|
1008
|
+
.then(src => {
|
|
1009
|
+
rotateAnim(outputData);
|
|
1010
|
+
Promise.all(src.frames.map(async (frame) => {
|
|
1011
|
+
const bitmap = bmp.encode(frame.bitmap).data;
|
|
1012
|
+
const instance = await jimp.Jimp.read(bitmap);
|
|
1013
|
+
const handler = new JimpHandler(instance, this);
|
|
1014
|
+
return transformCommand(output, handler, outputData, Image.MIME_GIF);
|
|
1015
|
+
}))
|
|
1016
|
+
.then(items => {
|
|
1017
|
+
const quantize = this.settings.jimp?.gifwrap_quantize || '';
|
|
1018
|
+
const frames = src.frames;
|
|
1019
|
+
for (let i = 0, length = items.length; i < length; ++i) {
|
|
1020
|
+
const img = new BitmapImage(items[i].handler.bitmap);
|
|
1021
|
+
switch (quantize) {
|
|
1022
|
+
case 'none':
|
|
1023
|
+
break;
|
|
1024
|
+
case 'dekker':
|
|
1025
|
+
GifUtil.quantizeDekker(img, 256);
|
|
1026
|
+
break;
|
|
1027
|
+
case 'wu':
|
|
1028
|
+
GifUtil.quantizeWu(img, 256);
|
|
1029
|
+
break;
|
|
1030
|
+
default:
|
|
1031
|
+
GifUtil.quantizeSorokin(img, 256);
|
|
1032
|
+
break;
|
|
915
1033
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1034
|
+
frames[i].bitmap = img.bitmap;
|
|
1035
|
+
}
|
|
1036
|
+
GifUtil.write(output, frames, src)
|
|
1037
|
+
.then(() => {
|
|
1038
|
+
void transformWebP(output, true);
|
|
921
1039
|
})
|
|
922
1040
|
.catch(reject);
|
|
923
1041
|
})
|
|
924
1042
|
.catch(reject);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
reject(err);
|
|
928
|
-
}
|
|
1043
|
+
})
|
|
1044
|
+
.catch(reject);
|
|
929
1045
|
}
|
|
930
|
-
|
|
931
|
-
|
|
1046
|
+
catch (err) {
|
|
1047
|
+
reject(err);
|
|
932
1048
|
}
|
|
933
1049
|
}
|
|
934
1050
|
else {
|
|
935
|
-
|
|
1051
|
+
void transformWebP(localUri, false);
|
|
936
1052
|
}
|
|
937
1053
|
}
|
|
938
|
-
else if (mimeType ===
|
|
1054
|
+
else if (mimeType === Image.MIME_WEBP) {
|
|
939
1055
|
const tryWebpMux = async () => {
|
|
940
1056
|
try {
|
|
941
1057
|
const webp = new (WEBPMUX ||= require('node-webpmux')).Image();
|
|
@@ -944,21 +1060,25 @@ class Jimp extends Image {
|
|
|
944
1060
|
WEBPMUX_INIT = true;
|
|
945
1061
|
}
|
|
946
1062
|
await webp.load(host.getBuffer(file));
|
|
947
|
-
if (!(webp.hasAnim && (outputType ===
|
|
948
|
-
|
|
1063
|
+
if (!(webp.hasAnim && (outputType === Image.MIME_WEBP || outputType === Image.MIME_GIF))) {
|
|
1064
|
+
const buffer = Image.toABGR(await (!webp.hasAnim ? webp.getImageData() : webp.getFrameData(0)));
|
|
1065
|
+
const bitmap = bmp.encode({ width: webp.width, height: webp.height, data: buffer }).data;
|
|
1066
|
+
transformBuffer(bitmap);
|
|
949
1067
|
return true;
|
|
950
1068
|
}
|
|
951
|
-
|
|
952
|
-
if (hasTransform(cmd)) {
|
|
1069
|
+
if (hasTransform(outputData)) {
|
|
953
1070
|
startMessage();
|
|
954
1071
|
Promise.all(webp.frames.map(async (frame, index) => {
|
|
955
|
-
const
|
|
1072
|
+
const buffer = Image.toABGR(await webp.getFrameData(index));
|
|
1073
|
+
const bitmap = bmp.encode({ width: frame.width, height: frame.height, data: buffer }).data;
|
|
1074
|
+
const instance = await jimp.Jimp.read(bitmap);
|
|
1075
|
+
const handler = new JimpHandler(instance, this);
|
|
956
1076
|
handler.background(webp.anim.bgColor);
|
|
957
|
-
return transformCommand(
|
|
1077
|
+
return transformCommand(output, handler, outputData, Image.MIME_BMP);
|
|
958
1078
|
}))
|
|
959
1079
|
.then(items => {
|
|
960
1080
|
const length = items.length;
|
|
961
|
-
if (outputType ===
|
|
1081
|
+
if (outputType === Image.MIME_GIF) {
|
|
962
1082
|
try {
|
|
963
1083
|
const { GifFrame, GifUtil, BitmapImage } = gifwrap;
|
|
964
1084
|
const quantize = this.settings.jimp?.gifwrap_quantize || '';
|
|
@@ -992,10 +1112,10 @@ class Jimp extends Image {
|
|
|
992
1112
|
}
|
|
993
1113
|
}
|
|
994
1114
|
else {
|
|
995
|
-
const { value: q = NaN, method: m = NaN, preset } =
|
|
1115
|
+
const { value: q = NaN, method: m = NaN, preset } = outputData.quality || {};
|
|
996
1116
|
const quality = !isNaN(q) ? q : undefined;
|
|
997
1117
|
const method = !isNaN(m) ? m : undefined;
|
|
998
|
-
rotateAnim(
|
|
1118
|
+
rotateAnim(outputData);
|
|
999
1119
|
const frames = new Array(length);
|
|
1000
1120
|
let w = 0, h = 0;
|
|
1001
1121
|
for (let i = 0; i < length; ++i) {
|
|
@@ -1006,7 +1126,7 @@ class Jimp extends Image {
|
|
|
1006
1126
|
}
|
|
1007
1127
|
Promise.all(frames)
|
|
1008
1128
|
.then(() => {
|
|
1009
|
-
webp.save(output, { width: w, height: h, bgColor:
|
|
1129
|
+
webp.save(output, { width: w, height: h, bgColor: outputData.rotate ? [0, 0, 0, 0] : undefined })
|
|
1010
1130
|
.then(() => {
|
|
1011
1131
|
finalize(output);
|
|
1012
1132
|
})
|
|
@@ -1033,11 +1153,9 @@ class Jimp extends Image {
|
|
|
1033
1153
|
}
|
|
1034
1154
|
return false;
|
|
1035
1155
|
};
|
|
1036
|
-
const settings = this.settings;
|
|
1037
|
-
const { path: webp_path } = settings.webp ||= {};
|
|
1038
1156
|
const bmpFile = getTempPath(this, 'bmp');
|
|
1039
1157
|
try {
|
|
1040
|
-
child_process.execFile(
|
|
1158
|
+
child_process.execFile(types.sanitizeCmd(await util.importBinary('dwebp', this.settings.webp?.path), [util.normalizePath(localUri), '-bmp', '-o', util.normalizePath(bmpFile)]), { shell: true, signal: this.signal, ...execOptions(this.settings) }, err => {
|
|
1041
1159
|
if (!err) {
|
|
1042
1160
|
transformBuffer(bmpFile);
|
|
1043
1161
|
}
|
|
@@ -1063,10 +1181,47 @@ class Jimp extends Image {
|
|
|
1063
1181
|
}
|
|
1064
1182
|
}
|
|
1065
1183
|
else {
|
|
1184
|
+
if (core.WorkerChannel.hasPermission(file) && this.parseWorker(outputData, outputType)) {
|
|
1185
|
+
try {
|
|
1186
|
+
let timer = null;
|
|
1187
|
+
const failed = (message) => {
|
|
1188
|
+
reject(types.errorMessage("jimp", message, localUri));
|
|
1189
|
+
};
|
|
1190
|
+
const worker = WORKER_JIMP.sendObject({ data: file.buffer || localUri, commandData: outputData, outputType, output, options: this.getEncodeOptions() }, [], (value) => {
|
|
1191
|
+
if (timer) {
|
|
1192
|
+
clearTimeout(timer);
|
|
1193
|
+
}
|
|
1194
|
+
if (value) {
|
|
1195
|
+
finalize(value, true);
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
failed("Worker did not finish");
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
if (worker) {
|
|
1202
|
+
if (typeof file.worker === 'number') {
|
|
1203
|
+
timer = setTimeout(() => {
|
|
1204
|
+
void worker.terminate();
|
|
1205
|
+
failed("Worker timeout was exceeded");
|
|
1206
|
+
}, core.WorkerGroup.checkTimeout(file.worker, true));
|
|
1207
|
+
}
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
catch (err) {
|
|
1212
|
+
this.addLog(3, err, { source: 'worker' });
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1066
1215
|
transformBuffer();
|
|
1067
1216
|
}
|
|
1068
1217
|
});
|
|
1069
1218
|
}
|
|
1219
|
+
getEncodeOptions() {
|
|
1220
|
+
if (this.qualityData && this.outputType === Image.MIME_JPEG) {
|
|
1221
|
+
return { quality: this.qualityData.value };
|
|
1222
|
+
}
|
|
1223
|
+
return this.settings.jimp?.options?.encode?.[this.outputType];
|
|
1224
|
+
}
|
|
1070
1225
|
get settings() {
|
|
1071
1226
|
return this.module.settings ||= {};
|
|
1072
1227
|
}
|