Skip to content

Commit

Permalink
Allow rotate before pre-resize extraction #145
Browse files Browse the repository at this point in the history
  • Loading branch information
lovell committed Jan 4, 2015
1 parent 022f9f8 commit 4001709
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ Rotate the output image by either an explicit angle or auto-orient based on the

Use this method without `angle` to determine the angle from EXIF data. Mirroring is supported and may infer the use of a `flip` operation.

Method order is important when both rotating and extracting regions, for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.

#### flip()

Flip the image about the vertical Y axis. This always occurs after rotation, if any.
Expand Down
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var Sharp = function(input) {
canvas: 'c',
gravity: 0,
angle: 0,
rotateBeforePreExtract: false,
flip: false,
flop: false,
withoutEnlargement: false,
Expand Down Expand Up @@ -136,6 +137,10 @@ Sharp.prototype.extract = function(topOffset, leftOffset, width, height) {
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
this.options[name + suffix] = values[index];
}.bind(this));
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && this.options.angle !== 0) {
this.options.rotateBeforePreExtract = true;
}
return this;
};

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sharp",
"version": "0.8.3",
"version": "0.9.0",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
Expand Down Expand Up @@ -34,13 +34,13 @@
"vips"
],
"dependencies": {
"bluebird": "^2.3.11",
"bluebird": "^2.5.3",
"color": "^0.7.3",
"nan": "^1.4.1",
"semver": "^4.1.0"
"semver": "^4.2.0"
},
"devDependencies": {
"mocha": "^2.0.1",
"mocha": "^2.1.0",
"mocha-jshint": "^0.0.9",
"istanbul": "^0.3.5",
"coveralls": "^2.11.2"
Expand Down
34 changes: 23 additions & 11 deletions src/resize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct ResizeBaton {
double gamma;
bool greyscale;
int angle;
bool rotateBeforePreExtract;
bool flip;
bool flop;
bool progressive;
Expand Down Expand Up @@ -146,6 +147,25 @@ class ResizeWorker : public NanAsyncWorker {
}
vips_object_local(hook, image);

// Calculate angle of rotation
Angle rotation;
bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
if (flip && !baton->flip) {
// Add flip operation due to EXIF mirroring
baton->flip = TRUE;
}

// Rotate pre-extract
if (baton->rotateBeforePreExtract && rotation != Angle::D0) {
VipsImage *rotated;
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, rotated);
image = rotated;
}

// Pre extraction
if (baton->topOffsetPre != -1) {
VipsImage *extractedPre;
Expand All @@ -156,24 +176,15 @@ class ResizeWorker : public NanAsyncWorker {
image = extractedPre;
}

// Get input image width and height
// Get pre-resize image width and height
int inputWidth = image->Xsize;
int inputHeight = image->Ysize;

// Calculate angle of rotation, to be carried out later
Angle rotation;
bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
if (rotation == Angle::D90 || rotation == Angle::D270) {
// Swap input output width and height when rotating by 90 or 270 degrees
int swap = inputWidth;
inputWidth = inputHeight;
inputHeight = swap;
}
if (flip && !baton->flip) {
// Add flip operation due to EXIF mirroring
baton->flip = TRUE;
}

// Get window size of interpolator, used for determining shrink vs affine
int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str());
Expand Down Expand Up @@ -392,7 +403,7 @@ class ResizeWorker : public NanAsyncWorker {
}

// Rotate
if (rotation != Angle::D0) {
if (!baton->rotateBeforePreExtract && rotation != Angle::D0) {
VipsImage *rotated;
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
return Error(baton, hook);
Expand Down Expand Up @@ -918,6 +929,7 @@ NAN_METHOD(resize) {
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
baton->greyscale = options->Get(NanNew<String>("greyscale"))->BooleanValue();
baton->angle = options->Get(NanNew<String>("angle"))->Int32Value();
baton->rotateBeforePreExtract = options->Get(NanNew<String>("rotateBeforePreExtract"))->BooleanValue();
baton->flip = options->Get(NanNew<String>("flip"))->BooleanValue();
baton->flop = options->Get(NanNew<String>("flop"))->BooleanValue();
// Output options
Expand Down
24 changes: 24 additions & 0 deletions test/unit/extract.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,28 @@ describe('Partial image extraction', function() {
});
});

it('Extract then rotate', function(done) {
sharp(fixtures.inputJpg)
.extract(10, 10, 100, 100)
.rotate(90)
.toFile(fixtures.path('output.extract.extract-then-rotate.jpg'), function(err, info) {
if (err) throw err;
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
done();
});
});

it('Rotate then extract', function(done) {
sharp(fixtures.inputJpg)
.rotate(90)
.extract(10, 10, 100, 100)
.toFile(fixtures.path('output.extract.rotate-then-extract.jpg'), function(err, info) {
if (err) throw err;
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
done();
});
});

});

0 comments on commit 4001709

Please sign in to comment.