sharp 0.25.3 → 0.26.2
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 +1 -1
- package/binding.gyp +82 -71
- package/install/dll-copy.js +3 -1
- package/install/libvips.js +26 -14
- package/lib/channel.js +5 -8
- package/lib/constructor.js +34 -18
- package/lib/input.js +27 -5
- package/lib/is.js +4 -4
- package/lib/libvips.js +2 -8
- package/lib/output.js +130 -35
- package/lib/platform.js +2 -1
- package/lib/resize.js +2 -1
- package/lib/utility.js +1 -1
- package/package.json +22 -17
- package/src/common.cc +125 -50
- package/src/common.h +24 -2
- package/src/libvips/cplusplus/VImage.cpp +451 -450
- package/src/libvips/cplusplus/vips-operators.cpp +117 -1
- package/src/metadata.cc +20 -0
- package/src/metadata.h +1 -0
- package/src/operations.cc +0 -23
- package/src/operations.h +0 -10
- package/src/pipeline.cc +82 -13
- package/src/pipeline.h +10 -2
- package/src/stats.cc +30 -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) {
|
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,20 @@ 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("optimize_gif_frames", TRUE)
|
|
786
|
+
->set("optimize_gif_transparency", TRUE)
|
|
787
|
+
->set("format", "gif")));
|
|
788
|
+
baton->bufferOut = static_cast<char*>(area->data);
|
|
789
|
+
baton->bufferOutLength = area->length;
|
|
790
|
+
area->free_fn = nullptr;
|
|
791
|
+
vips_area_unref(area);
|
|
792
|
+
baton->formatOut = "gif";
|
|
754
793
|
} else if (baton->formatOut == "tiff" ||
|
|
755
794
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::TIFF)) {
|
|
756
795
|
// Write TIFF to buffer
|
|
@@ -765,7 +804,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
765
804
|
VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
|
|
766
805
|
->set("strip", !baton->withMetadata)
|
|
767
806
|
->set("Q", baton->tiffQuality)
|
|
768
|
-
->set("
|
|
807
|
+
->set("bitdepth", baton->tiffBitdepth)
|
|
769
808
|
->set("compression", baton->tiffCompression)
|
|
770
809
|
->set("predictor", baton->tiffPredictor)
|
|
771
810
|
->set("pyramid", baton->tiffPyramid)
|
|
@@ -826,13 +865,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
826
865
|
bool const isJpeg = sharp::IsJpeg(baton->fileOut);
|
|
827
866
|
bool const isPng = sharp::IsPng(baton->fileOut);
|
|
828
867
|
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
|
868
|
+
bool const isGif = sharp::IsGif(baton->fileOut);
|
|
829
869
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
|
830
870
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
|
831
871
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
|
832
872
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
|
833
873
|
bool const isV = sharp::IsV(baton->fileOut);
|
|
834
874
|
bool const mightMatchInput = baton->formatOut == "input";
|
|
835
|
-
bool const willMatchInput = mightMatchInput &&
|
|
875
|
+
bool const willMatchInput = mightMatchInput &&
|
|
876
|
+
!(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
|
|
877
|
+
|
|
836
878
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
|
837
879
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
|
838
880
|
// Write JPEG to file
|
|
@@ -841,7 +883,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
841
883
|
->set("strip", !baton->withMetadata)
|
|
842
884
|
->set("Q", baton->jpegQuality)
|
|
843
885
|
->set("interlace", baton->jpegProgressive)
|
|
844
|
-
->set("
|
|
886
|
+
->set("subsample_mode", baton->jpegChromaSubsampling == "4:4:4"
|
|
887
|
+
? VIPS_FOREIGN_JPEG_SUBSAMPLE_OFF
|
|
888
|
+
: VIPS_FOREIGN_JPEG_SUBSAMPLE_ON)
|
|
845
889
|
->set("trellis_quant", baton->jpegTrellisQuantisation)
|
|
846
890
|
->set("quant_table", baton->jpegQuantisationTable)
|
|
847
891
|
->set("overshoot_deringing", baton->jpegOvershootDeringing)
|
|
@@ -850,8 +894,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
850
894
|
baton->formatOut = "jpeg";
|
|
851
895
|
baton->channels = std::min(baton->channels, 3);
|
|
852
896
|
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
|
853
|
-
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::GIF ||
|
|
854
|
-
|
|
897
|
+
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
|
898
|
+
inputImageType == sharp::ImageType::SVG))) {
|
|
855
899
|
// Write PNG to file
|
|
856
900
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
857
901
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
@@ -877,6 +921,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
877
921
|
->set("reduction_effort", baton->webpReductionEffort)
|
|
878
922
|
->set("alpha_q", baton->webpAlphaQuality));
|
|
879
923
|
baton->formatOut = "webp";
|
|
924
|
+
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
|
925
|
+
(willMatchInput && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
|
926
|
+
// Write GIF to file
|
|
927
|
+
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
928
|
+
image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
929
|
+
->set("strip", !baton->withMetadata)
|
|
930
|
+
->set("optimize_gif_frames", TRUE)
|
|
931
|
+
->set("optimize_gif_transparency", TRUE)
|
|
932
|
+
->set("format", "gif"));
|
|
933
|
+
baton->formatOut = "gif";
|
|
880
934
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
|
881
935
|
(willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
|
|
882
936
|
// Write TIFF to file
|
|
@@ -887,7 +941,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
887
941
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
888
942
|
->set("strip", !baton->withMetadata)
|
|
889
943
|
->set("Q", baton->tiffQuality)
|
|
890
|
-
->set("
|
|
944
|
+
->set("bitdepth", baton->tiffBitdepth)
|
|
891
945
|
->set("compression", baton->tiffCompression)
|
|
892
946
|
->set("predictor", baton->tiffPredictor)
|
|
893
947
|
->set("pyramid", baton->tiffPyramid)
|
|
@@ -936,7 +990,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
936
990
|
std::vector<std::pair<std::string, std::string>> options {
|
|
937
991
|
{"Q", std::to_string(baton->jpegQuality)},
|
|
938
992
|
{"interlace", baton->jpegProgressive ? "TRUE" : "FALSE"},
|
|
939
|
-
{"
|
|
993
|
+
{"subsample_mode", baton->jpegChromaSubsampling == "4:4:4" ? "off" : "on"},
|
|
940
994
|
{"trellis_quant", baton->jpegTrellisQuantisation ? "TRUE" : "FALSE"},
|
|
941
995
|
{"quant_table", std::to_string(baton->jpegQuantisationTable)},
|
|
942
996
|
{"overshoot_deringing", baton->jpegOvershootDeringing ? "TRUE": "FALSE"},
|
|
@@ -960,6 +1014,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
960
1014
|
->set("suffix", const_cast<char*>(suffix.data()))
|
|
961
1015
|
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
|
962
1016
|
->set("background", baton->tileBackground)
|
|
1017
|
+
->set("centre", baton->tileCentre)
|
|
963
1018
|
->set("skip_blanks", baton->tileSkipBlanks);
|
|
964
1019
|
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
|
965
1020
|
// not passing anything - libvips will handle choice
|
|
@@ -1278,6 +1333,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1278
1333
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
|
1279
1334
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
|
1280
1335
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
|
1336
|
+
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
|
1281
1337
|
// Format-specific
|
|
1282
1338
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
|
1283
1339
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
|
@@ -1302,7 +1358,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1302
1358
|
baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
|
|
1303
1359
|
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
|
1304
1360
|
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
|
1305
|
-
baton->
|
|
1361
|
+
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
|
1306
1362
|
baton->tiffTile = sharp::AttrAsBool(options, "tiffTile");
|
|
1307
1363
|
baton->tiffTileWidth = sharp::AttrAsUint32(options, "tiffTileWidth");
|
|
1308
1364
|
baton->tiffTileHeight = sharp::AttrAsUint32(options, "tiffTileHeight");
|
|
@@ -1320,6 +1376,18 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1320
1376
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
|
1321
1377
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1322
1378
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1379
|
+
|
|
1380
|
+
// Animated output
|
|
1381
|
+
if (sharp::HasAttr(options, "pageHeight")) {
|
|
1382
|
+
baton->pageHeight = sharp::AttrAsUint32(options, "pageHeight");
|
|
1383
|
+
}
|
|
1384
|
+
if (sharp::HasAttr(options, "loop")) {
|
|
1385
|
+
baton->loop = sharp::AttrAsUint32(options, "loop");
|
|
1386
|
+
}
|
|
1387
|
+
if (sharp::HasAttr(options, "delay")) {
|
|
1388
|
+
baton->delay = sharp::AttrAsInt32Vector(options, "delay");
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1323
1391
|
// Tile output
|
|
1324
1392
|
baton->tileSize = sharp::AttrAsUint32(options, "tileSize");
|
|
1325
1393
|
baton->tileOverlap = sharp::AttrAsUint32(options, "tileOverlap");
|
|
@@ -1336,6 +1404,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1336
1404
|
baton->tileDepth = static_cast<VipsForeignDzDepth>(
|
|
1337
1405
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH,
|
|
1338
1406
|
sharp::AttrAsStr(options, "tileDepth").data()));
|
|
1407
|
+
baton->tileCentre = sharp::AttrAsBool(options, "tileCentre");
|
|
1339
1408
|
|
|
1340
1409
|
// Force random access for certain operations
|
|
1341
1410
|
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
|
package/src/pipeline.h
CHANGED
|
@@ -79,6 +79,7 @@ struct PipelineBaton {
|
|
|
79
79
|
int cropOffsetLeft;
|
|
80
80
|
int cropOffsetTop;
|
|
81
81
|
bool premultiplied;
|
|
82
|
+
bool tileCentre;
|
|
82
83
|
std::string kernel;
|
|
83
84
|
bool fastShrinkOnLoad;
|
|
84
85
|
double tintA;
|
|
@@ -143,7 +144,7 @@ struct PipelineBaton {
|
|
|
143
144
|
VipsForeignTiffCompression tiffCompression;
|
|
144
145
|
VipsForeignTiffPredictor tiffPredictor;
|
|
145
146
|
bool tiffPyramid;
|
|
146
|
-
|
|
147
|
+
int tiffBitdepth;
|
|
147
148
|
bool tiffTile;
|
|
148
149
|
int tiffTileHeight;
|
|
149
150
|
int tiffTileWidth;
|
|
@@ -155,6 +156,7 @@ struct PipelineBaton {
|
|
|
155
156
|
std::string err;
|
|
156
157
|
bool withMetadata;
|
|
157
158
|
int withMetadataOrientation;
|
|
159
|
+
std::string withMetadataIcc;
|
|
158
160
|
std::unique_ptr<double[]> convKernel;
|
|
159
161
|
int convKernelWidth;
|
|
160
162
|
int convKernelHeight;
|
|
@@ -167,6 +169,9 @@ struct PipelineBaton {
|
|
|
167
169
|
bool removeAlpha;
|
|
168
170
|
bool ensureAlpha;
|
|
169
171
|
VipsInterpretation colourspace;
|
|
172
|
+
int pageHeight;
|
|
173
|
+
std::vector<int> delay;
|
|
174
|
+
int loop;
|
|
170
175
|
int tileSize;
|
|
171
176
|
int tileOverlap;
|
|
172
177
|
VipsForeignDzContainer tileContainer;
|
|
@@ -251,7 +256,7 @@ struct PipelineBaton {
|
|
|
251
256
|
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
|
252
257
|
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
|
253
258
|
tiffPyramid(false),
|
|
254
|
-
|
|
259
|
+
tiffBitdepth(8),
|
|
255
260
|
tiffTile(false),
|
|
256
261
|
tiffTileHeight(256),
|
|
257
262
|
tiffTileWidth(256),
|
|
@@ -273,6 +278,9 @@ struct PipelineBaton {
|
|
|
273
278
|
removeAlpha(false),
|
|
274
279
|
ensureAlpha(false),
|
|
275
280
|
colourspace(VIPS_INTERPRETATION_LAST),
|
|
281
|
+
pageHeight(0),
|
|
282
|
+
delay{-1},
|
|
283
|
+
loop(-1),
|
|
276
284
|
tileSize(256),
|
|
277
285
|
tileOverlap(0),
|
|
278
286
|
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
package/src/stats.cc
CHANGED
|
@@ -75,8 +75,31 @@ 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
|
+
if (image.width() > 1 || image.height() > 1) {
|
|
84
|
+
VImage laplacian = VImage::new_matrixv(3, 3,
|
|
85
|
+
0.0, 1.0, 0.0,
|
|
86
|
+
1.0, -4.0, 1.0,
|
|
87
|
+
0.0, 1.0, 0.0);
|
|
88
|
+
laplacian.set("scale", 9.0);
|
|
89
|
+
baton->sharpness = greyscale.conv(laplacian).deviate();
|
|
90
|
+
}
|
|
91
|
+
// Most dominant sRGB colour via 4096-bin 3D histogram
|
|
92
|
+
vips::VImage hist = sharp::RemoveAlpha(image)
|
|
93
|
+
.colourspace(VIPS_INTERPRETATION_sRGB)
|
|
94
|
+
.hist_find_ndim(VImage::option()->set("bins", 16));
|
|
95
|
+
std::complex<double> maxpos = hist.maxpos();
|
|
96
|
+
int const dx = static_cast<int>(std::real(maxpos));
|
|
97
|
+
int const dy = static_cast<int>(std::imag(maxpos));
|
|
98
|
+
std::vector<double> pel = hist(dx, dy);
|
|
99
|
+
int const dz = std::distance(pel.begin(), std::find(pel.begin(), pel.end(), hist.max()));
|
|
100
|
+
baton->dominantRed = dx * 16 + 8;
|
|
101
|
+
baton->dominantGreen = dy * 16 + 8;
|
|
102
|
+
baton->dominantBlue = dz * 16 + 8;
|
|
80
103
|
} catch (vips::VError const &err) {
|
|
81
104
|
(baton->err).append(err.what());
|
|
82
105
|
}
|
|
@@ -123,6 +146,12 @@ class StatsWorker : public Napi::AsyncWorker {
|
|
|
123
146
|
info.Set("channels", channels);
|
|
124
147
|
info.Set("isOpaque", baton->isOpaque);
|
|
125
148
|
info.Set("entropy", baton->entropy);
|
|
149
|
+
info.Set("sharpness", baton->sharpness);
|
|
150
|
+
Napi::Object dominant = Napi::Object::New(env);
|
|
151
|
+
dominant.Set("r", baton->dominantRed);
|
|
152
|
+
dominant.Set("g", baton->dominantGreen);
|
|
153
|
+
dominant.Set("b", baton->dominantBlue);
|
|
154
|
+
info.Set("dominant", dominant);
|
|
126
155
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
|
127
156
|
} else {
|
|
128
157
|
Callback().MakeCallback(Receiver().Value(), { Napi::Error::New(env, baton->err).Value() });
|
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
|
|