You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

converter.go 12KB

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