sharp 0.25.1 → 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.
- package/README.md +7 -6
- package/binding.gyp +81 -70
- package/install/dll-copy.js +5 -2
- package/install/libvips.js +20 -14
- package/lib/channel.js +7 -10
- package/lib/colour.js +5 -5
- package/lib/composite.js +2 -5
- package/lib/constructor.js +81 -18
- package/lib/input.js +39 -5
- package/lib/is.js +6 -6
- package/lib/libvips.js +2 -8
- package/lib/operation.js +27 -27
- package/lib/output.js +178 -88
- package/lib/platform.js +2 -1
- package/lib/resize.js +15 -15
- package/lib/utility.js +9 -9
- package/package.json +26 -20
- package/src/common.cc +129 -50
- package/src/common.h +23 -2
- package/src/libvips/cplusplus/VImage.cpp +451 -450
- package/src/libvips/cplusplus/vips-operators.cpp +117 -1
- package/src/metadata.cc +21 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +0 -23
- package/src/operations.h +0 -10
- package/src/pipeline.cc +78 -16
- package/src/pipeline.h +9 -2
- package/src/sharp.cc +7 -1
- package/src/stats.cc +29 -1
- package/src/stats.h +9 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// bodies for vips operations
|
|
2
|
-
//
|
|
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
|
@@ -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/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
|
@@ -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
|
|
@@ -680,6 +684,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
680
684
|
}
|
|
681
685
|
}
|
|
682
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
|
+
|
|
683
696
|
// Override EXIF Orientation tag
|
|
684
697
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
|
685
698
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
|
@@ -689,6 +702,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
689
702
|
baton->channels = image.bands();
|
|
690
703
|
baton->width = image.width();
|
|
691
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
|
+
|
|
692
715
|
// Output
|
|
693
716
|
if (baton->fileOut.empty()) {
|
|
694
717
|
// Buffer output
|
|
@@ -699,7 +722,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
699
722
|
->set("strip", !baton->withMetadata)
|
|
700
723
|
->set("Q", baton->jpegQuality)
|
|
701
724
|
->set("interlace", baton->jpegProgressive)
|
|
702
|
-
->set("
|
|
725
|
+
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
726
|
+
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
|
727
|
+
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
|
703
728
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
704
729
|
->set("quant_table", baton->jpegQuantisationTable)
|
|
705
730
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
@@ -716,8 +741,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
716
741
|
baton->channels = std::min(baton->channels, 3);
|
|
717
742
|
}
|
|
718
743
|
} else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
|
|
719
|
-
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
|
|
720
|
-
|
|
744
|
+
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
|
745
|
+
inputImageType == sharp::ImageType::SVG))) {
|
|
721
746
|
// Write PNG to buffer
|
|
722
747
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
723
748
|
VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
|
|
@@ -751,6 +776,18 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
751
776
|
area->free_fn = nullptr;
|
|
752
777
|
vips_area_unref(area);
|
|
753
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";
|
|
754
791
|
} else if (baton->formatOut == "tiff" ||
|
|
755
792
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
|
|
756
793
|
// Write TIFF to buffer
|
|
@@ -765,7 +802,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
765
802
|
VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
|
|
766
803
|
->set("strip", !baton->withMetadata)
|
|
767
804
|
->set("Q", baton->tiffQuality)
|
|
768
|
-
->set("
|
|
805
|
+
->set("bitdepth", baton->tiffBitdepth)
|
|
769
806
|
->set("compression", baton->tiffCompression)
|
|
770
807
|
->set("predictor", baton->tiffPredictor)
|
|
771
808
|
->set("pyramid", baton->tiffPyramid)
|
|
@@ -826,13 +863,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
826
863
|
bool const isJpeg = sharp::IsJpeg(baton->fileOut);
|
|
827
864
|
bool const isPng = sharp::IsPng(baton->fileOut);
|
|
828
865
|
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
|
866
|
+
bool const isGif = sharp::IsGif(baton->fileOut);
|
|
829
867
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
|
830
868
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
|
831
869
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
|
832
870
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
|
833
871
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
834
872
|
bool const mightMatchInput = baton->formatOut == "input";
|
|
835
|
-
bool const willMatchInput = mightMatchInput &&
|
|
873
|
+
bool const willMatchInput = mightMatchInput &&
|
|
874
|
+
!(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
|
|
875
|
+
|
|
836
876
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
|
837
877
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
838
878
|
// Write JPEG to file
|
|
@@ -841,7 +881,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
841
881
|
->set("strip", !baton->withMetadata)
|
|
842
882
|
->set("Q", baton->jpegQuality)
|
|
843
883
|
->set("interlace", baton->jpegProgressive)
|
|
844
|
-
->set("
|
|
884
|
+
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
885
|
+
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
|
886
|
+
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
|
845
887
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
846
888
|
->set("quant_table", baton->jpegQuantisationTable)
|
|
847
889
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
@@ -850,8 +892,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
850
892
|
baton->formatOut = "jpeg";
|
|
851
893
|
baton->channels = std::min(baton->channels, 3);
|
|
852
894
|
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
|
853
|
-
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
|
|
854
|
-
|
|
895
|
+
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
|
896
|
+
inputImageType == sharp::ImageType::SVG))) {
|
|
855
897
|
// Write PNG to file
|
|
856
898
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
857
899
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
@@ -877,6 +919,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
877
919
|
->set("reduction_effort", baton->webpReductionEffort)
|
|
878
920
|
->set("alpha_q", baton->webpAlphaQuality));
|
|
879
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";
|
|
880
930
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
881
931
|
(willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
|
|
882
932
|
// Write TIFF to file
|
|
@@ -887,7 +937,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
887
937
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
888
938
|
->set("strip", !baton->withMetadata)
|
|
889
939
|
->set("Q", baton->tiffQuality)
|
|
890
|
-
->set("
|
|
940
|
+
->set("bitdepth", baton->tiffBitdepth)
|
|
891
941
|
->set("compression", baton->tiffCompression)
|
|
892
942
|
->set("predictor", baton->tiffPredictor)
|
|
893
943
|
->set("pyramid", baton->tiffPyramid)
|
|
@@ -933,19 +983,17 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
933
983
|
};
|
|
934
984
|
suffix = AssembleSuffixString(".webp", options);
|
|
935
985
|
} else {
|
|
936
|
-
std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE
|
|
937
|
-
|| baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY
|
|
938
|
-
? ".jpg" : ".jpeg";
|
|
939
986
|
std::vector<std::pair<std::string, std::string>> options {
|
|
940
987
|
{"Q", std::to_string(baton->jpegQuality)},
|
|
941
988
|
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
|
|
942
|
-
{"
|
|
989
|
+
{"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
|
|
943
990
|
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
|
|
944
991
|
{"quant_table", std::to_string(baton->jpegQuantisationTable)},
|
|
945
992
|
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
|
|
946
993
|
{"optimize_scans", baton->jpegOptimiseScans ? "TRUE": "FALSE"},
|
|
947
994
|
{"optimize_coding", baton->jpegOptimiseCoding ? "TRUE": "FALSE"}
|
|
948
995
|
};
|
|
996
|
+
std::string extname = baton->tileLayout == VIPS_FOREIGN_DZ_LAYOUT_DZ ? ".jpeg" : ".jpg";
|
|
949
997
|
suffix = AssembleSuffixString(extname, options);
|
|
950
998
|
}
|
|
951
999
|
// Remove alpha channel from tile background if image does not contain an alpha channel
|
|
@@ -1280,6 +1328,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1280
1328
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
|
1281
1329
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
|
1282
1330
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
|
1331
|
+
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
|
1283
1332
|
// Format-specific
|
|
1284
1333
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
|
1285
1334
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
|
@@ -1304,7 +1353,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1304
1353
|
baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
|
|
1305
1354
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1306
1355
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1307
|
-
baton->
|
|
1356
|
+
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
1308
1357
|
baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
|
|
1309
1358
|
baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
|
|
1310
1359
|
baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
|
|
@@ -1322,6 +1371,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1322
1371
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1323
1372
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1324
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
|
+
|
|
1325
1386
|
// Tile output
|
|
1326
1387
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1327
1388
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|
|
@@ -1362,6 +1423,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1362
1423
|
// Join queue for worker thread
|
|
1363
1424
|
Napi::Function callback = info[1].As<Napi::Function>();
|
|
1364
1425
|
PipelineWorker *worker = new PipelineWorker(callback, baton, debuglog, queueListener);
|
|
1426
|
+
worker->Receiver().Set("options", options);
|
|
1365
1427
|
worker->Queue();
|
|
1366
1428
|
|
|
1367
1429
|
// Increment queued task counter
|
package/src/pipeline.h
CHANGED
|
@@ -143,7 +143,7 @@ struct PipelineBaton {
|
|
|
143
143
|
VipsForeignTiffCompression tiffCompression;
|
|
144
144
|
VipsForeignTiffPredictor tiffPredictor;
|
|
145
145
|
bool tiffPyramid;
|
|
146
|
-
|
|
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
|
-
|
|
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/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,29 @@ 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();
|
|
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;
|
|
80
101
|
} catch (vips::VError const &err) {
|
|
81
102
|
(baton->err).append(err.what());
|
|
82
103
|
}
|
|
@@ -123,6 +144,12 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
123
144
|
info.Set("channels", channels);
|
|
124
145
|
info.Set("isOpaque", baton->isOpaque);
|
|
125
146
|
info.Set("entropy", baton->entropy);
|
|
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);
|
|
126
153
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
|
127
154
|
} else {
|
|
128
155
|
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
|
|
@@ -154,6 +181,7 @@ Napi::Value stats(const Napi::CallbackInfo& info) {
|
|
|
154
181
|
// Join queue for worker thread
|
|
155
182
|
Napi::Function callback = info[1].As<Napi::Function>();
|
|
156
183
|
StatsWorker *worker = new StatsWorker(callback, baton, debuglog);
|
|
184
|
+
worker->Receiver().Set("options", options);
|
|
157
185
|
worker->Queue();
|
|
158
186
|
|
|
159
187
|
// Increment queued task counter
|
package/src/stats.h
CHANGED
|
@@ -47,13 +47,21 @@ struct StatsBaton {
|
|
|
47
47
|
std::vector<ChannelStats> channelStats;
|
|
48
48
|
bool isOpaque;
|
|
49
49
|
double entropy;
|
|
50
|
+
double sharpness;
|
|
51
|
+
int dominantRed;
|
|
52
|
+
int dominantGreen;
|
|
53
|
+
int dominantBlue;
|
|
50
54
|
|
|
51
55
|
std::string err;
|
|
52
56
|
|
|
53
57
|
StatsBaton():
|
|
54
58
|
input(nullptr),
|
|
55
59
|
isOpaque(true),
|
|
56
|
-
entropy(0.0)
|
|
60
|
+
entropy(0.0),
|
|
61
|
+
sharpness(0.0),
|
|
62
|
+
dominantRed(0),
|
|
63
|
+
dominantGreen(0),
|
|
64
|
+
dominantBlue(0)
|
|
57
65
|
{}
|
|
58
66
|
};
|
|
59
67
|
|