Allow rotate before pre-resize extraction #145

This commit is contained in:
Lovell Fuller 2015-01-04 19:29:37 +00:00
parent a190ae6b08
commit 5dab3c8482
5 changed files with 57 additions and 14 deletions

View File

@ -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.

View File

@ -37,6 +37,7 @@ var Sharp = function(input) {
canvas: 'c',
gravity: 0,
angle: 0,
rotateBeforePreExtract: false,
flip: false,
flop: false,
withoutEnlargement: false,
@ -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;
};

View File

@ -1,6 +1,6 @@
{
"name": "sharp",
"version": "0.8.4",
"version": "0.9.0",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
@ -37,10 +37,10 @@
"bluebird": "^2.6.4",
"color": "^0.7.3",
"nan": "^1.5.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"

View File

@ -58,6 +58,7 @@ struct ResizeBaton {
double gamma;
bool greyscale;
int angle;
bool rotateBeforePreExtract;
bool flip;
bool flop;
bool progressive;
@ -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;
@ -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());
@ -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);
@ -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

View File

@ -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();
});
});
});