Source file src/image/ycbcr.go

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

View as plain text