sharp 0.17.2 → 0.18.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/CONTRIBUTING.md +1 -7
- package/binding.gyp +1 -1
- package/binding.js +14 -14
- package/docs/api-colour.md +2 -2
- package/docs/api-composite.md +11 -3
- package/docs/api-constructor.md +25 -5
- package/docs/api-input.md +4 -3
- package/docs/api-operation.md +20 -17
- package/docs/api-output.md +39 -30
- package/docs/api-resize.md +9 -8
- package/docs/api-utility.md +4 -4
- package/docs/changelog.md +103 -0
- package/docs/index.md +4 -1
- package/docs/install.md +44 -14
- package/lib/channel.js +6 -6
- package/lib/colour.js +10 -10
- package/lib/composite.js +11 -6
- package/lib/constructor.js +41 -18
- package/lib/input.js +52 -24
- package/lib/is.js +9 -0
- package/lib/operation.js +41 -38
- package/lib/output.js +103 -112
- package/lib/resize.js +25 -20
- package/lib/utility.js +8 -8
- package/package.json +22 -18
- package/src/common.cc +94 -20
- package/src/common.h +30 -3
- package/src/libvips/cplusplus/VImage.cpp +110 -104
- package/src/libvips/cplusplus/vips-operators.cpp +108 -44
- package/src/metadata.cc +19 -4
- package/src/metadata.h +1 -0
- package/src/operations.cc +18 -165
- package/src/operations.h +4 -26
- package/src/pipeline.cc +201 -97
- package/src/pipeline.h +14 -0
- package/src/sharp.cc +3 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// bodies for vips operations
|
|
2
|
-
//
|
|
2
|
+
// Mon 13 Mar 13:22:17 GMT 2017
|
|
3
3
|
// this file is generated automatically, do not edit!
|
|
4
4
|
|
|
5
5
|
void VImage::system( char * cmd_format , VOption *options )
|
|
@@ -231,7 +231,7 @@ VImage VImage::round( VipsOperationRound round , VOption *options )
|
|
|
231
231
|
return( out );
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
VImage VImage::relational_const( std::vector<double> c ,
|
|
234
|
+
VImage VImage::relational_const( VipsOperationRelational relational , std::vector<double> c , VOption *options )
|
|
235
235
|
{
|
|
236
236
|
VImage out;
|
|
237
237
|
|
|
@@ -239,8 +239,8 @@ VImage VImage::relational_const( std::vector<double> c , VipsOperationRelational
|
|
|
239
239
|
(options ? options : VImage::option()) ->
|
|
240
240
|
set( "in", *this ) ->
|
|
241
241
|
set( "out", &out ) ->
|
|
242
|
-
set( "
|
|
243
|
-
set( "
|
|
242
|
+
set( "relational", relational ) ->
|
|
243
|
+
set( "c", c ) );
|
|
244
244
|
|
|
245
245
|
return( out );
|
|
246
246
|
}
|
|
@@ -258,7 +258,7 @@ VImage VImage::remainder_const( std::vector<double> c , VOption *options )
|
|
|
258
258
|
return( out );
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
VImage VImage::boolean_const( std::vector<double> c ,
|
|
261
|
+
VImage VImage::boolean_const( VipsOperationBoolean boolean , std::vector<double> c , VOption *options )
|
|
262
262
|
{
|
|
263
263
|
VImage out;
|
|
264
264
|
|
|
@@ -266,13 +266,13 @@ VImage VImage::boolean_const( std::vector<double> c , VipsOperationBoolean boole
|
|
|
266
266
|
(options ? options : VImage::option()) ->
|
|
267
267
|
set( "in", *this ) ->
|
|
268
268
|
set( "out", &out ) ->
|
|
269
|
-
set( "
|
|
270
|
-
set( "
|
|
269
|
+
set( "boolean", boolean ) ->
|
|
270
|
+
set( "c", c ) );
|
|
271
271
|
|
|
272
272
|
return( out );
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
VImage VImage::math2_const( std::vector<double> c ,
|
|
275
|
+
VImage VImage::math2_const( VipsOperationMath2 math2 , std::vector<double> c , VOption *options )
|
|
276
276
|
{
|
|
277
277
|
VImage out;
|
|
278
278
|
|
|
@@ -280,8 +280,8 @@ VImage VImage::math2_const( std::vector<double> c , VipsOperationMath2 math2 , V
|
|
|
280
280
|
(options ? options : VImage::option()) ->
|
|
281
281
|
set( "in", *this ) ->
|
|
282
282
|
set( "out", &out ) ->
|
|
283
|
-
set( "
|
|
284
|
-
set( "
|
|
283
|
+
set( "math2", math2 ) ->
|
|
284
|
+
set( "c", c ) );
|
|
285
285
|
|
|
286
286
|
return( out );
|
|
287
287
|
}
|
|
@@ -493,8 +493,8 @@ VImage VImage::copy( VOption *options )
|
|
|
493
493
|
|
|
494
494
|
call( "copy" ,
|
|
495
495
|
(options ? options : VImage::option()) ->
|
|
496
|
-
set( "
|
|
497
|
-
set( "
|
|
496
|
+
set( "in", *this ) ->
|
|
497
|
+
set( "out", &out ) );
|
|
498
498
|
|
|
499
499
|
return( out );
|
|
500
500
|
}
|
|
@@ -505,8 +505,8 @@ VImage VImage::tilecache( VOption *options )
|
|
|
505
505
|
|
|
506
506
|
call( "tilecache" ,
|
|
507
507
|
(options ? options : VImage::option()) ->
|
|
508
|
-
set( "
|
|
509
|
-
set( "
|
|
508
|
+
set( "in", *this ) ->
|
|
509
|
+
set( "out", &out ) );
|
|
510
510
|
|
|
511
511
|
return( out );
|
|
512
512
|
}
|
|
@@ -517,8 +517,8 @@ VImage VImage::linecache( VOption *options )
|
|
|
517
517
|
|
|
518
518
|
call( "linecache" ,
|
|
519
519
|
(options ? options : VImage::option()) ->
|
|
520
|
-
set( "
|
|
521
|
-
set( "
|
|
520
|
+
set( "in", *this ) ->
|
|
521
|
+
set( "out", &out ) );
|
|
522
522
|
|
|
523
523
|
return( out );
|
|
524
524
|
}
|
|
@@ -529,8 +529,8 @@ VImage VImage::sequential( VOption *options )
|
|
|
529
529
|
|
|
530
530
|
call( "sequential" ,
|
|
531
531
|
(options ? options : VImage::option()) ->
|
|
532
|
-
set( "
|
|
533
|
-
set( "
|
|
532
|
+
set( "in", *this ) ->
|
|
533
|
+
set( "out", &out ) );
|
|
534
534
|
|
|
535
535
|
return( out );
|
|
536
536
|
}
|
|
@@ -541,8 +541,8 @@ VImage VImage::cache( VOption *options )
|
|
|
541
541
|
|
|
542
542
|
call( "cache" ,
|
|
543
543
|
(options ? options : VImage::option()) ->
|
|
544
|
-
set( "
|
|
545
|
-
set( "
|
|
544
|
+
set( "in", *this ) ->
|
|
545
|
+
set( "out", &out ) );
|
|
546
546
|
|
|
547
547
|
return( out );
|
|
548
548
|
}
|
|
@@ -569,8 +569,8 @@ VImage VImage::flip( VipsDirection direction , VOption *options )
|
|
|
569
569
|
|
|
570
570
|
call( "flip" ,
|
|
571
571
|
(options ? options : VImage::option()) ->
|
|
572
|
-
set( "out", &out ) ->
|
|
573
572
|
set( "in", *this ) ->
|
|
573
|
+
set( "out", &out ) ->
|
|
574
574
|
set( "direction", direction ) );
|
|
575
575
|
|
|
576
576
|
return( out );
|
|
@@ -633,6 +633,20 @@ VImage VImage::extract_area( int left , int top , int width , int height , VOpti
|
|
|
633
633
|
return( out );
|
|
634
634
|
}
|
|
635
635
|
|
|
636
|
+
VImage VImage::smartcrop( int width , int height , VOption *options )
|
|
637
|
+
{
|
|
638
|
+
VImage out;
|
|
639
|
+
|
|
640
|
+
call( "smartcrop" ,
|
|
641
|
+
(options ? options : VImage::option()) ->
|
|
642
|
+
set( "input", *this ) ->
|
|
643
|
+
set( "out", &out ) ->
|
|
644
|
+
set( "width", width ) ->
|
|
645
|
+
set( "height", height ) );
|
|
646
|
+
|
|
647
|
+
return( out );
|
|
648
|
+
}
|
|
649
|
+
|
|
636
650
|
VImage VImage::extract_band( int band , VOption *options )
|
|
637
651
|
{
|
|
638
652
|
VImage out;
|
|
@@ -728,8 +742,8 @@ VImage VImage::cast( VipsBandFormat format , VOption *options )
|
|
|
728
742
|
|
|
729
743
|
call( "cast" ,
|
|
730
744
|
(options ? options : VImage::option()) ->
|
|
731
|
-
set( "out", &out ) ->
|
|
732
745
|
set( "in", *this ) ->
|
|
746
|
+
set( "out", &out ) ->
|
|
733
747
|
set( "format", format ) );
|
|
734
748
|
|
|
735
749
|
return( out );
|
|
@@ -741,8 +755,8 @@ VImage VImage::rot( VipsAngle angle , VOption *options )
|
|
|
741
755
|
|
|
742
756
|
call( "rot" ,
|
|
743
757
|
(options ? options : VImage::option()) ->
|
|
744
|
-
set( "out", &out ) ->
|
|
745
758
|
set( "in", *this ) ->
|
|
759
|
+
set( "out", &out ) ->
|
|
746
760
|
set( "angle", angle ) );
|
|
747
761
|
|
|
748
762
|
return( out );
|
|
@@ -754,8 +768,8 @@ VImage VImage::rot45( VOption *options )
|
|
|
754
768
|
|
|
755
769
|
call( "rot45" ,
|
|
756
770
|
(options ? options : VImage::option()) ->
|
|
757
|
-
set( "
|
|
758
|
-
set( "
|
|
771
|
+
set( "in", *this ) ->
|
|
772
|
+
set( "out", &out ) );
|
|
759
773
|
|
|
760
774
|
return( out );
|
|
761
775
|
}
|
|
@@ -766,8 +780,8 @@ VImage VImage::autorot( VOption *options )
|
|
|
766
780
|
|
|
767
781
|
call( "autorot" ,
|
|
768
782
|
(options ? options : VImage::option()) ->
|
|
769
|
-
set( "
|
|
770
|
-
set( "
|
|
783
|
+
set( "in", *this ) ->
|
|
784
|
+
set( "out", &out ) );
|
|
771
785
|
|
|
772
786
|
return( out );
|
|
773
787
|
}
|
|
@@ -805,8 +819,8 @@ VImage VImage::bandfold( VOption *options )
|
|
|
805
819
|
|
|
806
820
|
call( "bandfold" ,
|
|
807
821
|
(options ? options : VImage::option()) ->
|
|
808
|
-
set( "
|
|
809
|
-
set( "
|
|
822
|
+
set( "in", *this ) ->
|
|
823
|
+
set( "out", &out ) );
|
|
810
824
|
|
|
811
825
|
return( out );
|
|
812
826
|
}
|
|
@@ -817,8 +831,8 @@ VImage VImage::bandunfold( VOption *options )
|
|
|
817
831
|
|
|
818
832
|
call( "bandunfold" ,
|
|
819
833
|
(options ? options : VImage::option()) ->
|
|
820
|
-
set( "
|
|
821
|
-
set( "
|
|
834
|
+
set( "in", *this ) ->
|
|
835
|
+
set( "out", &out ) );
|
|
822
836
|
|
|
823
837
|
return( out );
|
|
824
838
|
}
|
|
@@ -829,8 +843,8 @@ VImage VImage::flatten( VOption *options )
|
|
|
829
843
|
|
|
830
844
|
call( "flatten" ,
|
|
831
845
|
(options ? options : VImage::option()) ->
|
|
832
|
-
set( "
|
|
833
|
-
set( "
|
|
846
|
+
set( "in", *this ) ->
|
|
847
|
+
set( "out", &out ) );
|
|
834
848
|
|
|
835
849
|
return( out );
|
|
836
850
|
}
|
|
@@ -841,8 +855,8 @@ VImage VImage::premultiply( VOption *options )
|
|
|
841
855
|
|
|
842
856
|
call( "premultiply" ,
|
|
843
857
|
(options ? options : VImage::option()) ->
|
|
844
|
-
set( "
|
|
845
|
-
set( "
|
|
858
|
+
set( "in", *this ) ->
|
|
859
|
+
set( "out", &out ) );
|
|
846
860
|
|
|
847
861
|
return( out );
|
|
848
862
|
}
|
|
@@ -853,8 +867,8 @@ VImage VImage::unpremultiply( VOption *options )
|
|
|
853
867
|
|
|
854
868
|
call( "unpremultiply" ,
|
|
855
869
|
(options ? options : VImage::option()) ->
|
|
856
|
-
set( "
|
|
857
|
-
set( "
|
|
870
|
+
set( "in", *this ) ->
|
|
871
|
+
set( "out", &out ) );
|
|
858
872
|
|
|
859
873
|
return( out );
|
|
860
874
|
}
|
|
@@ -865,8 +879,8 @@ VImage VImage::grid( int tile_height , int across , int down , VOption *options
|
|
|
865
879
|
|
|
866
880
|
call( "grid" ,
|
|
867
881
|
(options ? options : VImage::option()) ->
|
|
868
|
-
set( "out", &out ) ->
|
|
869
882
|
set( "in", *this ) ->
|
|
883
|
+
set( "out", &out ) ->
|
|
870
884
|
set( "tile-height", tile_height ) ->
|
|
871
885
|
set( "across", across ) ->
|
|
872
886
|
set( "down", down ) );
|
|
@@ -880,8 +894,8 @@ VImage VImage::scale( VOption *options )
|
|
|
880
894
|
|
|
881
895
|
call( "scale" ,
|
|
882
896
|
(options ? options : VImage::option()) ->
|
|
883
|
-
set( "
|
|
884
|
-
set( "
|
|
897
|
+
set( "in", *this ) ->
|
|
898
|
+
set( "out", &out ) );
|
|
885
899
|
|
|
886
900
|
return( out );
|
|
887
901
|
}
|
|
@@ -892,8 +906,8 @@ VImage VImage::wrap( VOption *options )
|
|
|
892
906
|
|
|
893
907
|
call( "wrap" ,
|
|
894
908
|
(options ? options : VImage::option()) ->
|
|
895
|
-
set( "
|
|
896
|
-
set( "
|
|
909
|
+
set( "in", *this ) ->
|
|
910
|
+
set( "out", &out ) );
|
|
897
911
|
|
|
898
912
|
return( out );
|
|
899
913
|
}
|
|
@@ -944,8 +958,8 @@ VImage VImage::byteswap( VOption *options )
|
|
|
944
958
|
|
|
945
959
|
call( "byteswap" ,
|
|
946
960
|
(options ? options : VImage::option()) ->
|
|
947
|
-
set( "
|
|
948
|
-
set( "
|
|
961
|
+
set( "in", *this ) ->
|
|
962
|
+
set( "out", &out ) );
|
|
949
963
|
|
|
950
964
|
return( out );
|
|
951
965
|
}
|
|
@@ -1757,6 +1771,18 @@ void VImage::dzsave( char * filename , VOption *options )
|
|
|
1757
1771
|
set( "filename", filename ) );
|
|
1758
1772
|
}
|
|
1759
1773
|
|
|
1774
|
+
VipsBlob * VImage::dzsave_buffer( VOption *options )
|
|
1775
|
+
{
|
|
1776
|
+
VipsBlob * buffer;
|
|
1777
|
+
|
|
1778
|
+
call( "dzsave_buffer" ,
|
|
1779
|
+
(options ? options : VImage::option()) ->
|
|
1780
|
+
set( "in", *this ) ->
|
|
1781
|
+
set( "buffer", &buffer ) );
|
|
1782
|
+
|
|
1783
|
+
return( buffer );
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1760
1786
|
void VImage::pngsave( char * filename , VOption *options )
|
|
1761
1787
|
{
|
|
1762
1788
|
call( "pngsave" ,
|
|
@@ -1832,6 +1858,18 @@ void VImage::tiffsave( char * filename , VOption *options )
|
|
|
1832
1858
|
set( "filename", filename ) );
|
|
1833
1859
|
}
|
|
1834
1860
|
|
|
1861
|
+
VipsBlob * VImage::tiffsave_buffer( VOption *options )
|
|
1862
|
+
{
|
|
1863
|
+
VipsBlob * buffer;
|
|
1864
|
+
|
|
1865
|
+
call( "tiffsave_buffer" ,
|
|
1866
|
+
(options ? options : VImage::option()) ->
|
|
1867
|
+
set( "in", *this ) ->
|
|
1868
|
+
set( "buffer", &buffer ) );
|
|
1869
|
+
|
|
1870
|
+
return( buffer );
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1835
1873
|
void VImage::fitssave( char * filename , VOption *options )
|
|
1836
1874
|
{
|
|
1837
1875
|
call( "fitssave" ,
|
|
@@ -1840,6 +1878,32 @@ void VImage::fitssave( char * filename , VOption *options )
|
|
|
1840
1878
|
set( "filename", filename ) );
|
|
1841
1879
|
}
|
|
1842
1880
|
|
|
1881
|
+
VImage VImage::thumbnail( char * filename , int width , VOption *options )
|
|
1882
|
+
{
|
|
1883
|
+
VImage out;
|
|
1884
|
+
|
|
1885
|
+
call( "thumbnail" ,
|
|
1886
|
+
(options ? options : VImage::option()) ->
|
|
1887
|
+
set( "filename", filename ) ->
|
|
1888
|
+
set( "out", &out ) ->
|
|
1889
|
+
set( "width", width ) );
|
|
1890
|
+
|
|
1891
|
+
return( out );
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
VImage VImage::thumbnail_buffer( VipsBlob * buffer , int width , VOption *options )
|
|
1895
|
+
{
|
|
1896
|
+
VImage out;
|
|
1897
|
+
|
|
1898
|
+
call( "thumbnail_buffer" ,
|
|
1899
|
+
(options ? options : VImage::option()) ->
|
|
1900
|
+
set( "buffer", buffer ) ->
|
|
1901
|
+
set( "out", &out ) ->
|
|
1902
|
+
set( "width", width ) );
|
|
1903
|
+
|
|
1904
|
+
return( out );
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1843
1907
|
VImage VImage::mapim( VImage index , VOption *options )
|
|
1844
1908
|
{
|
|
1845
1909
|
VImage out;
|
package/src/metadata.cc
CHANGED
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
class MetadataWorker : public Nan::AsyncWorker {
|
|
26
26
|
public:
|
|
27
27
|
MetadataWorker(
|
|
28
|
-
Nan::Callback *callback, MetadataBaton *baton,
|
|
29
|
-
std::vector<v8::Local<v8::Object>> const buffersToPersist)
|
|
30
|
-
|
|
28
|
+
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *debuglog,
|
|
29
|
+
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
|
|
30
|
+
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
|
|
31
|
+
buffersToPersist(buffersToPersist) {
|
|
31
32
|
// Protect Buffer objects from GC, keyed on index
|
|
32
33
|
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
|
|
33
34
|
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
|
|
@@ -56,6 +57,7 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
56
57
|
baton->height = image.height();
|
|
57
58
|
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
|
|
58
59
|
baton->channels = image.bands();
|
|
60
|
+
baton->depth = vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image.format());
|
|
59
61
|
if (sharp::HasDensity(image)) {
|
|
60
62
|
baton->density = sharp::GetDensity(image);
|
|
61
63
|
}
|
|
@@ -102,6 +104,7 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
102
104
|
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height));
|
|
103
105
|
Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked());
|
|
104
106
|
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(baton->channels));
|
|
107
|
+
Set(info, New("depth").ToLocalChecked(), New<v8::String>(baton->depth).ToLocalChecked());
|
|
105
108
|
if (baton->density > 0) {
|
|
106
109
|
Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density));
|
|
107
110
|
}
|
|
@@ -132,12 +135,21 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|
|
132
135
|
delete baton->input;
|
|
133
136
|
delete baton;
|
|
134
137
|
|
|
138
|
+
// Handle warnings
|
|
139
|
+
std::string warning = sharp::VipsWarningPop();
|
|
140
|
+
while (!warning.empty()) {
|
|
141
|
+
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
|
|
142
|
+
debuglog->Call(1, message);
|
|
143
|
+
warning = sharp::VipsWarningPop();
|
|
144
|
+
}
|
|
145
|
+
|
|
135
146
|
// Return to JavaScript
|
|
136
147
|
callback->Call(2, argv);
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
private:
|
|
140
151
|
MetadataBaton* baton;
|
|
152
|
+
Nan::Callback *debuglog;
|
|
141
153
|
std::vector<v8::Local<v8::Object>> buffersToPersist;
|
|
142
154
|
};
|
|
143
155
|
|
|
@@ -155,9 +167,12 @@ NAN_METHOD(metadata) {
|
|
|
155
167
|
// Input
|
|
156
168
|
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
|
|
157
169
|
|
|
170
|
+
// Function to notify of libvips warnings
|
|
171
|
+
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
|
|
172
|
+
|
|
158
173
|
// Join queue for worker thread
|
|
159
174
|
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
|
|
160
|
-
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, buffersToPersist));
|
|
175
|
+
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, debuglog, buffersToPersist));
|
|
161
176
|
|
|
162
177
|
// Increment queued task counter
|
|
163
178
|
g_atomic_int_inc(&sharp::counterQueue);
|
package/src/metadata.h
CHANGED
package/src/operations.cc
CHANGED
|
@@ -29,67 +29,32 @@ using vips::VError;
|
|
|
29
29
|
namespace sharp {
|
|
30
30
|
|
|
31
31
|
/*
|
|
32
|
-
|
|
33
|
-
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
|
32
|
+
Composite overlayImage over image at given position
|
|
33
|
+
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
|
34
34
|
*/
|
|
35
|
-
VImage Composite(VImage
|
|
36
|
-
if (
|
|
37
|
-
//
|
|
38
|
-
if (
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), gravity);
|
|
43
|
-
// Embed onto transparent background
|
|
44
|
-
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
|
|
45
|
-
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
|
|
35
|
+
VImage Composite(VImage image, VImage overlayImage, int const left, int const top) {
|
|
36
|
+
if (HasAlpha(overlayImage)) {
|
|
37
|
+
// Alpha composite
|
|
38
|
+
if (overlayImage.width() < image.width() || overlayImage.height() < image.height()) {
|
|
39
|
+
// Enlarge overlay
|
|
40
|
+
std::vector<double> const background { 0.0, 0.0, 0.0, 0.0 };
|
|
41
|
+
overlayImage = overlayImage.embed(left, top, image.width(), image.height(), VImage::option()
|
|
46
42
|
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
47
43
|
->set("background", background));
|
|
48
44
|
}
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (IsInputValidForComposition(src, dst)) {
|
|
57
|
-
// Enlarge overlay src, if required
|
|
58
|
-
if (src.width() < dst.width() || src.height() < dst.height()) {
|
|
59
|
-
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
|
|
60
|
-
int left;
|
|
61
|
-
int top;
|
|
62
|
-
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), x, y);
|
|
63
|
-
// Embed onto transparent background
|
|
64
|
-
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
|
|
65
|
-
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
|
|
66
|
-
->set("extend", VIPS_EXTEND_BACKGROUND)
|
|
67
|
-
->set("background", background));
|
|
45
|
+
return AlphaComposite(image, overlayImage);
|
|
46
|
+
} else {
|
|
47
|
+
if (HasAlpha(image)) {
|
|
48
|
+
// Add alpha channel to overlayImage so channels match
|
|
49
|
+
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
|
|
50
|
+
overlayImage = overlayImage.bandjoin(
|
|
51
|
+
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
|
|
68
52
|
}
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
// If the input was not valid for composition the return the input image itself
|
|
72
|
-
return dst;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
bool IsInputValidForComposition(VImage src, VImage dst) {
|
|
76
|
-
using sharp::CalculateCrop;
|
|
77
|
-
using sharp::HasAlpha;
|
|
78
|
-
|
|
79
|
-
if (!HasAlpha(src)) {
|
|
80
|
-
throw VError("Overlay image must have an alpha channel");
|
|
81
|
-
}
|
|
82
|
-
if (!HasAlpha(dst)) {
|
|
83
|
-
throw VError("Image to be overlaid must have an alpha channel");
|
|
84
|
-
}
|
|
85
|
-
if (src.width() > dst.width() || src.height() > dst.height()) {
|
|
86
|
-
throw VError("Overlay image must have same dimensions or smaller");
|
|
53
|
+
return image.insert(overlayImage, left, top);
|
|
87
54
|
}
|
|
88
|
-
|
|
89
|
-
return true;
|
|
90
55
|
}
|
|
91
56
|
|
|
92
|
-
VImage
|
|
57
|
+
VImage AlphaComposite(VImage dst, VImage src) {
|
|
93
58
|
// Split src into non-alpha and alpha channels
|
|
94
59
|
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
|
|
95
60
|
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
|
|
@@ -302,118 +267,6 @@ namespace sharp {
|
|
|
302
267
|
}
|
|
303
268
|
}
|
|
304
269
|
|
|
305
|
-
/*
|
|
306
|
-
Calculate the Shannon entropy
|
|
307
|
-
*/
|
|
308
|
-
double EntropyStrategy::operator()(VImage image) {
|
|
309
|
-
return image.hist_find().hist_entropy();
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/*
|
|
313
|
-
Calculate the intensity of edges, skin tone and saturation
|
|
314
|
-
*/
|
|
315
|
-
double AttentionStrategy::operator()(VImage image) {
|
|
316
|
-
// Flatten RGBA onto a mid-grey background
|
|
317
|
-
if (image.bands() == 4 && HasAlpha(image)) {
|
|
318
|
-
double const midgrey = sharp::Is16Bit(image.interpretation()) ? 32768.0 : 128.0;
|
|
319
|
-
std::vector<double> background { midgrey, midgrey, midgrey };
|
|
320
|
-
image = image.flatten(VImage::option()->set("background", background));
|
|
321
|
-
}
|
|
322
|
-
// Convert to LAB colourspace
|
|
323
|
-
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
|
|
324
|
-
VImage l = lab[0];
|
|
325
|
-
VImage a = lab[1];
|
|
326
|
-
VImage b = lab[2];
|
|
327
|
-
// Edge detect luminosity with the Sobel operator
|
|
328
|
-
VImage sobel = vips::VImage::new_matrixv(3, 3,
|
|
329
|
-
-1.0, 0.0, 1.0,
|
|
330
|
-
-2.0, 0.0, 2.0,
|
|
331
|
-
-1.0, 0.0, 1.0);
|
|
332
|
-
VImage edges = l.conv(sobel).abs() + l.conv(sobel.rot90()).abs();
|
|
333
|
-
// Skin tone chroma thresholds trained with http://humanae.tumblr.com/
|
|
334
|
-
VImage skin = (a >= 3) & (a <= 22) & (b >= 4) & (b <= 31);
|
|
335
|
-
// Chroma >~50% saturation
|
|
336
|
-
VImage lch = lab.colourspace(VIPS_INTERPRETATION_LCH);
|
|
337
|
-
VImage c = lch[1];
|
|
338
|
-
VImage saturation = c > 60;
|
|
339
|
-
// Find maximum in combined saliency mask
|
|
340
|
-
VImage mask = edges + skin + saturation;
|
|
341
|
-
return mask.max();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/*
|
|
345
|
-
Calculate crop area based on image entropy
|
|
346
|
-
*/
|
|
347
|
-
std::tuple<int, int> Crop(
|
|
348
|
-
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy
|
|
349
|
-
) {
|
|
350
|
-
int left = 0;
|
|
351
|
-
int top = 0;
|
|
352
|
-
int const inWidth = image.width();
|
|
353
|
-
int const inHeight = image.height();
|
|
354
|
-
if (inWidth > outWidth) {
|
|
355
|
-
// Reduce width by repeated removing slices from edge with lowest score
|
|
356
|
-
int width = inWidth;
|
|
357
|
-
double leftScore = 0.0;
|
|
358
|
-
double rightScore = 0.0;
|
|
359
|
-
// Max width of each slice
|
|
360
|
-
int const maxSliceWidth = static_cast<int>(ceil((inWidth - outWidth) / 8.0));
|
|
361
|
-
while (width > outWidth) {
|
|
362
|
-
// Width of current slice
|
|
363
|
-
int const slice = std::min(width - outWidth, maxSliceWidth);
|
|
364
|
-
if (leftScore == 0.0) {
|
|
365
|
-
// Update score of left slice
|
|
366
|
-
leftScore = strategy(image.extract_area(left, 0, slice, inHeight));
|
|
367
|
-
}
|
|
368
|
-
if (rightScore == 0.0) {
|
|
369
|
-
// Update score of right slice
|
|
370
|
-
rightScore = strategy(image.extract_area(width - slice - 1, 0, slice, inHeight));
|
|
371
|
-
}
|
|
372
|
-
// Keep slice with highest score
|
|
373
|
-
if (leftScore >= rightScore) {
|
|
374
|
-
// Discard right slice
|
|
375
|
-
rightScore = 0.0;
|
|
376
|
-
} else {
|
|
377
|
-
// Discard left slice
|
|
378
|
-
leftScore = 0.0;
|
|
379
|
-
left = left + slice;
|
|
380
|
-
}
|
|
381
|
-
width = width - slice;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (inHeight > outHeight) {
|
|
385
|
-
// Reduce height by repeated removing slices from edge with lowest score
|
|
386
|
-
int height = inHeight;
|
|
387
|
-
double topScore = 0.0;
|
|
388
|
-
double bottomScore = 0.0;
|
|
389
|
-
// Max height of each slice
|
|
390
|
-
int const maxSliceHeight = static_cast<int>(ceil((inHeight - outHeight) / 8.0));
|
|
391
|
-
while (height > outHeight) {
|
|
392
|
-
// Height of current slice
|
|
393
|
-
int const slice = std::min(height - outHeight, maxSliceHeight);
|
|
394
|
-
if (topScore == 0.0) {
|
|
395
|
-
// Update score of top slice
|
|
396
|
-
topScore = strategy(image.extract_area(0, top, inWidth, slice));
|
|
397
|
-
}
|
|
398
|
-
if (bottomScore == 0.0) {
|
|
399
|
-
// Update score of bottom slice
|
|
400
|
-
bottomScore = strategy(image.extract_area(0, height - slice - 1, inWidth, slice));
|
|
401
|
-
}
|
|
402
|
-
// Keep slice with highest score
|
|
403
|
-
if (topScore >= bottomScore) {
|
|
404
|
-
// Discard bottom slice
|
|
405
|
-
bottomScore = 0.0;
|
|
406
|
-
} else {
|
|
407
|
-
// Discard top slice
|
|
408
|
-
topScore = 0.0;
|
|
409
|
-
top = top + slice;
|
|
410
|
-
}
|
|
411
|
-
height = height - slice;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return std::make_tuple(left, top);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
270
|
/*
|
|
418
271
|
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
|
|
419
272
|
*/
|
package/src/operations.h
CHANGED
|
@@ -32,20 +32,14 @@ namespace sharp {
|
|
|
32
32
|
VImage Composite(VImage src, VImage dst, const int gravity);
|
|
33
33
|
|
|
34
34
|
/*
|
|
35
|
-
|
|
36
|
-
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
|
35
|
+
Composite overlayImage over image at given position
|
|
37
36
|
*/
|
|
38
|
-
VImage Composite(VImage
|
|
37
|
+
VImage Composite(VImage image, VImage overlayImage, int const x, int const y);
|
|
39
38
|
|
|
40
39
|
/*
|
|
41
|
-
|
|
40
|
+
Alpha composite overlayImage over image, assumes matching dimensions
|
|
42
41
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/*
|
|
46
|
-
Given a valid src and dst, returns the composite of the two images
|
|
47
|
-
*/
|
|
48
|
-
VImage CompositeImage(VImage src, VImage dst);
|
|
42
|
+
VImage AlphaComposite(VImage image, VImage overlayImage);
|
|
49
43
|
|
|
50
44
|
/*
|
|
51
45
|
Cutout src over dst with given gravity.
|
|
@@ -78,22 +72,6 @@ namespace sharp {
|
|
|
78
72
|
*/
|
|
79
73
|
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
|
|
80
74
|
|
|
81
|
-
/*
|
|
82
|
-
Crop strategy functors
|
|
83
|
-
*/
|
|
84
|
-
struct EntropyStrategy {
|
|
85
|
-
double operator()(VImage image);
|
|
86
|
-
};
|
|
87
|
-
struct AttentionStrategy {
|
|
88
|
-
double operator()(VImage image);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/*
|
|
92
|
-
Calculate crop area based on given strategy (Entropy, Attention)
|
|
93
|
-
*/
|
|
94
|
-
std::tuple<int, int> Crop(
|
|
95
|
-
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy);
|
|
96
|
-
|
|
97
75
|
/*
|
|
98
76
|
Insert a tile cache to prevent over-computation of any previous operations in the pipeline
|
|
99
77
|
*/
|