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.

227 lines
6.5 KiB

  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. // YCbCr converts ycc to a YCbCr image with the same subsample ratio
  79. // as the YCbCr image that ycc was generated from.
  80. func (p *ycc) YCbCr() *image.YCbCr {
  81. ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio)
  82. var off int
  83. switch ycbcr.SubsampleRatio {
  84. case image.YCbCrSubsampleRatio422:
  85. for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
  86. yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
  87. cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
  88. for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
  89. xx := (x - ycbcr.Rect.Min.X)
  90. yi := yy + xx
  91. ci := cy + xx/2
  92. ycbcr.Y[yi] = p.Pix[off+0]
  93. ycbcr.Cb[ci] = p.Pix[off+1]
  94. ycbcr.Cr[ci] = p.Pix[off+2]
  95. off += 3
  96. }
  97. }
  98. case image.YCbCrSubsampleRatio420:
  99. for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
  100. yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
  101. cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
  102. for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
  103. xx := (x - ycbcr.Rect.Min.X)
  104. yi := yy + xx
  105. ci := cy + xx/2
  106. ycbcr.Y[yi] = p.Pix[off+0]
  107. ycbcr.Cb[ci] = p.Pix[off+1]
  108. ycbcr.Cr[ci] = p.Pix[off+2]
  109. off += 3
  110. }
  111. }
  112. case image.YCbCrSubsampleRatio440:
  113. for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
  114. yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
  115. cy := (y/2 - ycbcr.Rect.Min.Y/2) * ycbcr.CStride
  116. for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
  117. xx := (x - ycbcr.Rect.Min.X)
  118. yi := yy + xx
  119. ci := cy + xx
  120. ycbcr.Y[yi] = p.Pix[off+0]
  121. ycbcr.Cb[ci] = p.Pix[off+1]
  122. ycbcr.Cr[ci] = p.Pix[off+2]
  123. off += 3
  124. }
  125. }
  126. default:
  127. // Default to 4:4:4 subsampling.
  128. for y := ycbcr.Rect.Min.Y; y < ycbcr.Rect.Max.Y; y++ {
  129. yy := (y - ycbcr.Rect.Min.Y) * ycbcr.YStride
  130. cy := (y - ycbcr.Rect.Min.Y) * ycbcr.CStride
  131. for x := ycbcr.Rect.Min.X; x < ycbcr.Rect.Max.X; x++ {
  132. xx := (x - ycbcr.Rect.Min.X)
  133. yi := yy + xx
  134. ci := cy + xx
  135. ycbcr.Y[yi] = p.Pix[off+0]
  136. ycbcr.Cb[ci] = p.Pix[off+1]
  137. ycbcr.Cr[ci] = p.Pix[off+2]
  138. off += 3
  139. }
  140. }
  141. }
  142. return ycbcr
  143. }
  144. // imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing.
  145. func imageYCbCrToYCC(in *image.YCbCr) *ycc {
  146. w, h := in.Rect.Dx(), in.Rect.Dy()
  147. r := image.Rect(0, 0, w, h)
  148. buf := make([]uint8, 3*w*h)
  149. p := ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: in.SubsampleRatio}
  150. var off int
  151. switch in.SubsampleRatio {
  152. case image.YCbCrSubsampleRatio422:
  153. for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
  154. yy := (y - in.Rect.Min.Y) * in.YStride
  155. cy := (y - in.Rect.Min.Y) * in.CStride
  156. for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
  157. xx := (x - in.Rect.Min.X)
  158. yi := yy + xx
  159. ci := cy + xx/2
  160. p.Pix[off+0] = in.Y[yi]
  161. p.Pix[off+1] = in.Cb[ci]
  162. p.Pix[off+2] = in.Cr[ci]
  163. off += 3
  164. }
  165. }
  166. case image.YCbCrSubsampleRatio420:
  167. for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
  168. yy := (y - in.Rect.Min.Y) * in.YStride
  169. cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
  170. for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
  171. xx := (x - in.Rect.Min.X)
  172. yi := yy + xx
  173. ci := cy + xx/2
  174. p.Pix[off+0] = in.Y[yi]
  175. p.Pix[off+1] = in.Cb[ci]
  176. p.Pix[off+2] = in.Cr[ci]
  177. off += 3
  178. }
  179. }
  180. case image.YCbCrSubsampleRatio440:
  181. for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
  182. yy := (y - in.Rect.Min.Y) * in.YStride
  183. cy := (y/2 - in.Rect.Min.Y/2) * in.CStride
  184. for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
  185. xx := (x - in.Rect.Min.X)
  186. yi := yy + xx
  187. ci := cy + xx
  188. p.Pix[off+0] = in.Y[yi]
  189. p.Pix[off+1] = in.Cb[ci]
  190. p.Pix[off+2] = in.Cr[ci]
  191. off += 3
  192. }
  193. }
  194. default:
  195. // Default to 4:4:4 subsampling.
  196. for y := in.Rect.Min.Y; y < in.Rect.Max.Y; y++ {
  197. yy := (y - in.Rect.Min.Y) * in.YStride
  198. cy := (y - in.Rect.Min.Y) * in.CStride
  199. for x := in.Rect.Min.X; x < in.Rect.Max.X; x++ {
  200. xx := (x - in.Rect.Min.X)
  201. yi := yy + xx
  202. ci := cy + xx
  203. p.Pix[off+0] = in.Y[yi]
  204. p.Pix[off+1] = in.Cb[ci]
  205. p.Pix[off+2] = in.Cr[ci]
  206. off += 3
  207. }
  208. }
  209. }
  210. return &p
  211. }