ycbcr.mx raw

   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 := []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 := []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  }
 330