Proper handling of the EXIF cases.
Kudos to https://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ Signed-off-by: Yoan Blanc <yoan@dosimple.ch>
BIN
fixtures/exif/Landscape_1.jpg
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
fixtures/exif/Landscape_1_out.jpg
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
fixtures/exif/Landscape_2.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
fixtures/exif/Landscape_2_out.jpg
Normal file
|
After Width: | Height: | Size: 199 KiB |
BIN
fixtures/exif/Landscape_3.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
fixtures/exif/Landscape_3_out.jpg
Normal file
|
After Width: | Height: | Size: 199 KiB |
BIN
fixtures/exif/Landscape_4.jpg
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
fixtures/exif/Landscape_4_out.jpg
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
fixtures/exif/Landscape_5.jpg
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
fixtures/exif/Landscape_5_out.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
fixtures/exif/Landscape_6.jpg
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
fixtures/exif/Landscape_6_out.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
fixtures/exif/Landscape_7.jpg
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
fixtures/exif/Landscape_7_out.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
fixtures/exif/Landscape_8.jpg
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
fixtures/exif/Landscape_8_out.jpg
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
fixtures/exif/Portrait_1.jpg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
fixtures/exif/Portrait_1_out.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
fixtures/exif/Portrait_2.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_2_out.jpg
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
fixtures/exif/Portrait_3.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_3_out.jpg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
fixtures/exif/Portrait_4.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_4_out.jpg
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
fixtures/exif/Portrait_5.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_5_out.jpg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
fixtures/exif/Portrait_6.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_6_out.jpg
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
fixtures/exif/Portrait_7.jpg
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
fixtures/exif/Portrait_7_out.jpg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
fixtures/exif/Portrait_8.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
fixtures/exif/Portrait_8_out.jpg
Normal file
|
After Width: | Height: | Size: 174 KiB |
|
|
@ -518,7 +518,7 @@ func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
|
||||||
break // flip 1
|
break // flip 1
|
||||||
case 7:
|
case 7:
|
||||||
flip = true
|
flip = true
|
||||||
rotate = D90
|
rotate = D270
|
||||||
break // flip 6
|
break // flip 6
|
||||||
case 4:
|
case 4:
|
||||||
flip = true
|
flip = true
|
||||||
|
|
@ -526,7 +526,7 @@ func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
|
||||||
break // flip 3
|
break // flip 3
|
||||||
case 5:
|
case 5:
|
||||||
flip = true
|
flip = true
|
||||||
rotate = D270
|
rotate = D90
|
||||||
break // flip 8
|
break // flip 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package bimg
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -393,6 +394,65 @@ func TestResizePngWithTransparency(t *testing.T) {
|
||||||
Write("fixtures/transparent_out.png", newImg)
|
Write("fixtures/transparent_out.png", newImg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRotationAndFlip(t *testing.T) {
|
||||||
|
files := []struct {
|
||||||
|
Name string
|
||||||
|
Angle Angle
|
||||||
|
Flip bool
|
||||||
|
}{
|
||||||
|
{"Landscape_1", 0, false},
|
||||||
|
{"Landscape_2", 0, true},
|
||||||
|
{"Landscape_3", D180, false},
|
||||||
|
{"Landscape_4", D180, true},
|
||||||
|
{"Landscape_5", D90, true},
|
||||||
|
{"Landscape_6", D90, false},
|
||||||
|
{"Landscape_7", D270, true},
|
||||||
|
{"Landscape_8", D270, false},
|
||||||
|
{"Portrait_1", 0, false},
|
||||||
|
{"Portrait_2", 0, true},
|
||||||
|
{"Portrait_3", D180, false},
|
||||||
|
{"Portrait_4", D180, true},
|
||||||
|
{"Portrait_5", D90, true},
|
||||||
|
{"Portrait_6", D90, false},
|
||||||
|
{"Portrait_7", D270, true},
|
||||||
|
{"Portrait_8", D270, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
img, err := os.Open(fmt.Sprintf("fixtures/exif/%s.jpg", file.Name))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := ioutil.ReadAll(img)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
img.Close()
|
||||||
|
|
||||||
|
image, _, err := loadImage(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
angle, flip := calculateRotationAndFlip(image, D0)
|
||||||
|
if angle != file.Angle {
|
||||||
|
t.Errorf("Rotation for %v expected to be %v. got %v", file.Name, file.Angle, angle)
|
||||||
|
}
|
||||||
|
if flip != file.Flip {
|
||||||
|
t.Errorf("Flip for %v expected to be %v. got %v", file.Name, file.Flip, flip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visual debugging.
|
||||||
|
newImg, err := Resize(buf, Options{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(fmt.Sprintf("fixtures/exif/%s_out.jpg", file.Name), newImg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIfBothSmartCropOptionsAreIdentical(t *testing.T) {
|
func TestIfBothSmartCropOptionsAreIdentical(t *testing.T) {
|
||||||
if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
|
if !(VipsMajorVersion >= 8 && VipsMinorVersion > 4) {
|
||||||
t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
|
t.Skipf("Skipping this test, libvips doesn't meet version requirement %s > 8.4", VipsVersion)
|
||||||
|
|
|
||||||