image: replace Width and Height by Bounds, and introduce the Point and
Rect types.
The actual image representation is unchanged. A future change will
replace the {[][]color} with {[]color, stride int, r Rectangle} and
possibly a clip region.
The draw.Color, draw.Point and draw.Rect types will be removed in a
future change. Trying to do it in this one polluted the diff with
trivia.
R=r, rsc
CC=golang-dev
https://golang.org/cl/1918047
diff --git a/src/pkg/exp/4s/xs.go b/src/pkg/exp/4s/xs.go
index c5493e7..8f6d62f 100644
--- a/src/pkg/exp/4s/xs.go
+++ b/src/pkg/exp/4s/xs.go
@@ -669,7 +669,7 @@
// if new && getwindow(display, Refmesg) < 0 {
// sysfatal("can't reattach to window");
// }
- r := draw.Rect(0, 0, screen.Width(), screen.Height())
+ r := draw.Rect(screen.Bounds().MinX, screen.Bounds().Min.Y, screen, Bounds().Max.X, screen.Bounds().Max.Y)
pos.X = (pos.X - rboard.Min.X) / pcsz
pos.Y = (pos.Y - rboard.Min.Y) / pcsz
dx := r.Max.X - r.Min.X
@@ -722,7 +722,7 @@
func Play(pp []Piece, ctxt draw.Context) {
display = ctxt
screen = ctxt.Screen()
- screenr = draw.Rect(0, 0, screen.Width(), screen.Height())
+ screenr = draw.Rect(screen.Bounds().MinX, screen.Bounds().Min.Y, screen, Bounds().Max.X, screen.Bounds().Max.Y)
pieces = pp
N = len(pieces[0].d)
initPieces()
diff --git a/src/pkg/exp/draw/color.go b/src/pkg/exp/draw/color.go
index 3fe7b4a..5fb543a 100644
--- a/src/pkg/exp/draw/color.go
+++ b/src/pkg/exp/draw/color.go
@@ -88,9 +88,7 @@
return r<<24 | g<<16 | b<<8 | Color(a)
}
-func (c Color) Width() int { return 1e9 }
-
-func (c Color) Height() int { return 1e9 }
+func (c Color) Bounds() image.Rectangle { return image.Rect(0, 0, 1e9, 1e9) }
func (c Color) At(x, y int) image.Color { return c }
diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/exp/draw/draw.go
index 415dd99..636501c 100644
--- a/src/pkg/exp/draw/draw.go
+++ b/src/pkg/exp/draw/draw.go
@@ -43,13 +43,15 @@
// The implementation is simple and slow.
// TODO(nigeltao): Optimize this.
func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
- dx, dy := src.Width()-sp.X, src.Height()-sp.Y
+ sb := src.Bounds()
+ dx, dy := sb.Dx()-sp.X, sb.Dy()-sp.Y
if mask != nil {
- if dx > mask.Width()-mp.X {
- dx = mask.Width() - mp.X
+ mb := mask.Bounds()
+ if dx > mb.Dx()-mp.X {
+ dx = mb.Dx() - mp.X
}
- if dy > mask.Height()-mp.Y {
- dy = mask.Height() - mp.Y
+ if dy > mb.Dy()-mp.Y {
+ dy = mb.Dy() - mp.Y
}
}
if r.Dx() > dx {
diff --git a/src/pkg/exp/draw/draw_test.go b/src/pkg/exp/draw/draw_test.go
index e9fde25..5b503f8 100644
--- a/src/pkg/exp/draw/draw_test.go
+++ b/src/pkg/exp/draw/draw_test.go
@@ -97,10 +97,11 @@
func makeGolden(dst image.Image, t drawTest) 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.
- golden := image.NewRGBA(dst.Width(), dst.Height())
- for y := 0; y < golden.Height(); y++ {
+ b := dst.Bounds()
+ golden := image.NewRGBA(b.Dx(), b.Dy())
+ for y := b.Min.Y; y < b.Max.Y; y++ {
my, sy := y, y
- for x := 0; x < golden.Width(); x++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
mx, sx := x, x
const M = 1<<16 - 1
var dr, dg, db, da uint32
@@ -129,9 +130,14 @@
for _, test := range drawTests {
dst := hgradRed(255)
// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
+ b := dst.Bounds()
golden := makeGolden(dst, test)
+ if !b.Eq(golden.Bounds()) {
+ t.Errorf("draw %s: bounds %v versus %v", test.desc, dst.Bounds(), golden.Bounds())
+ continue
+ }
// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
- DrawMask(dst, Rect(0, 0, dst.Width(), dst.Height()), test.src, ZP, test.mask, ZP, test.op)
+ DrawMask(dst, Rect(b.Min.X, b.Min.Y, b.Max.X, b.Max.Y), test.src, ZP, test.mask, ZP, test.op)
// 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) {
@@ -139,8 +145,8 @@
continue
}
// Check that the resultant dst image matches the golden output.
- for y := 0; y < golden.Height(); y++ {
- for x := 0; x < golden.Width(); x++ {
+ 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 %s: at (%d, %d), %v versus golden %v", test.desc, x, y, dst.At(x, y), golden.At(x, y))
continue loop
diff --git a/src/pkg/exp/draw/x11/conn.go b/src/pkg/exp/draw/x11/conn.go
index 979ce2b..eb498cf 100644
--- a/src/pkg/exp/draw/x11/conn.go
+++ b/src/pkg/exp/draw/x11/conn.go
@@ -67,14 +67,17 @@
return
}
+ b := c.img.Bounds()
+ if b.Empty() {
+ continue
+ }
// Each X request has a 16-bit length (in terms of 4-byte units). To avoid going over
// this limit, we send PutImage for each row of the image, rather than trying to paint
// the entire image in one X request. This approach could easily be optimized (or the
// X protocol may have an escape sequence to delimit very large requests).
// TODO(nigeltao): See what XCB's xcb_put_image does in this situation.
- w, h := c.img.Width(), c.img.Height()
- units := 6 + w
- if units > 0xffff || h > 0xffff {
+ units := 6 + b.Dx()
+ if units > 0xffff || b.Dy() > 0xffff {
// This window is too large for X.
close(c.flush)
return
@@ -86,10 +89,10 @@
c.flushBuf0[3] = uint8(units >> 8)
setU32LE(c.flushBuf0[4:8], uint32(c.window))
setU32LE(c.flushBuf0[8:12], uint32(c.gc))
- setU32LE(c.flushBuf0[12:16], 1<<16|uint32(w))
+ setU32LE(c.flushBuf0[12:16], 1<<16|uint32(b.Dx()))
c.flushBuf0[21] = 0x18 // depth = 24 bits.
- for y := 0; y < h; y++ {
+ for y := b.Min.Y; y < b.Max.Y; y++ {
setU32LE(c.flushBuf0[16:20], uint32(y<<16))
_, err := c.w.Write(c.flushBuf0[0:24])
if err != nil {
@@ -97,8 +100,8 @@
return
}
p := c.img.Pixel[y]
- for x := 0; x < w; {
- nx := w - x
+ for x := b.Min.X; x < b.Max.X; {
+ nx := b.Max.X - x
if nx > len(c.flushBuf1)/4 {
nx = len(c.flushBuf1) / 4
}
diff --git a/src/pkg/exp/nacl/av/image.go b/src/pkg/exp/nacl/av/image.go
index 8de5311..4c4c558 100644
--- a/src/pkg/exp/nacl/av/image.go
+++ b/src/pkg/exp/nacl/av/image.go
@@ -24,15 +24,13 @@
func (m *Image) ColorModel() image.ColorModel { return ColorModel }
-func (m *Image) Width() int {
+func (m *Image) Bounds() image.Rectangle {
if len(m.Pixel) == 0 {
- return 0
+ return image.ZR
}
- return len(m.Pixel[0])
+ return image.Rectangle{image.ZP, image.Point{len(m.Pixel[0]), len(m.Pixel)}}
}
-func (m *Image) Height() int { return len(m.Pixel) }
-
func (m *Image) At(x, y int) image.Color { return m.Pixel[y][x] }
func (m *Image) Set(x, y int, color image.Color) {
diff --git a/src/pkg/exp/spacewar/spacewar.go b/src/pkg/exp/spacewar/spacewar.go
index 7333220e..e7a1560 100644
--- a/src/pkg/exp/spacewar/spacewar.go
+++ b/src/pkg/exp/spacewar/spacewar.go
@@ -99,8 +99,8 @@
m.ctxt = ctxt
m.kc = ctxt.KeyboardChan()
m.screen = ctxt.Screen()
- m.dx = m.screen.Width()
- m.dy = m.screen.Height()
+ m.dx = m.screen.Bounds().Dx()
+ m.dy = m.screen.Bounds().Dy()
m.colorModel = m.screen.ColorModel()
m.pix = make([][]uint8, m.dy)
for i := range m.pix {
diff --git a/src/pkg/image/Makefile b/src/pkg/image/Makefile
index e26deea..9015ed6 100644
--- a/src/pkg/image/Makefile
+++ b/src/pkg/image/Makefile
@@ -8,6 +8,7 @@
GOFILES=\
color.go\
format.go\
+ geom.go\
image.go\
names.go\
diff --git a/src/pkg/image/geom.go b/src/pkg/image/geom.go
new file mode 100644
index 0000000..ecf0521
--- /dev/null
+++ b/src/pkg/image/geom.go
@@ -0,0 +1,125 @@
+// 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 image
+
+import (
+ "strconv"
+)
+
+// A Point is an X, Y coordinate pair. The axes increase right and down.
+type Point struct {
+ X, Y int
+}
+
+// String returns a string representation of p like "(3,4)".
+func (p Point) String() string {
+ return "(" + strconv.Itoa(p.X) + "," + strconv.Itoa(p.Y) + ")"
+}
+
+// Add returns the vector p+q.
+func (p Point) Add(q Point) Point {
+ return Point{p.X + q.X, p.Y + q.Y}
+}
+
+// Sub returns the vector p-q.
+func (p Point) Sub(q Point) Point {
+ return Point{p.X - q.X, p.Y - q.Y}
+}
+
+// ZP is the zero Point.
+var ZP Point
+
+// Pt is shorthand for Point{X, Y}.
+func Pt(X, Y int) Point {
+ return Point{X, Y}
+}
+
+// A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y.
+type Rectangle struct {
+ Min, Max Point
+}
+
+// String returns a string representation of r like "(3,4)-(6,5)".
+func (r Rectangle) String() string {
+ return r.Min.String() + "-" + r.Max.String()
+}
+
+// Dx returns r's width.
+func (r Rectangle) Dx() int {
+ return r.Max.X - r.Min.X
+}
+
+// Dy returns r's height.
+func (r Rectangle) Dy() int {
+ return r.Max.Y - r.Min.Y
+}
+
+// Add returns the rectangle r translated by p.
+func (r Rectangle) Add(p Point) Rectangle {
+ return Rectangle{
+ Point{r.Min.X + p.X, r.Min.Y + p.Y},
+ Point{r.Max.X + p.X, r.Max.Y + p.Y},
+ }
+}
+
+// Add returns the rectangle r translated by -p.
+func (r Rectangle) Sub(p Point) Rectangle {
+ return Rectangle{
+ Point{r.Min.X - p.X, r.Min.Y - p.Y},
+ Point{r.Max.X - p.X, r.Max.Y - p.Y},
+ }
+}
+
+// Inset returns the rectangle r inset by n, which may be negative.
+func (r Rectangle) Inset(n int) Rectangle {
+ return Rectangle{
+ Point{r.Min.X + n, r.Min.Y + n},
+ Point{r.Max.X - n, r.Max.Y - n},
+ }
+}
+
+// Empty returns whether the rectangle contains no points.
+func (r Rectangle) Empty() bool {
+ return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
+}
+
+// Eq returns whether r and s are equal.
+func (r Rectangle) Eq(s Rectangle) bool {
+ return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y &&
+ r.Max.X == s.Max.X && r.Max.Y == s.Max.Y
+}
+
+// Overlaps returns whether r and s have a non-empty intersection.
+func (r Rectangle) Overlaps(s Rectangle) bool {
+ return r.Min.X < s.Max.X && s.Min.X < r.Max.X &&
+ r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y
+}
+
+// Canon returns the canonical version of r. The returned rectangle has
+// minimum and maximum coordinates swapped if necessary so that Min.X <= Max.X
+// and Min.Y <= Max.Y.
+func (r Rectangle) Canon() Rectangle {
+ if r.Max.X < r.Min.X {
+ r.Min.X, r.Max.X = r.Max.X, r.Min.X
+ }
+ if r.Max.Y < r.Min.Y {
+ r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y
+ }
+ return r
+}
+
+// ZR is the zero Rectangle.
+var ZR Rectangle
+
+// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}.
+func Rect(x0, y0, x1, y1 int) Rectangle {
+ if x0 > x1 {
+ x0, x1 = x1, x0
+ }
+ if y0 > y1 {
+ y0, y1 = y1, y0
+ }
+ return Rectangle{Point{x0, y0}, Point{x1, y1}}
+}
diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go
index cfe4427..c352da2 100644
--- a/src/pkg/image/image.go
+++ b/src/pkg/image/image.go
@@ -5,13 +5,16 @@
// The image package implements a basic 2-D image library.
package image
-// An Image is a rectangular grid of Colors drawn from a ColorModel.
+// An Image is a finite rectangular grid of Colors drawn from a ColorModel.
type Image interface {
+ // ColorModel returns the Image's ColorModel.
ColorModel() ColorModel
- Width() int
- Height() int
- // At(0, 0) returns the upper-left pixel of the grid.
- // At(Width()-1, Height()-1) returns the lower-right pixel.
+ // Bounds returns the domain for which At can return non-zero color.
+ // The bounds do not necessarily contain the point (0, 0).
+ Bounds() Rectangle
+ // At returns the color of the pixel at (x, y).
+ // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
+ // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
At(x, y int) Color
}
@@ -23,18 +26,24 @@
func (p *RGBA) ColorModel() ColorModel { return RGBAColorModel }
-func (p *RGBA) Width() int {
+func (p *RGBA) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *RGBA) Height() int { return len(p.Pixel) }
+func (p *RGBA) At(x, y int) Color {
+ // TODO(nigeltao): Check if (x,y) is outside the bounds, and return zero.
+ // Similarly for the other concrete image types.
+ return p.Pixel[y][x]
+}
-func (p *RGBA) At(x, y int) Color { return p.Pixel[y][x] }
-
-func (p *RGBA) Set(x, y int, c Color) { p.Pixel[y][x] = toRGBAColor(c).(RGBAColor) }
+func (p *RGBA) Set(x, y int, c Color) {
+ // TODO(nigeltao): Check if (x,y) is outside the bounds, and return.
+ // Similarly for the other concrete image types.
+ p.Pixel[y][x] = toRGBAColor(c).(RGBAColor)
+}
// Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *RGBA) Opaque() bool {
@@ -71,15 +80,13 @@
func (p *RGBA64) ColorModel() ColorModel { return RGBA64ColorModel }
-func (p *RGBA64) Width() int {
+func (p *RGBA64) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *RGBA64) Height() int { return len(p.Pixel) }
-
func (p *RGBA64) At(x, y int) Color { return p.Pixel[y][x] }
func (p *RGBA64) Set(x, y int, c Color) { p.Pixel[y][x] = toRGBA64Color(c).(RGBA64Color) }
@@ -119,15 +126,13 @@
func (p *NRGBA) ColorModel() ColorModel { return NRGBAColorModel }
-func (p *NRGBA) Width() int {
+func (p *NRGBA) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *NRGBA) Height() int { return len(p.Pixel) }
-
func (p *NRGBA) At(x, y int) Color { return p.Pixel[y][x] }
func (p *NRGBA) Set(x, y int, c Color) { p.Pixel[y][x] = toNRGBAColor(c).(NRGBAColor) }
@@ -167,15 +172,13 @@
func (p *NRGBA64) ColorModel() ColorModel { return NRGBA64ColorModel }
-func (p *NRGBA64) Width() int {
+func (p *NRGBA64) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *NRGBA64) Height() int { return len(p.Pixel) }
-
func (p *NRGBA64) At(x, y int) Color { return p.Pixel[y][x] }
func (p *NRGBA64) Set(x, y int, c Color) { p.Pixel[y][x] = toNRGBA64Color(c).(NRGBA64Color) }
@@ -215,15 +218,13 @@
func (p *Alpha) ColorModel() ColorModel { return AlphaColorModel }
-func (p *Alpha) Width() int {
+func (p *Alpha) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *Alpha) Height() int { return len(p.Pixel) }
-
func (p *Alpha) At(x, y int) Color { return p.Pixel[y][x] }
func (p *Alpha) Set(x, y int, c Color) { p.Pixel[y][x] = toAlphaColor(c).(AlphaColor) }
@@ -263,15 +264,13 @@
func (p *Alpha16) ColorModel() ColorModel { return Alpha16ColorModel }
-func (p *Alpha16) Width() int {
+func (p *Alpha16) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *Alpha16) Height() int { return len(p.Pixel) }
-
func (p *Alpha16) At(x, y int) Color { return p.Pixel[y][x] }
func (p *Alpha16) Set(x, y int, c Color) { p.Pixel[y][x] = toAlpha16Color(c).(Alpha16Color) }
@@ -311,15 +310,13 @@
func (p *Gray) ColorModel() ColorModel { return GrayColorModel }
-func (p *Gray) Width() int {
+func (p *Gray) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *Gray) Height() int { return len(p.Pixel) }
-
func (p *Gray) At(x, y int) Color { return p.Pixel[y][x] }
func (p *Gray) Set(x, y int, c Color) { p.Pixel[y][x] = toGrayColor(c).(GrayColor) }
@@ -347,15 +344,13 @@
func (p *Gray16) ColorModel() ColorModel { return Gray16ColorModel }
-func (p *Gray16) Width() int {
+func (p *Gray16) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *Gray16) Height() int { return len(p.Pixel) }
-
func (p *Gray16) At(x, y int) Color { return p.Pixel[y][x] }
func (p *Gray16) Set(x, y int, c Color) { p.Pixel[y][x] = toGray16Color(c).(Gray16Color) }
@@ -421,15 +416,13 @@
func (p *Paletted) ColorModel() ColorModel { return p.Palette }
-func (p *Paletted) Width() int {
+func (p *Paletted) Bounds() Rectangle {
if len(p.Pixel) == 0 {
- return 0
+ return ZR
}
- return len(p.Pixel[0])
+ return Rectangle{ZP, Point{len(p.Pixel[0]), len(p.Pixel)}}
}
-func (p *Paletted) Height() int { return len(p.Pixel) }
-
func (p *Paletted) At(x, y int) Color { return p.Palette[p.Pixel[y][x]] }
func (p *Paletted) ColorIndexAt(x, y int) uint8 {
diff --git a/src/pkg/image/names.go b/src/pkg/image/names.go
index a5b4e48..198ac93 100644
--- a/src/pkg/image/names.go
+++ b/src/pkg/image/names.go
@@ -25,9 +25,7 @@
return ColorModelFunc(func(Color) Color { return c.C })
}
-func (c ColorImage) Width() int { return 1e9 }
-
-func (c ColorImage) Height() int { return 1e9 }
+func (c ColorImage) Bounds() Rectangle { return Rectangle{ZP, Point{1e9, 1e9}} }
func (c ColorImage) At(x, y int) Color { return c.C }
diff --git a/src/pkg/image/png/reader_test.go b/src/pkg/image/png/reader_test.go
index 1dc4599..f53d114 100644
--- a/src/pkg/image/png/reader_test.go
+++ b/src/pkg/image/png/reader_test.go
@@ -45,12 +45,13 @@
// An approximation of the sng command-line tool.
func sng(w io.WriteCloser, filename string, png image.Image) {
defer w.Close()
+ bounds := png.Bounds()
// For now, the go PNG parser only reads bitdepths of 8.
bitdepth := 8
// Write the filename and IHDR.
io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
- fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", png.Width(), png.Height(), bitdepth)
+ fmt.Fprintf(w, " width: %d; height: %d; bitdepth: %d;\n", bounds.Dx(), bounds.Dy(), bitdepth)
cm := png.ColorModel()
var paletted *image.Paletted
cpm, _ := cm.(image.PalettedColorModel)
@@ -86,20 +87,20 @@
// Write the IMAGE.
io.WriteString(w, "IMAGE {\n pixels hex\n")
- for y := 0; y < png.Height(); y++ {
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
switch {
case cm == image.RGBAColorModel:
- for x := 0; x < png.Width(); x++ {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
rgba := png.At(x, y).(image.RGBAColor)
fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B)
}
case cm == image.NRGBAColorModel:
- for x := 0; x < png.Width(); x++ {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
nrgba := png.At(x, y).(image.NRGBAColor)
fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
}
case cpm != nil:
- for x := 0; x < png.Width(); x++ {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y))
}
}
diff --git a/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go
index 323e66f..3ce30c8 100644
--- a/src/pkg/image/png/writer.go
+++ b/src/pkg/image/png/writer.go
@@ -41,8 +41,9 @@
if o, ok := m.(opaquer); ok {
return o.Opaque()
}
- for y := 0; y < m.Height(); y++ {
- for x := 0; x < m.Width(); x++ {
+ b := m.Bounds()
+ for y := b.Min.Y; y < b.Max.Y; y++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
_, _, _, a := m.At(x, y).RGBA()
if a != 0xffff {
return false
@@ -91,8 +92,9 @@
}
func (e *encoder) writeIHDR() {
- writeUint32(e.tmp[0:4], uint32(e.m.Width()))
- writeUint32(e.tmp[4:8], uint32(e.m.Height()))
+ b := e.m.Bounds()
+ writeUint32(e.tmp[0:4], uint32(b.Dx()))
+ writeUint32(e.tmp[4:8], uint32(b.Dy()))
e.tmp[8] = 8 // bit depth
e.tmp[9] = e.colorType
e.tmp[10] = 0 // default compression method
@@ -254,18 +256,19 @@
// cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
// other PNG filter types. These buffers are allocated once and re-used for each row.
// 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++ {
- cr[i] = make([]uint8, 1+bpp*m.Width())
+ cr[i] = make([]uint8, 1+bpp*b.Dx())
cr[i][0] = uint8(i)
}
- pr := make([]uint8, 1+bpp*m.Width())
+ pr := make([]uint8, 1+bpp*b.Dx())
- for y := 0; y < m.Height(); y++ {
+ for y := b.Min.Y; y < b.Max.Y; y++ {
// Convert from colors to bytes.
switch ct {
case ctTrueColor:
- for x := 0; x < m.Width(); x++ {
+ 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][3*x+1] = uint8(r >> 8)
@@ -273,12 +276,12 @@
cr[0][3*x+3] = uint8(b >> 8)
}
case ctPaletted:
- for x := 0; x < m.Width(); x++ {
+ for x := b.Min.X; x < b.Max.X; x++ {
cr[0][x+1] = paletted.ColorIndexAt(x, y)
}
case ctTrueColorAlpha:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
- for x := 0; x < m.Width(); x++ {
+ 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
@@ -327,7 +330,7 @@
// Obviously, negative widths and heights are invalid. Furthermore, the PNG
// spec section 11.2.2 says that zero is invalid. Excessively large images are
// also rejected.
- mw, mh := int64(m.Width()), int64(m.Height())
+ mw, mh := int64(m.Bounds().Dx()), int64(m.Bounds().Dy())
if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
return FormatError("invalid image size: " + strconv.Itoa64(mw) + "x" + strconv.Itoa64(mw))
}
diff --git a/src/pkg/image/png/writer_test.go b/src/pkg/image/png/writer_test.go
index a61e1c9..f25873e 100644
--- a/src/pkg/image/png/writer_test.go
+++ b/src/pkg/image/png/writer_test.go
@@ -13,11 +13,12 @@
)
func diff(m0, m1 image.Image) os.Error {
- if m0.Width() != m1.Width() || m0.Height() != m1.Height() {
- return os.NewError(fmt.Sprintf("dimensions differ: %dx%d vs %dx%d", m0.Width(), m0.Height(), m1.Width(), m1.Height()))
+ b0, b1 := m0.Bounds(), m1.Bounds()
+ if !b0.Eq(b1) {
+ return os.NewError(fmt.Sprintf("dimensions differ: %v vs %v", b0, b1))
}
- for y := 0; y < m0.Height(); y++ {
- for x := 0; x < m0.Width(); x++ {
+ 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()
if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {