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