From: Nigel Tao Date: Sat, 10 Oct 2015 00:25:47 +0000 (+1100) Subject: image: add NYCbCrA types. X-Git-Tag: go1.6beta1~858 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c478c48597e2fb7c52ea39d1dde51448949860ce;p=gostls13.git image: add NYCbCrA types. Fixes #12722 Change-Id: I6a630d8b072ef2b1c63de941743148f8c96b8e5f Reviewed-on: https://go-review.googlesource.com/15671 Reviewed-by: Brad Fitzpatrick --- diff --git a/src/image/color/ycbcr.go b/src/image/color/ycbcr.go index 4bcb07dce2..0c2ba84c57 100644 --- a/src/image/color/ycbcr.go +++ b/src/image/color/ycbcr.go @@ -137,6 +137,46 @@ func yCbCrModel(c Color) Color { return YCbCr{y, u, v} } +// NYCbCrA represents a non-alpha-premultiplied Y'CbCr-with-alpha color, having +// 8 bits each for one luma, two chroma and one alpha component. +type NYCbCrA struct { + YCbCr + A uint8 +} + +func (c NYCbCrA) RGBA() (r, g, b, a uint32) { + r8, g8, b8 := YCbCrToRGB(c.Y, c.Cb, c.Cr) + a = uint32(c.A) * 0x101 + r = uint32(r8) * 0x101 * a / 0xffff + g = uint32(g8) * 0x101 * a / 0xffff + b = uint32(b8) * 0x101 * a / 0xffff + return +} + +// NYCbCrAModel is the Model for non-alpha-premultiplied Y'CbCr-with-alpha +// colors. +var NYCbCrAModel Model = ModelFunc(nYCbCrAModel) + +func nYCbCrAModel(c Color) Color { + switch c := c.(type) { + case NYCbCrA: + return c + case YCbCr: + return NYCbCrA{c, 0xff} + } + r, g, b, a := c.RGBA() + + // Convert from alpha-premultiplied to non-alpha-premultiplied. + if a != 0 { + r = (r * 0xffff) / a + g = (g * 0xffff) / a + b = (b * 0xffff) / a + } + + y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) + return NYCbCrA{YCbCr{Y: y, Cb: u, Cr: v}, uint8(a >> 8)} +} + // RGBToCMYK converts an RGB triple to a CMYK quadruple. func RGBToCMYK(r, g, b uint8) (uint8, uint8, uint8, uint8) { rr := uint32(r) diff --git a/src/image/image.go b/src/image/image.go index 20b64d78e1..bebb9f70fa 100644 --- a/src/image/image.go +++ b/src/image/image.go @@ -148,7 +148,7 @@ func (p *RGBA) Opaque() bool { return true } -// NewRGBA returns a new RGBA with the given bounds. +// NewRGBA returns a new RGBA image with the given bounds. func NewRGBA(r Rectangle) *RGBA { w, h := r.Dx(), r.Dy() buf := make([]uint8, 4*w*h) @@ -260,7 +260,7 @@ func (p *RGBA64) Opaque() bool { return true } -// NewRGBA64 returns a new RGBA64 with the given bounds. +// NewRGBA64 returns a new RGBA64 image with the given bounds. func NewRGBA64(r Rectangle) *RGBA64 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) @@ -359,7 +359,7 @@ func (p *NRGBA) Opaque() bool { return true } -// NewNRGBA returns a new NRGBA with the given bounds. +// NewNRGBA returns a new NRGBA image with the given bounds. func NewNRGBA(r Rectangle) *NRGBA { w, h := r.Dx(), r.Dy() pix := make([]uint8, 4*w*h) @@ -471,7 +471,7 @@ func (p *NRGBA64) Opaque() bool { return true } -// NewNRGBA64 returns a new NRGBA64 with the given bounds. +// NewNRGBA64 returns a new NRGBA64 image with the given bounds. func NewNRGBA64(r Rectangle) *NRGBA64 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) @@ -563,7 +563,7 @@ func (p *Alpha) Opaque() bool { return true } -// NewAlpha returns a new Alpha with the given bounds. +// NewAlpha returns a new Alpha image with the given bounds. func NewAlpha(r Rectangle) *Alpha { w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) @@ -658,7 +658,7 @@ func (p *Alpha16) Opaque() bool { return true } -// NewAlpha16 returns a new Alpha16 with the given bounds. +// NewAlpha16 returns a new Alpha16 image with the given bounds. func NewAlpha16(r Rectangle) *Alpha16 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) @@ -737,7 +737,7 @@ func (p *Gray) Opaque() bool { return true } -// NewGray returns a new Gray with the given bounds. +// NewGray returns a new Gray image with the given bounds. func NewGray(r Rectangle) *Gray { w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) @@ -819,7 +819,7 @@ func (p *Gray16) Opaque() bool { return true } -// NewGray16 returns a new Gray16 with the given bounds. +// NewGray16 returns a new Gray16 image with the given bounds. func NewGray16(r Rectangle) *Gray16 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) @@ -905,7 +905,7 @@ func (p *CMYK) Opaque() bool { return true } -// NewCMYK returns a new CMYK with the given bounds. +// NewCMYK returns a new CMYK image with the given bounds. func NewCMYK(r Rectangle) *CMYK { w, h := r.Dx(), r.Dy() buf := make([]uint8, 4*w*h) @@ -1014,7 +1014,8 @@ func (p *Paletted) Opaque() bool { return true } -// NewPaletted returns a new Paletted with the given width, height and palette. +// NewPaletted returns a new Paletted image with the given width, height and +// palette. func NewPaletted(r Rectangle, p color.Palette) *Paletted { w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) diff --git a/src/image/ycbcr.go b/src/image/ycbcr.go index 93c354b33b..71c0518a81 100644 --- a/src/image/ycbcr.go +++ b/src/image/ycbcr.go @@ -138,9 +138,8 @@ 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 +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 @@ -162,6 +161,13 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { 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 @@ -176,3 +182,117 @@ func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr { 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, + } +}