sharp 0.28.2 → 0.29.2
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 +2 -2
- package/binding.gyp +12 -9
- package/install/can-compile.js +11 -0
- package/install/dll-copy.js +6 -6
- package/install/libvips.js +4 -9
- package/lib/channel.js +13 -7
- package/lib/colour.js +42 -1
- package/lib/composite.js +2 -0
- package/lib/constructor.js +21 -31
- package/lib/input.js +45 -3
- package/lib/is.js +19 -5
- package/lib/libvips.js +4 -19
- package/lib/operation.js +74 -7
- package/lib/output.js +145 -16
- package/lib/sharp.js +31 -0
- package/lib/utility.js +3 -2
- package/package.json +19 -15
- package/src/common.cc +67 -11
- package/src/common.h +25 -5
- package/src/libvips/cplusplus/VConnection.cpp +0 -26
- package/src/libvips/cplusplus/VImage.cpp +54 -16
- package/src/libvips/cplusplus/VInterpolate.cpp +0 -13
- package/src/libvips/cplusplus/vips-operators.cpp +185 -1
- package/src/metadata.cc +14 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +36 -3
- package/src/operations.h +18 -2
- package/src/pipeline.cc +104 -34
- package/src/pipeline.h +29 -3
- package/src/utilities.cc +1 -1
package/src/metadata.cc
CHANGED
|
@@ -90,6 +90,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
90
90
|
baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
|
|
91
91
|
}
|
|
92
92
|
baton->hasProfile = sharp::HasProfile(image);
|
|
93
|
+
if (image.get_typeof("background") == VIPS_TYPE_ARRAY_DOUBLE) {
|
|
94
|
+
baton->background = image.get_array_double("background");
|
|
95
|
+
}
|
|
93
96
|
// Derived attributes
|
|
94
97
|
baton->hasAlpha = sharp::HasAlpha(image);
|
|
95
98
|
baton->orientation = sharp::ExifOrientation(image);
|
|
@@ -209,6 +212,17 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
209
212
|
if (baton->subifds > 0) {
|
|
210
213
|
info.Set("subifds", baton->subifds);
|
|
211
214
|
}
|
|
215
|
+
if (!baton->background.empty()) {
|
|
216
|
+
if (baton->background.size() == 3) {
|
|
217
|
+
Napi::Object background = Napi::Object::New(env);
|
|
218
|
+
background.Set("r", baton->background[0]);
|
|
219
|
+
background.Set("g", baton->background[1]);
|
|
220
|
+
background.Set("b", baton->background[2]);
|
|
221
|
+
info.Set("background", background);
|
|
222
|
+
} else {
|
|
223
|
+
info.Set("background", baton->background[0]);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
212
226
|
info.Set("hasProfile", baton->hasProfile);
|
|
213
227
|
info.Set("hasAlpha", baton->hasAlpha);
|
|
214
228
|
if (baton->orientation > 0) {
|
package/src/metadata.h
CHANGED
package/src/operations.cc
CHANGED
|
@@ -92,6 +92,13 @@ namespace sharp {
|
|
|
92
92
|
return image;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/*
|
|
96
|
+
* Contrast limiting adapative histogram equalization (CLAHE)
|
|
97
|
+
*/
|
|
98
|
+
VImage Clahe(VImage image, int const width, int const height, int const maxSlope) {
|
|
99
|
+
return image.hist_local(width, height, VImage::option()->set("max_slope", maxSlope));
|
|
100
|
+
}
|
|
101
|
+
|
|
95
102
|
/*
|
|
96
103
|
* Gamma encoding/decoding
|
|
97
104
|
*/
|
|
@@ -105,6 +112,19 @@ namespace sharp {
|
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Produce the "negative" of the image.
|
|
117
|
+
*/
|
|
118
|
+
VImage Negate(VImage image, bool const negateAlpha) {
|
|
119
|
+
if (HasAlpha(image) && !negateAlpha) {
|
|
120
|
+
// Separate alpha channel
|
|
121
|
+
VImage alpha = image[image.bands() - 1];
|
|
122
|
+
return RemoveAlpha(image).invert().bandjoin(alpha);
|
|
123
|
+
} else {
|
|
124
|
+
return image.invert();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
108
128
|
/*
|
|
109
129
|
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
|
110
130
|
*/
|
|
@@ -162,7 +182,8 @@ namespace sharp {
|
|
|
162
182
|
0.0, 0.0, 0.0, 1.0));
|
|
163
183
|
}
|
|
164
184
|
|
|
165
|
-
VImage Modulate(VImage image, double const brightness, double const saturation,
|
|
185
|
+
VImage Modulate(VImage image, double const brightness, double const saturation,
|
|
186
|
+
int const hue, double const lightness) {
|
|
166
187
|
if (HasAlpha(image)) {
|
|
167
188
|
// Separate alpha channel
|
|
168
189
|
VImage alpha = image[image.bands() - 1];
|
|
@@ -170,7 +191,7 @@ namespace sharp {
|
|
|
170
191
|
.colourspace(VIPS_INTERPRETATION_LCH)
|
|
171
192
|
.linear(
|
|
172
193
|
{ brightness, saturation, 1},
|
|
173
|
-
{
|
|
194
|
+
{ lightness, 0.0, static_cast<double>(hue) }
|
|
174
195
|
)
|
|
175
196
|
.colourspace(VIPS_INTERPRETATION_sRGB)
|
|
176
197
|
.bandjoin(alpha);
|
|
@@ -179,7 +200,7 @@ namespace sharp {
|
|
|
179
200
|
.colourspace(VIPS_INTERPRETATION_LCH)
|
|
180
201
|
.linear(
|
|
181
202
|
{ brightness, saturation, 1 },
|
|
182
|
-
{
|
|
203
|
+
{ lightness, 0.0, static_cast<double>(hue) }
|
|
183
204
|
)
|
|
184
205
|
.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
185
206
|
}
|
|
@@ -275,4 +296,16 @@ namespace sharp {
|
|
|
275
296
|
return image.linear(a, b);
|
|
276
297
|
}
|
|
277
298
|
}
|
|
299
|
+
|
|
300
|
+
/*
|
|
301
|
+
* Ensure the image is in a given colourspace
|
|
302
|
+
*/
|
|
303
|
+
VImage EnsureColourspace(VImage image, VipsInterpretation colourspace) {
|
|
304
|
+
if (colourspace != VIPS_INTERPRETATION_LAST && image.interpretation() != colourspace) {
|
|
305
|
+
image = image.colourspace(colourspace,
|
|
306
|
+
VImage::option()->set("source_space", image.interpretation()));
|
|
307
|
+
}
|
|
308
|
+
return image;
|
|
309
|
+
}
|
|
310
|
+
|
|
278
311
|
} // namespace sharp
|
package/src/operations.h
CHANGED
|
@@ -35,11 +35,21 @@ namespace sharp {
|
|
|
35
35
|
*/
|
|
36
36
|
VImage Normalise(VImage image);
|
|
37
37
|
|
|
38
|
+
/*
|
|
39
|
+
* Contrast limiting adapative histogram equalization (CLAHE)
|
|
40
|
+
*/
|
|
41
|
+
VImage Clahe(VImage image, int const width, int const height, int const maxSlope);
|
|
42
|
+
|
|
38
43
|
/*
|
|
39
44
|
* Gamma encoding/decoding
|
|
40
45
|
*/
|
|
41
46
|
VImage Gamma(VImage image, double const exponent);
|
|
42
47
|
|
|
48
|
+
/*
|
|
49
|
+
* Produce the "negative" of the image.
|
|
50
|
+
*/
|
|
51
|
+
VImage Negate(VImage image, bool const negateAlpha);
|
|
52
|
+
|
|
43
53
|
/*
|
|
44
54
|
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
|
45
55
|
*/
|
|
@@ -88,9 +98,15 @@ namespace sharp {
|
|
|
88
98
|
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
|
|
89
99
|
|
|
90
100
|
/*
|
|
91
|
-
* Modulate brightness, saturation and
|
|
101
|
+
* Modulate brightness, saturation, hue and lightness
|
|
102
|
+
*/
|
|
103
|
+
VImage Modulate(VImage image, double const brightness, double const saturation,
|
|
104
|
+
int const hue, double const lightness);
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
* Ensure the image is in a given colourspace
|
|
92
108
|
*/
|
|
93
|
-
VImage
|
|
109
|
+
VImage EnsureColourspace(VImage image, VipsInterpretation colourspace);
|
|
94
110
|
|
|
95
111
|
} // namespace sharp
|
|
96
112
|
|
package/src/pipeline.cc
CHANGED
|
@@ -67,6 +67,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
67
67
|
vips::VImage image;
|
|
68
68
|
sharp::ImageType inputImageType;
|
|
69
69
|
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
|
|
70
|
+
image = sharp::EnsureColourspace(image, baton->colourspaceInput);
|
|
70
71
|
|
|
71
72
|
// Calculate angle of rotation
|
|
72
73
|
VipsAngle rotation;
|
|
@@ -89,7 +90,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
89
90
|
}
|
|
90
91
|
if (baton->rotationAngle != 0.0) {
|
|
91
92
|
std::vector<double> background;
|
|
92
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
|
93
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
|
|
93
94
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
94
95
|
}
|
|
95
96
|
}
|
|
@@ -214,7 +215,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
214
215
|
double yresidual = static_cast<double>(yshrink) / yfactor;
|
|
215
216
|
|
|
216
217
|
// If integral x and y shrink are equal, try to use shrink-on-load for JPEG and WebP,
|
|
217
|
-
// but not when applying gamma correction, pre-resize extract or
|
|
218
|
+
// but not when applying gamma correction, pre-resize extract, trim or input colourspace
|
|
218
219
|
int shrink_on_load = 1;
|
|
219
220
|
|
|
220
221
|
int shrink_on_load_factor = 1;
|
|
@@ -227,7 +228,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
227
228
|
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
|
228
229
|
(inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
|
|
229
230
|
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
|
|
230
|
-
|
|
231
|
+
baton->colourspaceInput == VIPS_INTERPRETATION_LAST &&
|
|
232
|
+
image.width() > 3 && image.height() > 3 && baton->input->pages == 1
|
|
231
233
|
) {
|
|
232
234
|
if (xshrink >= 8 * shrink_on_load_factor) {
|
|
233
235
|
xfactor = xfactor / 8;
|
|
@@ -287,16 +289,21 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
287
289
|
yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeHeight);
|
|
288
290
|
}
|
|
289
291
|
}
|
|
292
|
+
// Remove animation properties from single page images
|
|
293
|
+
if (baton->input->pages == 1) {
|
|
294
|
+
image = sharp::RemoveAnimationProperties(image);
|
|
295
|
+
}
|
|
290
296
|
|
|
291
297
|
// Ensure we're using a device-independent colour space
|
|
298
|
+
char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
|
|
292
299
|
if (
|
|
293
300
|
sharp::HasProfile(image) &&
|
|
294
301
|
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
|
295
302
|
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
|
296
303
|
) {
|
|
297
|
-
// Convert to sRGB using embedded profile
|
|
304
|
+
// Convert to sRGB/P3 using embedded profile
|
|
298
305
|
try {
|
|
299
|
-
image = image.icc_transform(
|
|
306
|
+
image = image.icc_transform(processingProfile, VImage::option()
|
|
300
307
|
->set("embedded", TRUE)
|
|
301
308
|
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
|
|
302
309
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
@@ -304,7 +311,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
304
311
|
// Ignore failure of embedded profile
|
|
305
312
|
}
|
|
306
313
|
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
|
307
|
-
image = image.icc_transform(
|
|
314
|
+
image = image.icc_transform(processingProfile, VImage::option()
|
|
308
315
|
->set("input_profile", "cmyk")
|
|
309
316
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
310
317
|
}
|
|
@@ -325,7 +332,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
325
332
|
|
|
326
333
|
// Negate the colours in the image
|
|
327
334
|
if (baton->negate) {
|
|
328
|
-
image = image
|
|
335
|
+
image = sharp::Negate(image, baton->negateAlpha);
|
|
329
336
|
}
|
|
330
337
|
|
|
331
338
|
// Gamma encoding (darken)
|
|
@@ -344,7 +351,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
344
351
|
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
|
345
352
|
bool const shouldApplyMedian = baton->medianSize > 0;
|
|
346
353
|
bool const shouldComposite = !baton->composite.empty();
|
|
347
|
-
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 ||
|
|
354
|
+
bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 ||
|
|
355
|
+
baton->hue != 0.0 || baton->lightness != 0.0;
|
|
356
|
+
bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0;
|
|
348
357
|
|
|
349
358
|
if (shouldComposite && !sharp::HasAlpha(image)) {
|
|
350
359
|
image = sharp::EnsureAlpha(image, 1);
|
|
@@ -410,6 +419,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
410
419
|
|
|
411
420
|
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
|
412
421
|
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
|
422
|
+
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
|
413
423
|
image = image.bandjoin(joinImage);
|
|
414
424
|
}
|
|
415
425
|
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
|
|
@@ -419,7 +429,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
419
429
|
if (image.width() != baton->width || image.height() != baton->height) {
|
|
420
430
|
if (baton->canvas == Canvas::EMBED) {
|
|
421
431
|
std::vector<double> background;
|
|
422
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground);
|
|
432
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->resizeBackground, shouldPremultiplyAlpha);
|
|
423
433
|
|
|
424
434
|
// Embed
|
|
425
435
|
|
|
@@ -476,7 +486,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
476
486
|
// Rotate post-extract non-90 angle
|
|
477
487
|
if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) {
|
|
478
488
|
std::vector<double> background;
|
|
479
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground);
|
|
489
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, shouldPremultiplyAlpha);
|
|
480
490
|
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
481
491
|
}
|
|
482
492
|
|
|
@@ -489,7 +499,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
489
499
|
// Affine transform
|
|
490
500
|
if (baton->affineMatrix.size() > 0) {
|
|
491
501
|
std::vector<double> background;
|
|
492
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground);
|
|
502
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->affineBackground, shouldPremultiplyAlpha);
|
|
493
503
|
image = image.affine(baton->affineMatrix, VImage::option()->set("background", background)
|
|
494
504
|
->set("idx", baton->affineIdx)
|
|
495
505
|
->set("idy", baton->affineIdy)
|
|
@@ -501,7 +511,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
501
511
|
// Extend edges
|
|
502
512
|
if (baton->extendTop > 0 || baton->extendBottom > 0 || baton->extendLeft > 0 || baton->extendRight > 0) {
|
|
503
513
|
std::vector<double> background;
|
|
504
|
-
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground);
|
|
514
|
+
std::tie(image, background) = sharp::ApplyAlpha(image, baton->extendBackground, shouldPremultiplyAlpha);
|
|
505
515
|
|
|
506
516
|
// Embed
|
|
507
517
|
baton->width = image.width() + baton->extendLeft + baton->extendRight;
|
|
@@ -538,7 +548,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
538
548
|
}
|
|
539
549
|
|
|
540
550
|
if (shouldModulate) {
|
|
541
|
-
image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue);
|
|
551
|
+
image = sharp::Modulate(image, baton->brightness, baton->saturation, baton->hue, baton->lightness);
|
|
542
552
|
}
|
|
543
553
|
|
|
544
554
|
// Sharpen
|
|
@@ -551,7 +561,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
551
561
|
for (Composite *composite : baton->composite) {
|
|
552
562
|
VImage compositeImage;
|
|
553
563
|
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
|
554
|
-
std::tie(compositeImage, compositeImageType) = OpenInput(composite->input);
|
|
564
|
+
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
|
565
|
+
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
|
555
566
|
// Verify within current dimensions
|
|
556
567
|
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
|
|
557
568
|
throw vips::VError("Image to composite must have same dimensions or smaller");
|
|
@@ -650,11 +661,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
650
661
|
image = sharp::Normalise(image);
|
|
651
662
|
}
|
|
652
663
|
|
|
664
|
+
// Apply contrast limiting adaptive histogram equalization (CLAHE)
|
|
665
|
+
if (shouldApplyClahe) {
|
|
666
|
+
image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
|
|
667
|
+
}
|
|
668
|
+
|
|
653
669
|
// Apply bitwise boolean operation between images
|
|
654
670
|
if (baton->boolean != nullptr) {
|
|
655
671
|
VImage booleanImage;
|
|
656
672
|
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
|
657
673
|
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
|
674
|
+
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
|
658
675
|
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
|
659
676
|
}
|
|
660
677
|
|
|
@@ -704,9 +721,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
704
721
|
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
|
705
722
|
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
|
706
723
|
// Transform colours from embedded profile to output profile
|
|
707
|
-
if (baton->withMetadata && sharp::HasProfile(image)) {
|
|
708
|
-
image = image.icc_transform(
|
|
709
|
-
|
|
724
|
+
if (baton->withMetadata && sharp::HasProfile(image) && baton->withMetadataIcc.empty()) {
|
|
725
|
+
image = image.icc_transform("srgb", VImage::option()
|
|
726
|
+
->set("embedded", TRUE)
|
|
727
|
+
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
710
728
|
}
|
|
711
729
|
}
|
|
712
730
|
|
|
@@ -715,7 +733,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
715
733
|
image = image.icc_transform(
|
|
716
734
|
const_cast<char*>(baton->withMetadataIcc.data()),
|
|
717
735
|
VImage::option()
|
|
718
|
-
->set("input_profile",
|
|
736
|
+
->set("input_profile", processingProfile)
|
|
737
|
+
->set("embedded", TRUE)
|
|
719
738
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
720
739
|
}
|
|
721
740
|
// Override EXIF Orientation tag
|
|
@@ -749,6 +768,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
749
768
|
baton->loop);
|
|
750
769
|
|
|
751
770
|
// Output
|
|
771
|
+
sharp::SetTimeout(image, baton->timeoutSeconds);
|
|
752
772
|
if (baton->fileOut.empty()) {
|
|
753
773
|
// Buffer output
|
|
754
774
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
@@ -759,8 +779,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
759
779
|
->set("Q", baton->jpegQuality)
|
|
760
780
|
->set("interlace", baton->jpegProgressive)
|
|
761
781
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
762
|
-
?
|
|
763
|
-
:
|
|
782
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
|
783
|
+
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
764
784
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
765
785
|
->set("quant_table", baton->jpegQuantisationTable)
|
|
766
786
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
@@ -776,6 +796,22 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
776
796
|
} else {
|
|
777
797
|
baton->channels = std::min(baton->channels, 3);
|
|
778
798
|
}
|
|
799
|
+
} else if (baton->formatOut == "jp2" || (baton->formatOut == "input"
|
|
800
|
+
&& inputImageType == sharp::ImageType::JP2)) {
|
|
801
|
+
// Write JP2 to Buffer
|
|
802
|
+
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JP2);
|
|
803
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.jp2ksave_buffer(VImage::option()
|
|
804
|
+
->set("Q", baton->jp2Quality)
|
|
805
|
+
->set("lossless", baton->jp2Lossless)
|
|
806
|
+
->set("subsample_mode", baton->jp2ChromaSubsampling == "4:4:4"
|
|
807
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
808
|
+
->set("tile_height", baton->jp2TileHeight)
|
|
809
|
+
->set("tile_width", baton->jp2TileWidth)));
|
|
810
|
+
baton->bufferOut = static_cast<char*>(area->data);
|
|
811
|
+
baton->bufferOutLength = area->length;
|
|
812
|
+
area->free_fn = nullptr;
|
|
813
|
+
vips_area_unref(area);
|
|
814
|
+
baton->formatOut = "jp2";
|
|
779
815
|
} else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
|
|
780
816
|
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
|
781
817
|
inputImageType == sharp::ImageType::SVG))) {
|
|
@@ -788,7 +824,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
788
824
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
789
825
|
->set("palette", baton->pngPalette)
|
|
790
826
|
->set("Q", baton->pngQuality)
|
|
791
|
-
->set("
|
|
827
|
+
->set("bitdepth", baton->pngBitdepth)
|
|
792
828
|
->set("dither", baton->pngDither)));
|
|
793
829
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
794
830
|
baton->bufferOutLength = area->length;
|
|
@@ -857,15 +893,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
857
893
|
} else if (baton->formatOut == "heif" ||
|
|
858
894
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
|
859
895
|
// Write HEIF to buffer
|
|
896
|
+
image = sharp::RemoveAnimationProperties(image);
|
|
860
897
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
861
898
|
->set("strip", !baton->withMetadata)
|
|
862
|
-
->set("compression", baton->heifCompression)
|
|
863
899
|
->set("Q", baton->heifQuality)
|
|
900
|
+
->set("compression", baton->heifCompression)
|
|
864
901
|
->set("speed", baton->heifSpeed)
|
|
865
|
-
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
866
902
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
867
903
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
868
|
-
#endif
|
|
869
904
|
->set("lossless", baton->heifLossless)));
|
|
870
905
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
871
906
|
baton->bufferOutLength = area->length;
|
|
@@ -880,9 +915,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
880
915
|
image = image[0];
|
|
881
916
|
baton->channels = 1;
|
|
882
917
|
}
|
|
883
|
-
if (image.format() !=
|
|
884
|
-
// Cast pixels to
|
|
885
|
-
image = image.cast(
|
|
918
|
+
if (image.format() != baton->rawDepth) {
|
|
919
|
+
// Cast pixels to requested format
|
|
920
|
+
image = image.cast(baton->rawDepth);
|
|
886
921
|
}
|
|
887
922
|
// Get raw image data
|
|
888
923
|
baton->bufferOut = static_cast<char*>(image.write_to_memory(&baton->bufferOutLength));
|
|
@@ -908,13 +943,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
908
943
|
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
|
909
944
|
bool const isGif = sharp::IsGif(baton->fileOut);
|
|
910
945
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
|
946
|
+
bool const isJp2 = sharp::IsJp2(baton->fileOut);
|
|
911
947
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
|
912
948
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
|
913
949
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
|
914
950
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
915
951
|
bool const mightMatchInput = baton->formatOut == "input";
|
|
916
952
|
bool const willMatchInput = mightMatchInput &&
|
|
917
|
-
!(isJpeg || isPng || isWebp || isGif || isTiff || isHeif || isDz || isDzZip || isV);
|
|
953
|
+
!(isJpeg || isPng || isWebp || isGif || isTiff || isJp2 || isHeif || isDz || isDzZip || isV);
|
|
918
954
|
|
|
919
955
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
|
920
956
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
@@ -925,8 +961,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
925
961
|
->set("Q", baton->jpegQuality)
|
|
926
962
|
->set("interlace", baton->jpegProgressive)
|
|
927
963
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
928
|
-
?
|
|
929
|
-
:
|
|
964
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF
|
|
965
|
+
: VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
930
966
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
931
967
|
->set("quant_table", baton->jpegQuantisationTable)
|
|
932
968
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
@@ -934,6 +970,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
934
970
|
->set("optimize_coding", baton->jpegOptimiseCoding));
|
|
935
971
|
baton->formatOut = "jpeg";
|
|
936
972
|
baton->channels = std::min(baton->channels, 3);
|
|
973
|
+
} else if (baton->formatOut == "jp2" || (mightMatchInput && isJp2) ||
|
|
974
|
+
(willMatchInput && (inputImageType == sharp::ImageType::JP2))) {
|
|
975
|
+
// Write JP2 to file
|
|
976
|
+
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JP2);
|
|
977
|
+
image.jp2ksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
978
|
+
->set("Q", baton->jp2Quality)
|
|
979
|
+
->set("lossless", baton->jp2Lossless)
|
|
980
|
+
->set("subsample_mode", baton->jp2ChromaSubsampling == "4:4:4"
|
|
981
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
982
|
+
->set("tile_height", baton->jp2TileHeight)
|
|
983
|
+
->set("tile_width", baton->jp2TileWidth));
|
|
984
|
+
baton->formatOut = "jp2";
|
|
937
985
|
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
|
938
986
|
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
|
939
987
|
inputImageType == sharp::ImageType::SVG))) {
|
|
@@ -946,7 +994,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
946
994
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
947
995
|
->set("palette", baton->pngPalette)
|
|
948
996
|
->set("Q", baton->pngQuality)
|
|
949
|
-
->set("
|
|
997
|
+
->set("bitdepth", baton->pngBitdepth)
|
|
950
998
|
->set("dither", baton->pngDither));
|
|
951
999
|
baton->formatOut = "png";
|
|
952
1000
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
|
@@ -999,15 +1047,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
999
1047
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
1000
1048
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
1001
1049
|
// Write HEIF to file
|
|
1050
|
+
image = sharp::RemoveAnimationProperties(image);
|
|
1002
1051
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1003
1052
|
->set("strip", !baton->withMetadata)
|
|
1004
1053
|
->set("Q", baton->heifQuality)
|
|
1005
1054
|
->set("compression", baton->heifCompression)
|
|
1006
1055
|
->set("speed", baton->heifSpeed)
|
|
1007
|
-
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
1008
1056
|
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
1009
1057
|
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
1010
|
-
#endif
|
|
1011
1058
|
->set("lossless", baton->heifLossless));
|
|
1012
1059
|
baton->formatOut = "heif";
|
|
1013
1060
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
@@ -1124,6 +1171,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1124
1171
|
info.Set("width", static_cast<uint32_t>(width));
|
|
1125
1172
|
info.Set("height", static_cast<uint32_t>(height));
|
|
1126
1173
|
info.Set("channels", static_cast<uint32_t>(baton->channels));
|
|
1174
|
+
if (baton->formatOut == "raw") {
|
|
1175
|
+
info.Set("depth", vips_enum_nick(VIPS_TYPE_BAND_FORMAT, baton->rawDepth));
|
|
1176
|
+
}
|
|
1127
1177
|
info.Set("premultiplied", baton->premultiplied);
|
|
1128
1178
|
if (baton->hasCropOffset) {
|
|
1129
1179
|
info.Set("cropOffsetLeft", static_cast<int32_t>(baton->cropOffsetLeft));
|
|
@@ -1313,10 +1363,12 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1313
1363
|
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
|
1314
1364
|
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
|
1315
1365
|
baton->negate = sharp::AttrAsBool(options, "negate");
|
|
1366
|
+
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
|
1316
1367
|
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
|
1317
1368
|
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
|
1318
1369
|
baton->saturation = sharp::AttrAsDouble(options, "saturation");
|
|
1319
1370
|
baton->hue = sharp::AttrAsInt32(options, "hue");
|
|
1371
|
+
baton->lightness = sharp::AttrAsDouble(options, "lightness");
|
|
1320
1372
|
baton->medianSize = sharp::AttrAsUint32(options, "medianSize");
|
|
1321
1373
|
baton->sharpenSigma = sharp::AttrAsDouble(options, "sharpenSigma");
|
|
1322
1374
|
baton->sharpenFlat = sharp::AttrAsDouble(options, "sharpenFlat");
|
|
@@ -1330,6 +1382,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1330
1382
|
baton->linearB = sharp::AttrAsDouble(options, "linearB");
|
|
1331
1383
|
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
|
1332
1384
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
|
1385
|
+
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
|
1386
|
+
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
|
1387
|
+
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
|
1333
1388
|
baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
|
|
1334
1389
|
baton->angle = sharp::AttrAsInt32(options, "angle");
|
|
1335
1390
|
baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
|
|
@@ -1380,6 +1435,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1380
1435
|
baton->recombMatrix[i] = sharp::AttrAsDouble(recombMatrix, i);
|
|
1381
1436
|
}
|
|
1382
1437
|
}
|
|
1438
|
+
baton->colourspaceInput = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspaceInput"));
|
|
1439
|
+
if (baton->colourspaceInput == VIPS_INTERPRETATION_ERROR) {
|
|
1440
|
+
baton->colourspaceInput = VIPS_INTERPRETATION_LAST;
|
|
1441
|
+
}
|
|
1383
1442
|
baton->colourspace = sharp::GetInterpretation(sharp::AttrAsStr(options, "colourspace"));
|
|
1384
1443
|
if (baton->colourspace == VIPS_INTERPRETATION_ERROR) {
|
|
1385
1444
|
baton->colourspace = VIPS_INTERPRETATION_sRGB;
|
|
@@ -1397,6 +1456,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1397
1456
|
std::string k = sharp::AttrAsStr(mdStrKeys, i);
|
|
1398
1457
|
baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
|
|
1399
1458
|
}
|
|
1459
|
+
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
|
1400
1460
|
// Format-specific
|
|
1401
1461
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
|
1402
1462
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
|
@@ -1411,8 +1471,13 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1411
1471
|
baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
|
|
1412
1472
|
baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
|
|
1413
1473
|
baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
|
|
1414
|
-
baton->
|
|
1474
|
+
baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
|
|
1415
1475
|
baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
|
|
1476
|
+
baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
|
|
1477
|
+
baton->jp2Lossless = sharp::AttrAsBool(options, "jp2Lossless");
|
|
1478
|
+
baton->jp2TileHeight = sharp::AttrAsUint32(options, "jp2TileHeight");
|
|
1479
|
+
baton->jp2TileWidth = sharp::AttrAsUint32(options, "jp2TileWidth");
|
|
1480
|
+
baton->jp2ChromaSubsampling = sharp::AttrAsStr(options, "jp2ChromaSubsampling");
|
|
1416
1481
|
baton->webpQuality = sharp::AttrAsUint32(options, "webpQuality");
|
|
1417
1482
|
baton->webpAlphaQuality = sharp::AttrAsUint32(options, "webpAlphaQuality");
|
|
1418
1483
|
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
|
@@ -1442,6 +1507,11 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1442
1507
|
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
|
1443
1508
|
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1444
1509
|
|
|
1510
|
+
// Raw output
|
|
1511
|
+
baton->rawDepth = static_cast<VipsBandFormat>(
|
|
1512
|
+
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
|
1513
|
+
sharp::AttrAsStr(options, "rawDepth").data()));
|
|
1514
|
+
|
|
1445
1515
|
// Animated output
|
|
1446
1516
|
if (sharp::HasAttr(options, "pageHeight")) {
|
|
1447
1517
|
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|