@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 CHANGED
@@ -1,14 +1,18 @@
1
- "use strict";
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
- dither565: 'dt',
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
- colorType: 'ce',
62
- cropQuiet: 'cq',
63
- deflateLevel: 'dl',
64
- deflateStrategy: 'ds',
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.length === 2) {
73
- value = value.toLowerCase();
74
- for (const alias in METHOD_ALIAS) {
75
- if (METHOD_ALIAS[alias] === value) {
76
- return alias;
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
- else if (METHOD_ALIAS[value] || METHOD_ALIAS[value = value.toLowerCase()]) {
81
- return value;
99
+ else if (METHOD_ALIAS[value] || METHOD_ALIAS[value = value.toLowerCase()]) {
100
+ return value;
101
+ }
82
102
  }
83
103
  }
84
- async function performCommand(host, instance, localUri, command, outputType, outputAs, { buffer, parent } = {}) {
85
- return await jimp.read((buffer || localUri))
86
- .then(async (img) => {
87
- return await transformCommand(localUri, new JimpHandler(img, instance, host), command, outputType, outputAs, parent);
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 ((0, types_1.isPlainObject)(exec)) {
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(localFile, handler, command, outputType, outputAs, parent) {
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 !== jimp.MIME_JPEG) {
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
- return handler;
137
+ break;
120
138
  case 1:
121
- return handler.rotate();
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.rotate(localFile, (err, result) => {
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
- TEMP_DIR ||= instance.getTempDir({ moduleDir: true, increment: 5 });
178
- return [null, TEMP_DIR ? path.join(TEMP_DIR, (0, node_crypto_1.randomUUID)()) : ''];
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
- TEMP_DIR = instance.getTempDir({ moduleDir: true, increment: 5 });
204
+ setTempDir(instance);
183
205
  const settings = instance.settings.jimp ||= {};
184
- const expires = (0, types_1.parseExpires)(settings.cache_expires || 0);
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 ((0, types_1.isPlainObject)(data) && fs.existsSync(data.tempFile)) {
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
- const tempDir = TEMP_DIR || instance.getTempDir({ moduleDir: true, createDir: true }) || instance.getTempDir();
232
- return path.join(tempDir, (0, node_crypto_1.randomUUID)() + '.' + ext);
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(localFile, callback) {
301
+ async rotate(output, callback) {
255
302
  if (this.aborted) {
256
- return this;
303
+ return;
257
304
  }
258
305
  const data = this.instance.rotateData;
259
306
  if (data) {
260
- const { values, color } = data;
261
- if (!isNaN(color)) {
262
- this.handler.background(color);
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
- const length = values.length;
266
- const deg = values[0];
267
- if (length > 1 && localFile) {
268
- const leading = localFile.substring(0, localFile.lastIndexOf('.') + 1);
269
- const ext = path.extname(localFile);
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
- if (deg) {
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 ((0, types_1.isString)(src) && typeof x === 'number' && typeof y === 'number') {
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
- throw (0, types_1.errorValue)("Invalid parameters", alias);
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
- this.handler[alias](...args);
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
- const { width, height, color, algorithm, align, mode } = data;
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
- if (Array.isArray(value)) {
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
- const instance = this.instance;
402
- if (instance.outputAs === 'webp' && path.extname(output).toLowerCase() !== '.webp') {
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 filename = (0, util_1.renameExt)(output, 'webp', replace);
408
- const args = [(0, util_1.normalizePath)(output)];
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', (0, util_1.normalizePath)(filename));
431
+ args.push('-o', util.normalizePath(outFile));
450
432
  try {
451
- child_process.execFile((0, types_1.sanitizeCmd)(await (0, util_1.importBinary)('cwebp', webp.path), args), { shell: true, signal: this.instance.signal, ...execOptions(settings) }, err => {
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(filename)], err, 2048);
435
+ this.instance.writeFail(["Unable to convert file", path.basename(outFile)], err, 2048);
454
436
  }
455
- else if (webp !== output) {
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 = filename;
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', "Unknown", { type: 2048, passThrough: !!callback });
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 && util_1.MIME_OUTPUT.has('image/' + (saveAs === 'jpg' ? 'jpeg' : saveAs)) ? saveAs : this.handler.getMIME().split('/').pop());
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, error => {
490
- if (error) {
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.crop(data.x, data.y, data.width, data.height);
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
- quality() {
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((0, types_1.createAbortError)(), '');
521
+ callback(types.createAbortError(), '');
568
522
  }
569
523
  return;
570
524
  }
571
- return this.handler.writeAsync(output)
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
- const { ext } = await this.resolveMime(file) || { ext: 'unknown' };
555
+ setTempDir(instance);
556
+ const { mime, ext } = await this.resolveMime(file) || { ext: 'unknown' };
610
557
  buffer = file;
611
- fs.writeFileSync(file = path.join(tempDir, (0, node_crypto_1.randomUUID)() + '.' + ext), buffer);
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 ((0, types_1.isPlainObject)(broadcastId)) {
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 + (0, util_1.showOutputType)(options.mimeType, outputType, outputAs), options.startTime, failed, cTimeMs);
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(null, instance, file, command, outputType, outputAs)
648
- .then(async (handler) => {
649
- const result = await handler.getBuffer(options.tempFile, saveAs);
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 (0, types_1.createAbortError)(true);
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 || !util_1.MIME_INPUT.has(mimeType)) {
693
- reject((0, types_1.errorValue)("Unknown", !localUri ? 'URI' : 'MIME'));
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((0, types_1.errorValue)("Not permitted to read file", localUri));
824
+ reject(types.errorValue("Not permitted to read file", localUri));
698
825
  return;
699
826
  }
700
- const [outputType, saveAs, outputAs] = (0, util_1.parseFormat)(command = command.trim(), mimeType, true);
827
+ const [outputType, saveAs, outputAs] = util.parseFormat(command = command.trim(), mimeType);
701
828
  if (!outputType) {
702
- reject((0, types_1.errorValue)("Invalid format", /^\w+/.exec(command)?.[0] || "Unknown"));
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((0, types_1.errorValue)("Not able to copy file", outputType));
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((0, types_1.errorValue)("Not permitted to write file", output));
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
- const document = file.document;
719
- if (document) {
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, (0, util_1.showInputType)(mimeType, outputType, outputAs) + filename, startTime, false, ctimeMs);
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' ? (0, util_1.renameExt)(output, 'webp', replace) : output;
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 finalize = (value) => {
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(host, this, localUri, command, outputType, outputAs, { buffer: bmpFile || file.buffer, parent: file })
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
- const errorResponse = (err) => {
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(errorResponse);
908
+ .catch(reject);
786
909
  }
787
910
  catch (err) {
788
- errorResponse(err);
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 (Image.isErrorCode(err, types_1.ERR_CODE.MODULE_NOT_FOUND)) {
919
+ else if (types.isErrorCode(err, types.ERR_CODE.MODULE_NOT_FOUND)) {
797
920
  resolve();
798
921
  }
799
922
  else {
800
- errorResponse(err || new Error("Unknown"));
923
+ reject(err || new Error("Unknown"));
801
924
  }
802
925
  });
803
926
  }
804
927
  })
805
- .catch((err) => {
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 === jimp.MIME_GIF) {
814
- if (outputType === jimp.MIME_GIF || outputType === "image/webp") {
815
- const cmd = this.parseCommand(command);
816
- const transformWebP = async (target, modified) => {
817
- if (outputAs === 'webp') {
818
- if (!modified) {
819
- startMessage();
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
- const { path: webp_path, gif2webp } = this.settings.webp ||= {};
822
- const webp = (0, util_1.renameExt)(output, 'webp', replace);
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
- if (Array.isArray(gif2webp)) {
835
- for (let i = 0, length = gif2webp.length; i < length; ++i) {
836
- const arg = gif2webp[i];
837
- switch (arg) {
838
- case '-o':
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
- case '-q':
844
- case '-m':
845
- if (args.includes(arg)) {
846
- ++i;
847
- continue;
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
- args.push('-o', (0, util_1.normalizePath)(webp));
858
- try {
859
- child_process.execFile((0, types_1.sanitizeCmd)(await (0, util_1.importBinary)('gif2webp', webp_path), args), { shell: true, signal: this.signal, ...execOptions(this.settings) }, (err, stdout) => {
860
- if (!err) {
861
- this.addLog(4, stdout);
862
- finalize(webp);
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
- else {
882
- resolve();
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 (hasTransform(cmd)) {
886
- try {
887
- startMessage();
888
- const { GifUtil, BitmapImage } = gifwrap;
889
- GifUtil.read(file.buffer || localUri)
890
- .then(gif => {
891
- rotateAnim(cmd);
892
- Promise.all(gif.frames.map(async (frame) => {
893
- const handler = new JimpHandler(GifUtil.shareAsJimp(jimp, frame), this);
894
- return transformCommand(localUri, handler, cmd, jimp.MIME_GIF);
895
- }))
896
- .then(items => {
897
- const quantize = this.settings.jimp?.gifwrap_quantize || '';
898
- const frames = gif.frames;
899
- for (let i = 0, length = items.length; i < length; ++i) {
900
- const img = new BitmapImage(items[i].handler.bitmap);
901
- switch (quantize) {
902
- case 'none':
903
- break;
904
- case 'dekker':
905
- GifUtil.quantizeDekker(img, 256);
906
- break;
907
- case 'wu':
908
- GifUtil.quantizeWu(img, 256);
909
- break;
910
- default:
911
- GifUtil.quantizeSorokin(img, 256);
912
- break;
913
- }
914
- frames[i].bitmap = img.bitmap;
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
- GifUtil.write(output, frames, gif)
917
- .then(() => {
918
- void transformWebP(output, true);
919
- })
920
- .catch(reject);
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
- catch (err) {
927
- reject(err);
928
- }
1043
+ })
1044
+ .catch(reject);
929
1045
  }
930
- else {
931
- void transformWebP(localUri, false);
1046
+ catch (err) {
1047
+ reject(err);
932
1048
  }
933
1049
  }
934
1050
  else {
935
- transformBuffer();
1051
+ void transformWebP(localUri, false);
936
1052
  }
937
1053
  }
938
- else if (mimeType === "image/webp") {
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 === "image/webp" || outputType === jimp.MIME_GIF))) {
948
- transformBuffer(bmp.encode({ width: webp.width, height: webp.height, data: Image.toABGR(await (!webp.hasAnim ? webp.getImageData() : webp.getFrameData(0))) }).data);
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
- const cmd = this.parseCommand(command);
952
- if (hasTransform(cmd)) {
1069
+ if (hasTransform(outputData)) {
953
1070
  startMessage();
954
1071
  Promise.all(webp.frames.map(async (frame, index) => {
955
- const handler = new JimpHandler(await jimp.read(bmp.encode({ width: frame.width, height: frame.height, data: Image.toABGR(await webp.getFrameData(index)) }).data), this);
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(localUri, handler, cmd, jimp.MIME_BMP);
1077
+ return transformCommand(output, handler, outputData, Image.MIME_BMP);
958
1078
  }))
959
1079
  .then(items => {
960
1080
  const length = items.length;
961
- if (outputType === jimp.MIME_GIF) {
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 } = cmd.quality || {};
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(cmd);
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: cmd.rotate ? [0, 0, 0, 0] : undefined })
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((0, types_1.sanitizeCmd)(await (0, util_1.importBinary)('dwebp', webp_path), [(0, util_1.normalizePath)(localUri), '-bmp', '-o', (0, util_1.normalizePath)(bmpFile)]), { shell: true, signal: this.signal, ...execOptions(settings) }, err => {
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
  }