sharp 0.33.0-alpha.9 → 0.33.0-rc.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 +6 -2
- package/lib/colour.js +4 -6
- package/lib/constructor.js +9 -7
- package/lib/index.d.ts +76 -13
- package/lib/libvips.js +25 -7
- package/lib/output.js +185 -49
- package/lib/resize.js +42 -45
- package/lib/sharp.js +70 -53
- package/lib/utility.js +10 -4
- package/package.json +36 -23
- package/src/binding.gyp +33 -23
- package/src/common.cc +48 -1
- package/src/common.h +18 -3
- package/src/metadata.cc +3 -3
- package/src/operations.cc +35 -18
- package/src/operations.h +3 -3
- package/src/pipeline.cc +64 -54
- package/src/pipeline.h +13 -9
- package/src/stats.cc +3 -3
- package/src/utilities.cc +5 -0
package/src/operations.cc
CHANGED
|
@@ -16,30 +16,44 @@ using vips::VError;
|
|
|
16
16
|
|
|
17
17
|
namespace sharp {
|
|
18
18
|
/*
|
|
19
|
-
* Tint an image using the
|
|
19
|
+
* Tint an image using the provided RGB.
|
|
20
20
|
*/
|
|
21
|
-
VImage Tint(VImage image, double const
|
|
22
|
-
|
|
21
|
+
VImage Tint(VImage image, std::vector<double> const tint) {
|
|
22
|
+
std::vector<double> const tintLab = (VImage::black(1, 1) + tint)
|
|
23
|
+
.colourspace(VIPS_INTERPRETATION_LAB, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB))
|
|
24
|
+
.getpoint(0, 0);
|
|
25
|
+
// LAB identity function
|
|
26
|
+
VImage identityLab = VImage::identity(VImage::option()->set("bands", 3))
|
|
27
|
+
.colourspace(VIPS_INTERPRETATION_LAB, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB));
|
|
28
|
+
// Scale luminance range, 0.0 to 1.0
|
|
29
|
+
VImage l = identityLab[0] / 100;
|
|
30
|
+
// Weighting functions
|
|
31
|
+
VImage weightL = 1.0 - 4.0 * ((l - 0.5) * (l - 0.5));
|
|
32
|
+
VImage weightAB = (weightL * tintLab).extract_band(1, VImage::option()->set("n", 2));
|
|
33
|
+
identityLab = identityLab[0].bandjoin(weightAB);
|
|
34
|
+
// Convert lookup table to sRGB
|
|
35
|
+
VImage lut = identityLab.colourspace(VIPS_INTERPRETATION_sRGB,
|
|
36
|
+
VImage::option()->set("source_space", VIPS_INTERPRETATION_LAB));
|
|
37
|
+
// Original colourspace
|
|
23
38
|
VipsInterpretation typeBeforeTint = image.interpretation();
|
|
24
39
|
if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
|
|
25
40
|
typeBeforeTint = VIPS_INTERPRETATION_sRGB;
|
|
26
41
|
}
|
|
27
|
-
//
|
|
28
|
-
VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0];
|
|
29
|
-
// Create the tinted version by combining the L from the original and the chroma from the tint
|
|
30
|
-
std::vector<double> chroma {a, b};
|
|
31
|
-
VImage tinted = luminance
|
|
32
|
-
.bandjoin(chroma)
|
|
33
|
-
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_LAB))
|
|
34
|
-
.colourspace(typeBeforeTint);
|
|
35
|
-
// Attach original alpha channel, if any
|
|
42
|
+
// Apply lookup table
|
|
36
43
|
if (HasAlpha(image)) {
|
|
37
|
-
// Extract original alpha channel
|
|
38
44
|
VImage alpha = image[image.bands() - 1];
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
image = RemoveAlpha(image)
|
|
46
|
+
.colourspace(VIPS_INTERPRETATION_B_W)
|
|
47
|
+
.maplut(lut)
|
|
48
|
+
.colourspace(typeBeforeTint)
|
|
49
|
+
.bandjoin(alpha);
|
|
50
|
+
} else {
|
|
51
|
+
image = image
|
|
52
|
+
.colourspace(VIPS_INTERPRETATION_B_W)
|
|
53
|
+
.maplut(lut)
|
|
54
|
+
.colourspace(typeBeforeTint);
|
|
41
55
|
}
|
|
42
|
-
return
|
|
56
|
+
return image;
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
/*
|
|
@@ -141,7 +155,7 @@ namespace sharp {
|
|
|
141
155
|
return image.conv(blur);
|
|
142
156
|
} else {
|
|
143
157
|
// Slower, accurate Gaussian blur
|
|
144
|
-
return image.gaussblur(sigma);
|
|
158
|
+
return StaySequential(image, VIPS_ACCESS_SEQUENTIAL).gaussblur(sigma);
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
161
|
|
|
@@ -265,7 +279,7 @@ namespace sharp {
|
|
|
265
279
|
/*
|
|
266
280
|
Trim an image
|
|
267
281
|
*/
|
|
268
|
-
VImage Trim(VImage image, std::vector<double> background, double threshold) {
|
|
282
|
+
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt) {
|
|
269
283
|
if (image.width() < 3 && image.height() < 3) {
|
|
270
284
|
throw VError("Image to trim must be at least 3x3 pixels");
|
|
271
285
|
}
|
|
@@ -287,6 +301,7 @@ namespace sharp {
|
|
|
287
301
|
int left, top, width, height;
|
|
288
302
|
left = image.find_trim(&top, &width, &height, VImage::option()
|
|
289
303
|
->set("background", background)
|
|
304
|
+
->set("line_art", lineArt)
|
|
290
305
|
->set("threshold", threshold));
|
|
291
306
|
if (HasAlpha(image)) {
|
|
292
307
|
// Search alpha channel (A)
|
|
@@ -294,6 +309,7 @@ namespace sharp {
|
|
|
294
309
|
VImage alpha = image[image.bands() - 1];
|
|
295
310
|
leftA = alpha.find_trim(&topA, &widthA, &heightA, VImage::option()
|
|
296
311
|
->set("background", backgroundAlpha)
|
|
312
|
+
->set("line_art", lineArt)
|
|
297
313
|
->set("threshold", threshold));
|
|
298
314
|
if (widthA > 0 && heightA > 0) {
|
|
299
315
|
if (width > 0 && height > 0) {
|
|
@@ -370,6 +386,7 @@ namespace sharp {
|
|
|
370
386
|
pages.reserve(nPages);
|
|
371
387
|
|
|
372
388
|
// Split the image into cropped frames
|
|
389
|
+
image = StaySequential(image, VIPS_ACCESS_SEQUENTIAL);
|
|
373
390
|
for (int i = 0; i < nPages; i++) {
|
|
374
391
|
pages.push_back(
|
|
375
392
|
image.extract_area(left, *pageHeight * i + top, width, height));
|
package/src/operations.h
CHANGED
|
@@ -15,9 +15,9 @@ using vips::VImage;
|
|
|
15
15
|
namespace sharp {
|
|
16
16
|
|
|
17
17
|
/*
|
|
18
|
-
* Tint an image using the
|
|
18
|
+
* Tint an image using the provided RGB.
|
|
19
19
|
*/
|
|
20
|
-
VImage Tint(VImage image, double const
|
|
20
|
+
VImage Tint(VImage image, std::vector<double> const tint);
|
|
21
21
|
|
|
22
22
|
/*
|
|
23
23
|
* Stretch luminance to cover full dynamic range.
|
|
@@ -79,7 +79,7 @@ namespace sharp {
|
|
|
79
79
|
/*
|
|
80
80
|
Trim an image
|
|
81
81
|
*/
|
|
82
|
-
VImage Trim(VImage image, std::vector<double> background, double const
|
|
82
|
+
VImage Trim(VImage image, std::vector<double> background, double threshold, bool const lineArt);
|
|
83
83
|
|
|
84
84
|
/*
|
|
85
85
|
* Linear adjustment (a * in + b)
|
package/src/pipeline.cc
CHANGED
|
@@ -126,10 +126,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Trim
|
|
129
|
-
if (baton->trimThreshold
|
|
129
|
+
if (baton->trimThreshold >= 0.0) {
|
|
130
130
|
MultiPageUnsupported(nPages, "Trim");
|
|
131
131
|
image = sharp::StaySequential(image, access);
|
|
132
|
-
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold);
|
|
132
|
+
image = sharp::Trim(image, baton->trimBackground, baton->trimThreshold, baton->trimLineArt);
|
|
133
133
|
baton->trimOffsetLeft = image.xoffset();
|
|
134
134
|
baton->trimOffsetTop = image.yoffset();
|
|
135
135
|
}
|
|
@@ -182,7 +182,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
182
182
|
// - trimming or pre-resize extract isn't required;
|
|
183
183
|
// - input colourspace is not specified;
|
|
184
184
|
bool const shouldPreShrink = (targetResizeWidth > 0 || targetResizeHeight > 0) &&
|
|
185
|
-
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold
|
|
185
|
+
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold < 0.0 &&
|
|
186
186
|
baton->colourspaceInput == VIPS_INTERPRETATION_LAST && !shouldRotateBefore;
|
|
187
187
|
|
|
188
188
|
if (shouldPreShrink) {
|
|
@@ -315,6 +315,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
// Ensure we're using a device-independent colour space
|
|
318
|
+
std::pair<char*, size_t> inputProfile(nullptr, 0);
|
|
319
|
+
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) && baton->withIccProfile.empty()) {
|
|
320
|
+
// Cache input profile for use with output
|
|
321
|
+
inputProfile = sharp::GetProfile(image);
|
|
322
|
+
}
|
|
318
323
|
char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
|
|
319
324
|
if (
|
|
320
325
|
sharp::HasProfile(image) &&
|
|
@@ -485,9 +490,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
485
490
|
image = sharp::StaySequential(image, access);
|
|
486
491
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
487
492
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
488
|
-
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
|
489
493
|
->set("premultiplied", shouldPremultiplyAlpha)
|
|
490
|
-
#endif
|
|
491
494
|
->set("attention_x", &attention_x)
|
|
492
495
|
->set("attention_y", &attention_y));
|
|
493
496
|
baton->hasCropOffset = true;
|
|
@@ -738,8 +741,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
738
741
|
}
|
|
739
742
|
|
|
740
743
|
// Tint the image
|
|
741
|
-
if (baton->
|
|
742
|
-
image = sharp::Tint(image, baton->
|
|
744
|
+
if (baton->tint[0] >= 0.0) {
|
|
745
|
+
image = sharp::Tint(image, baton->tint);
|
|
743
746
|
}
|
|
744
747
|
|
|
745
748
|
// Remove alpha channel, if any
|
|
@@ -760,7 +763,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
760
763
|
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
|
761
764
|
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
|
762
765
|
// Transform colours from embedded profile to output profile
|
|
763
|
-
if (baton->
|
|
766
|
+
if ((baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) &&
|
|
767
|
+
baton->withIccProfile.empty() && sharp::HasProfile(image)) {
|
|
764
768
|
image = image.icc_transform("srgb", VImage::option()
|
|
765
769
|
->set("embedded", TRUE)
|
|
766
770
|
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
|
@@ -789,27 +793,30 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
789
793
|
}
|
|
790
794
|
|
|
791
795
|
// Apply output ICC profile
|
|
792
|
-
if (baton->
|
|
793
|
-
image = image.icc_transform(
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
796
|
+
if (!baton->withIccProfile.empty()) {
|
|
797
|
+
image = image.icc_transform(const_cast<char*>(baton->withIccProfile.data()), VImage::option()
|
|
798
|
+
->set("input_profile", processingProfile)
|
|
799
|
+
->set("embedded", TRUE)
|
|
800
|
+
->set("depth", sharp::Is16Bit(image.interpretation()) ? 16 : 8)
|
|
801
|
+
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
802
|
+
} else if (baton->keepMetadata & VIPS_FOREIGN_KEEP_ICC) {
|
|
803
|
+
image = sharp::SetProfile(image, inputProfile);
|
|
800
804
|
}
|
|
801
805
|
// Override EXIF Orientation tag
|
|
802
|
-
if (baton->
|
|
806
|
+
if (baton->withMetadataOrientation != -1) {
|
|
803
807
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
|
804
808
|
}
|
|
805
809
|
// Override pixel density
|
|
806
810
|
if (baton->withMetadataDensity > 0) {
|
|
807
811
|
image = sharp::SetDensity(image, baton->withMetadataDensity);
|
|
808
812
|
}
|
|
809
|
-
//
|
|
810
|
-
if (
|
|
813
|
+
// EXIF key/value pairs
|
|
814
|
+
if (baton->keepMetadata & VIPS_FOREIGN_KEEP_EXIF) {
|
|
811
815
|
image = image.copy();
|
|
812
|
-
|
|
816
|
+
if (!baton->withExifMerge) {
|
|
817
|
+
image = sharp::RemoveExif(image);
|
|
818
|
+
}
|
|
819
|
+
for (const auto& s : baton->withExif) {
|
|
813
820
|
image.set(s.first.data(), s.second.data());
|
|
814
821
|
}
|
|
815
822
|
}
|
|
@@ -830,7 +837,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
830
837
|
// Write JPEG to buffer
|
|
831
838
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
832
839
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
|
|
833
|
-
->set("
|
|
840
|
+
->set("keep", baton->keepMetadata)
|
|
834
841
|
->set("Q", baton->jpegQuality)
|
|
835
842
|
->set("interlace", baton->jpegProgressive)
|
|
836
843
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
@@ -872,7 +879,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
872
879
|
// Write PNG to buffer
|
|
873
880
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
874
881
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
875
|
-
->set("
|
|
882
|
+
->set("keep", baton->keepMetadata)
|
|
876
883
|
->set("interlace", baton->pngProgressive)
|
|
877
884
|
->set("compression", baton->pngCompressionLevel)
|
|
878
885
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
@@ -891,7 +898,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
891
898
|
// Write WEBP to buffer
|
|
892
899
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
|
|
893
900
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.webpsave_buffer(VImage::option()
|
|
894
|
-
->set("
|
|
901
|
+
->set("keep", baton->keepMetadata)
|
|
895
902
|
->set("Q", baton->webpQuality)
|
|
896
903
|
->set("lossless", baton->webpLossless)
|
|
897
904
|
->set("near_lossless", baton->webpNearLossless)
|
|
@@ -911,7 +918,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
911
918
|
// Write GIF to buffer
|
|
912
919
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
913
920
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.gifsave_buffer(VImage::option()
|
|
914
|
-
->set("
|
|
921
|
+
->set("keep", baton->keepMetadata)
|
|
915
922
|
->set("bitdepth", baton->gifBitdepth)
|
|
916
923
|
->set("effort", baton->gifEffort)
|
|
917
924
|
->set("reuse", baton->gifReuse)
|
|
@@ -936,10 +943,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
936
943
|
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
937
944
|
}
|
|
938
945
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.tiffsave_buffer(VImage::option()
|
|
939
|
-
->set("
|
|
946
|
+
->set("keep", baton->keepMetadata)
|
|
940
947
|
->set("Q", baton->tiffQuality)
|
|
941
948
|
->set("bitdepth", baton->tiffBitdepth)
|
|
942
949
|
->set("compression", baton->tiffCompression)
|
|
950
|
+
->set("miniswhite", baton->tiffMiniswhite)
|
|
943
951
|
->set("predictor", baton->tiffPredictor)
|
|
944
952
|
->set("pyramid", baton->tiffPyramid)
|
|
945
953
|
->set("tile", baton->tiffTile)
|
|
@@ -959,7 +967,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
959
967
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
|
960
968
|
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
|
961
969
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
962
|
-
->set("
|
|
970
|
+
->set("keep", baton->keepMetadata)
|
|
963
971
|
->set("Q", baton->heifQuality)
|
|
964
972
|
->set("compression", baton->heifCompression)
|
|
965
973
|
->set("effort", baton->heifEffort)
|
|
@@ -991,7 +999,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
991
999
|
// Write JXL to buffer
|
|
992
1000
|
image = sharp::RemoveAnimationProperties(image);
|
|
993
1001
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.jxlsave_buffer(VImage::option()
|
|
994
|
-
->set("
|
|
1002
|
+
->set("keep", baton->keepMetadata)
|
|
995
1003
|
->set("distance", baton->jxlDistance)
|
|
996
1004
|
->set("tier", baton->jxlDecodingTier)
|
|
997
1005
|
->set("effort", baton->jxlEffort)
|
|
@@ -1052,7 +1060,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1052
1060
|
// Write JPEG to file
|
|
1053
1061
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
1054
1062
|
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1055
|
-
->set("
|
|
1063
|
+
->set("keep", baton->keepMetadata)
|
|
1056
1064
|
->set("Q", baton->jpegQuality)
|
|
1057
1065
|
->set("interlace", baton->jpegProgressive)
|
|
1058
1066
|
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
@@ -1082,7 +1090,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1082
1090
|
// Write PNG to file
|
|
1083
1091
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
1084
1092
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1085
|
-
->set("
|
|
1093
|
+
->set("keep", baton->keepMetadata)
|
|
1086
1094
|
->set("interlace", baton->pngProgressive)
|
|
1087
1095
|
->set("compression", baton->pngCompressionLevel)
|
|
1088
1096
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
|
@@ -1097,7 +1105,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1097
1105
|
// Write WEBP to file
|
|
1098
1106
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
|
|
1099
1107
|
image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1100
|
-
->set("
|
|
1108
|
+
->set("keep", baton->keepMetadata)
|
|
1101
1109
|
->set("Q", baton->webpQuality)
|
|
1102
1110
|
->set("lossless", baton->webpLossless)
|
|
1103
1111
|
->set("near_lossless", baton->webpNearLossless)
|
|
@@ -1113,7 +1121,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1113
1121
|
// Write GIF to file
|
|
1114
1122
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
1115
1123
|
image.gifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1116
|
-
->set("
|
|
1124
|
+
->set("keep", baton->keepMetadata)
|
|
1117
1125
|
->set("bitdepth", baton->gifBitdepth)
|
|
1118
1126
|
->set("effort", baton->gifEffort)
|
|
1119
1127
|
->set("reuse", baton->gifReuse)
|
|
@@ -1132,10 +1140,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1132
1140
|
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
1133
1141
|
}
|
|
1134
1142
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1135
|
-
->set("
|
|
1143
|
+
->set("keep", baton->keepMetadata)
|
|
1136
1144
|
->set("Q", baton->tiffQuality)
|
|
1137
1145
|
->set("bitdepth", baton->tiffBitdepth)
|
|
1138
1146
|
->set("compression", baton->tiffCompression)
|
|
1147
|
+
->set("miniswhite", baton->tiffMiniswhite)
|
|
1139
1148
|
->set("predictor", baton->tiffPredictor)
|
|
1140
1149
|
->set("pyramid", baton->tiffPyramid)
|
|
1141
1150
|
->set("tile", baton->tiffTile)
|
|
@@ -1151,7 +1160,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1151
1160
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
|
1152
1161
|
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
|
1153
1162
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1154
|
-
->set("
|
|
1163
|
+
->set("keep", baton->keepMetadata)
|
|
1155
1164
|
->set("Q", baton->heifQuality)
|
|
1156
1165
|
->set("compression", baton->heifCompression)
|
|
1157
1166
|
->set("effort", baton->heifEffort)
|
|
@@ -1165,7 +1174,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1165
1174
|
// Write JXL to file
|
|
1166
1175
|
image = sharp::RemoveAnimationProperties(image);
|
|
1167
1176
|
image.jxlsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1168
|
-
->set("
|
|
1177
|
+
->set("keep", baton->keepMetadata)
|
|
1169
1178
|
->set("distance", baton->jxlDistance)
|
|
1170
1179
|
->set("tier", baton->jxlDecodingTier)
|
|
1171
1180
|
->set("effort", baton->jxlEffort)
|
|
@@ -1187,7 +1196,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1187
1196
|
(willMatchInput && inputImageType == sharp::ImageType::VIPS)) {
|
|
1188
1197
|
// Write V to file
|
|
1189
1198
|
image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1190
|
-
->set("
|
|
1199
|
+
->set("keep", baton->keepMetadata));
|
|
1191
1200
|
baton->formatOut = "v";
|
|
1192
1201
|
} else {
|
|
1193
1202
|
// Unsupported output format
|
|
@@ -1215,7 +1224,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1215
1224
|
// Handle warnings
|
|
1216
1225
|
std::string warning = sharp::VipsWarningPop();
|
|
1217
1226
|
while (!warning.empty()) {
|
|
1218
|
-
debuglog.
|
|
1227
|
+
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
|
|
1219
1228
|
warning = sharp::VipsWarningPop();
|
|
1220
1229
|
}
|
|
1221
1230
|
|
|
@@ -1248,11 +1257,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1248
1257
|
info.Set("attentionX", static_cast<int32_t>(baton->attentionX));
|
|
1249
1258
|
info.Set("attentionY", static_cast<int32_t>(baton->attentionY));
|
|
1250
1259
|
}
|
|
1251
|
-
if (baton->trimThreshold
|
|
1260
|
+
if (baton->trimThreshold >= 0.0) {
|
|
1252
1261
|
info.Set("trimOffsetLeft", static_cast<int32_t>(baton->trimOffsetLeft));
|
|
1253
1262
|
info.Set("trimOffsetTop", static_cast<int32_t>(baton->trimOffsetTop));
|
|
1254
1263
|
}
|
|
1255
|
-
|
|
1256
1264
|
if (baton->input->textAutofitDpi) {
|
|
1257
1265
|
info.Set("textAutofitDpi", static_cast<uint32_t>(baton->input->textAutofitDpi));
|
|
1258
1266
|
}
|
|
@@ -1263,17 +1271,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1263
1271
|
// Pass ownership of output data to Buffer instance
|
|
1264
1272
|
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
|
1265
1273
|
baton->bufferOutLength, sharp::FreeCallback);
|
|
1266
|
-
Callback().
|
|
1274
|
+
Callback().Call(Receiver().Value(), { env.Null(), data, info });
|
|
1267
1275
|
} else {
|
|
1268
1276
|
// Add file size to info
|
|
1269
1277
|
struct STAT64_STRUCT st;
|
|
1270
1278
|
if (STAT64_FUNCTION(baton->fileOut.data(), &st) == 0) {
|
|
1271
1279
|
info.Set("size", static_cast<uint32_t>(st.st_size));
|
|
1272
1280
|
}
|
|
1273
|
-
Callback().
|
|
1281
|
+
Callback().Call(Receiver().Value(), { env.Null(), info });
|
|
1274
1282
|
}
|
|
1275
1283
|
} else {
|
|
1276
|
-
Callback().
|
|
1284
|
+
Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
|
|
1277
1285
|
}
|
|
1278
1286
|
|
|
1279
1287
|
// Delete baton
|
|
@@ -1291,7 +1299,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1291
1299
|
// Decrement processing task counter
|
|
1292
1300
|
sharp::counterProcess--;
|
|
1293
1301
|
Napi::Number queueLength = Napi::Number::New(env, static_cast<int>(sharp::counterQueue));
|
|
1294
|
-
queueListener.
|
|
1302
|
+
queueListener.Call(Receiver().Value(), { queueLength });
|
|
1295
1303
|
}
|
|
1296
1304
|
|
|
1297
1305
|
private:
|
|
@@ -1402,7 +1410,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1402
1410
|
suffix = AssembleSuffixString(extname, options);
|
|
1403
1411
|
}
|
|
1404
1412
|
vips::VOption *options = VImage::option()
|
|
1405
|
-
->set("
|
|
1413
|
+
->set("keep", baton->keepMetadata)
|
|
1406
1414
|
->set("tile_size", baton->tileSize)
|
|
1407
1415
|
->set("overlap", baton->tileOverlap)
|
|
1408
1416
|
->set("container", baton->tileContainer)
|
|
@@ -1519,6 +1527,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1519
1527
|
baton->thresholdGrayscale = sharp::AttrAsBool(options, "thresholdGrayscale");
|
|
1520
1528
|
baton->trimBackground = sharp::AttrAsVectorOfDouble(options, "trimBackground");
|
|
1521
1529
|
baton->trimThreshold = sharp::AttrAsDouble(options, "trimThreshold");
|
|
1530
|
+
baton->trimLineArt = sharp::AttrAsBool(options, "trimLineArt");
|
|
1522
1531
|
baton->gamma = sharp::AttrAsDouble(options, "gamma");
|
|
1523
1532
|
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
|
|
1524
1533
|
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
|
|
@@ -1527,8 +1536,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1527
1536
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
|
1528
1537
|
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
|
|
1529
1538
|
baton->normaliseUpper = sharp::AttrAsUint32(options, "normaliseUpper");
|
|
1530
|
-
baton->
|
|
1531
|
-
baton->tintB = sharp::AttrAsDouble(options, "tintB");
|
|
1539
|
+
baton->tint = sharp::AttrAsVectorOfDouble(options, "tint");
|
|
1532
1540
|
baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
|
|
1533
1541
|
baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
|
|
1534
1542
|
baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
|
|
@@ -1594,18 +1602,19 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1594
1602
|
// Output
|
|
1595
1603
|
baton->formatOut = sharp::AttrAsStr(options, "formatOut");
|
|
1596
1604
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
|
1597
|
-
baton->
|
|
1605
|
+
baton->keepMetadata = sharp::AttrAsUint32(options, "keepMetadata");
|
|
1598
1606
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
|
1599
1607
|
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
|
|
1600
|
-
baton->
|
|
1601
|
-
Napi::Object
|
|
1602
|
-
Napi::Array
|
|
1603
|
-
for (unsigned int i = 0; i <
|
|
1604
|
-
std::string k = sharp::AttrAsStr(
|
|
1605
|
-
if (
|
|
1606
|
-
baton->
|
|
1608
|
+
baton->withIccProfile = sharp::AttrAsStr(options, "withIccProfile");
|
|
1609
|
+
Napi::Object withExif = options.Get("withExif").As<Napi::Object>();
|
|
1610
|
+
Napi::Array withExifKeys = withExif.GetPropertyNames();
|
|
1611
|
+
for (unsigned int i = 0; i < withExifKeys.Length(); i++) {
|
|
1612
|
+
std::string k = sharp::AttrAsStr(withExifKeys, i);
|
|
1613
|
+
if (withExif.HasOwnProperty(k)) {
|
|
1614
|
+
baton->withExif.insert(std::make_pair(k, sharp::AttrAsStr(withExif, k)));
|
|
1607
1615
|
}
|
|
1608
1616
|
}
|
|
1617
|
+
baton->withExifMerge = sharp::AttrAsBool(options, "withExifMerge");
|
|
1609
1618
|
baton->timeoutSeconds = sharp::AttrAsUint32(options, "timeoutSeconds");
|
|
1610
1619
|
// Format-specific
|
|
1611
1620
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
|
@@ -1647,6 +1656,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1647
1656
|
baton->gifProgressive = sharp::AttrAsBool(options, "gifProgressive");
|
|
1648
1657
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1649
1658
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1659
|
+
baton->tiffMiniswhite = sharp::AttrAsBool(options, "tiffMiniswhite");
|
|
1650
1660
|
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
1651
1661
|
baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
|
|
1652
1662
|
baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
|
|
@@ -1708,7 +1718,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1708
1718
|
|
|
1709
1719
|
// Increment queued task counter
|
|
1710
1720
|
Napi::Number queueLength = Napi::Number::New(info.Env(), static_cast<int>(++sharp::counterQueue));
|
|
1711
|
-
queueListener.
|
|
1721
|
+
queueListener.Call(info.This(), { queueLength });
|
|
1712
1722
|
|
|
1713
1723
|
return info.Env().Undefined();
|
|
1714
1724
|
}
|
package/src/pipeline.h
CHANGED
|
@@ -69,8 +69,7 @@ struct PipelineBaton {
|
|
|
69
69
|
bool premultiplied;
|
|
70
70
|
bool tileCentre;
|
|
71
71
|
bool fastShrinkOnLoad;
|
|
72
|
-
double
|
|
73
|
-
double tintB;
|
|
72
|
+
std::vector<double> tint;
|
|
74
73
|
bool flatten;
|
|
75
74
|
std::vector<double> flattenBackground;
|
|
76
75
|
bool unflatten;
|
|
@@ -92,6 +91,7 @@ struct PipelineBaton {
|
|
|
92
91
|
bool thresholdGrayscale;
|
|
93
92
|
std::vector<double> trimBackground;
|
|
94
93
|
double trimThreshold;
|
|
94
|
+
bool trimLineArt;
|
|
95
95
|
int trimOffsetLeft;
|
|
96
96
|
int trimOffsetTop;
|
|
97
97
|
std::vector<double> linearA;
|
|
@@ -169,6 +169,7 @@ struct PipelineBaton {
|
|
|
169
169
|
VipsForeignTiffPredictor tiffPredictor;
|
|
170
170
|
bool tiffPyramid;
|
|
171
171
|
int tiffBitdepth;
|
|
172
|
+
bool tiffMiniswhite;
|
|
172
173
|
bool tiffTile;
|
|
173
174
|
int tiffTileHeight;
|
|
174
175
|
int tiffTileWidth;
|
|
@@ -186,11 +187,12 @@ struct PipelineBaton {
|
|
|
186
187
|
bool jxlLossless;
|
|
187
188
|
VipsBandFormat rawDepth;
|
|
188
189
|
std::string err;
|
|
189
|
-
|
|
190
|
+
int keepMetadata;
|
|
190
191
|
int withMetadataOrientation;
|
|
191
192
|
double withMetadataDensity;
|
|
192
|
-
std::string
|
|
193
|
-
std::unordered_map<std::string, std::string>
|
|
193
|
+
std::string withIccProfile;
|
|
194
|
+
std::unordered_map<std::string, std::string> withExif;
|
|
195
|
+
bool withExifMerge;
|
|
194
196
|
int timeoutSeconds;
|
|
195
197
|
std::unique_ptr<double[]> convKernel;
|
|
196
198
|
int convKernelWidth;
|
|
@@ -237,8 +239,7 @@ struct PipelineBaton {
|
|
|
237
239
|
attentionX(0),
|
|
238
240
|
attentionY(0),
|
|
239
241
|
premultiplied(false),
|
|
240
|
-
|
|
241
|
-
tintB(128.0),
|
|
242
|
+
tint{ -1.0, 0.0, 0.0, 0.0 },
|
|
242
243
|
flatten(false),
|
|
243
244
|
flattenBackground{ 0.0, 0.0, 0.0 },
|
|
244
245
|
unflatten(false),
|
|
@@ -259,7 +260,8 @@ struct PipelineBaton {
|
|
|
259
260
|
threshold(0),
|
|
260
261
|
thresholdGrayscale(true),
|
|
261
262
|
trimBackground{},
|
|
262
|
-
trimThreshold(
|
|
263
|
+
trimThreshold(-1.0),
|
|
264
|
+
trimLineArt(false),
|
|
263
265
|
trimOffsetLeft(0),
|
|
264
266
|
trimOffsetTop(0),
|
|
265
267
|
linearA{},
|
|
@@ -335,6 +337,7 @@ struct PipelineBaton {
|
|
|
335
337
|
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
|
336
338
|
tiffPyramid(false),
|
|
337
339
|
tiffBitdepth(8),
|
|
340
|
+
tiffMiniswhite(false),
|
|
338
341
|
tiffTile(false),
|
|
339
342
|
tiffTileHeight(256),
|
|
340
343
|
tiffTileWidth(256),
|
|
@@ -351,9 +354,10 @@ struct PipelineBaton {
|
|
|
351
354
|
jxlEffort(7),
|
|
352
355
|
jxlLossless(false),
|
|
353
356
|
rawDepth(VIPS_FORMAT_UCHAR),
|
|
354
|
-
|
|
357
|
+
keepMetadata(0),
|
|
355
358
|
withMetadataOrientation(-1),
|
|
356
359
|
withMetadataDensity(0.0),
|
|
360
|
+
withExifMerge(true),
|
|
357
361
|
timeoutSeconds(0),
|
|
358
362
|
convKernelWidth(0),
|
|
359
363
|
convKernelHeight(0),
|
package/src/stats.cc
CHANGED
|
@@ -106,7 +106,7 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
106
106
|
// Handle warnings
|
|
107
107
|
std::string warning = sharp::VipsWarningPop();
|
|
108
108
|
while (!warning.empty()) {
|
|
109
|
-
debuglog.
|
|
109
|
+
debuglog.Call(Receiver().Value(), { Napi::String::New(env, warning) });
|
|
110
110
|
warning = sharp::VipsWarningPop();
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -141,9 +141,9 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
141
141
|
dominant.Set("g", baton->dominantGreen);
|
|
142
142
|
dominant.Set("b", baton->dominantBlue);
|
|
143
143
|
info.Set("dominant", dominant);
|
|
144
|
-
Callback().
|
|
144
|
+
Callback().Call(Receiver().Value(), { env.Null(), info });
|
|
145
145
|
} else {
|
|
146
|
-
Callback().
|
|
146
|
+
Callback().Call(Receiver().Value(), { Napi::Error::New(env, sharp::TrimEnd(baton->err)).Value() });
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
delete baton->input;
|
package/src/utilities.cc
CHANGED
|
@@ -102,6 +102,11 @@ Napi::Value libvipsVersion(const Napi::CallbackInfo& info) {
|
|
|
102
102
|
version.Set("isGlobal", Napi::Boolean::New(env, true));
|
|
103
103
|
#else
|
|
104
104
|
version.Set("isGlobal", Napi::Boolean::New(env, false));
|
|
105
|
+
#endif
|
|
106
|
+
#ifdef __EMSCRIPTEN__
|
|
107
|
+
version.Set("isWasm", Napi::Boolean::New(env, true));
|
|
108
|
+
#else
|
|
109
|
+
version.Set("isWasm", Napi::Boolean::New(env, false));
|
|
105
110
|
#endif
|
|
106
111
|
return version;
|
|
107
112
|
}
|