bilinear.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2012 The Graphics-Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package interp
  5. import (
  6. "image"
  7. "image/color"
  8. "math"
  9. )
  10. // Bilinear implements bilinear interpolation.
  11. var Bilinear Interp = bilinear{}
  12. type bilinear struct{}
  13. func (i bilinear) Interp(src image.Image, x, y float64) color.Color {
  14. if src, ok := src.(*image.RGBA); ok {
  15. return i.RGBA(src, x, y)
  16. }
  17. return bilinearGeneral(src, x, y)
  18. }
  19. func bilinearGeneral(src image.Image, x, y float64) color.Color {
  20. p := findLinearSrc(src.Bounds(), x, y)
  21. var fr, fg, fb, fa float64
  22. var r, g, b, a uint32
  23. r, g, b, a = src.At(p.low.X, p.low.Y).RGBA()
  24. fr += float64(r) * p.frac00
  25. fg += float64(g) * p.frac00
  26. fb += float64(b) * p.frac00
  27. fa += float64(a) * p.frac00
  28. r, g, b, a = src.At(p.high.X, p.low.Y).RGBA()
  29. fr += float64(r) * p.frac01
  30. fg += float64(g) * p.frac01
  31. fb += float64(b) * p.frac01
  32. fa += float64(a) * p.frac01
  33. r, g, b, a = src.At(p.low.X, p.high.Y).RGBA()
  34. fr += float64(r) * p.frac10
  35. fg += float64(g) * p.frac10
  36. fb += float64(b) * p.frac10
  37. fa += float64(a) * p.frac10
  38. r, g, b, a = src.At(p.high.X, p.high.Y).RGBA()
  39. fr += float64(r) * p.frac11
  40. fg += float64(g) * p.frac11
  41. fb += float64(b) * p.frac11
  42. fa += float64(a) * p.frac11
  43. var c color.RGBA64
  44. c.R = uint16(fr + 0.5)
  45. c.G = uint16(fg + 0.5)
  46. c.B = uint16(fb + 0.5)
  47. c.A = uint16(fa + 0.5)
  48. return c
  49. }
  50. func (bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
  51. p := findLinearSrc(src.Bounds(), x, y)
  52. // Array offsets for the surrounding pixels.
  53. off00 := offRGBA(src, p.low.X, p.low.Y)
  54. off01 := offRGBA(src, p.high.X, p.low.Y)
  55. off10 := offRGBA(src, p.low.X, p.high.Y)
  56. off11 := offRGBA(src, p.high.X, p.high.Y)
  57. var fr, fg, fb, fa float64
  58. fr += float64(src.Pix[off00+0]) * p.frac00
  59. fg += float64(src.Pix[off00+1]) * p.frac00
  60. fb += float64(src.Pix[off00+2]) * p.frac00
  61. fa += float64(src.Pix[off00+3]) * p.frac00
  62. fr += float64(src.Pix[off01+0]) * p.frac01
  63. fg += float64(src.Pix[off01+1]) * p.frac01
  64. fb += float64(src.Pix[off01+2]) * p.frac01
  65. fa += float64(src.Pix[off01+3]) * p.frac01
  66. fr += float64(src.Pix[off10+0]) * p.frac10
  67. fg += float64(src.Pix[off10+1]) * p.frac10
  68. fb += float64(src.Pix[off10+2]) * p.frac10
  69. fa += float64(src.Pix[off10+3]) * p.frac10
  70. fr += float64(src.Pix[off11+0]) * p.frac11
  71. fg += float64(src.Pix[off11+1]) * p.frac11
  72. fb += float64(src.Pix[off11+2]) * p.frac11
  73. fa += float64(src.Pix[off11+3]) * p.frac11
  74. var c color.RGBA
  75. c.R = uint8(fr + 0.5)
  76. c.G = uint8(fg + 0.5)
  77. c.B = uint8(fb + 0.5)
  78. c.A = uint8(fa + 0.5)
  79. return c
  80. }
  81. func (bilinear) Gray(src *image.Gray, x, y float64) color.Gray {
  82. p := findLinearSrc(src.Bounds(), x, y)
  83. // Array offsets for the surrounding pixels.
  84. off00 := offGray(src, p.low.X, p.low.Y)
  85. off01 := offGray(src, p.high.X, p.low.Y)
  86. off10 := offGray(src, p.low.X, p.high.Y)
  87. off11 := offGray(src, p.high.X, p.high.Y)
  88. var fc float64
  89. fc += float64(src.Pix[off00]) * p.frac00
  90. fc += float64(src.Pix[off01]) * p.frac01
  91. fc += float64(src.Pix[off10]) * p.frac10
  92. fc += float64(src.Pix[off11]) * p.frac11
  93. var c color.Gray
  94. c.Y = uint8(fc + 0.5)
  95. return c
  96. }
  97. type bilinearSrc struct {
  98. // Top-left and bottom-right interpolation sources
  99. low, high image.Point
  100. // Fraction of each pixel to take. The 0 suffix indicates
  101. // top/left, and the 1 suffix indicates bottom/right.
  102. frac00, frac01, frac10, frac11 float64
  103. }
  104. func findLinearSrc(b image.Rectangle, sx, sy float64) bilinearSrc {
  105. maxX := float64(b.Max.X)
  106. maxY := float64(b.Max.Y)
  107. minX := float64(b.Min.X)
  108. minY := float64(b.Min.Y)
  109. lowX := math.Floor(sx - 0.5)
  110. lowY := math.Floor(sy - 0.5)
  111. if lowX < minX {
  112. lowX = minX
  113. }
  114. if lowY < minY {
  115. lowY = minY
  116. }
  117. highX := math.Ceil(sx - 0.5)
  118. highY := math.Ceil(sy - 0.5)
  119. if highX >= maxX {
  120. highX = maxX - 1
  121. }
  122. if highY >= maxY {
  123. highY = maxY - 1
  124. }
  125. // In the variables below, the 0 suffix indicates top/left, and the
  126. // 1 suffix indicates bottom/right.
  127. // Center of each surrounding pixel.
  128. x00 := lowX + 0.5
  129. y00 := lowY + 0.5
  130. x01 := highX + 0.5
  131. y01 := lowY + 0.5
  132. x10 := lowX + 0.5
  133. y10 := highY + 0.5
  134. x11 := highX + 0.5
  135. y11 := highY + 0.5
  136. p := bilinearSrc{
  137. low: image.Pt(int(lowX), int(lowY)),
  138. high: image.Pt(int(highX), int(highY)),
  139. }
  140. // Literally, edge cases. If we are close enough to the edge of
  141. // the image, curtail the interpolation sources.
  142. if lowX == highX && lowY == highY {
  143. p.frac00 = 1.0
  144. } else if sy-minY <= 0.5 && sx-minX <= 0.5 {
  145. p.frac00 = 1.0
  146. } else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
  147. p.frac11 = 1.0
  148. } else if sy-minY <= 0.5 || lowY == highY {
  149. p.frac00 = x01 - sx
  150. p.frac01 = sx - x00
  151. } else if sx-minX <= 0.5 || lowX == highX {
  152. p.frac00 = y10 - sy
  153. p.frac10 = sy - y00
  154. } else if maxY-sy <= 0.5 {
  155. p.frac10 = x11 - sx
  156. p.frac11 = sx - x10
  157. } else if maxX-sx <= 0.5 {
  158. p.frac01 = y11 - sy
  159. p.frac11 = sy - y01
  160. } else {
  161. p.frac00 = (x01 - sx) * (y10 - sy)
  162. p.frac01 = (sx - x00) * (y11 - sy)
  163. p.frac10 = (x11 - sx) * (sy - y00)
  164. p.frac11 = (sx - x10) * (sy - y01)
  165. }
  166. return p
  167. }
  168. // TODO(crawshaw): When we have inlining, consider func (p *RGBA) Off(x, y) int
  169. func offRGBA(src *image.RGBA, x, y int) int {
  170. return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
  171. }
  172. func offGray(src *image.Gray, x, y int) int {
  173. return (y-src.Rect.Min.Y)*src.Stride + (x - src.Rect.Min.X)
  174. }