sharp 0.26.3 → 0.28.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 +16 -15
- package/binding.gyp +3 -5
- package/install/dll-copy.js +4 -4
- package/install/libvips.js +65 -31
- package/lib/channel.js +1 -1
- package/lib/colour.js +7 -0
- package/lib/composite.js +10 -7
- package/lib/constructor.js +53 -19
- package/lib/input.js +41 -10
- package/lib/is.js +10 -0
- package/lib/libvips.js +25 -3
- package/lib/operation.js +9 -4
- package/lib/output.js +137 -58
- package/lib/resize.js +42 -15
- package/lib/utility.js +17 -10
- package/package.json +20 -18
- package/src/common.cc +73 -51
- package/src/common.h +10 -3
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +1 -1
- package/src/pipeline.cc +43 -14
- package/src/pipeline.h +12 -5
- package/src/sharp.cc +2 -0
- package/src/utilities.cc +16 -0
- package/src/utilities.h +1 -0
package/src/common.cc
CHANGED
|
@@ -109,7 +109,13 @@ namespace sharp {
|
|
|
109
109
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
|
110
110
|
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
|
111
111
|
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
|
112
|
-
|
|
112
|
+
if (HasAttr(input, "createNoiseType")) {
|
|
113
|
+
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
|
114
|
+
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
|
115
|
+
descriptor->createNoiseSigma = AttrAsDouble(input, "createNoiseSigma");
|
|
116
|
+
} else {
|
|
117
|
+
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
|
|
118
|
+
}
|
|
113
119
|
}
|
|
114
120
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
115
121
|
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
|
|
@@ -189,31 +195,40 @@ namespace sharp {
|
|
|
189
195
|
return id;
|
|
190
196
|
}
|
|
191
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Regenerate this table with something like:
|
|
200
|
+
*
|
|
201
|
+
* $ vips -l foreign | grep -i load | awk '{ print $2, $1; }'
|
|
202
|
+
*
|
|
203
|
+
* Plus a bit of editing.
|
|
204
|
+
*/
|
|
192
205
|
std::map<std::string, ImageType> loaderToType = {
|
|
193
|
-
{ "
|
|
194
|
-
{ "
|
|
195
|
-
{ "
|
|
196
|
-
{ "
|
|
197
|
-
{ "
|
|
198
|
-
{ "
|
|
199
|
-
{ "
|
|
200
|
-
{ "
|
|
201
|
-
{ "
|
|
202
|
-
{ "
|
|
203
|
-
{ "
|
|
204
|
-
{ "
|
|
205
|
-
{ "
|
|
206
|
-
{ "
|
|
207
|
-
{ "
|
|
208
|
-
{ "
|
|
209
|
-
{ "
|
|
210
|
-
{ "
|
|
211
|
-
{ "
|
|
212
|
-
{ "
|
|
213
|
-
{ "
|
|
214
|
-
{ "
|
|
215
|
-
{ "
|
|
216
|
-
{ "
|
|
206
|
+
{ "VipsForeignLoadJpegFile", ImageType::JPEG },
|
|
207
|
+
{ "VipsForeignLoadJpegBuffer", ImageType::JPEG },
|
|
208
|
+
{ "VipsForeignLoadPngFile", ImageType::PNG },
|
|
209
|
+
{ "VipsForeignLoadPngBuffer", ImageType::PNG },
|
|
210
|
+
{ "VipsForeignLoadWebpFile", ImageType::WEBP },
|
|
211
|
+
{ "VipsForeignLoadWebpBuffer", ImageType::WEBP },
|
|
212
|
+
{ "VipsForeignLoadTiffFile", ImageType::TIFF },
|
|
213
|
+
{ "VipsForeignLoadTiffBuffer", ImageType::TIFF },
|
|
214
|
+
{ "VipsForeignLoadGifFile", ImageType::GIF },
|
|
215
|
+
{ "VipsForeignLoadGifBuffer", ImageType::GIF },
|
|
216
|
+
{ "VipsForeignLoadNsgifFile", ImageType::GIF },
|
|
217
|
+
{ "VipsForeignLoadNsgifBuffer", ImageType::GIF },
|
|
218
|
+
{ "VipsForeignLoadSvgFile", ImageType::SVG },
|
|
219
|
+
{ "VipsForeignLoadSvgBuffer", ImageType::SVG },
|
|
220
|
+
{ "VipsForeignLoadHeifFile", ImageType::HEIF },
|
|
221
|
+
{ "VipsForeignLoadHeifBuffer", ImageType::HEIF },
|
|
222
|
+
{ "VipsForeignLoadPdfFile", ImageType::PDF },
|
|
223
|
+
{ "VipsForeignLoadPdfBuffer", ImageType::PDF },
|
|
224
|
+
{ "VipsForeignLoadMagickFile", ImageType::MAGICK },
|
|
225
|
+
{ "VipsForeignLoadMagickBuffer", ImageType::MAGICK },
|
|
226
|
+
{ "VipsForeignLoadOpenslide", ImageType::OPENSLIDE },
|
|
227
|
+
{ "VipsForeignLoadPpmFile", ImageType::PPM },
|
|
228
|
+
{ "VipsForeignLoadFits", ImageType::FITS },
|
|
229
|
+
{ "VipsForeignLoadOpenexr", ImageType::EXR },
|
|
230
|
+
{ "VipsForeignLoadVips", ImageType::VIPS },
|
|
231
|
+
{ "VipsForeignLoadRaw", ImageType::RAW }
|
|
217
232
|
};
|
|
218
233
|
|
|
219
234
|
/*
|
|
@@ -223,7 +238,7 @@ namespace sharp {
|
|
|
223
238
|
ImageType imageType = ImageType::UNKNOWN;
|
|
224
239
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
|
225
240
|
if (load != nullptr) {
|
|
226
|
-
auto it = loaderToType.find(
|
|
241
|
+
auto it = loaderToType.find(load);
|
|
227
242
|
if (it != loaderToType.end()) {
|
|
228
243
|
imageType = it->second;
|
|
229
244
|
}
|
|
@@ -238,7 +253,7 @@ namespace sharp {
|
|
|
238
253
|
ImageType imageType = ImageType::UNKNOWN;
|
|
239
254
|
char const *load = vips_foreign_find_load(file);
|
|
240
255
|
if (load != nullptr) {
|
|
241
|
-
auto it = loaderToType.find(
|
|
256
|
+
auto it = loaderToType.find(load);
|
|
242
257
|
if (it != loaderToType.end()) {
|
|
243
258
|
imageType = it->second;
|
|
244
259
|
}
|
|
@@ -318,15 +333,35 @@ namespace sharp {
|
|
|
318
333
|
} else {
|
|
319
334
|
if (descriptor->createChannels > 0) {
|
|
320
335
|
// Create new image
|
|
321
|
-
|
|
322
|
-
descriptor->
|
|
323
|
-
descriptor->
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
336
|
+
if (descriptor->createNoiseType == "gaussian") {
|
|
337
|
+
int const channels = descriptor->createChannels;
|
|
338
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight);
|
|
339
|
+
std::vector<VImage> bands = {};
|
|
340
|
+
bands.reserve(channels);
|
|
341
|
+
for (int _band = 0; _band < channels; _band++) {
|
|
342
|
+
bands.push_back(image.gaussnoise(
|
|
343
|
+
descriptor->createWidth,
|
|
344
|
+
descriptor->createHeight,
|
|
345
|
+
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
|
346
|
+
}
|
|
347
|
+
image = image.bandjoin(bands);
|
|
348
|
+
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
|
349
|
+
if (channels < 3) {
|
|
350
|
+
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
351
|
+
} else {
|
|
352
|
+
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
std::vector<double> background = {
|
|
356
|
+
descriptor->createBackground[0],
|
|
357
|
+
descriptor->createBackground[1],
|
|
358
|
+
descriptor->createBackground[2]
|
|
359
|
+
};
|
|
360
|
+
if (descriptor->createChannels == 4) {
|
|
361
|
+
background.push_back(descriptor->createBackground[3]);
|
|
362
|
+
}
|
|
363
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
328
364
|
}
|
|
329
|
-
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
330
365
|
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
|
331
366
|
imageType = ImageType::RAW;
|
|
332
367
|
} else {
|
|
@@ -388,12 +423,7 @@ namespace sharp {
|
|
|
388
423
|
Uses colour space interpretation with number of channels to guess this.
|
|
389
424
|
*/
|
|
390
425
|
bool HasAlpha(VImage image) {
|
|
391
|
-
|
|
392
|
-
VipsInterpretation const interpretation = image.interpretation();
|
|
393
|
-
return (
|
|
394
|
-
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
|
|
395
|
-
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
|
|
396
|
-
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
|
|
426
|
+
return image.has_alpha();
|
|
397
427
|
}
|
|
398
428
|
|
|
399
429
|
/*
|
|
@@ -658,26 +688,18 @@ namespace sharp {
|
|
|
658
688
|
int top = 0;
|
|
659
689
|
|
|
660
690
|
// assign only if valid
|
|
661
|
-
if (x
|
|
691
|
+
if (x < (inWidth - outWidth)) {
|
|
662
692
|
left = x;
|
|
663
693
|
} else if (x >= (inWidth - outWidth)) {
|
|
664
694
|
left = inWidth - outWidth;
|
|
665
695
|
}
|
|
666
696
|
|
|
667
|
-
if (y
|
|
697
|
+
if (y < (inHeight - outHeight)) {
|
|
668
698
|
top = y;
|
|
669
699
|
} else if (y >= (inHeight - outHeight)) {
|
|
670
700
|
top = inHeight - outHeight;
|
|
671
701
|
}
|
|
672
702
|
|
|
673
|
-
// the resulting left and top could have been outside the image after calculation from bottom/right edges
|
|
674
|
-
if (left < 0) {
|
|
675
|
-
left = 0;
|
|
676
|
-
}
|
|
677
|
-
if (top < 0) {
|
|
678
|
-
top = 0;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
703
|
return std::make_tuple(left, top);
|
|
682
704
|
}
|
|
683
705
|
|
package/src/common.h
CHANGED
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
// Verify platform and compiler compatibility
|
|
26
26
|
|
|
27
|
-
#if (VIPS_MAJOR_VERSION < 8 ||
|
|
28
|
-
|
|
27
|
+
#if (VIPS_MAJOR_VERSION < 8) || \
|
|
28
|
+
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
|
|
29
|
+
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 6)
|
|
30
|
+
#error "libvips version 8.10.6+ is required - please see https://sharp.pixelplumbing.com/install"
|
|
29
31
|
#endif
|
|
30
32
|
|
|
31
33
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
|
@@ -62,6 +64,9 @@ namespace sharp {
|
|
|
62
64
|
int createWidth;
|
|
63
65
|
int createHeight;
|
|
64
66
|
std::vector<double> createBackground;
|
|
67
|
+
std::string createNoiseType;
|
|
68
|
+
double createNoiseMean;
|
|
69
|
+
double createNoiseSigma;
|
|
65
70
|
|
|
66
71
|
InputDescriptor():
|
|
67
72
|
buffer(nullptr),
|
|
@@ -80,7 +85,9 @@ namespace sharp {
|
|
|
80
85
|
createChannels(0),
|
|
81
86
|
createWidth(0),
|
|
82
87
|
createHeight(0),
|
|
83
|
-
createBackground{ 0.0, 0.0, 0.0, 255.0 }
|
|
88
|
+
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
|
89
|
+
createNoiseMean(0.0),
|
|
90
|
+
createNoiseSigma(0.0) {}
|
|
84
91
|
};
|
|
85
92
|
|
|
86
93
|
// Convenience methods to access the attributes of a Napi::Object
|
package/src/metadata.cc
CHANGED
|
@@ -74,6 +74,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
74
74
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
|
75
75
|
baton->pagePrimary = image.get_int("heif-primary");
|
|
76
76
|
}
|
|
77
|
+
if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
|
|
78
|
+
baton->compression = image.get_string("heif-compression");
|
|
79
|
+
}
|
|
77
80
|
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
|
|
78
81
|
int const levels = std::stoi(image.get_string("openslide.level-count"));
|
|
79
82
|
for (int l = 0; l < levels; l++) {
|
|
@@ -186,6 +189,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
186
189
|
if (baton->pagePrimary > -1) {
|
|
187
190
|
info.Set("pagePrimary", baton->pagePrimary);
|
|
188
191
|
}
|
|
192
|
+
if (!baton->compression.empty()) {
|
|
193
|
+
info.Set("compression", baton->compression);
|
|
194
|
+
}
|
|
189
195
|
if (!baton->levels.empty()) {
|
|
190
196
|
int i = 0;
|
|
191
197
|
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
package/src/metadata.h
CHANGED
package/src/operations.cc
CHANGED
|
@@ -149,8 +149,8 @@ namespace sharp {
|
|
|
149
149
|
*/
|
|
150
150
|
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
|
|
151
151
|
double *m = matrix.get();
|
|
152
|
+
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
152
153
|
return image
|
|
153
|
-
.colourspace(VIPS_INTERPRETATION_sRGB)
|
|
154
154
|
.recomb(image.bands() == 3
|
|
155
155
|
? VImage::new_from_memory(
|
|
156
156
|
m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
|
package/src/pipeline.cc
CHANGED
|
@@ -562,15 +562,23 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
562
562
|
// Use gravity in overlay
|
|
563
563
|
if (compositeImage.width() <= baton->width) {
|
|
564
564
|
across = static_cast<int>(ceil(static_cast<double>(image.width()) / compositeImage.width()));
|
|
565
|
+
// Ensure odd number of tiles across when gravity is centre, north or south
|
|
566
|
+
if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) {
|
|
567
|
+
across |= 1;
|
|
568
|
+
}
|
|
565
569
|
}
|
|
566
570
|
if (compositeImage.height() <= baton->height) {
|
|
567
571
|
down = static_cast<int>(ceil(static_cast<double>(image.height()) / compositeImage.height()));
|
|
572
|
+
// Ensure odd number of tiles down when gravity is centre, east or west
|
|
573
|
+
if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) {
|
|
574
|
+
down |= 1;
|
|
575
|
+
}
|
|
568
576
|
}
|
|
569
577
|
if (across != 0 || down != 0) {
|
|
570
578
|
int left;
|
|
571
579
|
int top;
|
|
572
580
|
compositeImage = compositeImage.replicate(across, down);
|
|
573
|
-
if (composite->
|
|
581
|
+
if (composite->hasOffset) {
|
|
574
582
|
std::tie(left, top) = sharp::CalculateCrop(
|
|
575
583
|
compositeImage.width(), compositeImage.height(), image.width(), image.height(),
|
|
576
584
|
composite->left, composite->top);
|
|
@@ -592,10 +600,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
592
600
|
// Calculate position
|
|
593
601
|
int left;
|
|
594
602
|
int top;
|
|
595
|
-
if (composite->
|
|
603
|
+
if (composite->hasOffset) {
|
|
596
604
|
// Composite image at given offsets
|
|
597
|
-
|
|
598
|
-
|
|
605
|
+
if (composite->tile) {
|
|
606
|
+
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
|
|
607
|
+
compositeImage.width(), compositeImage.height(), composite->left, composite->top);
|
|
608
|
+
} else {
|
|
609
|
+
left = composite->left;
|
|
610
|
+
top = composite->top;
|
|
611
|
+
}
|
|
599
612
|
} else {
|
|
600
613
|
// Composite image with given gravity
|
|
601
614
|
std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
|
|
@@ -730,7 +743,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
730
743
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
731
744
|
// Write JPEG to buffer
|
|
732
745
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
733
|
-
VipsArea *area =
|
|
746
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
|
|
734
747
|
->set("strip", !baton->withMetadata)
|
|
735
748
|
->set("Q", baton->jpegQuality)
|
|
736
749
|
->set("interlace", baton->jpegProgressive)
|
|
@@ -757,7 +770,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
757
770
|
inputImageType == sharp::ImageType::SVG))) {
|
|
758
771
|
// Write PNG to buffer
|
|
759
772
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
760
|
-
VipsArea *area =
|
|
773
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
761
774
|
->set("strip", !baton->withMetadata)
|
|
762
775
|
->set("interlace", baton->pngProgressive)
|
|
763
776
|
->set("compression", baton->pngCompressionLevel)
|
|
@@ -775,7 +788,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
775
788
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::WEBP)) {
|
|
776
789
|
// Write WEBP to buffer
|
|
777
790
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
|
|
778
|
-
VipsArea *area =
|
|
791
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.webpsave_buffer(VImage::option()
|
|
779
792
|
->set("strip", !baton->withMetadata)
|
|
780
793
|
->set("Q", baton->webpQuality)
|
|
781
794
|
->set("lossless", baton->webpLossless)
|
|
@@ -792,7 +805,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
792
805
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
|
793
806
|
// Write GIF to buffer
|
|
794
807
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
795
|
-
VipsArea *area =
|
|
808
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.magicksave_buffer(VImage::option()
|
|
796
809
|
->set("strip", !baton->withMetadata)
|
|
797
810
|
->set("optimize_gif_frames", TRUE)
|
|
798
811
|
->set("optimize_gif_transparency", TRUE)
|
|
@@ -813,7 +826,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
813
826
|
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
814
827
|
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
815
828
|
}
|
|
816
|
-
VipsArea *area =
|
|
829
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.tiffsave_buffer(VImage::option()
|
|
817
830
|
->set("strip", !baton->withMetadata)
|
|
818
831
|
->set("Q", baton->tiffQuality)
|
|
819
832
|
->set("bitdepth", baton->tiffBitdepth)
|
|
@@ -833,10 +846,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
833
846
|
} else if (baton->formatOut == "heif" ||
|
|
834
847
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
|
835
848
|
// Write HEIF to buffer
|
|
836
|
-
VipsArea *area =
|
|
849
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
837
850
|
->set("strip", !baton->withMetadata)
|
|
838
851
|
->set("compression", baton->heifCompression)
|
|
839
852
|
->set("Q", baton->heifQuality)
|
|
853
|
+
->set("speed", baton->heifSpeed)
|
|
854
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
855
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
856
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
857
|
+
#endif
|
|
840
858
|
->set("lossless", baton->heifLossless)));
|
|
841
859
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
842
860
|
baton->bufferOutLength = area->length;
|
|
@@ -885,7 +903,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
885
903
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
886
904
|
bool const mightMatchInput = baton->formatOut == "input";
|
|
887
905
|
bool const willMatchInput = mightMatchInput &&
|
|
888
|
-
!(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
|
|
906
|
+
!(isJpeg || isPng || isWebp || isGif || isTiff || isHeif || isDz || isDzZip || isV);
|
|
889
907
|
|
|
890
908
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
|
891
909
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
@@ -950,6 +968,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
950
968
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
951
969
|
baton->channels = std::min(baton->channels, 3);
|
|
952
970
|
}
|
|
971
|
+
// Cast pixel values to float, if required
|
|
972
|
+
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
973
|
+
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
974
|
+
}
|
|
953
975
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
954
976
|
->set("strip", !baton->withMetadata)
|
|
955
977
|
->set("Q", baton->tiffQuality)
|
|
@@ -966,13 +988,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
966
988
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
967
989
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
968
990
|
// Write HEIF to file
|
|
969
|
-
if (sharp::IsAvif(baton->fileOut)) {
|
|
970
|
-
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
|
971
|
-
}
|
|
972
991
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
973
992
|
->set("strip", !baton->withMetadata)
|
|
974
993
|
->set("Q", baton->heifQuality)
|
|
975
994
|
->set("compression", baton->heifCompression)
|
|
995
|
+
->set("speed", baton->heifSpeed)
|
|
996
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
997
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
998
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
999
|
+
#endif
|
|
976
1000
|
->set("lossless", baton->heifLossless));
|
|
977
1001
|
baton->formatOut = "heif";
|
|
978
1002
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
@@ -1027,6 +1051,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1027
1051
|
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
|
1028
1052
|
->set("background", baton->tileBackground)
|
|
1029
1053
|
->set("centre", baton->tileCentre)
|
|
1054
|
+
->set("id", const_cast<char*>(baton->tileId.data()))
|
|
1030
1055
|
->set("skip_blanks", baton->tileSkipBlanks);
|
|
1031
1056
|
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
|
1032
1057
|
// not passing anything - libvips will handle choice
|
|
@@ -1254,6 +1279,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1254
1279
|
composite->gravity = sharp::AttrAsUint32(compositeObject, "gravity");
|
|
1255
1280
|
composite->left = sharp::AttrAsInt32(compositeObject, "left");
|
|
1256
1281
|
composite->top = sharp::AttrAsInt32(compositeObject, "top");
|
|
1282
|
+
composite->hasOffset = sharp::AttrAsBool(compositeObject, "hasOffset");
|
|
1257
1283
|
composite->tile = sharp::AttrAsBool(compositeObject, "tile");
|
|
1258
1284
|
composite->premultiplied = sharp::AttrAsBool(compositeObject, "premultiplied");
|
|
1259
1285
|
baton->composite.push_back(composite);
|
|
@@ -1395,6 +1421,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1395
1421
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1396
1422
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1397
1423
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1424
|
+
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
|
1425
|
+
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1398
1426
|
|
|
1399
1427
|
// Animated output
|
|
1400
1428
|
if (sharp::HasAttr(options, "pageHeight")) {
|
|
@@ -1424,6 +1452,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1424
1452
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
|
|
1425
1453
|
sharp::AttrAsStr(options, "tileDepth").data()));
|
|
1426
1454
|
baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
|
|
1455
|
+
baton->tileId = sharp::AttrAsStr(options, "tileId");
|
|
1427
1456
|
|
|
1428
1457
|
// Force random access for certain operations
|
|
1429
1458
|
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|
package/src/pipeline.h
CHANGED
|
@@ -40,6 +40,7 @@ struct Composite {
|
|
|
40
40
|
int gravity;
|
|
41
41
|
int left;
|
|
42
42
|
int top;
|
|
43
|
+
bool hasOffset;
|
|
43
44
|
bool tile;
|
|
44
45
|
bool premultiplied;
|
|
45
46
|
|
|
@@ -47,8 +48,9 @@ struct Composite {
|
|
|
47
48
|
input(nullptr),
|
|
48
49
|
mode(VIPS_BLEND_MODE_OVER),
|
|
49
50
|
gravity(0),
|
|
50
|
-
left(
|
|
51
|
-
top(
|
|
51
|
+
left(0),
|
|
52
|
+
top(0),
|
|
53
|
+
hasOffset(false),
|
|
52
54
|
tile(false),
|
|
53
55
|
premultiplied(false) {}
|
|
54
56
|
};
|
|
@@ -159,6 +161,8 @@ struct PipelineBaton {
|
|
|
159
161
|
double tiffYres;
|
|
160
162
|
int heifQuality;
|
|
161
163
|
VipsForeignHeifCompression heifCompression;
|
|
164
|
+
int heifSpeed;
|
|
165
|
+
std::string heifChromaSubsampling;
|
|
162
166
|
bool heifLossless;
|
|
163
167
|
std::string err;
|
|
164
168
|
bool withMetadata;
|
|
@@ -188,6 +192,7 @@ struct PipelineBaton {
|
|
|
188
192
|
std::vector<double> tileBackground;
|
|
189
193
|
int tileSkipBlanks;
|
|
190
194
|
VipsForeignDzDepth tileDepth;
|
|
195
|
+
std::string tileId;
|
|
191
196
|
std::unique_ptr<double[]> recombMatrix;
|
|
192
197
|
|
|
193
198
|
PipelineBaton():
|
|
@@ -254,7 +259,7 @@ struct PipelineBaton {
|
|
|
254
259
|
jpegOptimiseScans(false),
|
|
255
260
|
jpegOptimiseCoding(true),
|
|
256
261
|
pngProgressive(false),
|
|
257
|
-
pngCompressionLevel(
|
|
262
|
+
pngCompressionLevel(6),
|
|
258
263
|
pngAdaptiveFiltering(false),
|
|
259
264
|
pngPalette(false),
|
|
260
265
|
pngQuality(100),
|
|
@@ -276,8 +281,10 @@ struct PipelineBaton {
|
|
|
276
281
|
tiffTileWidth(256),
|
|
277
282
|
tiffXres(1.0),
|
|
278
283
|
tiffYres(1.0),
|
|
279
|
-
heifQuality(
|
|
280
|
-
heifCompression(
|
|
284
|
+
heifQuality(50),
|
|
285
|
+
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
|
286
|
+
heifSpeed(5),
|
|
287
|
+
heifChromaSubsampling("4:2:0"),
|
|
281
288
|
heifLossless(false),
|
|
282
289
|
withMetadata(false),
|
|
283
290
|
withMetadataOrientation(-1),
|
package/src/sharp.cc
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
#include "stats.h"
|
|
23
23
|
|
|
24
24
|
static void* sharp_vips_init(void*) {
|
|
25
|
+
g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE);
|
|
25
26
|
vips_init("sharp");
|
|
26
27
|
return nullptr;
|
|
27
28
|
}
|
|
@@ -43,6 +44,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
|
|
|
43
44
|
exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
|
|
44
45
|
exports.Set("format", Napi::Function::New(env, format));
|
|
45
46
|
exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
|
|
47
|
+
exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
|
|
46
48
|
exports.Set("stats", Napi::Function::New(env, stats));
|
|
47
49
|
return exports;
|
|
48
50
|
}
|
package/src/utilities.cc
CHANGED
|
@@ -225,3 +225,19 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
|
|
|
225
225
|
|
|
226
226
|
return Napi::Number::New(env, maxColourDistance);
|
|
227
227
|
}
|
|
228
|
+
|
|
229
|
+
#if defined(__GNUC__)
|
|
230
|
+
// mallctl will be resolved by the runtime linker when jemalloc is being used
|
|
231
|
+
extern "C" {
|
|
232
|
+
int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((weak));
|
|
233
|
+
}
|
|
234
|
+
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
|
|
235
|
+
Napi::Env env = info.Env();
|
|
236
|
+
return Napi::Boolean::New(env, mallctl != nullptr);
|
|
237
|
+
}
|
|
238
|
+
#else
|
|
239
|
+
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
|
|
240
|
+
Napi::Env env = info.Env();
|
|
241
|
+
return Napi::Boolean::New(env, false);
|
|
242
|
+
}
|
|
243
|
+
#endif
|
package/src/utilities.h
CHANGED
|
@@ -24,5 +24,6 @@ Napi::Value simd(const Napi::CallbackInfo& info);
|
|
|
24
24
|
Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
|
|
25
25
|
Napi::Value format(const Napi::CallbackInfo& info);
|
|
26
26
|
Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
|
|
27
|
+
Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
|
|
27
28
|
|
|
28
29
|
#endif // SRC_UTILITIES_H_
|