sharp 0.27.2 → 0.28.3

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/src/common.h CHANGED
@@ -26,8 +26,8 @@
26
26
 
27
27
  #if (VIPS_MAJOR_VERSION < 8) || \
28
28
  (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
29
- (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 5)
30
- #error "libvips version 8.10.5+ is required - please see https://sharp.pixelplumbing.com/install"
29
+ (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 6)
30
+ #error "libvips version 8.10.6+ 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)))
@@ -57,9 +57,11 @@ namespace sharp {
57
57
  int rawChannels;
58
58
  int rawWidth;
59
59
  int rawHeight;
60
+ bool rawPremultiplied;
60
61
  int pages;
61
62
  int page;
62
63
  int level;
64
+ int subifd;
63
65
  int createChannels;
64
66
  int createWidth;
65
67
  int createHeight;
@@ -79,9 +81,11 @@ namespace sharp {
79
81
  rawChannels(0),
80
82
  rawWidth(0),
81
83
  rawHeight(0),
84
+ rawPremultiplied(false),
82
85
  pages(1),
83
86
  page(0),
84
87
  level(0),
88
+ subifd(-1),
85
89
  createChannels(0),
86
90
  createWidth(0),
87
91
  createHeight(0),
@@ -93,6 +97,7 @@ namespace sharp {
93
97
  // Convenience methods to access the attributes of a Napi::Object
94
98
  bool HasAttr(Napi::Object obj, std::string attr);
95
99
  std::string AttrAsStr(Napi::Object obj, std::string attr);
100
+ std::string AttrAsStr(Napi::Object obj, unsigned int const attr);
96
101
  uint32_t AttrAsUint32(Napi::Object obj, std::string attr);
97
102
  int32_t AttrAsInt32(Napi::Object obj, std::string attr);
98
103
  int32_t AttrAsInt32(Napi::Object obj, unsigned int const attr);
@@ -296,7 +301,7 @@ namespace sharp {
296
301
  /*
297
302
  Ensures alpha channel, if missing.
298
303
  */
299
- VImage EnsureAlpha(VImage image);
304
+ VImage EnsureAlpha(VImage image, double const value);
300
305
 
301
306
  } // namespace sharp
302
307
 
package/src/metadata.cc CHANGED
@@ -86,6 +86,9 @@ class MetadataWorker : public Napi::AsyncWorker {
86
86
  baton->levels.push_back(std::pair<int, int>(width, height));
87
87
  }
88
88
  }
89
+ if (image.get_typeof(VIPS_META_N_SUBIFDS) == G_TYPE_INT) {
90
+ baton->subifds = image.get_int(VIPS_META_N_SUBIFDS);
91
+ }
89
92
  baton->hasProfile = sharp::HasProfile(image);
90
93
  // Derived attributes
91
94
  baton->hasAlpha = sharp::HasAlpha(image);
@@ -203,6 +206,9 @@ class MetadataWorker : public Napi::AsyncWorker {
203
206
  }
204
207
  info.Set("levels", levels);
205
208
  }
209
+ if (baton->subifds > 0) {
210
+ info.Set("subifds", baton->subifds);
211
+ }
206
212
  info.Set("hasProfile", baton->hasProfile);
207
213
  info.Set("hasAlpha", baton->hasAlpha);
208
214
  if (baton->orientation > 0) {
package/src/metadata.h CHANGED
@@ -41,6 +41,7 @@ struct MetadataBaton {
41
41
  int pagePrimary;
42
42
  std::string compression;
43
43
  std::vector<std::pair<int, int>> levels;
44
+ int subifds;
44
45
  bool hasProfile;
45
46
  bool hasAlpha;
46
47
  int orientation;
@@ -68,6 +69,7 @@ struct MetadataBaton {
68
69
  pageHeight(0),
69
70
  loop(-1),
70
71
  pagePrimary(-1),
72
+ subifds(0),
71
73
  hasProfile(false),
72
74
  hasAlpha(false),
73
75
  orientation(0),
package/src/operations.cc CHANGED
@@ -92,6 +92,13 @@ namespace sharp {
92
92
  return image;
93
93
  }
94
94
 
95
+ /*
96
+ * Contrast limiting adapative histogram equalization (CLAHE)
97
+ */
98
+ VImage Clahe(VImage image, int const width, int const height, int const maxSlope) {
99
+ return image.hist_local(width, height, VImage::option()->set("max_slope", maxSlope));
100
+ }
101
+
95
102
  /*
96
103
  * Gamma encoding/decoding
97
104
  */
package/src/operations.h CHANGED
@@ -35,6 +35,11 @@ namespace sharp {
35
35
  */
36
36
  VImage Normalise(VImage image);
37
37
 
38
+ /*
39
+ * Contrast limiting adapative histogram equalization (CLAHE)
40
+ */
41
+ VImage Clahe(VImage image, int const width, int const height, int const maxSlope);
42
+
38
43
  /*
39
44
  * Gamma encoding/decoding
40
45
  */
package/src/pipeline.cc CHANGED
@@ -226,7 +226,8 @@ class PipelineWorker : public Napi::AsyncWorker {
226
226
  if (
227
227
  xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
228
228
  (inputImageType == sharp::ImageType::JPEG || inputImageType == sharp::ImageType::WEBP) &&
229
- baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0
229
+ baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0 &&
230
+ image.width() > 3 && image.height() > 3 && baton->input->pages == 1
230
231
  ) {
231
232
  if (xshrink >= 8 * shrink_on_load_factor) {
232
233
  xfactor = xfactor / 8;
@@ -344,9 +345,10 @@ class PipelineWorker : public Napi::AsyncWorker {
344
345
  bool const shouldApplyMedian = baton->medianSize > 0;
345
346
  bool const shouldComposite = !baton->composite.empty();
346
347
  bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0;
348
+ bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0;
347
349
 
348
350
  if (shouldComposite && !sharp::HasAlpha(image)) {
349
- image = sharp::EnsureAlpha(image);
351
+ image = sharp::EnsureAlpha(image, 1);
350
352
  }
351
353
 
352
354
  bool const shouldPremultiplyAlpha = sharp::HasAlpha(image) &&
@@ -562,9 +564,17 @@ class PipelineWorker : public Napi::AsyncWorker {
562
564
  // Use gravity in overlay
563
565
  if (compositeImage.width() <= baton->width) {
564
566
  across = static_cast<int>(ceil(static_cast<double>(image.width()) / compositeImage.width()));
567
+ // Ensure odd number of tiles across when gravity is centre, north or south
568
+ if (composite->gravity == 0 || composite->gravity == 1 || composite->gravity == 3) {
569
+ across |= 1;
570
+ }
565
571
  }
566
572
  if (compositeImage.height() <= baton->height) {
567
573
  down = static_cast<int>(ceil(static_cast<double>(image.height()) / compositeImage.height()));
574
+ // Ensure odd number of tiles down when gravity is centre, east or west
575
+ if (composite->gravity == 0 || composite->gravity == 2 || composite->gravity == 4) {
576
+ down |= 1;
577
+ }
568
578
  }
569
579
  if (across != 0 || down != 0) {
570
580
  int left;
@@ -586,7 +596,7 @@ class PipelineWorker : public Napi::AsyncWorker {
586
596
  // Ensure image to composite is sRGB with premultiplied alpha
587
597
  compositeImage = compositeImage.colourspace(VIPS_INTERPRETATION_sRGB);
588
598
  if (!sharp::HasAlpha(compositeImage)) {
589
- compositeImage = sharp::EnsureAlpha(compositeImage);
599
+ compositeImage = sharp::EnsureAlpha(compositeImage, 1);
590
600
  }
591
601
  if (!composite->premultiplied) compositeImage = compositeImage.premultiply();
592
602
  // Calculate position
@@ -594,8 +604,13 @@ class PipelineWorker : public Napi::AsyncWorker {
594
604
  int top;
595
605
  if (composite->hasOffset) {
596
606
  // Composite image at given offsets
597
- std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
598
- compositeImage.width(), compositeImage.height(), composite->left, composite->top);
607
+ if (composite->tile) {
608
+ std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
609
+ compositeImage.width(), compositeImage.height(), composite->left, composite->top);
610
+ } else {
611
+ left = composite->left;
612
+ top = composite->top;
613
+ }
599
614
  } else {
600
615
  // Composite image with given gravity
601
616
  std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
@@ -636,6 +651,11 @@ class PipelineWorker : public Napi::AsyncWorker {
636
651
  image = sharp::Normalise(image);
637
652
  }
638
653
 
654
+ // Apply contrast limiting adaptive histogram equalization (CLAHE)
655
+ if (shouldApplyClahe) {
656
+ image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope);
657
+ }
658
+
639
659
  // Apply bitwise boolean operation between images
640
660
  if (baton->boolean != nullptr) {
641
661
  VImage booleanImage;
@@ -678,8 +698,8 @@ class PipelineWorker : public Napi::AsyncWorker {
678
698
  }
679
699
 
680
700
  // Ensure alpha channel, if missing
681
- if (baton->ensureAlpha) {
682
- image = sharp::EnsureAlpha(image);
701
+ if (baton->ensureAlpha != -1) {
702
+ image = sharp::EnsureAlpha(image, baton->ensureAlpha);
683
703
  }
684
704
 
685
705
  // Convert image to sRGB, if not already
@@ -704,11 +724,21 @@ class PipelineWorker : public Napi::AsyncWorker {
704
724
  ->set("input_profile", "srgb")
705
725
  ->set("intent", VIPS_INTENT_PERCEPTUAL));
706
726
  }
707
-
708
727
  // Override EXIF Orientation tag
709
728
  if (baton->withMetadata && baton->withMetadataOrientation != -1) {
710
729
  image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
711
730
  }
731
+ // Override pixel density
732
+ if (baton->withMetadataDensity > 0) {
733
+ image = sharp::SetDensity(image, baton->withMetadataDensity);
734
+ }
735
+ // Metadata key/value pairs, e.g. EXIF
736
+ if (!baton->withMetadataStrs.empty()) {
737
+ image = image.copy();
738
+ for (const auto& s : baton->withMetadataStrs) {
739
+ image.set(s.first.data(), s.second.data());
740
+ }
741
+ }
712
742
 
713
743
  // Number of channels used in output image
714
744
  baton->channels = image.bands();
@@ -1038,6 +1068,7 @@ class PipelineWorker : public Napi::AsyncWorker {
1038
1068
  ->set("angle", CalculateAngleRotation(baton->tileAngle))
1039
1069
  ->set("background", baton->tileBackground)
1040
1070
  ->set("centre", baton->tileCentre)
1071
+ ->set("id", const_cast<char*>(baton->tileId.data()))
1041
1072
  ->set("skip_blanks", baton->tileSkipBlanks);
1042
1073
  // libvips chooses a default depth based on layout. Instead of replicating that logic here by
1043
1074
  // not passing anything - libvips will handle choice
@@ -1305,6 +1336,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1305
1336
  baton->linearB = sharp::AttrAsDouble(options, "linearB");
1306
1337
  baton->greyscale = sharp::AttrAsBool(options, "greyscale");
1307
1338
  baton->normalise = sharp::AttrAsBool(options, "normalise");
1339
+ baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth");
1340
+ baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight");
1341
+ baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope");
1308
1342
  baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation");
1309
1343
  baton->angle = sharp::AttrAsInt32(options, "angle");
1310
1344
  baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle");
@@ -1327,7 +1361,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1327
1361
  baton->affineInterpolator = vips::VInterpolate::new_from_name(sharp::AttrAsStr(options, "affineInterpolator").data());
1328
1362
 
1329
1363
  baton->removeAlpha = sharp::AttrAsBool(options, "removeAlpha");
1330
- baton->ensureAlpha = sharp::AttrAsBool(options, "ensureAlpha");
1364
+ baton->ensureAlpha = sharp::AttrAsDouble(options, "ensureAlpha");
1331
1365
  if (options.Has("boolean")) {
1332
1366
  baton->boolean = sharp::CreateInputDescriptor(options.Get("boolean").As<Napi::Object>());
1333
1367
  baton->booleanOp = sharp::GetBooleanOperation(sharp::AttrAsStr(options, "booleanOp"));
@@ -1364,7 +1398,14 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1364
1398
  baton->fileOut = sharp::AttrAsStr(options, "fileOut");
1365
1399
  baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
1366
1400
  baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
1401
+ baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
1367
1402
  baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
1403
+ Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
1404
+ Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
1405
+ for (unsigned int i = 0; i < mdStrKeys.Length(); i++) {
1406
+ std::string k = sharp::AttrAsStr(mdStrKeys, i);
1407
+ baton->withMetadataStrs.insert(std::make_pair(k, sharp::AttrAsStr(mdStrs, k)));
1408
+ }
1368
1409
  // Format-specific
1369
1410
  baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
1370
1411
  baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
@@ -1438,6 +1479,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1438
1479
  vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
1439
1480
  sharp::AttrAsStr(options, "tileDepth").data()));
1440
1481
  baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
1482
+ baton->tileId = sharp::AttrAsStr(options, "tileId");
1441
1483
 
1442
1484
  // Force random access for certain operations
1443
1485
  if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
package/src/pipeline.h CHANGED
@@ -18,6 +18,7 @@
18
18
  #include <memory>
19
19
  #include <string>
20
20
  #include <vector>
21
+ #include <unordered_map>
21
22
 
22
23
  #include <napi.h>
23
24
  #include <vips/vips8>
@@ -108,6 +109,9 @@ struct PipelineBaton {
108
109
  double gammaOut;
109
110
  bool greyscale;
110
111
  bool normalise;
112
+ int claheWidth;
113
+ int claheHeight;
114
+ int claheMaxSlope;
111
115
  bool useExifOrientation;
112
116
  int angle;
113
117
  double rotationAngle;
@@ -167,7 +171,9 @@ struct PipelineBaton {
167
171
  std::string err;
168
172
  bool withMetadata;
169
173
  int withMetadataOrientation;
174
+ double withMetadataDensity;
170
175
  std::string withMetadataIcc;
176
+ std::unordered_map<std::string, std::string> withMetadataStrs;
171
177
  std::unique_ptr<double[]> convKernel;
172
178
  int convKernelWidth;
173
179
  int convKernelHeight;
@@ -178,7 +184,7 @@ struct PipelineBaton {
178
184
  VipsOperationBoolean bandBoolOp;
179
185
  int extractChannel;
180
186
  bool removeAlpha;
181
- bool ensureAlpha;
187
+ double ensureAlpha;
182
188
  VipsInterpretation colourspace;
183
189
  int pageHeight;
184
190
  std::vector<int> delay;
@@ -192,6 +198,7 @@ struct PipelineBaton {
192
198
  std::vector<double> tileBackground;
193
199
  int tileSkipBlanks;
194
200
  VipsForeignDzDepth tileDepth;
201
+ std::string tileId;
195
202
  std::unique_ptr<double[]> recombMatrix;
196
203
 
197
204
  PipelineBaton():
@@ -230,6 +237,9 @@ struct PipelineBaton {
230
237
  gamma(0.0),
231
238
  greyscale(false),
232
239
  normalise(false),
240
+ claheWidth(0),
241
+ claheHeight(0),
242
+ claheMaxSlope(3),
233
243
  useExifOrientation(false),
234
244
  angle(0),
235
245
  rotationAngle(0.0),
@@ -258,7 +268,7 @@ struct PipelineBaton {
258
268
  jpegOptimiseScans(false),
259
269
  jpegOptimiseCoding(true),
260
270
  pngProgressive(false),
261
- pngCompressionLevel(9),
271
+ pngCompressionLevel(6),
262
272
  pngAdaptiveFiltering(false),
263
273
  pngPalette(false),
264
274
  pngQuality(100),
@@ -287,6 +297,7 @@ struct PipelineBaton {
287
297
  heifLossless(false),
288
298
  withMetadata(false),
289
299
  withMetadataOrientation(-1),
300
+ withMetadataDensity(0.0),
290
301
  convKernelWidth(0),
291
302
  convKernelHeight(0),
292
303
  convKernelScale(0.0),
@@ -296,7 +307,7 @@ struct PipelineBaton {
296
307
  bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
297
308
  extractChannel(-1),
298
309
  removeAlpha(false),
299
- ensureAlpha(false),
310
+ ensureAlpha(-1.0),
300
311
  colourspace(VIPS_INTERPRETATION_LAST),
301
312
  pageHeight(0),
302
313
  delay{-1},
package/src/sharp.cc CHANGED
@@ -44,6 +44,7 @@ Napi::Object init(Napi::Env env, Napi::Object exports) {
44
44
  exports.Set("libvipsVersion", Napi::Function::New(env, libvipsVersion));
45
45
  exports.Set("format", Napi::Function::New(env, format));
46
46
  exports.Set("_maxColourDistance", Napi::Function::New(env, _maxColourDistance));
47
+ exports.Set("_isUsingJemalloc", Napi::Function::New(env, _isUsingJemalloc));
47
48
  exports.Set("stats", Napi::Function::New(env, stats));
48
49
  return exports;
49
50
  }
package/src/utilities.cc CHANGED
@@ -225,3 +225,19 @@ Napi::Value _maxColourDistance(const Napi::CallbackInfo& info) {
225
225
 
226
226
  return Napi::Number::New(env, maxColourDistance);
227
227
  }
228
+
229
+ #if defined(__GNUC__)
230
+ // mallctl will be resolved by the runtime linker when jemalloc is being used
231
+ extern "C" {
232
+ int mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) __attribute__((weak));
233
+ }
234
+ Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
235
+ Napi::Env env = info.Env();
236
+ return Napi::Boolean::New(env, mallctl != nullptr);
237
+ }
238
+ #else
239
+ Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info) {
240
+ Napi::Env env = info.Env();
241
+ return Napi::Boolean::New(env, false);
242
+ }
243
+ #endif
package/src/utilities.h CHANGED
@@ -24,5 +24,6 @@ Napi::Value simd(const Napi::CallbackInfo& info);
24
24
  Napi::Value libvipsVersion(const Napi::CallbackInfo& info);
25
25
  Napi::Value format(const Napi::CallbackInfo& info);
26
26
  Napi::Value _maxColourDistance(const Napi::CallbackInfo& info);
27
+ Napi::Value _isUsingJemalloc(const Napi::CallbackInfo& info);
27
28
 
28
29
  #endif // SRC_UTILITIES_H_