diff --git a/.jshintrc b/.jshintrc index b95ee9fd..4e909da6 100644 --- a/.jshintrc +++ b/.jshintrc @@ -2,7 +2,7 @@ "strict": true, "node": true, "maxparams": 4, - "maxcomplexity": 11, + "maxcomplexity": 13, "globals": { "describe": true, "it": true diff --git a/index.js b/index.js index 025b8ddf..86887bbe 100755 --- a/index.js +++ b/index.js @@ -83,11 +83,13 @@ var Sharp = function(input) { (input[0] === 0x52 && input[1] === 0x49) || // TIFF (input[0] === 0x4D && input[1] === 0x4D && input[2] === 0x00 && (input[3] === 0x2A || input[3] === 0x2B)) || - (input[0] === 0x49 && input[1] === 0x49 && (input[2] === 0x2A || input[2] === 0x2B) && input[3] === 0x00) + (input[0] === 0x49 && input[1] === 0x49 && (input[2] === 0x2A || input[2] === 0x2B) && input[3] === 0x00) || + // GIF + (input[0] === 0x47 && input[1] === 0x49 && input[2] === 0x46 && input[3] === 0x38 && (input[4] === 0x37 || input[4] === 0x39) && input[5] === 0x61) ) { this.options.bufferIn = input; } else { - throw new Error('Buffer contains an unsupported image format. JPEG, PNG, WebP and TIFF are currently supported.'); + throw new Error('Buffer contains an unsupported image format. JPEG, PNG, WebP, TIFF and GIF are currently supported.'); } } else { // input=stream diff --git a/src/common.cc b/src/common.cc index a8c670d3..ea690bbc 100755 --- a/src/common.cc +++ b/src/common.cc @@ -43,6 +43,15 @@ namespace sharp { ); } + static bool buffer_is_gif(char *buffer, size_t len) { + return ( + len >= 6 && ( + (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && + buffer[3] == '8' && (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a') + ) + ); + } + /* Determine image format of a buffer. */ @@ -57,6 +66,8 @@ namespace sharp { imageType = ImageType::WEBP; } else if (buffer_is_tiff(static_cast(buffer), length)) { imageType = ImageType::TIFF; + } else if (buffer_is_gif(static_cast(buffer), length)) { + imageType = ImageType::MAGICK; } } return imageType; @@ -75,6 +86,10 @@ namespace sharp { vips_webpload_buffer(buffer, length, &image, "access", access, NULL); } else if (imageType == ImageType::TIFF) { vips_tiffload_buffer(buffer, length, &image, "access", access, NULL); +#if (VIPS_MAJOR_VERSION >= 8) + } else if (imageType == ImageType::MAGICK) { + vips_magickload_buffer(buffer, length, &image, "access", access, NULL); +#endif } return image; } diff --git a/src/resize.cc b/src/resize.cc index 175b136e..380a2227 100755 --- a/src/resize.cc +++ b/src/resize.cc @@ -661,7 +661,7 @@ class ResizeWorker : public NanAsyncWorker { } } -#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5) +#if !(VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)) // Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+ if (baton->progressive) { VipsImage *cached; @@ -683,7 +683,7 @@ class ResizeWorker : public NanAsyncWorker { } baton->outputFormat = "jpeg"; } else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) { -#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42) +#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)) // Select PNG row filter int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL; // Write PNG to buffer @@ -706,7 +706,7 @@ class ResizeWorker : public NanAsyncWorker { return Error(baton, hook); } baton->outputFormat = "webp"; -#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42) +#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)) } else if (baton->output == "__raw") { // Write raw, uncompressed image data to buffer if (baton->greyscale) { @@ -750,7 +750,7 @@ class ResizeWorker : public NanAsyncWorker { } baton->outputFormat = "jpeg"; } else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) { -#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42) +#if (VIPS_MAJOR_VERSION >= 8 || (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)) // Select PNG row filter int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL; // Write PNG to file diff --git a/test/unit/io.js b/test/unit/io.js index 1501b6f3..101a13c4 100755 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -537,6 +537,24 @@ describe('Input/output', function() { }); } + if (semver.gte(sharp.libvipsVersion(), '8.0.0')) { + it('Load GIF from Buffer [libvips ' + sharp.libvipsVersion() + '>=8.0.0]', function(done) { + var inputGifBuffer = fs.readFileSync(fixtures.inputGif); + sharp(inputGifBuffer) + .resize(320, 240) + .jpeg() + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual(data.length, info.size); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + } + if (semver.gte(sharp.libvipsVersion(), '7.42.0')) { describe('Ouput raw, uncompressed image data [libvips ' + sharp.libvipsVersion() + '>=7.42.0]', function() { it('1 channel greyscale image', function(done) {