mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Allow rotate before pre-resize extraction #145
This commit is contained in:
parent
a190ae6b08
commit
5dab3c8482
@ -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.
|
||||
|
5
index.js
5
index.js
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user