sharp 0.25.3 → 0.26.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.
@@ -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) {
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,20 @@ 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("optimize_gif_frames", TRUE)
786
+ ->set("optimize_gif_transparency", TRUE)
787
+ ->set("format", "gif")));
788
+ baton->bufferOut = static_cast<char*>(area->data);
789
+ baton->bufferOutLength = area->length;
790
+ area->free_fn = nullptr;
791
+ vips_area_unref(area);
792
+ baton->formatOut = "gif";
754
793
  } else if (baton->formatOut == "tiff" ||
755
794
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
756
795
  // Write TIFF to buffer
@@ -765,7 +804,7 @@ class PipelineWorker : public Napi::AsyncWorker {
765
804
  VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
766
805
  ->set("strip", !baton->withMetadata)
767
806
  ->set("Q", baton->tiffQuality)
768
- ->set("squash", baton->tiffSquash)
807
+ ->set("bitdepth", baton->tiffBitdepth)
769
808
  ->set("compression", baton->tiffCompression)
770
809
  ->set("predictor", baton->tiffPredictor)
771
810
  ->set("pyramid", baton->tiffPyramid)
@@ -826,13 +865,16 @@ class PipelineWorker : public Napi::AsyncWorker {
826
865
  bool const isJpeg = sharp::IsJpeg(baton->fileOut);
827
866
  bool const isPng = sharp::IsPng(baton->fileOut);
828
867
  bool const isWebp = sharp::IsWebp(baton->fileOut);
868
+ bool const isGif = sharp::IsGif(baton->fileOut);
829
869
  bool const isTiff = sharp::IsTiff(baton->fileOut);
830
870
  bool const isHeif = sharp::IsHeif(baton->fileOut);
831
871
  bool const isDz = sharp::IsDz(baton->fileOut);
832
872
  bool const isDzZip = sharp::IsDzZip(baton->fileOut);
833
873
  bool const isV = sharp::IsV(baton->fileOut);
834
874
  bool const mightMatchInput = baton->formatOut == "input";
835
- bool const willMatchInput = mightMatchInput && !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV);
875
+ bool const willMatchInput = mightMatchInput &&
876
+ !(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
877
+
836
878
  if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
837
879
  (willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
838
880
  // Write JPEG to file
@@ -841,7 +883,9 @@ class PipelineWorker : public Napi::AsyncWorker {
841
883
  ->set("strip", !baton->withMetadata)
842
884
  ->set("Q", baton->jpegQuality)
843
885
  ->set("interlace", baton->jpegProgressive)
844
- ->set("no_subsample", baton->jpegChromaSubsampling == "4:4:4")
886
+ ->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
887
+ ? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
888
+ : VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
845
889
  ->set("trellis_quant", baton->jpegTrellisQuantisation)
846
890
  ->set("quant_table", baton->jpegQuantisationTable)
847
891
  ->set("overshoot_deringing", baton->jpegOvershootDeringing)
@@ -850,8 +894,8 @@ class PipelineWorker : public Napi::AsyncWorker {
850
894
  baton->formatOut = "jpeg";
851
895
  baton->channels = std::min(baton->channels, 3);
852
896
  } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
853
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
854
- inputImageType == sharp::ImageType::SVG))) {
897
+ (inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
898
+ inputImageType == sharp::ImageType::SVG))) {
855
899
  // Write PNG to file
856
900
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
857
901
  image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
@@ -877,6 +921,16 @@ class PipelineWorker : public Napi::AsyncWorker {
877
921
  ->set("reduction_effort", baton->webpReductionEffort)
878
922
  ->set("alpha_q", baton->webpAlphaQuality));
879
923
  baton->formatOut = "webp";
924
+ } else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
925
+ (willMatchInput && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
926
+ // Write GIF to file
927
+ sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
928
+ image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
929
+ ->set("strip", !baton->withMetadata)
930
+ ->set("optimize_gif_frames", TRUE)
931
+ ->set("optimize_gif_transparency", TRUE)
932
+ ->set("format", "gif"));
933
+ baton->formatOut = "gif";
880
934
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
881
935
  (willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
882
936
  // Write TIFF to file
@@ -887,7 +941,7 @@ class PipelineWorker : public Napi::AsyncWorker {
887
941
  image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
888
942
  ->set("strip", !baton->withMetadata)
889
943
  ->set("Q", baton->tiffQuality)
890
- ->set("squash", baton->tiffSquash)
944
+ ->set("bitdepth", baton->tiffBitdepth)
891
945
  ->set("compression", baton->tiffCompression)
892
946
  ->set("predictor", baton->tiffPredictor)
893
947
  ->set("pyramid", baton->tiffPyramid)
@@ -936,7 +990,7 @@ class PipelineWorker : public Napi::AsyncWorker {
936
990
  std::vector<std::pair<std::string, std::string>> options {
937
991
  {"Q", std::to_string(baton->jpegQuality)},
938
992
  {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
939
- {"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"},
993
+ {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
940
994
  {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
941
995
  {"quant_table", std::to_string(baton->jpegQuantisationTable)},
942
996
  {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
@@ -960,6 +1014,7 @@ class PipelineWorker : public Napi::AsyncWorker {
960
1014
  ->set("suffix", const_cast<char*>(suffix.data()))
961
1015
  ->set("angle", CalculateAngleRotation(baton->tileAngle))
962
1016
  ->set("background", baton->tileBackground)
1017
+ ->set("centre", baton->tileCentre)
963
1018
  ->set("skip_blanks", baton->tileSkipBlanks);
964
1019
  // libvips chooses a default depth based on layout. Instead of replicating that logic here by
965
1020
  // not passing anything - libvips will handle choice
@@ -1278,6 +1333,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1278
1333
  baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1279
1334
  baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1280
1335
  baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1336
+ baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
1281
1337
  // Format-specific
1282
1338
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1283
1339
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1302,7 +1358,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1302
1358
  baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
1303
1359
  baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1304
1360
  baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1305
- baton->tiffSquash = sharp::AttrAsBool(options, "tiffSquash");
1361
+ baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
1306
1362
  baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
1307
1363
  baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
1308
1364
  baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
@@ -1320,6 +1376,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1320
1376
  baton->heifCompression = static_cast<VipsForeignHeifCompression>(
1321
1377
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1322
1378
  sharp::AttrAsStr(options, "heifCompression").data()));
1379
+
1380
+ // Animated output
1381
+ if (sharp::HasAttr(options, "pageHeight")) {
1382
+ baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
1383
+ }
1384
+ if (sharp::HasAttr(options, "loop")) {
1385
+ baton->loop = sharp::AttrAsUint32(options, "loop");
1386
+ }
1387
+ if (sharp::HasAttr(options, "delay")) {
1388
+ baton->delay = sharp::AttrAsInt32Vector(options, "delay");
1389
+ }
1390
+
1323
1391
  // Tile output
1324
1392
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1325
1393
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
@@ -1336,6 +1404,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1336
1404
  baton->tileDepth = static_cast<VipsForeignDzDepth>(
1337
1405
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
1338
1406
  sharp::AttrAsStr(options, "tileDepth").data()));
1407
+ baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
1339
1408
 
1340
1409
  // Force random access for certain operations
1341
1410
  if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
package/src/pipeline.h CHANGED
@@ -79,6 +79,7 @@ struct PipelineBaton {
79
79
  int cropOffsetLeft;
80
80
  int cropOffsetTop;
81
81
  bool premultiplied;
82
+ bool tileCentre;
82
83
  std::string kernel;
83
84
  bool fastShrinkOnLoad;
84
85
  double tintA;
@@ -143,7 +144,7 @@ struct PipelineBaton {
143
144
  VipsForeignTiffCompression tiffCompression;
144
145
  VipsForeignTiffPredictor tiffPredictor;
145
146
  bool tiffPyramid;
146
- bool tiffSquash;
147
+ int tiffBitdepth;
147
148
  bool tiffTile;
148
149
  int tiffTileHeight;
149
150
  int tiffTileWidth;
@@ -155,6 +156,7 @@ struct PipelineBaton {
155
156
  std::string err;
156
157
  bool withMetadata;
157
158
  int withMetadataOrientation;
159
+ std::string withMetadataIcc;
158
160
  std::unique_ptr<double[]> convKernel;
159
161
  int convKernelWidth;
160
162
  int convKernelHeight;
@@ -167,6 +169,9 @@ struct PipelineBaton {
167
169
  bool removeAlpha;
168
170
  bool ensureAlpha;
169
171
  VipsInterpretation colourspace;
172
+ int pageHeight;
173
+ std::vector<int> delay;
174
+ int loop;
170
175
  int tileSize;
171
176
  int tileOverlap;
172
177
  VipsForeignDzContainer tileContainer;
@@ -251,7 +256,7 @@ struct PipelineBaton {
251
256
  tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
252
257
  tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
253
258
  tiffPyramid(false),
254
- tiffSquash(false),
259
+ tiffBitdepth(8),
255
260
  tiffTile(false),
256
261
  tiffTileHeight(256),
257
262
  tiffTileWidth(256),
@@ -273,6 +278,9 @@ struct PipelineBaton {
273
278
  removeAlpha(false),
274
279
  ensureAlpha(false),
275
280
  colourspace(VIPS_INTERPRETATION_LAST),
281
+ pageHeight(0),
282
+ delay{-1},
283
+ loop(-1),
276
284
  tileSize(256),
277
285
  tileOverlap(0),
278
286
  tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
package/src/stats.cc CHANGED
@@ -75,8 +75,31 @@ 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
+ if (image.width() > 1 || image.height() > 1) {
84
+ VImage laplacian = VImage::new_matrixv(3, 3,
85
+ 0.0, 1.0, 0.0,
86
+ 1.0, -4.0, 1.0,
87
+ 0.0, 1.0, 0.0);
88
+ laplacian.set("scale", 9.0);
89
+ baton->sharpness = greyscale.conv(laplacian).deviate();
90
+ }
91
+ // Most dominant sRGB colour via 4096-bin 3D histogram
92
+ vips::VImage hist = sharp::RemoveAlpha(image)
93
+ .colourspace(VIPS_INTERPRETATION_sRGB)
94
+ .hist_find_ndim(VImage::option()->set("bins", 16));
95
+ std::complex<double> maxpos = hist.maxpos();
96
+ int const dx = static_cast<int>(std::real(maxpos));
97
+ int const dy = static_cast<int>(std::imag(maxpos));
98
+ std::vector<double> pel = hist(dx, dy);
99
+ int const dz = std::distance(pel.begin(), std::find(pel.begin(), pel.end(), hist.max()));
100
+ baton->dominantRed = dx * 16 + 8;
101
+ baton->dominantGreen = dy * 16 + 8;
102
+ baton->dominantBlue = dz * 16 + 8;
80
103
  } catch (vips::VError const &err) {
81
104
  (baton->err).append(err.what());
82
105
  }
@@ -123,6 +146,12 @@ class StatsWorker : public Napi::AsyncWorker {
123
146
  info.Set("channels", channels);
124
147
  info.Set("isOpaque", baton->isOpaque);
125
148
  info.Set("entropy", baton->entropy);
149
+ info.Set("sharpness", baton->sharpness);
150
+ Napi::Object dominant = Napi::Object::New(env);
151
+ dominant.Set("r", baton->dominantRed);
152
+ dominant.Set("g", baton->dominantGreen);
153
+ dominant.Set("b", baton->dominantBlue);
154
+ info.Set("dominant", dominant);
126
155
  Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
127
156
  } else {
128
157
  Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
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