summaryrefslogtreecommitdiffstats
path: root/src/image/ycbcr.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/image/ycbcr.go')
-rw-r--r--src/image/ycbcr.go329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/image/ycbcr.go b/src/image/ycbcr.go
new file mode 100644
index 0000000..78f5ebe
--- /dev/null
+++ b/src/image/ycbcr.go
@@ -0,0 +1,329 @@
+// 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) RGBA64At(x, y int) color.RGBA64 {
+ r, g, b, a := p.YCbCrAt(x, y).RGBA()
+ return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
+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)
+
+ // totalLength should be the same as i2, below, for a valid Rectangle r.
+ totalLength := add2NonNeg(
+ mul3NonNeg(1, w, h),
+ mul3NonNeg(2, cw, ch),
+ )
+ if totalLength < 0 {
+ panic("image: NewYCbCr Rectangle has huge or negative dimensions")
+ }
+
+ 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) RGBA64At(x, y int) color.RGBA64 {
+ r, g, b, a := p.NYCbCrAAt(x, y).RGBA()
+ return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
+}
+
+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)
+
+ // totalLength should be the same as i3, below, for a valid Rectangle r.
+ totalLength := add2NonNeg(
+ mul3NonNeg(2, w, h),
+ mul3NonNeg(2, cw, ch),
+ )
+ if totalLength < 0 {
+ panic("image: NewNYCbCrA Rectangle has huge or negative dimension")
+ }
+
+ 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,
+ }
+}