github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/image/ycbcr.go (about) 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 5 package image 6 7 import ( 8 "image/color" 9 ) 10 11 // YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image. 12 type YCbCrSubsampleRatio int 13 14 const ( 15 YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota 16 YCbCrSubsampleRatio422 17 YCbCrSubsampleRatio420 18 YCbCrSubsampleRatio440 19 YCbCrSubsampleRatio411 20 YCbCrSubsampleRatio410 21 ) 22 23 func (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. 54 type YCbCr struct { 55 Y, Cb, Cr []uint8 56 YStride int 57 CStride int 58 SubsampleRatio YCbCrSubsampleRatio 59 Rect Rectangle 60 } 61 62 func (p *YCbCr) ColorModel() color.Model { 63 return color.YCbCrModel 64 } 65 66 func (p *YCbCr) Bounds() Rectangle { 67 return p.Rect 68 } 69 70 func (p *YCbCr) At(x, y int) color.Color { 71 return p.YCbCrAt(x, y) 72 } 73 74 func (p *YCbCr) RGBA64At(x, y int) color.RGBA64 { 75 r, g, b, a := p.YCbCrAt(x, y).RGBA() 76 return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} 77 } 78 79 func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr { 80 if !(Point{x, y}.In(p.Rect)) { 81 return color.YCbCr{} 82 } 83 yi := p.YOffset(x, y) 84 ci := p.COffset(x, y) 85 return color.YCbCr{ 86 p.Y[yi], 87 p.Cb[ci], 88 p.Cr[ci], 89 } 90 } 91 92 // YOffset returns the index of the first element of Y that corresponds to 93 // the pixel at (x, y). 94 func (p *YCbCr) YOffset(x, y int) int { 95 return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X) 96 } 97 98 // COffset returns the index of the first element of Cb or Cr that corresponds 99 // to the pixel at (x, y). 100 func (p *YCbCr) COffset(x, y int) int { 101 switch p.SubsampleRatio { 102 case YCbCrSubsampleRatio422: 103 return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2) 104 case YCbCrSubsampleRatio420: 105 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2) 106 case YCbCrSubsampleRatio440: 107 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X) 108 case YCbCrSubsampleRatio411: 109 return (y-p.Rect.Min.Y)*p.CStride + (x/4 - p.Rect.Min.X/4) 110 case YCbCrSubsampleRatio410: 111 return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/4 - p.Rect.Min.X/4) 112 } 113 // Default to 4:4:4 subsampling. 114 return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) 115 } 116 117 // SubImage returns an image representing the portion of the image p visible 118 // through r. The returned value shares pixels with the original image. 119 func (p *YCbCr) SubImage(r Rectangle) Image { 120 r = r.Intersect(p.Rect) 121 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 122 // either r1 or r2 if the intersection is empty. Without explicitly checking for 123 // this, the Pix[i:] expression below can panic. 124 if r.Empty() { 125 return &YCbCr{ 126 SubsampleRatio: p.SubsampleRatio, 127 } 128 } 129 yi := p.YOffset(r.Min.X, r.Min.Y) 130 ci := p.COffset(r.Min.X, r.Min.Y) 131 return &YCbCr{ 132 Y: p.Y[yi:], 133 Cb: p.Cb[ci:], 134 Cr: p.Cr[ci:], 135 SubsampleRatio: p.SubsampleRatio, 136 YStride: p.YStride, 137 CStride: p.CStride, 138 Rect: r, 139 } 140 } 141 142 func (p *YCbCr) Opaque() bool { 143 return true 144 } 145 146 func yCbCrSize(r Rectangle, subsampleRatio YCbCrSubsampleRatio) (w, h, cw, ch int) { 147 w, h = r.Dx(), r.Dy() 148 switch subsampleRatio { 149 case YCbCrSubsampleRatio422: 150 cw = (r.Max.X+1)/2 - r.Min.X/2 151 ch = h 152 case YCbCrSubsampleRatio420: 153 cw = (r.Max.X+1)/2 - r.Min.X/2 154 ch = (r.Max.Y+1)/2 - r.Min.Y/2 155 case YCbCrSubsampleRatio440: 156 cw = w 157 ch = (r.Max.Y+1)/2 - r.Min.Y/2 158 case YCbCrSubsampleRatio411: 159 cw = (r.Max.X+3)/4 - r.Min.X/4 160 ch = h 161 case YCbCrSubsampleRatio410: 162 cw = (r.Max.X+3)/4 - r.Min.X/4 163 ch = (r.Max.Y+1)/2 - r.Min.Y/2 164 default: 165 // Default to 4:4:4 subsampling. 166 cw = w 167 ch = h 168 } 169 return 170 } 171 172 // NewYCbCr returns a new YCbCr image with the given bounds and subsample 173 // ratio. 174 func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { 175 w, h, cw, ch := yCbCrSize(r, subsampleRatio) 176 177 // totalLength should be the same as i2, below, for a valid Rectangle r. 178 totalLength := add2NonNeg( 179 mul3NonNeg(1, w, h), 180 mul3NonNeg(2, cw, ch), 181 ) 182 if totalLength < 0 { 183 panic("image: NewYCbCr Rectangle has huge or negative dimensions") 184 } 185 186 i0 := w*h + 0*cw*ch 187 i1 := w*h + 1*cw*ch 188 i2 := w*h + 2*cw*ch 189 b := make([]byte, i2) 190 return &YCbCr{ 191 Y: b[:i0:i0], 192 Cb: b[i0:i1:i1], 193 Cr: b[i1:i2:i2], 194 SubsampleRatio: subsampleRatio, 195 YStride: w, 196 CStride: cw, 197 Rect: r, 198 } 199 } 200 201 // NYCbCrA is an in-memory image of non-alpha-premultiplied Y'CbCr-with-alpha 202 // colors. A and AStride are analogous to the Y and YStride fields of the 203 // embedded YCbCr. 204 type NYCbCrA struct { 205 YCbCr 206 A []uint8 207 AStride int 208 } 209 210 func (p *NYCbCrA) ColorModel() color.Model { 211 return color.NYCbCrAModel 212 } 213 214 func (p *NYCbCrA) At(x, y int) color.Color { 215 return p.NYCbCrAAt(x, y) 216 } 217 218 func (p *NYCbCrA) RGBA64At(x, y int) color.RGBA64 { 219 r, g, b, a := p.NYCbCrAAt(x, y).RGBA() 220 return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} 221 } 222 223 func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA { 224 if !(Point{X: x, Y: y}.In(p.Rect)) { 225 return color.NYCbCrA{} 226 } 227 yi := p.YOffset(x, y) 228 ci := p.COffset(x, y) 229 ai := p.AOffset(x, y) 230 return color.NYCbCrA{ 231 color.YCbCr{ 232 Y: p.Y[yi], 233 Cb: p.Cb[ci], 234 Cr: p.Cr[ci], 235 }, 236 p.A[ai], 237 } 238 } 239 240 // AOffset returns the index of the first element of A that corresponds to the 241 // pixel at (x, y). 242 func (p *NYCbCrA) AOffset(x, y int) int { 243 return (y-p.Rect.Min.Y)*p.AStride + (x - p.Rect.Min.X) 244 } 245 246 // SubImage returns an image representing the portion of the image p visible 247 // through r. The returned value shares pixels with the original image. 248 func (p *NYCbCrA) SubImage(r Rectangle) Image { 249 r = r.Intersect(p.Rect) 250 // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside 251 // either r1 or r2 if the intersection is empty. Without explicitly checking for 252 // this, the Pix[i:] expression below can panic. 253 if r.Empty() { 254 return &NYCbCrA{ 255 YCbCr: YCbCr{ 256 SubsampleRatio: p.SubsampleRatio, 257 }, 258 } 259 } 260 yi := p.YOffset(r.Min.X, r.Min.Y) 261 ci := p.COffset(r.Min.X, r.Min.Y) 262 ai := p.AOffset(r.Min.X, r.Min.Y) 263 return &NYCbCrA{ 264 YCbCr: YCbCr{ 265 Y: p.Y[yi:], 266 Cb: p.Cb[ci:], 267 Cr: p.Cr[ci:], 268 SubsampleRatio: p.SubsampleRatio, 269 YStride: p.YStride, 270 CStride: p.CStride, 271 Rect: r, 272 }, 273 A: p.A[ai:], 274 AStride: p.AStride, 275 } 276 } 277 278 // Opaque scans the entire image and reports whether it is fully opaque. 279 func (p *NYCbCrA) Opaque() bool { 280 if p.Rect.Empty() { 281 return true 282 } 283 i0, i1 := 0, p.Rect.Dx() 284 for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { 285 for _, a := range p.A[i0:i1] { 286 if a != 0xff { 287 return false 288 } 289 } 290 i0 += p.AStride 291 i1 += p.AStride 292 } 293 return true 294 } 295 296 // NewNYCbCrA returns a new NYCbCrA image with the given bounds and subsample 297 // ratio. 298 func NewNYCbCrA(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *NYCbCrA { 299 w, h, cw, ch := yCbCrSize(r, subsampleRatio) 300 301 // totalLength should be the same as i3, below, for a valid Rectangle r. 302 totalLength := add2NonNeg( 303 mul3NonNeg(2, w, h), 304 mul3NonNeg(2, cw, ch), 305 ) 306 if totalLength < 0 { 307 panic("image: NewNYCbCrA Rectangle has huge or negative dimension") 308 } 309 310 i0 := 1*w*h + 0*cw*ch 311 i1 := 1*w*h + 1*cw*ch 312 i2 := 1*w*h + 2*cw*ch 313 i3 := 2*w*h + 2*cw*ch 314 b := make([]byte, i3) 315 return &NYCbCrA{ 316 YCbCr: YCbCr{ 317 Y: b[:i0:i0], 318 Cb: b[i0:i1:i1], 319 Cr: b[i1:i2:i2], 320 SubsampleRatio: subsampleRatio, 321 YStride: w, 322 CStride: cw, 323 Rect: r, 324 }, 325 A: b[i2:], 326 AStride: w, 327 } 328 }