sharp 0.25.4 → 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
@@ -189,7 +189,7 @@ class MetadataWorker : public Napi::AsyncWorker {
189
189
  if (!baton->levels.empty()) {
190
190
  int i = 0;
191
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) {
192
+ for (std::pair<int, int> const &l : baton->levels) {
193
193
  Napi::Object level = Napi::Object::New(env);
194
194
  level.Set("width", l.first);
195
195
  level.Set("height", l.second);
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
@@ -684,6 +684,15 @@ class PipelineWorker : public Napi::AsyncWorker {
684
684
  }
685
685
  }
686
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
+
687
696
  // Override EXIF Orientation tag
688
697
  if (baton->withMetadata && baton->withMetadataOrientation != -1) {
689
698
  image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
@@ -693,6 +702,16 @@ class PipelineWorker : public Napi::AsyncWorker {
693
702
  baton->channels = image.bands();
694
703
  baton->width = image.width();
695
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
+
696
715
  // Output
697
716
  if (baton->fileOut.empty()) {
698
717
  // Buffer output
@@ -703,7 +722,9 @@ class PipelineWorker : public Napi::AsyncWorker {
703
722
  ->set("strip", !baton->withMetadata)
704
723
  ->set("Q", baton->jpegQuality)
705
724
  ->set("interlace", baton->jpegProgressive)
706
- ->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)
707
728
  ->set("trellis_quant", baton->jpegTrellisQuantisation)
708
729
  ->set("quant_table", baton->jpegQuantisationTable)
709
730
  ->set("overshoot_deringing", baton->jpegOvershootDeringing)
@@ -720,8 +741,8 @@ class PipelineWorker : public Napi::AsyncWorker {
720
741
  baton->channels = std::min(baton->channels, 3);
721
742
  }
722
743
  } else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
723
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
724
- inputImageType == sharp::ImageType::SVG))) {
744
+ (inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
745
+ inputImageType == sharp::ImageType::SVG))) {
725
746
  // Write PNG to buffer
726
747
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
727
748
  VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
@@ -755,6 +776,18 @@ class PipelineWorker : public Napi::AsyncWorker {
755
776
  area->free_fn = nullptr;
756
777
  vips_area_unref(area);
757
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";
758
791
  } else if (baton->formatOut == "tiff" ||
759
792
  (baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
760
793
  // Write TIFF to buffer
@@ -769,7 +802,7 @@ class PipelineWorker : public Napi::AsyncWorker {
769
802
  VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
770
803
  ->set("strip", !baton->withMetadata)
771
804
  ->set("Q", baton->tiffQuality)
772
- ->set("squash", baton->tiffSquash)
805
+ ->set("bitdepth", baton->tiffBitdepth)
773
806
  ->set("compression", baton->tiffCompression)
774
807
  ->set("predictor", baton->tiffPredictor)
775
808
  ->set("pyramid", baton->tiffPyramid)
@@ -830,13 +863,16 @@ class PipelineWorker : public Napi::AsyncWorker {
830
863
  bool const isJpeg = sharp::IsJpeg(baton->fileOut);
831
864
  bool const isPng = sharp::IsPng(baton->fileOut);
832
865
  bool const isWebp = sharp::IsWebp(baton->fileOut);
866
+ bool const isGif = sharp::IsGif(baton->fileOut);
833
867
  bool const isTiff = sharp::IsTiff(baton->fileOut);
834
868
  bool const isHeif = sharp::IsHeif(baton->fileOut);
835
869
  bool const isDz = sharp::IsDz(baton->fileOut);
836
870
  bool const isDzZip = sharp::IsDzZip(baton->fileOut);
837
871
  bool const isV = sharp::IsV(baton->fileOut);
838
872
  bool const mightMatchInput = baton->formatOut == "input";
839
- 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
+
840
876
  if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
841
877
  (willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
842
878
  // Write JPEG to file
@@ -845,7 +881,9 @@ class PipelineWorker : public Napi::AsyncWorker {
845
881
  ->set("strip", !baton->withMetadata)
846
882
  ->set("Q", baton->jpegQuality)
847
883
  ->set("interlace", baton->jpegProgressive)
848
- ->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)
849
887
  ->set("trellis_quant", baton->jpegTrellisQuantisation)
850
888
  ->set("quant_table", baton->jpegQuantisationTable)
851
889
  ->set("overshoot_deringing", baton->jpegOvershootDeringing)
@@ -854,8 +892,8 @@ class PipelineWorker : public Napi::AsyncWorker {
854
892
  baton->formatOut = "jpeg";
855
893
  baton->channels = std::min(baton->channels, 3);
856
894
  } else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
857
- (inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
858
- inputImageType == sharp::ImageType::SVG))) {
895
+ (inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
896
+ inputImageType == sharp::ImageType::SVG))) {
859
897
  // Write PNG to file
860
898
  sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
861
899
  image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
@@ -881,6 +919,14 @@ class PipelineWorker : public Napi::AsyncWorker {
881
919
  ->set("reduction_effort", baton->webpReductionEffort)
882
920
  ->set("alpha_q", baton->webpAlphaQuality));
883
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";
884
930
  } else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
885
931
  (willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
886
932
  // Write TIFF to file
@@ -891,7 +937,7 @@ class PipelineWorker : public Napi::AsyncWorker {
891
937
  image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
892
938
  ->set("strip", !baton->withMetadata)
893
939
  ->set("Q", baton->tiffQuality)
894
- ->set("squash", baton->tiffSquash)
940
+ ->set("bitdepth", baton->tiffBitdepth)
895
941
  ->set("compression", baton->tiffCompression)
896
942
  ->set("predictor", baton->tiffPredictor)
897
943
  ->set("pyramid", baton->tiffPyramid)
@@ -940,7 +986,7 @@ class PipelineWorker : public Napi::AsyncWorker {
940
986
  std::vector<std::pair<std::string, std::string>> options {
941
987
  {"Q", std::to_string(baton->jpegQuality)},
942
988
  {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
943
- {"no_subsample", baton->jpegChromaSubsampling == "4:4:4" ? "TRUE": "FALSE"},
989
+ {"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
944
990
  {"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
945
991
  {"quant_table", std::to_string(baton->jpegQuantisationTable)},
946
992
  {"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
@@ -1282,6 +1328,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1282
1328
  baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1283
1329
  baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1284
1330
  baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1331
+ baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
1285
1332
  // Format-specific
1286
1333
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1287
1334
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1306,7 +1353,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1306
1353
  baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
1307
1354
  baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
1308
1355
  baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
1309
- baton->tiffSquash = sharp::AttrAsBool(options, "tiffSquash");
1356
+ baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
1310
1357
  baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
1311
1358
  baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
1312
1359
  baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
@@ -1324,6 +1371,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1324
1371
  baton->heifCompression = static_cast<VipsForeignHeifCompression>(
1325
1372
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
1326
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
+
1327
1386
  // Tile output
1328
1387
  baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
1329
1388
  baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
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/stats.cc CHANGED
@@ -86,6 +86,18 @@ class StatsWorker : public Napi::AsyncWorker {
86
86
  0.0, 1.0, 0.0);
87
87
  laplacian.set("scale", 9.0);
88
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;
89
101
  } catch (vips::VError const &err) {
90
102
  (baton->err).append(err.what());
91
103
  }
@@ -133,6 +145,11 @@ class StatsWorker : public Napi::AsyncWorker {
133
145
  info.Set("isOpaque", baton->isOpaque);
134
146
  info.Set("entropy", baton->entropy);
135
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);
136
153
  Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
137
154
  } else {
138
155
  Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
package/src/stats.h CHANGED
@@ -48,6 +48,9 @@ struct StatsBaton {
48
48
  bool isOpaque;
49
49
  double entropy;
50
50
  double sharpness;
51
+ int dominantRed;
52
+ int dominantGreen;
53
+ int dominantBlue;
51
54
 
52
55
  std::string err;
53
56
 
@@ -55,7 +58,10 @@ struct StatsBaton {
55
58
  input(nullptr),
56
59
  isOpaque(true),
57
60
  entropy(0.0),
58
- sharpness(0.0)
61
+ sharpness(0.0),
62
+ dominantRed(0),
63
+ dominantGreen(0),
64
+ dominantBlue(0)
59
65
  {}
60
66
  };
61
67