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.

178 lines
4.2 KiB

  1. package images
  2. import (
  3. "bytes"
  4. "github.com/rwcarlsen/goexif/exif"
  5. "image"
  6. "image/draw"
  7. "image/jpeg"
  8. "log"
  9. )
  10. //many code is copied from http://camlistore.org/pkg/images/images.go
  11. func FixJpgOrientation(data []byte) (oriented []byte) {
  12. ex, err := exif.Decode(bytes.NewReader(data))
  13. if err != nil {
  14. return data
  15. }
  16. tag, err := ex.Get(exif.Orientation)
  17. if err != nil {
  18. return data
  19. }
  20. angle := 0
  21. flipMode := FlipDirection(0)
  22. orient := tag.Int(0)
  23. switch orient {
  24. case topLeftSide:
  25. // do nothing
  26. return data
  27. case topRightSide:
  28. flipMode = 2
  29. case bottomRightSide:
  30. angle = 180
  31. case bottomLeftSide:
  32. angle = 180
  33. flipMode = 2
  34. case leftSideTop:
  35. angle = -90
  36. flipMode = 2
  37. case rightSideTop:
  38. angle = -90
  39. case rightSideBottom:
  40. angle = 90
  41. flipMode = 2
  42. case leftSideBottom:
  43. angle = 90
  44. }
  45. if srcImage, _, err := image.Decode(bytes.NewReader(data)); err == nil {
  46. dstImage := flip(rotate(srcImage, angle), flipMode)
  47. var buf bytes.Buffer
  48. jpeg.Encode(&buf, dstImage, nil)
  49. return buf.Bytes()
  50. }
  51. return data
  52. }
  53. // Exif Orientation Tag values
  54. // http://sylvana.net/jpegcrop/exif_orientation.html
  55. const (
  56. topLeftSide = 1
  57. topRightSide = 2
  58. bottomRightSide = 3
  59. bottomLeftSide = 4
  60. leftSideTop = 5
  61. rightSideTop = 6
  62. rightSideBottom = 7
  63. leftSideBottom = 8
  64. )
  65. // The FlipDirection type is used by the Flip option in DecodeOpts
  66. // to indicate in which direction to flip an image.
  67. type FlipDirection int
  68. // FlipVertical and FlipHorizontal are two possible FlipDirections
  69. // values to indicate in which direction an image will be flipped.
  70. const (
  71. FlipVertical FlipDirection = 1 << iota
  72. FlipHorizontal
  73. )
  74. type DecodeOpts struct {
  75. // Rotate specifies how to rotate the image.
  76. // If nil, the image is rotated automatically based on EXIF metadata.
  77. // If an int, Rotate is the number of degrees to rotate
  78. // counter clockwise and must be one of 0, 90, -90, 180, or
  79. // -180.
  80. Rotate interface{}
  81. // Flip specifies how to flip the image.
  82. // If nil, the image is flipped automatically based on EXIF metadata.
  83. // Otherwise, Flip is a FlipDirection bitfield indicating how to flip.
  84. Flip interface{}
  85. }
  86. func rotate(im image.Image, angle int) image.Image {
  87. var rotated *image.NRGBA
  88. // trigonometric (i.e counter clock-wise)
  89. switch angle {
  90. case 90:
  91. newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
  92. rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
  93. for y := 0; y < newH; y++ {
  94. for x := 0; x < newW; x++ {
  95. rotated.Set(x, y, im.At(newH-1-y, x))
  96. }
  97. }
  98. case -90:
  99. newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
  100. rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
  101. for y := 0; y < newH; y++ {
  102. for x := 0; x < newW; x++ {
  103. rotated.Set(x, y, im.At(y, newW-1-x))
  104. }
  105. }
  106. case 180, -180:
  107. newW, newH := im.Bounds().Dx(), im.Bounds().Dy()
  108. rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
  109. for y := 0; y < newH; y++ {
  110. for x := 0; x < newW; x++ {
  111. rotated.Set(x, y, im.At(newW-1-x, newH-1-y))
  112. }
  113. }
  114. default:
  115. return im
  116. }
  117. return rotated
  118. }
  119. // flip returns a flipped version of the image im, according to
  120. // the direction(s) in dir.
  121. // It may flip the imput im in place and return it, or it may allocate a
  122. // new NRGBA (if im is an *image.YCbCr).
  123. func flip(im image.Image, dir FlipDirection) image.Image {
  124. if dir == 0 {
  125. return im
  126. }
  127. ycbcr := false
  128. var nrgba image.Image
  129. dx, dy := im.Bounds().Dx(), im.Bounds().Dy()
  130. di, ok := im.(draw.Image)
  131. if !ok {
  132. if _, ok := im.(*image.YCbCr); !ok {
  133. log.Printf("failed to flip image: input does not satisfy draw.Image")
  134. return im
  135. }
  136. // because YCbCr does not implement Set, we replace it with a new NRGBA
  137. ycbcr = true
  138. nrgba = image.NewNRGBA(image.Rect(0, 0, dx, dy))
  139. di, ok = nrgba.(draw.Image)
  140. if !ok {
  141. log.Print("failed to flip image: could not cast an NRGBA to a draw.Image")
  142. return im
  143. }
  144. }
  145. if dir&FlipHorizontal != 0 {
  146. for y := 0; y < dy; y++ {
  147. for x := 0; x < dx/2; x++ {
  148. old := im.At(x, y)
  149. di.Set(x, y, im.At(dx-1-x, y))
  150. di.Set(dx-1-x, y, old)
  151. }
  152. }
  153. }
  154. if dir&FlipVertical != 0 {
  155. for y := 0; y < dy/2; y++ {
  156. for x := 0; x < dx; x++ {
  157. old := im.At(x, y)
  158. di.Set(x, y, im.At(x, dy-1-y))
  159. di.Set(x, dy-1-y, old)
  160. }
  161. }
  162. }
  163. if ycbcr {
  164. return nrgba
  165. }
  166. return im
  167. }