From afc4c5bf7915c591bdae2c3a61eec713ae7d62ca Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Sun, 26 Jun 2022 23:39:29 +0200 Subject: [PATCH] Upgrade to libvips v8.13.0-rc1 (#3230) * Switch from decompress-zip to extract-zip The former seems to hang when unzipping a ZIP64 file that uses the general purpose bit flag 3 as file entry. See: https://github.com/thejoshwolfe/yauzl#no-streaming-unzip-api * Prefer to call via static member instead Makes it clearer that a static method is being called. * `flatten-orange.jpg`: save without chroma subsampling To ensure no down-scaling of the Cr/Cb channels. --- binding.gyp | 5 +-- package.json | 26 +++++++------- src/common.cc | 23 ++++++------ src/libvips/cplusplus/VConnection.cpp | 1 - src/libvips/cplusplus/VError.cpp | 1 - src/libvips/cplusplus/VImage.cpp | 29 +++++++++++++-- src/libvips/cplusplus/VInterpolate.cpp | 1 - src/libvips/cplusplus/VRegion.cpp | 27 ++++++++++++++ src/libvips/cplusplus/vips-operators.cpp | 17 ++++++++- src/pipeline.cc | 2 +- src/sharp.cc | 1 - .../fixtures/expected/embed-lab-into-rgba.png | Bin 7758 -> 9375 bytes test/fixtures/expected/flatten-orange.jpg | Bin 3829 -> 6599 bytes test/fixtures/expected/hilutite.jpg | Bin 433832 -> 433840 bytes test/unit/alpha.js | 2 ++ test/unit/avif.js | 10 ++++-- test/unit/beforeEach.js | 8 ++--- test/unit/metadata.js | 2 +- test/unit/tile.js | 34 ++++++++---------- test/unit/util.js | 1 - 20 files changed, 128 insertions(+), 62 deletions(-) create mode 100644 src/libvips/cplusplus/VRegion.cpp diff --git a/binding.gyp b/binding.gyp index a2a400ed..fbd03fa5 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,10 +15,11 @@ '_ALLOW_KEYWORD_MACROS' ], 'sources': [ - 'src/libvips/cplusplus/VError.cpp', 'src/libvips/cplusplus/VConnection.cpp', + 'src/libvips/cplusplus/VError.cpp', + 'src/libvips/cplusplus/VImage.cpp', 'src/libvips/cplusplus/VInterpolate.cpp', - 'src/libvips/cplusplus/VImage.cpp' + 'src/libvips/cplusplus/VRegion.cpp' ], 'include_dirs': [ '<(sharp_vendor_dir)/include', diff --git a/package.json b/package.json index 2ec4deba..8d592236 100644 --- a/package.json +++ b/package.json @@ -140,9 +140,9 @@ "devDependencies": { "async": "^3.2.4", "cc": "^3.0.1", - "decompress-zip": "^0.3.3", "documentation": "^13.2.5", "exif-reader": "^1.0.3", + "extract-zip": "^2.0.1", "icc": "^2.0.0", "license-checker": "^25.0.1", "mocha": "^10.0.0", @@ -154,19 +154,19 @@ }, "license": "Apache-2.0", "config": { - "libvips": "8.12.2", + "libvips": "8.13.0-rc1", "integrity": { - "darwin-arm64v8": "sha512-p46s/bbJAjkOXzPISZt9HUpG9GWjwQkYnLLRLKzsBJHLtB3X6C6Y/zXI5Hd0DOojcFkks9a0kTN+uDQ/XJY19g==", - "darwin-x64": "sha512-6vOHVZnvXwe6EXRsy29jdkUzBE6ElNpXUwd+m8vV7qy32AnXu7B9YemHsZ44vWviIwPZeXF6Nhd9EFLM0wWohw==", - "linux-arm64v8": "sha512-XwZdS63yhqLtbFtx/0eoLF/Agf5qtTrI11FMnMRpuBJWd4jHB60RAH+uzYUgoChCmKIS+AeXYMLm4d8Ns2QX8w==", - "linux-armv6": "sha512-Rh0Q0kqwPG2MjXWOkPCuPEyiUKFgKJYWLgS835D4MrXgdKr8Tft/eVrc2iGIxt2re30VpDiZ1h0Rby1aCZt2zw==", - "linux-armv7": "sha512-heTS/MsmRvu4JljINxP+vDiS9ZZfuGhr3IStb5F7Gc0/QLRhllYAg4rcO8L1eTK9sIIzG5ARvI19+YUZe7WbzA==", - "linux-x64": "sha512-SSWAwBFi0hx8V/h/v82tTFGKWTFv9FiCK3Timz5OExuI+sX1Ngrd0PVQaWXOThGNdel/fcD3Bz9YjSt4feBR1g==", - "linuxmusl-arm64v8": "sha512-Rhks+5C7p7aO6AucLT1uvzo8ohlqcqCUPgZmN+LZjsPWob/Iix3MfiDYtv/+gTvdeEfXxbCU6/YuPBwHQ7/crA==", - "linuxmusl-x64": "sha512-IOyjSQqpWVntqOUpCHVWuQwACwmmjdi15H8Pc+Ma1JkhPogTfVsFQWyL7DuOTD3Yr23EuYGzovUX00duOtfy/g==", - "win32-arm64v8": "sha512-A+Qe8Ipewtvw9ldvF6nWed2J8kphzrUE04nFeKCtNx6pfGQ/MAlCKMjt/U8VgUKNjB01zJDUW9XE0+FhGZ/UpQ==", - "win32-ia32": "sha512-cMrAvwFdDeAVnLJt0IPMPRKaIFhyXYGTprsM0DND9VUHE8F7dJMR44tS5YkXsGh1QNDtjKT6YuxAVUglmiXtpA==", - "win32-x64": "sha512-vLFIfw6aW2zABa8jpgzWDhljnE6glktrddErVyazAIoHl6BFFe/Da+LK1DbXvIYHz7fyOoKhSfCJHCiJG1Vg6w==" + "darwin-arm64v8": "sha512-tczrNbjU/+bUhTHilUNd2OL9DgwPvUX3L6LUTQHmLZlatxT5ztj6of1erNXYhgt4lLhruKcKkYYQyRicBoKsRA==", + "darwin-x64": "sha512-eonsBwyPEHxFL++bCG+rEUokSOaCbImSJ5VA//Qkv34QAO+vZWzj9RMf/ETvT3pHzrx56Wm3SwpGgJ/BIeyKgg==", + "linux-arm64v8": "sha512-/hLYMwmWG3wKyUeDvNZCMJL3mZ+/8mj+VfVbo2DsNsI6MxBnZ4aXg3qMJ17FcEa2sPDHhkVqaJl5DnXGn87gGQ==", + "linux-armv6": "sha512-25Ml2+J+KWj7yKnZVcrxZmZHFIwzbW6d/kACT7duz48NmGWDD0jnu/nroAETYiMqS1W5fHlPRiwG82/d/PeIpg==", + "linux-armv7": "sha512-7u6RS3IFptQMGRMujyvz1kh8YFPBRdEu9T1zJKYl0/cZkxeNNiljW4PenOQ6cxoPlXivUDpEOoifo4B6a3B4/A==", + "linux-x64": "sha512-v6/vcp+71p4NtoEpWUghgboVzavwX8FVa1NXBDVYMx/up4AmQEjEzOicSiLxXc97Ydhk6FJz8Qkrfp05AN0SfQ==", + "linuxmusl-arm64v8": "sha512-8DNTwBbAna/R0/ksmaO/2TKZxjdkgSppTjmM9xIAZO7tbknuDCqi0YyJiiCP5Nh5yGtpf+PcQdSu/ca21ICwAQ==", + "linuxmusl-x64": "sha512-3B12ehfury7m39sZ1lyTxzcf3hPAcCC/+4l16C0Mkoc6FGKPV/CJFtoCzNjUEsEJR5rMKOFBfsgZOoNk0KEzSQ==", + "win32-arm64v8": "sha512-45gnLXiq4EtJNnhhgSY8Kgr5L0ed637Kn2SCW7jhQu9qZS3riLSM5Kk+6L1Kqc+cyNnsFbtWWBWCZp01Tg9HSw==", + "win32-ia32": "sha512-LEodsYlTo0gkrfpHFNr9IwSMI7MTBBy/vry0MazcUAIPkiPhx3CCNbPZL7pd7vuHZ4CP5XNsArTb0RaHpMrc+w==", + "win32-x64": "sha512-5usGDnN1pmFoqfEuHmdvKF/35NbQLlnal5T2bMWDx9kvLIOy7WSahvSGAeDg9cXjHv9SONX/qdpKuJ55c6aD4g==" }, "runtime": "napi", "target": 5 diff --git a/src/common.cc b/src/common.cc index 751e41d7..307549b3 100644 --- a/src/common.cc +++ b/src/common.cc @@ -366,32 +366,33 @@ namespace sharp { } } } else { - if (descriptor->createChannels > 0) { + int const channels = descriptor->createChannels; + if (channels > 0) { // Create new image if (descriptor->createNoiseType == "gaussian") { - int const channels = descriptor->createChannels; - image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight); std::vector bands = {}; bands.reserve(channels); for (int _band = 0; _band < channels; _band++) { - bands.push_back(image.gaussnoise( - descriptor->createWidth, - descriptor->createHeight, - VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma))); + bands.push_back(VImage::gaussnoise(descriptor->createWidth, descriptor->createHeight, VImage::option() + ->set("mean", descriptor->createNoiseMean) + ->set("sigma", descriptor->createNoiseSigma))); } - image = image.bandjoin(bands); + image = VImage::bandjoin(bands).copy(VImage::option()->set("interpretation", + channels < 3 ? VIPS_INTERPRETATION_B_W: VIPS_INTERPRETATION_sRGB)); } else { std::vector background = { descriptor->createBackground[0], descriptor->createBackground[1], descriptor->createBackground[2] }; - if (descriptor->createChannels == 4) { + if (channels == 4) { background.push_back(descriptor->createBackground[3]); } - image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background); + image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight) + .copy(VImage::option()->set("interpretation", + channels < 3 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_sRGB)) + .new_from_image(background); } - image.get_image()->Type = image.guess_interpretation(); image = image.cast(VIPS_FORMAT_UCHAR); imageType = ImageType::RAW; } else { diff --git a/src/libvips/cplusplus/VConnection.cpp b/src/libvips/cplusplus/VConnection.cpp index adea2e17..55451415 100644 --- a/src/libvips/cplusplus/VConnection.cpp +++ b/src/libvips/cplusplus/VConnection.cpp @@ -31,7 +31,6 @@ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ -#include #include diff --git a/src/libvips/cplusplus/VError.cpp b/src/libvips/cplusplus/VError.cpp index a3d82188..3be49068 100644 --- a/src/libvips/cplusplus/VError.cpp +++ b/src/libvips/cplusplus/VError.cpp @@ -30,7 +30,6 @@ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ -#include #include diff --git a/src/libvips/cplusplus/VImage.cpp b/src/libvips/cplusplus/VImage.cpp index 5a08d80f..72c3fa9d 100644 --- a/src/libvips/cplusplus/VImage.cpp +++ b/src/libvips/cplusplus/VImage.cpp @@ -38,7 +38,6 @@ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ -#include #include @@ -733,7 +732,7 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size, set( "in", *this )-> set( "target", target ) ); - g_object_get( target.get_target(), "blob", &blob, NULL ); + g_object_get( target.get_target(), "blob", &blob, (void *) NULL ); } else if( (operation_name = vips_foreign_find_save_buffer( filename )) ) { call_option_string( operation_name, option_string, @@ -778,6 +777,32 @@ VImage::write_to_target( const char *suffix, VTarget target, set( "target", target ) ); } +VRegion +VImage::region() const +{ + return VRegion::new_from_image( *this ); +} + +VRegion +VImage::region( VipsRect *rect ) const +{ + VRegion region = VRegion::new_from_image( *this ); + + region.prepare( rect ); + + return region; +} + +VRegion +VImage::region( int left, int top, int width, int height ) const +{ + VRegion region = VRegion::new_from_image( *this ); + + region.prepare( left, top, width, height ); + + return region; +} + #include "vips-operators.cpp" std::vector diff --git a/src/libvips/cplusplus/VInterpolate.cpp b/src/libvips/cplusplus/VInterpolate.cpp index 303a9b8b..ed1e2bd9 100644 --- a/src/libvips/cplusplus/VInterpolate.cpp +++ b/src/libvips/cplusplus/VInterpolate.cpp @@ -31,7 +31,6 @@ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ -#include #include diff --git a/src/libvips/cplusplus/VRegion.cpp b/src/libvips/cplusplus/VRegion.cpp new file mode 100644 index 00000000..4a042821 --- /dev/null +++ b/src/libvips/cplusplus/VRegion.cpp @@ -0,0 +1,27 @@ +// Object part of VRegion class + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ + +#include + +#include + +VIPS_NAMESPACE_START + +VRegion +VRegion::new_from_image( VImage image ) +{ + VipsRegion *region; + + if( !(region = vips_region_new( image.get_image() )) ) { + throw VError(); + } + + VRegion out( region ); + + return( out ); +} + +VIPS_NAMESPACE_END diff --git a/src/libvips/cplusplus/vips-operators.cpp b/src/libvips/cplusplus/vips-operators.cpp index 38b858b3..9360bd89 100644 --- a/src/libvips/cplusplus/vips-operators.cpp +++ b/src/libvips/cplusplus/vips-operators.cpp @@ -1,5 +1,4 @@ // bodies for vips operations -// Mon Nov 1 03:31:09 PM CET 2021 // this file is generated automatically, do not edit! VImage VImage::CMC2LCh( VOption *options ) const @@ -943,6 +942,14 @@ VipsBlob *VImage::dzsave_buffer( VOption *options ) const return( buffer ); } +void VImage::dzsave_target( VTarget target, VOption *options ) const +{ + call( "dzsave_target", + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "target", target ) ); +} + VImage VImage::embed( int x, int y, int width, int height, VOption *options ) const { VImage out; @@ -3521,6 +3528,14 @@ VipsBlob *VImage::tiffsave_buffer( VOption *options ) const return( buffer ); } +void VImage::tiffsave_target( VTarget target, VOption *options ) const +{ + call( "tiffsave_target", + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "target", target ) ); +} + VImage VImage::tilecache( VOption *options ) const { VImage out; diff --git a/src/pipeline.cc b/src/pipeline.cc index 65f994b0..23806cde 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -668,7 +668,7 @@ class PipelineWorker : public Napi::AsyncWorker { xs.push_back(left); ys.push_back(top); } - image = image.composite(images, modes, VImage::option()->set("x", xs)->set("y", ys)); + image = VImage::composite(images, modes, VImage::option()->set("x", xs)->set("y", ys)); } // Reverse premultiplication after all transformations: diff --git a/src/sharp.cc b/src/sharp.cc index f76a5cb0..53853743 100644 --- a/src/sharp.cc +++ b/src/sharp.cc @@ -22,7 +22,6 @@ #include "stats.h" static void* sharp_vips_init(void*) { - g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE); vips_init("sharp"); return nullptr; } diff --git a/test/fixtures/expected/embed-lab-into-rgba.png b/test/fixtures/expected/embed-lab-into-rgba.png index 23c5d9ae6591f03d3d6ad9a4fb1a2d47e9444cec..6e48a7213d07ba6674fc477a6717c00400b4ba10 100644 GIT binary patch literal 9375 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 literal 7758 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 diff --git a/test/fixtures/expected/flatten-orange.jpg b/test/fixtures/expected/flatten-orange.jpg index 18f49bb87e5c5fd010bc3e74a3ecc2dc9662eff2..5f00a736d46ebce7667936c64643c88e28414c16 100644 GIT binary patch literal 6599 zcmeHKSyU6&x(=wIwrW!b5gCpkpdw-jlT10ST9mrTv+jAgYu%^&(*I@ud+q&y``h21 z{;ix=vH@2CY6lOg9XhC{c1TTKUG4DE&yF5Fa^$Gy@e`kZc2ZME=cJ~#wywU3q3&s8 zJ#Fp3T>Oi%soD7p=T8}0T(&rO+2q{$bH9VAsH>|VJ$zK-=uwSxy4t$u{?DfTPXO?+ z%0-nQ4yXVDsz8+kKo#XM;50x5a6sjw0sd(p$*LYuQUBNo`Ah|%s;UY&sH%S8=t1@0 zd#I=$ICu!C_W2o*#urAn@5le`=}+pKue6MNqN}K?9zLLU=+Hk1{EiMh13L7%#uvBapZ;|JZ(T-KUxvKe&@`Ty z7ipcn`mo!pSjhr>df;Ou@Bk110hB3fcMt!Q`k&693jBX5aJZ(9x$d7bU6I#p5e&(` zKy5J1K3t~+9DETkC9EKYTUVctP1CXSgei;9@BL0P(GG*~rwsegO-P8jm0U3b=;#tU z8c>4lkXaMTIAIbE#l0#Df|T)wWidEAKIibi&J5UR5jo52#YgO?nDZ3e`=)xeb#9zr zLvBZqS=?TTPQ_Onb6etoUcor5-R3C)e7f8uUh$Z8rbx!(+eVRdEX1{J`sUEnjGL$DsfJ|`$)|z` zFZ+Ih^6M2d<1J9LqCP!{1gSWcJ%%dXzr1RaI&o)C8<#%pPDxo=6$a}vGEILQB(}Tx zGjZPdB7S&!E*kTtQFzMZCzNY(muxn<(`3=*uuhLm%Jp{VVTlAv6)Z>JViY!0t$fbL z`OO)027Y_MqM56Yd9qE2IIg=+pe+MaS2fsPrjMU>@a8gZ z6ydg!6r>PHo_Si(WEC_~7UFn?;O&GmLbV_>@E3bud=&6_f)DK`wrXk@?(9BY+nQQd z$cum){P#{!v}S}<2QWB!QVXrOj0Fi!B8KAQCpqJ=^^eD9O5)pQzrnm7OpNxYFM)nF zJX3x)jD|?w7sUAwD~yn1?k?to`x&8J(*)OYYVdb$CC+@uRYo}6Z6AD@ss!A6a(6Xi zehCDWQ{>i5O-nW{FYI@`Bjnf8aj3f~V$loZ@)pQkD?`$X%e=HIO=Sj3mN`9+PQ1>W zYY}VvNV7SFP^BMTyAV?kz=lXTvBrbAaBV+{kv4Fb!7HI1dK|+ z!dI3mWz{?!BRp%rAzM59Mo6dA=G)yK9QoO6!KEi-F;&Qdy%nLxl18`aW~N5*f(>#O zN$o2ih54WN&8_yA)<-kCR>9IQ1MDrUX8m2I+Z5Ru+H>+lVob^2K2U%Ch2Wi2QPD9X z%=4FUY+Xq-H3IuGD10Qwt|oQq#5ducsQBWV=Bqr|xCQoDn7l9Mt`cAqXE>7G*oX-b z?U=Fd*G?wle}OKck>!-04J9DTUkUIIx*$;kF8S8;&c=UO-VSnpj}uJBi<)-*qPTj} zmcrx>3M!LQIz@h$%B&_?OQhZwkcYj+PwiIOrJ-p%36k2M;$Ue{GTkqk2U8$>j zb9*}%Q-d-+$K*-y``T0S6ounbWnRa%jI?&&Iz9xik6B5WllzZ1q~gX!yeZ+1hgf`P z@_vi+-S*bPHeXiCB4d4LIB}CZ)m;s^fb#2)g$$v}#_cOdJ#6!ng?z#UcJ(shr;$^5 z%_cF2W&fuBMtF?_eC~>1i1zG);%1{>+?vd`*DzB*CZ&Tc?V=($OLs8_vFzZJ1a75l zPE+zC<9Jb(&&F^D0@6S)U9<`r!?uw_I zkno_kEhr1@0u;#;VYId?YCN}47#jpM0B^{}sQ2gbhC~iL(R9k9*t|&A;cfPOh3<=v zruMHmXA+ls?%MBD?hu3YvOV`56RqP*1fBv$;dWL(%5Fh2I?7{?{Qbd9r5OGoKMfuv zaAx%Q=I&6aFVRFw6o1o=E^=LUl*gfaBedNbgWY!lOvIaKnx3!4GSkI^jP zL~ZYxs5NZ5#TkGkkNQ2lO%WiVK@V7b75EY%EnIv$LW>bk^f$Ig4t2%4$?dy&X#bGr z5a#IWX08z1dmoF?x84gN@bqus$WLg8(VUbuUuM|b>msj55`GtG9;gA{70#s1&r+&U zwx9Nc6?;z8JswIxY*`g6+d;Pdd`0w?6Wu?d4zvgwu(nQXHPylIycW5{J3=Rz8e=Qs zo~k6QUCWi+O^;;0kQL(0?_o9*TNgH6s!i)-={5U%sL&b*`if}9Zf(a^NdGkrn#lfP zwx!M~`>UD62ZR#s3)$`_MES0fabIQqjokPG&4tSK+$EReoBSuY^*5c2oPtw^IgaKY!ZDvKC4p+h$wlP+`EgKE5mvBz&*UX^eGex@Pv`w{PC6$<1}Ps z?5bF|LW=8tjX>4zSO&=nUd-0)7{{^v?Ktbv(;oIiRh4b0K;xqGhbPY|U~iy?^8yO1 zgwL+rV;>5gOAJEc(M-~-j4xURi44N-z|tH0F-Vh*b5R|mb#Boh@mEcQGrahXpd;L* zE(r+Rhv5^X7?C4xAI#b>aazGwTG28Q%LKI)EL7K1+v)afv|KpS+{{P$b)iXaA9!_x z3W5?49z)*st*1h6L@}~wDOtYqH!NqK@aw=jH^@N_7YRcXm@w#`G-&x8+Y7lT{{DDD z?G~<{W5X@@>p!ntwNIpN{NL(6sUaJ>UrjLDG@K@olzQ;A%nE1<>+n(=4A5@HCfZh1)K3P{y|m~swRe283F=l4 z;%?%wI1e3IoOa9Zgb7+J5oYTcLK-HQ9KT6Ur+I0-B{MZZ+YLi?)8XO$w-olTH>z*9 zb|Q0oMFgFS+UC`tnz7^+FBnuS{(dVSE~n52C<=b&gYf_YHHx^DR*w9Je$1$X7y0N> z6yPqhK4>?<#lv32Ho=sfNbX1l1ItPvO>T^Q5!%{6B35Io)HCJE=wmpLq$3|9eZIV~ z-~<_6exF-2BN2x-c}O&(@>V5jjbl2Abd$bo!$ih>FTz>GtQjVE8{9HZskSF1*>1dN z)dr-`v{#=PX>K4=Jd!~mXNp4pY@&^9QzXd-;o;rkw83XgHYphT^+ZQq3q2U$%a#fD?c?4*Oad|U3ZOoj z(shnP#2!g%8B{g2XmezpvRN&eXVGEabbW~p&fghwaN0`hQ$UHM&DnnOeB??O)hK0A z;P_&x)l%%T@V;3b_9QW~*|;Bu^~*)qNZ7NMK6tEuO5wW|`E23Ku7S1A4ceicFT058 zLH%r<-7yW9QhX~lwsFyA0y6)7VR-jUcBzIm1c`d#D&6${?L&LcPLHg`Ua?J+iIL2l zF?>NC4-9h|>>Q^=kgJ{=x|QcsR}JM26Oi32nREHqtj>2dZ9H#IdSrf%lE z=;mS&`694it*l66C|h3<^kAq=+8OO}y}_1+B+|;6*yx_~qD= zKPfq`=LZfYE}s%{_iHzH8fjV*v|o`D;8S&)QQ5$#ZLwoTl;bICYgKqF(sDUnvnY%73mR78;S3Hn# zS&?KtE*)agpgViB^XCNix+KIU0ihi?T`?2`8#LJz7q7j^?!Fb81=mLzQl52Tl(`CBR0+(X8TM20dORxWg`LT!tt%h<4{!YC5 zo|8=xzr3A)D~p?|KfkzaVRr^|y6Ut;FR|@Z7fh2iDA~M5j^F8t4_gz$1cUU?8B{c$ z933}q>g<;Vezb(QUT!Ra7rq_R>~s{oZx+@`I+FFT4;DsX2aOl#)b`1RfyF8oofgG1 zvJ>-M*xi>S@yKPJhN+H(IlIdFA=0CaHO4*i#xJ2{yTO~yA%hvT6vAyGSkjG&#kZu) z^tegjTAa+fqQF0t{6~58+^9$qw?JOi9YDRli7xF9%j?MU<@>xO1{!Q`V}=&>vN}Ex zf&+0`x}qHlVk_u5?yoAaobPz?Q86n%P|ekth2DT^p+O`O<4mDt@1u*U_%uxVLUnux z!thuryy2BUOwZ^0*0k;Q)^{6OPTL}Q=mH^%>|R_?pj*OIO zRw)hmoZuCI(tghhH{IM7BJ!C$nQ!qg*$i~$ zRje6s7?U}Yzk^vCi@sxSM_@Wzc~Hw9X$@OA0w}GpeIh@@ja;IUR+2A zHGvf#5`tiUoiEV9?1guDkIUQpBjt4!pEl^46O)+mQ^E88f2It0r`K(cZSX+u43g=dB&!_nO#e eg&DUtA%=&yUC+UOzVz+?r1od+PX!>#ng0TJHZsrv delta 3132 zcmX|DdsNbC-p5-rvu7qFYT8tEM`!fRTV`lp^2TmsSxNe;Go_#~qp2yW;ayRF`@U0| zTCzr|iOP(nyyk_x;4Ou9lvoFm)dEx$5(Fkm1Qh{AS=)Ecex5&`bH1PF`}v;p`JU(b zK5Q=?^oV)Z{1$ZR@`XzmKo%Atkj4H2Rarg;oI#dg3oEdNc^qWFAG5Uj!2So;HkMWg zEI!&N&mP(*EiFOTmLFSNAF%px!ot$Z+6H{!@E0)1Uye9O{($?Rs-HhP>KSz|A*;*f z%deV7e<5{G@2A>X{Eq*~f~>49Eo?r>hQPodbU{`&2W)J9ue`4dw*CTk_%BsIYxYIE zMyH)UBNLWvATH;!qRjK4Pp$S-z*b-o98_(!p8x(&Jva+53;&N7DtZ6Zd31SiTy40* zw5i={0)qrs>AN~qSTwy+LEilF4#tZQE-6sda_JkyH$)c!so zjm2bw_TZ~4Bgt&SAaD;&@h3$Fiv_bi!J^38$OhCutmrYCvp#ev_cm}lCZPH25Finz6}f1L;9 z1%)hHIROLVK?+SHWWQ5(eif6bXT{1#g`8thR)p=vx>pK9guQ!UN6PcHdkrIv0eh|G^a*SPUuA-ylF7f8+-3N0(Kw3E zVAm(<-fZ47+v6--_M~jKtafjHvR`Ye>@;D18w}hCAD)K6U=k_2jH`X2=qFT>ze?0p z=HEEz<`W^OjGN+jRCeY`DP^mS*xy(S|36;hktE_~E#xx_grO4`^l z-2;Q|gx#+nEL`QRMytZZLzS=0%y#T(knOG#r7v#vCq z%^)oWn#jggG8VUWKN3O-YWwj>M0;UJP^cWLH=HAtO^-g67O8ISrD4ApQkusB)DTxm z^PuYSlQd&E{FqBA)KhXQAaVG5vsT1On-8dz0-sbD=iOyG(KNZKa#}VP!OQFA4Nvmg zfBlw=OsHeWYIIOBKkJ7+L|5uNJEdd=zgzZaPkNhimnS#5ZY|_1Sa$_9OeUH#M5>Z8 z?wEY%__Sutjj3=mhzB5zJG+T=cG)da;_W%sW#ksKI*?weNaRBM*0+W8OTM`{cA*zm z0!<*ywU2u*O&sZftFko$Mi!gmAQqZI4WVqj@tlv+LqpGzp`2bVF0_X`#OoXvxh;h6 zDyI(nrUWnc`veE!JFxtsc4WXtAz>|s$hWn%WtKWY0eTzxK+uAJj(~$OC^j3L0#vPK zW$I&bgg^rw8~<{Nk%?9wb$x7VN(L4tc88+BeY3`Ht=OpY>wnbgsuQw>xgt}f#99beBl@;=gLPev!d z0cIF6ToQ%@`MfuRKw2-WEvv=FjQKC9grRx$eDPDlV8mc{?8a5sAS(YiuimDtK_#kk zmK(d0da3vFx4g)O?QwD{1Lu=IG`^%L9fitdiE)LsQgUlcsXXgYL zCU9Mm?3(ftlMl~4Bo}ce-mS2(T|^*xXdvB@pCya`O^tjuX;bF__MA}LtuziI*!`Nykcq-4Zx{=sv!5_FVfx8g#*GvEX z`&>FAJ}rZj4cYi`mzNR;E}ZZ9Vh}2u@LM}Zi&4l<`Ef2a4US8`6>aR5v=lBGh5G#DAa%I9xriJge#AW9IJlnI3fK`}O9CioQ{E2X zZy9!c8+a=9gV0B3ZI6qLDFI7+X+ba<&z}t0LsY#%>`uc(t)j62yTKPM1?eFPa|X}G zsmr8}CysglvWhcOpDU=5nXA1;<9pGENNgFm;v1CXqKcGItBR|k(L2B@cpGQAB7wTs zHekplo~5|;0<1JS(_<_>g8j@GQaFfqD^(hH{coru>}f~a6B{Jn-Ds?%7>Ni8*nVr&?y4Vy{qz@P!6MmZ36Fh?d`=Pks4gB|J z4Hv9FT&%kw4}5|xA8DKVNi?A?MzNQu z?b1<_`?fJe7qcrbc&ZUPb=~#xH3lcf>Lt4ZJ7lPdYIjIdvh&AtX0)iif{DRBa( z5OTnE&d&IiYDx!2kf_f6-%V-v3_dcG#-+G@Qu<|HGQ)v!#jE4)yl#{P^-O3#pU{7= zcI8m_Er(!?O1hb{W;kjFX_MXdt(HHzlU`nJnm+uLdD~!b23>z6ex{8fOKiH( mFNZi8bDXPKDV3kqP>x?NKtKdlOQ4U>?Ek23EUe(>xqkt;LU}|0 diff --git a/test/fixtures/expected/hilutite.jpg b/test/fixtures/expected/hilutite.jpg index 6ae6a71c5fbe0c8c96fb7e73c54ce57ecc483b0f..d11facb2f4a11f5791acf2d790f9de67bb36240e 100644 GIT binary patch delta 1198 zcmV;f1X25_y&AB+8i0fWgaU*Egam{IvOKz$xk+QU_Ngk9rd?M;(DRJD? zl5>|2@1FGn@<14E8P|Rrw$oxOL<+avVn~V-hhvUtAax+tyTxoZeX~cJ&wsTjOQ`yE zq`BN9J~NESXO}3H6se}p7Mz~>qg*pJ=uCbYbV}I4JdS9_M|6YJYE}OLpdCI&@dtw% zt@{(FPYi&Dlj9?Ddr(;pIO0}RwGu(o;hV+m2#K6EKa@F;p!)ZvscoV&qS zDt=B_qcPEdqqm(Cwb~?3b$=@5s4@=fImL3qjO&b&gc1|f`BT3SI+Z|3$GiTkEpnq% zDnQ3^3U$g=#rB?%TMh+vZ2=_wNZiwR49>j<0Au==n^rk45H$|q5_9yVW|BjwKA`l+ zdeC?t;~BflFsHC(M|iJF64x`VpQsW@9 zUy!#n6M~F?o`)M`bfp!#>ry#{4P!apw2S1Y`!dGyVF0wR%|?GpR0%$2jKq-QNLwn> zw4F&%I3R9!zX$xt_*bs zs!EN;%SFoF#ZDC7ZA*50Xa!Hjcs5<_l05xss>-W>y`7Xru^2I+g%xRPko- zDzO$gEXn)KSJKTDMTbG5DNaeokV!_KO?SseMHk+qr)c+ttJcr!J4S zu)}HzLnzV}o%ha9Zq<)*T*lf_ik)auLNqHQVe#u)6SAC5(V3{ZG_;|mHntL?8&-Yd zJjEw}T-U@oLBcC(Q$(&L!80n5dMP`roh{7z(+Kf6rdHTsPCS&e4ZY+$U#|4 zs8Q~P{{S^zstT9vr8Ejg6kFfwJJAI{h6`=J+sV&V?Z3i|vjdP~JTaDhf(Q4^ffcrAk_z7-z99lKZhxg|M=f5!$Fkg`AOutFA^t M=~AU^^#knx*>(dYs{jB1 delta 1210 zcmV;r1V#I>y&9;!8i0fWgaU*Egam{Ivj6&0s{d70000000000000051^@#G z1OorW05lK)0Rsa91Ox*H2bT|;1{N%FGeVIE6eBW$p%o-jvBA;s|HJ?p0RRI50RaF2 z000000000000IL900jY;QJMxEe-be=K_X%Q+5ij#0RRFK0}%i}05Jmp0BCyLRUT&~ zp*}WV1t&h0mhra|Ddd}E75GoaSUaemqPt?sx7?OXZmKkqvb3v79+WlC5_vKdxbA8M z=Pn=Kpk7D=4Wl~taNV|>5n3QszU>l3P?S3yb3p^C2DR=ij@9i zr%wz3hKG!e^V)*Qb;nY&rKpk*Q%1O!7u|0UqZkT0$Lm6rj{=9KT}~N83Cp|{a;N0w zi@bDTDDCG&?UsoXok0udbBg7K8P^yk2qY(|@~3_tbt-_7kC(T#Yh0+*)gWWI zg+Apj;`>iXEr$ZSwt#|t$lTL+49>j<0Au-fwb9QI78?-l7nTIMY)#@cq+oyOE$rCjF|&LxaK!ZQBTCd85%f3`u383@8pH9W;E z_un85{DvW~MeL{hHI3DT0@A+~ zy#7?E5`4`Wi6Ov4jH^o0btORHq=S$}JX<(2!i^l+CQf(M>&Zy{5}p45dS{PgvamS} zu^9jWqUS9ndvCoJ*9JO+O(jO+<)YID#TeJGbis6WF@zB=qp0I9@swBV%Y$ZfCto+1zich&(lI()z*BjY^c4j#*-5x~S?}ib{ulBDt7+xS|x4ZQNv3sY_F%4EYktFS!*6TMH>s Y9Ys)yE5RcPS6qyP(xpl-kPn&v*%!qj!T { format: 'jpeg', hasAlpha: false, hasProfile: false, - height: 14, + // 32 / (2048 / 858) = 13.40625 + // Math.round(13.40625) = 13 + height: 13, isProgressive: false, space: 'srgb', width: 32 @@ -70,7 +72,11 @@ describe('AVIF', () => { format: 'heif', hasAlpha: false, hasProfile: false, - height: 14, + // FIXME(kleisauke): https://github.com/strukturag/libheif/issues/365 + // $ vips black x.avif 32 13 + // $ vipsheader x.avif + // x.avif: 32x12 uchar, 3 bands, srgb, heifload + height: 12, isProgressive: false, pagePrimary: 0, pages: 1, diff --git a/test/unit/beforeEach.js b/test/unit/beforeEach.js index f2b82e51..932cb8d7 100644 --- a/test/unit/beforeEach.js +++ b/test/unit/beforeEach.js @@ -5,11 +5,9 @@ const sharp = require('../../'); const libcFamily = detectLibc.familySync(); const usingCache = libcFamily !== detectLibc.MUSL; -const usingSimd = !process.env.G_DEBUG; -const concurrency = - libcFamily === detectLibc.MUSL || process.arch === 'arm' - ? 1 - : undefined; +const usingSimd = !(process.env.G_DEBUG || process.env.VIPS_NOVECTOR); +const concurrency = process.env.VIPS_CONCURRENCY || + (libcFamily === detectLibc.MUSL || process.arch === 'arm' ? 1 : undefined); beforeEach(function () { sharp.cache(usingCache); diff --git a/test/unit/metadata.js b/test/unit/metadata.js index 30ee777c..df5f1d61 100644 --- a/test/unit/metadata.js +++ b/test/unit/metadata.js @@ -343,7 +343,7 @@ describe('Image metadata', function () { assert.strictEqual(depth, 'uchar'); assert.strictEqual(isProgressive, false); assert.strictEqual(pages, 10); - assert.strictEqual(loop, 2); + assert.strictEqual(loop, 3); assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]); assert.strictEqual(hasProfile, false); assert.strictEqual(hasAlpha, true); diff --git a/test/unit/tile.js b/test/unit/tile.js index b5ff7da2..9b09a527 100644 --- a/test/unit/tile.js +++ b/test/unit/tile.js @@ -6,7 +6,7 @@ const assert = require('assert'); const eachLimit = require('async/eachLimit'); const rimraf = require('rimraf'); -const DecompressZip = require('decompress-zip'); +const extractZip = require('extract-zip'); const sharp = require('../../'); const fixtures = require('../fixtures'); @@ -14,7 +14,8 @@ const fixtures = require('../fixtures'); // Verifies all tiles in a given dz output directory are <= size const assertDeepZoomTiles = function (directory, expectedSize, expectedLevels, done) { // Get levels - const levels = fs.readdirSync(directory); + const dirents = fs.readdirSync(directory, { withFileTypes: true }); + const levels = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name); assert.strictEqual(expectedLevels, levels.length); // Get tiles const tiles = []; @@ -67,8 +68,10 @@ const assertZoomifyTiles = function (directory, expectedTileSize, expectedLevels }; const assertGoogleTiles = function (directory, expectedTileSize, expectedLevels, done) { - const levels = fs.readdirSync(directory); - assert.strictEqual(expectedLevels, levels.length - 1); // subtract one to account for default blank tile + // Get levels + const dirents = fs.readdirSync(directory, { withFileTypes: true }); + const levels = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name); + assert.strictEqual(expectedLevels, levels.length); fs.stat(path.join(directory, 'blank.png'), function (err, stat) { if (err) throw err; @@ -94,7 +97,8 @@ const assertGoogleTiles = function (directory, expectedTileSize, expectedLevels, // Verifies tiles at specified level in a given output directory are > size+overlap const assertTileOverlap = function (directory, tileSize, done) { // Get sorted levels - const levels = fs.readdirSync(directory).sort((a, b) => a - b); + const dirents = fs.readdirSync(directory, { withFileTypes: true }); + const levels = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name).sort((a, b) => a - b); // Select the highest tile level const highestLevel = levels[levels.length - 1]; // Get sorted tiles from greatest level @@ -908,14 +912,10 @@ describe('Tile', function () { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); - new DecompressZip(container) - .on('extract', function () { + extractZip(container, { dir: path.dirname(extractTo) }) + .then(() => { assertDeepZoomTiles(directory, 256, 13, done); - }) - .on('error', function (err) { - throw err; - }) - .extract({ path: path.dirname(extractTo) }); + }); }); }); }); @@ -942,14 +942,10 @@ describe('Tile', function () { if (err) throw err; assert.strictEqual(true, stat.isFile()); assert.strictEqual(true, stat.size > 0); - new DecompressZip(container) - .on('extract', function () { + extractZip(container, { dir: path.dirname(extractTo) }) + .then(() => { assertDeepZoomTiles(directory, 256, 13, done); - }) - .on('error', function (err) { - throw err; - }) - .extract({ path: path.dirname(extractTo) }); + }); }); }); }); diff --git a/test/unit/util.js b/test/unit/util.js index 0965280a..23ba2b59 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -140,7 +140,6 @@ describe('Utilities', function () { assert.strictEqual('object', typeof sharp.vendor); assert.strictEqual('string', typeof sharp.vendor.current); assert.strictEqual(true, Array.isArray(sharp.vendor.installed)); - assert.strictEqual(true, sharp.vendor.installed.length > 0); }); }); });