From c213e9878d3d6335ebbff3f6811fb5ea5b77b7b3 Mon Sep 17 00:00:00 2001 From: beig Date: Thu, 8 Oct 2020 10:51:54 +0200 Subject: [PATCH] Add centre/center option to tile-based output (#2397) --- docs/api-output.md | 1 + lib/constructor.js | 1 + lib/output.js | 5 +++ src/pipeline.cc | 2 + src/pipeline.h | 1 + test/fixtures/expected/tile_centered.jpg | Bin 0 -> 7132 bytes test/unit/tile.js | 48 +++++++++++++++++++++++ 7 files changed, 58 insertions(+) create mode 100644 test/fixtures/expected/tile_centered.jpg diff --git a/docs/api-output.md b/docs/api-output.md index 3fa0dc21..083ad80d 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -372,6 +372,7 @@ Warning: multiple sharp instances concurrently producing tile output can expose - `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`) - `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`) - `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. (optional, default `'dz'`) + - `options.centre|center` **[boolean][7]** center image in tile (optional, default `false`) ### Examples diff --git a/lib/constructor.js b/lib/constructor.js index e1691889..becc49b9 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -238,6 +238,7 @@ const Sharp = function (input, options) { tileAngle: 0, tileSkipBlanks: -1, tileBackground: [255, 255, 255, 255], + tileCentre: false, linearA: 1, linearB: 0, // Function to notify of libvips warnings diff --git a/lib/output.js b/lib/output.js index 13d349e0..8ec94873 100644 --- a/lib/output.js +++ b/lib/output.js @@ -658,6 +658,7 @@ function raw () { * @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images * @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file). * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. + * @param {boolean} [options.centre=false] center image in tile. * @returns {Sharp} * @throws {Error} Invalid parameters */ @@ -726,6 +727,10 @@ function tile (options) { } else if (is.defined(options.layout) && options.layout === 'google') { this.options.tileSkipBlanks = 5; } + // Center image in tile + if (is.defined(options.centre) || is.defined(options.center)) { + this._setBooleanOption('tileCentre', options.centre || options.center); + } } // Format if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) { diff --git a/src/pipeline.cc b/src/pipeline.cc index 3081c336..b182d7bb 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -1014,6 +1014,7 @@ class PipelineWorker : public Napi::AsyncWorker { ->set("suffix", const_cast(suffix.data())) ->set("angle", CalculateAngleRotation(baton->tileAngle)) ->set("background", baton->tileBackground) + ->set("centre", baton->tileCentre) ->set("skip_blanks", baton->tileSkipBlanks); // libvips chooses a default depth based on layout. Instead of replicating that logic here by // not passing anything - libvips will handle choice @@ -1403,6 +1404,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) { baton->tileDepth = static_cast( vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_DZ_DEPTH, sharp::AttrAsStr(options, "tileDepth").data())); + baton->tileCentre = sharp::AttrAsBool(options, "tileCentre"); // Force random access for certain operations if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) { diff --git a/src/pipeline.h b/src/pipeline.h index ef2d668d..f4ebf6f1 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -79,6 +79,7 @@ struct PipelineBaton { int cropOffsetLeft; int cropOffsetTop; bool premultiplied; + bool tileCentre; std::string kernel; bool fastShrinkOnLoad; double tintA; diff --git a/test/fixtures/expected/tile_centered.jpg b/test/fixtures/expected/tile_centered.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9ae14ff6d5644f2c1e707728a9041004454cdb2a GIT binary patch literal 7132 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