sharp 0.33.5 → 0.34.0-rc.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/lib/channel.js +2 -1
- package/lib/colour.js +3 -2
- package/lib/composite.js +4 -2
- package/lib/constructor.js +29 -2
- package/lib/index.d.ts +150 -55
- package/lib/input.js +92 -16
- package/lib/operation.js +44 -25
- package/lib/output.js +11 -9
- package/lib/resize.js +5 -2
- package/lib/utility.js +1 -0
- package/package.json +39 -39
- package/src/binding.gyp +15 -8
- package/src/common.cc +42 -15
- package/src/common.h +26 -19
- package/src/metadata.cc +26 -8
- package/src/metadata.h +4 -2
- package/src/operations.cc +9 -9
- package/src/pipeline.cc +93 -36
- package/src/pipeline.h +3 -2
- package/src/stats.cc +1 -1
- package/src/utilities.cc +3 -3
package/src/pipeline.cc
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#include <algorithm>
|
|
5
5
|
#include <cmath>
|
|
6
|
+
#include <filesystem>
|
|
6
7
|
#include <map>
|
|
7
8
|
#include <memory>
|
|
8
9
|
#include <numeric>
|
|
@@ -20,17 +21,6 @@
|
|
|
20
21
|
#include "operations.h"
|
|
21
22
|
#include "pipeline.h"
|
|
22
23
|
|
|
23
|
-
#ifdef _WIN32
|
|
24
|
-
#define STAT64_STRUCT __stat64
|
|
25
|
-
#define STAT64_FUNCTION _stat64
|
|
26
|
-
#elif defined(_LARGEFILE64_SOURCE)
|
|
27
|
-
#define STAT64_STRUCT stat64
|
|
28
|
-
#define STAT64_FUNCTION stat64
|
|
29
|
-
#else
|
|
30
|
-
#define STAT64_STRUCT stat
|
|
31
|
-
#define STAT64_FUNCTION stat
|
|
32
|
-
#endif
|
|
33
|
-
|
|
34
24
|
class PipelineWorker : public Napi::AsyncWorker {
|
|
35
25
|
public:
|
|
36
26
|
PipelineWorker(Napi::Function callback, PipelineBaton *baton,
|
|
@@ -52,7 +42,39 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
52
42
|
// Open input
|
|
53
43
|
vips::VImage image;
|
|
54
44
|
sharp::ImageType inputImageType;
|
|
55
|
-
|
|
45
|
+
if (baton->join.empty()) {
|
|
46
|
+
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
|
47
|
+
} else {
|
|
48
|
+
std::vector<VImage> images;
|
|
49
|
+
bool hasAlpha = false;
|
|
50
|
+
for (auto &join : baton->join) {
|
|
51
|
+
std::tie(image, inputImageType) = sharp::OpenInput(join);
|
|
52
|
+
image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
|
|
53
|
+
images.push_back(image);
|
|
54
|
+
hasAlpha |= image.has_alpha();
|
|
55
|
+
}
|
|
56
|
+
if (hasAlpha) {
|
|
57
|
+
for (auto &image : images) {
|
|
58
|
+
if (!image.has_alpha()) {
|
|
59
|
+
image = sharp::EnsureAlpha(image, 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
baton->input->joinBackground.pop_back();
|
|
64
|
+
}
|
|
65
|
+
inputImageType = sharp::ImageType::PNG;
|
|
66
|
+
image = VImage::arrayjoin(images, VImage::option()
|
|
67
|
+
->set("across", baton->input->joinAcross)
|
|
68
|
+
->set("shim", baton->input->joinShim)
|
|
69
|
+
->set("background", baton->input->joinBackground)
|
|
70
|
+
->set("halign", baton->input->joinHalign)
|
|
71
|
+
->set("valign", baton->input->joinValign));
|
|
72
|
+
if (baton->input->joinAnimated) {
|
|
73
|
+
image = image.copy();
|
|
74
|
+
image.set(VIPS_META_N_PAGES, static_cast<int>(images.size()));
|
|
75
|
+
image.set(VIPS_META_PAGE_HEIGHT, static_cast<int>(image.height() / images.size()));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
56
78
|
VipsAccess access = baton->input->access;
|
|
57
79
|
image = sharp::EnsureColourspace(image, baton->colourspacePipeline);
|
|
58
80
|
|
|
@@ -73,14 +95,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
73
95
|
bool autoFlip = false;
|
|
74
96
|
bool autoFlop = false;
|
|
75
97
|
|
|
76
|
-
if (baton->
|
|
98
|
+
if (baton->input->autoOrient) {
|
|
77
99
|
// Rotate and flip image according to Exif orientation
|
|
78
100
|
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
|
79
101
|
image = sharp::RemoveExifOrientation(image);
|
|
80
|
-
} else {
|
|
81
|
-
rotation = CalculateAngleRotation(baton->angle);
|
|
82
102
|
}
|
|
83
103
|
|
|
104
|
+
rotation = CalculateAngleRotation(baton->angle);
|
|
105
|
+
|
|
84
106
|
// Rotate pre-extract
|
|
85
107
|
bool const shouldRotateBefore = baton->rotateBeforePreExtract &&
|
|
86
108
|
(rotation != VIPS_ANGLE_D0 || autoRotation != VIPS_ANGLE_D0 ||
|
|
@@ -102,18 +124,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
102
124
|
image = image.rot(autoRotation);
|
|
103
125
|
autoRotation = VIPS_ANGLE_D0;
|
|
104
126
|
}
|
|
105
|
-
if (autoFlip) {
|
|
127
|
+
if (autoFlip != baton->flip) {
|
|
106
128
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
107
129
|
autoFlip = false;
|
|
108
|
-
} else if (baton->flip) {
|
|
109
|
-
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
110
130
|
baton->flip = false;
|
|
111
131
|
}
|
|
112
|
-
if (autoFlop) {
|
|
132
|
+
if (autoFlop != baton->flop) {
|
|
113
133
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
114
134
|
autoFlop = false;
|
|
115
|
-
} else if (baton->flop) {
|
|
116
|
-
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
117
135
|
baton->flop = false;
|
|
118
136
|
}
|
|
119
137
|
if (rotation != VIPS_ANGLE_D0) {
|
|
@@ -354,7 +372,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
354
372
|
}
|
|
355
373
|
|
|
356
374
|
// Flatten image to remove alpha channel
|
|
357
|
-
if (baton->flatten &&
|
|
375
|
+
if (baton->flatten && image.has_alpha()) {
|
|
358
376
|
image = sharp::Flatten(image, baton->flattenBackground);
|
|
359
377
|
}
|
|
360
378
|
|
|
@@ -374,12 +392,12 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
374
392
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
|
375
393
|
bool const shouldComposite = !baton->composite.empty();
|
|
376
394
|
|
|
377
|
-
if (shouldComposite && !
|
|
395
|
+
if (shouldComposite && !image.has_alpha()) {
|
|
378
396
|
image = sharp::EnsureAlpha(image, 1);
|
|
379
397
|
}
|
|
380
398
|
|
|
381
399
|
VipsBandFormat premultiplyFormat = image.format();
|
|
382
|
-
bool const shouldPremultiplyAlpha =
|
|
400
|
+
bool const shouldPremultiplyAlpha = image.has_alpha() &&
|
|
383
401
|
(shouldResize || shouldBlur || shouldConv || shouldSharpen);
|
|
384
402
|
|
|
385
403
|
if (shouldPremultiplyAlpha) {
|
|
@@ -406,11 +424,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
406
424
|
image = image.rot(autoRotation);
|
|
407
425
|
}
|
|
408
426
|
// Mirror vertically (up-down) about the x-axis
|
|
409
|
-
if (baton->flip
|
|
427
|
+
if (baton->flip != autoFlip) {
|
|
410
428
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
411
429
|
}
|
|
412
430
|
// Mirror horizontally (left-right) about the y-axis
|
|
413
|
-
if (baton->flop
|
|
431
|
+
if (baton->flop != autoFlop) {
|
|
414
432
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
415
433
|
}
|
|
416
434
|
// Rotate post-extract 90-angle
|
|
@@ -641,6 +659,30 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
641
659
|
composite->input->access = access;
|
|
642
660
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
|
643
661
|
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline);
|
|
662
|
+
|
|
663
|
+
if (composite->input->autoOrient) {
|
|
664
|
+
// Respect EXIF Orientation
|
|
665
|
+
VipsAngle compositeAutoRotation = VIPS_ANGLE_D0;
|
|
666
|
+
bool compositeAutoFlip = false;
|
|
667
|
+
bool compositeAutoFlop = false;
|
|
668
|
+
std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) =
|
|
669
|
+
CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage));
|
|
670
|
+
|
|
671
|
+
compositeImage = sharp::RemoveExifOrientation(compositeImage);
|
|
672
|
+
compositeImage = sharp::StaySequential(compositeImage,
|
|
673
|
+
compositeAutoRotation != VIPS_ANGLE_D0 || compositeAutoFlip);
|
|
674
|
+
|
|
675
|
+
if (compositeAutoRotation != VIPS_ANGLE_D0) {
|
|
676
|
+
compositeImage = compositeImage.rot(compositeAutoRotation);
|
|
677
|
+
}
|
|
678
|
+
if (compositeAutoFlip) {
|
|
679
|
+
compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL);
|
|
680
|
+
}
|
|
681
|
+
if (compositeAutoFlop) {
|
|
682
|
+
compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
644
686
|
// Verify within current dimensions
|
|
645
687
|
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
|
646
688
|
throw vips::VError("Image to composite must have same dimensions or smaller");
|
|
@@ -683,9 +725,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
683
725
|
}
|
|
684
726
|
// Ensure image to composite is sRGB with unpremultiplied alpha
|
|
685
727
|
compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
686
|
-
|
|
687
|
-
compositeImage = sharp::EnsureAlpha(compositeImage, 1);
|
|
688
|
-
}
|
|
728
|
+
compositeImage = sharp::EnsureAlpha(compositeImage, 1);
|
|
689
729
|
if (composite->premultiplied) compositeImage = compositeImage.unpremultiply();
|
|
690
730
|
// Calculate position
|
|
691
731
|
int left;
|
|
@@ -786,7 +826,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
786
826
|
// Extract channel
|
|
787
827
|
if (baton->extractChannel > -1) {
|
|
788
828
|
if (baton->extractChannel >= image.bands()) {
|
|
789
|
-
if (baton->extractChannel == 3 &&
|
|
829
|
+
if (baton->extractChannel == 3 && image.has_alpha()) {
|
|
790
830
|
baton->extractChannel = image.bands() - 1;
|
|
791
831
|
} else {
|
|
792
832
|
(baton->err)
|
|
@@ -929,6 +969,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
929
969
|
->set("lossless", baton->webpLossless)
|
|
930
970
|
->set("near_lossless", baton->webpNearLossless)
|
|
931
971
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
972
|
+
->set("smart_deblock", baton->webpSmartDeblock)
|
|
932
973
|
->set("preset", baton->webpPreset)
|
|
933
974
|
->set("effort", baton->webpEffort)
|
|
934
975
|
->set("min_size", baton->webpMinSize)
|
|
@@ -1009,7 +1050,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1009
1050
|
} else if (baton->formatOut == "dz") {
|
|
1010
1051
|
// Write DZ to buffer
|
|
1011
1052
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
|
1012
|
-
if (!
|
|
1053
|
+
if (!image.has_alpha()) {
|
|
1013
1054
|
baton->tileBackground.pop_back();
|
|
1014
1055
|
}
|
|
1015
1056
|
image = sharp::StaySequential(image, baton->tileAngle != 0);
|
|
@@ -1058,6 +1099,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1058
1099
|
// Unsupported output format
|
|
1059
1100
|
(baton->err).append("Unsupported output format ");
|
|
1060
1101
|
if (baton->formatOut == "input") {
|
|
1102
|
+
(baton->err).append("when trying to match input format of ");
|
|
1061
1103
|
(baton->err).append(ImageTypeId(inputImageType));
|
|
1062
1104
|
} else {
|
|
1063
1105
|
(baton->err).append(baton->formatOut);
|
|
@@ -1136,6 +1178,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1136
1178
|
->set("lossless", baton->webpLossless)
|
|
1137
1179
|
->set("near_lossless", baton->webpNearLossless)
|
|
1138
1180
|
->set("smart_subsample", baton->webpSmartSubsample)
|
|
1181
|
+
->set("smart_deblock", baton->webpSmartDeblock)
|
|
1139
1182
|
->set("preset", baton->webpPreset)
|
|
1140
1183
|
->set("effort", baton->webpEffort)
|
|
1141
1184
|
->set("min_size", baton->webpMinSize)
|
|
@@ -1211,7 +1254,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1211
1254
|
if (isDzZip) {
|
|
1212
1255
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
|
1213
1256
|
}
|
|
1214
|
-
if (!
|
|
1257
|
+
if (!image.has_alpha()) {
|
|
1215
1258
|
baton->tileBackground.pop_back();
|
|
1216
1259
|
}
|
|
1217
1260
|
image = sharp::StaySequential(image, baton->tileAngle != 0);
|
|
@@ -1304,9 +1347,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1304
1347
|
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
|
1305
1348
|
} else {
|
|
1306
1349
|
// Add file size to info
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1350
|
+
if (baton->formatOut != "dz" || sharp::IsDzZip(baton->fileOut)) {
|
|
1351
|
+
try {
|
|
1352
|
+
uint32_t const size = static_cast<uint32_t>(std::filesystem::file_size(baton->fileOut));
|
|
1353
|
+
info.Set("size", size);
|
|
1354
|
+
} catch (...) {}
|
|
1310
1355
|
}
|
|
1311
1356
|
Callback().Call(Receiver().Value(), { env.Null(), info });
|
|
1312
1357
|
}
|
|
@@ -1324,6 +1369,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1324
1369
|
for (sharp::InputDescriptor *input : baton->joinChannelIn) {
|
|
1325
1370
|
delete input;
|
|
1326
1371
|
}
|
|
1372
|
+
for (sharp::InputDescriptor *input : baton->join) {
|
|
1373
|
+
delete input;
|
|
1374
|
+
}
|
|
1327
1375
|
delete baton;
|
|
1328
1376
|
|
|
1329
1377
|
// Decrement processing task counter
|
|
@@ -1419,6 +1467,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1419
1467
|
{"lossless", baton->webpLossless ? "true" : "false"},
|
|
1420
1468
|
{"near_lossless", baton->webpNearLossless ? "true" : "false"},
|
|
1421
1469
|
{"smart_subsample", baton->webpSmartSubsample ? "true" : "false"},
|
|
1470
|
+
{"smart_deblock", baton->webpSmartDeblock ? "true" : "false"},
|
|
1422
1471
|
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
|
|
1423
1472
|
{"min_size", baton->webpMinSize ? "true" : "false"},
|
|
1424
1473
|
{"mixed", baton->webpMixed ? "true" : "false"},
|
|
@@ -1480,6 +1529,14 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1480
1529
|
|
|
1481
1530
|
// Input
|
|
1482
1531
|
baton->input = sharp::CreateInputDescriptor(options.Get("input").As<Napi::Object>());
|
|
1532
|
+
// Join images together
|
|
1533
|
+
if (sharp::HasAttr(options, "join")) {
|
|
1534
|
+
Napi::Array join = options.Get("join").As<Napi::Array>();
|
|
1535
|
+
for (unsigned int i = 0; i < join.Length(); i++) {
|
|
1536
|
+
baton->join.push_back(
|
|
1537
|
+
sharp::CreateInputDescriptor(join.Get(i).As<Napi::Object>()));
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1483
1540
|
// Extract image options
|
|
1484
1541
|
baton->topOffsetPre = sharp::AttrAsInt32(options, "topOffsetPre");
|
|
1485
1542
|
baton->leftOffsetPre = sharp::AttrAsInt32(options, "leftOffsetPre");
|
|
@@ -1572,7 +1629,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1572
1629
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
|
1573
1630
|
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
|
1574
1631
|
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
|
1575
|
-
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
|
1576
1632
|
baton->angle = sharp::AttrAsInt32(options, "angle");
|
|
1577
1633
|
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
|
1578
1634
|
baton->rotationBackground = sharp::AttrAsVectorOfDouble(options, "rotationBackground");
|
|
@@ -1676,6 +1732,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1676
1732
|
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
|
1677
1733
|
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
|
1678
1734
|
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
|
1735
|
+
baton->webpSmartDeblock = sharp::AttrAsBool(options, "webpSmartDeblock");
|
|
1679
1736
|
baton->webpPreset = sharp::AttrAsEnum<VipsForeignWebpPreset>(options, "webpPreset", VIPS_TYPE_FOREIGN_WEBP_PRESET);
|
|
1680
1737
|
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
|
1681
1738
|
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
package/src/pipeline.h
CHANGED
|
@@ -39,6 +39,7 @@ struct Composite {
|
|
|
39
39
|
|
|
40
40
|
struct PipelineBaton {
|
|
41
41
|
sharp::InputDescriptor *input;
|
|
42
|
+
std::vector<sharp::InputDescriptor *> join;
|
|
42
43
|
std::string formatOut;
|
|
43
44
|
std::string fileOut;
|
|
44
45
|
void *bufferOut;
|
|
@@ -109,7 +110,6 @@ struct PipelineBaton {
|
|
|
109
110
|
int claheWidth;
|
|
110
111
|
int claheHeight;
|
|
111
112
|
int claheMaxSlope;
|
|
112
|
-
bool useExifOrientation;
|
|
113
113
|
int angle;
|
|
114
114
|
double rotationAngle;
|
|
115
115
|
std::vector<double> rotationBackground;
|
|
@@ -157,6 +157,7 @@ struct PipelineBaton {
|
|
|
157
157
|
bool webpNearLossless;
|
|
158
158
|
bool webpLossless;
|
|
159
159
|
bool webpSmartSubsample;
|
|
160
|
+
bool webpSmartDeblock;
|
|
160
161
|
VipsForeignWebpPreset webpPreset;
|
|
161
162
|
int webpEffort;
|
|
162
163
|
bool webpMinSize;
|
|
@@ -281,7 +282,6 @@ struct PipelineBaton {
|
|
|
281
282
|
claheWidth(0),
|
|
282
283
|
claheHeight(0),
|
|
283
284
|
claheMaxSlope(3),
|
|
284
|
-
useExifOrientation(false),
|
|
285
285
|
angle(0),
|
|
286
286
|
rotationAngle(0.0),
|
|
287
287
|
rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
|
|
@@ -328,6 +328,7 @@ struct PipelineBaton {
|
|
|
328
328
|
webpNearLossless(false),
|
|
329
329
|
webpLossless(false),
|
|
330
330
|
webpSmartSubsample(false),
|
|
331
|
+
webpSmartDeblock(false),
|
|
331
332
|
webpPreset(VIPS_FOREIGN_WEBP_PRESET_DEFAULT),
|
|
332
333
|
webpEffort(4),
|
|
333
334
|
webpMinSize(false),
|
package/src/stats.cc
CHANGED
|
@@ -58,7 +58,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
58
58
|
baton->channelStats.push_back(cStats);
|
|
59
59
|
}
|
|
60
60
|
// Image is not opaque when alpha layer is present and contains a non-mamixa value
|
|
61
|
-
if (
|
|
61
|
+
if (image.has_alpha()) {
|
|
62
62
|
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
|
|
63
63
|
if (minAlpha != sharp::MaximumImageAlpha(image.interpretation())) {
|
|
64
64
|
baton->isOpaque = false;
|
package/src/utilities.cc
CHANGED
|
@@ -119,7 +119,7 @@ Napi::Value format(const Napi::CallbackInfo& info) {
|
|
|
119
119
|
Napi::Object format = Napi::Object::New(env);
|
|
120
120
|
for (std::string const f : {
|
|
121
121
|
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz",
|
|
122
|
-
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl"
|
|
122
|
+
"ppm", "fits", "gif", "svg", "heif", "pdf", "vips", "jp2k", "jxl", "rad"
|
|
123
123
|
}) {
|
|
124
124
|
// Input
|
|
125
125
|
const VipsObjectClass *oc = vips_class_find("VipsOperation", (f + "load").c_str());
|
|
@@ -233,10 +233,10 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
|
|
|
233
233
|
double maxColourDistance;
|
|
234
234
|
try {
|
|
235
235
|
// Premultiply and remove alpha
|
|
236
|
-
if (
|
|
236
|
+
if (image1.has_alpha()) {
|
|
237
237
|
image1 = image1.premultiply().extract_band(1, VImage::option()->set("n", image1.bands() - 1));
|
|
238
238
|
}
|
|
239
|
-
if (
|
|
239
|
+
if (image2.has_alpha()) {
|
|
240
240
|
image2 = image2.premultiply().extract_band(1, VImage::option()->set("n", image2.bands() - 1));
|
|
241
241
|
}
|
|
242
242
|
// Calculate colour distance
|