From af92e385667da3fc91ac7f9f0867a56c111110b8 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 25 Jan 2012 21:54:22 +0000 Subject: libgo: Update to weekly.2012-01-20. From-SVN: r183540 --- libgo/go/image/color/color.go | 63 +++++++++--------- libgo/go/image/color/ycbcr.go | 12 ++-- libgo/go/image/draw/draw.go | 52 +++++++-------- libgo/go/image/geom.go | 2 +- libgo/go/image/image.go | 144 +++++++++++++++++++++++++++++------------- libgo/go/image/jpeg/reader.go | 15 +---- libgo/go/image/tiff/reader.go | 12 ++-- libgo/go/image/ycbcr.go | 105 +++++++++++++++++++++++------- libgo/go/image/ycbcr_test.go | 104 ++++++++++++++++++++++++++++++ 9 files changed, 355 insertions(+), 154 deletions(-) create mode 100644 libgo/go/image/ycbcr_test.go (limited to 'libgo/go/image') diff --git a/libgo/go/image/color/color.go b/libgo/go/image/color/color.go index 2948db7f38c..29a7b8a4001 100644 --- a/libgo/go/image/color/color.go +++ b/libgo/go/image/color/color.go @@ -152,26 +152,35 @@ func (m *modelFunc) Convert(c Color) Color { return m.f(c) } -// RGBAModel is the Model for RGBA colors. -var RGBAModel Model = ModelFunc(func(c Color) Color { +// Models for the standard color types. +var ( + RGBAModel Model = ModelFunc(rgbaModel) + RGBA64Model Model = ModelFunc(rgba64Model) + NRGBAModel Model = ModelFunc(nrgbaModel) + NRGBA64Model Model = ModelFunc(nrgba64Model) + AlphaModel Model = ModelFunc(alphaModel) + Alpha16Model Model = ModelFunc(alpha16Model) + GrayModel Model = ModelFunc(grayModel) + Gray16Model Model = ModelFunc(gray16Model) +) + +func rgbaModel(c Color) Color { if _, ok := c.(RGBA); ok { return c } r, g, b, a := c.RGBA() return RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -}) +} -// RGBAModel is the Model for RGBA64 colors. -var RGBA64Model Model = ModelFunc(func(c Color) Color { +func rgba64Model(c Color) Color { if _, ok := c.(RGBA64); ok { return c } r, g, b, a := c.RGBA() return RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} -}) +} -// NRGBAModel is the Model for NRGBA colors. -var NRGBAModel Model = ModelFunc(func(c Color) Color { +func nrgbaModel(c Color) Color { if _, ok := c.(NRGBA); ok { return c } @@ -187,10 +196,9 @@ var NRGBAModel Model = ModelFunc(func(c Color) Color { g = (g * 0xffff) / a b = (b * 0xffff) / a return NRGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} -}) +} -// NRGBAModel is the Model for NRGBA64 colors. -var NRGBA64Model Model = ModelFunc(func(c Color) Color { +func nrgba64Model(c Color) Color { if _, ok := c.(NRGBA64); ok { return c } @@ -206,45 +214,41 @@ var NRGBA64Model Model = ModelFunc(func(c Color) Color { g = (g * 0xffff) / a b = (b * 0xffff) / a return NRGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} -}) +} -// AlphaModel is the Model for Alpha colors. -var AlphaModel Model = ModelFunc(func(c Color) Color { +func alphaModel(c Color) Color { if _, ok := c.(Alpha); ok { return c } _, _, _, a := c.RGBA() return Alpha{uint8(a >> 8)} -}) +} -// Alpha16Model is the Model for Alpha16 colors. -var Alpha16Model Model = ModelFunc(func(c Color) Color { +func alpha16Model(c Color) Color { if _, ok := c.(Alpha16); ok { return c } _, _, _, a := c.RGBA() return Alpha16{uint16(a)} -}) +} -// GrayModel is the Model for Gray colors. -var GrayModel Model = ModelFunc(func(c Color) Color { +func grayModel(c Color) Color { if _, ok := c.(Gray); ok { return c } r, g, b, _ := c.RGBA() y := (299*r + 587*g + 114*b + 500) / 1000 return Gray{uint8(y >> 8)} -}) +} -// Gray16Model is the Model for Gray16 colors. -var Gray16Model Model = ModelFunc(func(c Color) Color { +func gray16Model(c Color) Color { if _, ok := c.(Gray16); ok { return c } r, g, b, _ := c.RGBA() y := (299*r + 587*g + 114*b + 500) / 1000 return Gray16{uint16(y)} -}) +} // Palette is a palette of colors. type Palette []Color @@ -290,13 +294,10 @@ func (p Palette) Index(c Color) int { return ret } +// Standard colors. var ( - // Black is an opaque black Color. - Black = Gray16{0} - // White is an opaque white Color. - White = Gray16{0xffff} - // Transparent is a fully transparent Color. + Black = Gray16{0} + White = Gray16{0xffff} Transparent = Alpha16{0} - // Opaque is a fully opaque Color. - Opaque = Alpha16{0xffff} + Opaque = Alpha16{0xffff} ) diff --git a/libgo/go/image/color/ycbcr.go b/libgo/go/image/color/ycbcr.go index c79816dd45a..4c2f29ea021 100644 --- a/libgo/go/image/color/ycbcr.go +++ b/libgo/go/image/color/ycbcr.go @@ -4,8 +4,7 @@ package color -// RGBToYCbCr converts an RGB triple to a Y'CbCr triple. All components lie -// within the range [0, 255]. +// RGBToYCbCr converts an RGB triple to a Y'CbCr triple. func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { // The JFIF specification says: // Y' = 0.2990*R + 0.5870*G + 0.1140*B @@ -36,8 +35,7 @@ func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) { return uint8(yy), uint8(cb), uint8(cr) } -// YCbCrToRGB converts a Y'CbCr triple to an RGB triple. All components lie -// within the range [0, 255]. +// YCbCrToRGB converts a Y'CbCr triple to an RGB triple. func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) { // The JFIF specification says: // R = Y' + 1.40200*(Cr-128) @@ -89,11 +87,13 @@ func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) { } // YCbCrModel is the Model for Y'CbCr colors. -var YCbCrModel Model = ModelFunc(func(c Color) Color { +var YCbCrModel Model = ModelFunc(yCbCrModel) + +func yCbCrModel(c Color) Color { if _, ok := c.(YCbCr); ok { return c } r, g, b, _ := c.RGBA() y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8)) return YCbCr{y, u, v} -}) +} diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go index 228ed6e719c..3b6679f7c7e 100644 --- a/libgo/go/image/draw/draw.go +++ b/libgo/go/image/draw/draw.go @@ -171,7 +171,7 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { sr, sg, sb, sa := src.RGBA() // The 0x101 is here for the same reason as in drawRGBA. a := (m - sa) * 0x101 - i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 + i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for y := r.Min.Y; y != r.Max.Y; y++ { for i := i0; i < i1; i += 4 { @@ -195,7 +195,7 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { // The built-in copy function is faster than a straightforward for loop to fill the destination with // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and // then use the first row as the slice source for the remaining rows. - i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 + i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for i := i0; i < i1; i += 4 { dst.Pix[i+0] = uint8(sr >> 8) @@ -213,8 +213,8 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { dx, dy := r.Dx(), r.Dy() - d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 - s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + (sp.X-src.Rect.Min.X)*4 + d0 := dst.PixOffset(r.Min.X, r.Min.Y) + s0 := src.PixOffset(sp.X, sp.Y) var ( ddelta, sdelta int i0, i1, idelta int @@ -261,8 +261,8 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image. func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { n, dy := 4*r.Dx(), r.Dy() - d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 - s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + (sp.X-src.Rect.Min.X)*4 + d0 := dst.PixOffset(r.Min.X, r.Min.Y) + s0 := src.PixOffset(sp.X, sp.Y) var ddelta, sdelta int if r.Min.Y <= sp.Y { ddelta = dst.Stride @@ -348,9 +348,6 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) { // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil // (i.e. fully opaque) then the op is effectively always Src. - var ( - yy, cb, cr uint8 - ) x0 := (r.Min.X - dst.Rect.Min.X) * 4 x1 := (r.Max.X - dst.Rect.Min.X) * 4 y0 := r.Min.Y - dst.Rect.Min.Y @@ -359,12 +356,11 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po case image.YCbCrSubsampleRatio422: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] - for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 { - i := sx / 2 - yy = src.Y[sy*src.YStride+sx] - cb = src.Cb[sy*src.CStride+i] - cr = src.Cr[sy*src.CStride+i] - rr, gg, bb := color.YCbCrToRGB(yy, cb, cr) + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + ci := ciBase + sx/2 + rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb @@ -374,12 +370,11 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po case image.YCbCrSubsampleRatio420: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] - for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 { - i, j := sx/2, sy/2 - yy = src.Y[sy*src.YStride+sx] - cb = src.Cb[j*src.CStride+i] - cr = src.Cr[j*src.CStride+i] - rr, gg, bb := color.YCbCrToRGB(yy, cb, cr) + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 + for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { + ci := ciBase + sx/2 + rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb @@ -390,11 +385,10 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po // Default to 4:4:4 subsampling. for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] - for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 { - yy = src.Y[sy*src.YStride+sx] - cb = src.Cb[sy*src.CStride+sx] - cr = src.Cr[sy*src.CStride+sx] - rr, gg, bb := color.YCbCrToRGB(yy, cb, cr) + yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) + ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) + for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { + rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb @@ -405,9 +399,9 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Po } func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { - i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4 + i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 - mi0 := (mp.Y-mask.Rect.Min.Y)*mask.Stride + mp.X - mask.Rect.Min.X + mi0 := mask.PixOffset(mp.X, mp.Y) sr, sg, sb, sa := src.RGBA() for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 { for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 { @@ -451,7 +445,7 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin sx0 := sp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X sx1 := sx0 + (x1 - x0) - i0 := (y0-dst.Rect.Min.Y)*dst.Stride + (x0-dst.Rect.Min.X)*4 + i0 := dst.PixOffset(x0, y0) di := dx * 4 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { diff --git a/libgo/go/image/geom.go b/libgo/go/image/geom.go index 667aee6259e..e123483314c 100644 --- a/libgo/go/image/geom.go +++ b/libgo/go/image/geom.go @@ -112,7 +112,7 @@ func (r Rectangle) Add(p Point) Rectangle { } } -// Add returns the rectangle r translated by -p. +// Sub returns the rectangle r translated by -p. func (r Rectangle) Sub(p Point) Rectangle { return Rectangle{ Point{r.Min.X - p.X, r.Min.Y - p.Y}, diff --git a/libgo/go/image/image.go b/libgo/go/image/image.go index a0dd930c7c0..9dd676acedd 100644 --- a/libgo/go/image/image.go +++ b/libgo/go/image/image.go @@ -61,15 +61,21 @@ func (p *RGBA) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.RGBA{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) return color.RGBA{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *RGBA) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 +} + func (p *RGBA) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) c1 := color.RGBAModel.Convert(c).(color.RGBA) p.Pix[i+0] = c1.R p.Pix[i+1] = c1.G @@ -81,7 +87,7 @@ func (p *RGBA) SetRGBA(x, y int, c color.RGBA) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) p.Pix[i+0] = c.R p.Pix[i+1] = c.G p.Pix[i+2] = c.B @@ -98,7 +104,7 @@ func (p *RGBA) SubImage(r Rectangle) Image { if r.Empty() { return &RGBA{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*4 + i := p.PixOffset(r.Min.X, r.Min.Y) return &RGBA{ Pix: p.Pix[i:], Stride: p.Stride, @@ -124,7 +130,7 @@ func (p *RGBA) Opaque() bool { return true } -// NewRGBA returns a new RGBA with the given width and height. +// NewRGBA returns a new RGBA with the given bounds. func NewRGBA(r Rectangle) *RGBA { w, h := r.Dx(), r.Dy() buf := make([]uint8, 4*w*h) @@ -150,7 +156,7 @@ func (p *RGBA64) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.RGBA64{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) return color.RGBA64{ uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]), uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]), @@ -159,11 +165,17 @@ func (p *RGBA64) At(x, y int) color.Color { } } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *RGBA64) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 +} + func (p *RGBA64) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) c1 := color.RGBA64Model.Convert(c).(color.RGBA64) p.Pix[i+0] = uint8(c1.R >> 8) p.Pix[i+1] = uint8(c1.R) @@ -179,7 +191,7 @@ func (p *RGBA64) SetRGBA64(x, y int, c color.RGBA64) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) p.Pix[i+0] = uint8(c.R >> 8) p.Pix[i+1] = uint8(c.R) p.Pix[i+2] = uint8(c.G >> 8) @@ -200,7 +212,7 @@ func (p *RGBA64) SubImage(r Rectangle) Image { if r.Empty() { return &RGBA64{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*8 + i := p.PixOffset(r.Min.X, r.Min.Y) return &RGBA64{ Pix: p.Pix[i:], Stride: p.Stride, @@ -226,7 +238,7 @@ func (p *RGBA64) Opaque() bool { return true } -// NewRGBA64 returns a new RGBA64 with the given width and height. +// NewRGBA64 returns a new RGBA64 with the given bounds. func NewRGBA64(r Rectangle) *RGBA64 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) @@ -252,15 +264,21 @@ func (p *NRGBA) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.NRGBA{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) return color.NRGBA{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *NRGBA) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 +} + func (p *NRGBA) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) c1 := color.NRGBAModel.Convert(c).(color.NRGBA) p.Pix[i+0] = c1.R p.Pix[i+1] = c1.G @@ -272,7 +290,7 @@ func (p *NRGBA) SetNRGBA(x, y int, c color.NRGBA) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4 + i := p.PixOffset(x, y) p.Pix[i+0] = c.R p.Pix[i+1] = c.G p.Pix[i+2] = c.B @@ -289,7 +307,7 @@ func (p *NRGBA) SubImage(r Rectangle) Image { if r.Empty() { return &NRGBA{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*4 + i := p.PixOffset(r.Min.X, r.Min.Y) return &NRGBA{ Pix: p.Pix[i:], Stride: p.Stride, @@ -315,7 +333,7 @@ func (p *NRGBA) Opaque() bool { return true } -// NewNRGBA returns a new NRGBA with the given width and height. +// NewNRGBA returns a new NRGBA with the given bounds. func NewNRGBA(r Rectangle) *NRGBA { w, h := r.Dx(), r.Dy() pix := make([]uint8, 4*w*h) @@ -341,7 +359,7 @@ func (p *NRGBA64) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.NRGBA64{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) return color.NRGBA64{ uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]), uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]), @@ -350,11 +368,17 @@ func (p *NRGBA64) At(x, y int) color.Color { } } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *NRGBA64) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 +} + func (p *NRGBA64) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) c1 := color.NRGBA64Model.Convert(c).(color.NRGBA64) p.Pix[i+0] = uint8(c1.R >> 8) p.Pix[i+1] = uint8(c1.R) @@ -370,7 +394,7 @@ func (p *NRGBA64) SetNRGBA64(x, y int, c color.NRGBA64) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8 + i := p.PixOffset(x, y) p.Pix[i+0] = uint8(c.R >> 8) p.Pix[i+1] = uint8(c.R) p.Pix[i+2] = uint8(c.G >> 8) @@ -391,7 +415,7 @@ func (p *NRGBA64) SubImage(r Rectangle) Image { if r.Empty() { return &NRGBA64{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*8 + i := p.PixOffset(r.Min.X, r.Min.Y) return &NRGBA64{ Pix: p.Pix[i:], Stride: p.Stride, @@ -417,7 +441,7 @@ func (p *NRGBA64) Opaque() bool { return true } -// NewNRGBA64 returns a new NRGBA64 with the given width and height. +// NewNRGBA64 returns a new NRGBA64 with the given bounds. func NewNRGBA64(r Rectangle) *NRGBA64 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 8*w*h) @@ -443,15 +467,21 @@ func (p *Alpha) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.Alpha{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) return color.Alpha{p.Pix[i]} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *Alpha) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 +} + func (p *Alpha) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = color.AlphaModel.Convert(c).(color.Alpha).A } @@ -459,7 +489,7 @@ func (p *Alpha) SetAlpha(x, y int, c color.Alpha) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = c.A } @@ -473,7 +503,7 @@ func (p *Alpha) SubImage(r Rectangle) Image { if r.Empty() { return &Alpha{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1 + i := p.PixOffset(r.Min.X, r.Min.Y) return &Alpha{ Pix: p.Pix[i:], Stride: p.Stride, @@ -499,7 +529,7 @@ func (p *Alpha) Opaque() bool { return true } -// NewAlpha returns a new Alpha with the given width and height. +// NewAlpha returns a new Alpha with the given bounds. func NewAlpha(r Rectangle) *Alpha { w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) @@ -525,15 +555,21 @@ func (p *Alpha16) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.Alpha16{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) return color.Alpha16{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *Alpha16) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 +} + func (p *Alpha16) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) c1 := color.Alpha16Model.Convert(c).(color.Alpha16) p.Pix[i+0] = uint8(c1.A >> 8) p.Pix[i+1] = uint8(c1.A) @@ -543,7 +579,7 @@ func (p *Alpha16) SetAlpha16(x, y int, c color.Alpha16) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) p.Pix[i+0] = uint8(c.A >> 8) p.Pix[i+1] = uint8(c.A) } @@ -558,7 +594,7 @@ func (p *Alpha16) SubImage(r Rectangle) Image { if r.Empty() { return &Alpha16{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*2 + i := p.PixOffset(r.Min.X, r.Min.Y) return &Alpha16{ Pix: p.Pix[i:], Stride: p.Stride, @@ -584,7 +620,7 @@ func (p *Alpha16) Opaque() bool { return true } -// NewAlpha16 returns a new Alpha16 with the given width and height. +// NewAlpha16 returns a new Alpha16 with the given bounds. func NewAlpha16(r Rectangle) *Alpha16 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) @@ -610,15 +646,21 @@ func (p *Gray) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.Gray{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) return color.Gray{p.Pix[i]} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *Gray) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 +} + func (p *Gray) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = color.GrayModel.Convert(c).(color.Gray).Y } @@ -626,7 +668,7 @@ func (p *Gray) SetGray(x, y int, c color.Gray) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = c.Y } @@ -640,7 +682,7 @@ func (p *Gray) SubImage(r Rectangle) Image { if r.Empty() { return &Gray{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1 + i := p.PixOffset(r.Min.X, r.Min.Y) return &Gray{ Pix: p.Pix[i:], Stride: p.Stride, @@ -653,7 +695,7 @@ func (p *Gray) Opaque() bool { return true } -// NewGray returns a new Gray with the given width and height. +// NewGray returns a new Gray with the given bounds. func NewGray(r Rectangle) *Gray { w, h := r.Dx(), r.Dy() pix := make([]uint8, 1*w*h) @@ -679,15 +721,21 @@ func (p *Gray16) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return color.Gray16{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) return color.Gray16{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])} } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *Gray16) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 +} + func (p *Gray16) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) c1 := color.Gray16Model.Convert(c).(color.Gray16) p.Pix[i+0] = uint8(c1.Y >> 8) p.Pix[i+1] = uint8(c1.Y) @@ -697,7 +745,7 @@ func (p *Gray16) SetGray16(x, y int, c color.Gray16) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2 + i := p.PixOffset(x, y) p.Pix[i+0] = uint8(c.Y >> 8) p.Pix[i+1] = uint8(c.Y) } @@ -712,7 +760,7 @@ func (p *Gray16) SubImage(r Rectangle) Image { if r.Empty() { return &Gray16{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*2 + i := p.PixOffset(r.Min.X, r.Min.Y) return &Gray16{ Pix: p.Pix[i:], Stride: p.Stride, @@ -725,7 +773,7 @@ func (p *Gray16) Opaque() bool { return true } -// NewGray16 returns a new Gray16 with the given width and height. +// NewGray16 returns a new Gray16 with the given bounds. func NewGray16(r Rectangle) *Gray16 { w, h := r.Dx(), r.Dy() pix := make([]uint8, 2*w*h) @@ -756,15 +804,21 @@ func (p *Paletted) At(x, y int) color.Color { if !(Point{x, y}.In(p.Rect)) { return p.Palette[0] } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) return p.Palette[p.Pix[i]] } +// PixOffset returns the index of the first element of Pix that corresponds to +// the pixel at (x, y). +func (p *Paletted) PixOffset(x, y int) int { + return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*1 +} + func (p *Paletted) Set(x, y int, c color.Color) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = uint8(p.Palette.Index(c)) } @@ -772,7 +826,7 @@ func (p *Paletted) ColorIndexAt(x, y int) uint8 { if !(Point{x, y}.In(p.Rect)) { return 0 } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) return p.Pix[i] } @@ -780,7 +834,7 @@ func (p *Paletted) SetColorIndex(x, y int, index uint8) { if !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) + i := p.PixOffset(x, y) p.Pix[i] = index } @@ -796,7 +850,7 @@ func (p *Paletted) SubImage(r Rectangle) Image { Palette: p.Palette, } } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1 + i := p.PixOffset(r.Min.X, r.Min.Y) return &Paletted{ Pix: p.Pix[i:], Stride: p.Stride, diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go index ed1a962586d..2e412ad1716 100644 --- a/libgo/go/image/jpeg/reader.go +++ b/libgo/go/image/jpeg/reader.go @@ -203,8 +203,7 @@ func (d *decoder) makeImg(h0, v0, mxx, myy int) { return } var subsampleRatio image.YCbCrSubsampleRatio - n := h0 * v0 - switch n { + switch h0 * v0 { case 1: subsampleRatio = image.YCbCrSubsampleRatio444 case 2: @@ -214,16 +213,8 @@ func (d *decoder) makeImg(h0, v0, mxx, myy int) { default: panic("unreachable") } - b := make([]byte, mxx*myy*(1*8*8*n+2*8*8)) - d.img3 = &image.YCbCr{ - Y: b[mxx*myy*(0*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+0*8*8)], - Cb: b[mxx*myy*(1*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+1*8*8)], - Cr: b[mxx*myy*(1*8*8*n+1*8*8) : mxx*myy*(1*8*8*n+2*8*8)], - SubsampleRatio: subsampleRatio, - YStride: mxx * 8 * h0, - CStride: mxx * 8, - Rect: image.Rect(0, 0, d.width, d.height), - } + m := image.NewYCbCr(image.Rect(0, 0, 8*h0*mxx, 8*v0*myy), subsampleRatio) + d.img3 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.YCbCr) } // Specified in section B.2.3. diff --git a/libgo/go/image/tiff/reader.go b/libgo/go/image/tiff/reader.go index 00a51db9f4c..dc5a87a9fb9 100644 --- a/libgo/go/image/tiff/reader.go +++ b/libgo/go/image/tiff/reader.go @@ -223,8 +223,8 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error { } case mRGB: img := dst.(*image.RGBA) - min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 - max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 + min := img.PixOffset(0, ymin) + max := img.PixOffset(0, ymax) var off int for i := min; i < max; i += 4 { img.Pix[i+0] = d.buf[off+0] @@ -235,16 +235,16 @@ func (d *decoder) decode(dst image.Image, ymin, ymax int) error { } case mNRGBA: img := dst.(*image.NRGBA) - min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 - max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 + min := img.PixOffset(0, ymin) + max := img.PixOffset(0, ymax) if len(d.buf) != max-min { return FormatError("short data strip") } copy(img.Pix[min:max], d.buf) case mRGBA: img := dst.(*image.RGBA) - min := (ymin-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 - max := (ymax-img.Rect.Min.Y)*img.Stride - img.Rect.Min.X*4 + min := img.PixOffset(0, ymin) + max := img.PixOffset(0, ymax) if len(d.buf) != max-min { return FormatError("short data strip") } diff --git a/libgo/go/image/ycbcr.go b/libgo/go/image/ycbcr.go index 81f3c9fe03d..c1a0b666f83 100644 --- a/libgo/go/image/ycbcr.go +++ b/libgo/go/image/ycbcr.go @@ -17,6 +17,18 @@ const ( YCbCrSubsampleRatio420 ) +func (s YCbCrSubsampleRatio) String() string { + switch s { + case YCbCrSubsampleRatio444: + return "YCbCrSubsampleRatio444" + case YCbCrSubsampleRatio422: + return "YCbCrSubsampleRatio422" + case YCbCrSubsampleRatio420: + return "YCbCrSubsampleRatio420" + } + 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. @@ -28,9 +40,7 @@ const ( // 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. type YCbCr struct { - Y []uint8 - Cb []uint8 - Cr []uint8 + Y, Cb, Cr []uint8 YStride int CStride int SubsampleRatio YCbCrSubsampleRatio @@ -49,39 +59,86 @@ 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: - i := x / 2 - return color.YCbCr{ - p.Y[y*p.YStride+x], - p.Cb[y*p.CStride+i], - p.Cr[y*p.CStride+i], - } + return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2) case YCbCrSubsampleRatio420: - i, j := x/2, y/2 - return color.YCbCr{ - p.Y[y*p.YStride+x], - p.Cb[j*p.CStride+i], - p.Cr[j*p.CStride+i], - } + return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2) } // Default to 4:4:4 subsampling. - return color.YCbCr{ - p.Y[y*p.YStride+x], - p.Cb[y*p.CStride+x], - p.Cr[y*p.CStride+x], - } + 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 { - q := new(YCbCr) - *q = *p - q.Rect = q.Rect.Intersect(r) - return q + 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 + 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, + } +} diff --git a/libgo/go/image/ycbcr_test.go b/libgo/go/image/ycbcr_test.go new file mode 100644 index 00000000000..eb8b1950bfe --- /dev/null +++ b/libgo/go/image/ycbcr_test.go @@ -0,0 +1,104 @@ +// Copyright 2012 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_test + +import ( + . "image" + "image/color" + "testing" +) + +func TestYCbCr(t *testing.T) { + rects := []Rectangle{ + Rect(0, 0, 16, 16), + Rect(1, 0, 16, 16), + Rect(0, 1, 16, 16), + Rect(1, 1, 16, 16), + Rect(1, 1, 15, 16), + Rect(1, 1, 16, 15), + Rect(1, 1, 15, 15), + Rect(2, 3, 14, 15), + Rect(7, 0, 7, 16), + Rect(0, 8, 16, 8), + Rect(0, 0, 10, 11), + Rect(5, 6, 16, 16), + Rect(7, 7, 8, 8), + Rect(7, 8, 8, 9), + Rect(8, 7, 9, 8), + Rect(8, 8, 9, 9), + Rect(7, 7, 17, 17), + Rect(8, 8, 17, 17), + Rect(9, 9, 17, 17), + Rect(10, 10, 17, 17), + } + subsampleRatios := []YCbCrSubsampleRatio{ + YCbCrSubsampleRatio444, + YCbCrSubsampleRatio422, + YCbCrSubsampleRatio420, + } + deltas := []Point{ + Pt(0, 0), + Pt(1000, 1001), + Pt(5001, -400), + Pt(-701, -801), + } + for _, r := range rects { + for _, subsampleRatio := range subsampleRatios { + for _, delta := range deltas { + testYCbCr(t, r, subsampleRatio, delta) + } + } + } +} + +func testYCbCr(t *testing.T, r Rectangle, subsampleRatio YCbCrSubsampleRatio, delta Point) { + // Create a YCbCr image m, whose bounds are r translated by (delta.X, delta.Y). + r1 := r.Add(delta) + m := NewYCbCr(r1, subsampleRatio) + + // Test that the image buffer is reasonably small even if (delta.X, delta.Y) is far from the origin. + if len(m.Y) > 100*100 { + t.Errorf("r=%v, subsampleRatio=%v, delta=%v: image buffer is too large", + r, subsampleRatio, delta) + return + } + + // Initialize m's pixels. For 422 and 420 subsampling, some of the Cb and Cr elements + // will be set multiple times. That's OK. We just want to avoid a uniform image. + for y := r1.Min.Y; y < r1.Max.Y; y++ { + for x := r1.Min.X; x < r1.Max.X; x++ { + yi := m.YOffset(x, y) + ci := m.COffset(x, y) + m.Y[yi] = uint8(16*y + x) + m.Cb[ci] = uint8(y + 16*x) + m.Cr[ci] = uint8(y + 16*x) + } + } + + // Make various sub-images of m. + for y0 := delta.Y + 3; y0 < delta.Y+7; y0++ { + for y1 := delta.Y + 8; y1 < delta.Y+13; y1++ { + for x0 := delta.X + 3; x0 < delta.X+7; x0++ { + for x1 := delta.X + 8; x1 < delta.X+13; x1++ { + subRect := Rect(x0, y0, x1, y1) + sub := m.SubImage(subRect).(*YCbCr) + + // For each point in the sub-image's bounds, check that m.At(x, y) equals sub.At(x, y). + for y := sub.Rect.Min.Y; y < sub.Rect.Max.Y; y++ { + for x := sub.Rect.Min.X; x < sub.Rect.Max.X; x++ { + color0 := m.At(x, y).(color.YCbCr) + color1 := sub.At(x, y).(color.YCbCr) + if color0 != color1 { + t.Errorf("r=%v, subsampleRatio=%v, delta=%v, x=%d, y=%d, color0=%v, color1=%v", + r, subsampleRatio, delta, x, y, color0, color1) + return + } + } + } + } + } + } + } +} -- cgit v1.2.3