diff --git a/docs/changelog.md b/docs/changelog.md index d5febb59d..89db76746 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,13 @@ # Changelog +## v0.30 - *dresser* + +Requires libvips v8.12.0 + +### v0.30.0 - TBD + +* Reduce minimum Linux ARM64v8 glibc requirement to 2.17. + ## v0.29 - *circle* Requires libvips v8.11.3 diff --git a/install/libvips.js b/install/libvips.js index 7a83393e4..6ca27ad00 100644 --- a/install/libvips.js +++ b/install/libvips.js @@ -18,7 +18,7 @@ const platform = require('../lib/platform'); const minimumGlibcVersionByArch = { arm: '2.28', - arm64: '2.29', + arm64: '2.17', x64: '2.17' }; @@ -120,7 +120,7 @@ try { } // Download to per-process temporary file - const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br'; + const tarFilename = ['libvips', minimumLibvipsVersionLabelled, platformAndArch].join('-') + '.tar.br'; const tarPathCache = path.join(libvips.cachePath(), tarFilename); if (fs.existsSync(tarPathCache)) { libvips.log(`Using cached ${tarPathCache}`); diff --git a/package.json b/package.json index 3c2471fb9..4b06d7bad 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ }, "license": "Apache-2.0", "config": { - "libvips": "8.11.3", + "libvips": "8.12.0-rc1", "runtime": "napi", "target": 5 }, diff --git a/src/common.h b/src/common.h index 11452a456..e4dd244a6 100644 --- a/src/common.h +++ b/src/common.h @@ -25,9 +25,9 @@ // Verify platform and compiler compatibility #if (VIPS_MAJOR_VERSION < 8) || \ - (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 11) || \ - (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 11 && VIPS_MICRO_VERSION < 3) -#error "libvips version 8.11.3+ is required - please see https://sharp.pixelplumbing.com/install" + (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 12) || \ + (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 12 && VIPS_MICRO_VERSION < 0) +#error "libvips version 8.12.0+ is required - please see https://sharp.pixelplumbing.com/install" #endif #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) diff --git a/src/libvips/cplusplus/VImage.cpp b/src/libvips/cplusplus/VImage.cpp index 49b006a4d..5a08d80fc 100644 --- a/src/libvips/cplusplus/VImage.cpp +++ b/src/libvips/cplusplus/VImage.cpp @@ -93,7 +93,7 @@ negate( std::vector vector ) { std::vector new_vector( vector.size() ); - for( unsigned int i = 0; i < vector.size(); i++ ) + for( std::vector::size_type i = 0; i < vector.size(); i++ ) new_vector[i] = vector[i] * -1; return( new_vector ); @@ -104,7 +104,7 @@ invert( std::vector vector ) { std::vector new_vector( vector.size() ); - for( unsigned int i = 0; i < vector.size(); i++ ) + for( std::vector::size_type i = 0; i < vector.size(); i++ ) new_vector[i] = 1.0 / vector[i]; return( new_vector ); @@ -210,7 +210,6 @@ VOption::set( const char *name, std::vector value ) Pair *pair = new Pair( name ); int *array; - unsigned int i; pair->input = true; @@ -219,7 +218,7 @@ VOption::set( const char *name, std::vector value ) static_cast< int >( value.size() ) ); array = vips_value_get_array_int( &pair->value, NULL ); - for( i = 0; i < value.size(); i++ ) + for( std::vector::size_type i = 0; i < value.size(); i++ ) array[i] = value[i]; options.push_back( pair ); @@ -234,7 +233,6 @@ VOption::set( const char *name, std::vector value ) Pair *pair = new Pair( name ); double *array; - unsigned int i; pair->input = true; @@ -243,7 +241,7 @@ VOption::set( const char *name, std::vector value ) static_cast< int >( value.size() ) ); array = vips_value_get_array_double( &pair->value, NULL ); - for( i = 0; i < value.size(); i++ ) + for( std::vector::size_type i = 0; i < value.size(); i++ ) array[i] = value[i]; options.push_back( pair ); @@ -258,7 +256,6 @@ VOption::set( const char *name, std::vector value ) Pair *pair = new Pair( name ); VipsImage **array; - unsigned int i; pair->input = true; @@ -267,7 +264,7 @@ VOption::set( const char *name, std::vector value ) static_cast< int >( value.size() ) ); array = vips_value_get_array_image( &pair->value, NULL ); - for( i = 0; i < value.size(); i++ ) { + for( std::vector::size_type i = 0; i < value.size(); i++ ) { VipsImage *vips_image = value[i].get_image(); array[i] = vips_image; @@ -488,10 +485,9 @@ VOption::get_operation( VipsOperation *operation ) double *array = vips_value_get_array_double( value, &length ); - int j; ((*i)->vvector)->resize( length ); - for( j = 0; j < length; j++ ) + for( int j = 0; j < length; j++ ) (*((*i)->vvector))[j] = array[j]; } else if( type == VIPS_TYPE_BLOB ) { @@ -718,17 +714,38 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size, const char *operation_name; VipsBlob *blob; + /* Save with the new target API if we can. Fall back to the older + * mechanism in case the saver we need has not been converted yet. + * + * We need to hide any errors from this first phase. + */ vips__filename_split8( suffix, filename, option_string ); - if( !(operation_name = vips_foreign_find_save_buffer( filename )) ) { + + vips_error_freeze(); + operation_name = vips_foreign_find_save_target( filename ); + vips_error_thaw(); + + if( operation_name ) { + VTarget target = VTarget::new_to_memory(); + + call_option_string( operation_name, option_string, + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "target", target ) ); + + g_object_get( target.get_target(), "blob", &blob, NULL ); + } + else if( (operation_name = vips_foreign_find_save_buffer( filename )) ) { + call_option_string( operation_name, option_string, + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "buffer", &blob ) ); + } + else { delete options; throw VError(); } - call_option_string( operation_name, option_string, - (options ? options : VImage::option())-> - set( "in", *this )-> - set( "buffer", &blob ) ); - if( blob ) { if( buf ) { *buf = VIPS_AREA( blob )->data; @@ -767,6 +784,7 @@ std::vector VImage::bandsplit( VOption *options ) const { std::vector b; + b.reserve(bands()); for( int i = 0; i < bands(); i++ ) b.push_back( extract_band( i ) ); diff --git a/src/libvips/cplusplus/vips-operators.cpp b/src/libvips/cplusplus/vips-operators.cpp index 7f9a94c08..38b858b30 100644 --- a/src/libvips/cplusplus/vips-operators.cpp +++ b/src/libvips/cplusplus/vips-operators.cpp @@ -1,5 +1,5 @@ // bodies for vips operations -// Wed May 12 11:30:00 AM CEST 2021 +// Mon Nov 1 03:31:09 PM CET 2021 // this file is generated automatically, do not edit! VImage VImage::CMC2LCh( VOption *options ) const @@ -1262,6 +1262,34 @@ VImage VImage::gifload_source( VSource source, VOption *options ) return( out ); } +void VImage::gifsave( const char *filename, VOption *options ) const +{ + call( "gifsave", + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "filename", filename ) ); +} + +VipsBlob *VImage::gifsave_buffer( VOption *options ) const +{ + VipsBlob *buffer; + + call( "gifsave_buffer", + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "buffer", &buffer ) ); + + return( buffer ); +} + +void VImage::gifsave_target( VTarget target, VOption *options ) const +{ + call( "gifsave_target", + (options ? options : VImage::option())-> + set( "in", *this )-> + set( "target", target ) ); +} + VImage VImage::globalbalance( VOption *options ) const { VImage out; diff --git a/src/metadata.cc b/src/metadata.cc index 9143b5942..219ea5892 100644 --- a/src/metadata.cc +++ b/src/metadata.cc @@ -180,6 +180,8 @@ class MetadataWorker : public Napi::AsyncWorker { } if (baton->pageHeight > 0) { info.Set("pageHeight", baton->pageHeight); + } else if (baton->pages > 0) { + info.Set("pageHeight", baton->height); } if (baton->loop >= 0) { info.Set("loop", baton->loop); diff --git a/test/fixtures/expected/composite-cutout.png b/test/fixtures/expected/composite-cutout.png index 5f75e8fff..b98f2c35c 100644 Binary files a/test/fixtures/expected/composite-cutout.png and b/test/fixtures/expected/composite-cutout.png differ diff --git a/test/fixtures/expected/median_1.jpg b/test/fixtures/expected/median_1.jpg deleted file mode 100644 index df1535927..000000000 Binary files a/test/fixtures/expected/median_1.jpg and /dev/null differ diff --git a/test/fixtures/expected/median_3.jpg b/test/fixtures/expected/median_3.jpg deleted file mode 100644 index ccb091ab6..000000000 Binary files a/test/fixtures/expected/median_3.jpg and /dev/null differ diff --git a/test/fixtures/expected/median_5.jpg b/test/fixtures/expected/median_5.jpg deleted file mode 100644 index ba536e98b..000000000 Binary files a/test/fixtures/expected/median_5.jpg and /dev/null differ diff --git a/test/fixtures/expected/median_color.jpg b/test/fixtures/expected/median_color.jpg deleted file mode 100644 index f521f0af8..000000000 Binary files a/test/fixtures/expected/median_color.jpg and /dev/null differ diff --git a/test/unit/composite.js b/test/unit/composite.js index 389e6e629..4ab9974a3 100644 --- a/test/unit/composite.js +++ b/test/unit/composite.js @@ -247,7 +247,7 @@ describe('composite', () => { sharp(fixtures.inputJpg) .resize(300, 300) .composite([{ - input: Buffer.from(''), + input: Buffer.from(''), density: 96, blend: 'dest-in', cutout: true diff --git a/test/unit/median.js b/test/unit/median.js index a25ef1a1f..bbeb7acc7 100644 --- a/test/unit/median.js +++ b/test/unit/median.js @@ -5,68 +5,46 @@ const assert = require('assert'); const sharp = require('../../'); const fixtures = require('../fixtures'); -describe('Median filter', function () { - it('1x1 window', function (done) { - sharp(fixtures.inputJpgThRandom) +describe('Median filter', () => { + it('1x1 window', async () => { + const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) .median(1) - .toBuffer(function (err, data, info) { - if (err) throw err; - assert.strictEqual('jpeg', info.format); - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - fixtures.assertSimilar(fixtures.expected('median_1.jpg'), data, done); - }); + .raw() + .toBuffer(); + + assert.deepStrictEqual({ r: 0, g: 0, b: 0 }, { r, g, b }); }); - it('3x3 window', function (done) { - sharp(fixtures.inputJpgThRandom) + it('3x3 window', async () => { + const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) .median(3) - .toBuffer(function (err, data, info) { - if (err) throw err; - assert.strictEqual('jpeg', info.format); - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - fixtures.assertSimilar(fixtures.expected('median_3.jpg'), data, done); - }); - }); - it('5x5 window', function (done) { - sharp(fixtures.inputJpgThRandom) - .median(5) - .toBuffer(function (err, data, info) { - if (err) throw err; - assert.strictEqual('jpeg', info.format); - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - fixtures.assertSimilar(fixtures.expected('median_5.jpg'), data, done); - }); + .raw() + .toBuffer(); + + assert.deepStrictEqual({ r: 255, g: 0, b: 127 }, { r, g, b }); }); - it('color image', function (done) { - sharp(fixtures.inputJpgRandom) - .median(5) - .toBuffer(function (err, data, info) { - if (err) throw err; - assert.strictEqual('jpeg', info.format); - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - fixtures.assertSimilar(fixtures.expected('median_color.jpg'), data, done); - }); + it('7x7 window', async () => { + const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) + .median(7) + .raw() + .toBuffer(); + + assert.deepStrictEqual({ r: 255, g: 19, b: 146 }, { r, g, b }); }); - it('no windows size', function (done) { - sharp(fixtures.inputJpgThRandom) - .median() - .toBuffer(function (err, data, info) { - if (err) throw err; - assert.strictEqual('jpeg', info.format); - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - fixtures.assertSimilar(fixtures.expected('median_3.jpg'), data, done); - }); + it('default window (3x3)', async () => { + const [r, g, b] = await sharp(fixtures.inputSvgSmallViewBox) + .median(3) + .raw() + .toBuffer(); + + assert.deepStrictEqual({ r: 255, g: 0, b: 127 }, { r, g, b }); }); - it('invalid radius', function () { - assert.throws(function () { - sharp(fixtures.inputJpg).median(0.1); + + it('invalid radius', () => { + assert.throws(() => { + sharp().median(0.1); }); }); });