converter.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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.NRGBA64, 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.NRGBA, 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 resizeNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []int16, 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]int32
  126. var sum int32
  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 *= 4
  136. case xi >= maxX:
  137. xi = 4 * maxX
  138. default:
  139. xi = 0
  140. }
  141. rgba[0] += int32(coeff) * int32(row[xi+0])
  142. rgba[1] += int32(coeff) * int32(row[xi+1])
  143. rgba[2] += int32(coeff) * int32(row[xi+2])
  144. rgba[3] += int32(coeff) * int32(row[xi+3])
  145. sum += int32(coeff)
  146. }
  147. }
  148. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
  149. out.Pix[xo+0] = clampUint8(rgba[0] / sum)
  150. out.Pix[xo+1] = clampUint8(rgba[1] / sum)
  151. out.Pix[xo+2] = clampUint8(rgba[2] / sum)
  152. out.Pix[xo+3] = clampUint8(rgba[3] / sum)
  153. }
  154. }
  155. }
  156. func resizeRGBA64(in *image.RGBA64, out *image.NRGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
  157. newBounds := out.Bounds()
  158. maxX := in.Bounds().Dx() - 1
  159. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  160. row := in.Pix[x*in.Stride:]
  161. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  162. var rgba [4]int64
  163. var sum int64
  164. start := offset[y]
  165. ci := y * filterLength
  166. for i := 0; i < filterLength; i++ {
  167. coeff := coeffs[ci+i]
  168. if coeff != 0 {
  169. xi := start + i
  170. switch {
  171. case uint(xi) < uint(maxX):
  172. xi *= 8
  173. case xi >= maxX:
  174. xi = 8 * maxX
  175. default:
  176. xi = 0
  177. }
  178. rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
  179. rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
  180. rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
  181. rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(row[xi+7]))
  182. sum += int64(coeff)
  183. }
  184. }
  185. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
  186. value := clampUint16(rgba[0] / sum)
  187. out.Pix[xo+0] = uint8(value >> 8)
  188. out.Pix[xo+1] = uint8(value)
  189. value = clampUint16(rgba[1] / sum)
  190. out.Pix[xo+2] = uint8(value >> 8)
  191. out.Pix[xo+3] = uint8(value)
  192. value = clampUint16(rgba[2] / sum)
  193. out.Pix[xo+4] = uint8(value >> 8)
  194. out.Pix[xo+5] = uint8(value)
  195. value = clampUint16(rgba[3] / sum)
  196. out.Pix[xo+6] = uint8(value >> 8)
  197. out.Pix[xo+7] = uint8(value)
  198. }
  199. }
  200. }
  201. func resizeNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []int32, offset []int, filterLength int) {
  202. newBounds := out.Bounds()
  203. maxX := in.Bounds().Dx() - 1
  204. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  205. row := in.Pix[x*in.Stride:]
  206. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  207. var rgba [4]int64
  208. var sum int64
  209. start := offset[y]
  210. ci := y * filterLength
  211. for i := 0; i < filterLength; i++ {
  212. coeff := coeffs[ci+i]
  213. if coeff != 0 {
  214. xi := start + i
  215. switch {
  216. case uint(xi) < uint(maxX):
  217. xi *= 8
  218. case xi >= maxX:
  219. xi = 8 * maxX
  220. default:
  221. xi = 0
  222. }
  223. rgba[0] += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
  224. rgba[1] += int64(coeff) * int64(uint16(row[xi+2])<<8|uint16(row[xi+3]))
  225. rgba[2] += int64(coeff) * int64(uint16(row[xi+4])<<8|uint16(row[xi+5]))
  226. rgba[3] += int64(coeff) * int64(uint16(row[xi+6])<<8|uint16(row[xi+7]))
  227. sum += int64(coeff)
  228. }
  229. }
  230. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
  231. value := clampUint16(rgba[0] / sum)
  232. out.Pix[xo+0] = uint8(value >> 8)
  233. out.Pix[xo+1] = uint8(value)
  234. value = clampUint16(rgba[1] / sum)
  235. out.Pix[xo+2] = uint8(value >> 8)
  236. out.Pix[xo+3] = uint8(value)
  237. value = clampUint16(rgba[2] / sum)
  238. out.Pix[xo+4] = uint8(value >> 8)
  239. out.Pix[xo+5] = uint8(value)
  240. value = clampUint16(rgba[3] / sum)
  241. out.Pix[xo+6] = uint8(value >> 8)
  242. out.Pix[xo+7] = uint8(value)
  243. }
  244. }
  245. }
  246. func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) {
  247. newBounds := out.Bounds()
  248. maxX := in.Bounds().Dx() - 1
  249. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  250. row := in.Pix[(x-newBounds.Min.X)*in.Stride:]
  251. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  252. var gray int32
  253. var sum int32
  254. start := offset[y]
  255. ci := y * filterLength
  256. for i := 0; i < filterLength; i++ {
  257. coeff := coeffs[ci+i]
  258. if coeff != 0 {
  259. xi := start + i
  260. switch {
  261. case xi < 0:
  262. xi = 0
  263. case xi >= maxX:
  264. xi = maxX
  265. }
  266. gray += int32(coeff) * int32(row[xi])
  267. sum += int32(coeff)
  268. }
  269. }
  270. offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
  271. out.Pix[offset] = clampUint8(gray / sum)
  272. }
  273. }
  274. }
  275. func resizeGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []int32, offset []int, filterLength int) {
  276. newBounds := out.Bounds()
  277. maxX := in.Bounds().Dx() - 1
  278. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  279. row := in.Pix[x*in.Stride:]
  280. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  281. var gray int64
  282. var sum int64
  283. start := offset[y]
  284. ci := y * filterLength
  285. for i := 0; i < filterLength; i++ {
  286. coeff := coeffs[ci+i]
  287. if coeff != 0 {
  288. xi := start + i
  289. switch {
  290. case uint(xi) < uint(maxX):
  291. xi *= 2
  292. case xi >= maxX:
  293. xi = 2 * maxX
  294. default:
  295. xi = 0
  296. }
  297. gray += int64(coeff) * int64(uint16(row[xi+0])<<8|uint16(row[xi+1]))
  298. sum += int64(coeff)
  299. }
  300. }
  301. offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
  302. value := clampUint16(gray / sum)
  303. out.Pix[offset+0] = uint8(value >> 8)
  304. out.Pix[offset+1] = uint8(value)
  305. }
  306. }
  307. }
  308. func resizeYCbCr(in *ycc, out *ycc, scale float64, coeffs []int16, offset []int, filterLength int) {
  309. newBounds := out.Bounds()
  310. maxX := in.Bounds().Dx() - 1
  311. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  312. row := in.Pix[x*in.Stride:]
  313. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  314. var p [3]int32
  315. var sum int32
  316. start := offset[y]
  317. ci := y * filterLength
  318. for i := 0; i < filterLength; i++ {
  319. coeff := coeffs[ci+i]
  320. if coeff != 0 {
  321. xi := start + i
  322. switch {
  323. case uint(xi) < uint(maxX):
  324. xi *= 3
  325. case xi >= maxX:
  326. xi = 3 * maxX
  327. default:
  328. xi = 0
  329. }
  330. p[0] += int32(coeff) * int32(row[xi+0])
  331. p[1] += int32(coeff) * int32(row[xi+1])
  332. p[2] += int32(coeff) * int32(row[xi+2])
  333. sum += int32(coeff)
  334. }
  335. }
  336. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
  337. out.Pix[xo+0] = clampUint8(p[0] / sum)
  338. out.Pix[xo+1] = clampUint8(p[1] / sum)
  339. out.Pix[xo+2] = clampUint8(p[2] / sum)
  340. }
  341. }
  342. }
  343. func nearestYCbCr(in *ycc, out *ycc, scale float64, coeffs []bool, offset []int, filterLength int) {
  344. newBounds := out.Bounds()
  345. maxX := in.Bounds().Dx() - 1
  346. for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
  347. row := in.Pix[x*in.Stride:]
  348. for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
  349. var p [3]float32
  350. var sum float32
  351. start := offset[y]
  352. ci := y * filterLength
  353. for i := 0; i < filterLength; i++ {
  354. if coeffs[ci+i] {
  355. xi := start + i
  356. switch {
  357. case uint(xi) < uint(maxX):
  358. xi *= 3
  359. case xi >= maxX:
  360. xi = 3 * maxX
  361. default:
  362. xi = 0
  363. }
  364. p[0] += float32(row[xi+0])
  365. p[1] += float32(row[xi+1])
  366. p[2] += float32(row[xi+2])
  367. sum++
  368. }
  369. }
  370. xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*3
  371. out.Pix[xo+0] = floatToUint8(p[0] / sum)
  372. out.Pix[xo+1] = floatToUint8(p[1] / sum)
  373. out.Pix[xo+2] = floatToUint8(p[2] / sum)
  374. }
  375. }
  376. }