package images import ( "bytes" "github.com/rwcarlsen/goexif/exif" "image" "image/draw" "image/jpeg" "log" ) //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 := tag.Int(0) 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 }