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.

190 lines
4.4 KiB

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