converter.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
  3. Permission to use, copy, modify, and/or distribute this software for any purpose
  4. with or without fee is hereby granted, provided that the above copyright notice
  5. and this permission notice appear in all copies.
  6. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  7. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  8. FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  9. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  10. OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  11. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  12. THIS SOFTWARE.
  13. */
  14. package resize
  15. import "image"
  16. // Keep value in [0,255] range.
  17. func clampUint8(in int32) uint8 {
  18. // casting a negative int to an uint will result in an overflown
  19. // large uint. this behavior will be exploited here and in other functions
  20. // to achieve a higher performance.
  21. if uint32(in) < 256 {
  22. return uint8(in)
  23. }
  24. if in > 255 {
  25. return 255
  26. }
  27. return 0
  28. }
  29. // Keep value in [0,65535] range.
  30. func clampUint16(in int64) uint16 {
  31. if uint64(in) < 65536 {
  32. return uint16(in)
  33. }
  34. if in > 65535 {
  35. return 65535
  36. }
  37. return 0
  38. }
  39. func resizeGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
  40. newBounds := out.Bounds()
  41. maxX := in.Bounds().Dx() - 1
  42. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  43. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  44. var rgba [4]int64
  45. var sum int64
  46. start := offset[y]
  47. ci := y * filterLength
  48. for i := 0; i < filterLength; i++ {
  49. coeff := coeffs[ci+i]
  50. if coeff != 0 {
  51. xi := start + i
  52. switch {
  53. case xi < 0:
  54. xi = 0
  55. case xi >= maxX:
  56. xi = maxX
  57. }
  58. r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
  59. rgba[0] += int64(coeff) * int64(r)
  60. rgba[1] += int64(coeff) * int64(g)
  61. rgba[2] += int64(coeff) * int64(b)
  62. rgba[3] += int64(coeff) * int64(a)
  63. sum += int64(coeff)
  64. }
  65. }
  66. offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
  67. value := clampUint16(rgba[0] / sum)
  68. out.Pix[offset+0] = uint8(value >> 8)
  69. out.Pix[offset+1] = uint8(value)
  70. value = clampUint16(rgba[1] / sum)
  71. out.Pix[offset+2] = uint8(value >> 8)
  72. out.Pix[offset+3] = uint8(value)
  73. value = clampUint16(rgba[2] / sum)
  74. out.Pix[offset+4] = uint8(value >> 8)
  75. out.Pix[offset+5] = uint8(value)
  76. value = clampUint16(rgba[3] / sum)
  77. out.Pix[offset+6] = uint8(value >> 8)
  78. out.Pix[offset+7] = uint8(value)
  79. }
  80. }
  81. }
  82. func resizeRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []int16, offset []int, filterLength int) {
  83. newBounds := out.Bounds()
  84. maxX := in.Bounds().Dx() - 1
  85. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  86. row := in.Pix[x*in.Stride:]
  87. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  88. var rgba [4]int32
  89. var sum int32
  90. start := offset[y]
  91. ci := y * filterLength
  92. for i := 0; i < filterLength; i++ {
  93. coeff := coeffs[ci+i]
  94. if coeff != 0 {
  95. xi := start + i
  96. switch {
  97. case uint(xi) < uint(maxX):
  98. xi *= 4
  99. case xi >= maxX:
  100. xi = 4 * maxX
  101. default:
  102. xi = 0
  103. }
  104. rgba[0] += int32(coeff) * int32(row[xi+0])
  105. rgba[1] += int32(coeff) * int32(row[xi+1])
  106. rgba[2] += int32(coeff) * int32(row[xi+2])
  107. rgba[3] += int32(coeff) * int32(row[xi+3])
  108. sum += int32(coeff)
  109. }
  110. }
  111. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
  112. out.Pix[xo+0] = clampUint8(rgba[0] / sum)
  113. out.Pix[xo+1] = clampUint8(rgba[1] / sum)
  114. out.Pix[xo+2] = clampUint8(rgba[2] / sum)
  115. out.Pix[xo+3] = clampUint8(rgba[3] / sum)
  116. }
  117. }
  118. }
  119. func resizeRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
  120. newBounds := out.Bounds()
  121. maxX := in.Bounds().Dx() - 1
  122. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  123. row := in.Pix[x*in.Stride:]
  124. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  125. var rgba [4]int64
  126. var sum int64
  127. start := offset[y]
  128. ci := y * filterLength
  129. for i := 0; i < filterLength; i++ {
  130. coeff := coeffs[ci+i]
  131. if coeff != 0 {
  132. xi := start + i
  133. switch {
  134. case uint(xi) < uint(maxX):
  135. xi *= 8
  136. case xi >= maxX:
  137. xi = 8 * maxX
  138. default:
  139. xi = 0
  140. }
  141. rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
  142. rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
  143. rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
  144. rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(row[xi+7]))
  145. sum += int64(coeff)
  146. }
  147. }
  148. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
  149. value := clampUint16(rgba[0] / sum)
  150. out.Pix[xo+0] = uint8(value >> 8)
  151. out.Pix[xo+1] = uint8(value)
  152. value = clampUint16(rgba[1] / sum)
  153. out.Pix[xo+2] = uint8(value >> 8)
  154. out.Pix[xo+3] = uint8(value)
  155. value = clampUint16(rgba[2] / sum)
  156. out.Pix[xo+4] = uint8(value >> 8)
  157. out.Pix[xo+5] = uint8(value)
  158. value = clampUint16(rgba[3] / sum)
  159. out.Pix[xo+6] = uint8(value >> 8)
  160. out.Pix[xo+7] = uint8(value)
  161. }
  162. }
  163. }
  164. func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) {
  165. newBounds := out.Bounds()
  166. maxX := in.Bounds().Dx() - 1
  167. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  168. row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
  169. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  170. var gray int32
  171. var sum int32
  172. start := offset[y]
  173. ci := y * filterLength
  174. for i := 0; i < filterLength; i++ {
  175. coeff := coeffs[ci+i]
  176. if coeff != 0 {
  177. xi := start + i
  178. switch {
  179. case xi < 0:
  180. xi = 0
  181. case xi >= maxX:
  182. xi = maxX
  183. }
  184. gray += int32(coeff) * int32(row[xi])
  185. sum += int32(coeff)
  186. }
  187. }
  188. offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
  189. out.Pix[offset] = clampUint8(gray / sum)
  190. }
  191. }
  192. }
  193. func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) {
  194. newBounds := out.Bounds()
  195. maxX := in.Bounds().Dx() - 1
  196. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  197. row := in.Pix[x*in.Stride:]
  198. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  199. var gray int64
  200. var sum int64
  201. start := offset[y]
  202. ci := y * filterLength
  203. for i := 0; i < filterLength; i++ {
  204. coeff := coeffs[ci+i]
  205. if coeff != 0 {
  206. xi := start + i
  207. switch {
  208. case uint(xi) < uint(maxX):
  209. xi *= 2
  210. case xi >= maxX:
  211. xi = 2 * maxX
  212. default:
  213. xi = 0
  214. }
  215. gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
  216. sum += int64(coeff)
  217. }
  218. }
  219. offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
  220. value := clampUint16(gray / sum)
  221. out.Pix[offset+0] = uint8(value >> 8)
  222. out.Pix[offset+1] = uint8(value)
  223. }
  224. }
  225. }
  226. func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) {
  227. newBounds := out.Bounds()
  228. maxX := in.Bounds().Dx() - 1
  229. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  230. row := in.Pix[x*in.Stride:]
  231. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  232. var p [3]int32
  233. var sum int32
  234. start := offset[y]
  235. ci := y * filterLength
  236. for i := 0; i < filterLength; i++ {
  237. coeff := coeffs[ci+i]
  238. if coeff != 0 {
  239. xi := start + i
  240. switch {
  241. case uint(xi) < uint(maxX):
  242. xi *= 3
  243. case xi >= maxX:
  244. xi = 3 * maxX
  245. default:
  246. xi = 0
  247. }
  248. p[0] += int32(coeff) * int32(row[xi+0])
  249. p[1] += int32(coeff) * int32(row[xi+1])
  250. p[2] += int32(coeff) * int32(row[xi+2])
  251. sum += int32(coeff)
  252. }
  253. }
  254. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
  255. out.Pix[xo+0] = clampUint8(p[0] / sum)
  256. out.Pix[xo+1] = clampUint8(p[1] / sum)
  257. out.Pix[xo+2] = clampUint8(p[2] / sum)
  258. }
  259. }
  260. }
  261. func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) {
  262. newBounds := out.Bounds()
  263. maxX := in.Bounds().Dx() - 1
  264. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  265. row := in.Pix[x*in.Stride:]
  266. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  267. var p [3]float32
  268. var sum float32
  269. start := offset[y]
  270. ci := y * filterLength
  271. for i := 0; i < filterLength; i++ {
  272. if coeffs[ci+i] {
  273. xi := start + i
  274. switch {
  275. case uint(xi) < uint(maxX):
  276. xi *= 3
  277. case xi >= maxX:
  278. xi = 3 * maxX
  279. default:
  280. xi = 0
  281. }
  282. p[0] += float32(row[xi+0])
  283. p[1] += float32(row[xi+1])
  284. p[2] += float32(row[xi+2])
  285. sum++
  286. }
  287. }
  288. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
  289. out.Pix[xo+0] = floatToUint8(p[0] / sum)
  290. out.Pix[xo+1] = floatToUint8(p[1] / sum)
  291. out.Pix[xo+2] = floatToUint8(p[2] / sum)
  292. }
  293. }
  294. }