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/README.md +7 -6
- package/binding.gyp +33 -6
- package/install/dll-copy.js +2 -1
- package/install/libvips.js +2 -1
- package/lib/channel.js +7 -10
- package/lib/colour.js +5 -5
- package/lib/composite.js +2 -5
- package/lib/constructor.js +74 -17
- package/lib/input.js +30 -5
- package/lib/is.js +6 -6
- package/lib/libvips.js +1 -1
- package/lib/operation.js +27 -27
- package/lib/output.js +91 -83
- package/lib/resize.js +15 -15
- package/lib/utility.js +8 -8
- package/package.json +17 -15
- package/src/common.cc +16 -0
- package/src/common.h +2 -0
- package/src/metadata.cc +21 -0
- package/src/metadata.h +1 -0
- package/src/pipeline.cc +8 -5
- package/src/sharp.cc +7 -1
- package/src/stats.cc +12 -1
- package/src/stats.h +3 -1
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
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->
|
|
649
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|