sharp 0.31.2 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/binding.gyp +1 -1
- package/install/can-compile.js +3 -0
- package/install/dll-copy.js +3 -0
- package/install/libvips.js +6 -6
- package/lib/agent.js +5 -1
- package/lib/channel.js +4 -1
- package/lib/colour.js +3 -0
- package/lib/composite.js +3 -0
- package/lib/constructor.js +23 -9
- package/lib/index.d.ts +1614 -0
- package/lib/index.js +3 -0
- package/lib/input.js +39 -8
- package/lib/is.js +12 -0
- package/lib/libvips.js +4 -0
- package/lib/operation.js +94 -52
- package/lib/output.js +113 -11
- package/lib/platform.js +3 -0
- package/lib/resize.js +54 -19
- package/lib/sharp.js +3 -0
- package/lib/utility.js +5 -1
- package/package.json +30 -23
- package/src/common.cc +43 -14
- package/src/common.h +22 -16
- package/src/libvips/cplusplus/vips-operators.cpp +7 -0
- package/src/metadata.cc +17 -22
- package/src/metadata.h +3 -13
- package/src/operations.cc +13 -23
- package/src/operations.h +4 -15
- package/src/pipeline.cc +129 -69
- package/src/pipeline.h +30 -15
- package/src/sharp.cc +2 -13
- package/src/stats.cc +7 -17
- package/src/stats.h +2 -13
- package/src/utilities.cc +17 -28
- package/src/utilities.h +2 -13
package/src/operations.cc
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
// Copyright 2013
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
14
3
|
|
|
15
4
|
#include <algorithm>
|
|
16
5
|
#include <functional>
|
|
17
6
|
#include <memory>
|
|
18
7
|
#include <tuple>
|
|
19
8
|
#include <vector>
|
|
20
|
-
|
|
21
9
|
#include <vips/vips8>
|
|
22
10
|
|
|
23
11
|
#include "common.h"
|
|
@@ -57,7 +45,7 @@ namespace sharp {
|
|
|
57
45
|
/*
|
|
58
46
|
* Stretch luminance to cover full dynamic range.
|
|
59
47
|
*/
|
|
60
|
-
VImage Normalise(VImage image) {
|
|
48
|
+
VImage Normalise(VImage image, int const lower, int const upper) {
|
|
61
49
|
// Get original colourspace
|
|
62
50
|
VipsInterpretation typeBeforeNormalize = image.interpretation();
|
|
63
51
|
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
|
|
@@ -67,9 +55,11 @@ namespace sharp {
|
|
|
67
55
|
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
|
|
68
56
|
// Extract luminance
|
|
69
57
|
VImage luminance = lab[0];
|
|
58
|
+
|
|
70
59
|
// Find luminance range
|
|
71
|
-
int const min = luminance.percent(
|
|
72
|
-
int const max = luminance.percent(
|
|
60
|
+
int const min = lower == 0 ? luminance.min() : luminance.percent(lower);
|
|
61
|
+
int const max = upper == 100 ? luminance.max() : luminance.percent(upper);
|
|
62
|
+
|
|
73
63
|
if (std::abs(max - min) > 1) {
|
|
74
64
|
// Extract chroma
|
|
75
65
|
VImage chroma = lab.extract_band(1, VImage::option()->set("n", 2));
|
|
@@ -345,9 +335,9 @@ namespace sharp {
|
|
|
345
335
|
if (HasAlpha(image) && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
|
|
346
336
|
// Separate alpha channel
|
|
347
337
|
VImage alpha = image[bands - 1];
|
|
348
|
-
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
|
|
338
|
+
return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar", TRUE)).bandjoin(alpha);
|
|
349
339
|
} else {
|
|
350
|
-
return image.linear(a, b);
|
|
340
|
+
return image.linear(a, b, VImage::option()->set("uchar", TRUE));
|
|
351
341
|
}
|
|
352
342
|
}
|
|
353
343
|
|
|
@@ -395,11 +385,11 @@ namespace sharp {
|
|
|
395
385
|
* Split into frames, embed each frame, reassemble, and update pageHeight.
|
|
396
386
|
*/
|
|
397
387
|
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
|
|
398
|
-
std::vector<double> background, int nPages, int *pageHeight) {
|
|
388
|
+
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight) {
|
|
399
389
|
if (top == 0 && height == *pageHeight) {
|
|
400
390
|
// Fast path; no need to adjust the height of the multi-page image
|
|
401
391
|
return image.embed(left, 0, width, image.height(), VImage::option()
|
|
402
|
-
->set("extend",
|
|
392
|
+
->set("extend", extendWith)
|
|
403
393
|
->set("background", background));
|
|
404
394
|
} else if (left == 0 && width == image.width()) {
|
|
405
395
|
// Fast path; no need to adjust the width of the multi-page image
|
|
@@ -411,7 +401,7 @@ namespace sharp {
|
|
|
411
401
|
|
|
412
402
|
// Do the embed on the wide image
|
|
413
403
|
image = image.embed(0, top, image.width(), height, VImage::option()
|
|
414
|
-
->set("extend",
|
|
404
|
+
->set("extend", extendWith)
|
|
415
405
|
->set("background", background));
|
|
416
406
|
|
|
417
407
|
// Split the wide image into frames
|
|
@@ -441,7 +431,7 @@ namespace sharp {
|
|
|
441
431
|
// Embed each frame in the target size
|
|
442
432
|
for (int i = 0; i < nPages; i++) {
|
|
443
433
|
pages[i] = pages[i].embed(left, top, width, height, VImage::option()
|
|
444
|
-
->set("extend",
|
|
434
|
+
->set("extend", extendWith)
|
|
445
435
|
->set("background", background));
|
|
446
436
|
}
|
|
447
437
|
|
package/src/operations.h
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
// Copyright 2013
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
14
3
|
|
|
15
4
|
#ifndef SRC_OPERATIONS_H_
|
|
16
5
|
#define SRC_OPERATIONS_H_
|
|
@@ -33,7 +22,7 @@ namespace sharp {
|
|
|
33
22
|
/*
|
|
34
23
|
* Stretch luminance to cover full dynamic range.
|
|
35
24
|
*/
|
|
36
|
-
VImage Normalise(VImage image);
|
|
25
|
+
VImage Normalise(VImage image, int const lower, int const upper);
|
|
37
26
|
|
|
38
27
|
/*
|
|
39
28
|
* Contrast limiting adapative histogram equalization (CLAHE)
|
|
@@ -124,7 +113,7 @@ namespace sharp {
|
|
|
124
113
|
* Split into frames, embed each frame, reassemble, and update pageHeight.
|
|
125
114
|
*/
|
|
126
115
|
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
|
|
127
|
-
std::vector<double> background, int nPages, int *pageHeight);
|
|
116
|
+
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
|
|
128
117
|
|
|
129
118
|
} // namespace sharp
|
|
130
119
|
|
package/src/pipeline.cc
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
// Copyright 2013
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
1
|
+
// Copyright 2013 Lovell Fuller and others.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
14
3
|
|
|
15
4
|
#include <algorithm>
|
|
16
5
|
#include <cmath>
|
|
@@ -85,6 +74,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
85
74
|
VipsAngle autoRotation = VIPS_ANGLE_D0;
|
|
86
75
|
bool autoFlip = FALSE;
|
|
87
76
|
bool autoFlop = FALSE;
|
|
77
|
+
|
|
88
78
|
if (baton->useExifOrientation) {
|
|
89
79
|
// Rotate and flip image according to Exif orientation
|
|
90
80
|
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
|
@@ -205,7 +195,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
205
195
|
if (jpegShrinkOnLoad > 1 && static_cast<int>(shrink) == jpegShrinkOnLoad) {
|
|
206
196
|
jpegShrinkOnLoad /= 2;
|
|
207
197
|
}
|
|
208
|
-
} else if (inputImageType == sharp::ImageType::WEBP && shrink > 1.0) {
|
|
198
|
+
} else if (inputImageType == sharp::ImageType::WEBP && baton->fastShrinkOnLoad && shrink > 1.0) {
|
|
209
199
|
// Avoid upscaling via webp
|
|
210
200
|
scale = 1.0 / shrink;
|
|
211
201
|
} else if (inputImageType == sharp::ImageType::SVG ||
|
|
@@ -320,7 +310,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
320
310
|
if (
|
|
321
311
|
sharp::HasProfile(image) &&
|
|
322
312
|
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
|
323
|
-
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
|
313
|
+
image.interpretation() != VIPS_INTERPRETATION_GREY16 &&
|
|
314
|
+
!baton->input->ignoreIcc
|
|
324
315
|
) {
|
|
325
316
|
// Convert to sRGB/P3 using embedded profile
|
|
326
317
|
try {
|
|
@@ -329,7 +320,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
329
320
|
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
|
|
330
321
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
331
322
|
} catch(...) {
|
|
332
|
-
|
|
323
|
+
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
|
|
333
324
|
}
|
|
334
325
|
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
|
335
326
|
image = image.icc_transform(processingProfile, VImage::option()
|
|
@@ -367,11 +358,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
367
358
|
image = sharp::EnsureAlpha(image, 1);
|
|
368
359
|
}
|
|
369
360
|
|
|
361
|
+
VipsBandFormat premultiplyFormat = image.format();
|
|
370
362
|
bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
|
|
371
363
|
(shouldResize || shouldBlur || shouldConv || shouldSharpen);
|
|
372
364
|
|
|
373
365
|
if (shouldPremultiplyAlpha) {
|
|
374
|
-
image = image.premultiply();
|
|
366
|
+
image = image.premultiply().cast(premultiplyFormat);
|
|
375
367
|
}
|
|
376
368
|
|
|
377
369
|
// Resize
|
|
@@ -399,7 +391,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
399
391
|
}
|
|
400
392
|
|
|
401
393
|
// Join additional color channels to the image
|
|
402
|
-
if (baton->joinChannelIn.
|
|
394
|
+
if (!baton->joinChannelIn.empty()) {
|
|
403
395
|
VImage joinImage;
|
|
404
396
|
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
|
405
397
|
|
|
@@ -409,6 +401,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
409
401
|
image = image.bandjoin(joinImage);
|
|
410
402
|
}
|
|
411
403
|
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
|
404
|
+
image = sharp::RemoveGifPalette(image);
|
|
412
405
|
}
|
|
413
406
|
|
|
414
407
|
inputWidth = image.width();
|
|
@@ -438,7 +431,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
438
431
|
|
|
439
432
|
image = nPages > 1
|
|
440
433
|
? sharp::EmbedMultiPage(image,
|
|
441
|
-
left, top, width, height, background, nPages, &targetPageHeight)
|
|
434
|
+
left, top, width, height, VIPS_EXTEND_BACKGROUND, background, nPages, &targetPageHeight)
|
|
442
435
|
: image.embed(left, top, width, height, VImage::option()
|
|
443
436
|
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
444
437
|
->set("background", background));
|
|
@@ -455,6 +448,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
455
448
|
// Gravity-based crop
|
|
456
449
|
int left;
|
|
457
450
|
int top;
|
|
451
|
+
|
|
458
452
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
459
453
|
inputWidth, inputHeight, baton->width, baton->height, baton->position);
|
|
460
454
|
int width = std::min(inputWidth, baton->width);
|
|
@@ -465,16 +459,25 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
465
459
|
left, top, width, height, nPages, &targetPageHeight)
|
|
466
460
|
: image.extract_area(left, top, width, height);
|
|
467
461
|
} else {
|
|
462
|
+
int attention_x;
|
|
463
|
+
int attention_y;
|
|
464
|
+
|
|
468
465
|
// Attention-based or Entropy-based crop
|
|
469
466
|
MultiPageUnsupported(nPages, "Resize strategy");
|
|
470
467
|
image = image.tilecache(VImage::option()
|
|
471
468
|
->set("access", VIPS_ACCESS_RANDOM)
|
|
472
469
|
->set("threaded", TRUE));
|
|
470
|
+
|
|
473
471
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
474
|
-
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
472
|
+
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
473
|
+
->set("attention_x", &attention_x)
|
|
474
|
+
->set("attention_y", &attention_y));
|
|
475
475
|
baton->hasCropOffset = true;
|
|
476
476
|
baton->cropOffsetLeft = static_cast<int>(image.xoffset());
|
|
477
477
|
baton->cropOffsetTop = static_cast<int>(image.yoffset());
|
|
478
|
+
baton->hasAttentionCenter = true;
|
|
479
|
+
baton->attentionX = static_cast<int>(attention_x * jpegShrinkOnLoad / scale);
|
|
480
|
+
baton->attentionY = static_cast<int>(attention_y * jpegShrinkOnLoad / scale);
|
|
478
481
|
}
|
|
479
482
|
}
|
|
480
483
|
}
|
|
@@ -503,7 +506,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
503
506
|
}
|
|
504
507
|
|
|
505
508
|
// Affine transform
|
|
506
|
-
if (baton->affineMatrix.
|
|
509
|
+
if (!baton->affineMatrix.empty()) {
|
|
507
510
|
MultiPageUnsupported(nPages, "Affine");
|
|
508
511
|
std::vector<double> background;
|
|
509
512
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
|
@@ -519,18 +522,29 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
519
522
|
|
|
520
523
|
// Extend edges
|
|
521
524
|
if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
|
|
522
|
-
std::vector<double> background;
|
|
523
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
|
|
524
|
-
|
|
525
525
|
// Embed
|
|
526
526
|
baton->width = image.width() + baton->extendLeft + baton->extendRight;
|
|
527
527
|
baton->height = (nPages > 1 ? targetPageHeight : image.height()) + baton->extendTop + baton->extendBottom;
|
|
528
528
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
if (baton->extendWith == VIPS_EXTEND_BACKGROUND) {
|
|
530
|
+
std::vector<double> background;
|
|
531
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
|
|
532
|
+
|
|
533
|
+
image = nPages > 1
|
|
534
|
+
? sharp::EmbedMultiPage(image,
|
|
535
|
+
baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
536
|
+
baton->extendWith, background, nPages, &targetPageHeight)
|
|
537
|
+
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
538
|
+
VImage::option()->set("extend", baton->extendWith)->set("background", background));
|
|
539
|
+
} else {
|
|
540
|
+
std::vector<double> ignoredBackground(1);
|
|
541
|
+
image = nPages > 1
|
|
542
|
+
? sharp::EmbedMultiPage(image,
|
|
543
|
+
baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
544
|
+
baton->extendWith, ignoredBackground, nPages, &targetPageHeight)
|
|
545
|
+
: image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
|
|
546
|
+
VImage::option()->set("extend", baton->extendWith));
|
|
547
|
+
}
|
|
534
548
|
}
|
|
535
549
|
// Median - must happen before blurring, due to the utility of blurring after thresholding
|
|
536
550
|
if (baton->medianSize > 0) {
|
|
@@ -572,13 +586,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
572
586
|
|
|
573
587
|
// Reverse premultiplication after all transformations
|
|
574
588
|
if (shouldPremultiplyAlpha) {
|
|
575
|
-
image = image.unpremultiply();
|
|
576
|
-
// Cast pixel values to integer
|
|
577
|
-
if (sharp::Is16Bit(image.interpretation())) {
|
|
578
|
-
image = image.cast(VIPS_FORMAT_USHORT);
|
|
579
|
-
} else {
|
|
580
|
-
image = image.cast(VIPS_FORMAT_UCHAR);
|
|
581
|
-
}
|
|
589
|
+
image = image.unpremultiply().cast(premultiplyFormat);
|
|
582
590
|
}
|
|
583
591
|
baton->premultiplied = shouldPremultiplyAlpha;
|
|
584
592
|
|
|
@@ -660,6 +668,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
660
668
|
ys.push_back(top);
|
|
661
669
|
}
|
|
662
670
|
image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys));
|
|
671
|
+
image = sharp::RemoveGifPalette(image);
|
|
663
672
|
}
|
|
664
673
|
|
|
665
674
|
// Gamma decoding (brighten)
|
|
@@ -674,7 +683,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
674
683
|
|
|
675
684
|
// Apply normalisation - stretch luminance to cover full dynamic range
|
|
676
685
|
if (baton->normalise) {
|
|
677
|
-
image = sharp::Normalise(image);
|
|
686
|
+
image = sharp::Normalise(image, baton->normaliseLower, baton->normaliseUpper);
|
|
678
687
|
}
|
|
679
688
|
|
|
680
689
|
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
|
@@ -689,6 +698,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
689
698
|
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
|
690
699
|
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
|
691
700
|
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
|
701
|
+
image = sharp::RemoveGifPalette(image);
|
|
692
702
|
}
|
|
693
703
|
|
|
694
704
|
// Apply per-channel Bandbool bitwise operations after all other operations
|
|
@@ -701,24 +711,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
701
711
|
image = sharp::Tint(image, baton->tintA, baton->tintB);
|
|
702
712
|
}
|
|
703
713
|
|
|
704
|
-
// Extract an image channel (aka vips band)
|
|
705
|
-
if (baton->extractChannel > -1) {
|
|
706
|
-
if (baton->extractChannel >= image.bands()) {
|
|
707
|
-
if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
|
|
708
|
-
baton->extractChannel = image.bands() - 1;
|
|
709
|
-
} else {
|
|
710
|
-
(baton->err).append("Cannot extract channel from image. Too few channels in image.");
|
|
711
|
-
return Error();
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
|
|
715
|
-
? VIPS_INTERPRETATION_GREY16
|
|
716
|
-
: VIPS_INTERPRETATION_B_W;
|
|
717
|
-
image = image
|
|
718
|
-
.extract_band(baton->extractChannel)
|
|
719
|
-
.copy(VImage::option()->set("interpretation", interpretation));
|
|
720
|
-
}
|
|
721
|
-
|
|
722
714
|
// Remove alpha channel, if any
|
|
723
715
|
if (baton->removeAlpha) {
|
|
724
716
|
image = sharp::RemoveAlpha(image);
|
|
@@ -744,6 +736,26 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
744
736
|
}
|
|
745
737
|
}
|
|
746
738
|
|
|
739
|
+
// Extract channel
|
|
740
|
+
if (baton->extractChannel > -1) {
|
|
741
|
+
if (baton->extractChannel >= image.bands()) {
|
|
742
|
+
if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
|
|
743
|
+
baton->extractChannel = image.bands() - 1;
|
|
744
|
+
} else {
|
|
745
|
+
(baton->err)
|
|
746
|
+
.append("Cannot extract channel ").append(std::to_string(baton->extractChannel))
|
|
747
|
+
.append(" from image with channels 0-").append(std::to_string(image.bands() - 1));
|
|
748
|
+
return Error();
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
VipsInterpretation colourspace = sharp::Is16Bit(image.interpretation())
|
|
752
|
+
? VIPS_INTERPRETATION_GREY16
|
|
753
|
+
: VIPS_INTERPRETATION_B_W;
|
|
754
|
+
image = image
|
|
755
|
+
.extract_band(baton->extractChannel)
|
|
756
|
+
.copy(VImage::option()->set("interpretation", colourspace));
|
|
757
|
+
}
|
|
758
|
+
|
|
747
759
|
// Apply output ICC profile
|
|
748
760
|
if (!baton->withMetadataIcc.empty()) {
|
|
749
761
|
image = image.icc_transform(
|
|
@@ -868,7 +880,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
868
880
|
->set("strip", !baton->withMetadata)
|
|
869
881
|
->set("bitdepth", baton->gifBitdepth)
|
|
870
882
|
->set("effort", baton->gifEffort)
|
|
871
|
-
->set("
|
|
883
|
+
->set("reuse", baton->gifReuse)
|
|
884
|
+
->set("interlace", baton->gifProgressive)
|
|
885
|
+
->set("interframe_maxerror", baton->gifInterFrameMaxError)
|
|
886
|
+
->set("interpalette_maxerror", baton->gifInterPaletteMaxError)
|
|
872
887
|
->set("dither", baton->gifDither)));
|
|
873
888
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
874
889
|
baton->bufferOutLength = area->length;
|
|
@@ -935,6 +950,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
935
950
|
area->free_fn = nullptr;
|
|
936
951
|
vips_area_unref(area);
|
|
937
952
|
baton->formatOut = "dz";
|
|
953
|
+
} else if (baton->formatOut == "jxl" ||
|
|
954
|
+
(baton->formatOut == "input" && inputImageType == sharp::ImageType::JXL)) {
|
|
955
|
+
// Write JXL to buffer
|
|
956
|
+
image = sharp::RemoveAnimationProperties(image);
|
|
957
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.jxlsave_buffer(VImage::option()
|
|
958
|
+
->set("strip", !baton->withMetadata)
|
|
959
|
+
->set("distance", baton->jxlDistance)
|
|
960
|
+
->set("tier", baton->jxlDecodingTier)
|
|
961
|
+
->set("effort", baton->jxlEffort)
|
|
962
|
+
->set("lossless", baton->jxlLossless)));
|
|
963
|
+
baton->bufferOut = static_cast<char*>(area->data);
|
|
964
|
+
baton->bufferOutLength = area->length;
|
|
965
|
+
area->free_fn = nullptr;
|
|
966
|
+
vips_area_unref(area);
|
|
967
|
+
baton->formatOut = "jxl";
|
|
938
968
|
} else if (baton->formatOut == "raw" ||
|
|
939
969
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::RAW)) {
|
|
940
970
|
// Write raw, uncompressed image data to buffer
|
|
@@ -973,6 +1003,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
973
1003
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
|
974
1004
|
bool const isJp2 = sharp::IsJp2(baton->fileOut);
|
|
975
1005
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
|
1006
|
+
bool const isJxl = sharp::IsJxl(baton->fileOut);
|
|
976
1007
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
|
977
1008
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
|
978
1009
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
@@ -1048,7 +1079,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1048
1079
|
->set("strip", !baton->withMetadata)
|
|
1049
1080
|
->set("bitdepth", baton->gifBitdepth)
|
|
1050
1081
|
->set("effort", baton->gifEffort)
|
|
1051
|
-
->set("
|
|
1082
|
+
->set("reuse", baton->gifReuse)
|
|
1083
|
+
->set("interlace", baton->gifProgressive)
|
|
1052
1084
|
->set("dither", baton->gifDither));
|
|
1053
1085
|
baton->formatOut = "gif";
|
|
1054
1086
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
@@ -1090,6 +1122,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1090
1122
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
1091
1123
|
->set("lossless", baton->heifLossless));
|
|
1092
1124
|
baton->formatOut = "heif";
|
|
1125
|
+
} else if (baton->formatOut == "jxl" || (mightMatchInput && isJxl) ||
|
|
1126
|
+
(willMatchInput && inputImageType == sharp::ImageType::JXL)) {
|
|
1127
|
+
// Write JXL to file
|
|
1128
|
+
image = sharp::RemoveAnimationProperties(image);
|
|
1129
|
+
image.jxlsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1130
|
+
->set("strip", !baton->withMetadata)
|
|
1131
|
+
->set("distance", baton->jxlDistance)
|
|
1132
|
+
->set("tier", baton->jxlDecodingTier)
|
|
1133
|
+
->set("effort", baton->jxlEffort)
|
|
1134
|
+
->set("lossless", baton->jxlLossless));
|
|
1135
|
+
baton->formatOut = "jxl";
|
|
1093
1136
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
1094
1137
|
// Write DZ to file
|
|
1095
1138
|
if (isDzZip) {
|
|
@@ -1133,7 +1176,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1133
1176
|
// Handle warnings
|
|
1134
1177
|
std::string warning = sharp::VipsWarningPop();
|
|
1135
1178
|
while (!warning.empty()) {
|
|
1136
|
-
debuglog.
|
|
1179
|
+
debuglog.MakeCallback(Receiver().Value(), { Napi::String::New(env, warning) });
|
|
1137
1180
|
warning = sharp::VipsWarningPop();
|
|
1138
1181
|
}
|
|
1139
1182
|
|
|
@@ -1162,6 +1205,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1162
1205
|
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
|
1163
1206
|
info.Set("cropOffsetTop", static_cast<int32_t>(baton->cropOffsetTop));
|
|
1164
1207
|
}
|
|
1208
|
+
if (baton->hasAttentionCenter) {
|
|
1209
|
+
info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
|
|
1210
|
+
info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
|
|
1211
|
+
}
|
|
1165
1212
|
if (baton->trimThreshold > 0.0) {
|
|
1166
1213
|
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
|
|
1167
1214
|
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
|
|
@@ -1175,8 +1222,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1175
1222
|
// Add buffer size to info
|
|
1176
1223
|
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
|
1177
1224
|
// Pass ownership of output data to Buffer instance
|
|
1178
|
-
Napi::Buffer<char> data =
|
|
1179
|
-
baton->bufferOutLength
|
|
1225
|
+
Napi::Buffer<char> data = sharp::NewOrCopyBuffer(env, static_cast<char*>(baton->bufferOut),
|
|
1226
|
+
baton->bufferOutLength);
|
|
1180
1227
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
|
|
1181
1228
|
} else {
|
|
1182
1229
|
// Add file size to info
|
|
@@ -1187,7 +1234,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1187
1234
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
|
1188
1235
|
}
|
|
1189
1236
|
} else {
|
|
1190
|
-
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
|
|
1237
|
+
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
|
|
1191
1238
|
}
|
|
1192
1239
|
|
|
1193
1240
|
// Delete baton
|
|
@@ -1205,7 +1252,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1205
1252
|
// Decrement processing task counter
|
|
1206
1253
|
g_atomic_int_dec_and_test(&sharp::counterProcess);
|
|
1207
1254
|
Napi::Number queueLength = Napi::Number::New(env, static_cast<double>(sharp::counterQueue));
|
|
1208
|
-
queueListener.
|
|
1255
|
+
queueListener.MakeCallback(Receiver().Value(), { queueLength });
|
|
1209
1256
|
}
|
|
1210
1257
|
|
|
1211
1258
|
private:
|
|
@@ -1351,7 +1398,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1351
1398
|
Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
1352
1399
|
// V8 objects are converted to non-V8 types held in the baton struct
|
|
1353
1400
|
PipelineBaton *baton = new PipelineBaton;
|
|
1354
|
-
Napi::Object options = info[0].As<Napi::Object>();
|
|
1401
|
+
Napi::Object options = info[size_t(0)].As<Napi::Object>();
|
|
1355
1402
|
|
|
1356
1403
|
// Input
|
|
1357
1404
|
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
|
|
@@ -1437,6 +1484,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1437
1484
|
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
|
|
1438
1485
|
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
|
1439
1486
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
|
1487
|
+
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
|
|
1488
|
+
baton->normaliseUpper = sharp::AttrAsUint32(options, "normaliseUpper");
|
|
1440
1489
|
baton->tintA = sharp::AttrAsDouble(options, "tintA");
|
|
1441
1490
|
baton->tintB = sharp::AttrAsDouble(options, "tintB");
|
|
1442
1491
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
|
@@ -1454,6 +1503,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1454
1503
|
baton->extendLeft = sharp::AttrAsInt32(options, "extendLeft");
|
|
1455
1504
|
baton->extendRight = sharp::AttrAsInt32(options, "extendRight");
|
|
1456
1505
|
baton->extendBackground = sharp::AttrAsVectorOfDouble(options, "extendBackground");
|
|
1506
|
+
baton->extendWith = sharp::AttrAsEnum<VipsExtend>(options, "extendWith", VIPS_TYPE_EXTEND);
|
|
1457
1507
|
baton->extractChannel = sharp::AttrAsInt32(options, "extractChannel");
|
|
1458
1508
|
baton->affineMatrix = sharp::AttrAsVectorOfDouble(options, "affineMatrix");
|
|
1459
1509
|
baton->affineBackground = sharp::AttrAsVectorOfDouble(options, "affineBackground");
|
|
@@ -1549,7 +1599,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1549
1599
|
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
|
1550
1600
|
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
|
1551
1601
|
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
|
1552
|
-
baton->
|
|
1602
|
+
baton->gifInterFrameMaxError = sharp::AttrAsDouble(options, "gifInterFrameMaxError");
|
|
1603
|
+
baton->gifInterPaletteMaxError = sharp::AttrAsDouble(options, "gifInterPaletteMaxError");
|
|
1604
|
+
baton->gifReuse = sharp::AttrAsBool(options, "gifReuse");
|
|
1605
|
+
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
|
1553
1606
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1554
1607
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1555
1608
|
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
@@ -1573,6 +1626,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1573
1626
|
options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
|
|
1574
1627
|
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
|
1575
1628
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1629
|
+
baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance");
|
|
1630
|
+
baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier");
|
|
1631
|
+
baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
|
|
1632
|
+
baton->jxlLossless = sharp::AttrAsBool(options, "jxlLossless");
|
|
1576
1633
|
baton->rawDepth = sharp::AttrAsEnum<VipsBandFormat>(options, "rawDepth", VIPS_TYPE_BAND_FORMAT);
|
|
1577
1634
|
// Animated output properties
|
|
1578
1635
|
if (sharp::HasAttr(options, "loop")) {
|
|
@@ -1601,9 +1658,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1601
1658
|
baton->trimThreshold > 0.0 ||
|
|
1602
1659
|
baton->normalise ||
|
|
1603
1660
|
baton->position == 16 || baton->position == 17 ||
|
|
1604
|
-
baton->angle
|
|
1605
|
-
|
|
1606
|
-
baton->
|
|
1661
|
+
baton->angle != 0 ||
|
|
1662
|
+
baton->rotationAngle != 0.0 ||
|
|
1663
|
+
baton->tileAngle != 0 ||
|
|
1664
|
+
baton->useExifOrientation ||
|
|
1665
|
+
baton->claheWidth != 0 ||
|
|
1666
|
+
!baton->affineMatrix.empty()
|
|
1607
1667
|
) {
|
|
1608
1668
|
baton->input->access = VIPS_ACCESS_RANDOM;
|
|
1609
1669
|
}
|
|
@@ -1616,7 +1676,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1616
1676
|
Napi::Function queueListener = options.Get("queueListener").As<Napi::Function>();
|
|
1617
1677
|
|
|
1618
1678
|
// Join queue for worker thread
|
|
1619
|
-
Napi::Function callback = info[1].As<Napi::Function>();
|
|
1679
|
+
Napi::Function callback = info[size_t(1)].As<Napi::Function>();
|
|
1620
1680
|
PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
|
|
1621
1681
|
worker->Receiver().Set("options", options);
|
|
1622
1682
|
worker->Queue();
|
|
@@ -1624,7 +1684,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1624
1684
|
// Increment queued task counter
|
|
1625
1685
|
g_atomic_int_inc(&sharp::counterQueue);
|
|
1626
1686
|
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<double>(sharp::counterQueue));
|
|
1627
|
-
queueListener.
|
|
1687
|
+
queueListener.MakeCallback(info.This(), { queueLength });
|
|
1628
1688
|
|
|
1629
1689
|
return info.Env().Undefined();
|
|
1630
1690
|
}
|