ycc.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. Copyright (c) 2014, Charlie Vieth <charlie.vieth@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 (
  16. "image"
  17. "image/color"
  18. )
  19. // ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a
  20. // single slice to increase resizing performance.
  21. type ycc struct {
  22. // Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at
  23. // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
  24. Pix []uint8
  25. // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
  26. Stride int
  27. // Rect is the image's bounds.
  28. Rect image.Rectangle
  29. // SubsampleRatio is the subsample ratio of the original YCbCr image.
  30. SubsampleRatio image.YCbCrSubsampleRatio
  31. }
  32. // PixOffset returns the index of the first element of Pix that corresponds to
  33. // the pixel at (x, y).
  34. func (p *ycc) PixOffset(x, y int) int {
  35. return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3
  36. }
  37. func (p *ycc) Bounds() image.Rectangle {
  38. return p.Rect
  39. }
  40. func (p *ycc) ColorModel() color.Model {
  41. return color.YCbCrModel
  42. }
  43. func (p *ycc) At(x, y int) color.Color {
  44. if !(image.Point{x, y}.In(p.Rect)) {
  45. return color.YCbCr{}
  46. }
  47. i := p.PixOffset(x, y)
  48. return color.YCbCr{
  49. p.Pix[i+0],
  50. p.Pix[i+1],
  51. p.Pix[i+2],
  52. }
  53. }
  54. func (p *ycc) Opaque() bool {
  55. return true
  56. }
  57. // SubImage returns an image representing the portion of the image p visible
  58. // through r. The returned value shares pixels with the original image.
  59. func (p *ycc) SubImage(r image.Rectangle) image.Image {
  60. r = r.Intersect(p.Rect)
  61. if r.Empty() {
  62. return &ycc{SubsampleRatio: p.SubsampleRatio}
  63. }
  64. i := p.PixOffset(r.Min.X, r.Min.Y)
  65. return &ycc{
  66. Pix: p.Pix[i:],
  67. Stride: p.Stride,
  68. Rect: r,
  69. SubsampleRatio: p.SubsampleRatio,
  70. }
  71. }
  72. // newYCC returns a new ycc with the given bounds and subsample ratio.
  73. func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc {
  74. w, h := r.Dx(), r.Dy()
  75. buf := make([]uint8, 3*w*h)
  76. return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s}
  77. }
  78. // Copy of image.YCbCrSubsampleRatio constants - this allows us to support
  79. // older versions of Go where these constants are not defined (i.e. Go 1.4)
  80. const (
  81. ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota
  82. ycbcrSubsampleRatio422
  83. ycbcrSubsampleRatio420
  84. ycbcrSubsampleRatio440
  85. ycbcrSubsampleRatio411
  86. ycbcrSubsampleRatio410
  87. )
  88. // YCbCr converts ycc to a YCbCr image with the same subsample ratio
  89. // as the YCbCr image that ycc was generated from.
  90. func (p *ycc) YCbCr() *image.YCbCr {
  91. ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
  92. switch ycbcr.SubsampleRatio {
  93. case ycbcrSubsampleRatio422:
  94. return p.ycbcr422(ycbcr)
  95. case ycbcrSubsampleRatio420:
  96. return p.ycbcr420(ycbcr)
  97. case ycbcrSubsampleRatio440:
  98. return p.ycbcr440(ycbcr)
  99. case ycbcrSubsampleRatio444:
  100. return p.ycbcr444(ycbcr)
  101. case ycbcrSubsampleRatio411:
  102. return p.ycbcr411(ycbcr)
  103. case ycbcrSubsampleRatio410:
  104. return p.ycbcr410(ycbcr)
  105. }
  106. return ycbcr
  107. }
  108. // imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
  109. func imageYCbCrToYCC(in *image.YCbCr) *ycc {
  110. w, h := in.Rect.Dx(), in.Rect.Dy()
  111. p := ycc{
  112. Pix: make([]uint8, 3*w*h),
  113. Stride: 3 * w,
  114. Rect: image.Rect(0, 0, w, h),
  115. SubsampleRatio: in.SubsampleRatio,
  116. }
  117. switch in.SubsampleRatio {
  118. case ycbcrSubsampleRatio422:
  119. return convertToYCC422(in, &p)
  120. case ycbcrSubsampleRatio420:
  121. return convertToYCC420(in, &p)
  122. case ycbcrSubsampleRatio440:
  123. return convertToYCC440(in, &p)
  124. case ycbcrSubsampleRatio444:
  125. return convertToYCC444(in, &p)
  126. case ycbcrSubsampleRatio411:
  127. return convertToYCC411(in, &p)
  128. case ycbcrSubsampleRatio410:
  129. return convertToYCC410(in, &p)
  130. }
  131. return &p
  132. }
  133. func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr {
  134. var off int
  135. Pix := p.Pix
  136. Y := ycbcr.Y
  137. Cb := ycbcr.Cb
  138. Cr := ycbcr.Cr
  139. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  140. yy := y * ycbcr.YStride
  141. cy := y * ycbcr.CStride
  142. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  143. ci := cy + x/2
  144. Y[yy+x] = Pix[off+0]
  145. Cb[ci] = Pix[off+1]
  146. Cr[ci] = Pix[off+2]
  147. off += 3
  148. }
  149. }
  150. return ycbcr
  151. }
  152. func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr {
  153. var off int
  154. Pix := p.Pix
  155. Y := ycbcr.Y
  156. Cb := ycbcr.Cb
  157. Cr := ycbcr.Cr
  158. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  159. yy := y * ycbcr.YStride
  160. cy := (y / 2) * ycbcr.CStride
  161. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  162. ci := cy + x/2
  163. Y[yy+x] = Pix[off+0]
  164. Cb[ci] = Pix[off+1]
  165. Cr[ci] = Pix[off+2]
  166. off += 3
  167. }
  168. }
  169. return ycbcr
  170. }
  171. func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr {
  172. var off int
  173. Pix := p.Pix
  174. Y := ycbcr.Y
  175. Cb := ycbcr.Cb
  176. Cr := ycbcr.Cr
  177. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  178. yy := y * ycbcr.YStride
  179. cy := (y / 2) * ycbcr.CStride
  180. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  181. ci := cy + x
  182. Y[yy+x] = Pix[off+0]
  183. Cb[ci] = Pix[off+1]
  184. Cr[ci] = Pix[off+2]
  185. off += 3
  186. }
  187. }
  188. return ycbcr
  189. }
  190. func (p *ycc) ycbcr444(ycbcr *image.YCbCr) *image.YCbCr {
  191. var off int
  192. Pix := p.Pix
  193. Y := ycbcr.Y
  194. Cb := ycbcr.Cb
  195. Cr := ycbcr.Cr
  196. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  197. yy := y * ycbcr.YStride
  198. cy := y * ycbcr.CStride
  199. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  200. ci := cy + x
  201. Y[yy+x] = Pix[off+0]
  202. Cb[ci] = Pix[off+1]
  203. Cr[ci] = Pix[off+2]
  204. off += 3
  205. }
  206. }
  207. return ycbcr
  208. }
  209. func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr {
  210. var off int
  211. Pix := p.Pix
  212. Y := ycbcr.Y
  213. Cb := ycbcr.Cb
  214. Cr := ycbcr.Cr
  215. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  216. yy := y * ycbcr.YStride
  217. cy := y * ycbcr.CStride
  218. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  219. ci := cy + x/4
  220. Y[yy+x] = Pix[off+0]
  221. Cb[ci] = Pix[off+1]
  222. Cr[ci] = Pix[off+2]
  223. off += 3
  224. }
  225. }
  226. return ycbcr
  227. }
  228. func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr {
  229. var off int
  230. Pix := p.Pix
  231. Y := ycbcr.Y
  232. Cb := ycbcr.Cb
  233. Cr := ycbcr.Cr
  234. for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ {
  235. yy := y * ycbcr.YStride
  236. cy := (y / 2) * ycbcr.CStride
  237. for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ {
  238. ci := cy + x/4
  239. Y[yy+x] = Pix[off+0]
  240. Cb[ci] = Pix[off+1]
  241. Cr[ci] = Pix[off+2]
  242. off += 3
  243. }
  244. }
  245. return ycbcr
  246. }
  247. func convertToYCC422(in *image.YCbCr, p *ycc) *ycc {
  248. var off int
  249. Pix := p.Pix
  250. Y := in.Y
  251. Cb := in.Cb
  252. Cr := in.Cr
  253. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  254. yy := y * in.YStride
  255. cy := y * in.CStride
  256. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  257. ci := cy + x/2
  258. Pix[off+0] = Y[yy+x]
  259. Pix[off+1] = Cb[ci]
  260. Pix[off+2] = Cr[ci]
  261. off += 3
  262. }
  263. }
  264. return p
  265. }
  266. func convertToYCC420(in *image.YCbCr, p *ycc) *ycc {
  267. var off int
  268. Pix := p.Pix
  269. Y := in.Y
  270. Cb := in.Cb
  271. Cr := in.Cr
  272. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  273. yy := y * in.YStride
  274. cy := (y / 2) * in.CStride
  275. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  276. ci := cy + x/2
  277. Pix[off+0] = Y[yy+x]
  278. Pix[off+1] = Cb[ci]
  279. Pix[off+2] = Cr[ci]
  280. off += 3
  281. }
  282. }
  283. return p
  284. }
  285. func convertToYCC440(in *image.YCbCr, p *ycc) *ycc {
  286. var off int
  287. Pix := p.Pix
  288. Y := in.Y
  289. Cb := in.Cb
  290. Cr := in.Cr
  291. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  292. yy := y * in.YStride
  293. cy := (y / 2) * in.CStride
  294. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  295. ci := cy + x
  296. Pix[off+0] = Y[yy+x]
  297. Pix[off+1] = Cb[ci]
  298. Pix[off+2] = Cr[ci]
  299. off += 3
  300. }
  301. }
  302. return p
  303. }
  304. func convertToYCC444(in *image.YCbCr, p *ycc) *ycc {
  305. var off int
  306. Pix := p.Pix
  307. Y := in.Y
  308. Cb := in.Cb
  309. Cr := in.Cr
  310. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  311. yy := y * in.YStride
  312. cy := y * in.CStride
  313. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  314. ci := cy + x
  315. Pix[off+0] = Y[yy+x]
  316. Pix[off+1] = Cb[ci]
  317. Pix[off+2] = Cr[ci]
  318. off += 3
  319. }
  320. }
  321. return p
  322. }
  323. func convertToYCC411(in *image.YCbCr, p *ycc) *ycc {
  324. var off int
  325. Pix := p.Pix
  326. Y := in.Y
  327. Cb := in.Cb
  328. Cr := in.Cr
  329. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  330. yy := y * in.YStride
  331. cy := y * in.CStride
  332. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  333. ci := cy + x/4
  334. Pix[off+0] = Y[yy+x]
  335. Pix[off+1] = Cb[ci]
  336. Pix[off+2] = Cr[ci]
  337. off += 3
  338. }
  339. }
  340. return p
  341. }
  342. func convertToYCC410(in *image.YCbCr, p *ycc) *ycc {
  343. var off int
  344. Pix := p.Pix
  345. Y := in.Y
  346. Cb := in.Cb
  347. Cr := in.Cr
  348. for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ {
  349. yy := y * in.YStride
  350. cy := (y / 2) * in.CStride
  351. for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ {
  352. ci := cy + x/4
  353. Pix[off+0] = Y[yy+x]
  354. Pix[off+1] = Cb[ci]
  355. Pix[off+2] = Cr[ci]
  356. off += 3
  357. }
  358. }
  359. return p
  360. }