1
0
mirror of https://github.com/golang/go synced 2024-11-19 11:04:47 -07:00

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
This commit is contained in:
Nigel Tao 2011-07-12 16:39:38 +10:00
parent 6ec6f1ef62
commit 8bd5089513
8 changed files with 367 additions and 296 deletions

View File

@ -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())
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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}
}

View File

@ -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:

View File

@ -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)

View File

@ -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: