123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package images
- import (
- "bytes"
- "image"
- "image/draw"
- "image/jpeg"
- "log"
- "github.com/seaweedfs/goexif/exif"
- )
- // many code is copied from http://camlistore.org/pkg/images/images.go
- func FixJpgOrientation(data []byte) (oriented []byte) {
- ex, err := exif.Decode(bytes.NewReader(data))
- if err != nil {
- return data
- }
- tag, err := ex.Get(exif.Orientation)
- if err != nil {
- return data
- }
- angle := 0
- flipMode := FlipDirection(0)
- orient, err := tag.Int(0)
- if err != nil {
- return data
- }
- switch orient {
- case topLeftSide:
- // do nothing
- return data
- case topRightSide:
- flipMode = 2
- case bottomRightSide:
- angle = 180
- case bottomLeftSide:
- angle = 180
- flipMode = 2
- case leftSideTop:
- angle = -90
- flipMode = 2
- case rightSideTop:
- angle = -90
- case rightSideBottom:
- angle = 90
- flipMode = 2
- case leftSideBottom:
- angle = 90
- }
- if srcImage, _, err := image.Decode(bytes.NewReader(data)); err == nil {
- dstImage := flip(rotate(srcImage, angle), flipMode)
- var buf bytes.Buffer
- jpeg.Encode(&buf, dstImage, nil)
- return buf.Bytes()
- }
- return data
- }
- // Exif Orientation Tag values
- // http://sylvana.net/jpegcrop/exif_orientation.html
- const (
- topLeftSide = 1
- topRightSide = 2
- bottomRightSide = 3
- bottomLeftSide = 4
- leftSideTop = 5
- rightSideTop = 6
- rightSideBottom = 7
- leftSideBottom = 8
- )
- // The FlipDirection type is used by the Flip option in DecodeOpts
- // to indicate in which direction to flip an image.
- type FlipDirection int
- // FlipVertical and FlipHorizontal are two possible FlipDirections
- // values to indicate in which direction an image will be flipped.
- const (
- FlipVertical FlipDirection = 1 << iota
- FlipHorizontal
- )
- type DecodeOpts struct {
- // Rotate specifies how to rotate the image.
- // If nil, the image is rotated automatically based on EXIF metadata.
- // If an int, Rotate is the number of degrees to rotate
- // counter clockwise and must be one of 0, 90, -90, 180, or
- // -180.
- Rotate interface{}
- // Flip specifies how to flip the image.
- // If nil, the image is flipped automatically based on EXIF metadata.
- // Otherwise, Flip is a FlipDirection bitfield indicating how to flip.
- Flip interface{}
- }
- func rotate(im image.Image, angle int) image.Image {
- var rotated *image.NRGBA
- // trigonometric (i.e counter clock-wise)
- switch angle {
- case 90:
- newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
- rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
- for y := 0; y < newH; y++ {
- for x := 0; x < newW; x++ {
- rotated.Set(x, y, im.At(newH-1-y, x))
- }
- }
- case -90:
- newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
- rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
- for y := 0; y < newH; y++ {
- for x := 0; x < newW; x++ {
- rotated.Set(x, y, im.At(y, newW-1-x))
- }
- }
- case 180, -180:
- newW, newH := im.Bounds().Dx(), im.Bounds().Dy()
- rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
- for y := 0; y < newH; y++ {
- for x := 0; x < newW; x++ {
- rotated.Set(x, y, im.At(newW-1-x, newH-1-y))
- }
- }
- default:
- return im
- }
- return rotated
- }
- // flip returns a flipped version of the image im, according to
- // the direction(s) in dir.
- // It may flip the imput im in place and return it, or it may allocate a
- // new NRGBA (if im is an *image.YCbCr).
- func flip(im image.Image, dir FlipDirection) image.Image {
- if dir == 0 {
- return im
- }
- ycbcr := false
- var nrgba image.Image
- dx, dy := im.Bounds().Dx(), im.Bounds().Dy()
- di, ok := im.(draw.Image)
- if !ok {
- if _, ok := im.(*image.YCbCr); !ok {
- log.Printf("failed to flip image: input does not satisfy draw.Image")
- return im
- }
- // because YCbCr does not implement Set, we replace it with a new NRGBA
- ycbcr = true
- nrgba = image.NewNRGBA(image.Rect(0, 0, dx, dy))
- di, ok = nrgba.(draw.Image)
- if !ok {
- log.Print("failed to flip image: could not cast an NRGBA to a draw.Image")
- return im
- }
- }
- if dir&FlipHorizontal != 0 {
- for y := 0; y < dy; y++ {
- for x := 0; x < dx/2; x++ {
- old := im.At(x, y)
- di.Set(x, y, im.At(dx-1-x, y))
- di.Set(dx-1-x, y, old)
- }
- }
- }
- if dir&FlipVertical != 0 {
- for y := 0; y < dy/2; y++ {
- for x := 0; x < dx; x++ {
- old := im.At(x, y)
- di.Set(x, y, im.At(x, dy-1-y))
- di.Set(x, dy-1-y, old)
- }
- }
- }
- if ycbcr {
- return nrgba
- }
- return im
- }
|