diff --git a/lib/resize.js b/lib/resize.js index 8444fda5f..76680ad41 100644 --- a/lib/resize.js +++ b/lib/resize.js @@ -353,7 +353,7 @@ function extract (options) { } }, this); // Ensure existing rotation occurs before pre-resize extraction - if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) { + if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true || this.options.rotationAngle !== 0)) { this.options.rotateBeforePreExtract = true; } return this; diff --git a/src/pipeline.cc b/src/pipeline.cc index bc5e67a63..a4e1b4e74 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -108,9 +108,16 @@ class PipelineWorker : public Nan::AsyncWorker { } // Rotate pre-extract - if (baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { - image = image.rot(rotation); - sharp::RemoveExifOrientation(image); + if (baton->rotateBeforePreExtract) { + if (rotation != VIPS_ANGLE_D0) { + image = image.rot(rotation); + sharp::RemoveExifOrientation(image); + } + if (baton->rotationAngle != 0.0) { + std::vector background; + std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground); + image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)); + } } // Trim @@ -403,12 +410,13 @@ class PipelineWorker : public Nan::AsyncWorker { ->set("kernel", kernel)); } - // Rotate - if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { - image = image.rot(rotation); - sharp::RemoveExifOrientation(image); + // Rotate post-extract 90-angle + if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { + image = image.rot(rotation); + sharp::RemoveExifOrientation(image); } + // Flip (mirror about Y axis) if (baton->flip) { image = image.flip(VIPS_DIRECTION_VERTICAL); @@ -491,8 +499,8 @@ class PipelineWorker : public Nan::AsyncWorker { } } - // Rotate by degree - if (baton->rotationAngle != 0.0) { + // Rotate post-extract non-90 angle + if (!baton->rotateBeforePreExtract && baton->rotationAngle != 0.0) { std::vector background; std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground); image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)); diff --git a/test/fixtures/expected/extract-rotate-45.jpg b/test/fixtures/expected/extract-rotate-45.jpg new file mode 100644 index 000000000..38efece21 Binary files /dev/null and b/test/fixtures/expected/extract-rotate-45.jpg differ diff --git a/test/fixtures/expected/rotate-extract-45.jpg b/test/fixtures/expected/rotate-extract-45.jpg new file mode 100644 index 000000000..e97349bdb Binary files /dev/null and b/test/fixtures/expected/rotate-extract-45.jpg differ diff --git a/test/unit/extract.js b/test/unit/extract.js index 107bbbaf4..41be03875 100644 --- a/test/unit/extract.js +++ b/test/unit/extract.js @@ -121,6 +121,32 @@ describe('Partial image extraction', function () { }); }); + it('Extract then rotate non-90 anagle', function (done) { + sharp(fixtures.inputPngWithGreyAlpha) + .extract({ left: 20, top: 10, width: 380, height: 280 }) + .rotate(45) + .jpeg() + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual(467, info.width); + assert.strictEqual(467, info.height); + fixtures.assertSimilar(fixtures.expected('extract-rotate-45.jpg'), data, done); + }); + }); + + it('Rotate then extract non-90 angle', function (done) { + sharp(fixtures.inputPngWithGreyAlpha) + .rotate(45) + .extract({ left: 20, top: 10, width: 380, height: 280 }) + .jpeg() + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual(380, info.width); + assert.strictEqual(280, info.height); + fixtures.assertSimilar(fixtures.expected('rotate-extract-45.jpg'), data, { threshold: 7 }, done); + }); + }); + describe('Invalid parameters', function () { describe('using the legacy extract(top,left,width,height) syntax', function () { it('String top', function () {