sharp 0.29.1 → 0.30.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sharp",
3
- "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
4
- "version": "0.29.1",
3
+ "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
4
+ "version": "0.30.1",
5
5
  "author": "Lovell Fuller <npm@lovell.info>",
6
6
  "homepage": "https://github.com/lovell/sharp",
7
7
  "contributors": [
@@ -79,7 +79,9 @@
79
79
  "Michael Nutt <michael@nutt.im>",
80
80
  "Brad Parham <baparham@gmail.com>",
81
81
  "Taneli Vatanen <taneli.vatanen@gmail.com>",
82
- "Joris Dugué <zaruike10@gmail.com>"
82
+ "Joris Dugué <zaruike10@gmail.com>",
83
+ "Chris Banks <christopher.bradley.banks@gmail.com>",
84
+ "Ompal Singh <ompal.hitm09@gmail.com>"
83
85
  ],
84
86
  "scripts": {
85
87
  "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
@@ -124,33 +126,46 @@
124
126
  "vips"
125
127
  ],
126
128
  "dependencies": {
127
- "color": "^4.0.1",
128
- "detect-libc": "^1.0.3",
129
- "node-addon-api": "^4.1.0",
130
- "prebuild-install": "^6.1.4",
129
+ "color": "^4.2.0",
130
+ "detect-libc": "^2.0.0",
131
+ "node-addon-api": "^4.3.0",
132
+ "prebuild-install": "^7.0.1",
131
133
  "semver": "^7.3.5",
132
- "simple-get": "^3.1.0",
134
+ "simple-get": "^4.0.1",
133
135
  "tar-fs": "^2.1.1",
134
136
  "tunnel-agent": "^0.6.0"
135
137
  },
136
138
  "devDependencies": {
137
- "async": "^3.2.1",
139
+ "async": "^3.2.3",
138
140
  "cc": "^3.0.1",
139
141
  "decompress-zip": "^0.3.3",
140
142
  "documentation": "^13.2.5",
141
143
  "exif-reader": "^1.0.3",
142
144
  "icc": "^2.0.0",
143
145
  "license-checker": "^25.0.1",
144
- "mocha": "^9.1.1",
145
- "mock-fs": "^5.0.0",
146
+ "mocha": "^9.2.0",
147
+ "mock-fs": "^5.1.2",
146
148
  "nyc": "^15.1.0",
147
- "prebuild": "^10.0.1",
149
+ "prebuild": "^11.0.3",
148
150
  "rimraf": "^3.0.2",
149
151
  "semistandard": "^16.0.1"
150
152
  },
151
153
  "license": "Apache-2.0",
152
154
  "config": {
153
- "libvips": "8.11.3",
155
+ "libvips": "8.12.2",
156
+ "integrity": {
157
+ "darwin-arm64v8": "sha512-p46s/bbJAjkOXzPISZt9HUpG9GWjwQkYnLLRLKzsBJHLtB3X6C6Y/zXI5Hd0DOojcFkks9a0kTN+uDQ/XJY19g==",
158
+ "darwin-x64": "sha512-6vOHVZnvXwe6EXRsy29jdkUzBE6ElNpXUwd+m8vV7qy32AnXu7B9YemHsZ44vWviIwPZeXF6Nhd9EFLM0wWohw==",
159
+ "linux-arm64v8": "sha512-XwZdS63yhqLtbFtx/0eoLF/Agf5qtTrI11FMnMRpuBJWd4jHB60RAH+uzYUgoChCmKIS+AeXYMLm4d8Ns2QX8w==",
160
+ "linux-armv6": "sha512-Rh0Q0kqwPG2MjXWOkPCuPEyiUKFgKJYWLgS835D4MrXgdKr8Tft/eVrc2iGIxt2re30VpDiZ1h0Rby1aCZt2zw==",
161
+ "linux-armv7": "sha512-heTS/MsmRvu4JljINxP+vDiS9ZZfuGhr3IStb5F7Gc0/QLRhllYAg4rcO8L1eTK9sIIzG5ARvI19+YUZe7WbzA==",
162
+ "linux-x64": "sha512-SSWAwBFi0hx8V/h/v82tTFGKWTFv9FiCK3Timz5OExuI+sX1Ngrd0PVQaWXOThGNdel/fcD3Bz9YjSt4feBR1g==",
163
+ "linuxmusl-arm64v8": "sha512-Rhks+5C7p7aO6AucLT1uvzo8ohlqcqCUPgZmN+LZjsPWob/Iix3MfiDYtv/+gTvdeEfXxbCU6/YuPBwHQ7/crA==",
164
+ "linuxmusl-x64": "sha512-IOyjSQqpWVntqOUpCHVWuQwACwmmjdi15H8Pc+Ma1JkhPogTfVsFQWyL7DuOTD3Yr23EuYGzovUX00duOtfy/g==",
165
+ "win32-arm64v8": "sha512-A+Qe8Ipewtvw9ldvF6nWed2J8kphzrUE04nFeKCtNx6pfGQ/MAlCKMjt/U8VgUKNjB01zJDUW9XE0+FhGZ/UpQ==",
166
+ "win32-ia32": "sha512-cMrAvwFdDeAVnLJt0IPMPRKaIFhyXYGTprsM0DND9VUHE8F7dJMR44tS5YkXsGh1QNDtjKT6YuxAVUglmiXtpA==",
167
+ "win32-x64": "sha512-vLFIfw6aW2zABa8jpgzWDhljnE6glktrddErVyazAIoHl6BFFe/Da+LK1DbXvIYHz7fyOoKhSfCJHCiJG1Vg6w=="
168
+ },
154
169
  "runtime": "napi",
155
170
  "target": 5
156
171
  },
