aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/image
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/image')
-rw-r--r--libgo/go/image/bmp/reader.go151
-rw-r--r--libgo/go/image/color.go24
-rw-r--r--libgo/go/image/decode_test.go68
-rw-r--r--libgo/go/image/draw/bench_test.go206
-rw-r--r--libgo/go/image/draw/clip_test.go193
-rw-r--r--libgo/go/image/draw/draw.go493
-rw-r--r--libgo/go/image/draw/draw_test.go354
-rw-r--r--libgo/go/image/geom.go19
-rw-r--r--libgo/go/image/gif/reader.go112
-rw-r--r--libgo/go/image/image.go554
-rw-r--r--libgo/go/image/image_test.go113
-rw-r--r--libgo/go/image/jpeg/idct.go124
-rw-r--r--libgo/go/image/jpeg/reader.go237
-rw-r--r--libgo/go/image/jpeg/writer.go36
-rw-r--r--libgo/go/image/png/writer.go90
-rw-r--r--libgo/go/image/png/writer_test.go73
-rw-r--r--libgo/go/image/testdata/video-001.5bpp.gifbin0 -> 6214 bytes
-rw-r--r--libgo/go/image/testdata/video-001.interlaced.gifbin0 -> 14142 bytes
-rw-r--r--libgo/go/image/testdata/video-005.gray.jpegbin0 -> 5618 bytes
-rw-r--r--libgo/go/image/testdata/video-005.gray.pngbin0 -> 14974 bytes
-rw-r--r--libgo/go/image/tiff/consts.go1
-rw-r--r--libgo/go/image/tiff/reader.go132
-rw-r--r--libgo/go/image/tiff/reader_test.go25
-rw-r--r--libgo/go/image/tiff/testdata/no_rps.tiffbin0 -> 1294 bytes
-rw-r--r--libgo/go/image/ycbcr/ycbcr.go11
25 files changed, 2509 insertions, 507 deletions
diff --git a/libgo/go/image/bmp/reader.go b/libgo/go/image/bmp/reader.go
new file mode 100644
index 0000000..357da1d
--- /dev/null
+++ b/libgo/go/image/bmp/reader.go
@@ -0,0 +1,151 @@
+// 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 bmp implements a BMP image decoder.
+//
+// The BMP specification is at http://www.digicamsoft.com/bmp/bmp.html.
+package bmp
+
+import (
+ "image"
+ "io"
+ "os"
+)
+
+// ErrUnsupported means that the input BMP image uses a valid but unsupported
+// feature.
+var ErrUnsupported = os.NewError("bmp: unsupported BMP image")
+
+func readUint16(b []byte) uint16 {
+ return uint16(b[0]) | uint16(b[1])<<8
+}
+
+func readUint32(b []byte) uint32 {
+ return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+}
+
+// decodePaletted reads an 8 bit-per-pixel BMP image from r.
+func decodePaletted(r io.Reader, c image.Config) (image.Image, os.Error) {
+ var tmp [4]byte
+ paletted := image.NewPaletted(c.Width, c.Height, c.ColorModel.(image.PalettedColorModel))
+ // BMP images are stored bottom-up rather than top-down.
+ for y := c.Height - 1; y >= 0; y-- {
+ p := paletted.Pix[y*paletted.Stride : y*paletted.Stride+c.Width]
+ _, err := io.ReadFull(r, p)
+ if err != nil {
+ return nil, err
+ }
+ // Each row is 4-byte aligned.
+ if c.Width%4 != 0 {
+ _, err := io.ReadFull(r, tmp[:4-c.Width%4])
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ return paletted, nil
+}
+
+// decodeRGBA reads a 24 bit-per-pixel BMP image from r.
+func decodeRGBA(r io.Reader, c image.Config) (image.Image, os.Error) {
+ rgba := image.NewRGBA(c.Width, c.Height)
+ // There are 3 bytes per pixel, and each row is 4-byte aligned.
+ b := make([]byte, (3*c.Width+3)&^3)
+ // BMP images are stored bottom-up rather than top-down.
+ for y := c.Height - 1; y >= 0; y-- {
+ _, err := io.ReadFull(r, b)
+ if err != nil {
+ return nil, err
+ }
+ p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width*4]
+ for i, j := 0, 0; i < len(p); i, j = i+4, j+3 {
+ // BMP images are stored in BGR order rather than RGB order.
+ p[i+0] = b[j+2]
+ p[i+1] = b[j+1]
+ p[i+2] = b[j+0]
+ p[i+3] = 0xFF
+ }
+ }
+ return rgba, nil
+}
+
+// Decode reads a BMP image from r and returns it as an image.Image.
+// Limitation: The file must be 8 or 24 bits per pixel.
+func Decode(r io.Reader) (image.Image, os.Error) {
+ c, err := DecodeConfig(r)
+ if err != nil {
+ return nil, err
+ }
+ if c.ColorModel == image.RGBAColorModel {
+ return decodeRGBA(r, c)
+ }
+ return decodePaletted(r, c)
+}
+
+// DecodeConfig returns the color model and dimensions of a BMP image without
+// decoding the entire image.
+// Limitation: The file must be 8 or 24 bits per pixel.
+func DecodeConfig(r io.Reader) (config image.Config, err os.Error) {
+ // We only support those BMP images that are a BITMAPFILEHEADER
+ // immediately followed by a BITMAPINFOHEADER.
+ const (
+ fileHeaderLen = 14
+ infoHeaderLen = 40
+ )
+ var b [1024]byte
+ if _, err = io.ReadFull(r, b[:fileHeaderLen+infoHeaderLen]); err != nil {
+ return
+ }
+ if string(b[:2]) != "BM" {
+ err = os.NewError("bmp: invalid format")
+ return
+ }
+ offset := readUint32(b[10:14])
+ if readUint32(b[14:18]) != infoHeaderLen {
+ err = ErrUnsupported
+ return
+ }
+ width := int(readUint32(b[18:22]))
+ height := int(readUint32(b[22:26]))
+ if width < 0 || height < 0 {
+ err = ErrUnsupported
+ return
+ }
+ // We only support 1 plane, 8 or 24 bits per pixel and no compression.
+ planes, bpp, compression := readUint16(b[26:28]), readUint16(b[28:30]), readUint32(b[30:34])
+ if planes != 1 || compression != 0 {
+ err = ErrUnsupported
+ return
+ }
+ switch bpp {
+ case 8:
+ if offset != fileHeaderLen+infoHeaderLen+256*4 {
+ err = ErrUnsupported
+ return
+ }
+ _, err = io.ReadFull(r, b[:256*4])
+ if err != nil {
+ return
+ }
+ pcm := make(image.PalettedColorModel, 256)
+ for i := range pcm {
+ // BMP images are stored in BGR order rather than RGB order.
+ // Every 4th byte is padding.
+ pcm[i] = image.RGBAColor{b[4*i+2], b[4*i+1], b[4*i+0], 0xFF}
+ }
+ return image.Config{pcm, width, height}, nil
+ case 24:
+ if offset != fileHeaderLen+infoHeaderLen {
+ err = ErrUnsupported
+ return
+ }
+ return image.Config{image.RGBAColorModel, width, height}, nil
+ }
+ err = ErrUnsupported
+ return
+}
+
+func init() {
+ image.RegisterFormat("bmp", "BM????\x00\x00\x00\x00", Decode, DecodeConfig)
+}
diff --git a/libgo/go/image/color.go b/libgo/go/image/color.go
index c1345c0..501a882 100644
--- a/libgo/go/image/color.go
+++ b/libgo/go/image/color.go
@@ -4,14 +4,14 @@
package image
-// All Colors can convert themselves, with a possible loss of precision,
-// to 64-bit alpha-premultiplied RGBA. Each channel value ranges within
-// [0, 0xFFFF].
+// Color can convert itself to alpha-premultiplied RGBA, with a possible loss
+// of precision. Each value ranges within [0, 0xFFFF], but is represented by a
+// uint32 so that multiplying by a blend factor up to 0xFFFF will not overflow.
type Color interface {
RGBA() (r, g, b, a uint32)
}
-// An RGBAColor represents a traditional 32-bit alpha-premultiplied color,
+// RGBAColor represents a traditional 32-bit alpha-premultiplied color,
// having 8 bits for each of red, green, blue and alpha.
type RGBAColor struct {
R, G, B, A uint8
@@ -29,7 +29,7 @@ func (c RGBAColor) RGBA() (r, g, b, a uint32) {
return
}
-// An RGBA64Color represents a 64-bit alpha-premultiplied color,
+// RGBA64Color represents a 64-bit alpha-premultiplied color,
// having 16 bits for each of red, green, blue and alpha.
type RGBA64Color struct {
R, G, B, A uint16
@@ -39,7 +39,7 @@ func (c RGBA64Color) RGBA() (r, g, b, a uint32) {
return uint32(c.R), uint32(c.G), uint32(c.B), uint32(c.A)
}
-// An NRGBAColor represents a non-alpha-premultiplied 32-bit color.
+// NRGBAColor represents a non-alpha-premultiplied 32-bit color.
type NRGBAColor struct {
R, G, B, A uint8
}
@@ -62,7 +62,7 @@ func (c NRGBAColor) RGBA() (r, g, b, a uint32) {
return
}
-// An NRGBA64Color represents a non-alpha-premultiplied 64-bit color,
+// NRGBA64Color represents a non-alpha-premultiplied 64-bit color,
// having 16 bits for each of red, green, blue and alpha.
type NRGBA64Color struct {
R, G, B, A uint16
@@ -82,7 +82,7 @@ func (c NRGBA64Color) RGBA() (r, g, b, a uint32) {
return
}
-// An AlphaColor represents an 8-bit alpha.
+// AlphaColor represents an 8-bit alpha.
type AlphaColor struct {
A uint8
}
@@ -93,7 +93,7 @@ func (c AlphaColor) RGBA() (r, g, b, a uint32) {
return a, a, a, a
}
-// An Alpha16Color represents a 16-bit alpha.
+// Alpha16Color represents a 16-bit alpha.
type Alpha16Color struct {
A uint16
}
@@ -103,7 +103,7 @@ func (c Alpha16Color) RGBA() (r, g, b, a uint32) {
return a, a, a, a
}
-// A GrayColor represents an 8-bit grayscale color.
+// GrayColor represents an 8-bit grayscale color.
type GrayColor struct {
Y uint8
}
@@ -114,7 +114,7 @@ func (c GrayColor) RGBA() (r, g, b, a uint32) {
return y, y, y, 0xffff
}
-// A Gray16Color represents a 16-bit grayscale color.
+// Gray16Color represents a 16-bit grayscale color.
type Gray16Color struct {
Y uint16
}
@@ -124,7 +124,7 @@ func (c Gray16Color) RGBA() (r, g, b, a uint32) {
return y, y, y, 0xffff
}
-// A ColorModel can convert foreign Colors, with a possible loss of precision,
+// ColorModel can convert foreign Colors, with a possible loss of precision,
// to a Color from its own color model.
type ColorModel interface {
Convert(c Color) Color
diff --git a/libgo/go/image/decode_test.go b/libgo/go/image/decode_test.go
index fee537c..540d5ed 100644
--- a/libgo/go/image/decode_test.go
+++ b/libgo/go/image/decode_test.go
@@ -10,29 +10,34 @@ import (
"os"
"testing"
- // TODO(nigeltao): implement bmp decoder.
+ _ "image/bmp"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
_ "image/tiff"
)
-const goldenFile = "testdata/video-001.png"
-
type imageTest struct {
- filename string
- tolerance int
+ goldenFilename string
+ filename string
+ tolerance int
}
var imageTests = []imageTest{
- //{"testdata/video-001.bmp", 0},
+ {"testdata/video-001.png", "testdata/video-001.bmp", 0},
// GIF images are restricted to a 256-color palette and the conversion
// to GIF loses significant image quality.
- {"testdata/video-001.gif", 64 << 8},
+ {"testdata/video-001.png", "testdata/video-001.gif", 64 << 8},
+ {"testdata/video-001.png", "testdata/video-001.interlaced.gif", 64 << 8},
+ {"testdata/video-001.png", "testdata/video-001.5bpp.gif", 128 << 8},
// JPEG is a lossy format and hence needs a non-zero tolerance.
- {"testdata/video-001.jpeg", 8 << 8},
- {"testdata/video-001.png", 0},
- {"testdata/video-001.tiff", 0},
+ {"testdata/video-001.png", "testdata/video-001.jpeg", 8 << 8},
+ {"testdata/video-001.png", "testdata/video-001.png", 0},
+ {"testdata/video-001.png", "testdata/video-001.tiff", 0},
+
+ // Test grayscale images.
+ {"testdata/video-005.gray.png", "testdata/video-005.gray.jpeg", 8 << 8},
+ {"testdata/video-005.gray.png", "testdata/video-005.gray.png", 0},
}
func decode(filename string) (image.Image, string, os.Error) {
@@ -44,6 +49,15 @@ func decode(filename string) (image.Image, string, os.Error) {
return image.Decode(bufio.NewReader(f))
}
+func decodeConfig(filename string) (image.Config, string, os.Error) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return image.Config{}, "", err
+ }
+ defer f.Close()
+ return image.DecodeConfig(bufio.NewReader(f))
+}
+
func delta(u0, u1 uint32) int {
d := int(u0) - int(u1)
if d < 0 {
@@ -63,29 +77,47 @@ func withinTolerance(c0, c1 image.Color, tolerance int) bool {
}
func TestDecode(t *testing.T) {
- golden, _, err := decode(goldenFile)
- if err != nil {
- t.Errorf("%s: %v", goldenFile, err)
- }
+ golden := make(map[string]image.Image)
loop:
for _, it := range imageTests {
- m, _, err := decode(it.filename)
+ g := golden[it.goldenFilename]
+ if g == nil {
+ var err os.Error
+ g, _, err = decode(it.goldenFilename)
+ if err != nil {
+ t.Errorf("%s: %v", it.goldenFilename, err)
+ continue loop
+ }
+ golden[it.goldenFilename] = g
+ }
+ m, imageFormat, err := decode(it.filename)
if err != nil {
t.Errorf("%s: %v", it.filename, err)
continue loop
}
- b := golden.Bounds()
+ b := g.Bounds()
if !b.Eq(m.Bounds()) {
t.Errorf("%s: want bounds %v got %v", it.filename, b, m.Bounds())
continue loop
}
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
- if !withinTolerance(golden.At(x, y), m.At(x, y), it.tolerance) {
- t.Errorf("%s: at (%d, %d), want %v got %v", it.filename, x, y, golden.At(x, y), m.At(x, y))
+ if !withinTolerance(g.At(x, y), m.At(x, y), it.tolerance) {
+ t.Errorf("%s: at (%d, %d), want %v got %v", it.filename, x, y, g.At(x, y), m.At(x, y))
continue loop
}
}
}
+ if imageFormat == "gif" {
+ // Each frame of a GIF can have a frame-local palette override the
+ // GIF-global palette. Thus, image.Decode can yield a different ColorModel
+ // than image.DecodeConfig.
+ continue
+ }
+ c, _, err := decodeConfig(it.filename)
+ if m.ColorModel() != c.ColorModel {
+ t.Errorf("%s: color models differ", it.filename)
+ continue loop
+ }
}
}
diff --git a/libgo/go/image/draw/bench_test.go b/libgo/go/image/draw/bench_test.go
new file mode 100644
index 0000000..a99b408
--- /dev/null
+++ b/libgo/go/image/draw/bench_test.go
@@ -0,0 +1,206 @@
+// 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 draw
+
+import (
+ "image"
+ "image/ycbcr"
+ "testing"
+)
+
+const (
+ dstw, dsth = 640, 480
+ srcw, srch = 400, 300
+)
+
+// bench benchmarks drawing src and mask images onto a dst image with the
+// given op and the color models to create those images from.
+// The created images' pixels are initialized to non-zero values.
+func bench(b *testing.B, dcm, scm, mcm image.ColorModel, op Op) {
+ b.StopTimer()
+
+ var dst Image
+ switch dcm {
+ case image.RGBAColorModel:
+ dst1 := image.NewRGBA(dstw, dsth)
+ for y := 0; y < dsth; y++ {
+ for x := 0; x < dstw; x++ {
+ dst1.SetRGBA(x, y, image.RGBAColor{
+ uint8(5 * x % 0x100),
+ uint8(7 * y % 0x100),
+ uint8((7*x + 5*y) % 0x100),
+ 0xff,
+ })
+ }
+ }
+ dst = dst1
+ case image.RGBA64ColorModel:
+ dst1 := image.NewRGBA64(dstw, dsth)
+ for y := 0; y < dsth; y++ {
+ for x := 0; x < dstw; x++ {
+ dst1.SetRGBA64(x, y, image.RGBA64Color{
+ uint16(53 * x % 0x10000),
+ uint16(59 * y % 0x10000),
+ uint16((59*x + 53*y) % 0x10000),
+ 0xffff,
+ })
+ }
+ }
+ dst = dst1
+ default:
+ panic("unreachable")
+ }
+
+ var src image.Image
+ switch scm {
+ case nil:
+ src = &image.ColorImage{image.RGBAColor{0x11, 0x22, 0x33, 0xff}}
+ case image.RGBAColorModel:
+ src1 := image.NewRGBA(srcw, srch)
+ for y := 0; y < srch; y++ {
+ for x := 0; x < srcw; x++ {
+ src1.SetRGBA(x, y, image.RGBAColor{
+ uint8(13 * x % 0x80),
+ uint8(11 * y % 0x80),
+ uint8((11*x + 13*y) % 0x80),
+ 0x7f,
+ })
+ }
+ }
+ src = src1
+ case image.RGBA64ColorModel:
+ src1 := image.NewRGBA64(srcw, srch)
+ for y := 0; y < srch; y++ {
+ for x := 0; x < srcw; x++ {
+ src1.SetRGBA64(x, y, image.RGBA64Color{
+ uint16(103 * x % 0x8000),
+ uint16(101 * y % 0x8000),
+ uint16((101*x + 103*y) % 0x8000),
+ 0x7fff,
+ })
+ }
+ }
+ src = src1
+ case image.NRGBAColorModel:
+ src1 := image.NewNRGBA(srcw, srch)
+ for y := 0; y < srch; y++ {
+ for x := 0; x < srcw; x++ {
+ src1.SetNRGBA(x, y, image.NRGBAColor{
+ uint8(13 * x % 0x100),
+ uint8(11 * y % 0x100),
+ uint8((11*x + 13*y) % 0x100),
+ 0x7f,
+ })
+ }
+ }
+ src = src1
+ case ycbcr.YCbCrColorModel:
+ yy := make([]uint8, srcw*srch)
+ cb := make([]uint8, srcw*srch)
+ cr := make([]uint8, srcw*srch)
+ for i := range yy {
+ yy[i] = uint8(3 * i % 0x100)
+ cb[i] = uint8(5 * i % 0x100)
+ cr[i] = uint8(7 * i % 0x100)
+ }
+ src = &ycbcr.YCbCr{
+ Y: yy,
+ Cb: cb,
+ Cr: cr,
+ YStride: srcw,
+ CStride: srcw,
+ SubsampleRatio: ycbcr.SubsampleRatio444,
+ Rect: image.Rect(0, 0, srcw, srch),
+ }
+ default:
+ panic("unreachable")
+ }
+
+ var mask image.Image
+ switch mcm {
+ case nil:
+ // No-op.
+ case image.AlphaColorModel:
+ mask1 := image.NewAlpha(srcw, srch)
+ for y := 0; y < srch; y++ {
+ for x := 0; x < srcw; x++ {
+ a := uint8((23*x + 29*y) % 0x100)
+ // Glyph masks are typically mostly zero,
+ // so we only set a quarter of mask1's pixels.
+ if a >= 0xc0 {
+ mask1.SetAlpha(x, y, image.AlphaColor{a})
+ }
+ }
+ }
+ mask = mask1
+ default:
+ panic("unreachable")
+ }
+
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ // Scatter the destination rectangle to draw into.
+ x := 3 * i % (dstw - srcw)
+ y := 7 * i % (dsth - srch)
+
+ DrawMask(dst, dst.Bounds().Add(image.Point{x, y}), src, image.ZP, mask, image.ZP, op)
+ }
+}
+
+// The BenchmarkFoo functions exercise a drawFoo fast-path function in draw.go.
+
+func BenchmarkFillOver(b *testing.B) {
+ bench(b, image.RGBAColorModel, nil, nil, Over)
+}
+
+func BenchmarkFillSrc(b *testing.B) {
+ bench(b, image.RGBAColorModel, nil, nil, Src)
+}
+
+func BenchmarkCopyOver(b *testing.B) {
+ bench(b, image.RGBAColorModel, image.RGBAColorModel, nil, Over)
+}
+
+func BenchmarkCopySrc(b *testing.B) {
+ bench(b, image.RGBAColorModel, image.RGBAColorModel, nil, Src)
+}
+
+func BenchmarkNRGBAOver(b *testing.B) {
+ bench(b, image.RGBAColorModel, image.NRGBAColorModel, nil, Over)
+}
+
+func BenchmarkNRGBASrc(b *testing.B) {
+ bench(b, image.RGBAColorModel, image.NRGBAColorModel, nil, Src)
+}
+
+func BenchmarkYCbCr(b *testing.B) {
+ bench(b, image.RGBAColorModel, ycbcr.YCbCrColorModel, nil, Over)
+}
+
+func BenchmarkGlyphOver(b *testing.B) {
+ bench(b, image.RGBAColorModel, nil, image.AlphaColorModel, Over)
+}
+
+func BenchmarkRGBA(b *testing.B) {
+ bench(b, image.RGBAColorModel, image.RGBA64ColorModel, nil, Src)
+}
+
+// The BenchmarkGenericFoo functions exercise the generic, slow-path code.
+
+func BenchmarkGenericOver(b *testing.B) {
+ bench(b, image.RGBA64ColorModel, image.RGBA64ColorModel, nil, Over)
+}
+
+func BenchmarkGenericMaskOver(b *testing.B) {
+ bench(b, image.RGBA64ColorModel, image.RGBA64ColorModel, image.AlphaColorModel, Over)
+}
+
+func BenchmarkGenericSrc(b *testing.B) {
+ bench(b, image.RGBA64ColorModel, image.RGBA64ColorModel, nil, Src)
+}
+
+func BenchmarkGenericMaskSrc(b *testing.B) {
+ bench(b, image.RGBA64ColorModel, image.RGBA64ColorModel, image.AlphaColorModel, Src)
+}
diff --git a/libgo/go/image/draw/clip_test.go b/libgo/go/image/draw/clip_test.go
new file mode 100644
index 0000000..db40d82
--- /dev/null
+++ b/libgo/go/image/draw/clip_test.go
@@ -0,0 +1,193 @@
+// 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 draw
+
+import (
+ "image"
+ "testing"
+)
+
+type clipTest struct {
+ desc string
+ r, dr, sr, mr image.Rectangle
+ sp, mp image.Point
+ nilMask bool
+ r0 image.Rectangle
+ sp0, mp0 image.Point
+}
+
+var clipTests = []clipTest{
+ // The following tests all have a nil mask.
+ {
+ "basic",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 100, 100),
+ image.ZR,
+ image.ZP,
+ image.ZP,
+ true,
+ image.Rect(0, 0, 100, 100),
+ image.ZP,
+ image.ZP,
+ },
+ {
+ "clip dr",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(40, 40, 60, 60),
+ image.Rect(0, 0, 100, 100),
+ image.ZR,
+ image.ZP,
+ image.ZP,
+ true,
+ image.Rect(40, 40, 60, 60),
+ image.Pt(40, 40),
+ image.ZP,
+ },
+ {
+ "clip sr",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 100, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.ZP,
+ image.ZP,
+ true,
+ image.Rect(20, 20, 80, 80),
+ image.Pt(20, 20),
+ image.ZP,
+ },
+ {
+ "clip dr and sr",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 50, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.ZP,
+ image.ZP,
+ true,
+ image.Rect(20, 20, 50, 80),
+ image.Pt(20, 20),
+ image.ZP,
+ },
+ {
+ "clip dr and sr, sp outside sr (top-left)",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 50, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.Pt(15, 8),
+ image.ZP,
+ true,
+ image.Rect(5, 12, 50, 72),
+ image.Pt(20, 20),
+ image.ZP,
+ },
+ {
+ "clip dr and sr, sp outside sr (middle-left)",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 50, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.Pt(15, 66),
+ image.ZP,
+ true,
+ image.Rect(5, 0, 50, 14),
+ image.Pt(20, 66),
+ image.ZP,
+ },
+ {
+ "clip dr and sr, sp outside sr (bottom-left)",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 50, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.Pt(15, 91),
+ image.ZP,
+ true,
+ image.ZR,
+ image.Pt(15, 91),
+ image.ZP,
+ },
+ {
+ "clip dr and sr, sp inside sr",
+ image.Rect(0, 0, 100, 100),
+ image.Rect(0, 0, 50, 100),
+ image.Rect(20, 20, 80, 80),
+ image.ZR,
+ image.Pt(44, 33),
+ image.ZP,
+ true,
+ image.Rect(0, 0, 36, 47),
+ image.Pt(44, 33),
+ image.ZP,
+ },
+
+ // The following tests all have a non-nil mask.
+ {
+ "basic mask",
+ image.Rect(0, 0, 80, 80),
+ image.Rect(20, 0, 100, 80),
+ image.Rect(0, 0, 50, 49),
+ image.Rect(0, 0, 46, 47),
+ image.ZP,
+ image.ZP,
+ false,
+ image.Rect(20, 0, 46, 47),
+ image.Pt(20, 0),
+ image.Pt(20, 0),
+ },
+ // TODO(nigeltao): write more tests.
+}
+
+func TestClip(t *testing.T) {
+ dst0 := image.NewRGBA(100, 100)
+ src0 := image.NewRGBA(100, 100)
+ mask0 := image.NewRGBA(100, 100)
+ for _, c := range clipTests {
+ dst := dst0.SubImage(c.dr).(*image.RGBA)
+ src := src0.SubImage(c.sr).(*image.RGBA)
+ var mask image.Image
+ if !c.nilMask {
+ mask = mask0.SubImage(c.mr)
+ }
+ r, sp, mp := c.r, c.sp, c.mp
+ clip(dst, &r, src, &sp, mask, &mp)
+
+ // Check that the actual results equal the expected results.
+ if !c.r0.Eq(r) {
+ t.Errorf("%s: clip rectangle want %v got %v", c.desc, c.r0, r)
+ continue
+ }
+ if !c.sp0.Eq(sp) {
+ t.Errorf("%s: sp want %v got %v", c.desc, c.sp0, sp)
+ continue
+ }
+ if !c.nilMask {
+ if !c.mp0.Eq(mp) {
+ t.Errorf("%s: mp want %v got %v", c.desc, c.mp0, mp)
+ continue
+ }
+ }
+
+ // Check that the clipped rectangle is contained by the dst / src / mask
+ // rectangles, in their respective co-ordinate spaces.
+ if !r.In(c.dr) {
+ t.Errorf("%s: c.dr %v does not contain r %v", c.desc, c.dr, r)
+ }
+ // sr is r translated into src's co-ordinate space.
+ sr := r.Add(c.sp.Sub(c.dr.Min))
+ if !sr.In(c.sr) {
+ t.Errorf("%s: c.sr %v does not contain sr %v", c.desc, c.sr, sr)
+ }
+ if !c.nilMask {
+ // mr is r translated into mask's co-ordinate space.
+ mr := r.Add(c.mp.Sub(c.dr.Min))
+ if !mr.In(c.mr) {
+ t.Errorf("%s: c.mr %v does not contain mr %v", c.desc, c.mr, mr)
+ }
+ }
+ }
+}
diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go
new file mode 100644
index 0000000..a748ff8
--- /dev/null
+++ b/libgo/go/image/draw/draw.go
@@ -0,0 +1,493 @@
+// Copyright 2009 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 draw provides image composition functions
+// in the style of the Plan 9 graphics library
+// (see http://plan9.bell-labs.com/magic/man2html/2/draw)
+// and the X Render extension.
+package draw
+
+import (
+ "image"
+ "image/ycbcr"
+)
+
+// m is the maximum color value returned by image.Color.RGBA.
+const m = 1<<16 - 1
+
+// Op is a Porter-Duff compositing operator.
+type Op int
+
+const (
+ // Over specifies ``(src in mask) over dst''.
+ Over Op = iota
+ // Src specifies ``src in mask''.
+ Src
+)
+
+var zeroColor image.Color = image.AlphaColor{0}
+
+// A draw.Image is an image.Image with a Set method to change a single pixel.
+type Image interface {
+ image.Image
+ Set(x, y int, c image.Color)
+}
+
+// Draw calls DrawMask with a nil mask.
+func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) {
+ DrawMask(dst, r, src, sp, nil, image.ZP, op)
+}
+
+// clip clips r against each image's bounds (after translating into the
+// destination image's co-ordinate space) and shifts the points sp and mp by
+// the same amount as the change in r.Min.
+func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) {
+ orig := r.Min
+ *r = r.Intersect(dst.Bounds())
+ *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp)))
+ if mask != nil {
+ *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp)))
+ }
+ dx := r.Min.X - orig.X
+ dy := r.Min.Y - orig.Y
+ if dx == 0 && dy == 0 {
+ return
+ }
+ (*sp).X += dx
+ (*sp).Y += dy
+ (*mp).X += dx
+ (*mp).Y += dy
+}
+
+// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
+// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
+func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
+ clip(dst, &r, src, &sp, mask, &mp)
+ if r.Empty() {
+ return
+ }
+
+ // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
+ if dst0, ok := dst.(*image.RGBA); ok {
+ if op == Over {
+ if mask == nil {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
+ drawFillOver(dst0, r, src0)
+ return
+ case *image.RGBA:
+ drawCopyOver(dst0, r, src0, sp)
+ return
+ case *image.NRGBA:
+ drawNRGBAOver(dst0, r, src0, sp)
+ return
+ case *ycbcr.YCbCr:
+ drawYCbCr(dst0, r, src0, sp)
+ return
+ }
+ } else if mask0, ok := mask.(*image.Alpha); ok {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
+ drawGlyphOver(dst0, r, src0, mask0, mp)
+ return
+ }
+ }
+ } else {
+ if mask == nil {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
+ drawFillSrc(dst0, r, src0)
+ return
+ case *image.RGBA:
+ drawCopySrc(dst0, r, src0, sp)
+ return
+ case *image.NRGBA:
+ drawNRGBASrc(dst0, r, src0, sp)
+ return
+ case *ycbcr.YCbCr:
+ drawYCbCr(dst0, r, src0, sp)
+ return
+ }
+ }
+ }
+ drawRGBA(dst0, r, src, sp, mask, mp, op)
+ return
+ }
+
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ // Rectangles overlap: process backward?
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ var out *image.RGBA64Color
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
+ sx := sp.X + x0 - r.Min.X
+ mx := mp.X + x0 - r.Min.X
+ for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
+ ma := uint32(m)
+ if mask != nil {
+ _, _, _, ma = mask.At(mx, my).RGBA()
+ }
+ switch {
+ case ma == 0:
+ if op == Over {
+ // No-op.
+ } else {
+ dst.Set(x, y, zeroColor)
+ }
+ case ma == m && op == Src:
+ dst.Set(x, y, src.At(sx, sy))
+ default:
+ sr, sg, sb, sa := src.At(sx, sy).RGBA()
+ if out == nil {
+ out = new(image.RGBA64Color)
+ }
+ if op == Over {
+ dr, dg, db, da := dst.At(x, y).RGBA()
+ a := m - (sa * ma / m)
+ out.R = uint16((dr*a + sr*ma) / m)
+ out.G = uint16((dg*a + sg*ma) / m)
+ out.B = uint16((db*a + sb*ma) / m)
+ out.A = uint16((da*a + sa*ma) / m)
+ } else {
+ out.R = uint16(sr * ma / m)
+ out.G = uint16(sg * ma / m)
+ out.B = uint16(sb * ma / m)
+ out.A = uint16(sa * ma / m)
+ }
+ dst.Set(x, y, out)
+ }
+ }
+ }
+}
+
+func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
+ 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
+ i1 := i0 + r.Dx()*4
+ for y := r.Min.Y; y != r.Max.Y; y++ {
+ for i := i0; i < i1; i += 4 {
+ dr := uint32(dst.Pix[i+0])
+ dg := uint32(dst.Pix[i+1])
+ db := uint32(dst.Pix[i+2])
+ da := uint32(dst.Pix[i+3])
+
+ dst.Pix[i+0] = uint8((dr*a/m + sr) >> 8)
+ dst.Pix[i+1] = uint8((dg*a/m + sg) >> 8)
+ dst.Pix[i+2] = uint8((db*a/m + sb) >> 8)
+ dst.Pix[i+3] = uint8((da*a/m + sa) >> 8)
+ }
+ i0 += dst.Stride
+ i1 += dst.Stride
+ }
+}
+
+func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
+ sr, sg, sb, sa := src.RGBA()
+ // 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
+ i1 := i0 + r.Dx()*4
+ for i := i0; i < i1; i += 4 {
+ dst.Pix[i+0] = uint8(sr >> 8)
+ dst.Pix[i+1] = uint8(sg >> 8)
+ dst.Pix[i+2] = uint8(sb >> 8)
+ dst.Pix[i+3] = uint8(sa >> 8)
+ }
+ firstRow := dst.Pix[i0:i1]
+ for y := r.Min.Y + 1; y < r.Max.Y; y++ {
+ i0 += dst.Stride
+ i1 += dst.Stride
+ copy(dst.Pix[i0:i1], firstRow)
+ }
+}
+
+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
+ var (
+ ddelta, sdelta int
+ i0, i1, idelta int
+ )
+ if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X {
+ ddelta = dst.Stride
+ sdelta = src.Stride
+ i0, i1, idelta = 0, dx*4, +4
+ } else {
+ // If the source start point is higher than the destination start point, or equal height but to the left,
+ // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down.
+ d0 += (dy - 1) * dst.Stride
+ s0 += (dy - 1) * src.Stride
+ ddelta = -dst.Stride
+ sdelta = -src.Stride
+ i0, i1, idelta = (dx-1)*4, -4, -4
+ }
+ for ; dy > 0; dy-- {
+ dpix := dst.Pix[d0:]
+ spix := src.Pix[s0:]
+ for i := i0; i != i1; i += idelta {
+ sr := uint32(spix[i+0]) * 0x101
+ sg := uint32(spix[i+1]) * 0x101
+ sb := uint32(spix[i+2]) * 0x101
+ sa := uint32(spix[i+3]) * 0x101
+
+ dr := uint32(dpix[i+0])
+ dg := uint32(dpix[i+1])
+ db := uint32(dpix[i+2])
+ da := uint32(dpix[i+3])
+
+ // The 0x101 is here for the same reason as in drawRGBA.
+ a := (m - sa) * 0x101
+
+ dpix[i+0] = uint8((dr*a/m + sr) >> 8)
+ dpix[i+1] = uint8((dg*a/m + sg) >> 8)
+ dpix[i+2] = uint8((db*a/m + sb) >> 8)
+ dpix[i+3] = uint8((da*a/m + sa) >> 8)
+ }
+ d0 += ddelta
+ s0 += sdelta
+ }
+}
+
+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
+ var ddelta, sdelta int
+ if r.Min.Y <= sp.Y {
+ ddelta = dst.Stride
+ sdelta = src.Stride
+ } else {
+ // If the source start point is higher than the destination start point, then we compose the rows
+ // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to
+ // check the x co-ordinates because the built-in copy function can handle overlapping slices.
+ d0 += (dy - 1) * dst.Stride
+ s0 += (dy - 1) * src.Stride
+ ddelta = -dst.Stride
+ sdelta = -src.Stride
+ }
+ for ; dy > 0; dy-- {
+ copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n])
+ d0 += ddelta
+ s0 += sdelta
+ }
+}
+
+func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
+ i0 := (r.Min.X - dst.Rect.Min.X) * 4
+ i1 := (r.Max.X - dst.Rect.Min.X) * 4
+ si0 := (sp.X - src.Rect.Min.X) * 4
+ yMax := r.Max.Y - dst.Rect.Min.Y
+
+ y := r.Min.Y - dst.Rect.Min.Y
+ sy := sp.Y - src.Rect.Min.Y
+ for ; y != yMax; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride:]
+ spix := src.Pix[sy*src.Stride:]
+
+ for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
+ // Convert from non-premultiplied color to pre-multiplied color.
+ sa := uint32(spix[si+3]) * 0x101
+ sr := uint32(spix[si+0]) * sa / 0xff
+ sg := uint32(spix[si+1]) * sa / 0xff
+ sb := uint32(spix[si+2]) * sa / 0xff
+
+ dr := uint32(dpix[i+0])
+ dg := uint32(dpix[i+1])
+ db := uint32(dpix[i+2])
+ da := uint32(dpix[i+3])
+
+ // The 0x101 is here for the same reason as in drawRGBA.
+ a := (m - sa) * 0x101
+
+ dpix[i+0] = uint8((dr*a/m + sr) >> 8)
+ dpix[i+1] = uint8((dg*a/m + sg) >> 8)
+ dpix[i+2] = uint8((db*a/m + sb) >> 8)
+ dpix[i+3] = uint8((da*a/m + sa) >> 8)
+ }
+ }
+}
+
+func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
+ i0 := (r.Min.X - dst.Rect.Min.X) * 4
+ i1 := (r.Max.X - dst.Rect.Min.X) * 4
+ si0 := (sp.X - src.Rect.Min.X) * 4
+ yMax := r.Max.Y - dst.Rect.Min.Y
+
+ y := r.Min.Y - dst.Rect.Min.Y
+ sy := sp.Y - src.Rect.Min.Y
+ for ; y != yMax; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride:]
+ spix := src.Pix[sy*src.Stride:]
+
+ for i, si := i0, si0; i < i1; i, si = i+4, si+4 {
+ // Convert from non-premultiplied color to pre-multiplied color.
+ sa := uint32(spix[si+3]) * 0x101
+ sr := uint32(spix[si+0]) * sa / 0xff
+ sg := uint32(spix[si+1]) * sa / 0xff
+ sb := uint32(spix[si+2]) * sa / 0xff
+
+ dpix[i+0] = uint8(sr >> 8)
+ dpix[i+1] = uint8(sg >> 8)
+ dpix[i+2] = uint8(sb >> 8)
+ dpix[i+3] = uint8(sa >> 8)
+ }
+ }
+}
+
+func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Point) {
+ // A YCbCr image 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
+ y1 := r.Max.Y - dst.Rect.Min.Y
+ switch src.SubsampleRatio {
+ case ycbcr.SubsampleRatio422:
+ 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 := ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x+0] = rr
+ dpix[x+1] = gg
+ dpix[x+2] = bb
+ dpix[x+3] = 255
+ }
+ }
+ case ycbcr.SubsampleRatio420:
+ 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 := ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x+0] = rr
+ dpix[x+1] = gg
+ dpix[x+2] = bb
+ dpix[x+3] = 255
+ }
+ }
+ default:
+ // 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 := ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x+0] = rr
+ dpix[x+1] = gg
+ dpix[x+2] = bb
+ dpix[x+3] = 255
+ }
+ }
+ }
+}
+
+func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) {
+ i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + (r.Min.X-dst.Rect.Min.X)*4
+ i1 := i0 + r.Dx()*4
+ mi0 := (mp.Y-mask.Rect.Min.Y)*mask.Stride + mp.X - mask.Rect.Min.X
+ 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 {
+ ma := uint32(mask.Pix[mi])
+ if ma == 0 {
+ continue
+ }
+ ma |= ma << 8
+
+ dr := uint32(dst.Pix[i+0])
+ dg := uint32(dst.Pix[i+1])
+ db := uint32(dst.Pix[i+2])
+ da := uint32(dst.Pix[i+3])
+
+ // The 0x101 is here for the same reason as in drawRGBA.
+ a := (m - (sa * ma / m)) * 0x101
+
+ dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8)
+ dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8)
+ dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8)
+ dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8)
+ }
+ i0 += dst.Stride
+ i1 += dst.Stride
+ mi0 += mask.Stride
+ }
+}
+
+func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
+ x0, x1, dx := r.Min.X, r.Max.X, 1
+ y0, y1, dy := r.Min.Y, r.Max.Y, 1
+ if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
+ if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
+ x0, x1, dx = x1-1, x0-1, -1
+ y0, y1, dy = y1-1, y0-1, -1
+ }
+ }
+
+ sy := sp.Y + y0 - r.Min.Y
+ my := mp.Y + y0 - r.Min.Y
+ 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
+ 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 {
+ ma := uint32(m)
+ if mask != nil {
+ _, _, _, ma = mask.At(mx, my).RGBA()
+ }
+ sr, sg, sb, sa := src.At(sx, sy).RGBA()
+ if op == Over {
+ dr := uint32(dst.Pix[i+0])
+ dg := uint32(dst.Pix[i+1])
+ db := uint32(dst.Pix[i+2])
+ da := uint32(dst.Pix[i+3])
+
+ // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255].
+ // We work in 16-bit color, and so would normally do:
+ // dr |= dr << 8
+ // and similarly for dg, db and da, but instead we multiply a
+ // (which is a 16-bit color, ranging in [0,65535]) by 0x101.
+ // This yields the same result, but is fewer arithmetic operations.
+ a := (m - (sa * ma / m)) * 0x101
+
+ dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8)
+ dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8)
+ dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8)
+ dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8)
+
+ } else {
+ dst.Pix[i+0] = uint8(sr * ma / m >> 8)
+ dst.Pix[i+1] = uint8(sg * ma / m >> 8)
+ dst.Pix[i+2] = uint8(sb * ma / m >> 8)
+ dst.Pix[i+3] = uint8(sa * ma / m >> 8)
+ }
+ }
+ i0 += dy * dst.Stride
+ }
+}
diff --git a/libgo/go/image/draw/draw_test.go b/libgo/go/image/draw/draw_test.go
new file mode 100644
index 0000000..55435cc
--- /dev/null
+++ b/libgo/go/image/draw/draw_test.go
@@ -0,0 +1,354 @@
+// Copyright 2010 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 draw
+
+import (
+ "image"
+ "image/ycbcr"
+ "testing"
+)
+
+func eq(c0, c1 image.Color) bool {
+ r0, g0, b0, a0 := c0.RGBA()
+ r1, g1, b1, a1 := c1.RGBA()
+ return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
+}
+
+func fillBlue(alpha int) image.Image {
+ return image.NewColorImage(image.RGBAColor{0, 0, uint8(alpha), uint8(alpha)})
+}
+
+func fillAlpha(alpha int) image.Image {
+ return image.NewColorImage(image.AlphaColor{uint8(alpha)})
+}
+
+func vgradGreen(alpha int) image.Image {
+ m := image.NewRGBA(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.RGBAColor{0, uint8(y * alpha / 15), 0, uint8(alpha)})
+ }
+ }
+ return m
+}
+
+func vgradAlpha(alpha int) image.Image {
+ m := image.NewAlpha(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.AlphaColor{uint8(y * alpha / 15)})
+ }
+ }
+ return m
+}
+
+func vgradGreenNRGBA(alpha int) image.Image {
+ m := image.NewNRGBA(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.RGBAColor{0, uint8(y * 0x11), 0, uint8(alpha)})
+ }
+ }
+ return m
+}
+
+func vgradCr() image.Image {
+ m := &ycbcr.YCbCr{
+ Y: make([]byte, 16*16),
+ Cb: make([]byte, 16*16),
+ Cr: make([]byte, 16*16),
+ YStride: 16,
+ CStride: 16,
+ SubsampleRatio: ycbcr.SubsampleRatio444,
+ Rect: image.Rect(0, 0, 16, 16),
+ }
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Cr[y*m.CStride+x] = uint8(y * 0x11)
+ }
+ }
+ return m
+}
+
+func hgradRed(alpha int) Image {
+ m := image.NewRGBA(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
+ }
+ }
+ return m
+}
+
+func gradYellow(alpha int) Image {
+ m := image.NewRGBA(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)})
+ }
+ }
+ return m
+}
+
+type drawTest struct {
+ desc string
+ src image.Image
+ mask image.Image
+ op Op
+ expected image.Color
+}
+
+var drawTests = []drawTest{
+ // Uniform mask (0% opaque).
+ {"nop", vgradGreen(255), fillAlpha(0), Over, image.RGBAColor{136, 0, 0, 255}},
+ {"clear", vgradGreen(255), fillAlpha(0), Src, image.RGBAColor{0, 0, 0, 0}},
+ // Uniform mask (100%, 75%, nil) and uniform source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 0, 90, 90}.
+ {"fill", fillBlue(90), fillAlpha(255), Over, image.RGBAColor{88, 0, 90, 255}},
+ {"fillSrc", fillBlue(90), fillAlpha(255), Src, image.RGBAColor{0, 0, 90, 90}},
+ {"fillAlpha", fillBlue(90), fillAlpha(192), Over, image.RGBAColor{100, 0, 68, 255}},
+ {"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, image.RGBAColor{0, 0, 68, 68}},
+ {"fillNil", fillBlue(90), nil, Over, image.RGBAColor{88, 0, 90, 255}},
+ {"fillNilSrc", fillBlue(90), nil, Src, image.RGBAColor{0, 0, 90, 90}},
+ // Uniform mask (100%, 75%, nil) and variable source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 48, 0, 90}.
+ {"copy", vgradGreen(90), fillAlpha(255), Over, image.RGBAColor{88, 48, 0, 255}},
+ {"copySrc", vgradGreen(90), fillAlpha(255), Src, image.RGBAColor{0, 48, 0, 90}},
+ {"copyAlpha", vgradGreen(90), fillAlpha(192), Over, image.RGBAColor{100, 36, 0, 255}},
+ {"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, image.RGBAColor{0, 36, 0, 68}},
+ {"copyNil", vgradGreen(90), nil, Over, image.RGBAColor{88, 48, 0, 255}},
+ {"copyNilSrc", vgradGreen(90), nil, Src, image.RGBAColor{0, 48, 0, 90}},
+ // Uniform mask (100%, 75%, nil) and variable NRGBA source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 136, 0, 90} in NRGBA-space, which is {0, 48, 0, 90} in RGBA-space.
+ // The result pixel is different than in the "copy*" test cases because of rounding errors.
+ {"nrgba", vgradGreenNRGBA(90), fillAlpha(255), Over, image.RGBAColor{88, 46, 0, 255}},
+ {"nrgbaSrc", vgradGreenNRGBA(90), fillAlpha(255), Src, image.RGBAColor{0, 46, 0, 90}},
+ {"nrgbaAlpha", vgradGreenNRGBA(90), fillAlpha(192), Over, image.RGBAColor{100, 34, 0, 255}},
+ {"nrgbaAlphaSrc", vgradGreenNRGBA(90), fillAlpha(192), Src, image.RGBAColor{0, 34, 0, 68}},
+ {"nrgbaNil", vgradGreenNRGBA(90), nil, Over, image.RGBAColor{88, 46, 0, 255}},
+ {"nrgbaNilSrc", vgradGreenNRGBA(90), nil, Src, image.RGBAColor{0, 46, 0, 90}},
+ // Uniform mask (100%, 75%, nil) and variable YCbCr source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 0, 136} in YCbCr-space, which is {11, 38, 0, 255} in RGB-space.
+ {"ycbcr", vgradCr(), fillAlpha(255), Over, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrSrc", vgradCr(), fillAlpha(255), Src, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrAlpha", vgradCr(), fillAlpha(192), Over, image.RGBAColor{42, 28, 0, 255}},
+ {"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, image.RGBAColor{8, 28, 0, 192}},
+ {"ycbcrNil", vgradCr(), nil, Over, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrNilSrc", vgradCr(), nil, Src, image.RGBAColor{11, 38, 0, 255}},
+ // Variable mask and variable source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 0, 255, 255}.
+ // The mask pixel's alpha is 102, or 40%.
+ {"generic", fillBlue(255), vgradAlpha(192), Over, image.RGBAColor{81, 0, 102, 255}},
+ {"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}},
+}
+
+func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
+ // Since golden is a newly allocated image, we don't have to check if the
+ // input source and mask images and the output golden image overlap.
+ b := dst.Bounds()
+ sb := src.Bounds()
+ mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
+ if mask != nil {
+ mb = mask.Bounds()
+ }
+ golden := image.NewRGBA(b.Max.X, b.Max.Y)
+ for y := r.Min.Y; y < r.Max.Y; y++ {
+ sy := y + sp.Y - r.Min.Y
+ my := y + mp.Y - r.Min.Y
+ for x := r.Min.X; x < r.Max.X; x++ {
+ if !(image.Point{x, y}.In(b)) {
+ continue
+ }
+ sx := x + sp.X - r.Min.X
+ if !(image.Point{sx, sy}.In(sb)) {
+ continue
+ }
+ mx := x + mp.X - r.Min.X
+ if !(image.Point{mx, my}.In(mb)) {
+ continue
+ }
+
+ const M = 1<<16 - 1
+ var dr, dg, db, da uint32
+ if op == Over {
+ dr, dg, db, da = dst.At(x, y).RGBA()
+ }
+ sr, sg, sb, sa := src.At(sx, sy).RGBA()
+ ma := uint32(M)
+ if mask != nil {
+ _, _, _, ma = mask.At(mx, my).RGBA()
+ }
+ a := M - (sa * ma / M)
+ golden.Set(x, y, image.RGBA64Color{
+ uint16((dr*a + sr*ma) / M),
+ uint16((dg*a + sg*ma) / M),
+ uint16((db*a + sb*ma) / M),
+ uint16((da*a + sa*ma) / M),
+ })
+ }
+ }
+ return golden.SubImage(b)
+}
+
+func TestDraw(t *testing.T) {
+ rr := []image.Rectangle{
+ image.Rect(0, 0, 0, 0),
+ image.Rect(0, 0, 16, 16),
+ image.Rect(3, 5, 12, 10),
+ image.Rect(0, 0, 9, 9),
+ image.Rect(8, 8, 16, 16),
+ image.Rect(8, 0, 9, 16),
+ image.Rect(0, 8, 16, 9),
+ image.Rect(8, 8, 9, 9),
+ image.Rect(8, 8, 8, 8),
+ }
+ for _, r := range rr {
+ loop:
+ for _, test := range drawTests {
+ dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
+ // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
+ b := dst.Bounds()
+ if !b.Eq(golden.Bounds()) {
+ t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
+ continue
+ }
+ // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
+ DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
+ if image.Pt(8, 8).In(r) {
+ // Check that the resultant pixel at (8, 8) matches what we expect
+ // (the expected value can be verified by hand).
+ if !eq(dst.At(8, 8), test.expected) {
+ t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
+ continue
+ }
+ }
+ // Check that the resultant dst image matches the golden output.
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(dst.At(x, y), golden.At(x, y)) {
+ t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
+ continue loop
+ }
+ }
+ }
+ }
+ }
+}
+
+func TestDrawOverlap(t *testing.T) {
+ for _, op := range []Op{Over, Src} {
+ for yoff := -2; yoff <= 2; yoff++ {
+ loop:
+ for xoff := -2; xoff <= 2; xoff++ {
+ m := gradYellow(127).(*image.RGBA)
+ dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
+ src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
+ b := dst.Bounds()
+ // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
+ if !b.Eq(golden.Bounds()) {
+ t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
+ continue
+ }
+ // Draw the same combination onto the actual dst using the optimized DrawMask implementation.
+ DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
+ // Check that the resultant dst image matches the golden output.
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(dst.At(x, y), golden.At(x, y)) {
+ t.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff, yoff, x, y, dst.At(x, y), golden.At(x, y))
+ continue loop
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// TestNonZeroSrcPt checks drawing with a non-zero src point parameter.
+func TestNonZeroSrcPt(t *testing.T) {
+ a := image.NewRGBA(1, 1)
+ b := image.NewRGBA(2, 2)
+ b.Set(0, 0, image.RGBAColor{0, 0, 0, 5})
+ b.Set(1, 0, image.RGBAColor{0, 0, 5, 5})
+ b.Set(0, 1, image.RGBAColor{0, 5, 0, 5})
+ b.Set(1, 1, image.RGBAColor{5, 0, 0, 5})
+ Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over)
+ if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) {
+ t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0))
+ }
+}
+
+func TestFill(t *testing.T) {
+ rr := []image.Rectangle{
+ image.Rect(0, 0, 0, 0),
+ image.Rect(0, 0, 40, 30),
+ image.Rect(10, 0, 40, 30),
+ image.Rect(0, 20, 40, 30),
+ image.Rect(10, 20, 40, 30),
+ image.Rect(10, 20, 15, 25),
+ image.Rect(10, 0, 35, 30),
+ image.Rect(0, 15, 40, 16),
+ image.Rect(24, 24, 25, 25),
+ image.Rect(23, 23, 26, 26),
+ image.Rect(22, 22, 27, 27),
+ image.Rect(21, 21, 28, 28),
+ image.Rect(20, 20, 29, 29),
+ }
+ for _, r := range rr {
+ m := image.NewRGBA(40, 30).SubImage(r).(*image.RGBA)
+ b := m.Bounds()
+ c := image.RGBAColor{11, 0, 0, 255}
+ src := &image.ColorImage{c}
+ check := func(desc string) {
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ if !eq(c, m.At(x, y)) {
+ t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
+ return
+ }
+ }
+ }
+ }
+ // Draw 1 pixel at a time.
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
+ DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
+ }
+ }
+ check("pixel")
+ // Draw 1 row at a time.
+ c = image.RGBAColor{0, 22, 0, 255}
+ src = &image.ColorImage{c}
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
+ }
+ check("row")
+ // Draw 1 column at a time.
+ c = image.RGBAColor{0, 0, 33, 255}
+ src = &image.ColorImage{c}
+ for x := b.Min.X; x < b.Max.X; x++ {
+ DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
+ }
+ check("column")
+ // Draw the whole image at once.
+ c = image.RGBAColor{44, 55, 66, 77}
+ src = &image.ColorImage{c}
+ DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
+ check("whole")
+ }
+}
diff --git a/libgo/go/image/geom.go b/libgo/go/image/geom.go
index ccfe9cd..667aee6 100644
--- a/libgo/go/image/geom.go
+++ b/libgo/go/image/geom.go
@@ -38,6 +38,12 @@ func (p Point) Div(k int) Point {
return Point{p.X / k, p.Y / k}
}
+// In returns whether p is in r.
+func (p Point) In(r Rectangle) bool {
+ return r.Min.X <= p.X && p.X < r.Max.X &&
+ r.Min.Y <= p.Y && p.Y < r.Max.Y
+}
+
// Mod returns the point q in r such that p.X-q.X is a multiple of r's width
// and p.Y-q.Y is a multiple of r's height.
func (p Point) Mod(r Rectangle) Point {
@@ -190,10 +196,15 @@ func (r Rectangle) Overlaps(s Rectangle) bool {
r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y
}
-// Contains returns whether r contains p.
-func (r Rectangle) Contains(p Point) bool {
- return p.X >= r.Min.X && p.X < r.Max.X &&
- p.Y >= r.Min.Y && p.Y < r.Max.Y
+// In returns whether every point in r is in s.
+func (r Rectangle) In(s Rectangle) bool {
+ if r.Empty() {
+ return true
+ }
+ // Note that r.Max is an exclusive bound for r, so that r.In(s)
+ // does not require that r.Max.In(s).
+ return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X &&
+ s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y
}
// Canon returns the canonical version of r. The returned rectangle has minimum
diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go
index d37f526..e39b797 100644
--- a/libgo/go/image/gif/reader.go
+++ b/libgo/go/image/gif/reader.go
@@ -28,7 +28,9 @@ const (
fColorMapFollows = 1 << 7
// Image fields.
- ifInterlace = 1 << 6
+ ifLocalColorTable = 1 << 7
+ ifInterlace = 1 << 6
+ ifPixelSizeMask = 7
// Graphic control flags.
gcTransparentColorSet = 1 << 0
@@ -94,28 +96,26 @@ type blockReader struct {
tmp [256]byte
}
-func (b *blockReader) Read(p []byte) (n int, err os.Error) {
+func (b *blockReader) Read(p []byte) (int, os.Error) {
if len(p) == 0 {
- return
+ return 0, nil
}
- if len(b.slice) > 0 {
- n = copy(p, b.slice)
- b.slice = b.slice[n:]
- return
- }
- var blockLen uint8
- blockLen, err = b.r.ReadByte()
- if err != nil {
- return
- }
- if blockLen == 0 {
- return 0, os.EOF
- }
- b.slice = b.tmp[0:blockLen]
- if _, err = io.ReadFull(b.r, b.slice); err != nil {
- return
+ if len(b.slice) == 0 {
+ blockLen, err := b.r.ReadByte()
+ if err != nil {
+ return 0, err
+ }
+ if blockLen == 0 {
+ return 0, os.EOF
+ }
+ b.slice = b.tmp[0:blockLen]
+ if _, err = io.ReadFull(b.r, b.slice); err != nil {
+ return 0, err
+ }
}
- return b.Read(p)
+ n := copy(p, b.slice)
+ b.slice = b.slice[n:]
+ return n, nil
}
// decode reads a GIF image from r and stores the result in d.
@@ -141,8 +141,6 @@ func (d *decoder) decode(r io.Reader, configOnly bool) os.Error {
}
}
- d.image = nil
-
Loop:
for err == nil {
var c byte
@@ -175,11 +173,10 @@ Loop:
if err != nil {
return err
}
- if litWidth > 8 {
+ if litWidth < 2 || litWidth > 8 {
return fmt.Errorf("gif: pixel size in decode out of range: %d", litWidth)
}
- // A wonderfully Go-like piece of magic. Unfortunately it's only at its
- // best for 8-bit pixels.
+ // A wonderfully Go-like piece of magic.
lzwr := lzw.NewReader(&blockReader{r: d.r}, lzw.LSB, int(litWidth))
if _, err = io.ReadFull(lzwr, m.Pix); err != nil {
break
@@ -191,8 +188,14 @@ Loop:
return err
}
if c != 0 {
- return os.ErrorString("gif: extra data after image")
+ return os.NewError("gif: extra data after image")
+ }
+
+ // Undo the interlacing if necessary.
+ if d.imageFields&ifInterlace != 0 {
+ uninterlace(m)
}
+
d.image = append(d.image, m)
d.delay = append(d.delay, d.delayTime)
d.delayTime = 0 // TODO: is this correct, or should we hold on to the value?
@@ -237,6 +240,9 @@ func (d *decoder) readColorMap() (image.PalettedColorModel, os.Error) {
return nil, fmt.Errorf("gif: can't handle %d bits per pixel", d.pixelSize)
}
numColors := 1 << d.pixelSize
+ if d.imageFields&ifLocalColorTable != 0 {
+ numColors = 1 << ((d.imageFields & ifPixelSizeMask) + 1)
+ }
numValues := 3 * numColors
_, err := io.ReadFull(d.r, d.tmp[0:numValues])
if err != nil {
@@ -275,7 +281,7 @@ func (d *decoder) readExtension() os.Error {
return fmt.Errorf("gif: unknown extension 0x%.2x", extension)
}
if size > 0 {
- if _, err := d.r.Read(d.tmp[0:size]); err != nil {
+ if _, err := io.ReadFull(d.r, d.tmp[0:size]); err != nil {
return err
}
}
@@ -323,15 +329,15 @@ func (d *decoder) newImageFromDescriptor() (*image.Paletted, os.Error) {
if _, err := io.ReadFull(d.r, d.tmp[0:9]); err != nil {
return nil, fmt.Errorf("gif: can't read image descriptor: %s", err)
}
- _ = int(d.tmp[0]) + int(d.tmp[1])<<8 // TODO: honor left value
- _ = int(d.tmp[2]) + int(d.tmp[3])<<8 // TODO: honor top value
+ left := int(d.tmp[0]) + int(d.tmp[1])<<8
+ top := int(d.tmp[2]) + int(d.tmp[3])<<8
width := int(d.tmp[4]) + int(d.tmp[5])<<8
height := int(d.tmp[6]) + int(d.tmp[7])<<8
d.imageFields = d.tmp[8]
- if d.imageFields&ifInterlace != 0 {
- return nil, os.ErrorString("gif: can't handle interlaced images")
- }
- return image.NewPaletted(width, height, nil), nil
+ m := image.NewPaletted(width, height, nil)
+ // Overwrite the rectangle to take account of left and top.
+ m.Rect = image.Rect(left, top, left+width, top+height)
+ return m, nil
}
func (d *decoder) readBlock() (int, os.Error) {
@@ -342,9 +348,39 @@ func (d *decoder) readBlock() (int, os.Error) {
return io.ReadFull(d.r, d.tmp[0:n])
}
+// interlaceScan defines the ordering for a pass of the interlace algorithm.
+type interlaceScan struct {
+ skip, start int
+}
+
+// interlacing represents the set of scans in an interlaced GIF image.
+var interlacing = []interlaceScan{
+ {8, 0}, // Group 1 : Every 8th. row, starting with row 0.
+ {8, 4}, // Group 2 : Every 8th. row, starting with row 4.
+ {4, 2}, // Group 3 : Every 4th. row, starting with row 2.
+ {2, 1}, // Group 4 : Every 2nd. row, starting with row 1.
+}
+
+// uninterlace rearranges the pixels in m to account for interlaced input.
+func uninterlace(m *image.Paletted) {
+ var nPix []uint8
+ dx := m.Bounds().Dx()
+ dy := m.Bounds().Dy()
+ nPix = make([]uint8, dx*dy)
+ offset := 0 // steps through the input by sequential scan lines.
+ for _, pass := range interlacing {
+ nOffset := pass.start * dx // steps through the output as defined by pass.
+ for y := pass.start; y < dy; y += pass.skip {
+ copy(nPix[nOffset:nOffset+dx], m.Pix[offset:offset+dx])
+ offset += dx
+ nOffset += dx * pass.skip
+ }
+ }
+ m.Pix = nPix
+}
+
// Decode reads a GIF image from r and returns the first embedded
// image as an image.Image.
-// Limitation: The file must be 8 bits per pixel and have no interlacing.
func Decode(r io.Reader) (image.Image, os.Error) {
var d decoder
if err := d.decode(r, false); err != nil {
@@ -362,7 +398,6 @@ type GIF struct {
// DecodeAll reads a GIF image from r and returns the sequential frames
// and timing information.
-// Limitation: The file must be 8 bits per pixel and have no interlacing.
func DecodeAll(r io.Reader) (*GIF, os.Error) {
var d decoder
if err := d.decode(r, false); err != nil {
@@ -376,15 +411,14 @@ func DecodeAll(r io.Reader) (*GIF, os.Error) {
return gif, nil
}
-// DecodeConfig returns the color model and dimensions of a GIF image without
-// decoding the entire image.
+// DecodeConfig returns the global color model and dimensions of a GIF image
+// without decoding the entire image.
func DecodeConfig(r io.Reader) (image.Config, os.Error) {
var d decoder
if err := d.decode(r, true); err != nil {
return image.Config{}, err
}
- colorMap := d.globalColorMap
- return image.Config{colorMap, d.width, d.height}, nil
+ return image.Config{d.globalColorMap, d.width, d.height}, nil
}
func init() {
diff --git a/libgo/go/image/image.go b/libgo/go/image/image.go
index 4350acc..11def94 100644
--- a/libgo/go/image/image.go
+++ b/libgo/go/image/image.go
@@ -5,13 +5,13 @@
// Package image implements a basic 2-D image library.
package image
-// A Config consists of an image's color model and dimensions.
+// Config holds an image's color model and dimensions.
type Config struct {
ColorModel ColorModel
Width, Height int
}
-// An Image is a finite rectangular grid of Colors drawn from a ColorModel.
+// Image is a finite rectangular grid of Colors drawn from a ColorModel.
type Image interface {
// ColorModel returns the Image's ColorModel.
ColorModel() ColorModel
@@ -24,10 +24,12 @@ type Image interface {
At(x, y int) Color
}
-// An RGBA is an in-memory image of RGBAColor values.
+// RGBA is an in-memory image of RGBAColor values.
type RGBA struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []RGBAColor
+ // Pix holds the image's pixels, in R, G, B, A order. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -38,24 +40,52 @@ func (p *RGBA) ColorModel() ColorModel { return RGBAColorModel }
func (p *RGBA) Bounds() Rectangle { return p.Rect }
func (p *RGBA) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return RGBAColor{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ return RGBAColor{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]}
}
func (p *RGBA) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ c1 := toRGBAColor(c).(RGBAColor)
+ p.Pix[i+0] = c1.R
+ p.Pix[i+1] = c1.G
+ p.Pix[i+2] = c1.B
+ p.Pix[i+3] = c1.A
}
func (p *RGBA) SetRGBA(x, y int, c RGBAColor) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ p.Pix[i+0] = c.R
+ p.Pix[i+1] = c.G
+ p.Pix[i+2] = c.B
+ p.Pix[i+3] = c.A
+}
+
+// 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 *RGBA) 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 &RGBA{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*4
+ return &RGBA{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -63,11 +93,10 @@ func (p *RGBA) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 3, p.Rect.Dx()*4
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xff {
+ for i := i0; i < i1; i += 4 {
+ if p.Pix[i] != 0xff {
return false
}
}
@@ -79,14 +108,16 @@ func (p *RGBA) Opaque() bool {
// NewRGBA returns a new RGBA with the given width and height.
func NewRGBA(w, h int) *RGBA {
- buf := make([]RGBAColor, w*h)
- return &RGBA{buf, w, Rectangle{ZP, Point{w, h}}}
+ buf := make([]uint8, 4*w*h)
+ return &RGBA{buf, 4 * w, Rectangle{ZP, Point{w, h}}}
}
-// An RGBA64 is an in-memory image of RGBA64Color values.
+// RGBA64 is an in-memory image of RGBA64Color values.
type RGBA64 struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []RGBA64Color
+ // Pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*8].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -97,24 +128,65 @@ func (p *RGBA64) ColorModel() ColorModel { return RGBA64ColorModel }
func (p *RGBA64) Bounds() Rectangle { return p.Rect }
func (p *RGBA64) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return RGBA64Color{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ return RGBA64Color{
+ uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]),
+ uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]),
+ uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]),
+ uint16(p.Pix[i+6])<<8 | uint16(p.Pix[i+7]),
+ }
}
func (p *RGBA64) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ c1 := toRGBA64Color(c).(RGBA64Color)
+ p.Pix[i+0] = uint8(c1.R >> 8)
+ p.Pix[i+1] = uint8(c1.R)
+ p.Pix[i+2] = uint8(c1.G >> 8)
+ p.Pix[i+3] = uint8(c1.G)
+ p.Pix[i+4] = uint8(c1.B >> 8)
+ p.Pix[i+5] = uint8(c1.B)
+ p.Pix[i+6] = uint8(c1.A >> 8)
+ p.Pix[i+7] = uint8(c1.A)
}
func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ p.Pix[i+0] = uint8(c.R >> 8)
+ p.Pix[i+1] = uint8(c.R)
+ p.Pix[i+2] = uint8(c.G >> 8)
+ p.Pix[i+3] = uint8(c.G)
+ p.Pix[i+4] = uint8(c.B >> 8)
+ p.Pix[i+5] = uint8(c.B)
+ p.Pix[i+6] = uint8(c.A >> 8)
+ p.Pix[i+7] = uint8(c.A)
+}
+
+// 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 *RGBA64) 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 &RGBA64{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*8
+ return &RGBA64{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -122,11 +194,10 @@ func (p *RGBA64) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 6, p.Rect.Dx()*8
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xffff {
+ for i := i0; i < i1; i += 8 {
+ if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff {
return false
}
}
@@ -138,14 +209,16 @@ func (p *RGBA64) Opaque() bool {
// NewRGBA64 returns a new RGBA64 with the given width and height.
func NewRGBA64(w, h int) *RGBA64 {
- pix := make([]RGBA64Color, w*h)
- return &RGBA64{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 8*w*h)
+ return &RGBA64{pix, 8 * w, Rectangle{ZP, Point{w, h}}}
}
-// An NRGBA is an in-memory image of NRGBAColor values.
+// NRGBA is an in-memory image of NRGBAColor values.
type NRGBA struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []NRGBAColor
+ // Pix holds the image's pixels, in R, G, B, A order. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -156,24 +229,52 @@ func (p *NRGBA) ColorModel() ColorModel { return NRGBAColorModel }
func (p *NRGBA) Bounds() Rectangle { return p.Rect }
func (p *NRGBA) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return NRGBAColor{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ return NRGBAColor{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2], p.Pix[i+3]}
}
func (p *NRGBA) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ c1 := toNRGBAColor(c).(NRGBAColor)
+ p.Pix[i+0] = c1.R
+ p.Pix[i+1] = c1.G
+ p.Pix[i+2] = c1.B
+ p.Pix[i+3] = c1.A
}
func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
+ p.Pix[i+0] = c.R
+ p.Pix[i+1] = c.G
+ p.Pix[i+2] = c.B
+ p.Pix[i+3] = c.A
+}
+
+// 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 *NRGBA) 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 &NRGBA{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*4
+ return &NRGBA{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -181,11 +282,10 @@ func (p *NRGBA) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 3, p.Rect.Dx()*4
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xff {
+ for i := i0; i < i1; i += 4 {
+ if p.Pix[i] != 0xff {
return false
}
}
@@ -197,14 +297,16 @@ func (p *NRGBA) Opaque() bool {
// NewNRGBA returns a new NRGBA with the given width and height.
func NewNRGBA(w, h int) *NRGBA {
- pix := make([]NRGBAColor, w*h)
- return &NRGBA{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 4*w*h)
+ return &NRGBA{pix, 4 * w, Rectangle{ZP, Point{w, h}}}
}
-// An NRGBA64 is an in-memory image of NRGBA64Color values.
+// NRGBA64 is an in-memory image of NRGBA64Color values.
type NRGBA64 struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []NRGBA64Color
+ // Pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*8].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -215,24 +317,65 @@ func (p *NRGBA64) ColorModel() ColorModel { return NRGBA64ColorModel }
func (p *NRGBA64) Bounds() Rectangle { return p.Rect }
func (p *NRGBA64) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return NRGBA64Color{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ return NRGBA64Color{
+ uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]),
+ uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]),
+ uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]),
+ uint16(p.Pix[i+6])<<8 | uint16(p.Pix[i+7]),
+ }
}
func (p *NRGBA64) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ c1 := toNRGBA64Color(c).(NRGBA64Color)
+ p.Pix[i+0] = uint8(c1.R >> 8)
+ p.Pix[i+1] = uint8(c1.R)
+ p.Pix[i+2] = uint8(c1.G >> 8)
+ p.Pix[i+3] = uint8(c1.G)
+ p.Pix[i+4] = uint8(c1.B >> 8)
+ p.Pix[i+5] = uint8(c1.B)
+ p.Pix[i+6] = uint8(c1.A >> 8)
+ p.Pix[i+7] = uint8(c1.A)
}
func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*8
+ p.Pix[i+0] = uint8(c.R >> 8)
+ p.Pix[i+1] = uint8(c.R)
+ p.Pix[i+2] = uint8(c.G >> 8)
+ p.Pix[i+3] = uint8(c.G)
+ p.Pix[i+4] = uint8(c.B >> 8)
+ p.Pix[i+5] = uint8(c.B)
+ p.Pix[i+6] = uint8(c.A >> 8)
+ p.Pix[i+7] = uint8(c.A)
+}
+
+// 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 *NRGBA64) 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 &NRGBA64{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*8
+ return &NRGBA64{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -240,11 +383,10 @@ func (p *NRGBA64) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 6, p.Rect.Dx()*8
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xffff {
+ for i := i0; i < i1; i += 8 {
+ if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff {
return false
}
}
@@ -256,14 +398,16 @@ func (p *NRGBA64) Opaque() bool {
// NewNRGBA64 returns a new NRGBA64 with the given width and height.
func NewNRGBA64(w, h int) *NRGBA64 {
- pix := make([]NRGBA64Color, w*h)
- return &NRGBA64{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 8*w*h)
+ return &NRGBA64{pix, 8 * w, Rectangle{ZP, Point{w, h}}}
}
-// An Alpha is an in-memory image of AlphaColor values.
+// Alpha is an in-memory image of AlphaColor values.
type Alpha struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []AlphaColor
+ // Pix holds the image's pixels, as alpha values. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -274,24 +418,45 @@ func (p *Alpha) ColorModel() ColorModel { return AlphaColorModel }
func (p *Alpha) Bounds() Rectangle { return p.Rect }
func (p *Alpha) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return AlphaColor{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ return AlphaColor{p.Pix[i]}
}
func (p *Alpha) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = toAlphaColor(c).(AlphaColor).A
}
func (p *Alpha) SetAlpha(x, y int, c AlphaColor) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = c.A
+}
+
+// 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 *Alpha) 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 &Alpha{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1
+ return &Alpha{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -299,11 +464,10 @@ func (p *Alpha) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 0, p.Rect.Dx()
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xff {
+ for i := i0; i < i1; i++ {
+ if p.Pix[i] != 0xff {
return false
}
}
@@ -315,14 +479,16 @@ func (p *Alpha) Opaque() bool {
// NewAlpha returns a new Alpha with the given width and height.
func NewAlpha(w, h int) *Alpha {
- pix := make([]AlphaColor, w*h)
- return &Alpha{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 1*w*h)
+ return &Alpha{pix, 1 * w, Rectangle{ZP, Point{w, h}}}
}
-// An Alpha16 is an in-memory image of Alpha16Color values.
+// Alpha16 is an in-memory image of Alpha16Color values.
type Alpha16 struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []Alpha16Color
+ // Pix holds the image's pixels, as alpha values in big-endian format. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*2].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -333,24 +499,48 @@ func (p *Alpha16) ColorModel() ColorModel { return Alpha16ColorModel }
func (p *Alpha16) Bounds() Rectangle { return p.Rect }
func (p *Alpha16) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return Alpha16Color{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ return Alpha16Color{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])}
}
func (p *Alpha16) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ c1 := toAlpha16Color(c).(Alpha16Color)
+ p.Pix[i+0] = uint8(c1.A >> 8)
+ p.Pix[i+1] = uint8(c1.A)
}
func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ p.Pix[i+0] = uint8(c.A >> 8)
+ p.Pix[i+1] = uint8(c.A)
+}
+
+// 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 *Alpha16) 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 &Alpha16{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*2
+ return &Alpha16{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -358,11 +548,10 @@ func (p *Alpha16) Opaque() bool {
if p.Rect.Empty() {
return true
}
- base := p.Rect.Min.Y * p.Stride
- i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
+ i0, i1 := 0, p.Rect.Dx()*2
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
- for _, c := range p.Pix[i0:i1] {
- if c.A != 0xffff {
+ for i := i0; i < i1; i += 2 {
+ if p.Pix[i+0] != 0xff || p.Pix[i+1] != 0xff {
return false
}
}
@@ -374,14 +563,16 @@ func (p *Alpha16) Opaque() bool {
// NewAlpha16 returns a new Alpha16 with the given width and height.
func NewAlpha16(w, h int) *Alpha16 {
- pix := make([]Alpha16Color, w*h)
- return &Alpha16{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 2*w*h)
+ return &Alpha16{pix, 2 * w, Rectangle{ZP, Point{w, h}}}
}
-// A Gray is an in-memory image of GrayColor values.
+// Gray is an in-memory image of GrayColor values.
type Gray struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []GrayColor
+ // Pix holds the image's pixels, as gray values. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -392,24 +583,45 @@ func (p *Gray) ColorModel() ColorModel { return GrayColorModel }
func (p *Gray) Bounds() Rectangle { return p.Rect }
func (p *Gray) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return GrayColor{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ return GrayColor{p.Pix[i]}
}
func (p *Gray) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = toGrayColor(c).(GrayColor).Y
}
func (p *Gray) SetGray(x, y int, c GrayColor) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = c.Y
+}
+
+// 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 *Gray) 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 &Gray{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1
+ return &Gray{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -419,14 +631,16 @@ func (p *Gray) Opaque() bool {
// NewGray returns a new Gray with the given width and height.
func NewGray(w, h int) *Gray {
- pix := make([]GrayColor, w*h)
- return &Gray{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 1*w*h)
+ return &Gray{pix, 1 * w, Rectangle{ZP, Point{w, h}}}
}
-// A Gray16 is an in-memory image of Gray16Color values.
+// Gray16 is an in-memory image of Gray16Color values.
type Gray16 struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []Gray16Color
+ // Pix holds the image's pixels, as gray values in big-endian format. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*2].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -437,24 +651,48 @@ func (p *Gray16) ColorModel() ColorModel { return Gray16ColorModel }
func (p *Gray16) Bounds() Rectangle { return p.Rect }
func (p *Gray16) At(x, y int) Color {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return Gray16Color{}
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ return Gray16Color{uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])}
}
func (p *Gray16) Set(x, y int, c Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color)
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ c1 := toGray16Color(c).(Gray16Color)
+ p.Pix[i+0] = uint8(c1.Y >> 8)
+ p.Pix[i+1] = uint8(c1.Y)
}
func (p *Gray16) SetGray16(x, y int, c Gray16Color) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = c
+ i := (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2
+ p.Pix[i+0] = uint8(c.Y >> 8)
+ p.Pix[i+1] = uint8(c.Y)
+}
+
+// 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 *Gray16) 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 &Gray16{}
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*2
+ return &Gray16{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: r,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
@@ -464,11 +702,11 @@ func (p *Gray16) Opaque() bool {
// NewGray16 returns a new Gray16 with the given width and height.
func NewGray16(w, h int) *Gray16 {
- pix := make([]Gray16Color, w*h)
- return &Gray16{pix, w, Rectangle{ZP, Point{w, h}}}
+ pix := make([]uint8, 2*w*h)
+ return &Gray16{pix, 2 * w, Rectangle{ZP, Point{w, h}}}
}
-// A PalettedColorModel represents a fixed palette of colors.
+// A PalettedColorModel represents a fixed palette of at most 256 colors.
type PalettedColorModel []Color
func diff(a, b uint32) uint32 {
@@ -483,14 +721,19 @@ func (p PalettedColorModel) Convert(c Color) Color {
if len(p) == 0 {
return nil
}
+ return p[p.Index(c)]
+}
+
+// Index returns the index of the palette color closest to c in Euclidean
+// R,G,B space.
+func (p PalettedColorModel) Index(c Color) int {
cr, cg, cb, _ := c.RGBA()
// Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference.
cr >>= 1
cg >>= 1
cb >>= 1
- result := Color(nil)
- bestSSD := uint32(1<<32 - 1)
- for _, v := range p {
+ ret, bestSSD := 0, uint32(1<<32-1)
+ for i, v := range p {
vr, vg, vb, _ := v.RGBA()
vr >>= 1
vg >>= 1
@@ -498,17 +741,18 @@ func (p PalettedColorModel) Convert(c Color) Color {
dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb)
ssd := (dr * dr) + (dg * dg) + (db * db)
if ssd < bestSSD {
- bestSSD = ssd
- result = v
+ ret, bestSSD = i, ssd
}
}
- return result
+ return ret
}
-// A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel.
+// Paletted is an in-memory image of uint8 indices into a given palette.
type Paletted struct {
- // Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x].
- Pix []uint8
+ // Pix holds the image's pixels, as palette indices. The pixel at
+ // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*1].
+ Pix []uint8
+ // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
@@ -524,29 +768,73 @@ func (p *Paletted) At(x, y int) Color {
if len(p.Palette) == 0 {
return nil
}
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return p.Palette[0]
}
- return p.Palette[p.Pix[y*p.Stride+x]]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ return p.Palette[p.Pix[i]]
+}
+
+func (p *Paletted) Set(x, y int, c Color) {
+ if !(Point{x, y}.In(p.Rect)) {
+ return
+ }
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = uint8(p.Palette.Index(c))
}
func (p *Paletted) ColorIndexAt(x, y int) uint8 {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return 0
}
- return p.Pix[y*p.Stride+x]
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ return p.Pix[i]
}
func (p *Paletted) SetColorIndex(x, y int, index uint8) {
- if !p.Rect.Contains(Point{x, y}) {
+ if !(Point{x, y}.In(p.Rect)) {
return
}
- p.Pix[y*p.Stride+x] = index
+ i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
+ p.Pix[i] = index
+}
+
+// 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 *Paletted) 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 &Paletted{
+ Palette: p.Palette,
+ }
+ }
+ i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X-p.Rect.Min.X)*1
+ return &Paletted{
+ Pix: p.Pix[i:],
+ Stride: p.Stride,
+ Rect: p.Rect.Intersect(r),
+ Palette: p.Palette,
+ }
}
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Paletted) Opaque() bool {
- for _, c := range p.Palette {
+ var present [256]bool
+ i0, i1 := 0, p.Rect.Dx()
+ for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
+ for _, c := range p.Pix[i0:i1] {
+ present[c] = true
+ }
+ i0 += p.Stride
+ i1 += p.Stride
+ }
+ for i, c := range p.Palette {
+ if !present[i] {
+ continue
+ }
_, _, _, a := c.RGBA()
if a != 0xffff {
return false
@@ -557,6 +845,6 @@ func (p *Paletted) Opaque() bool {
// NewPaletted returns a new Paletted with the given width, height and palette.
func NewPaletted(w, h int, m PalettedColorModel) *Paletted {
- pix := make([]uint8, w*h)
- return &Paletted{pix, w, Rectangle{ZP, Point{w, h}}, m}
+ pix := make([]uint8, 1*w*h)
+ return &Paletted{pix, 1 * w, Rectangle{ZP, Point{w, h}}, m}
}
diff --git a/libgo/go/image/image_test.go b/libgo/go/image/image_test.go
new file mode 100644
index 0000000..9519acf
--- /dev/null
+++ b/libgo/go/image/image_test.go
@@ -0,0 +1,113 @@
+// 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_test
+
+import (
+ . "image"
+ "testing"
+)
+
+type image interface {
+ Image
+ Opaque() bool
+ Set(int, int, Color)
+ SubImage(Rectangle) Image
+}
+
+func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool {
+ r0, g0, b0, a0 := cm.Convert(c0).RGBA()
+ r1, g1, b1, a1 := cm.Convert(c1).RGBA()
+ return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
+}
+
+func TestImage(t *testing.T) {
+ testImage := []image{
+ NewRGBA(10, 10),
+ NewRGBA64(10, 10),
+ NewNRGBA(10, 10),
+ NewNRGBA64(10, 10),
+ NewAlpha(10, 10),
+ NewAlpha16(10, 10),
+ NewGray(10, 10),
+ NewGray16(10, 10),
+ NewPaletted(10, 10, PalettedColorModel{
+ Transparent,
+ Opaque,
+ }),
+ }
+ for _, m := range testImage {
+ if !Rect(0, 0, 10, 10).Eq(m.Bounds()) {
+ t.Errorf("%T: want bounds %v, got %v", m, Rect(0, 0, 10, 10), m.Bounds())
+ continue
+ }
+ if !cmp(t, m.ColorModel(), Transparent, m.At(6, 3)) {
+ t.Errorf("%T: at (6, 3), want a zero color, got %v", m, m.At(6, 3))
+ continue
+ }
+ m.Set(6, 3, Opaque)
+ if !cmp(t, m.ColorModel(), Opaque, m.At(6, 3)) {
+ t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
+ continue
+ }
+ if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() {
+ t.Errorf("%T: at (6, 3) was not opaque", m)
+ continue
+ }
+ m = m.SubImage(Rect(3, 2, 9, 8)).(image)
+ if !Rect(3, 2, 9, 8).Eq(m.Bounds()) {
+ t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds())
+ continue
+ }
+ if !cmp(t, m.ColorModel(), Opaque, m.At(6, 3)) {
+ t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", m, m.At(6, 3))
+ continue
+ }
+ if !cmp(t, m.ColorModel(), Transparent, m.At(3, 3)) {
+ t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", m, m.At(3, 3))
+ continue
+ }
+ m.Set(3, 3, Opaque)
+ if !cmp(t, m.ColorModel(), Opaque, m.At(3, 3)) {
+ t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3))
+ continue
+ }
+ // Test that taking an empty sub-image starting at a corner does not panic.
+ m.SubImage(Rect(0, 0, 0, 0))
+ m.SubImage(Rect(10, 0, 10, 0))
+ m.SubImage(Rect(0, 10, 0, 10))
+ m.SubImage(Rect(10, 10, 10, 10))
+ }
+}
+
+func Test16BitsPerColorChannel(t *testing.T) {
+ testColorModel := []ColorModel{
+ RGBA64ColorModel,
+ NRGBA64ColorModel,
+ Alpha16ColorModel,
+ Gray16ColorModel,
+ }
+ for _, cm := range testColorModel {
+ c := cm.Convert(RGBA64Color{0x1234, 0x1234, 0x1234, 0x1234}) // Premultiplied alpha.
+ r, _, _, _ := c.RGBA()
+ if r != 0x1234 {
+ t.Errorf("%T: want red value 0x%04x got 0x%04x", c, 0x1234, r)
+ continue
+ }
+ }
+ testImage := []image{
+ NewRGBA64(10, 10),
+ NewNRGBA64(10, 10),
+ NewAlpha16(10, 10),
+ NewGray16(10, 10),
+ }
+ for _, m := range testImage {
+ m.Set(1, 2, NRGBA64Color{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha.
+ r, _, _, _ := m.At(1, 2).RGBA()
+ if r != 0x1357 {
+ t.Errorf("%T: want red value 0x%04x got 0x%04x", m, 0x1357, r)
+ continue
+ }
+ }
+}
diff --git a/libgo/go/image/jpeg/idct.go b/libgo/go/image/jpeg/idct.go
index e5a2f40..b387dfd 100644
--- a/libgo/go/image/jpeg/idct.go
+++ b/libgo/go/image/jpeg/idct.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+package jpeg
+
// This is a Go translation of idct.c from
//
// http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/verifier/mpeg2decode_960109.tar.gz
@@ -35,8 +37,6 @@
*
*/
-package jpeg
-
const (
w1 = 2841 // 2048*sqrt(2)*cos(1*pi/16)
w2 = 2676 // 2048*sqrt(2)*cos(2*pi/16)
@@ -55,41 +55,45 @@ const (
r2 = 181 // 256/sqrt(2)
)
-// 2-D Inverse Discrete Cosine Transformation, followed by a +128 level shift.
+// idct performs a 2-D Inverse Discrete Cosine Transformation, followed by a
+// +128 level shift and a clip to [0, 255], writing the results to dst.
+// stride is the number of elements between successive rows of dst.
//
-// The input coefficients should already have been multiplied by the appropriate quantization table.
-// We use fixed-point computation, with the number of bits for the fractional component varying over the
-// intermediate stages. The final values are expected to range within [0, 255], after a +128 level shift.
+// The input coefficients should already have been multiplied by the
+// appropriate quantization table. We use fixed-point computation, with the
+// number of bits for the fractional component varying over the intermediate
+// stages.
//
-// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the discrete W transform and
-// for the discrete Fourier transform", IEEE Trans. on ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
-func idct(b *block) {
+// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
+// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
+// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
+func idct(dst []byte, stride int, src *block) {
// Horizontal 1-D IDCT.
for y := 0; y < 8; y++ {
// If all the AC components are zero, then the IDCT is trivial.
- if b[y*8+1] == 0 && b[y*8+2] == 0 && b[y*8+3] == 0 &&
- b[y*8+4] == 0 && b[y*8+5] == 0 && b[y*8+6] == 0 && b[y*8+7] == 0 {
- dc := b[y*8+0] << 3
- b[y*8+0] = dc
- b[y*8+1] = dc
- b[y*8+2] = dc
- b[y*8+3] = dc
- b[y*8+4] = dc
- b[y*8+5] = dc
- b[y*8+6] = dc
- b[y*8+7] = dc
+ if src[y*8+1] == 0 && src[y*8+2] == 0 && src[y*8+3] == 0 &&
+ src[y*8+4] == 0 && src[y*8+5] == 0 && src[y*8+6] == 0 && src[y*8+7] == 0 {
+ dc := src[y*8+0] << 3
+ src[y*8+0] = dc
+ src[y*8+1] = dc
+ src[y*8+2] = dc
+ src[y*8+3] = dc
+ src[y*8+4] = dc
+ src[y*8+5] = dc
+ src[y*8+6] = dc
+ src[y*8+7] = dc
continue
}
// Prescale.
- x0 := (b[y*8+0] << 11) + 128
- x1 := b[y*8+4] << 11
- x2 := b[y*8+6]
- x3 := b[y*8+2]
- x4 := b[y*8+1]
- x5 := b[y*8+7]
- x6 := b[y*8+5]
- x7 := b[y*8+3]
+ x0 := (src[y*8+0] << 11) + 128
+ x1 := src[y*8+4] << 11
+ x2 := src[y*8+6]
+ x3 := src[y*8+2]
+ x4 := src[y*8+1]
+ x5 := src[y*8+7]
+ x6 := src[y*8+5]
+ x7 := src[y*8+3]
// Stage 1.
x8 := w7 * (x4 + x5)
@@ -119,14 +123,14 @@ func idct(b *block) {
x4 = (r2*(x4-x5) + 128) >> 8
// Stage 4.
- b[8*y+0] = (x7 + x1) >> 8
- b[8*y+1] = (x3 + x2) >> 8
- b[8*y+2] = (x0 + x4) >> 8
- b[8*y+3] = (x8 + x6) >> 8
- b[8*y+4] = (x8 - x6) >> 8
- b[8*y+5] = (x0 - x4) >> 8
- b[8*y+6] = (x3 - x2) >> 8
- b[8*y+7] = (x7 - x1) >> 8
+ src[8*y+0] = (x7 + x1) >> 8
+ src[8*y+1] = (x3 + x2) >> 8
+ src[8*y+2] = (x0 + x4) >> 8
+ src[8*y+3] = (x8 + x6) >> 8
+ src[8*y+4] = (x8 - x6) >> 8
+ src[8*y+5] = (x0 - x4) >> 8
+ src[8*y+6] = (x3 - x2) >> 8
+ src[8*y+7] = (x7 - x1) >> 8
}
// Vertical 1-D IDCT.
@@ -136,14 +140,14 @@ func idct(b *block) {
// we do not bother to check for the all-zero case.
// Prescale.
- y0 := (b[8*0+x] << 8) + 8192
- y1 := b[8*4+x] << 8
- y2 := b[8*6+x]
- y3 := b[8*2+x]
- y4 := b[8*1+x]
- y5 := b[8*7+x]
- y6 := b[8*5+x]
- y7 := b[8*3+x]
+ y0 := (src[8*0+x] << 8) + 8192
+ y1 := src[8*4+x] << 8
+ y2 := src[8*6+x]
+ y3 := src[8*2+x]
+ y4 := src[8*1+x]
+ y5 := src[8*7+x]
+ y6 := src[8*5+x]
+ y7 := src[8*3+x]
// Stage 1.
y8 := w7*(y4+y5) + 4
@@ -173,18 +177,28 @@ func idct(b *block) {
y4 = (r2*(y4-y5) + 128) >> 8
// Stage 4.
- b[8*0+x] = (y7 + y1) >> 14
- b[8*1+x] = (y3 + y2) >> 14
- b[8*2+x] = (y0 + y4) >> 14
- b[8*3+x] = (y8 + y6) >> 14
- b[8*4+x] = (y8 - y6) >> 14
- b[8*5+x] = (y0 - y4) >> 14
- b[8*6+x] = (y3 - y2) >> 14
- b[8*7+x] = (y7 - y1) >> 14
+ src[8*0+x] = (y7 + y1) >> 14
+ src[8*1+x] = (y3 + y2) >> 14
+ src[8*2+x] = (y0 + y4) >> 14
+ src[8*3+x] = (y8 + y6) >> 14
+ src[8*4+x] = (y8 - y6) >> 14
+ src[8*5+x] = (y0 - y4) >> 14
+ src[8*6+x] = (y3 - y2) >> 14
+ src[8*7+x] = (y7 - y1) >> 14
}
- // Level shift.
- for i := range *b {
- b[i] += 128
+ // Level shift by +128, clip to [0, 255], and write to dst.
+ for y := 0; y < 8; y++ {
+ for x := 0; x < 8; x++ {
+ c := src[y*8+x]
+ if c < -128 {
+ c = 0
+ } else if c > 127 {
+ c = 255
+ } else {
+ c += 128
+ }
+ dst[y*stride+x] = uint8(c)
+ }
}
}
diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go
index 21a6fff..3f22c52 100644
--- a/libgo/go/image/jpeg/reader.go
+++ b/libgo/go/image/jpeg/reader.go
@@ -41,16 +41,22 @@ type block [blockSize]int
const (
blockSize = 64 // A DCT block is 8x8.
- dcTableClass = 0
- acTableClass = 1
- maxTc = 1
- maxTh = 3
- maxTq = 3
-
- // We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and assume that the components are Y, Cb, Cr.
- nComponent = 3
- maxH = 2
- maxV = 2
+ dcTable = 0
+ acTable = 1
+ maxTc = 1
+ maxTh = 3
+ maxTq = 3
+
+ // A grayscale JPEG image has only a Y component.
+ nGrayComponent = 1
+ // A color JPEG image has Y, Cb and Cr components.
+ nColorComponent = 3
+
+ // We only support 4:4:4, 4:2:2 and 4:2:0 downsampling, and therefore the
+ // number of luma samples per chroma sample is at most 2 in the horizontal
+ // and 2 in the vertical direction.
+ maxH = 2
+ maxV = 2
)
const (
@@ -90,13 +96,14 @@ type Reader interface {
type decoder struct {
r Reader
width, height int
- img *ycbcr.YCbCr
+ img1 *image.Gray
+ img3 *ycbcr.YCbCr
ri int // Restart Interval.
- comps [nComponent]component
+ nComp int
+ comp [nColorComponent]component
huff [maxTc + 1][maxTh + 1]huffman
quant [maxTq + 1]block
b bits
- blocks [nComponent][maxH * maxV]block
tmp [1024]byte
}
@@ -118,10 +125,15 @@ func (d *decoder) ignore(n int) os.Error {
// Specified in section B.2.2.
func (d *decoder) processSOF(n int) os.Error {
- if n != 6+3*nComponent {
+ switch n {
+ case 6 + 3*nGrayComponent:
+ d.nComp = nGrayComponent
+ case 6 + 3*nColorComponent:
+ d.nComp = nColorComponent
+ default:
return UnsupportedError("SOF has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[0:6+3*nComponent])
+ _, err := io.ReadFull(d.r, d.tmp[:n])
if err != nil {
return err
}
@@ -131,26 +143,28 @@ func (d *decoder) processSOF(n int) os.Error {
}
d.height = int(d.tmp[1])<<8 + int(d.tmp[2])
d.width = int(d.tmp[3])<<8 + int(d.tmp[4])
- if d.tmp[5] != nComponent {
+ if int(d.tmp[5]) != d.nComp {
return UnsupportedError("SOF has wrong number of image components")
}
- for i := 0; i < nComponent; i++ {
+ for i := 0; i < d.nComp; i++ {
hv := d.tmp[7+3*i]
- d.comps[i].h = int(hv >> 4)
- d.comps[i].v = int(hv & 0x0f)
- d.comps[i].c = d.tmp[6+3*i]
- d.comps[i].tq = d.tmp[8+3*i]
- // We only support YCbCr images, and 4:4:4, 4:2:2 or 4:2:0 chroma downsampling ratios. This implies that
- // the (h, v) values for the Y component are either (1, 1), (2, 1) or (2, 2), and the
- // (h, v) values for the Cr and Cb components must be (1, 1).
+ d.comp[i].h = int(hv >> 4)
+ d.comp[i].v = int(hv & 0x0f)
+ d.comp[i].c = d.tmp[6+3*i]
+ d.comp[i].tq = d.tmp[8+3*i]
+ if d.nComp == nGrayComponent {
+ continue
+ }
+ // For color images, we only support 4:4:4, 4:2:2 or 4:2:0 chroma
+ // downsampling ratios. This implies that the (h, v) values for the Y
+ // component are either (1, 1), (2, 1) or (2, 2), and the (h, v)
+ // values for the Cr and Cb components must be (1, 1).
if i == 0 {
if hv != 0x11 && hv != 0x21 && hv != 0x22 {
return UnsupportedError("luma downsample ratio")
}
- } else {
- if hv != 0x11 {
- return UnsupportedError("chroma downsample ratio")
- }
+ } else if hv != 0x11 {
+ return UnsupportedError("chroma downsample ratio")
}
}
return nil
@@ -182,110 +196,88 @@ func (d *decoder) processDQT(n int) os.Error {
return nil
}
-// Clip x to the range [0, 255] inclusive.
-func clip(x int) uint8 {
- if x < 0 {
- return 0
- }
- if x > 255 {
- return 255
+// makeImg allocates and initializes the destination image.
+func (d *decoder) makeImg(h0, v0, mxx, myy int) {
+ if d.nComp == nGrayComponent {
+ m := image.NewGray(8*mxx, 8*myy)
+ d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
+ return
}
- return uint8(x)
-}
-
-// Store the MCU to the image.
-func (d *decoder) storeMCU(mx, my int) {
- h0, v0 := d.comps[0].h, d.comps[0].v
- // Store the luma blocks.
- for v := 0; v < v0; v++ {
- for h := 0; h < h0; h++ {
- p := 8 * ((v0*my+v)*d.img.YStride + (h0*mx + h))
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- d.img.Y[p] = clip(d.blocks[0][h0*v+h][8*y+x])
- p++
- }
- p += d.img.YStride - 8
- }
- }
+ var subsampleRatio ycbcr.SubsampleRatio
+ n := h0 * v0
+ switch n {
+ case 1:
+ subsampleRatio = ycbcr.SubsampleRatio444
+ case 2:
+ subsampleRatio = ycbcr.SubsampleRatio422
+ case 4:
+ subsampleRatio = ycbcr.SubsampleRatio420
+ default:
+ panic("unreachable")
}
- // Store the chroma blocks.
- p := 8 * (my*d.img.CStride + mx)
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- d.img.Cb[p] = clip(d.blocks[1][0][8*y+x])
- d.img.Cr[p] = clip(d.blocks[2][0][8*y+x])
- p++
- }
- p += d.img.CStride - 8
+ b := make([]byte, mxx*myy*(1*8*8*n+2*8*8))
+ d.img3 = &ycbcr.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),
}
}
// Specified in section B.2.3.
func (d *decoder) processSOS(n int) os.Error {
- if n != 4+2*nComponent {
+ if d.nComp == 0 {
+ return FormatError("missing SOF marker")
+ }
+ if n != 4+2*d.nComp {
return UnsupportedError("SOS has wrong length")
}
- _, err := io.ReadFull(d.r, d.tmp[0:4+2*nComponent])
+ _, err := io.ReadFull(d.r, d.tmp[0:4+2*d.nComp])
if err != nil {
return err
}
- if d.tmp[0] != nComponent {
+ if int(d.tmp[0]) != d.nComp {
return UnsupportedError("SOS has wrong number of image components")
}
- var scanComps [nComponent]struct {
+ var scan [nColorComponent]struct {
td uint8 // DC table selector.
ta uint8 // AC table selector.
}
- for i := 0; i < nComponent; i++ {
+ for i := 0; i < d.nComp; i++ {
cs := d.tmp[1+2*i] // Component selector.
- if cs != d.comps[i].c {
+ if cs != d.comp[i].c {
return UnsupportedError("scan components out of order")
}
- scanComps[i].td = d.tmp[2+2*i] >> 4
- scanComps[i].ta = d.tmp[2+2*i] & 0x0f
+ scan[i].td = d.tmp[2+2*i] >> 4
+ scan[i].ta = d.tmp[2+2*i] & 0x0f
}
// mxx and myy are the number of MCUs (Minimum Coded Units) in the image.
- h0, v0 := d.comps[0].h, d.comps[0].v // The h and v values from the Y components.
+ h0, v0 := d.comp[0].h, d.comp[0].v // The h and v values from the Y components.
mxx := (d.width + 8*h0 - 1) / (8 * h0)
myy := (d.height + 8*v0 - 1) / (8 * v0)
- if d.img == nil {
- var subsampleRatio ycbcr.SubsampleRatio
- n := h0 * v0
- switch n {
- case 1:
- subsampleRatio = ycbcr.SubsampleRatio444
- case 2:
- subsampleRatio = ycbcr.SubsampleRatio422
- case 4:
- subsampleRatio = ycbcr.SubsampleRatio420
- default:
- panic("unreachable")
- }
- b := make([]byte, mxx*myy*(1*8*8*n+2*8*8))
- d.img = &ycbcr.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),
- }
+ if d.img1 == nil && d.img3 == nil {
+ d.makeImg(h0, v0, mxx, myy)
}
mcu, expectedRST := 0, uint8(rst0Marker)
- var allZeroes block
- var dc [nComponent]int
+ var (
+ b block
+ dc [nColorComponent]int
+ )
for my := 0; my < myy; my++ {
for mx := 0; mx < mxx; mx++ {
- for i := 0; i < nComponent; i++ {
- qt := &d.quant[d.comps[i].tq]
- for j := 0; j < d.comps[i].h*d.comps[i].v; j++ {
- d.blocks[i][j] = allZeroes
+ for i := 0; i < d.nComp; i++ {
+ qt := &d.quant[d.comp[i].tq]
+ for j := 0; j < d.comp[i].h*d.comp[i].v; j++ {
+ // TODO(nigeltao): make this a "var b block" once the compiler's escape
+ // analysis is good enough to allocate it on the stack, not the heap.
+ b = block{}
// Decode the DC coefficient, as specified in section F.2.2.1.
- value, err := d.decodeHuffman(&d.huff[dcTableClass][scanComps[i].td])
+ value, err := d.decodeHuffman(&d.huff[dcTable][scan[i].td])
if err != nil {
return err
}
@@ -297,11 +289,11 @@ func (d *decoder) processSOS(n int) os.Error {
return err
}
dc[i] += dcDelta
- d.blocks[i][j][0] = dc[i] * qt[0]
+ b[0] = dc[i] * qt[0]
// Decode the AC coefficients, as specified in section F.2.2.2.
for k := 1; k < blockSize; k++ {
- value, err := d.decodeHuffman(&d.huff[acTableClass][scanComps[i].ta])
+ value, err := d.decodeHuffman(&d.huff[acTable][scan[i].ta])
if err != nil {
return err
}
@@ -316,7 +308,7 @@ func (d *decoder) processSOS(n int) os.Error {
if err != nil {
return err
}
- d.blocks[i][j][unzig[k]] = ac * qt[k]
+ b[unzig[k]] = ac * qt[k]
} else {
if val0 != 0x0f {
break
@@ -325,10 +317,23 @@ func (d *decoder) processSOS(n int) os.Error {
}
}
- idct(&d.blocks[i][j])
+ // Perform the inverse DCT and store the MCU component to the image.
+ if d.nComp == nGrayComponent {
+ idct(d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride, &b)
+ } else {
+ switch i {
+ case 0:
+ mx0 := h0*mx + (j % 2)
+ my0 := v0*my + (j / 2)
+ idct(d.img3.Y[8*(my0*d.img3.YStride+mx0):], d.img3.YStride, &b)
+ case 1:
+ idct(d.img3.Cb[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
+ case 2:
+ idct(d.img3.Cr[8*(my*d.img3.CStride+mx):], d.img3.CStride, &b)
+ }
+ }
} // for j
} // for i
- d.storeMCU(mx, my)
mcu++
if d.ri > 0 && mcu%d.ri == 0 && mcu < mxx*myy {
// A more sophisticated decoder could use RST[0-7] markers to resynchronize from corrupt input,
@@ -347,9 +352,7 @@ func (d *decoder) processSOS(n int) os.Error {
// Reset the Huffman decoder.
d.b = bits{}
// Reset the DC components, as per section F.2.1.3.1.
- for i := 0; i < nComponent; i++ {
- dc[i] = 0
- }
+ dc = [nColorComponent]int{}
}
} // for mx
} // for my
@@ -437,7 +440,13 @@ func (d *decoder) decode(r io.Reader, configOnly bool) (image.Image, os.Error) {
return nil, err
}
}
- return d.img, nil
+ if d.img1 != nil {
+ return d.img1, nil
+ }
+ if d.img3 != nil {
+ return d.img3, nil
+ }
+ return nil, FormatError("missing SOS marker")
}
// Decode reads a JPEG image from r and returns it as an image.Image.
@@ -453,7 +462,13 @@ func DecodeConfig(r io.Reader) (image.Config, os.Error) {
if _, err := d.decode(r, true); err != nil {
return image.Config{}, err
}
- return image.Config{image.RGBAColorModel, d.width, d.height}, nil
+ switch d.nComp {
+ case nGrayComponent:
+ return image.Config{image.GrayColorModel, d.width, d.height}, nil
+ case nColorComponent:
+ return image.Config{ycbcr.YCbCrColorModel, d.width, d.height}, nil
+ }
+ return image.Config{}, FormatError("missing SOF marker")
}
func init() {
diff --git a/libgo/go/image/jpeg/writer.go b/libgo/go/image/jpeg/writer.go
index 52b3dc4..2bb6df5 100644
--- a/libgo/go/image/jpeg/writer.go
+++ b/libgo/go/image/jpeg/writer.go
@@ -221,8 +221,7 @@ type encoder struct {
// buf is a scratch buffer.
buf [16]byte
// bits and nBits are accumulated bits to write to w.
- bits uint32
- nBits uint8
+ bits, nBits uint32
// quant is the scaled quantization tables.
quant [nQuantIndex][blockSize]byte
}
@@ -250,7 +249,7 @@ func (e *encoder) writeByte(b byte) {
// emit emits the least significant nBits bits of bits to the bitstream.
// The precondition is bits < 1<<nBits && nBits <= 16.
-func (e *encoder) emit(bits uint32, nBits uint8) {
+func (e *encoder) emit(bits, nBits uint32) {
nBits += e.nBits
bits <<= 32 - nBits
bits |= e.bits
@@ -269,7 +268,7 @@ func (e *encoder) emit(bits uint32, nBits uint8) {
// emitHuff emits the given value with the given Huffman encoder.
func (e *encoder) emitHuff(h huffIndex, value int) {
x := theHuffmanLUT[h][value]
- e.emit(x&(1<<24-1), uint8(x>>24))
+ e.emit(x&(1<<24-1), x>>24)
}
// emitHuffRLE emits a run of runLength copies of value encoded with the given
@@ -279,11 +278,11 @@ func (e *encoder) emitHuffRLE(h huffIndex, runLength, value int) {
if a < 0 {
a, b = -value, value-1
}
- var nBits uint8
+ var nBits uint32
if a < 0x100 {
- nBits = bitCount[a]
+ nBits = uint32(bitCount[a])
} else {
- nBits = 8 + bitCount[a>>8]
+ nBits = 8 + uint32(bitCount[a>>8])
}
e.emitHuff(h, runLength<<4|int(nBits))
if nBits > 0 {
@@ -302,34 +301,31 @@ func (e *encoder) writeMarkerHeader(marker uint8, markerlen int) {
// writeDQT writes the Define Quantization Table marker.
func (e *encoder) writeDQT() {
- markerlen := 2
- for _, q := range e.quant {
- markerlen += 1 + len(q)
- }
+ markerlen := 2 + int(nQuantIndex)*(1+blockSize)
e.writeMarkerHeader(dqtMarker, markerlen)
- for i, q := range e.quant {
+ for i := range e.quant {
e.writeByte(uint8(i))
- e.write(q[:])
+ e.write(e.quant[i][:])
}
}
// writeSOF0 writes the Start Of Frame (Baseline) marker.
func (e *encoder) writeSOF0(size image.Point) {
- markerlen := 8 + 3*nComponent
+ markerlen := 8 + 3*nColorComponent
e.writeMarkerHeader(sof0Marker, markerlen)
e.buf[0] = 8 // 8-bit color.
e.buf[1] = uint8(size.Y >> 8)
e.buf[2] = uint8(size.Y & 0xff)
e.buf[3] = uint8(size.X >> 8)
e.buf[4] = uint8(size.X & 0xff)
- e.buf[5] = nComponent
- for i := 0; i < nComponent; i++ {
+ e.buf[5] = nColorComponent
+ for i := 0; i < nColorComponent; i++ {
e.buf[3*i+6] = uint8(i + 1)
// We use 4:2:0 chroma subsampling.
e.buf[3*i+7] = "\x22\x11\x11"[i]
e.buf[3*i+8] = "\x00\x01\x01"[i]
}
- e.write(e.buf[:3*(nComponent-1)+9])
+ e.write(e.buf[:3*(nColorComponent-1)+9])
}
// writeDHT writes the Define Huffman Table marker.
@@ -401,14 +397,14 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block)
if sj > ymax {
sj = ymax
}
- yoff := sj * m.Stride
+ offset := (sj-b.Min.Y)*m.Stride - b.Min.X*4
for i := 0; i < 8; i++ {
sx := p.X + i
if sx > xmax {
sx = xmax
}
- col := &m.Pix[yoff+sx]
- yy, cb, cr := ycbcr.RGBToYCbCr(col.R, col.G, col.B)
+ pix := m.Pix[offset+sx*4:]
+ yy, cb, cr := ycbcr.RGBToYCbCr(pix[0], pix[1], pix[2])
yBlock[8*j+i] = int(yy)
cbBlock[8*j+i] = int(cb)
crBlock[8*j+i] = int(cr)
diff --git a/libgo/go/image/png/writer.go b/libgo/go/image/png/writer.go
index a27586f..55ca97e 100644
--- a/libgo/go/image/png/writer.go
+++ b/libgo/go/image/png/writer.go
@@ -174,7 +174,7 @@ func (e *encoder) Write(b []byte) (int, os.Error) {
// Chooses the filter to use for encoding the current row, and applies it.
// The return value is the index of the filter and also of the row in cr that has had it applied.
-func filter(cr [][]byte, pr []byte, bpp int) int {
+func filter(cr *[nFilter][]byte, pr []byte, bpp int) int {
// We try all five filter types, and pick the one that minimizes the sum of absolute differences.
// This is the same heuristic that libpng uses, although the filters are attempted in order of
// estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
@@ -275,11 +275,6 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
bpp := 0 // Bytes per pixel.
- // Used by fast paths for common image types
- var paletted *image.Paletted
- var rgba *image.RGBA
- rgba, _ = m.(*image.RGBA)
-
switch cb {
case cbG8:
bpp = 1
@@ -287,7 +282,6 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
bpp = 3
case cbP8:
bpp = 1
- paletted = m.(*image.Paletted)
case cbTCA8:
bpp = 4
case cbTC16:
@@ -304,7 +298,7 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
// The +1 is for the per-row filter type, which is at cr[*][0].
b := m.Bounds()
var cr [nFilter][]uint8
- for i := 0; i < len(cr); i++ {
+ for i := range cr {
cr[i] = make([]uint8, 1+bpp*b.Dx())
cr[i][0] = uint8(i)
}
@@ -312,78 +306,86 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes.
+ i := 1
switch cb {
case cbG8:
for x := b.Min.X; x < b.Max.X; x++ {
c := image.GrayColorModel.Convert(m.At(x, y)).(image.GrayColor)
- cr[0][x+1] = c.Y
+ cr[0][i] = c.Y
+ i++
}
case cbTC8:
// We have previously verified that the alpha value is fully opaque.
cr0 := cr[0]
- if rgba != nil {
- yoff := y * rgba.Stride
- xoff := 3*b.Min.X + 1
- for _, color := range rgba.Pix[yoff+b.Min.X : yoff+b.Max.X] {
- cr0[xoff] = color.R
- cr0[xoff+1] = color.G
- cr0[xoff+2] = color.B
- xoff += 3
+ if rgba, _ := m.(*image.RGBA); rgba != nil {
+ j0 := (y - b.Min.Y) * rgba.Stride
+ j1 := j0 + b.Dx()*4
+ for j := j0; j < j1; j += 4 {
+ cr0[i+0] = rgba.Pix[j+0]
+ cr0[i+1] = rgba.Pix[j+1]
+ cr0[i+2] = rgba.Pix[j+2]
+ i += 3
}
} else {
for x := b.Min.X; x < b.Max.X; x++ {
r, g, b, _ := m.At(x, y).RGBA()
- cr0[3*x+1] = uint8(r >> 8)
- cr0[3*x+2] = uint8(g >> 8)
- cr0[3*x+3] = uint8(b >> 8)
+ cr0[i+0] = uint8(r >> 8)
+ cr0[i+1] = uint8(g >> 8)
+ cr0[i+2] = uint8(b >> 8)
+ i += 3
}
}
case cbP8:
- rowOffset := y * paletted.Stride
- copy(cr[0][b.Min.X+1:], paletted.Pix[rowOffset+b.Min.X:rowOffset+b.Max.X])
+ paletted := m.(*image.Paletted)
+ offset := (y - b.Min.Y) * paletted.Stride
+ copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
case cbTCA8:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ {
c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor)
- cr[0][4*x+1] = c.R
- cr[0][4*x+2] = c.G
- cr[0][4*x+3] = c.B
- cr[0][4*x+4] = c.A
+ cr[0][i+0] = c.R
+ cr[0][i+1] = c.G
+ cr[0][i+2] = c.B
+ cr[0][i+3] = c.A
+ i += 4
}
case cbG16:
for x := b.Min.X; x < b.Max.X; x++ {
c := image.Gray16ColorModel.Convert(m.At(x, y)).(image.Gray16Color)
- cr[0][2*x+1] = uint8(c.Y >> 8)
- cr[0][2*x+2] = uint8(c.Y)
+ cr[0][i+0] = uint8(c.Y >> 8)
+ cr[0][i+1] = uint8(c.Y)
+ i += 2
}
case cbTC16:
+ // We have previously verified that the alpha value is fully opaque.
for x := b.Min.X; x < b.Max.X; x++ {
- // We have previously verified that the alpha value is fully opaque.
r, g, b, _ := m.At(x, y).RGBA()
- cr[0][6*x+1] = uint8(r >> 8)
- cr[0][6*x+2] = uint8(r)
- cr[0][6*x+3] = uint8(g >> 8)
- cr[0][6*x+4] = uint8(g)
- cr[0][6*x+5] = uint8(b >> 8)
- cr[0][6*x+6] = uint8(b)
+ cr[0][i+0] = uint8(r >> 8)
+ cr[0][i+1] = uint8(r)
+ cr[0][i+2] = uint8(g >> 8)
+ cr[0][i+3] = uint8(g)
+ cr[0][i+4] = uint8(b >> 8)
+ cr[0][i+5] = uint8(b)
+ i += 6
}
case cbTCA16:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ {
c := image.NRGBA64ColorModel.Convert(m.At(x, y)).(image.NRGBA64Color)
- cr[0][8*x+1] = uint8(c.R >> 8)
- cr[0][8*x+2] = uint8(c.R)
- cr[0][8*x+3] = uint8(c.G >> 8)
- cr[0][8*x+4] = uint8(c.G)
- cr[0][8*x+5] = uint8(c.B >> 8)
- cr[0][8*x+6] = uint8(c.B)
- cr[0][8*x+7] = uint8(c.A >> 8)
- cr[0][8*x+8] = uint8(c.A)
+ cr[0][i+0] = uint8(c.R >> 8)
+ cr[0][i+1] = uint8(c.R)
+ cr[0][i+2] = uint8(c.G >> 8)
+ cr[0][i+3] = uint8(c.G)
+ cr[0][i+4] = uint8(c.B >> 8)
+ cr[0][i+5] = uint8(c.B)
+ cr[0][i+6] = uint8(c.A >> 8)
+ cr[0][i+7] = uint8(c.A)
+ i += 8
}
}
// Apply the filter.
- f := filter(cr[0:nFilter], pr, bpp)
+ f := filter(&cr, pr, bpp)
// Write the compressed bytes.
_, err = zw.Write(cr[f])
diff --git a/libgo/go/image/png/writer_test.go b/libgo/go/image/png/writer_test.go
index 6b054aa..1599791 100644
--- a/libgo/go/image/png/writer_test.go
+++ b/libgo/go/image/png/writer_test.go
@@ -5,9 +5,9 @@
package png
import (
+ "bytes"
"fmt"
"image"
- "io"
"io/ioutil"
"os"
"testing"
@@ -15,21 +15,38 @@ import (
func diff(m0, m1 image.Image) os.Error {
b0, b1 := m0.Bounds(), m1.Bounds()
- if !b0.Eq(b1) {
+ if !b0.Size().Eq(b1.Size()) {
return fmt.Errorf("dimensions differ: %v vs %v", b0, b1)
}
+ dx := b1.Min.X - b0.Min.X
+ dy := b1.Min.Y - b0.Min.Y
for y := b0.Min.Y; y < b0.Max.Y; y++ {
for x := b0.Min.X; x < b0.Max.X; x++ {
- r0, g0, b0, a0 := m0.At(x, y).RGBA()
- r1, g1, b1, a1 := m1.At(x, y).RGBA()
+ c0 := m0.At(x, y)
+ c1 := m1.At(x+dx, y+dy)
+ r0, g0, b0, a0 := c0.RGBA()
+ r1, g1, b1, a1 := c1.RGBA()
if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
- return fmt.Errorf("colors differ at (%d, %d): %v vs %v", x, y, m0.At(x, y), m1.At(x, y))
+ return fmt.Errorf("colors differ at (%d, %d): %v vs %v", x, y, c0, c1)
}
}
}
return nil
}
+func encodeDecode(m image.Image) (image.Image, os.Error) {
+ b := bytes.NewBuffer(nil)
+ err := Encode(b, m)
+ if err != nil {
+ return nil, err
+ }
+ m, err = Decode(b)
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
func TestWriter(t *testing.T) {
// The filenames variable is declared in reader_test.go.
names := filenames
@@ -44,26 +61,16 @@ func TestWriter(t *testing.T) {
t.Error(fn, err)
continue
}
- // Read the image again, and push it through a pipe that encodes at the write end, and decodes at the read end.
- pr, pw := io.Pipe()
- defer pr.Close()
- go func() {
- defer pw.Close()
- m1, err := readPng(qfn)
- if err != nil {
- t.Error(fn, err)
- return
- }
- err = Encode(pw, m1)
- if err != nil {
- t.Error(fn, err)
- return
- }
- }()
- m2, err := Decode(pr)
+ // Read the image again, encode it, and decode it.
+ m1, err := readPng(qfn)
if err != nil {
t.Error(fn, err)
- continue
+ return
+ }
+ m2, err := encodeDecode(m1)
+ if err != nil {
+ t.Error(fn, err)
+ return
}
// Compare the two.
err = diff(m0, m2)
@@ -74,6 +81,26 @@ func TestWriter(t *testing.T) {
}
}
+func TestSubImage(t *testing.T) {
+ m0 := image.NewRGBA(256, 256)
+ for y := 0; y < 256; y++ {
+ for x := 0; x < 256; x++ {
+ m0.Set(x, y, image.RGBAColor{uint8(x), uint8(y), 0, 255})
+ }
+ }
+ m0 = m0.SubImage(image.Rect(50, 30, 250, 130)).(*image.RGBA)
+ m1, err := encodeDecode(m0)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ err = diff(m0, m1)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+}
+
func BenchmarkEncodePaletted(b *testing.B) {
b.StopTimer()
img := image.NewPaletted(640, 480,
diff --git a/libgo/go/image/testdata/video-001.5bpp.gif b/libgo/go/image/testdata/video-001.5bpp.gif
new file mode 100644
index 0000000..ce53104
--- /dev/null
+++ b/libgo/go/image/testdata/video-001.5bpp.gif
Binary files differ
diff --git a/libgo/go/image/testdata/video-001.interlaced.gif b/libgo/go/image/testdata/video-001.interlaced.gif
new file mode 100644
index 0000000..590594e
--- /dev/null
+++ b/libgo/go/image/testdata/video-001.interlaced.gif
Binary files differ
diff --git a/libgo/go/image/testdata/video-005.gray.jpeg b/libgo/go/image/testdata/video-005.gray.jpeg
new file mode 100644
index 0000000..f9d6e5c
--- /dev/null
+++ b/libgo/go/image/testdata/video-005.gray.jpeg
Binary files differ
diff --git a/libgo/go/image/testdata/video-005.gray.png b/libgo/go/image/testdata/video-005.gray.png
new file mode 100644
index 0000000..0b0ee75
--- /dev/null
+++ b/libgo/go/image/testdata/video-005.gray.png
Binary files differ
diff --git a/libgo/go/image/tiff/consts.go b/libgo/go/image/tiff/consts.go
index 761ac9d..169ba27 100644
--- a/libgo/go/image/tiff/consts.go
+++ b/libgo/go/image/tiff/consts.go
@@ -54,6 +54,7 @@ const (
tPredictor = 317
tColorMap = 320
tExtraSamples = 338
+ tSampleFormat = 339
)
// Compression types (defined in various places in the spec and supplements).
diff --git a/libgo/go/image/tiff/reader.go b/libgo/go/image/tiff/reader.go
index 40f659c..f565266 100644
--- a/libgo/go/image/tiff/reader.go
+++ b/libgo/go/image/tiff/reader.go
@@ -46,6 +46,11 @@ type decoder struct {
mode imageMode
features map[int][]uint
palette []image.Color
+
+ buf []byte
+ off int // Current offset in buf.
+ v uint32 // Buffer value for reading with arbitrary bit depths.
+ nbits uint // Remaining number of bits in v.
}
// firstVal returns the first uint of the features entry with the given tag,
@@ -133,82 +138,112 @@ func (d *decoder) parseIFD(p []byte) os.Error {
0xffff,
}
}
+ case tSampleFormat:
+ // Page 27 of the spec: If the SampleFormat is present and
+ // the value is not 1 [= unsigned integer data], a Baseline
+ // TIFF reader that cannot handle the SampleFormat value
+ // must terminate the import process gracefully.
+ val, err := d.ifdUint(p)
+ if err != nil {
+ return err
+ }
+ for _, v := range val {
+ if v != 1 {
+ return UnsupportedError("sample format")
+ }
+ }
}
return nil
}
-// decode decodes the raw data of an image with 8 bits in each sample.
-// It reads from p and writes the strip with ymin <= y < ymax into dst.
-func (d *decoder) decode(dst image.Image, p []byte, ymin, ymax int) os.Error {
+// readBits reads n bits from the internal buffer starting at the current offset.
+func (d *decoder) readBits(n uint) uint32 {
+ for d.nbits < n {
+ d.v <<= 8
+ d.v |= uint32(d.buf[d.off])
+ d.off++
+ d.nbits += 8
+ }
+ d.nbits -= n
+ rv := d.v >> d.nbits
+ d.v &^= rv << d.nbits
+ return rv
+}
+
+// flushBits discards the unread bits in the buffer used by readBits.
+// It is used at the end of a line.
+func (d *decoder) flushBits() {
+ d.v = 0
+ d.nbits = 0
+}
+
+// decode decodes the raw data of an image.
+// It reads from d.buf and writes the strip with ymin <= y < ymax into dst.
+func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error {
spp := len(d.features[tBitsPerSample]) // samples per pixel
- off := 0
+ d.off = 0
width := dst.Bounds().Dx()
- if len(p) < spp*(ymax-ymin)*width {
- return FormatError("short data strip")
- }
-
// Apply horizontal predictor if necessary.
// In this case, p contains the color difference to the preceding pixel.
// See page 64-65 of the spec.
- if d.firstVal(tPredictor) == prHorizontal {
+ if d.firstVal(tPredictor) == prHorizontal && d.firstVal(tBitsPerSample) == 8 {
for y := ymin; y < ymax; y++ {
- off += spp
+ d.off += spp
for x := 0; x < (width-1)*spp; x++ {
- p[off] += p[off-spp]
- off++
+ d.buf[d.off] += d.buf[d.off-spp]
+ d.off++
}
}
- off = 0
+ d.off = 0
}
switch d.mode {
- case mGray:
- img := dst.(*image.Gray)
- for y := ymin; y < ymax; y++ {
- for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.Set(x, y, image.GrayColor{p[off]})
- off += spp
- }
- }
- case mGrayInvert:
+ case mGray, mGrayInvert:
img := dst.(*image.Gray)
+ bpp := d.firstVal(tBitsPerSample)
+ max := uint32((1 << bpp) - 1)
for y := ymin; y < ymax; y++ {
for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.Set(x, y, image.GrayColor{0xff - p[off]})
- off += spp
+ v := uint8(d.readBits(bpp) * 0xff / max)
+ if d.mode == mGrayInvert {
+ v = 0xff - v
+ }
+ img.SetGray(x, y, image.GrayColor{v})
}
+ d.flushBits()
}
case mPaletted:
img := dst.(*image.Paletted)
+ bpp := d.firstVal(tBitsPerSample)
for y := ymin; y < ymax; y++ {
for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.SetColorIndex(x, y, p[off])
- off += spp
+ img.SetColorIndex(x, y, uint8(d.readBits(bpp)))
}
+ d.flushBits()
}
case mRGB:
img := dst.(*image.RGBA)
for y := ymin; y < ymax; y++ {
for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], 0xff})
- off += spp
+ img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], 0xff})
+ d.off += spp
}
}
case mNRGBA:
img := dst.(*image.NRGBA)
for y := ymin; y < ymax; y++ {
for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.Set(x, y, image.NRGBAColor{p[off], p[off+1], p[off+2], p[off+3]})
- off += spp
+ img.SetNRGBA(x, y, image.NRGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]})
+ d.off += spp
}
}
case mRGBA:
img := dst.(*image.RGBA)
for y := ymin; y < ymax; y++ {
for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
- img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], p[off+3]})
- off += spp
+ img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]})
+ d.off += spp
}
}
}
@@ -258,9 +293,18 @@ func newDecoder(r io.Reader) (*decoder, os.Error) {
d.config.Width = int(d.firstVal(tImageWidth))
d.config.Height = int(d.firstVal(tImageLength))
+ if _, ok := d.features[tBitsPerSample]; !ok {
+ return nil, FormatError("BitsPerSample tag missing")
+ }
+
// Determine the image mode.
switch d.firstVal(tPhotometricInterpretation) {
case pRGB:
+ for _, b := range d.features[tBitsPerSample] {
+ if b != 8 {
+ return nil, UnsupportedError("non-8-bit RGB image")
+ }
+ }
d.config.ColorModel = image.RGBAColorModel
// RGB images normally have 3 samples per pixel.
// If there are more, ExtraSamples (p. 31-32 of the spec)
@@ -295,15 +339,6 @@ func newDecoder(r io.Reader) (*decoder, os.Error) {
return nil, UnsupportedError("color model")
}
- if _, ok := d.features[tBitsPerSample]; !ok {
- return nil, FormatError("BitsPerSample tag missing")
- }
- for _, b := range d.features[tBitsPerSample] {
- if b != 8 {
- return nil, UnsupportedError("not an 8-bit image")
- }
- }
-
return d, nil
}
@@ -327,6 +362,10 @@ func Decode(r io.Reader) (img image.Image, err os.Error) {
// Check if we have the right number of strips, offsets and counts.
rps := int(d.firstVal(tRowsPerStrip))
+ if rps == 0 {
+ // Assume only one strip.
+ rps = d.config.Height
+ }
numStrips := (d.config.Height + rps - 1) / rps
if rps == 0 || len(d.features[tStripOffsets]) < numStrips || len(d.features[tStripByteCounts]) < numStrips {
return nil, FormatError("inconsistent header")
@@ -343,7 +382,6 @@ func Decode(r io.Reader) (img image.Image, err os.Error) {
img = image.NewRGBA(d.config.Width, d.config.Height)
}
- var p []byte
for i := 0; i < numStrips; i++ {
ymin := i * rps
// The last strip may be shorter.
@@ -355,18 +393,18 @@ func Decode(r io.Reader) (img image.Image, err os.Error) {
switch d.firstVal(tCompression) {
case cNone:
// TODO(bsiegert): Avoid copy if r is a tiff.buffer.
- p = make([]byte, 0, n)
- _, err = d.r.ReadAt(p, offset)
+ d.buf = make([]byte, n)
+ _, err = d.r.ReadAt(d.buf, offset)
case cLZW:
r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8)
- p, err = ioutil.ReadAll(r)
+ d.buf, err = ioutil.ReadAll(r)
r.Close()
case cDeflate, cDeflateOld:
r, err := zlib.NewReader(io.NewSectionReader(d.r, offset, n))
if err != nil {
return nil, err
}
- p, err = ioutil.ReadAll(r)
+ d.buf, err = ioutil.ReadAll(r)
r.Close()
default:
err = UnsupportedError("compression")
@@ -374,7 +412,7 @@ func Decode(r io.Reader) (img image.Image, err os.Error) {
if err != nil {
return
}
- err = d.decode(img, p, ymin, ymin+rps)
+ err = d.decode(img, ymin, ymin+rps)
}
return
}
diff --git a/libgo/go/image/tiff/reader_test.go b/libgo/go/image/tiff/reader_test.go
new file mode 100644
index 0000000..f2122c4
--- /dev/null
+++ b/libgo/go/image/tiff/reader_test.go
@@ -0,0 +1,25 @@
+// 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 tiff
+
+import (
+ "os"
+ "testing"
+)
+
+// TestNoRPS tries to decode an image that has no RowsPerStrip tag.
+// The tag is mandatory according to the spec but some software omits
+// it in the case of a single strip.
+func TestNoRPS(t *testing.T) {
+ f, err := os.Open("testdata/no_rps.tiff")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ _, err = Decode(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/libgo/go/image/tiff/testdata/no_rps.tiff b/libgo/go/image/tiff/testdata/no_rps.tiff
new file mode 100644
index 0000000..3280cf8
--- /dev/null
+++ b/libgo/go/image/tiff/testdata/no_rps.tiff
Binary files differ
diff --git a/libgo/go/image/ycbcr/ycbcr.go b/libgo/go/image/ycbcr/ycbcr.go
index cda4599..f2de3d6 100644
--- a/libgo/go/image/ycbcr/ycbcr.go
+++ b/libgo/go/image/ycbcr/ycbcr.go
@@ -142,7 +142,7 @@ func (p *YCbCr) Bounds() image.Rectangle {
}
func (p *YCbCr) At(x, y int) image.Color {
- if !p.Rect.Contains(image.Point{x, y}) {
+ if !(image.Point{x, y}.In(p.Rect)) {
return YCbCrColor{}
}
switch p.SubsampleRatio {
@@ -169,6 +169,15 @@ func (p *YCbCr) At(x, y int) image.Color {
}
}
+// 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 image.Rectangle) image.Image {
+ q := new(YCbCr)
+ *q = *p
+ q.Rect = q.Rect.Intersect(r)
+ return q
+}
+
func (p *YCbCr) Opaque() bool {
return true
}