sharp 0.25.0 → 0.25.4

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.cc CHANGED
@@ -88,6 +88,10 @@ namespace sharp {
88
88
  if (HasAttr(input, "page")) {
89
89
  descriptor->page = AttrAsUint32(input, "page");
90
90
  }
91
+ // Multi-level input (OpenSlide)
92
+ if (HasAttr(input, "level")) {
93
+ descriptor->level = AttrAsUint32(input, "level");
94
+ }
91
95
  // Create new image
92
96
  if (HasAttr(input, "createChannels")) {
93
97
  descriptor->createChannels = AttrAsUint32(input, "createChannels");
@@ -279,6 +283,9 @@ namespace sharp {
279
283
  vips::VOption *option = VImage::option()
280
284
  ->set("access", descriptor->access)
281
285
  ->set("fail", descriptor->failOnError);
286
+ if (imageType == ImageType::SVG) {
287
+ option->set("unlimited", TRUE);
288
+ }
282
289
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
283
290
  option->set("dpi", descriptor->density);
284
291
  }
@@ -289,6 +296,9 @@ namespace sharp {
289
296
  option->set("n", descriptor->pages);
290
297
  option->set("page", descriptor->page);
291
298
  }
299
+ if (imageType == ImageType::OPENSLIDE) {
300
+ option->set("level", descriptor->level);
301
+ }
292
302
  image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
293
303
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
294
304
  image = SetDensity(image, descriptor->density);
@@ -325,6 +335,9 @@ namespace sharp {
325
335
  vips::VOption *option = VImage::option()
326
336
  ->set("access", descriptor->access)
327
337
  ->set("fail", descriptor->failOnError);
338
+ if (imageType == ImageType::SVG) {
339
+ option->set("unlimited", TRUE);
340
+ }
328
341
  if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
329
342
  option->set("dpi", descriptor->density);
330
343
  }
@@ -335,6 +348,9 @@ namespace sharp {
335
348
  option->set("n", descriptor->pages);
336
349
  option->set("page", descriptor->page);
337
350
  }
351
+ if (imageType == ImageType::OPENSLIDE) {
352
+ option->set("level", descriptor->level);
353
+ }
338
354
  image = VImage::new_from_file(descriptor->file.data(), option);
339
355
  if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
340
356
  image = SetDensity(image, descriptor->density);
package/src/common.h CHANGED
@@ -57,6 +57,7 @@ namespace sharp {
57
57
  int rawHeight;
58
58
  int pages;
59
59
  int page;
60
+ int level;
60
61
  int createChannels;
61
62
  int createWidth;
62
63
  int createHeight;
@@ -75,6 +76,7 @@ namespace sharp {
75
76
  rawHeight(0),
76
77
  pages(1),
77
78
  page(0),
79
+ level(0),
78
80
  createChannels(0),
79
81
  createWidth(0),
80
82
  createHeight(0),
package/src/metadata.cc CHANGED
@@ -74,6 +74,15 @@ class MetadataWorker : public Napi::AsyncWorker {
74
74
  if (image.get_typeof("heif-primary") == G_TYPE_INT) {
75
75
  baton->pagePrimary = image.get_int("heif-primary");
76
76
  }
77
+ if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
78
+ int const levels = std::stoi(image.get_string("openslide.level-count"));
79
+ for (int l = 0; l < levels; l++) {
80
+ std::string prefix = "openslide.level[" + std::to_string(l) + "].";
81
+ int const width = std::stoi(image.get_string((prefix + "width").data()));
82
+ int const height = std::stoi(image.get_string((prefix + "height").data()));
83
+ baton->levels.push_back(std::pair<int, int>(width, height));
84
+ }
85
+ }
77
86
  baton->hasProfile = sharp::HasProfile(image);
78
87
  // Derived attributes
79
88
  baton->hasAlpha = sharp::HasAlpha(image);
@@ -177,6 +186,17 @@ class MetadataWorker : public Napi::AsyncWorker {
177
186
  if (baton->pagePrimary > -1) {
178
187
  info.Set("pagePrimary", baton->pagePrimary);
179
188
  }
189
+ if (!baton->levels.empty()) {
190
+ int i = 0;
191
+ Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
192
+ for (std::pair<int, int> const l : baton->levels) {
193
+ Napi::Object level = Napi::Object::New(env);
194
+ level.Set("width", l.first);
195
+ level.Set("height", l.second);
196
+ levels.Set(i++, level);
197
+ }
198
+ info.Set("levels", levels);
199
+ }
180
200
  info.Set("hasProfile", baton->hasProfile);
181
201
  info.Set("hasAlpha", baton->hasAlpha);
182
202
  if (baton->orientation > 0) {
@@ -229,6 +249,7 @@ Napi::Value metadata(const Napi::CallbackInfo& info) {
229
249
  // Join queue for worker thread
230
250
  Napi::Function callback = info[1].As<Napi::Function>();
231
251
  MetadataWorker *worker = new MetadataWorker(callback, baton, debuglog);
252
+ worker->Receiver().Set("options", options);
232
253
  worker->Queue();
233
254
 
234
255
  // Increment queued task counter
package/src/metadata.h CHANGED
@@ -39,6 +39,7 @@ struct MetadataBaton {
39
39
  int loop;
40
40
  std::vector<int> delay;
41
41
  int pagePrimary;
42
+ std::vector<std::pair<int, int>> levels;
42
43
  bool hasProfile;
43
44
  bool hasAlpha;
44
45
  int orientation;
package/src/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
@@ -933,9 +937,6 @@ class PipelineWorker : public Napi::AsyncWorker {
933
937
  };
934
938
  suffix = AssembleSuffixString(".webp", options);
935
939
  } else {
936
- std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE
937
- || baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY
938
- ? ".jpg" : ".jpeg";
939
940
  std::vector<std::pair<std::string, std::string>> options {
940
941
  {"Q", std::to_string(baton->jpegQuality)},
941
942
  {"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
@@ -946,6 +947,7 @@ class PipelineWorker : public Napi::AsyncWorker {
946
947
  {"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
947
948
  {"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
948
949
  };
950
+ std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
949
951
  suffix = AssembleSuffixString(extname, options);
950
952
  }
951
953
  // Remove alpha channel from tile background if image does not contain an alpha channel
@@ -1362,6 +1364,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
1362
1364
  // Join queue for worker thread
1363
1365
  Napi::Function callback = info[1].As<Napi::Function>();
1364
1366
  PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
1367
+ worker->Receiver().Set("options", options);
1365
1368
  worker->Queue();
1366
1369
 
1367
1370
  // Increment queued task counter
package/src/sharp.cc CHANGED
@@ -21,8 +21,14 @@
21
21
  #include "utilities.h"
22
22
  #include "stats.h"
23
23
 
24
- Napi::Object init(Napi::Env env, Napi::Object exports) {
24
+ static void* sharp_vips_init(void*) {
25
25
  vips_init("sharp");
26
+ return nullptr;
27
+ }
28
+
29
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
30
+ static GOnce sharp_vips_init_once = G_ONCE_INIT;
31
+ g_once(&sharp_vips_init_once, static_cast<GThreadFunc>(sharp_vips_init), nullptr);
26
32
 
27
33
  g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
28
34
  static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
package/src/stats.cc CHANGED
@@ -75,8 +75,17 @@ class StatsWorker : public Napi::AsyncWorker {
75
75
  baton->isOpaque = false;
76
76
  }
77
77
  }
78
+ // Convert to greyscale
79
+ vips::VImage greyscale = image.colourspace(VIPS_INTERPRETATION_B_W)[0];
78
80
  // Estimate entropy via histogram of greyscale value frequency
79
- baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
81
+ baton->entropy = std::abs(greyscale.hist_find().hist_entropy());
82
+ // Estimate sharpness via standard deviation of greyscale laplacian
83
+ VImage laplacian = VImage::new_matrixv(3, 3,
84
+ 0.0, 1.0, 0.0,
85
+ 1.0, -4.0, 1.0,
86
+ 0.0, 1.0, 0.0);
87
+ laplacian.set("scale", 9.0);
88
+ baton->sharpness = greyscale.conv(laplacian).deviate();
80
89
  } catch (vips::VError const &err) {
81
90
  (baton->err).append(err.what());
82
91
  }
@@ -123,6 +132,7 @@ class StatsWorker : public Napi::AsyncWorker {
123
132
  info.Set("channels", channels);
124
133
  info.Set("isOpaque", baton->isOpaque);
125
134
  info.Set("entropy", baton->entropy);
135
+ info.Set("sharpness", baton->sharpness);
126
136
  Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
127
137
  } else {
128
138
  Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
@@ -154,6 +164,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
154
164
  // Join queue for worker thread
155
165
  Napi::Function callback = info[1].As<Napi::Function>();
156
166
  StatsWorker *worker = new StatsWorker(callback, baton, debuglog);
167
+ worker->Receiver().Set("options", options);
157
168
  worker->Queue();
158
169
 
159
170
  // Increment queued task counter
package/src/stats.h CHANGED
@@ -47,13 +47,15 @@ struct StatsBaton {
47
47
  std::vector<ChannelStats> channelStats;
48
48
  bool isOpaque;
49
49
  double entropy;
50
+ double sharpness;
50
51
 
51
52
  std::string err;
52
53
 
53
54
  StatsBaton():
54
55
  input(nullptr),
55
56
  isOpaque(true),
56
- entropy(0.0)
57
+ entropy(0.0),
58
+ sharpness(0.0)
57
59
  {}
58
60
  };
59
61