diff --git a/lib/composite.js b/lib/composite.js index 2dd122fb..4a133d68 100644 --- a/lib/composite.js +++ b/lib/composite.js @@ -110,6 +110,7 @@ const blend = { * @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified. * @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `Red!`. * @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified. + * @param {Boolean} [images[].autoOrient=false] - set to true to use EXIF orientation data, if present, to orient the image. * @param {String} [images[].blend='over'] - how to blend this image with the image below. * @param {String} [images[].gravity='centre'] - gravity at which to place the overlay. * @param {Number} [images[].top] - the pixel offset from the top edge. @@ -136,8 +137,11 @@ function composite (images) { throw is.invalidParameterError('image to composite', 'object', image); } const inputOptions = this._inputOptionsFromObject(image); + const descriptor = this._createInputDescriptor(image.input, inputOptions, { allowStream: false }); + console.log('inputOptions', inputOptions); + console.log('descriptor', descriptor); const composite = { - input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }), + input: descriptor, blend: 'over', tile: false, left: 0, diff --git a/lib/input.js b/lib/input.js index 41233094..9aa8dd8b 100644 --- a/lib/input.js +++ b/lib/input.js @@ -36,6 +36,7 @@ function _inputOptionsFromObject (obj) { */ function _createInputDescriptor (input, inputOptions, containerOptions) { const inputDescriptor = { + autoOrient: false, failOn: 'warning', limitInputPixels: Math.pow(0x3FFF, 2), ignoreIcc: false, diff --git a/src/common.cc b/src/common.cc index 1d6a8f63..3b0610e7 100644 --- a/src/common.cc +++ b/src/common.cc @@ -166,6 +166,8 @@ namespace sharp { descriptor->access = AttrAsBool(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM; // Remove safety features and allow unlimited input descriptor->unlimited = AttrAsBool(input, "unlimited"); + // Use the EXIF orientation to auto orient the image + descriptor->autoOrient = AttrAsBool(input, "autoOrient"); return descriptor; } diff --git a/src/common.h b/src/common.h index 67d86411..b8a6d438 100644 --- a/src/common.h +++ b/src/common.h @@ -33,6 +33,7 @@ namespace sharp { struct InputDescriptor { // NOLINT(runtime/indentation_namespace) std::string name; std::string file; + bool autoOrient; char *buffer; VipsFailOn failOn; uint64_t limitInputPixels; @@ -73,6 +74,7 @@ namespace sharp { std::vector pdfBackground; InputDescriptor(): + autoOrient(false), buffer(nullptr), failOn(VIPS_FAIL_ON_WARNING), limitInputPixels(0x3FFF * 0x3FFF), diff --git a/src/pipeline.cc b/src/pipeline.cc index ec714eea..eae463a6 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -627,6 +627,32 @@ class PipelineWorker : public Napi::AsyncWorker { composite->input->access = access; std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input); compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspacePipeline); + + if (composite->input->autoOrient) { + // Calculate angle of rotation + VipsAngle compositeAutoRotation = VIPS_ANGLE_D0; + bool compositeAutoFlip = false; + bool compositeAutoFlop = false; + + // Rotate and flip image according to Exif orientation + std::tie(compositeAutoRotation, compositeAutoFlip, compositeAutoFlop) = + CalculateExifRotationAndFlip(sharp::ExifOrientation(compositeImage)); + + compositeImage = sharp::RemoveExifOrientation(compositeImage); + + if (compositeAutoRotation != VIPS_ANGLE_D0) { + compositeImage = compositeImage.rot(compositeAutoRotation); + } + // Mirror vertically (up-down) about the x-axis + if (compositeAutoFlip) { + compositeImage = compositeImage.flip(VIPS_DIRECTION_VERTICAL); + } + // Mirror horizontally (left-right) about the y-axis + if (compositeAutoFlop) { + compositeImage = compositeImage.flip(VIPS_DIRECTION_HORIZONTAL); + } + } + // Verify within current dimensions if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) { throw vips::VError("Image to composite must have same dimensions or smaller");