处理系统业务的时候需要裁切图片,发现某些图片裁切的不对,是旋转了之后再裁切的

**原因:**苹果手机等设备拍照后,图片文件上带有orientation方向属性,系统打开显示时会自动根据方向属性进行调整,后台处理后,orientation方向属性丢失(类似安卓手机拍的照片),导致保存后的新图片被旋转。


import (
	"bytes"
	"errors"
	"github.com/rwcarlsen/goexif/exif"
	"golang.org/x/image/bmp"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"jiajiantech.com/util/log"
)

// ClipPhoto 图片裁切
// @param in 文件
// @param out 文件输出流
// @param x0 坐标1 x
// @param y0 坐标1 y
// @param x1 坐标2 x
// @param y1 坐标2 y
func ClipPhoto(in []byte, out io.Writer, x0, y0, x1, y1 int) error {
	im, fm, err := image.Decode(bytes.NewBuffer(in))
	if err != nil {
		return err
	}
	origin := im
	ori := ReadOrientation(in)
	//苹果手机拍照的图片,会有方向属性Orientation,经过Decode和Encode,编码处理后,方向属性会丢失,导致图片被旋转
	switch ori {
	case 6:
		origin = rotate90(im)
	case 3:
		origin = rotate180(im)
	case 8:
		origin = rotate270(im)
	}
	switch fm {
	case "jpeg":
		switch origin.(type) {
		case *image.RGBA:
			img := origin.(*image.RGBA)
			subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.RGBA)
			return jpeg.Encode(out, subImg, &jpeg.Options{Quality: 80})
		case *image.YCbCr:
			img := origin.(*image.YCbCr)
			subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.YCbCr)
			return jpeg.Encode(out, subImg, &jpeg.Options{Quality: 80})
		}
	case "png":
		switch origin.(type) {
		case *image.NRGBA:
			img := origin.(*image.NRGBA)
			subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.NRGBA)
			return png.Encode(out, subImg)
		case *image.RGBA:
			img := origin.(*image.RGBA)
			subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.RGBA)
			return png.Encode(out, subImg)
		}
	case "gif":
		img := origin.(*image.Paletted)
		subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.Paletted)
		return gif.Encode(out, subImg, &gif.Options{})
	case "bmp":
		img := origin.(*image.RGBA)
		subImg := img.SubImage(image.Rect(x0, y0, x1, y1)).(*image.RGBA)
		return bmp.Encode(out, subImg)
	default:
		return errors.New("ERROR FORMAT")
	}
	return nil
}

// ReadOrientation 方向判断
func ReadOrientation(fileByte []byte) int {

	x, err := exif.Decode(bytes.NewBuffer(fileByte))
	if err != nil {
		log.Error("failed to decode file, err: ", err)
		return 0
	}

	orientation, err := x.Get(exif.Orientation)
	if err != nil {
		log.Error("failed to get orientation, err: ", err)
		return 0
	}
	orientVal, err := orientation.Int(0)
	if err != nil {
		log.Error("failed to convert type of orientation, err: ", err)
		return 0
	}

	log.Debug("the value of photo orientation is :", orientVal)
	return orientVal
}

// 旋转90度
func rotate90(m image.Image) image.Image {
	rotate90 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx()))
	// 矩阵旋转
	for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ {
		for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- {
			//  设置像素点
			rotate90.Set(m.Bounds().Max.Y-x, y, m.At(y, x))
		}
	}
	return rotate90
}

// 旋转180度
func rotate180(m image.Image) image.Image {
	rotate180 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dx(), m.Bounds().Dy()))
	// 矩阵旋转
	for x := m.Bounds().Min.X; x < m.Bounds().Max.X; x++ {
		for y := m.Bounds().Min.Y; y < m.Bounds().Max.Y; y++ {
			//  设置像素点
			rotate180.Set(m.Bounds().Max.X-x, m.Bounds().Max.Y-y, m.At(x, y))
		}
	}
	return rotate180
}

// 旋转270度
func rotate270(m image.Image) image.Image {
	rotate270 := image.NewRGBA(image.Rect(0, 0, m.Bounds().Dy(), m.Bounds().Dx()))
	// 矩阵旋转
	for x := m.Bounds().Min.Y; x < m.Bounds().Max.Y; x++ {
		for y := m.Bounds().Max.X - 1; y >= m.Bounds().Min.X; y-- {
			// 设置像素点
			rotate270.Set(x, m.Bounds().Max.X-y, m.At(y, x))
		}
	}
	return rotate270
}