shear.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package transform
  2. import (
  3. "image"
  4. "math"
  5. "github.com/anthonynsimon/bild/clone"
  6. "github.com/anthonynsimon/bild/parallel"
  7. )
  8. // ShearH applies a shear linear transformation along the horizontal axis,
  9. // the parameter angle is the shear angle to be applied.
  10. // The transformation will be applied with the center of the image as the pivot.
  11. func ShearH(img image.Image, angle float64) *image.RGBA {
  12. src := clone.AsShallowRGBA(img)
  13. srcW, srcH := src.Bounds().Dx(), src.Bounds().Dy()
  14. // Supersample, currently hard set to 2x
  15. srcW, srcH = srcW*2, srcH*2
  16. src = Resize(src, srcW, srcH, NearestNeighbor)
  17. // Calculate shear factor
  18. kx := math.Tan(angle * (math.Pi / 180))
  19. dstW, dstH := srcW+int(float64(srcH)*math.Abs(kx)), srcH
  20. dst := image.NewRGBA(image.Rect(0, 0, dstW, dstH))
  21. pivotX := float64(dstW) / 2
  22. pivotY := float64(dstH) / 2
  23. // Calculate offset since we are resizing the bounds to
  24. // fit the sheared image.
  25. dx := (dstW - srcW) / 2
  26. dy := (dstH - srcH) / 2
  27. parallel.Line(dstH, func(start, end int) {
  28. for y := start; y < end; y++ {
  29. for x := 0; x < dstW; x++ {
  30. // Move positions to revolve around pivot
  31. ix := x - int(pivotX) - dx
  32. iy := y - int(pivotY) - dy
  33. // Apply linear transformation
  34. ix = ix + int(float64(iy)*kx)
  35. // Move positions back to image coordinates
  36. ix += int(pivotX)
  37. iy += int(pivotY)
  38. if ix < 0 || ix >= srcW || iy < 0 || iy >= srcH {
  39. continue
  40. }
  41. srcPos := iy*src.Stride + ix*4
  42. dstPos := y*dst.Stride + x*4
  43. dst.Pix[dstPos+0] = src.Pix[srcPos+0]
  44. dst.Pix[dstPos+1] = src.Pix[srcPos+1]
  45. dst.Pix[dstPos+2] = src.Pix[srcPos+2]
  46. dst.Pix[dstPos+3] = src.Pix[srcPos+3]
  47. }
  48. }
  49. })
  50. // Downsample to original bounds as part of the Supersampling
  51. dst = Resize(dst, dstW/2, dstH/2, Linear)
  52. return dst
  53. }
  54. // ShearV applies a shear linear transformation along the vertical axis,
  55. // the parameter angle is the shear angle to be applied.
  56. // The transformation will be applied with the center of the image as the pivot.
  57. func ShearV(img image.Image, angle float64) *image.RGBA {
  58. src := clone.AsRGBA(img)
  59. srcW, srcH := src.Bounds().Dx(), src.Bounds().Dy()
  60. // Supersample, currently hard set to 2x
  61. srcW, srcH = srcW*2, srcH*2
  62. src = Resize(src, srcW, srcH, NearestNeighbor)
  63. // Calculate shear factor
  64. ky := math.Tan(angle * (math.Pi / 180))
  65. dstW, dstH := srcW, srcH+int(float64(srcW)*math.Abs(ky))
  66. dst := image.NewRGBA(image.Rect(0, 0, dstW, dstH))
  67. pivotX := float64(dstW) / 2
  68. pivotY := float64(dstH) / 2
  69. // Calculate offset since we are resizing the bounds to
  70. // fit the sheared image.
  71. dx := (dstW - srcW) / 2
  72. dy := (dstH - srcH) / 2
  73. parallel.Line(dstH, func(start, end int) {
  74. for y := start; y < end; y++ {
  75. for x := 0; x < dstW; x++ {
  76. // Move positions to revolve around pivot
  77. ix := x - int(pivotX) - dx
  78. iy := y - int(pivotY) - dy
  79. // Apply linear transformation
  80. iy = iy + int(float64(ix)*ky)
  81. // Move positions back to image coordinates
  82. ix += int(pivotX)
  83. iy += int(pivotY)
  84. if ix < 0 || ix >= srcW || iy < 0 || iy >= srcH {
  85. continue
  86. }
  87. srcPos := iy*src.Stride + ix*4
  88. dstPos := y*dst.Stride + x*4
  89. dst.Pix[dstPos+0] = src.Pix[srcPos+0]
  90. dst.Pix[dstPos+1] = src.Pix[srcPos+1]
  91. dst.Pix[dstPos+2] = src.Pix[srcPos+2]
  92. dst.Pix[dstPos+3] = src.Pix[srcPos+3]
  93. }
  94. }
  95. })
  96. // Downsample to original bounds as part of the Supersampling
  97. dst = Resize(dst, dstW/2, dstH/2, Linear)
  98. return dst
  99. }