| // 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 | |
| YCbCrSubsampleRatio411 | |
| YCbCrSubsampleRatio410 | |
| ) | |
| func (s YCbCrSubsampleRatio) String() string { | |
| switch s { | |
| case YCbCrSubsampleRatio444: | |
| return "YCbCrSubsampleRatio444" | |
| case YCbCrSubsampleRatio422: | |
| return "YCbCrSubsampleRatio422" | |
| case YCbCrSubsampleRatio420: | |
| return "YCbCrSubsampleRatio420" | |
| case YCbCrSubsampleRatio440: | |
| return "YCbCrSubsampleRatio440" | |
| case YCbCrSubsampleRatio411: | |
| return "YCbCrSubsampleRatio411" | |
| case YCbCrSubsampleRatio410: | |
| return "YCbCrSubsampleRatio410" | |
| } | |
| 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. | |
| // For 4:1:1, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/4. | |
| // For 4:1:0, CStride == YStride/4 && len(Cb) == len(Cr) == len(Y)/8. | |
| 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 { | |
| return p.YCbCrAt(x, y) | |
| } | |
| func (p *YCbCr) YCbCrAt(x, y int) color.YCbCr { | |
| 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) | |
| case YCbCrSubsampleRatio411: | |
| return (y-p.Rect.Min.Y)*p.CStride + (x/4 - p.Rect.Min.X/4) | |
| case YCbCrSubsampleRatio410: | |
| return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/4 - p.Rect.Min.X/4) | |
| } | |
| // 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 | |
| } | |
| func yCbCrSize(r Rectangle, subsampleRatio YCbCrSubsampleRatio) (w, h, cw, ch int) { | |
| w, h = r.Dx(), r.Dy() | |
| 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 | |
| case YCbCrSubsampleRatio411: | |
| cw = (r.Max.X+3)/4 - r.Min.X/4 | |
| ch = h | |
| case YCbCrSubsampleRatio410: | |
| cw = (r.Max.X+3)/4 - r.Min.X/4 | |
| ch = (r.Max.Y+1)/2 - r.Min.Y/2 | |
| default: | |
| // Default to 4:4:4 subsampling. | |
| cw = w | |
| ch = h | |
| } | |
| return | |
| } | |
| // NewYCbCr returns a new YCbCr image with the given bounds and subsample | |
| // ratio. | |
| func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { | |
| w, h, cw, ch := yCbCrSize(r, subsampleRatio) | |
| i0 := w*h + 0*cw*ch | |
| i1 := w*h + 1*cw*ch | |
| i2 := w*h + 2*cw*ch | |
| b := make([]byte, i2) | |
| return &YCbCr{ | |
| Y: b[:i0:i0], | |
| Cb: b[i0:i1:i1], | |
| Cr: b[i1:i2:i2], | |
| SubsampleRatio: subsampleRatio, | |
| YStride: w, | |
| CStride: cw, | |
| Rect: r, | |
| } | |
| } | |
| // NYCbCrA is an in-memory image of non-alpha-premultiplied Y'CbCr-with-alpha | |
| // colors. A and AStride are analogous to the Y and YStride fields of the | |
| // embedded YCbCr. | |
| type NYCbCrA struct { | |
| YCbCr | |
| A []uint8 | |
| AStride int | |
| } | |
| func (p *NYCbCrA) ColorModel() color.Model { | |
| return color.NYCbCrAModel | |
| } | |
| func (p *NYCbCrA) At(x, y int) color.Color { | |
| return p.NYCbCrAAt(x, y) | |
| } | |
| func (p *NYCbCrA) NYCbCrAAt(x, y int) color.NYCbCrA { | |
| if !(Point{X: x, Y: y}.In(p.Rect)) { | |
| return color.NYCbCrA{} | |
| } | |
| yi := p.YOffset(x, y) | |
| ci := p.COffset(x, y) | |
| ai := p.AOffset(x, y) | |
| return color.NYCbCrA{ | |
| color.YCbCr{ | |
| Y: p.Y[yi], | |
| Cb: p.Cb[ci], | |
| Cr: p.Cr[ci], | |
| }, | |
| p.A[ai], | |
| } | |
| } | |
| // AOffset returns the index of the first element of A that corresponds to the | |
| // pixel at (x, y). | |
| func (p *NYCbCrA) AOffset(x, y int) int { | |
| return (y-p.Rect.Min.Y)*p.AStride + (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 *NYCbCrA) 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 &NYCbCrA{ | |
| YCbCr: YCbCr{ | |
| SubsampleRatio: p.SubsampleRatio, | |
| }, | |
| } | |
| } | |
| yi := p.YOffset(r.Min.X, r.Min.Y) | |
| ci := p.COffset(r.Min.X, r.Min.Y) | |
| ai := p.AOffset(r.Min.X, r.Min.Y) | |
| return &NYCbCrA{ | |
| YCbCr: YCbCr{ | |
| Y: p.Y[yi:], | |
| Cb: p.Cb[ci:], | |
| Cr: p.Cr[ci:], | |
| SubsampleRatio: p.SubsampleRatio, | |
| YStride: p.YStride, | |
| CStride: p.CStride, | |
| Rect: r, | |
| }, | |
| A: p.A[ai:], | |
| AStride: p.AStride, | |
| } | |
| } | |
| // Opaque scans the entire image and reports whether it is fully opaque. | |
| func (p *NYCbCrA) Opaque() bool { | |
| if p.Rect.Empty() { | |
| return true | |
| } | |
| i0, i1 := 0, p.Rect.Dx() | |
| for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { | |
| for _, a := range p.A[i0:i1] { | |
| if a != 0xff { | |
| return false | |
| } | |
| } | |
| i0 += p.AStride | |
| i1 += p.AStride | |
| } | |
| return true | |
| } | |
| // NewNYCbCrA returns a new NYCbCrA image with the given bounds and subsample | |
| // ratio. | |
| func NewNYCbCrA(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *NYCbCrA { | |
| w, h, cw, ch := yCbCrSize(r, subsampleRatio) | |
| i0 := 1*w*h + 0*cw*ch | |
| i1 := 1*w*h + 1*cw*ch | |
| i2 := 1*w*h + 2*cw*ch | |
| i3 := 2*w*h + 2*cw*ch | |
| b := make([]byte, i3) | |
| return &NYCbCrA{ | |
| YCbCr: YCbCr{ | |
| Y: b[:i0:i0], | |
| Cb: b[i0:i1:i1], | |
| Cr: b[i1:i2:i2], | |
| SubsampleRatio: subsampleRatio, | |
| YStride: w, | |
| CStride: cw, | |
| Rect: r, | |
| }, | |
| A: b[i2:], | |
| AStride: w, | |
| } | |
| } |