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