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

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