Black Lives Matter. Support the Equal Justice Initiative.

Source file src/image/ycbcr.go

Documentation: image

     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  }
   329  

View as plain text