1// Copyright 2011 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package image 6 7import ( 8 "image/color" 9) 10 11// YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image. 12type YCbCrSubsampleRatio int 13 14const ( 15 YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota 16 YCbCrSubsampleRatio422 17 YCbCrSubsampleRatio420 18 YCbCrSubsampleRatio440 19 YCbCrSubsampleRatio411 20 YCbCrSubsampleRatio410 21) 22 23func (s YCbCrSubsampleRatio) String() string { 24 switch s { 25 case YCbCrSubsampleRatio444: 26 return "YCbCrSubsampleRatio444" 27 case YCbCrSubsampleRatio422: 28 return "YCbCrSubsampleRatio422" 29 case YCbCrSubsampleRatio420: 30 return "YCbCrSubsampleRatio420" 31 case YCbCrSubsampleRatio440: 32 return "YCbCrSubsampleRatio440" 33 case YCbCrSubsampleRatio411: 34 return "YCbCrSubsampleRatio411" 35 case YCbCrSubsampleRatio410: 36 return "YCbCrSubsampleRatio410" 37 } 38 return "YCbCrSubsampleRatioUnknown" 39} 40 41// YCbCr is an in-memory image of Y'CbCr colors. There is one Y sample per 42// pixel, but each Cb and Cr sample can span one or more pixels. 43// YStride is the Y slice index delta between vertically adjacent pixels. 44// CStride is the Cb and Cr slice index delta between vertically adjacent pixels 45// that map to separate chroma samples. 46// It is not an absolute requirement, but YStride and len(Y) are typically 47// multiples of 8, and: 48// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1. 49// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2. 50// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4. 51// For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2. 52// For 4:1:1, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/4. 53// For 4:1:0, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/8. 54type YCbCr struct { 55 Y, Cb, Cr []uint8 56 YStride int 57 CStride int 58 SubsampleRatio YCbCrSubsampleRatio 59 Rect Rectangle 60} 61 62func (p *YCbCr) ColorModel() color.Model { 63 return color.YCbCrModel 64} 65 66func (p *YCbCr) Bounds() Rectangle { 67 return p.Rect 68} 69 70func (p *YCbCr) At(x, y int) color.Color { 71 return p.YCbCrAt(x, y) 72} 73 74func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr { 75 if !(Point{x, y}.In(p.Rect)) { 76 return color.YCbCr{} 77 } 78 yi := p.YOffset(x, y) 79 ci := p.COffset(x, y) 80 return color.YCbCr{ 81 p.Y[yi], 82 p.Cb[ci], 83 p.Cr[ci], 84 } 85} 86 87// YOffset returns the index of the first element of Y that corresponds to 88// the pixel at (x, y). 89func (p *YCbCr) YOffset(x, y int) int { 90 return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X) 91} 92 93// COffset returns the index of the first element of Cb or Cr that corresponds 94// to the pixel at (x, y). 95func (p *YCbCr) COffset(x, y int) int { 96 switch p.SubsampleRatio { 97 case YCbCrSubsampleRatio422: 98 return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2) 99 case YCbCrSubsampleRatio420: 100 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2) 101 case YCbCrSubsampleRatio440: 102 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X) 103 case YCbCrSubsampleRatio411: 104 return (y-p.Rect.Min.Y)*p.CStride + (x/4 - p.Rect.Min.X/4) 105 case YCbCrSubsampleRatio410: 106 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/4 - p.Rect.Min.X/4) 107 } 108 // Default to 4:4:4 subsampling. 109 return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) 110} 111 112// SubImage returns an image representing the portion of the image p visible 113// through r. The returned value shares pixels with the original image. 114func (p *YCbCr) SubImage(r Rectangle) Image { 115 r = r.Intersect(p.Rect) 116 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 117 // either r1 or r2 if the intersection is empty. Without explicitly checking for 118 // this, the Pix[i:] expression below can panic. 119 if r.Empty() { 120 return &YCbCr{ 121 SubsampleRatio: p.SubsampleRatio, 122 } 123 } 124 yi := p.YOffset(r.Min.X, r.Min.Y) 125 ci := p.COffset(r.Min.X, r.Min.Y) 126 return &YCbCr{ 127 Y: p.Y[yi:], 128 Cb: p.Cb[ci:], 129 Cr: p.Cr[ci:], 130 SubsampleRatio: p.SubsampleRatio, 131 YStride: p.YStride, 132 CStride: p.CStride, 133 Rect: r, 134 } 135} 136 137func (p *YCbCr) Opaque() bool { 138 return true 139} 140 141func yCbCrSize(r Rectangle, subsampleRatio YCbCrSubsampleRatio) (w, h, cw, ch int) { 142 w, h = r.Dx(), r.Dy() 143 switch subsampleRatio { 144 case YCbCrSubsampleRatio422: 145 cw = (r.Max.X+1)/2 - r.Min.X/2 146 ch = h 147 case YCbCrSubsampleRatio420: 148 cw = (r.Max.X+1)/2 - r.Min.X/2 149 ch = (r.Max.Y+1)/2 - r.Min.Y/2 150 case YCbCrSubsampleRatio440: 151 cw = w 152 ch = (r.Max.Y+1)/2 - r.Min.Y/2 153 case YCbCrSubsampleRatio411: 154 cw = (r.Max.X+3)/4 - r.Min.X/4 155 ch = h 156 case YCbCrSubsampleRatio410: 157 cw = (r.Max.X+3)/4 - r.Min.X/4 158 ch = (r.Max.Y+1)/2 - r.Min.Y/2 159 default: 160 // Default to 4:4:4 subsampling. 161 cw = w 162 ch = h 163 } 164 return 165} 166 167// NewYCbCr returns a new YCbCr image with the given bounds and subsample 168// ratio. 169func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { 170 w, h, cw, ch := yCbCrSize(r, subsampleRatio) 171 i0 := w*h + 0*cw*ch 172 i1 := w*h + 1*cw*ch 173 i2 := w*h + 2*cw*ch 174 b := make([]byte, i2) 175 return &YCbCr{ 176 Y: b[:i0:i0], 177 Cb: b[i0:i1:i1], 178 Cr: b[i1:i2:i2], 179 SubsampleRatio: subsampleRatio, 180 YStride: w, 181 CStride: cw, 182 Rect: r, 183 } 184} 185 186// NYCbCrA is an in-memory image of non-alpha-premultiplied Y'CbCr-with-alpha 187// colors. A and AStride are analogous to the Y and YStride fields of the 188// embedded YCbCr. 189type NYCbCrA struct { 190 YCbCr 191 A []uint8 192 AStride int 193} 194 195func (p *NYCbCrA) ColorModel() color.Model { 196 return color.NYCbCrAModel 197} 198 199func (p *NYCbCrA) At(x, y int) color.Color { 200 return p.NYCbCrAAt(x, y) 201} 202 203func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA { 204 if !(Point{X: x, Y: y}.In(p.Rect)) { 205 return color.NYCbCrA{} 206 } 207 yi := p.YOffset(x, y) 208 ci := p.COffset(x, y) 209 ai := p.AOffset(x, y) 210 return color.NYCbCrA{ 211 color.YCbCr{ 212 Y: p.Y[yi], 213 Cb: p.Cb[ci], 214 Cr: p.Cr[ci], 215 }, 216 p.A[ai], 217 } 218} 219 220// AOffset returns the index of the first element of A that corresponds to the 221// pixel at (x, y). 222func (p *NYCbCrA) AOffset(x, y int) int { 223 return (y-p.Rect.Min.Y)*p.AStride + (x - p.Rect.Min.X) 224} 225 226// SubImage returns an image representing the portion of the image p visible 227// through r. The returned value shares pixels with the original image. 228func (p *NYCbCrA) SubImage(r Rectangle) Image { 229 r = r.Intersect(p.Rect) 230 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 231 // either r1 or r2 if the intersection is empty. Without explicitly checking for 232 // this, the Pix[i:] expression below can panic. 233 if r.Empty() { 234 return &NYCbCrA{ 235 YCbCr: YCbCr{ 236 SubsampleRatio: p.SubsampleRatio, 237 }, 238 } 239 } 240 yi := p.YOffset(r.Min.X, r.Min.Y) 241 ci := p.COffset(r.Min.X, r.Min.Y) 242 ai := p.AOffset(r.Min.X, r.Min.Y) 243 return &NYCbCrA{ 244 YCbCr: YCbCr{ 245 Y: p.Y[yi:], 246 Cb: p.Cb[ci:], 247 Cr: p.Cr[ci:], 248 SubsampleRatio: p.SubsampleRatio, 249 YStride: p.YStride, 250 CStride: p.CStride, 251 Rect: r, 252 }, 253 A: p.A[ai:], 254 AStride: p.AStride, 255 } 256} 257 258// Opaque scans the entire image and reports whether it is fully opaque. 259func (p *NYCbCrA) Opaque() bool { 260 if p.Rect.Empty() { 261 return true 262 } 263 i0, i1 := 0, p.Rect.Dx() 264 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 265 for _, a := range p.A[i0:i1] { 266 if a != 0xff { 267 return false 268 } 269 } 270 i0 += p.AStride 271 i1 += p.AStride 272 } 273 return true 274} 275 276// NewNYCbCrA returns a new NYCbCrA image with the given bounds and subsample 277// ratio. 278func NewNYCbCrA(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *NYCbCrA { 279 w, h, cw, ch := yCbCrSize(r, subsampleRatio) 280 i0 := 1*w*h + 0*cw*ch 281 i1 := 1*w*h + 1*cw*ch 282 i2 := 1*w*h + 2*cw*ch 283 i3 := 2*w*h + 2*cw*ch 284 b := make([]byte, i3) 285 return &NYCbCrA{ 286 YCbCr: YCbCr{ 287 Y: b[:i0:i0], 288 Cb: b[i0:i1:i1], 289 Cr: b[i1:i2:i2], 290 SubsampleRatio: subsampleRatio, 291 YStride: w, 292 CStride: cw, 293 Rect: r, 294 }, 295 A: b[i2:], 296 AStride: w, 297 } 298} 299