package/src/common.cc CHANGED
@@ -132,6 +132,8 @@ namespace sharp {
132
132
  descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
133
133
  // Allow switch from random to sequential access
134
134
  descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
135
+ // Remove safety features and allow unlimited SVG/PNG input
136
+ descriptor->unlimited = AttrAsBool(input, "unlimited");
135
137
  return descriptor;
136
138
  }
137
139
 
@@ -328,7 +330,7 @@ namespace sharp {
328
330
  vips::VOption *option = VImage::option()
329
331
  ->set("access", descriptor->access)
330
332
  ->set("fail", descriptor->failOnError);
331
- if (imageType == ImageType::SVG) {
333
+ if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
332
334
  option->set("unlimited", TRUE);
333
335
  }
334
336
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
@@ -403,7 +405,7 @@ namespace sharp {
403
405
  vips::VOption *option = VImage::option()
404
406
  ->set("access", descriptor->access)
405
407
  ->set("fail", descriptor->failOnError);
406
- if (imageType == ImageType::SVG) {
408
+ if (descriptor->unlimited && (imageType == ImageType::SVG || imageType == ImageType::PNG)) {
407
409
  option->set("unlimited", TRUE);
408
410
  }
409
411
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
@@ -488,30 +490,24 @@ namespace sharp {
488
490
 
489
491
  /*
490
492
  Set animation properties if necessary.
491
- Non-provided properties will be loaded from image.
492
493
  */
493
- VImage SetAnimationProperties(VImage image, int pageHeight, std::vector<int> delay, int loop) {
494
- bool hasDelay = delay.size() != 1 || delay.front() != -1;
494
+ VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop) {
495
+ bool hasDelay = !delay.empty();
495
496
 
496
- if (pageHeight == 0 && image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
497
- pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
498
- }
499
-
500
- if (!hasDelay && image.get_typeof("delay") == VIPS_TYPE_ARRAY_INT) {
501
- delay = image.get_array_int("delay");
502
- hasDelay = true;
503
- }
497
+ // Avoid a copy if none of the animation properties are needed.
498
+ if (nPages == 1 && !hasDelay && loop == -1) return image;
504
499
 
505
- if (loop == -1 && image.get_typeof("loop") == G_TYPE_INT) {
506
- loop = image.get_int("loop");
500
+ if (delay.size() == 1) {
501
+ // We have just one delay, repeat that value for all frames.
502
+ delay.insert(delay.end(), nPages - 1, delay[0]);
507
503
  }
508
504
 
509
- if (pageHeight == 0) return image;
510
-
511
- // It is necessary to create the copy as otherwise, pageHeight will be ignored!
505
+ // Attaching metadata, need to copy the image.
512
506
  VImage copy = image.copy();
513
507
 
514
- copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
508
+ // Only set page-height if we have more than one page, or this could
509
+ // accidentally turn into an animated image later.
510
+ if (nPages > 1) copy.set(VIPS_META_PAGE_HEIGHT, pageHeight);
515
511
  if (hasDelay) copy.set("delay", delay);
516
512
  if (loop != -1) copy.set("loop", loop);
517
513
 
@@ -554,6 +550,14 @@ namespace sharp {
554
550
  return copy;
555
551
  }
556
552
 
553
+ /*
554
+ Multi-page images can have a page height. Fetch it, and sanity check it.
555
+ If page-height is not set, it defaults to the image height
556
+ */
557
+ int GetPageHeight(VImage image) {
558
+ return vips_image_get_page_height(image.get_image());
559
+ }
560
+
557
561
  /*
558
562
  Check the proposed format supports the current dimensions.
559
563
  */
@@ -610,6 +614,33 @@ namespace sharp {
610
614
  return warning;
611
615
  }
612
616
 
617
+ /*
618
+ Attach an event listener for progress updates, used to detect timeout
619
+ */
620
+ void SetTimeout(VImage image, int const seconds) {
621
+ if (seconds > 0) {
622
+ VipsImage *im = image.get_image();
623
+ if (im->progress_signal == NULL) {
624
+ int *timeout = VIPS_NEW(im, int);
625
+ *timeout = seconds;
626
+ g_signal_connect(im, "eval", G_CALLBACK(VipsProgressCallBack), timeout);
627
+ vips_image_set_progress(im, TRUE);
628
+ }
629
+ }
630
+ }
631
+
632
+ /*
633
+ Event listener for progress updates, used to detect timeout
634
+ */
635
+ void VipsProgressCallBack(VipsImage *im, VipsProgress *progress, int *timeout) {
636
+ // printf("VipsProgressCallBack progress=%d run=%d timeout=%d\n", progress->percent, progress->run, *timeout);
637
+ if (*timeout > 0 && progress->run >= *timeout) {
638
+ vips_image_set_kill(im, TRUE);
639
+ vips_error("timeout", "%d%% complete", progress->percent);
640
+ *timeout = 0;
641
+ }
642
+ }
643
+
613
644
  /*
614
645
  Calculate the (left, top) coordinates of the output image
615
646
  within the input image, applying the given gravity during an embed.
@@ -853,4 +884,79 @@ namespace sharp {
853
884
  return image;
854
885
  }
855
886
 
887
+ std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
888
+ Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction) {
889
+ if (swap) {
890
+ // Swap input width and height when requested.
891
+ std::swap(width, height);
892
+ }
893
+
894
+ double hshrink = 1.0;
895
+ double vshrink = 1.0;
896
+
897
+ if (targetWidth > 0 && targetHeight > 0) {
898
+ // Fixed width and height
899
+ hshrink = static_cast<double>(width) / targetWidth;
900
+ vshrink = static_cast<double>(height) / targetHeight;
901
+
902
+ switch (canvas) {
903
+ case Canvas::CROP:
904
+ case Canvas::MIN:
905
+ if (hshrink < vshrink) {
906
+ vshrink = hshrink;
907
+ } else {
908
+ hshrink = vshrink;
909
+ }
910
+ break;
911
+ case Canvas::EMBED:
912
+ case Canvas::MAX:
913
+ if (hshrink > vshrink) {
914
+ vshrink = hshrink;
915
+ } else {
916
+ hshrink = vshrink;
917
+ }
918
+ break;
919
+ case Canvas::IGNORE_ASPECT:
920
+ if (swap) {
921
+ std::swap(hshrink, vshrink);
922
+ }
923
+ break;
924
+ }
925
+ } else if (targetWidth > 0) {
926
+ // Fixed width
927
+ hshrink = static_cast<double>(width) / targetWidth;
928
+
929
+ if (canvas != Canvas::IGNORE_ASPECT) {
930
+ // Auto height
931
+ vshrink = hshrink;
932
+ }
933
+ } else if (targetHeight > 0) {
934
+ // Fixed height
935
+ vshrink = static_cast<double>(height) / targetHeight;
936
+
937
+ if (canvas != Canvas::IGNORE_ASPECT) {
938
+ // Auto width
939
+ hshrink = vshrink;
940
+ }
941
+ }
942
+
943
+ // We should not reduce or enlarge the output image, if
944
+ // withoutReduction or withoutEnlargement is specified.
945
+ if (withoutReduction) {
946
+ // Equivalent of VIPS_SIZE_UP
947
+ hshrink = std::min(1.0, hshrink);
948
+ vshrink = std::min(1.0, vshrink);
949
+ } else if (withoutEnlargement) {
950
+ // Equivalent of VIPS_SIZE_DOWN
951
+ hshrink = std::max(1.0, hshrink);
952
+ vshrink = std::max(1.0, vshrink);
953
+ }
954
+
955
+ // We don't want to shrink so much that we send an axis to 0
956
+ hshrink = std::min(hshrink, static_cast<double>(width));
957
+ vshrink = std::min(vshrink, static_cast<double>(height));
958
+
959
+ return std::make_pair(hshrink, vshrink);
960
+ }
961
+
856
962
  } // namespace sharp
package/src/common.h CHANGED
@@ -25,9 +25,9 @@
25
25
  // Verify platform and compiler compatibility
26
26
 
27
27
  #if (VIPS_MAJOR_VERSION < 8) || \
28
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 11) || \
29
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 11 && VIPS_MICRO_VERSION < 3)
30
- #error "libvips version 8.11.3+ is required - please see https://sharp.pixelplumbing.com/install"
28
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 12) || \
29
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 12 && VIPS_MICRO_VERSION < 2)
30
+ #error "libvips version 8.12.2+ is required - please see https://sharp.pixelplumbing.com/install"
31
31
  #endif
32
32
 
33
33
  #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@@ -50,6 +50,7 @@ namespace sharp {
50
50
  char *buffer;
51
51
  bool failOnError;
52
52
  int limitInputPixels;
53
+ bool unlimited;
53
54
  VipsAccess access;
54
55
  size_t bufferLength;
55
56
  bool isBuffer;
@@ -75,6 +76,7 @@ namespace sharp {
75
76
  buffer(nullptr),
76
77
  failOnError(TRUE),
77
78
  limitInputPixels(0x3FFF * 0x3FFF),
79
+ unlimited(FALSE),
78
80
  access(VIPS_ACCESS_RANDOM),
79
81
  bufferLength(0),
80
82
  isBuffer(FALSE),
@@ -133,6 +135,14 @@ namespace sharp {
133
135
  MISSING
134
136
  };
135
137
 
138
+ enum class Canvas {
139
+ CROP,
140
+ EMBED,
141
+ MAX,
142
+ MIN,
143
+ IGNORE_ASPECT
144
+ };
145
+
136
146
  // How many tasks are in the queue?
137
147
  extern volatile int counterQueue;
138
148
 
@@ -206,9 +216,8 @@ namespace sharp {
206
216
 
207
217
  /*
208
218
  Set animation properties if necessary.
209
- Non-provided properties will be loaded from image.
210
219
  */
211
- VImage SetAnimationProperties(VImage image, int pageHeight, std::vector<int> delay, int loop);
220
+ VImage SetAnimationProperties(VImage image, int nPages, int pageHeight, std::vector<int> delay, int loop);
212
221
 
213
222
  /*
214
223
  Remove animation properties from image.
@@ -230,6 +239,12 @@ namespace sharp {
230
239
  */
231
240
  VImage SetDensity(VImage image, const double density);
232
241
 
242
+ /*
243
+ Multi-page images can have a page height. Fetch it, and sanity check it.
244
+ If page-height is not set, it defaults to the image height
245
+ */
246
+ int GetPageHeight(VImage image);
247
+
233
248
  /*
234
249
  Check the proposed format supports the current dimensions.
235
250
  */
@@ -250,6 +265,16 @@ namespace sharp {
250
265
  */
251
266
  std::string VipsWarningPop();
252
267
 
268
+ /*
269
+ Attach an event listener for progress updates, used to detect timeout
270
+ */
271
+ void SetTimeout(VImage image, int const timeoutSeconds);
272
+
273
+ /*
274
+ Event listener for progress updates, used to detect timeout
275
+ */
276
+ void VipsProgressCallBack(VipsImage *image, VipsProgress *progress, int *timeoutSeconds);
277
+
253
278
  /*
254
279
  Calculate the (left, top) coordinates of the output image
255
280
  within the input image, applying the given gravity during an embed.
@@ -313,6 +338,15 @@ namespace sharp {
313
338
  */
314
339
  VImage EnsureAlpha(VImage image, double const value);
315
340
 
341
+ /*
342
+ Calculate the shrink factor, taking into account auto-rotate, the canvas
343
+ mode, and so on. The hshrink/vshrink are the amount to shrink the input
344
+ image axes by in order for the output axes (ie. after rotation) to match
345
+ the required thumbnail width/height and canvas mode.
346
+ */
347
+ std::pair<double, double> ResolveShrink(int width, int height, int targetWidth, int targetHeight,
348
+ Canvas canvas, bool swap, bool withoutEnlargement, bool withoutReduction);
349
+
316
350
  } // namespace sharp
317
351
 
318
352
  #endif // SRC_COMMON_H_
@@ -93,7 +93,7 @@ negate( std::vector<double> vector )
93
93
  {
94
94
  std::vector<double> new_vector( vector.size() );
95
95
 
96
- for( unsigned int i = 0; i < vector.size(); i++ )
96
+ for( std::vector<double>::size_type i = 0; i < vector.size(); i++ )
97
97
  new_vector[i] = vector[i] * -1;
98
98
 
99
99
  return( new_vector );
@@ -104,7 +104,7 @@ invert( std::vector<double> vector )
104
104
  {
105
105
  std::vector<double> new_vector( vector.size() );
106
106
 
107
- for( unsigned int i = 0; i < vector.size(); i++ )
107
+ for( std::vector<double>::size_type i = 0; i < vector.size(); i++ )
108
108
  new_vector[i] = 1.0 / vector[i];
109
109
 
110
110
  return( new_vector );
@@ -210,7 +210,6 @@ VOption::set( const char *name, std::vector<int> value )
210
210
  Pair *pair = new Pair( name );
211
211
 
212
212
  int *array;
213
- unsigned int i;
214
213
 
215
214
  pair->input = true;
216
215
 
@@ -219,7 +218,7 @@ VOption::set( const char *name, std::vector<int> value )
219
218
  static_cast< int >( value.size() ) );
220
219
  array = vips_value_get_array_int( &pair->value, NULL );
221
220
 
222
- for( i = 0; i < value.size(); i++ )
221
+ for( std::vector<double>::size_type i = 0; i < value.size(); i++ )
223
222
  array[i] = value[i];
224
223
 
225
224
  options.push_back( pair );
@@ -234,7 +233,6 @@ VOption::set( const char *name, std::vector<double> value )
234
233
  Pair *pair = new Pair( name );
235
234
 
236
235
  double *array;
237
- unsigned int i;
238
236
 
239
237
  pair->input = true;
240
238
 
@@ -243,7 +241,7 @@ VOption::set( const char *name, std::vector<double> value )
243
241
  static_cast< int >( value.size() ) );
244
242
  array = vips_value_get_array_double( &pair->value, NULL );
245
243
 
246
- for( i = 0; i < value.size(); i++ )
244
+ for( std::vector<double>::size_type i = 0; i < value.size(); i++ )
247
245
  array[i] = value[i];
248
246
 
249
247
  options.push_back( pair );
@@ -258,7 +256,6 @@ VOption::set( const char *name, std::vector<VImage> value )
258
256
  Pair *pair = new Pair( name );
259
257
 
260
258
  VipsImage **array;
261
- unsigned int i;
262
259
 
263
260
  pair->input = true;
264
261
 
@@ -267,7 +264,7 @@ VOption::set( const char *name, std::vector<VImage> value )
267
264
  static_cast< int >( value.size() ) );
268
265
  array = vips_value_get_array_image( &pair->value, NULL );
269
266
 
270
- for( i = 0; i < value.size(); i++ ) {
267
+ for( std::vector<double>::size_type i = 0; i < value.size(); i++ ) {
271
268
  VipsImage *vips_image = value[i].get_image();
272
269
 
273
270
  array[i] = vips_image;
@@ -488,10 +485,9 @@ VOption::get_operation( VipsOperation *operation )
488
485
  double *array =
489
486
  vips_value_get_array_double( value,
490
487
  &length );
491
- int j;
492
488
 
493
489
  ((*i)->vvector)->resize( length );
494
- for( j = 0; j < length; j++ )
490
+ for( int j = 0; j < length; j++ )
495
491
  (*((*i)->vvector))[j] = array[j];
496
492
  }
497
493
  else if( type == VIPS_TYPE_BLOB ) {
@@ -718,17 +714,38 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
718
714
  const char *operation_name;
719
715
  VipsBlob *blob;
720
716
 
717
+ /* Save with the new target API if we can. Fall back to the older
718
+ * mechanism in case the saver we need has not been converted yet.
719
+ *
720
+ * We need to hide any errors from this first phase.
721
+ */
721
722
  vips__filename_split8( suffix, filename, option_string );
722
- if( !(operation_name = vips_foreign_find_save_buffer( filename )) ) {
723
+
724
+ vips_error_freeze();
725
+ operation_name = vips_foreign_find_save_target( filename );
726
+ vips_error_thaw();
727
+
728
+ if( operation_name ) {
729
+ VTarget target = VTarget::new_to_memory();
730
+
731
+ call_option_string( operation_name, option_string,
732
+ (options ? options : VImage::option())->
733
+ set( "in", *this )->
734
+ set( "target", target ) );
735
+
736
+ g_object_get( target.get_target(), "blob", &blob, NULL );
737
+ }
738
+ else if( (operation_name = vips_foreign_find_save_buffer( filename )) ) {
739
+ call_option_string( operation_name, option_string,
740
+ (options ? options : VImage::option())->
741
+ set( "in", *this )->
742
+ set( "buffer", &blob ) );
743
+ }
744
+ else {
723
745
  delete options;
724
746
  throw VError();
725
747
  }
726
748
 
727
- call_option_string( operation_name, option_string,
728
- (options ? options : VImage::option())->
729
- set( "in", *this )->
730
- set( "buffer", &blob ) );
731
-
732
749
  if( blob ) {
733
750
  if( buf ) {
734
751
  *buf = VIPS_AREA( blob )->data;
@@ -767,6 +784,7 @@ std::vector<VImage>
767
784
  VImage::bandsplit( VOption *options ) const
768
785
  {
769
786
  std::vector<VImage> b;
787
+ b.reserve(bands());
770
788
 
771
789
  for( int i = 0; i < bands(); i++ )
772
790
  b.push_back( extract_band( i ) );
@@ -1,5 +1,5 @@
1
1
  // bodies for vips operations
2
- // Wed May 12 11:30:00 AM CEST 2021
2
+ // Mon Nov 1 03:31:09 PM CET 2021
3
3
  // this file is generated automatically, do not edit!
4
4
 
5
5
  VImage VImage::CMC2LCh( VOption *options ) const
@@ -1262,6 +1262,34 @@ VImage VImage::gifload_source( VSource source, VOption *options )
1262
1262
  return( out );
1263
1263
  }
1264
1264
 
1265
+ void VImage::gifsave( const char *filename, VOption *options ) const
1266
+ {
1267
+ call( "gifsave",
1268
+ (options ? options : VImage::option())->
1269
+ set( "in", *this )->
1270
+ set( "filename", filename ) );
1271
+ }
1272
+
1273
+ VipsBlob *VImage::gifsave_buffer( VOption *options ) const
1274
+ {
1275
+ VipsBlob *buffer;
1276
+
1277
+ call( "gifsave_buffer",
1278
+ (options ? options : VImage::option())->
1279
+ set( "in", *this )->
1280
+ set( "buffer", &buffer ) );
1281
+
1282
+ return( buffer );
1283
+ }
1284
+
1285
+ void VImage::gifsave_target( VTarget target, VOption *options ) const
1286
+ {
1287
+ call( "gifsave_target",
1288
+ (options ? options : VImage::option())->
1289
+ set( "in", *this )->
1290
+ set( "target", target ) );
1291
+ }
1292
+
1265
1293
  VImage VImage::globalbalance( VOption *options ) const
1266
1294
  {
1267
1295
  VImage out;
package/src/metadata.cc CHANGED
@@ -77,6 +77,9 @@ class MetadataWorker : public Napi::AsyncWorker {
77
77
  if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
78
78
  baton->compression = image.get_string("heif-compression");
79
79
  }
80
+ if (image.get_typeof(VIPS_META_RESOLUTION_UNIT) == VIPS_TYPE_REF_STRING) {
81
+ baton->resolutionUnit = image.get_string(VIPS_META_RESOLUTION_UNIT);
82
+ }
80
83
  if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
81
84
  int const levels = std::stoi(image.get_string("openslide.level-count"));
82
85
  for (int l = 0; l < levels; l++) {
@@ -198,6 +201,9 @@ class MetadataWorker : public Napi::AsyncWorker {
198
201
  if (!baton->compression.empty()) {
199
202
  info.Set("compression", baton->compression);
200
203
  }
204
+ if (!baton->resolutionUnit.empty()) {
205
+ info.Set("resolutionUnit", baton->resolutionUnit == "in" ? "inch" : baton->resolutionUnit);
206
+ }
201
207
  if (!baton->levels.empty()) {
202
208
  int i = 0;
203
209
  Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
package/src/metadata.h CHANGED
@@ -40,6 +40,7 @@ struct MetadataBaton {
40
40
  std::vector<int> delay;
41
41
  int pagePrimary;
42
42
  std::string compression;
43
+ std::string resolutionUnit;
43
44
  std::vector<std::pair<int, int>> levels;
44
45
  int subifds;
45
46
  std::vector<double> background;
package/src/operations.cc CHANGED
@@ -308,4 +308,98 @@ namespace sharp {
308
308
  return image;
309
309
  }
310
310
 
311
+ /*
312
+ * Split and crop each frame, reassemble, and update pageHeight.
313
+ */
314
+ VImage CropMultiPage(VImage image, int left, int top, int width, int height,
315
+ int nPages, int *pageHeight) {
316
+ if (top == 0 && height == *pageHeight) {
317
+ // Fast path; no need to adjust the height of the multi-page image
318
+ return image.extract_area(left, 0, width, image.height());
319
+ } else {
320
+ std::vector<VImage> pages;
321
+ pages.reserve(nPages);
322
+
323
+ // Split the image into cropped frames
324
+ for (int i = 0; i < nPages; i++) {
325
+ pages.push_back(
326
+ image.extract_area(left, *pageHeight * i + top, width, height));
327
+ }
328
+
329
+ // Reassemble the frames into a tall, thin image
330
+ VImage assembled = VImage::arrayjoin(pages,
331
+ VImage::option()->set("across", 1));
332
+
333
+ // Update the page height
334
+ *pageHeight = height;
335
+
336
+ return assembled;
337
+ }
338
+ }
339
+
340
+ /*
341
+ * Split into frames, embed each frame, reassemble, and update pageHeight.
342
+ */
343
+ VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
344
+ std::vector<double> background, int nPages, int *pageHeight) {
345
+ if (top == 0 && height == *pageHeight) {
346
+ // Fast path; no need to adjust the height of the multi-page image
347
+ return image.embed(left, 0, width, image.height(), VImage::option()
348
+ ->set("extend", VIPS_EXTEND_BACKGROUND)
349
+ ->set("background", background));
350
+ } else if (left == 0 && width == image.width()) {
351
+ // Fast path; no need to adjust the width of the multi-page image
352
+ std::vector<VImage> pages;
353
+ pages.reserve(nPages);
354
+
355
+ // Rearrange the tall image into a vertical grid
356
+ image = image.grid(*pageHeight, nPages, 1);
357
+
358
+ // Do the embed on the wide image
359
+ image = image.embed(0, top, image.width(), height, VImage::option()
360
+ ->set("extend", VIPS_EXTEND_BACKGROUND)
361
+ ->set("background", background));
362
+
363
+ // Split the wide image into frames
364
+ for (int i = 0; i < nPages; i++) {
365
+ pages.push_back(
366
+ image.extract_area(width * i, 0, width, height));
367
+ }
368
+
369
+ // Reassemble the frames into a tall, thin image
370
+ VImage assembled = VImage::arrayjoin(pages,
371
+ VImage::option()->set("across", 1));
372
+
373
+ // Update the page height
374
+ *pageHeight = height;
375
+
376
+ return assembled;
377
+ } else {
378
+ std::vector<VImage> pages;
379
+ pages.reserve(nPages);
380
+
381
+ // Split the image into frames
382
+ for (int i = 0; i < nPages; i++) {
383
+ pages.push_back(
384
+ image.extract_area(0, *pageHeight * i, image.width(), *pageHeight));
385
+ }
386
+
387
+ // Embed each frame in the target size
388
+ for (int i = 0; i < nPages; i++) {
389
+ pages[i] = pages[i].embed(left, top, width, height, VImage::option()
390
+ ->set("extend", VIPS_EXTEND_BACKGROUND)
391
+ ->set("background", background));
392
+ }
393
+
394
+ // Reassemble the frames into a tall, thin image
395
+ VImage assembled = VImage::arrayjoin(pages,
396
+ VImage::option()->set("across", 1));
397
+
398
+ // Update the page height
399
+ *pageHeight = height;
400
+
401
+ return assembled;
402
+ }
403
+ }
404
+
311
405
  } // namespace sharp