From 8bd5089513a668ed3d9600d3cd60e200708bed0d Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Tue, 12 Jul 2011 16:39:38 +1000 Subject: [PATCH] image: change Pix from []FooColor to []uint8. Some benchmark numbers below. The image/draw fast-paths show dramatic improvement, the generic slow-paths show a smaller slow-down. BEFORE png.BenchmarkEncodePaletted 200 8203800 ns/op 37.45 MB/s png.BenchmarkEncodeRGBOpaque 100 26940440 ns/op 45.61 MB/s png.BenchmarkEncodeRGBA 20 73821000 ns/op 16.65 MB/s jpeg.BenchmarkEncodeRGBOpaque 50 35598640 ns/op 34.52 MB/s draw.BenchmarkFillOver 500 4024226 ns/op draw.BenchmarkFillSrc 10000 152736 ns/op draw.BenchmarkCopyOver 500 3452824 ns/op draw.BenchmarkCopySrc 50000 73218 ns/op draw.BenchmarkNRGBAOver 500 3941234 ns/op draw.BenchmarkNRGBASrc 1000 2484400 ns/op draw.BenchmarkYCbCr 1000 2609005 ns/op draw.BenchmarkGlyphOver 2000 1169575 ns/op draw.BenchmarkRGBA 200 9031390 ns/op draw.BenchmarkGenericOver 50 34636620 ns/op draw.BenchmarkGenericMaskOver 100 16561150 ns/op draw.BenchmarkGenericSrc 100 13873760 ns/op draw.BenchmarkGenericMaskSrc 100 25198860 ns/op AFTER png.BenchmarkEncodePaletted 200 8206600 ns/op 37.43 MB/s png.BenchmarkEncodeRGBOpaque 100 26129530 ns/op 47.03 MB/s png.BenchmarkEncodeRGBA 20 75776750 ns/op 16.22 MB/s jpeg.BenchmarkEncodeRGBOpaque 50 37192940 ns/op 33.04 MB/s draw.BenchmarkFillOver 500 3008134 ns/op draw.BenchmarkFillSrc 10000 154214 ns/op draw.BenchmarkCopyOver 1000 2169988 ns/op draw.BenchmarkCopySrc 50000 73095 ns/op draw.BenchmarkNRGBAOver 1000 2491079 ns/op draw.BenchmarkNRGBASrc 2000 1361244 ns/op draw.BenchmarkYCbCr 1000 2554269 ns/op draw.BenchmarkGlyphOver 2000 1042225 ns/op draw.BenchmarkRGBA 100 10233340 ns/op draw.BenchmarkGenericOver 50 38421560 ns/op draw.BenchmarkGenericMaskOver 100 17521190 ns/op draw.BenchmarkGenericSrc 100 16351200 ns/op draw.BenchmarkGenericMaskSrc 100 26538190 ns/op R=r CC=golang-dev https://golang.org/cl/4675076 --- src/pkg/exp/gui/x11/conn.go | 17 +- src/pkg/image/bmp/reader.go | 9 +- src/pkg/image/color.go | 24 +-- src/pkg/image/draw/draw.go | 259 ++++++++++++++-------------- src/pkg/image/image.go | 317 +++++++++++++++++++++-------------- src/pkg/image/jpeg/reader.go | 11 +- src/pkg/image/jpeg/writer.go | 6 +- src/pkg/image/png/writer.go | 20 +-- 8 files changed, 367 insertions(+), 296 deletions(-) diff --git a/src/pkg/exp/gui/x11/conn.go b/src/pkg/exp/gui/x11/conn.go index 420bdd82a7..1d237816ab 100644 --- a/src/pkg/exp/gui/x11/conn.go +++ b/src/pkg/exp/gui/x11/conn.go @@ -92,18 +92,19 @@ func (c *conn) writeSocket() { return } p := c.img.Pix[(y-b.Min.Y)*c.img.Stride:] - for x, dx := 0, b.Dx(); x < dx; { + for x, dx := 0, 4*b.Dx(); x < dx; { nx := dx - x - if nx > len(c.flushBuf1)/4 { - nx = len(c.flushBuf1) / 4 + if nx > len(c.flushBuf1) { + nx = len(c.flushBuf1) &^ 3 } - for i, rgba := range p[x : x+nx] { - c.flushBuf1[4*i+0] = rgba.B - c.flushBuf1[4*i+1] = rgba.G - c.flushBuf1[4*i+2] = rgba.R + for i := 0; i < nx; i += 4 { + // X11's order is BGRX, not RGBA. + c.flushBuf1[i+0] = p[x+i+2] + c.flushBuf1[i+1] = p[x+i+1] + c.flushBuf1[i+2] = p[x+i+0] } x += nx - if _, err := c.w.Write(c.flushBuf1[:4*nx]); err != nil { + if _, err := c.w.Write(c.flushBuf1[:nx]); err != nil { if err != os.EOF { log.Println("x11:", err.String()) } diff --git a/src/pkg/image/bmp/reader.go b/src/pkg/image/bmp/reader.go index f2842caedd..357da1dacd 100644 --- a/src/pkg/image/bmp/reader.go +++ b/src/pkg/image/bmp/reader.go @@ -58,10 +58,13 @@ func decodeRGBA(r io.Reader, c image.Config) (image.Image, os.Error) { if err != nil { return nil, err } - p := rgba.Pix[y*rgba.Stride : y*rgba.Stride+c.Width] - for x := range p { + 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[x] = image.RGBAColor{b[3*x+2], b[3*x+1], b[3*x+0], 0xFF} + 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 diff --git a/src/pkg/image/color.go b/src/pkg/image/color.go index c1345c0252..501a882f02 100644 --- a/src/pkg/image/color.go +++ b/src/pkg/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/src/pkg/image/draw/draw.go b/src/pkg/image/draw/draw.go index 5c4dedb818..6468952b14 100644 --- a/src/pkg/image/draw/draw.go +++ b/src/pkg/image/draw/draw.go @@ -170,19 +170,22 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas } func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { - cr, cg, cb, ca := src.RGBA() + sr, sg, sb, sa := src.RGBA() // The 0x101 is here for the same reason as in drawRGBA. - a := (m - ca) * 0x101 - i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X - i1 := i0 + r.Dx() + 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++ { - dpix := dst.Pix[i0:i1] - for i, rgba := range dpix { - dr := (uint32(rgba.R)*a)/m + cr - dg := (uint32(rgba.G)*a)/m + cg - db := (uint32(rgba.B)*a)/m + cb - da := (uint32(rgba.A)*a)/m + ca - dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + 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 @@ -191,8 +194,8 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { 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 - s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + sp.X - src.Rect.Min.X + 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 @@ -200,7 +203,7 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image. 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, +1 + 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. @@ -208,28 +211,29 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image. s0 += (dy - 1) * src.Stride ddelta = -dst.Stride sdelta = -src.Stride - i0, i1, idelta = dx-1, -1, -1 + 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 { - // For unknown reasons, even though both dpix[i] and spix[i] are - // image.RGBAColors, on an x86 CPU it seems fastest to call RGBA - // for the source but to do it manually for the destination. - sr, sg, sb, sa := spix[i].RGBA() - rgba := dpix[i] - dr := uint32(rgba.R) - dg := uint32(rgba.G) - db := uint32(rgba.B) - da := uint32(rgba.A) + 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 - dr = (dr*a)/m + sr - dg = (dg*a)/m + sg - db = (db*a)/m + sb - da = (da*a)/m + sa - dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + + 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 @@ -237,7 +241,9 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image. } func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { - xMax := r.Max.X - dst.Rect.Min.X + 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 @@ -246,63 +252,58 @@ func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp imag dpix := dst.Pix[y*dst.Stride:] spix := src.Pix[sy*src.Stride:] - x := r.Min.X - dst.Rect.Min.X - sx := sp.X - src.Rect.Min.X - for ; x != xMax; x, sx = x+1, sx+1 { + for i, si := i0, si0; i < i1; i, si = i+4, si+4 { // Convert from non-premultiplied color to pre-multiplied color. - // The order of operations here is to match the NRGBAColor.RGBA - // method in image/color.go. - snrgba := spix[sx] - sa := uint32(snrgba.A) - sr := uint32(snrgba.R) * 0x101 * sa / 0xff - sg := uint32(snrgba.G) * 0x101 * sa / 0xff - sb := uint32(snrgba.B) * 0x101 * sa / 0xff - sa *= 0x101 + 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 - rgba := dpix[x] - dr := uint32(rgba.R) - dg := uint32(rgba.G) - db := uint32(rgba.B) - da := uint32(rgba.A) + 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 - dr = (dr*a + sr*m) / m - dg = (dg*a + sg*m) / m - db = (db*a + sb*m) / m - da = (da*a + sa*m) / m - dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + + 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 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 - i1 := i0 + r.Dx() - j0 := (mp.Y-mask.Rect.Min.Y)*mask.Stride + mp.X - mask.Rect.Min.X - cr, cg, cb, ca := src.RGBA() + 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 { - dpix := dst.Pix[i0:i1] - mpix := mask.Pix[j0:] - for i, rgba := range dpix { - ma := uint32(mpix[i].A) + 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(rgba.R) - dg := uint32(rgba.G) - db := uint32(rgba.B) - da := uint32(rgba.A) + + 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 - (ca * ma / m)) * 0x101 - dr = (dr*a + cr*ma) / m - dg = (dg*a + cg*ma) / m - db = (db*a + cb*ma) / m - da = (da*a + ca*ma) / m - dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + 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 - j0 += mask.Stride + mi0 += mask.Stride } } @@ -310,17 +311,19 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { if r.Dy() < 1 { return } - cr, cg, cb, ca := src.RGBA() - color := image.RGBAColor{uint8(cr >> 8), uint8(cg >> 8), uint8(cb >> 8), uint8(ca >> 8)} + 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 - i1 := i0 + r.Dx() - firstRow := dst.Pix[i0:i1] - for i := range firstRow { - firstRow[i] = color + 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 @@ -329,9 +332,9 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) { } func drawCopySrc(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 - s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + sp.X - src.Rect.Min.X + 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 @@ -346,14 +349,16 @@ func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.P sdelta = -src.Stride } for ; dy > 0; dy-- { - copy(dst.Pix[d0:d0+dx], src.Pix[s0:s0+dx]) + copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n]) d0 += ddelta s0 += sdelta } } func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { - xMax := r.Max.X - dst.Rect.Min.X + 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 @@ -362,20 +367,17 @@ func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image dpix := dst.Pix[y*dst.Stride:] spix := src.Pix[sy*src.Stride:] - x := r.Min.X - dst.Rect.Min.X - sx := sp.X - src.Rect.Min.X - for ; x != xMax; x, sx = x+1, sx+1 { + for i, si := i0, si0; i < i1; i, si = i+4, si+4 { // Convert from non-premultiplied color to pre-multiplied color. - // The order of operations here is to match the NRGBAColor.RGBA - // method in image/color.go. - snrgba := spix[sx] - sa := uint32(snrgba.A) - sr := uint32(snrgba.R) * 0x101 * sa / 0xff - sg := uint32(snrgba.G) * 0x101 * sa / 0xff - sb := uint32(snrgba.B) * 0x101 * sa / 0xff - sa *= 0x101 + 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[x] = image.RGBAColor{uint8(sr >> 8), uint8(sg >> 8), uint8(sb >> 8), uint8(sa >> 8)} + dpix[i+0] = uint8(sr >> 8) + dpix[i+1] = uint8(sg >> 8) + dpix[i+2] = uint8(sb >> 8) + dpix[i+3] = uint8(sa >> 8) } } } @@ -385,47 +387,55 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po // (i.e. fully opaque) then the op is effectively always Src. var ( yy, cb, cr uint8 - rr, gg, bb uint8 ) - x0 := r.Min.X - dst.Rect.Min.X - x1 := r.Max.X - dst.Rect.Min.X + 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+1, sx+1 { + 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] = image.RGBAColor{rr, gg, bb, 255} + 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+1, sx+1 { + 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] = image.RGBAColor{rr, gg, bb, 255} + 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+1, sx+1 { + 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] = image.RGBAColor{rr, gg, bb, 255} + rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr) + dpix[x+0] = rr + dpix[x+1] = gg + dpix[x+2] = bb + dpix[x+3] = 255 } } } @@ -445,22 +455,22 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin my := mp.Y + y0 - r.Min.Y sx0 := sp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X - i0 := (y0 - dst.Rect.Min.Y) * dst.Stride + 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 { - dpix := dst.Pix[i0:] - for x, sx, mx := x0, sx0, mx0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { + 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() - var dr, dg, db, da uint32 if op == Over { - rgba := dpix[x-dst.Rect.Min.X] - dr = uint32(rgba.R) - dg = uint32(rgba.G) - db = uint32(rgba.B) - da = uint32(rgba.A) + 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 @@ -468,17 +478,18 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin // (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 - dr = (dr*a + sr*ma) / m - dg = (dg*a + sg*ma) / m - db = (db*a + sb*ma) / m - da = (da*a + sa*ma) / m + + 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 { - dr = sr * ma / m - dg = sg * ma / m - db = sb * ma / m - da = sa * ma / m + 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) } - dpix[x-dst.Rect.Min.X] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } i0 += dy * dst.Stride } diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index f2726d7d8e..11def94354 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/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,11 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -42,24 +43,31 @@ func (p *RGBA) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return RGBAColor{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -72,7 +80,7 @@ func (p *RGBA) SubImage(r Rectangle) Image { if r.Empty() { return &RGBA{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -85,10 +93,10 @@ func (p *RGBA) Opaque() bool { if p.Rect.Empty() { return true } - i0, i1 := 0, p.Rect.Dx() + 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 } } @@ -100,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -122,24 +131,44 @@ func (p *RGBA64) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return RGBA64Color{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -152,7 +181,7 @@ func (p *RGBA64) SubImage(r Rectangle) Image { if r.Empty() { return &RGBA64{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -165,10 +194,10 @@ func (p *RGBA64) Opaque() bool { if p.Rect.Empty() { return true } - i0, i1 := 0, p.Rect.Dx() + 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 } } @@ -180,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -202,24 +232,31 @@ func (p *NRGBA) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return NRGBAColor{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -232,7 +269,7 @@ func (p *NRGBA) SubImage(r Rectangle) Image { if r.Empty() { return &NRGBA{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -245,10 +282,10 @@ func (p *NRGBA) Opaque() bool { if p.Rect.Empty() { return true } - i0, i1 := 0, p.Rect.Dx() + 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 } } @@ -260,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -282,24 +320,44 @@ func (p *NRGBA64) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return NRGBA64Color{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -312,7 +370,7 @@ func (p *NRGBA64) SubImage(r Rectangle) Image { if r.Empty() { return &NRGBA64{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -325,10 +383,10 @@ func (p *NRGBA64) Opaque() bool { if p.Rect.Empty() { return true } - i0, i1 := 0, p.Rect.Dx() + 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 } } @@ -340,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -363,7 +422,7 @@ func (p *Alpha) At(x, y int) Color { return AlphaColor{} } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + return AlphaColor{p.Pix[i]} } func (p *Alpha) Set(x, y int, c Color) { @@ -371,7 +430,7 @@ func (p *Alpha) Set(x, y int, c Color) { return } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = toAlphaColor(c).(AlphaColor) + p.Pix[i] = toAlphaColor(c).(AlphaColor).A } func (p *Alpha) SetAlpha(x, y int, c AlphaColor) { @@ -379,7 +438,7 @@ func (p *Alpha) SetAlpha(x, y int, c AlphaColor) { return } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = c + p.Pix[i] = c.A } // SubImage returns an image representing the portion of the image p visible @@ -392,7 +451,7 @@ func (p *Alpha) SubImage(r Rectangle) Image { if r.Empty() { return &Alpha{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -407,8 +466,8 @@ func (p *Alpha) Opaque() 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] { - if c.A != 0xff { + for i := i0; i < i1; i++ { + if p.Pix[i] != 0xff { return false } } @@ -420,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -442,24 +502,27 @@ func (p *Alpha16) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return Alpha16Color{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -472,7 +535,7 @@ func (p *Alpha16) SubImage(r Rectangle) Image { if r.Empty() { return &Alpha16{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -485,10 +548,10 @@ func (p *Alpha16) Opaque() bool { if p.Rect.Empty() { return true } - i0, i1 := 0, p.Rect.Dx() + 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 } } @@ -500,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -523,7 +587,7 @@ func (p *Gray) At(x, y int) Color { return GrayColor{} } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + return GrayColor{p.Pix[i]} } func (p *Gray) Set(x, y int, c Color) { @@ -531,7 +595,7 @@ func (p *Gray) Set(x, y int, c Color) { return } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = toGrayColor(c).(GrayColor) + p.Pix[i] = toGrayColor(c).(GrayColor).Y } func (p *Gray) SetGray(x, y int, c GrayColor) { @@ -539,7 +603,7 @@ func (p *Gray) SetGray(x, y int, c GrayColor) { return } i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = c + p.Pix[i] = c.Y } // SubImage returns an image representing the portion of the image p visible @@ -552,7 +616,7 @@ func (p *Gray) SubImage(r Rectangle) Image { if r.Empty() { return &Gray{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -567,15 +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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -589,24 +654,27 @@ func (p *Gray16) At(x, y int) Color { if !(Point{x, y}.In(p.Rect)) { return Gray16Color{} } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - return p.Pix[i] + 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 !(Point{x, y}.In(p.Rect)) { return } - i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X) - p.Pix[i] = 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 @@ -619,7 +687,7 @@ func (p *Gray16) SubImage(r Rectangle) Image { if r.Empty() { return &Gray16{} } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -634,8 +702,8 @@ 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 at most 256 colors. @@ -679,11 +747,12 @@ func (p PalettedColorModel) Index(c Color) int { 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-Rect.Min.Y)*Stride + (x-Rect.Min.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 @@ -742,7 +811,7 @@ func (p *Paletted) SubImage(r Rectangle) Image { Palette: p.Palette, } } - i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X) + 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, @@ -776,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/src/pkg/image/jpeg/reader.go b/src/pkg/image/jpeg/reader.go index 8798919637..3f22c5271f 100644 --- a/src/pkg/image/jpeg/reader.go +++ b/src/pkg/image/jpeg/reader.go @@ -319,16 +319,7 @@ func (d *decoder) processSOS(n int) os.Error { // Perform the inverse DCT and store the MCU component to the image. if d.nComp == nGrayComponent { - idct(d.tmp[:64], 8, &b) - // Convert from []uint8 to []image.GrayColor. - p := d.img1.Pix[8*(my*d.img1.Stride+mx):] - for y := 0; y < 8; y++ { - dst := p[y*d.img1.Stride:] - src := d.tmp[8*y:] - for x := 0; x < 8; x++ { - dst[x] = image.GrayColor{src[x]} - } - } + idct(d.img1.Pix[8*(my*d.img1.Stride+mx):], d.img1.Stride, &b) } else { switch i { case 0: diff --git a/src/pkg/image/jpeg/writer.go b/src/pkg/image/jpeg/writer.go index 76a85adb05..2bb6df5dd1 100644 --- a/src/pkg/image/jpeg/writer.go +++ b/src/pkg/image/jpeg/writer.go @@ -397,14 +397,14 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block) if sj > ymax { sj = ymax } - offset := (sj-b.Min.Y)*m.Stride - b.Min.X + 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[offset+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/src/pkg/image/png/writer.go b/src/pkg/image/png/writer.go index 81d402fc9f..55ca97e062 100644 --- a/src/pkg/image/png/writer.go +++ b/src/pkg/image/png/writer.go @@ -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: @@ -323,12 +317,13 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error { case cbTC8: // We have previously verified that the alpha value is fully opaque. cr0 := cr[0] - if rgba != nil { - offset := (y - b.Min.Y) * rgba.Stride - for _, color := range rgba.Pix[offset : offset+b.Dx()] { - cr0[i+0] = color.R - cr0[i+1] = color.G - cr0[i+2] = color.B + 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 { @@ -341,6 +336,7 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error { } } case cbP8: + paletted := m.(*image.Paletted) offset := (y - b.Min.Y) * paletted.Stride copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()]) case cbTCA8: