sharp 0.25.1 → 0.26.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.
@@ -1,5 +1,5 @@
1
1
  // bodies for vips operations
2
- // Wed 01 Jan 2020 12:22:12 PM CET
2
+ // Sun 5 Jul 22:36:37 BST 2020
3
3
  // this file is generated automatically, do not edit!
4
4
 
5
5
  VImage VImage::CMC2LCh( VOption *options ) const
@@ -754,6 +754,18 @@ VImage VImage::csvload( const char *filename, VOption *options )
754
754
  return( out );
755
755
  }
756
756
 
757
+ VImage VImage::csvload_source( VSource source, VOption *options )
758
+ {
759
+ VImage out;
760
+
761
+ call( "csvload_source",
762
+ (options ? options : VImage::option())->
763
+ set( "out", &out )->
764
+ set( "source", source ) );
765
+
766
+ return( out );
767
+ }
768
+
757
769
  void VImage::csvsave( const char *filename, VOption *options ) const
758
770
  {
759
771
  call( "csvsave",
@@ -762,6 +774,14 @@ void VImage::csvsave( const char *filename, VOption *options ) const
762
774
  set( "filename", filename ) );
763
775
  }
764
776
 
777
+ void VImage::csvsave_target( VTarget target, VOption *options ) const
778
+ {
779
+ call( "csvsave_target",
780
+ (options ? options : VImage::option())->
781
+ set( "in", *this )->
782
+ set( "target", target ) );
783
+ }
784
+
765
785
  VImage VImage::dE00( VImage right, VOption *options ) const
766
786
  {
767
787
  VImage out;
@@ -1218,6 +1238,18 @@ VImage VImage::gifload_buffer( VipsBlob *buffer, VOption *options )
1218
1238
  return( out );
1219
1239
  }
1220
1240
 
1241
+ VImage VImage::gifload_source( VSource source, VOption *options )
1242
+ {
1243
+ VImage out;
1244
+
1245
+ call( "gifload_source",
1246
+ (options ? options : VImage::option())->
1247
+ set( "out", &out )->
1248
+ set( "source", source ) );
1249
+
1250
+ return( out );
1251
+ }
1252
+
1221
1253
  VImage VImage::globalbalance( VOption *options ) const
1222
1254
  {
1223
1255
  VImage out;
@@ -1297,6 +1329,18 @@ VImage VImage::heifload_buffer( VipsBlob *buffer, VOption *options )
1297
1329
  return( out );
1298
1330
  }
1299
1331
 
1332
+ VImage VImage::heifload_source( VSource source, VOption *options )
1333
+ {
1334
+ VImage out;
1335
+
1336
+ call( "heifload_source",
1337
+ (options ? options : VImage::option())->
1338
+ set( "out", &out )->
1339
+ set( "source", source ) );
1340
+
1341
+ return( out );
1342
+ }
1343
+
1300
1344
  void VImage::heifsave( const char *filename, VOption *options ) const
1301
1345
  {
1302
1346
  call( "heifsave",
@@ -1317,6 +1361,14 @@ VipsBlob *VImage::heifsave_buffer( VOption *options ) const
1317
1361
  return( buffer );
1318
1362
  }
1319
1363
 
1364
+ void VImage::heifsave_target( VTarget target, VOption *options ) const
1365
+ {
1366
+ call( "heifsave_target",
1367
+ (options ? options : VImage::option())->
1368
+ set( "in", *this )->
1369
+ set( "target", target ) );
1370
+ }
1371
+
1320
1372
  VImage VImage::hist_cum( VOption *options ) const
1321
1373
  {
1322
1374
  VImage out;
@@ -2028,6 +2080,18 @@ VImage VImage::matload( const char *filename, VOption *options )
2028
2080
  return( out );
2029
2081
  }
2030
2082
 
2083
+ VImage VImage::matrixinvert( VOption *options ) const
2084
+ {
2085
+ VImage out;
2086
+
2087
+ call( "matrixinvert",
2088
+ (options ? options : VImage::option())->
2089
+ set( "in", *this )->
2090
+ set( "out", &out ) );
2091
+
2092
+ return( out );
2093
+ }
2094
+
2031
2095
  VImage VImage::matrixload( const char *filename, VOption *options )
2032
2096
  {
2033
2097
  VImage out;
@@ -2040,6 +2104,18 @@ VImage VImage::matrixload( const char *filename, VOption *options )
2040
2104
  return( out );
2041
2105
  }
2042
2106
 
2107
+ VImage VImage::matrixload_source( VSource source, VOption *options )
2108
+ {
2109
+ VImage out;
2110
+
2111
+ call( "matrixload_source",
2112
+ (options ? options : VImage::option())->
2113
+ set( "out", &out )->
2114
+ set( "source", source ) );
2115
+
2116
+ return( out );
2117
+ }
2118
+
2043
2119
  void VImage::matrixprint( VOption *options ) const
2044
2120
  {
2045
2121
  call( "matrixprint",
@@ -2055,6 +2131,14 @@ void VImage::matrixsave( const char *filename, VOption *options ) const
2055
2131
  set( "filename", filename ) );
2056
2132
  }
2057
2133
 
2134
+ void VImage::matrixsave_target( VTarget target, VOption *options ) const
2135
+ {
2136
+ call( "matrixsave_target",
2137
+ (options ? options : VImage::option())->
2138
+ set( "in", *this )->
2139
+ set( "target", target ) );
2140
+ }
2141
+
2058
2142
  double VImage::max( VOption *options ) const
2059
2143
  {
2060
2144
  double out;
@@ -2256,6 +2340,18 @@ VImage VImage::pdfload_buffer( VipsBlob *buffer, VOption *options )
2256
2340
  return( out );
2257
2341
  }
2258
2342
 
2343
+ VImage VImage::pdfload_source( VSource source, VOption *options )
2344
+ {
2345
+ VImage out;
2346
+
2347
+ call( "pdfload_source",
2348
+ (options ? options : VImage::option())->
2349
+ set( "out", &out )->
2350
+ set( "source", source ) );
2351
+
2352
+ return( out );
2353
+ }
2354
+
2259
2355
  int VImage::percent( double percent, VOption *options ) const
2260
2356
  {
2261
2357
  int threshold;
@@ -2371,6 +2467,18 @@ VImage VImage::ppmload( const char *filename, VOption *options )
2371
2467
  return( out );
2372
2468
  }
2373
2469
 
2470
+ VImage VImage::ppmload_source( VSource source, VOption *options )
2471
+ {
2472
+ VImage out;
2473
+
2474
+ call( "ppmload_source",
2475
+ (options ? options : VImage::option())->
2476
+ set( "out", &out )->
2477
+ set( "source", source ) );
2478
+
2479
+ return( out );
2480
+ }
2481
+
2374
2482
  void VImage::ppmsave( const char *filename, VOption *options ) const
2375
2483
  {
2376
2484
  call( "ppmsave",
@@ -2379,6 +2487,14 @@ void VImage::ppmsave( const char *filename, VOption *options ) const
2379
2487
  set( "filename", filename ) );
2380
2488
  }
2381
2489
 
2490
+ void VImage::ppmsave_target( VTarget target, VOption *options ) const
2491
+ {
2492
+ call( "ppmsave_target",
2493
+ (options ? options : VImage::option())->
2494
+ set( "in", *this )->
2495
+ set( "target", target ) );
2496
+ }
2497
+
2382
2498
  VImage VImage::premultiply( VOption *options ) const
2383
2499
  {
2384
2500
  VImage out;
package/src/metadata.cc CHANGED
@@ -74,6 +74,15 @@ 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("openslide.level-count") == VIPS_TYPE_REF_STRING) {
78
+ int const levels = std::stoi(image.get_string("openslide.level-count"));
79
+ for (int l = 0; l < levels; l++) {
80
+ std::string prefix = "openslide.level[" + std::to_string(l) + "].";
81
+ int const width = std::stoi(image.get_string((prefix + "width").data()));
82
+ int const height = std::stoi(image.get_string((prefix + "height").data()));
83
+ baton->levels.push_back(std::pair<int, int>(width, height));
84
+ }
85
+ }
77
86
  baton->hasProfile = sharp::HasProfile(image);
78
87
  // Derived attributes
79
88
  baton->hasAlpha = sharp::HasAlpha(image);
@@ -177,6 +186,17 @@ class MetadataWorker : public Napi::AsyncWorker {
177
186
  if (baton->pagePrimary > -1) {
178
187
  info.Set("pagePrimary", baton->pagePrimary);
179
188
  }
189
+ if (!baton->levels.empty()) {
190
+ int i = 0;
191
+ Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
192
+ for (std::pair<int, int> const &l : baton->levels) {
193
+ Napi::Object level = Napi::Object::New(env);
194
+ level.Set("width", l.first);
195
+ level.Set("height", l.second);
196
+ levels.Set(i++, level);
197
+ }
198
+ info.Set("levels", levels);
199
+ }
180
200
  info.Set("hasProfile", baton->hasProfile);
181
201
  info.Set("hasAlpha", baton->hasAlpha);
182
202
  if (baton->orientation > 0) {
@@ -229,6 +249,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
229
249
  // Join queue for worker thread
230
250
  Napi::Function callback = info[1].As<Napi::Function>();
231
251
  MetadataWorker *worker = new MetadataWorker(callback, baton, debuglog);
252
+ worker->Receiver().Set("options", options);
232
253
  worker->Queue();
233
254
 
234
255
  // Increment queued task counter
package/src/metadata.h CHANGED
@@ -39,6 +39,7 @@ struct MetadataBaton {
39
39
  int loop;
40
40
  std::vector<int> delay;
41
41
  int pagePrimary;
42
+ std::vector<std::pair<int, int>> levels;
42
43
  bool hasProfile;
43
44
  bool hasAlpha;
44
45
  int orientation;
package/src/operations.cc CHANGED
@@ -27,29 +27,6 @@ using vips::VImage;
27
27
  using vips::VError;
28
28
 
29
29
  namespace sharp {
30
-
31
- /*
32
- Removes alpha channel, if any.
33
- */
34
- VImage RemoveAlpha(VImage image) {
35
- if (HasAlpha(image)) {
36
- image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
37
- }
38
- return image;
39
- }
40
-
41
- /*
42
- Ensures alpha channel, if missing.
43
- */
44
- VImage EnsureAlpha(VImage image) {
45
- if (!HasAlpha(image)) {
46
- std::vector<double> alpha;
47
- alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
48
- image = image.bandjoin_const(alpha);
49
- }
50
- return image;
51
- }
52
-
53
30
  /*
54
31
  * Tint an image using the specified chroma, preserving the original image luminance
55
32
  */
package/src/operations.h CHANGED
@@ -25,16 +25,6 @@ using vips::VImage;
25
25
 
26
26
  namespace sharp {
27
27
 
28
- /*
29
- Removes alpha channel, if any.
30
- */
31
- VImage RemoveAlpha(VImage image);
32
-
33
- /*
34
- Ensures alpha channel, if missing.
35
- */
36
- VImage EnsureAlpha(VImage image);
37
-
38
28
  /*
39
29
  * Tint an image using the specified chroma, preserving the original image luminance
40
30
  */
package/src/pipeline.cc CHANGED
@@ -645,8 +645,12 @@ class PipelineWorker : public Napi::AsyncWorker {
645
645
  // Extract an image channel (aka vips band)
646
646
  if (baton->extractChannel > -1) {
647
647
  if (baton->extractChannel >= image.bands()) {
648
- (baton->err).append("Cannot extract channel from image. Too few channels in image.");
649
- return Error();
648
+ if (baton->extractChannel == 3 && sharp::HasAlpha(image)) {
649
+ baton->extractChannel = image.bands() - 1;
650
+ } else {
651
+ (baton->err).append("Cannot extract channel from image. Too few channels in image.");
652
+ return Error();
653
+ }
650
654
  }
651
655
  VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation())
652
656
  ? VIPS_INTERPRETATION_GREY16
@@ -680,6 +684,15 @@ class PipelineWorker : public Napi::AsyncWorker {
680
684
  }
681
685
  }
682
686
 
687
+ // Apply output ICC profile
688
+ if (!baton->withMetadataIcc.empty()) {
689
+ image = image.icc_transform(
690
+ const_cast<char*>(baton->withMetadataIcc.data()),
691
+ VImage::option()
692
+ ->set("input_profile", "srgb")
693
+ ->set("intent", VIPS_INTENT_PERCEPTUAL));
694
+ }
695
+
683
696
  // Override EXIF Orientation tag
684
697
  if (baton->withMetadata && baton->withMetadataOrientation != -1) {
685
698
  image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
@@ -689,6 +702,16 @@ class PipelineWorker : public Napi::AsyncWorker {
689
702
  baton->channels = image.bands();
690
703
  baton->width = image.width();
691
704
  baton->height = image.height();
705
+
706
+ bool const supportsGifOutput = vips_type_find("VipsOperation", "magicksave") != 0 &&
707
+ vips_type_find("VipsOperation", "magicksave_buffer") != 0;
708
+
709
+ image = sharp::SetAnimationProperties(
710
+ image,
711
+ baton->pageHeight,
712
+ baton->delay,
713
+ baton->loop);
714
+
692
715
  // Output
693
716
  if (baton->fileOut.empty()) {
694
717
  // Buffer output
@@ -699,7 +722,9 @@ class PipelineWorker : public Napi::AsyncWorker {
699
722
  ->set("strip", !baton->withMetadata)
700
723
  ->set("Q", baton->jpegQuality)
701
724
  ->set("interlace", baton->jpegProgressive)
702
- ->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
725
+ ->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
726
+ ? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
727
+ : VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
703
728
  ->set("trellis_quant", baton->jpegTrellisQuantisation)
704
729
  ->set("quant_table", baton->jpegQuantisationTable)
705
730
  ->set("overshoot_deringing", baton->jpegOvershootDeringing)
@@ -716,8 +741,8 @@ class PipelineWorker : public Napi::AsyncWorker {
716
741
  baton->channels = std::min(baton->channels, 3);
717
742
  }
718
743
  } else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
719
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
720
- inputImageType == sharp::ImageType::SVG))) {
744
+ (inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
745
+ inputImageType == sharp::ImageType::SVG))) {
721
746
  // Write PNG to buffer
722
747
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
723
748
  VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
@@ -751,6 +776,18 @@ class PipelineWorker : public Napi::AsyncWorker {
751
776
  area->free_fn = nullptr;
752
777
  vips_area_unref(area);
753
778
  baton->formatOut = "webp";
779
+ } else if (baton->formatOut == "gif" ||
780
+ (baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
781
+ // Write GIF to buffer
782
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
783
+ VipsArea *area = VIPS_AREA(image.magicksave_buffer(VImage::option()
784
+ ->set("strip", !baton->withMetadata)
785
+ ->set("format", "gif")));
786
+ baton->bufferOut = static_cast<char*>(area->data);
787
+ baton->bufferOutLength = area->length;
788
+ area->free_fn = nullptr;
789
+ vips_area_unref(area);
790
+ baton->formatOut = "gif";
754
791
  } else if (baton->formatOut == "tiff" ||
755
792
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
756
793
  // Write TIFF to buffer
@@ -765,7 +802,7 @@ class PipelineWorker : public Napi::AsyncWorker {
765
802
  VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
766
803
  ->set("strip", !baton->withMetadata)
767
804
  ->set("Q", baton->tiffQuality)
768
- ->set("squash", baton->tiffSquash)
805
+ ->set("bitdepth", baton->tiffBitdepth)
769
806
  ->set("compression", baton->tiffCompression)
770
807
  ->set("predictor", baton->tiffPredictor)
771
808
  ->set("pyramid", baton->tiffPyramid)
@@ -826,13 +863,16 @@ class PipelineWorker : public Napi::AsyncWorker {
826
863
  bool const isJpeg = sharp::IsJpeg(baton->fileOut);
827
864
  bool const isPng = sharp::IsPng(baton->fileOut);
828
865
  bool const isWebp = sharp::IsWebp(baton->fileOut);
866
+ bool const isGif = sharp::IsGif(baton->fileOut);
829
867
  bool const isTiff = sharp::IsTiff(baton->fileOut);
830
868
  bool const isHeif = sharp::IsHeif(baton->fileOut);
831
869
  bool const isDz = sharp::IsDz(baton->fileOut);
832
870
  bool const isDzZip = sharp::IsDzZip(baton->fileOut);
833
871
  bool const isV = sharp::IsV(baton->fileOut);
834
872
  bool const mightMatchInput = baton->formatOut == "input";
835
- bool const willMatchInput = mightMatchInput && !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV);
873
+ bool const willMatchInput = mightMatchInput &&
874
+ !(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
875
+
836
876
  if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
837
877
  (willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
838
878
  // Write JPEG to file
@@ -841,7 +881,9 @@ class PipelineWorker : public Napi::AsyncWorker {
841
881
  ->set("strip", !baton->withMetadata)
842
882
  ->set("Q", baton->jpegQuality)
843
883
  ->set("interlace", baton->jpegProgressive)
844
- ->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
884
+ ->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
885
+ ? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
886
+ : VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
845
887
  ->set("trellis_quant", baton->jpegTrellisQuantisation)
846
888
  ->set("quant_table", baton->jpegQuantisationTable)
847
889
  ->set("overshoot_deringing", baton->jpegOvershootDeringing)
@@ -850,8 +892,8 @@ class PipelineWorker : public Napi::AsyncWorker {
850
892
  baton->formatOut = "jpeg";
851
893
  baton->channels = std::min(baton->channels, 3);
852
894
  } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
853
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
854
- inputImageType == sharp::ImageType::SVG))) {
895
+ (inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
896
+ inputImageType == sharp::ImageType::SVG))) {
855
897
  // Write PNG to file
856
898
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
857
899
  image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
@@ -877,6 +919,14 @@ class PipelineWorker : public Napi::AsyncWorker {
877
919
  ->set("reduction_effort", baton->webpReductionEffort)
878
920
  ->set("alpha_q", baton->webpAlphaQuality));
879
921
  baton->formatOut = "webp";
922
+ } else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
923
+ (willMatchInput && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
924
+ // Write GIF to file
925
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
926
+ image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
927
+ ->set("strip", !baton->withMetadata)
928
+ ->set("format", "gif"));
929
+ baton->formatOut = "gif";
880
930
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
881
931
  (willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
882
932
  // Write TIFF to file
@@ -887,7 +937,7 @@ class PipelineWorker : public Napi::AsyncWorker {
887
937
  image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
888
938
  ->set("strip", !baton->withMetadata)
889
939
  ->set("Q", baton->tiffQuality)
890
- ->set("squash", baton->tiffSquash)
940
+ ->set("bitdepth", baton->tiffBitdepth)
891
941
  ->set("compression", baton->tiffCompression)
892
942
  ->set("predictor", baton->tiffPredictor)
893
943
  ->set("pyramid", baton->tiffPyramid)
@@ -933,19 +983,17 @@ class PipelineWorker : public Napi::AsyncWorker {
933
983
  };
934
984
  suffix = AssembleSuffixString(".webp", options);
935
985
  } else {
936
- std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE
937
- || baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY
938
- ? ".jpg" : ".jpeg";
939
986
  std::vector<std::pair<std::string, std::string>> options {
940
987
  {"Q", std::to_string(baton->jpegQuality)},
941
988
  {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
942
- {"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"},
989
+ {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
943
990
  {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
944
991
  {"quant_table", std::to_string(baton->jpegQuantisationTable)},
945
992
  {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
946
993
  {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
947
994
  {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
948
995
  };
996
+ std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
949
997
  suffix = AssembleSuffixString(extname, options);
950
998
  }
951
999
  // Remove alpha channel from tile background if image does not contain an alpha channel
@@ -1280,6 +1328,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1280
1328
  baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1281
1329
  baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1282
1330
  baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1331
+ baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
1283
1332
  // Format-specific
1284
1333
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1285
1334
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1304,7 +1353,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1304
1353
  baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
1305
1354
  baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1306
1355
  baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1307
- baton->tiffSquash = sharp::AttrAsBool(options, "tiffSquash");
1356
+ baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
1308
1357
  baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
1309
1358
  baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
1310
1359
  baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
@@ -1322,6 +1371,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1322
1371
  baton->heifCompression = static_cast<VipsForeignHeifCompression>(
1323
1372
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1324
1373
  sharp::AttrAsStr(options, "heifCompression").data()));
1374
+
1375
+ // Animated output
1376
+ if (sharp::HasAttr(options, "pageHeight")) {
1377
+ baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
1378
+ }
1379
+ if (sharp::HasAttr(options, "loop")) {
1380
+ baton->loop = sharp::AttrAsUint32(options, "loop");
1381
+ }
1382
+ if (sharp::HasAttr(options, "delay")) {
1383
+ baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1384
+ }
1385
+
1325
1386
  // Tile output
1326
1387
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1327
1388
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
@@ -1362,6 +1423,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1362
1423
  // Join queue for worker thread
1363
1424
  Napi::Function callback = info[1].As<Napi::Function>();
1364
1425
  PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
1426
+ worker->Receiver().Set("options", options);
1365
1427
  worker->Queue();
1366
1428
 
1367
1429
  // Increment queued task counter
package/src/pipeline.h CHANGED
@@ -143,7 +143,7 @@ struct PipelineBaton {
143
143
  VipsForeignTiffCompression tiffCompression;
144
144
  VipsForeignTiffPredictor tiffPredictor;
145
145
  bool tiffPyramid;
146
- bool tiffSquash;
146
+ int tiffBitdepth;
147
147
  bool tiffTile;
148
148
  int tiffTileHeight;
149
149
  int tiffTileWidth;
@@ -155,6 +155,7 @@ struct PipelineBaton {
155
155
  std::string err;
156
156
  bool withMetadata;
157
157
  int withMetadataOrientation;
158
+ std::string withMetadataIcc;
158
159
  std::unique_ptr<double[]> convKernel;
159
160
  int convKernelWidth;
160
161
  int convKernelHeight;
@@ -167,6 +168,9 @@ struct PipelineBaton {
167
168
  bool removeAlpha;
168
169
  bool ensureAlpha;
169
170
  VipsInterpretation colourspace;
171
+ int pageHeight;
172
+ std::vector<int> delay;
173
+ int loop;
170
174
  int tileSize;
171
175
  int tileOverlap;
172
176
  VipsForeignDzContainer tileContainer;
@@ -251,7 +255,7 @@ struct PipelineBaton {
251
255
  tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
252
256
  tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
253
257
  tiffPyramid(false),
254
- tiffSquash(false),
258
+ tiffBitdepth(8),
255
259
  tiffTile(false),
256
260
  tiffTileHeight(256),
257
261
  tiffTileWidth(256),
@@ -273,6 +277,9 @@ struct PipelineBaton {
273
277
  removeAlpha(false),
274
278
  ensureAlpha(false),
275
279
  colourspace(VIPS_INTERPRETATION_LAST),
280
+ pageHeight(0),
281
+ delay{-1},
282
+ loop(-1),
276
283
  tileSize(256),
277
284
  tileOverlap(0),
278
285
  tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
package/src/sharp.cc CHANGED
@@ -21,8 +21,14 @@
21
21
  #include "utilities.h"
22
22
  #include "stats.h"
23
23
 
24
- Napi::Object init(Napi::Env env, Napi::Object exports) {
24
+ static void* sharp_vips_init(void*) {
25
25
  vips_init("sharp");
26
+ return nullptr;
27
+ }
28
+
29
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
30
+ static GOnce sharp_vips_init_once = G_ONCE_INIT;
31
+ g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
26
32
 
27
33
  g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
28
34
  static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
package/src/stats.cc CHANGED
@@ -75,8 +75,29 @@ class StatsWorker : public Napi::AsyncWorker {
75
75
  baton->isOpaque = false;
76
76
  }
77
77
  }
78
+ // Convert to greyscale
79
+ vips::VImage greyscale = image.colourspace(VIPS_INTERPRETATION_B_W)[0];
78
80
  // Estimate entropy via histogram of greyscale value frequency
79
- baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
81
+ baton->entropy = std::abs(greyscale.hist_find().hist_entropy());
82
+ // Estimate sharpness via standard deviation of greyscale laplacian
83
+ VImage laplacian = VImage::new_matrixv(3, 3,
84
+ 0.0, 1.0, 0.0,
85
+ 1.0, -4.0, 1.0,
86
+ 0.0, 1.0, 0.0);
87
+ laplacian.set("scale", 9.0);
88
+ baton->sharpness = greyscale.conv(laplacian).deviate();
89
+ // Most dominant sRGB colour via 4096-bin 3D histogram
90
+ vips::VImage hist = sharp::RemoveAlpha(image)
91
+ .colourspace(VIPS_INTERPRETATION_sRGB)
92
+ .hist_find_ndim(VImage::option()->set("bins", 16));
93
+ std::complex<double> maxpos = hist.maxpos();
94
+ int const dx = static_cast<int>(std::real(maxpos));
95
+ int const dy = static_cast<int>(std::imag(maxpos));
96
+ std::vector<double> pel = hist(dx, dy);
97
+ int const dz = std::distance(pel.begin(), std::find(pel.begin(), pel.end(), hist.max()));
98
+ baton->dominantRed = dx * 16 + 8;
99
+ baton->dominantGreen = dy * 16 + 8;
100
+ baton->dominantBlue = dz * 16 + 8;
80
101
  } catch (vips::VError const &err) {
81
102
  (baton->err).append(err.what());
82
103
  }
@@ -123,6 +144,12 @@ class StatsWorker : public Napi::AsyncWorker {
123
144
  info.Set("channels", channels);
124
145
  info.Set("isOpaque", baton->isOpaque);
125
146
  info.Set("entropy", baton->entropy);
147
+ info.Set("sharpness", baton->sharpness);
148
+ Napi::Object dominant = Napi::Object::New(env);
149
+ dominant.Set("r", baton->dominantRed);
150
+ dominant.Set("g", baton->dominantGreen);
151
+ dominant.Set("b", baton->dominantBlue);
152
+ info.Set("dominant", dominant);
126
153
  Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
127
154
  } else {
128
155
  Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
@@ -154,6 +181,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
154
181
  // Join queue for worker thread
155
182
  Napi::Function callback = info[1].As<Napi::Function>();
156
183
  StatsWorker *worker = new StatsWorker(callback, baton, debuglog);
184
+ worker->Receiver().Set("options", options);
157
185
  worker->Queue();
158
186
 
159
187
  // Increment queued task counter
package/src/stats.h CHANGED
@@ -47,13 +47,21 @@ struct StatsBaton {
47
47
  std::vector<ChannelStats> channelStats;
48
48
  bool isOpaque;
49
49
  double entropy;
50
+ double sharpness;
51
+ int dominantRed;
52
+ int dominantGreen;
53
+ int dominantBlue;
50
54
 
51
55
  std::string err;
52
56
 
53
57
  StatsBaton():
54
58
  input(nullptr),
55
59
  isOpaque(true),
56
- entropy(0.0)
60
+ entropy(0.0),
61
+ sharpness(0.0),
62
+ dominantRed(0),
63
+ dominantGreen(0),
64
+ dominantBlue(0)
57
65
  {}
58
66
  };
59
67