summaryrefslogtreecommitdiff
path: root/libgo/go/image
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-25 21:54:22 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-25 21:54:22 +0000
commitaf92e385667da3fc91ac7f9f0867a56c111110b8 (patch)
treec8e8990a2197e33f6fe50a28a16714aafe982102 /libgo/go/image
parentdf1304ee03f41aed179545d1e8b4684cfd22bbdf (diff)
libgo: Update to weekly.2012-01-20.
From-SVN: r183540
Diffstat (limited to 'libgo/go/image')
-rw-r--r--libgo/go/image/color/color.go63
-rw-r--r--libgo/go/image/color/ycbcr.go12
-rw-r--r--libgo/go/image/draw/draw.go52
-rw-r--r--libgo/go/image/geom.go2
-rw-r--r--libgo/go/image/image.go144
-rw-r--r--libgo/go/image/jpeg/reader.go15
-rw-r--r--libgo/go/image/tiff/reader.go12
-rw-r--r--libgo/go/image/ycbcr.go105
-rw-r--r--libgo/go/image/ycbcr_test.go104
9 files changed, 355 insertions, 154 deletions
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
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}