From c8e59f08ec06bc4e27b5fa6137f6e6f1be461d61 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Thu, 4 May 2017 16:40:49 +0100 Subject: [PATCH] Add support for Buffer and Stream-based TIFF output --- docs/api-output.md | 2 +- docs/changelog.md | 4 ++++ docs/index.md | 2 +- lib/constructor.js | 2 +- lib/output.js | 2 +- src/pipeline.cc | 20 +++++++++++++++++++- test/unit/io.js | 16 +++++++++++++++- 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/docs/api-output.md b/docs/api-output.md index a846f739..8f41c9f3 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -38,7 +38,7 @@ Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe ## toBuffer Write output to a Buffer. -JPEG, PNG, WebP, and RAW output are supported. +JPEG, PNG, WebP, TIFF and RAW output are supported. By default, the format will match the input image, except GIF and SVG input which become PNG output. `callback`, if present, gets three arguments `(err, data, info)` where: diff --git a/docs/changelog.md b/docs/changelog.md index 4ab48f45..9c04ca84 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,10 @@ Requires libvips v8.5.2. [#573](https://github.com/lovell/sharp/issues/573) [@strarsis](https://github.com/strarsis) +* Add support for Buffer and Stream-based TIFF output. + [#587](https://github.com/lovell/sharp/issues/587) + [@strarsis](https://github.com/strarsis) + * Expose warnings from libvips via NODE_DEBUG=sharp environment variable. [#607](https://github.com/lovell/sharp/issues/607) [@puzrin](https://github.com/puzrin) diff --git a/docs/index.md b/docs/index.md index a4d966f4..bcfdd81e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,7 +22,7 @@ the installation of any external runtime dependencies. This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images. -Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data. +Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data. Streams, Buffer objects and the filesystem can be used for input and output. diff --git a/lib/constructor.js b/lib/constructor.js index d73e586d..deb5ed4c 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -32,7 +32,7 @@ const debuglog = util.debuglog('sharp'); * * Constructor factory to create an instance of `sharp`, to which further methods are chained. * - * JPEG, PNG or WebP format image data can be streamed out from this object. + * JPEG, PNG, WebP or TIFF format image data can be streamed out from this object. * When using Stream based output, derived attributes are available from the `info` event. * * Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class. diff --git a/lib/output.js b/lib/output.js index a3d375ba..3b1f9664 100644 --- a/lib/output.js +++ b/lib/output.js @@ -46,7 +46,7 @@ function toFile (fileOut, callback) { /** * Write output to a Buffer. - * JPEG, PNG, WebP, and RAW output are supported. + * JPEG, PNG, WebP, TIFF and RAW output are supported. * By default, the format will match the input image, except GIF and SVG input which become PNG output. * * `callback`, if present, gets three arguments `(err, data, info)` where: diff --git a/src/pipeline.cc b/src/pipeline.cc index fd800058..6a54e1a2 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -734,7 +734,7 @@ class PipelineWorker : public Nan::AsyncWorker { baton->width = image.width(); baton->height = image.height(); // Output - if (baton->fileOut == "") { + if (baton->fileOut.empty()) { // Buffer output if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == ImageType::JPEG)) { // Write JPEG to buffer @@ -786,6 +786,24 @@ class PipelineWorker : public Nan::AsyncWorker { area->free_fn = nullptr; vips_area_unref(area); baton->formatOut = "webp"; + } else if (baton->formatOut == "tiff" || (baton->formatOut == "input" && inputImageType == ImageType::TIFF)) { + // Cast pixel values to float, if required + if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) { + image = image.cast(VIPS_FORMAT_FLOAT); + } + // Write TIFF to buffer + VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option() + ->set("strip", !baton->withMetadata) + ->set("Q", baton->tiffQuality) + ->set("squash", baton->tiffSquash) + ->set("compression", baton->tiffCompression) + ->set("predictor", baton->tiffPredictor))); + baton->bufferOut = static_cast(area->data); + baton->bufferOutLength = area->length; + area->free_fn = nullptr; + vips_area_unref(area); + baton->formatOut = "tiff"; + baton->channels = std::min(baton->channels, 3); } else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) { // Write raw, uncompressed image data to buffer if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) { diff --git a/test/unit/io.js b/test/unit/io.js index c73e100a..958ba739 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -837,6 +837,20 @@ describe('Input/output', function () { }); }); + it('Save TIFF to Buffer', function (done) { + sharp(fixtures.inputTiff) + .resize(320, 240) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual(data.length, info.size); + assert.strictEqual('tiff', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + done(); + }); + }); + it('Invalid WebP quality throws error', function () { assert.throws(function () { sharp().webp({ quality: 101 }); @@ -914,7 +928,7 @@ describe('Input/output', function () { }); }); - it('TIFF deflate compression with hoizontal predictor shrinks test file', function (done) { + it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) { const startSize = fs.statSync(fixtures.inputTiffUncompressed).size; sharp(fixtures.inputTiffUncompressed) .tiff({