transform.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package imaging
  2. import (
  3. "image"
  4. "image/color"
  5. "math"
  6. )
  7. // FlipH flips the image horizontally (from left to right) and returns the transformed image.
  8. func FlipH(img image.Image) *image.NRGBA {
  9. src := newScanner(img)
  10. dstW := src.w
  11. dstH := src.h
  12. rowSize := dstW * 4
  13. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  14. parallel(0, dstH, func(ys <-chan int) {
  15. for dstY := range ys {
  16. i := dstY * dst.Stride
  17. srcY := dstY
  18. src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
  19. reverse(dst.Pix[i : i+rowSize])
  20. }
  21. })
  22. return dst
  23. }
  24. // FlipV flips the image vertically (from top to bottom) and returns the transformed image.
  25. func FlipV(img image.Image) *image.NRGBA {
  26. src := newScanner(img)
  27. dstW := src.w
  28. dstH := src.h
  29. rowSize := dstW * 4
  30. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  31. parallel(0, dstH, func(ys <-chan int) {
  32. for dstY := range ys {
  33. i := dstY * dst.Stride
  34. srcY := dstH - dstY - 1
  35. src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
  36. }
  37. })
  38. return dst
  39. }
  40. // Transpose flips the image horizontally and rotates 90 degrees counter-clockwise.
  41. func Transpose(img image.Image) *image.NRGBA {
  42. src := newScanner(img)
  43. dstW := src.h
  44. dstH := src.w
  45. rowSize := dstW * 4
  46. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  47. parallel(0, dstH, func(ys <-chan int) {
  48. for dstY := range ys {
  49. i := dstY * dst.Stride
  50. srcX := dstY
  51. src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
  52. }
  53. })
  54. return dst
  55. }
  56. // Transverse flips the image vertically and rotates 90 degrees counter-clockwise.
  57. func Transverse(img image.Image) *image.NRGBA {
  58. src := newScanner(img)
  59. dstW := src.h
  60. dstH := src.w
  61. rowSize := dstW * 4
  62. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  63. parallel(0, dstH, func(ys <-chan int) {
  64. for dstY := range ys {
  65. i := dstY * dst.Stride
  66. srcX := dstH - dstY - 1
  67. src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
  68. reverse(dst.Pix[i : i+rowSize])
  69. }
  70. })
  71. return dst
  72. }
  73. // Rotate90 rotates the image 90 degrees counter-clockwise and returns the transformed image.
  74. func Rotate90(img image.Image) *image.NRGBA {
  75. src := newScanner(img)
  76. dstW := src.h
  77. dstH := src.w
  78. rowSize := dstW * 4
  79. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  80. parallel(0, dstH, func(ys <-chan int) {
  81. for dstY := range ys {
  82. i := dstY * dst.Stride
  83. srcX := dstH - dstY - 1
  84. src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
  85. }
  86. })
  87. return dst
  88. }
  89. // Rotate180 rotates the image 180 degrees counter-clockwise and returns the transformed image.
  90. func Rotate180(img image.Image) *image.NRGBA {
  91. src := newScanner(img)
  92. dstW := src.w
  93. dstH := src.h
  94. rowSize := dstW * 4
  95. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  96. parallel(0, dstH, func(ys <-chan int) {
  97. for dstY := range ys {
  98. i := dstY * dst.Stride
  99. srcY := dstH - dstY - 1
  100. src.scan(0, srcY, src.w, srcY+1, dst.Pix[i:i+rowSize])
  101. reverse(dst.Pix[i : i+rowSize])
  102. }
  103. })
  104. return dst
  105. }
  106. // Rotate270 rotates the image 270 degrees counter-clockwise and returns the transformed image.
  107. func Rotate270(img image.Image) *image.NRGBA {
  108. src := newScanner(img)
  109. dstW := src.h
  110. dstH := src.w
  111. rowSize := dstW * 4
  112. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  113. parallel(0, dstH, func(ys <-chan int) {
  114. for dstY := range ys {
  115. i := dstY * dst.Stride
  116. srcX := dstY
  117. src.scan(srcX, 0, srcX+1, src.h, dst.Pix[i:i+rowSize])
  118. reverse(dst.Pix[i : i+rowSize])
  119. }
  120. })
  121. return dst
  122. }
  123. // Rotate rotates an image by the given angle counter-clockwise .
  124. // The angle parameter is the rotation angle in degrees.
  125. // The bgColor parameter specifies the color of the uncovered zone after the rotation.
  126. func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA {
  127. angle = angle - math.Floor(angle/360)*360
  128. switch angle {
  129. case 0:
  130. return Clone(img)
  131. case 90:
  132. return Rotate90(img)
  133. case 180:
  134. return Rotate180(img)
  135. case 270:
  136. return Rotate270(img)
  137. }
  138. src := toNRGBA(img)
  139. srcW := src.Bounds().Max.X
  140. srcH := src.Bounds().Max.Y
  141. dstW, dstH := rotatedSize(srcW, srcH, angle)
  142. dst := image.NewNRGBA(image.Rect(0, 0, dstW, dstH))
  143. if dstW <= 0 || dstH <= 0 {
  144. return dst
  145. }
  146. srcXOff := float64(srcW)/2 - 0.5
  147. srcYOff := float64(srcH)/2 - 0.5
  148. dstXOff := float64(dstW)/2 - 0.5
  149. dstYOff := float64(dstH)/2 - 0.5
  150. bgColorNRGBA := color.NRGBAModel.Convert(bgColor).(color.NRGBA)
  151. sin, cos := math.Sincos(math.Pi * angle / 180)
  152. parallel(0, dstH, func(ys <-chan int) {
  153. for dstY := range ys {
  154. for dstX := 0; dstX < dstW; dstX++ {
  155. xf, yf := rotatePoint(float64(dstX)-dstXOff, float64(dstY)-dstYOff, sin, cos)
  156. xf, yf = xf+srcXOff, yf+srcYOff
  157. interpolatePoint(dst, dstX, dstY, src, xf, yf, bgColorNRGBA)
  158. }
  159. }
  160. })
  161. return dst
  162. }
  163. func rotatePoint(x, y, sin, cos float64) (float64, float64) {
  164. return x*cos - y*sin, x*sin + y*cos
  165. }
  166. func rotatedSize(w, h int, angle float64) (int, int) {
  167. if w <= 0 || h <= 0 {
  168. return 0, 0
  169. }
  170. sin, cos := math.Sincos(math.Pi * angle / 180)
  171. x1, y1 := rotatePoint(float64(w-1), 0, sin, cos)
  172. x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos)
  173. x3, y3 := rotatePoint(0, float64(h-1), sin, cos)
  174. minx := math.Min(x1, math.Min(x2, math.Min(x3, 0)))
  175. maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0)))
  176. miny := math.Min(y1, math.Min(y2, math.Min(y3, 0)))
  177. maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0)))
  178. neww := maxx - minx + 1
  179. if neww-math.Floor(neww) > 0.1 {
  180. neww++
  181. }
  182. newh := maxy - miny + 1
  183. if newh-math.Floor(newh) > 0.1 {
  184. newh++
  185. }
  186. return int(neww), int(newh)
  187. }
  188. func interpolatePoint(dst *image.NRGBA, dstX, dstY int, src *image.NRGBA, xf, yf float64, bgColor color.NRGBA) {
  189. j := dstY*dst.Stride + dstX*4
  190. d := dst.Pix[j : j+4 : j+4]
  191. x0 := int(math.Floor(xf))
  192. y0 := int(math.Floor(yf))
  193. bounds := src.Bounds()
  194. if !image.Pt(x0, y0).In(image.Rect(bounds.Min.X-1, bounds.Min.Y-1, bounds.Max.X, bounds.Max.Y)) {
  195. d[0] = bgColor.R
  196. d[1] = bgColor.G
  197. d[2] = bgColor.B
  198. d[3] = bgColor.A
  199. return
  200. }
  201. xq := xf - float64(x0)
  202. yq := yf - float64(y0)
  203. points := [4]image.Point{
  204. {x0, y0},
  205. {x0 + 1, y0},
  206. {x0, y0 + 1},
  207. {x0 + 1, y0 + 1},
  208. }
  209. weights := [4]float64{
  210. (1 - xq) * (1 - yq),
  211. xq * (1 - yq),
  212. (1 - xq) * yq,
  213. xq * yq,
  214. }
  215. var r, g, b, a float64
  216. for i := 0; i < 4; i++ {
  217. p := points[i]
  218. w := weights[i]
  219. if p.In(bounds) {
  220. i := p.Y*src.Stride + p.X*4
  221. s := src.Pix[i : i+4 : i+4]
  222. wa := float64(s[3]) * w
  223. r += float64(s[0]) * wa
  224. g += float64(s[1]) * wa
  225. b += float64(s[2]) * wa
  226. a += wa
  227. } else {
  228. wa := float64(bgColor.A) * w
  229. r += float64(bgColor.R) * wa
  230. g += float64(bgColor.G) * wa
  231. b += float64(bgColor.B) * wa
  232. a += wa
  233. }
  234. }
  235. if a != 0 {
  236. aInv := 1 / a
  237. d[0] = clamp(r * aInv)
  238. d[1] = clamp(g * aInv)
  239. d[2] = clamp(b * aInv)
  240. d[3] = clamp(a)
  241. }
  242. }