| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package image |
| |
| import ( |
| "image/color" |
| ) |
| |
| // YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image. |
| type YCbCrSubsampleRatio int |
| |
| const ( |
| YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota |
| YCbCrSubsampleRatio422 |
| YCbCrSubsampleRatio420 |
| YCbCrSubsampleRatio440 |
| ) |
| |
| func (s YCbCrSubsampleRatio) String() string { |
| switch s { |
| case YCbCrSubsampleRatio444: |
| return "YCbCrSubsampleRatio444" |
| case YCbCrSubsampleRatio422: |
| return "YCbCrSubsampleRatio422" |
| case YCbCrSubsampleRatio420: |
| return "YCbCrSubsampleRatio420" |
| case YCbCrSubsampleRatio440: |
| return "YCbCrSubsampleRatio440" |
| } |
| return "YCbCrSubsampleRatioUnknown" |
| } |
| |
| // YCbCr is an in-memory image of Y'CbCr colors. There is one Y sample per |
| // pixel, but each Cb and Cr sample can span one or more pixels. |
| // YStride is the Y slice index delta between vertically adjacent pixels. |
| // CStride is the Cb and Cr slice index delta between vertically adjacent pixels |
| // that map to separate chroma samples. |
| // It is not an absolute requirement, but YStride and len(Y) are typically |
| // multiples of 8, and: |
| // For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1. |
| // For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2. |
| // For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4. |
| // For 4:4:0, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/2. |
| type YCbCr struct { |
| Y, Cb, Cr []uint8 |
| YStride int |
| CStride int |
| SubsampleRatio YCbCrSubsampleRatio |
| Rect Rectangle |
| } |
| |
| func (p *YCbCr) ColorModel() color.Model { |
| return color.YCbCrModel |
| } |
| |
| func (p *YCbCr) Bounds() Rectangle { |
| return p.Rect |
| } |
| |
| func (p *YCbCr) At(x, y int) color.Color { |
| if !(Point{x, y}.In(p.Rect)) { |
| return color.YCbCr{} |
| } |
| yi := p.YOffset(x, y) |
| ci := p.COffset(x, y) |
| return color.YCbCr{ |
| p.Y[yi], |
| p.Cb[ci], |
| p.Cr[ci], |
| } |
| } |
| |
| // YOffset returns the index of the first element of Y that corresponds to |
| // the pixel at (x, y). |
| func (p *YCbCr) YOffset(x, y int) int { |
| return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X) |
| } |
| |
| // COffset returns the index of the first element of Cb or Cr that corresponds |
| // to the pixel at (x, y). |
| func (p *YCbCr) COffset(x, y int) int { |
| switch p.SubsampleRatio { |
| case YCbCrSubsampleRatio422: |
| return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2) |
| case YCbCrSubsampleRatio420: |
| return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2) |
| case YCbCrSubsampleRatio440: |
| return (y/2-p.Rect.Min.Y/2)*p.CStride + (x - p.Rect.Min.X) |
| } |
| // Default to 4:4:4 subsampling. |
| return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) |
| } |
| |
| // SubImage returns an image representing the portion of the image p visible |
| // through r. The returned value shares pixels with the original image. |
| func (p *YCbCr) SubImage(r Rectangle) Image { |
| r = r.Intersect(p.Rect) |
| // If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside |
| // either r1 or r2 if the intersection is empty. Without explicitly checking for |
| // this, the Pix[i:] expression below can panic. |
| if r.Empty() { |
| return &YCbCr{ |
| SubsampleRatio: p.SubsampleRatio, |
| } |
| } |
| yi := p.YOffset(r.Min.X, r.Min.Y) |
| ci := p.COffset(r.Min.X, r.Min.Y) |
| return &YCbCr{ |
| Y: p.Y[yi:], |
| Cb: p.Cb[ci:], |
| Cr: p.Cr[ci:], |
| SubsampleRatio: p.SubsampleRatio, |
| YStride: p.YStride, |
| CStride: p.CStride, |
| Rect: r, |
| } |
| } |
| |
| func (p *YCbCr) Opaque() bool { |
| return true |
| } |
| |
| // NewYCbCr returns a new YCbCr with the given bounds and subsample ratio. |
| func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { |
| w, h, cw, ch := r.Dx(), r.Dy(), 0, 0 |
| switch subsampleRatio { |
| case YCbCrSubsampleRatio422: |
| cw = (r.Max.X+1)/2 - r.Min.X/2 |
| ch = h |
| case YCbCrSubsampleRatio420: |
| cw = (r.Max.X+1)/2 - r.Min.X/2 |
| ch = (r.Max.Y+1)/2 - r.Min.Y/2 |
| case YCbCrSubsampleRatio440: |
| cw = w |
| ch = (r.Max.Y+1)/2 - r.Min.Y/2 |
| default: |
| // Default to 4:4:4 subsampling. |
| cw = w |
| ch = h |
| } |
| b := make([]byte, w*h+2*cw*ch) |
| return &YCbCr{ |
| Y: b[:w*h], |
| Cb: b[w*h+0*cw*ch : w*h+1*cw*ch], |
| Cr: b[w*h+1*cw*ch : w*h+2*cw*ch], |
| SubsampleRatio: subsampleRatio, |
| YStride: w, |
| CStride: cw, |
| Rect: r, |
| } |
| } |