1/* 2Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com> 3 4Permission to use, copy, modify, and/or distribute this software for any purpose 5with or without fee is hereby granted, provided that the above copyright notice 6and this permission notice appear in all copies. 7 8THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 10FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 12OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 13TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 14THIS SOFTWARE. 15*/ 16 17package resize 18 19import ( 20 "image" 21 "image/color" 22) 23 24// ycc is an in memory YCbCr image. The Y, Cb and Cr samples are held in a 25// single slice to increase resizing performance. 26type ycc struct { 27 // Pix holds the image's pixels, in Y, Cb, Cr order. The pixel at 28 // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3]. 29 Pix []uint8 30 // Stride is the Pix stride (in bytes) between vertically adjacent pixels. 31 Stride int 32 // Rect is the image's bounds. 33 Rect image.Rectangle 34 // SubsampleRatio is the subsample ratio of the original YCbCr image. 35 SubsampleRatio image.YCbCrSubsampleRatio 36} 37 38// PixOffset returns the index of the first element of Pix that corresponds to 39// the pixel at (x, y). 40func (p *ycc) PixOffset(x, y int) int { 41 return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*3 42} 43 44func (p *ycc) Bounds() image.Rectangle { 45 return p.Rect 46} 47 48func (p *ycc) ColorModel() color.Model { 49 return color.YCbCrModel 50} 51 52func (p *ycc) At(x, y int) color.Color { 53 if !(image.Point{x, y}.In(p.Rect)) { 54 return color.YCbCr{} 55 } 56 i := p.PixOffset(x, y) 57 return color.YCbCr{ 58 p.Pix[i+0], 59 p.Pix[i+1], 60 p.Pix[i+2], 61 } 62} 63 64func (p *ycc) Opaque() bool { 65 return true 66} 67 68// SubImage returns an image representing the portion of the image p visible 69// through r. The returned value shares pixels with the original image. 70func (p *ycc) SubImage(r image.Rectangle) image.Image { 71 r = r.Intersect(p.Rect) 72 if r.Empty() { 73 return &ycc{SubsampleRatio: p.SubsampleRatio} 74 } 75 i := p.PixOffset(r.Min.X, r.Min.Y) 76 return &ycc{ 77 Pix: p.Pix[i:], 78 Stride: p.Stride, 79 Rect: r, 80 SubsampleRatio: p.SubsampleRatio, 81 } 82} 83 84// newYCC returns a new ycc with the given bounds and subsample ratio. 85func newYCC(r image.Rectangle, s image.YCbCrSubsampleRatio) *ycc { 86 w, h := r.Dx(), r.Dy() 87 buf := make([]uint8, 3*w*h) 88 return &ycc{Pix: buf, Stride: 3 * w, Rect: r, SubsampleRatio: s} 89} 90 91// Copy of image.YCbCrSubsampleRatio constants - this allows us to support 92// older versions of Go where these constants are not defined (i.e. Go 1.4) 93const ( 94 ycbcrSubsampleRatio444 image.YCbCrSubsampleRatio = iota 95 ycbcrSubsampleRatio422 96 ycbcrSubsampleRatio420 97 ycbcrSubsampleRatio440 98 ycbcrSubsampleRatio411 99 ycbcrSubsampleRatio410 100) 101 102// YCbCr converts ycc to a YCbCr image with the same subsample ratio 103// as the YCbCr image that ycc was generated from. 104func (p *ycc) YCbCr() *image.YCbCr { 105 ycbcr := image.NewYCbCr(p.Rect, p.SubsampleRatio) 106 switch ycbcr.SubsampleRatio { 107 case ycbcrSubsampleRatio422: 108 return p.ycbcr422(ycbcr) 109 case ycbcrSubsampleRatio420: 110 return p.ycbcr420(ycbcr) 111 case ycbcrSubsampleRatio440: 112 return p.ycbcr440(ycbcr) 113 case ycbcrSubsampleRatio444: 114 return p.ycbcr444(ycbcr) 115 case ycbcrSubsampleRatio411: 116 return p.ycbcr411(ycbcr) 117 case ycbcrSubsampleRatio410: 118 return p.ycbcr410(ycbcr) 119 } 120 return ycbcr 121} 122 123// imageYCbCrToYCC converts a YCbCr image to a ycc image for resizing. 124func imageYCbCrToYCC(in *image.YCbCr) *ycc { 125 w, h := in.Rect.Dx(), in.Rect.Dy() 126 p := ycc{ 127 Pix: make([]uint8, 3*w*h), 128 Stride: 3 * w, 129 Rect: image.Rect(0, 0, w, h), 130 SubsampleRatio: in.SubsampleRatio, 131 } 132 switch in.SubsampleRatio { 133 case ycbcrSubsampleRatio422: 134 return convertToYCC422(in, &p) 135 case ycbcrSubsampleRatio420: 136 return convertToYCC420(in, &p) 137 case ycbcrSubsampleRatio440: 138 return convertToYCC440(in, &p) 139 case ycbcrSubsampleRatio444: 140 return convertToYCC444(in, &p) 141 case ycbcrSubsampleRatio411: 142 return convertToYCC411(in, &p) 143 case ycbcrSubsampleRatio410: 144 return convertToYCC410(in, &p) 145 } 146 return &p 147} 148 149func (p *ycc) ycbcr422(ycbcr *image.YCbCr) *image.YCbCr { 150 var off int 151 Pix := p.Pix 152 Y := ycbcr.Y 153 Cb := ycbcr.Cb 154 Cr := ycbcr.Cr 155 for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { 156 yy := y * ycbcr.YStride 157 cy := y * ycbcr.CStride 158 for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { 159 ci := cy + x/2 160 Y[yy+x] = Pix[off+0] 161 Cb[ci] = Pix[off+1] 162 Cr[ci] = Pix[off+2] 163 off += 3 164 } 165 } 166 return ycbcr 167} 168 169func (p *ycc) ycbcr420(ycbcr *image.YCbCr) *image.YCbCr { 170 var off int 171 Pix := p.Pix 172 Y := ycbcr.Y 173 Cb := ycbcr.Cb 174 Cr := ycbcr.Cr 175 for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { 176 yy := y * ycbcr.YStride 177 cy := (y / 2) * ycbcr.CStride 178 for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { 179 ci := cy + x/2 180 Y[yy+x] = Pix[off+0] 181 Cb[ci] = Pix[off+1] 182 Cr[ci] = Pix[off+2] 183 off += 3 184 } 185 } 186 return ycbcr 187} 188 189func (p *ycc) ycbcr440(ycbcr *image.YCbCr) *image.YCbCr { 190 var off int 191 Pix := p.Pix 192 Y := ycbcr.Y 193 Cb := ycbcr.Cb 194 Cr := ycbcr.Cr 195 for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { 196 yy := y * ycbcr.YStride 197 cy := (y / 2) * ycbcr.CStride 198 for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { 199 ci := cy + x 200 Y[yy+x] = Pix[off+0] 201 Cb[ci] = Pix[off+1] 202 Cr[ci] = Pix[off+2] 203 off += 3 204 } 205 } 206 return ycbcr 207} 208 209func (p *ycc) ycbcr444(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 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 229func (p *ycc) ycbcr411(ycbcr *image.YCbCr) *image.YCbCr { 230 var off int 231 Pix := p.Pix 232 Y := ycbcr.Y 233 Cb := ycbcr.Cb 234 Cr := ycbcr.Cr 235 for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { 236 yy := y * ycbcr.YStride 237 cy := y * ycbcr.CStride 238 for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { 239 ci := cy + x/4 240 Y[yy+x] = Pix[off+0] 241 Cb[ci] = Pix[off+1] 242 Cr[ci] = Pix[off+2] 243 off += 3 244 } 245 } 246 return ycbcr 247} 248 249func (p *ycc) ycbcr410(ycbcr *image.YCbCr) *image.YCbCr { 250 var off int 251 Pix := p.Pix 252 Y := ycbcr.Y 253 Cb := ycbcr.Cb 254 Cr := ycbcr.Cr 255 for y := 0; y < ycbcr.Rect.Max.Y-ycbcr.Rect.Min.Y; y++ { 256 yy := y * ycbcr.YStride 257 cy := (y / 2) * ycbcr.CStride 258 for x := 0; x < ycbcr.Rect.Max.X-ycbcr.Rect.Min.X; x++ { 259 ci := cy + x/4 260 Y[yy+x] = Pix[off+0] 261 Cb[ci] = Pix[off+1] 262 Cr[ci] = Pix[off+2] 263 off += 3 264 } 265 } 266 return ycbcr 267} 268 269func convertToYCC422(in *image.YCbCr, p *ycc) *ycc { 270 var off int 271 Pix := p.Pix 272 Y := in.Y 273 Cb := in.Cb 274 Cr := in.Cr 275 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 276 yy := y * in.YStride 277 cy := y * in.CStride 278 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 279 ci := cy + x/2 280 Pix[off+0] = Y[yy+x] 281 Pix[off+1] = Cb[ci] 282 Pix[off+2] = Cr[ci] 283 off += 3 284 } 285 } 286 return p 287} 288 289func convertToYCC420(in *image.YCbCr, p *ycc) *ycc { 290 var off int 291 Pix := p.Pix 292 Y := in.Y 293 Cb := in.Cb 294 Cr := in.Cr 295 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 296 yy := y * in.YStride 297 cy := (y / 2) * in.CStride 298 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 299 ci := cy + x/2 300 Pix[off+0] = Y[yy+x] 301 Pix[off+1] = Cb[ci] 302 Pix[off+2] = Cr[ci] 303 off += 3 304 } 305 } 306 return p 307} 308 309func convertToYCC440(in *image.YCbCr, p *ycc) *ycc { 310 var off int 311 Pix := p.Pix 312 Y := in.Y 313 Cb := in.Cb 314 Cr := in.Cr 315 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 316 yy := y * in.YStride 317 cy := (y / 2) * in.CStride 318 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 319 ci := cy + x 320 Pix[off+0] = Y[yy+x] 321 Pix[off+1] = Cb[ci] 322 Pix[off+2] = Cr[ci] 323 off += 3 324 } 325 } 326 return p 327} 328 329func convertToYCC444(in *image.YCbCr, p *ycc) *ycc { 330 var off int 331 Pix := p.Pix 332 Y := in.Y 333 Cb := in.Cb 334 Cr := in.Cr 335 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 336 yy := y * in.YStride 337 cy := y * in.CStride 338 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 339 ci := cy + x 340 Pix[off+0] = Y[yy+x] 341 Pix[off+1] = Cb[ci] 342 Pix[off+2] = Cr[ci] 343 off += 3 344 } 345 } 346 return p 347} 348 349func convertToYCC411(in *image.YCbCr, p *ycc) *ycc { 350 var off int 351 Pix := p.Pix 352 Y := in.Y 353 Cb := in.Cb 354 Cr := in.Cr 355 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 356 yy := y * in.YStride 357 cy := y * in.CStride 358 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 359 ci := cy + x/4 360 Pix[off+0] = Y[yy+x] 361 Pix[off+1] = Cb[ci] 362 Pix[off+2] = Cr[ci] 363 off += 3 364 } 365 } 366 return p 367} 368 369func convertToYCC410(in *image.YCbCr, p *ycc) *ycc { 370 var off int 371 Pix := p.Pix 372 Y := in.Y 373 Cb := in.Cb 374 Cr := in.Cr 375 for y := 0; y < in.Rect.Max.Y-in.Rect.Min.Y; y++ { 376 yy := y * in.YStride 377 cy := (y / 2) * in.CStride 378 for x := 0; x < in.Rect.Max.X-in.Rect.Min.X; x++ { 379 ci := cy + x/4 380 Pix[off+0] = Y[yy+x] 381 Pix[off+1] = Cb[ci] 382 Pix[off+2] = Cr[ci] 383 off += 3 384 } 385 } 386 return p 387} 388