EXIF Orientation tags 2 and 4 were flipping instead of flopping

This commit is contained in:
Brandon Aaron 2015-10-16 15:41:40 -04:00
parent 3cefa6f2bf
commit 79470d2e07
35 changed files with 47 additions and 5 deletions

View File

@ -250,11 +250,16 @@ class PipelineWorker : public AsyncWorker {
// Calculate angle of rotation // Calculate angle of rotation
Angle rotation; Angle rotation;
bool flip; bool flip;
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image); bool flop;
std::tie(rotation, flip, flop) = CalculateRotationAndFlip(baton->angle, image);
if (flip && !baton->flip) { if (flip && !baton->flip) {
// Add flip operation due to EXIF mirroring // Add flip operation due to EXIF mirroring
baton->flip = TRUE; baton->flip = TRUE;
} }
if (flop && !baton->flop) {
// Add flip operation due to EXIF mirroring
baton->flop = TRUE;
}
// Rotate pre-extract // Rotate pre-extract
if (baton->rotateBeforePreExtract && rotation != Angle::D0) { if (baton->rotateBeforePreExtract && rotation != Angle::D0) {
@ -1041,18 +1046,19 @@ class PipelineWorker : public AsyncWorker {
2. Use input image EXIF Orientation header - supports mirroring 2. Use input image EXIF Orientation header - supports mirroring
3. Otherwise default to zero, i.e. no rotation 3. Otherwise default to zero, i.e. no rotation
*/ */
std::tuple<Angle, bool> std::tuple<Angle, bool, bool>
CalculateRotationAndFlip(int const angle, VipsImage const *input) { CalculateRotationAndFlip(int const angle, VipsImage const *input) {
Angle rotate = Angle::D0; Angle rotate = Angle::D0;
bool flip = FALSE; bool flip = FALSE;
bool flop = FALSE;
if (angle == -1) { if (angle == -1) {
switch(ExifOrientation(input)) { switch(ExifOrientation(input)) {
case 6: rotate = Angle::D90; break; case 6: rotate = Angle::D90; break;
case 3: rotate = Angle::D180; break; case 3: rotate = Angle::D180; break;
case 8: rotate = Angle::D270; break; case 8: rotate = Angle::D270; break;
case 2: flip = TRUE; break; // flip 1 case 2: flop = TRUE; break; // flop 1
case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6 case 7: flip = TRUE; rotate = Angle::D90; break; // flip 6
case 4: flip = TRUE; rotate = Angle::D180; break; // flip 3 case 4: flop = TRUE; rotate = Angle::D180; break; // flop 3
case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8 case 5: flip = TRUE; rotate = Angle::D270; break; // flip 8
} }
} else { } else {
@ -1064,7 +1070,7 @@ class PipelineWorker : public AsyncWorker {
rotate = Angle::D270; rotate = Angle::D270;
} }
} }
return std::make_tuple(rotate, flip); return std::make_tuple(rotate, flip, flop);
} }
/* /*
@ -1240,3 +1246,4 @@ NAN_METHOD(pipeline) {
Local<Value> queueLength[1] = { New<Uint32>(counterQueue) }; Local<Value> queueLength[1] = { New<Uint32>(counterQueue) };
queueListener->Call(1, queueLength); queueListener->Call(1, queueLength);
} }

BIN
test/fixtures/Landscape_1.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
test/fixtures/Landscape_2.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_3.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Landscape_4.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

0
test/fixtures/Landscape_5.jpg vendored Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_6.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_7.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

0
test/fixtures/Landscape_8.jpg vendored Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Portrait_1.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
test/fixtures/Portrait_2.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_3.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_4.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
test/fixtures/Portrait_5.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
test/fixtures/Portrait_6.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_7.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
test/fixtures/Portrait_8.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@ -40,6 +40,24 @@ var fingerprint = function(image, callback) {
module.exports = { module.exports = {
inputJpgWithLandscapeExif1: getPath('Landscape_1.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif2: getPath('Landscape_2.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif3: getPath('Landscape_3.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif4: getPath('Landscape_4.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif5: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif6: getPath('Landscape_6.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif7: getPath('Landscape_7.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithLandscapeExif8: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif1: getPath('Portrait_1.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif2: getPath('Portrait_2.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif3: getPath('Portrait_3.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif4: getPath('Portrait_4.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif5: getPath('Portrait_5.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif6: getPath('Portrait_6.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif7: getPath('Portrait_7.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpgWithPortraitExif8: getPath('Portrait_8.jpg'), // https://github.com/recurser/exif-orientation-examples
inputJpg: getPath('2569067123_aca715a2ee_o.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/ inputJpg: getPath('2569067123_aca715a2ee_o.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/
inputJpgWithExif: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg inputJpgWithExif: getPath('Landscape_8.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg
inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg inputJpgWithExifMirroring: getPath('Landscape_5.jpg'), // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_5.jpg

View File

@ -9,6 +9,23 @@ sharp.cache(0);
describe('Rotation', function() { describe('Rotation', function() {
['Landscape', 'Portrait'].forEach(function(orientation) {
[1,2,3,4,5,6,7,8].forEach(function(exifTag) {
it('Input image has Orientation EXIF tag value of (' + exifTag + '), auto-rotate', function(done) {
sharp(fixtures['inputJpgWith'+orientation+'Exif'+exifTag])
.rotate()
.resize(320)
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(orientation === 'Landscape' ? 240 : 427, info.height);
fixtures.assertSimilar(fixtures.expected(orientation + '_' + exifTag + '-out.jpg'), data, done);
});
});
});
});
it('Rotate by 90 degrees, respecting output input size', function(done) { it('Rotate by 90 degrees, respecting output input size', function(done) {
sharp(fixtures.inputJpg).rotate(90).resize(320, 240).toBuffer(function(err, data, info) { sharp(fixtures.inputJpg).rotate(90).resize(320, 240).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;