You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							182 lines
						
					
					
						
							4.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							182 lines
						
					
					
						
							4.2 KiB
						
					
					
				| 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 | |
| }
 |