1
0
mirror of https://github.com/golang/go synced 2024-11-19 14:44:40 -07:00

image: change Pix[0] to mean top-left corner of an image's Rect instead

of the origin.

image/png and image/jpeg benchmarks show no significant changes.

The image/draw changes suggest to me that making a gofix for this is not
feasible. People are just going to have to make manual fixes.

R=r
CC=golang-dev
https://golang.org/cl/4681044
This commit is contained in:
Nigel Tao 2011-07-10 14:29:47 +10:00
parent 4d47600f46
commit 87d9e7e166
8 changed files with 299 additions and 185 deletions

View File

@ -85,15 +85,15 @@ func (c *conn) writeSocket() {
for y := b.Min.Y; y < b.Max.Y; y++ { for y := b.Min.Y; y < b.Max.Y; y++ {
setU32LE(c.flushBuf0[16:20], uint32(y<<16)) setU32LE(c.flushBuf0[16:20], uint32(y<<16))
if _, err := c.w.Write(c.flushBuf0[0:24]); err != nil { if _, err := c.w.Write(c.flushBuf0[:24]); err != nil {
if err != os.EOF { if err != os.EOF {
log.Println("x11:", err.String()) log.Println("x11:", err.String())
} }
return return
} }
p := c.img.Pix[y*c.img.Stride : (y+1)*c.img.Stride] p := c.img.Pix[(y-b.Min.Y)*c.img.Stride:]
for x := b.Min.X; x < b.Max.X; { for x, dx := 0, b.Dx(); x < dx; {
nx := b.Max.X - x nx := dx - x
if nx > len(c.flushBuf1)/4 { if nx > len(c.flushBuf1)/4 {
nx = len(c.flushBuf1) / 4 nx = len(c.flushBuf1) / 4
} }
@ -103,7 +103,7 @@ func (c *conn) writeSocket() {
c.flushBuf1[4*i+2] = rgba.R c.flushBuf1[4*i+2] = rgba.R
} }
x += nx x += nx
if _, err := c.w.Write(c.flushBuf1[0 : 4*nx]); err != nil { if _, err := c.w.Write(c.flushBuf1[:4*nx]); err != nil {
if err != os.EOF { if err != os.EOF {
log.Println("x11:", err.String()) log.Println("x11:", err.String())
} }
@ -154,7 +154,7 @@ func (c *conn) readSocket() {
defer close(c.eventc) defer close(c.eventc)
for { for {
// X events are always 32 bytes long. // X events are always 32 bytes long.
if _, err := io.ReadFull(c.r, c.buf[0:32]); err != nil { if _, err := io.ReadFull(c.r, c.buf[:32]); err != nil {
if err != os.EOF { if err != os.EOF {
c.eventc <- gui.ErrEvent{err} c.eventc <- gui.ErrEvent{err}
} }
@ -177,7 +177,7 @@ func (c *conn) readSocket() {
for i := keymapLo; i <= keymapHi; i++ { for i := keymapLo; i <= keymapHi; i++ {
m := keymap[i] m := keymap[i]
for j := range m { for j := range m {
u, err := readU32LE(c.r, c.buf[0:4]) u, err := readU32LE(c.r, c.buf[:4])
if err != nil { if err != nil {
if err != os.EOF { if err != os.EOF {
c.eventc <- gui.ErrEvent{err} c.eventc <- gui.ErrEvent{err}
@ -260,14 +260,14 @@ func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
// Parse the section before the colon. // Parse the section before the colon.
var protocol, host, socket string var protocol, host, socket string
if display[0] == '/' { if display[0] == '/' {
socket = display[0:colonIdx] socket = display[:colonIdx]
} else { } else {
if i := strings.LastIndex(display, "/"); i < 0 { if i := strings.LastIndex(display, "/"); i < 0 {
// The default protocol is TCP. // The default protocol is TCP.
protocol = "tcp" protocol = "tcp"
host = display[0:colonIdx] host = display[:colonIdx]
} else { } else {
protocol = display[0:i] protocol = display[:i]
host = display[i+1 : colonIdx] host = display[i+1 : colonIdx]
} }
} }
@ -279,7 +279,7 @@ func connect(display string) (conn net.Conn, displayStr string, err os.Error) {
if i := strings.LastIndex(after, "."); i < 0 { if i := strings.LastIndex(after, "."); i < 0 {
displayStr = after displayStr = after
} else { } else {
displayStr = after[0:i] displayStr = after[:i]
} }
displayInt, err := strconv.Atoi(displayStr) displayInt, err := strconv.Atoi(displayStr)
if err != nil || displayInt < 0 { if err != nil || displayInt < 0 {
@ -339,7 +339,7 @@ func authenticate(w *bufio.Writer, displayStr string) os.Error {
// readU8 reads a uint8 from r, using b as a scratch buffer. // readU8 reads a uint8 from r, using b as a scratch buffer.
func readU8(r io.Reader, b []byte) (uint8, os.Error) { func readU8(r io.Reader, b []byte) (uint8, os.Error) {
_, err := io.ReadFull(r, b[0:1]) _, err := io.ReadFull(r, b[:1])
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -348,7 +348,7 @@ func readU8(r io.Reader, b []byte) (uint8, os.Error) {
// readU16LE reads a little-endian uint16 from r, using b as a scratch buffer. // readU16LE reads a little-endian uint16 from r, using b as a scratch buffer.
func readU16LE(r io.Reader, b []byte) (uint16, os.Error) { func readU16LE(r io.Reader, b []byte) (uint16, os.Error) {
_, err := io.ReadFull(r, b[0:2]) _, err := io.ReadFull(r, b[:2])
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -357,14 +357,14 @@ func readU16LE(r io.Reader, b []byte) (uint16, os.Error) {
// readU32LE reads a little-endian uint32 from r, using b as a scratch buffer. // readU32LE reads a little-endian uint32 from r, using b as a scratch buffer.
func readU32LE(r io.Reader, b []byte) (uint32, os.Error) { func readU32LE(r io.Reader, b []byte) (uint32, os.Error) {
_, err := io.ReadFull(r, b[0:4]) _, err := io.ReadFull(r, b[:4])
if err != nil { if err != nil {
return 0, err return 0, err
} }
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, nil
} }
// setU32LE sets b[0:4] to be the little-endian representation of u. // setU32LE sets b[:4] to be the little-endian representation of u.
func setU32LE(b []byte, u uint32) { func setU32LE(b []byte, u uint32) {
b[0] = byte((u >> 0) & 0xff) b[0] = byte((u >> 0) & 0xff)
b[1] = byte((u >> 8) & 0xff) b[1] = byte((u >> 8) & 0xff)
@ -375,7 +375,7 @@ func setU32LE(b []byte, u uint32) {
// checkPixmapFormats checks that we have an agreeable X pixmap Format. // checkPixmapFormats checks that we have an agreeable X pixmap Format.
func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error) { func checkPixmapFormats(r io.Reader, b []byte, n int) (agree bool, err os.Error) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
_, err = io.ReadFull(r, b[0:8]) _, err = io.ReadFull(r, b[:8])
if err != nil { if err != nil {
return return
} }
@ -400,7 +400,7 @@ func checkDepths(r io.Reader, b []byte, n int, visual uint32) (agree bool, err o
return return
} }
// Ignore 4 bytes of padding. // Ignore 4 bytes of padding.
_, err = io.ReadFull(r, b[0:4]) _, err = io.ReadFull(r, b[:4])
if err != nil { if err != nil {
return return
} }
@ -433,7 +433,7 @@ func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Err
} }
// Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks, // Ignore the next 7x4 bytes, which is: colormap, whitepixel, blackpixel, current input masks,
// width and height (pixels), width and height (mm), min and max installed maps. // width and height (pixels), width and height (mm), min and max installed maps.
_, err = io.ReadFull(r, b[0:28]) _, err = io.ReadFull(r, b[:28])
if err != nil { if err != nil {
return return
} }
@ -462,26 +462,26 @@ func checkScreens(r io.Reader, b []byte, n int) (root, visual uint32, err os.Err
// handshake performs the protocol handshake with the X server, and ensures // handshake performs the protocol handshake with the X server, and ensures
// that the server provides a compatible Screen, Depth, etc. // that the server provides a compatible Screen, Depth, etc.
func (c *conn) handshake() os.Error { func (c *conn) handshake() os.Error {
_, err := io.ReadFull(c.r, c.buf[0:8]) _, err := io.ReadFull(c.r, c.buf[:8])
if err != nil { if err != nil {
return err return err
} }
// Byte 0:1 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0). // Byte 0 should be 1 (success), bytes 2:6 should be 0xb0000000 (major/minor version 11.0).
if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 { if c.buf[0] != 1 || c.buf[2] != 11 || c.buf[3] != 0 || c.buf[4] != 0 || c.buf[5] != 0 {
return os.NewError("unsupported X version") return os.NewError("unsupported X version")
} }
// Ignore the release number. // Ignore the release number.
_, err = io.ReadFull(c.r, c.buf[0:4]) _, err = io.ReadFull(c.r, c.buf[:4])
if err != nil { if err != nil {
return err return err
} }
// Read the resource ID base. // Read the resource ID base.
resourceIdBase, err := readU32LE(c.r, c.buf[0:4]) resourceIdBase, err := readU32LE(c.r, c.buf[:4])
if err != nil { if err != nil {
return err return err
} }
// Read the resource ID mask. // Read the resource ID mask.
resourceIdMask, err := readU32LE(c.r, c.buf[0:4]) resourceIdMask, err := readU32LE(c.r, c.buf[:4])
if err != nil { if err != nil {
return err return err
} }
@ -489,19 +489,19 @@ func (c *conn) handshake() os.Error {
return os.NewError("X resource ID mask is too small") return os.NewError("X resource ID mask is too small")
} }
// Ignore the motion buffer size. // Ignore the motion buffer size.
_, err = io.ReadFull(c.r, c.buf[0:4]) _, err = io.ReadFull(c.r, c.buf[:4])
if err != nil { if err != nil {
return err return err
} }
// Read the vendor length and round it up to a multiple of 4, // Read the vendor length and round it up to a multiple of 4,
// for X11 protocol alignment reasons. // for X11 protocol alignment reasons.
vendorLen, err := readU16LE(c.r, c.buf[0:2]) vendorLen, err := readU16LE(c.r, c.buf[:2])
if err != nil { if err != nil {
return err return err
} }
vendorLen = (vendorLen + 3) &^ 3 vendorLen = (vendorLen + 3) &^ 3
// Read the maximum request length. // Read the maximum request length.
maxReqLen, err := readU16LE(c.r, c.buf[0:2]) maxReqLen, err := readU16LE(c.r, c.buf[:2])
if err != nil { if err != nil {
return err return err
} }
@ -509,12 +509,12 @@ func (c *conn) handshake() os.Error {
return os.NewError("unsupported X maximum request length") return os.NewError("unsupported X maximum request length")
} }
// Read the roots length. // Read the roots length.
rootsLen, err := readU8(c.r, c.buf[0:1]) rootsLen, err := readU8(c.r, c.buf[:1])
if err != nil { if err != nil {
return err return err
} }
// Read the pixmap formats length. // Read the pixmap formats length.
pixmapFormatsLen, err := readU8(c.r, c.buf[0:1]) pixmapFormatsLen, err := readU8(c.r, c.buf[:1])
if err != nil { if err != nil {
return err return err
} }
@ -524,12 +524,12 @@ func (c *conn) handshake() os.Error {
if 10+int(vendorLen) > cap(c.buf) { if 10+int(vendorLen) > cap(c.buf) {
return os.NewError("unsupported X vendor") return os.NewError("unsupported X vendor")
} }
_, err = io.ReadFull(c.r, c.buf[0:10+int(vendorLen)]) _, err = io.ReadFull(c.r, c.buf[:10+int(vendorLen)])
if err != nil { if err != nil {
return err return err
} }
// Check that we have an agreeable pixmap format. // Check that we have an agreeable pixmap format.
agree, err := checkPixmapFormats(c.r, c.buf[0:8], int(pixmapFormatsLen)) agree, err := checkPixmapFormats(c.r, c.buf[:8], int(pixmapFormatsLen))
if err != nil { if err != nil {
return err return err
} }
@ -537,7 +537,7 @@ func (c *conn) handshake() os.Error {
return os.NewError("unsupported X pixmap formats") return os.NewError("unsupported X pixmap formats")
} }
// Check that we have an agreeable screen. // Check that we have an agreeable screen.
root, visual, err := checkScreens(c.r, c.buf[0:24], int(rootsLen)) root, visual, err := checkScreens(c.r, c.buf[:24], int(rootsLen))
if err != nil { if err != nil {
return err return err
} }
@ -608,7 +608,7 @@ func NewWindowDisplay(display string) (gui.Window, os.Error) {
setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long. setU32LE(c.buf[72:76], 0x00020008) // 0x08 is the MapWindow opcode, and the message is 2 x 4 bytes long.
setU32LE(c.buf[76:80], uint32(c.window)) setU32LE(c.buf[76:80], uint32(c.window))
// Write the bytes. // Write the bytes.
_, err = c.w.Write(c.buf[0:80]) _, err = c.w.Write(c.buf[:80])
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -173,11 +173,10 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
cr, cg, cb, ca := src.RGBA() cr, cg, cb, ca := src.RGBA()
// The 0x101 is here for the same reason as in drawRGBA. // The 0x101 is here for the same reason as in drawRGBA.
a := (m - ca) * 0x101 a := (m - ca) * 0x101
x0, x1 := r.Min.X, r.Max.X i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X
y0, y1 := r.Min.Y, r.Max.Y i1 := i0 + r.Dx()
for y := y0; y != y1; y++ { for y := r.Min.Y; y != r.Max.Y; y++ {
dbase := y * dst.Stride dpix := dst.Pix[i0:i1]
dpix := dst.Pix[dbase+x0 : dbase+x1]
for i, rgba := range dpix { for i, rgba := range dpix {
dr := (uint32(rgba.R)*a)/m + cr dr := (uint32(rgba.R)*a)/m + cr
dg := (uint32(rgba.G)*a)/m + cg dg := (uint32(rgba.G)*a)/m + cg
@ -185,18 +184,15 @@ func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
da := (uint32(rgba.A)*a)/m + ca da := (uint32(rgba.A)*a)/m + ca
dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
} }
i0 += dst.Stride
i1 += dst.Stride
} }
} }
func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
dx0, dx1 := r.Min.X, r.Max.X dx, dy := r.Dx(), r.Dy()
dy0, dy1 := r.Min.Y, r.Max.Y d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X
nrows := dy1 - dy0 s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + sp.X - src.Rect.Min.X
sx0, sx1 := sp.X, sp.X+dx1-dx0
d0 := dy0*dst.Stride + dx0
d1 := dy0*dst.Stride + dx1
s0 := sp.Y*src.Stride + sx0
s1 := sp.Y*src.Stride + sx1
var ( var (
ddelta, sdelta int ddelta, sdelta int
i0, i1, idelta int i0, i1, idelta int
@ -204,21 +200,19 @@ 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 { if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X {
ddelta = dst.Stride ddelta = dst.Stride
sdelta = src.Stride sdelta = src.Stride
i0, i1, idelta = 0, d1-d0, +1 i0, i1, idelta = 0, dx, +1
} else { } else {
// If the source start point is higher than the destination start point, or equal height but to the left, // 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. // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down.
d0 += (nrows - 1) * dst.Stride d0 += (dy - 1) * dst.Stride
d1 += (nrows - 1) * dst.Stride s0 += (dy - 1) * src.Stride
s0 += (nrows - 1) * src.Stride
s1 += (nrows - 1) * src.Stride
ddelta = -dst.Stride ddelta = -dst.Stride
sdelta = -src.Stride sdelta = -src.Stride
i0, i1, idelta = d1-d0-1, -1, -1 i0, i1, idelta = dx-1, -1, -1
} }
for ; nrows > 0; nrows-- { for ; dy > 0; dy-- {
dpix := dst.Pix[d0:d1] dpix := dst.Pix[d0:]
spix := src.Pix[s0:s1] spix := src.Pix[s0:]
for i := i0; i != i1; i += idelta { for i := i0; i != i1; i += idelta {
// For unknown reasons, even though both dpix[i] and spix[i] are // For unknown reasons, even though both dpix[i] and spix[i] are
// image.RGBAColors, on an x86 CPU it seems fastest to call RGBA // image.RGBAColors, on an x86 CPU it seems fastest to call RGBA
@ -238,17 +232,23 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.
dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
} }
d0 += ddelta d0 += ddelta
d1 += ddelta
s0 += sdelta s0 += sdelta
s1 += sdelta
} }
} }
func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 { xMax := r.Max.X - dst.Rect.Min.X
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] yMax := r.Max.Y - dst.Rect.Min.Y
spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 { y := r.Min.Y - dst.Rect.Min.Y
sy := sp.Y - src.Rect.Min.Y
for ; y != yMax; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
spix := src.Pix[sy*src.Stride:]
x := r.Min.X - dst.Rect.Min.X
sx := sp.X - src.Rect.Min.X
for ; x != xMax; x, sx = x+1, sx+1 {
// Convert from non-premultiplied color to pre-multiplied color. // Convert from non-premultiplied color to pre-multiplied color.
// The order of operations here is to match the NRGBAColor.RGBA // The order of operations here is to match the NRGBAColor.RGBA
// method in image/color.go. // method in image/color.go.
@ -275,14 +275,13 @@ func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp imag
} }
func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) { func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) {
x0, x1 := r.Min.X, r.Max.X i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X
y0, y1 := r.Min.Y, r.Max.Y 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() cr, cg, cb, ca := src.RGBA()
for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 { for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 {
dbase := y * dst.Stride dpix := dst.Pix[i0:i1]
dpix := dst.Pix[dbase+x0 : dbase+x1] mpix := mask.Pix[j0:]
mbase := my * mask.Stride
mpix := mask.Pix[mbase+mp.X:]
for i, rgba := range dpix { for i, rgba := range dpix {
ma := uint32(mpix[i].A) ma := uint32(mpix[i].A)
if ma == 0 { if ma == 0 {
@ -301,6 +300,9 @@ func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, ma
da = (da*a + ca*ma) / m da = (da*a + ca*ma) / m
dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} dpix[i] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
} }
i0 += dst.Stride
i1 += dst.Stride
j0 += mask.Stride
} }
} }
@ -313,15 +315,13 @@ func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.ColorImage) {
// The built-in copy function is faster than a straightforward for loop to fill the destination with // 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 // 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. // then use the first row as the slice source for the remaining rows.
dx0, dx1 := r.Min.X, r.Max.X i0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X
dy0, dy1 := r.Min.Y, r.Max.Y i1 := i0 + r.Dx()
dbase := dy0 * dst.Stride
i0, i1 := dbase+dx0, dbase+dx1
firstRow := dst.Pix[i0:i1] firstRow := dst.Pix[i0:i1]
for i := range firstRow { for i := range firstRow {
firstRow[i] = color firstRow[i] = color
} }
for y := dy0 + 1; y < dy1; y++ { for y := r.Min.Y + 1; y < r.Max.Y; y++ {
i0 += dst.Stride i0 += dst.Stride
i1 += dst.Stride i1 += dst.Stride
copy(dst.Pix[i0:i1], firstRow) copy(dst.Pix[i0:i1], firstRow)
@ -329,14 +329,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) { func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
dx0, dx1 := r.Min.X, r.Max.X dx, dy := r.Dx(), r.Dy()
dy0, dy1 := r.Min.Y, r.Max.Y d0 := (r.Min.Y-dst.Rect.Min.Y)*dst.Stride + r.Min.X - dst.Rect.Min.X
nrows := dy1 - dy0 s0 := (sp.Y-src.Rect.Min.Y)*src.Stride + sp.X - src.Rect.Min.X
sx0, sx1 := sp.X, sp.X+dx1-dx0
d0 := dy0*dst.Stride + dx0
d1 := dy0*dst.Stride + dx1
s0 := sp.Y*src.Stride + sx0
s1 := sp.Y*src.Stride + sx1
var ddelta, sdelta int var ddelta, sdelta int
if r.Min.Y <= sp.Y { if r.Min.Y <= sp.Y {
ddelta = dst.Stride ddelta = dst.Stride
@ -345,27 +340,31 @@ func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.P
// If the source start point is higher than the destination start point, then we compose the rows // If the source start point is higher than the destination start point, then we compose the rows
// in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to // in bottom-up order instead of top-down. Unlike the drawCopyOver function, we don't have to
// check the x co-ordinates because the built-in copy function can handle overlapping slices. // check the x co-ordinates because the built-in copy function can handle overlapping slices.
d0 += (nrows - 1) * dst.Stride d0 += (dy - 1) * dst.Stride
d1 += (nrows - 1) * dst.Stride s0 += (dy - 1) * src.Stride
s0 += (nrows - 1) * src.Stride
s1 += (nrows - 1) * src.Stride
ddelta = -dst.Stride ddelta = -dst.Stride
sdelta = -src.Stride sdelta = -src.Stride
} }
for ; nrows > 0; nrows-- { for ; dy > 0; dy-- {
copy(dst.Pix[d0:d1], src.Pix[s0:s1]) copy(dst.Pix[d0:d0+dx], src.Pix[s0:s0+dx])
d0 += ddelta d0 += ddelta
d1 += ddelta
s0 += sdelta s0 += sdelta
s1 += sdelta
} }
} }
func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) { func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 { xMax := r.Max.X - dst.Rect.Min.X
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] yMax := r.Max.Y - dst.Rect.Min.Y
spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 { y := r.Min.Y - dst.Rect.Min.Y
sy := sp.Y - src.Rect.Min.Y
for ; y != yMax; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
spix := src.Pix[sy*src.Stride:]
x := r.Min.X - dst.Rect.Min.X
sx := sp.X - src.Rect.Min.X
for ; x != xMax; x, sx = x+1, sx+1 {
// Convert from non-premultiplied color to pre-multiplied color. // Convert from non-premultiplied color to pre-multiplied color.
// The order of operations here is to match the NRGBAColor.RGBA // The order of operations here is to match the NRGBAColor.RGBA
// method in image/color.go. // method in image/color.go.
@ -388,11 +387,15 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
yy, cb, cr uint8 yy, cb, cr uint8
rr, gg, bb uint8 rr, gg, bb uint8
) )
x0 := r.Min.X - dst.Rect.Min.X
x1 := r.Max.X - dst.Rect.Min.X
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio { switch src.SubsampleRatio {
case ycbcr.SubsampleRatio422: case ycbcr.SubsampleRatio422:
for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 { for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] dpix := dst.Pix[y*dst.Stride:]
for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 { for x, sx := x0, sp.X; x != x1; x, sx = x+1, sx+1 {
i := sx / 2 i := sx / 2
yy = src.Y[sy*src.YStride+sx] yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[sy*src.CStride+i] cb = src.Cb[sy*src.CStride+i]
@ -402,9 +405,9 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
} }
} }
case ycbcr.SubsampleRatio420: case ycbcr.SubsampleRatio420:
for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 { for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] dpix := dst.Pix[y*dst.Stride:]
for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 { for x, sx := x0, sp.X; x != x1; x, sx = x+1, sx+1 {
i, j := sx/2, sy/2 i, j := sx/2, sy/2
yy = src.Y[sy*src.YStride+sx] yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[j*src.CStride+i] cb = src.Cb[j*src.CStride+i]
@ -415,9 +418,9 @@ func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Po
} }
default: default:
// Default to 4:4:4 subsampling. // Default to 4:4:4 subsampling.
for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 { for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride] dpix := dst.Pix[y*dst.Stride:]
for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 { for x, sx := x0, sp.X; x != x1; x, sx = x+1, sx+1 {
yy = src.Y[sy*src.YStride+sx] yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[sy*src.CStride+sx] cb = src.Cb[sy*src.CStride+sx]
cr = src.Cr[sy*src.CStride+sx] cr = src.Cr[sy*src.CStride+sx]
@ -440,11 +443,12 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
sy := sp.Y + y0 - r.Min.Y sy := sp.Y + y0 - r.Min.Y
my := mp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y
sx0 := sp.X + x0 - r.Min.X
mx0 := mp.X + x0 - r.Min.X
i0 := (y0 - dst.Rect.Min.Y) * dst.Stride
for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
sx := sp.X + x0 - r.Min.X dpix := dst.Pix[i0:]
mx := mp.X + x0 - r.Min.X for x, sx, mx := x0, sx0, mx0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
ma := uint32(m) ma := uint32(m)
if mask != nil { if mask != nil {
_, _, _, ma = mask.At(mx, my).RGBA() _, _, _, ma = mask.At(mx, my).RGBA()
@ -452,7 +456,7 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
sr, sg, sb, sa := src.At(sx, sy).RGBA() sr, sg, sb, sa := src.At(sx, sy).RGBA()
var dr, dg, db, da uint32 var dr, dg, db, da uint32
if op == Over { if op == Over {
rgba := dpix[x] rgba := dpix[x-dst.Rect.Min.X]
dr = uint32(rgba.R) dr = uint32(rgba.R)
dg = uint32(rgba.G) dg = uint32(rgba.G)
db = uint32(rgba.B) db = uint32(rgba.B)
@ -474,7 +478,8 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin
db = sb * ma / m db = sb * ma / m
da = sa * ma / m da = sa * ma / m
} }
dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 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

@ -26,7 +26,8 @@ type Image interface {
// An RGBA is an in-memory image of RGBAColor values. // An RGBA is an in-memory image of RGBAColor values.
type RGBA struct { type RGBA struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []RGBAColor
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -41,30 +42,41 @@ func (p *RGBA) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return RGBAColor{} return RGBAColor{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *RGBA) Set(x, y int, c Color) { func (p *RGBA) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toRGBAColor(c).(RGBAColor)
} }
func (p *RGBA) SetRGBA(x, y int, c RGBAColor) { func (p *RGBA) SetRGBA(x, y int, c RGBAColor) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *RGBA) SubImage(r Rectangle) Image { func (p *RGBA) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &RGBA{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &RGBA{ return &RGBA{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -73,8 +85,7 @@ func (p *RGBA) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xff { if c.A != 0xff {
@ -95,7 +106,8 @@ func NewRGBA(w, h int) *RGBA {
// An RGBA64 is an in-memory image of RGBA64Color values. // An RGBA64 is an in-memory image of RGBA64Color values.
type RGBA64 struct { type RGBA64 struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []RGBA64Color
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -110,30 +122,41 @@ func (p *RGBA64) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return RGBA64Color{} return RGBA64Color{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *RGBA64) Set(x, y int, c Color) { func (p *RGBA64) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toRGBA64Color(c).(RGBA64Color)
} }
func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) { func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *RGBA64) SubImage(r Rectangle) Image { func (p *RGBA64) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &RGBA64{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &RGBA64{ return &RGBA64{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -142,8 +165,7 @@ func (p *RGBA64) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xffff { if c.A != 0xffff {
@ -164,7 +186,8 @@ func NewRGBA64(w, h int) *RGBA64 {
// An NRGBA is an in-memory image of NRGBAColor values. // An NRGBA is an in-memory image of NRGBAColor values.
type NRGBA struct { type NRGBA struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []NRGBAColor
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -179,30 +202,41 @@ func (p *NRGBA) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return NRGBAColor{} return NRGBAColor{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *NRGBA) Set(x, y int, c Color) { func (p *NRGBA) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toNRGBAColor(c).(NRGBAColor)
} }
func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) { func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *NRGBA) SubImage(r Rectangle) Image { func (p *NRGBA) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &NRGBA{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &NRGBA{ return &NRGBA{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -211,8 +245,7 @@ func (p *NRGBA) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xff { if c.A != 0xff {
@ -233,7 +266,8 @@ func NewNRGBA(w, h int) *NRGBA {
// An NRGBA64 is an in-memory image of NRGBA64Color values. // An NRGBA64 is an in-memory image of NRGBA64Color values.
type NRGBA64 struct { type NRGBA64 struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []NRGBA64Color
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -248,30 +282,41 @@ func (p *NRGBA64) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return NRGBA64Color{} return NRGBA64Color{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *NRGBA64) Set(x, y int, c Color) { func (p *NRGBA64) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toNRGBA64Color(c).(NRGBA64Color)
} }
func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) { func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *NRGBA64) SubImage(r Rectangle) Image { func (p *NRGBA64) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &NRGBA64{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &NRGBA64{ return &NRGBA64{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -280,8 +325,7 @@ func (p *NRGBA64) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xffff { if c.A != 0xffff {
@ -302,7 +346,8 @@ func NewNRGBA64(w, h int) *NRGBA64 {
// An Alpha is an in-memory image of AlphaColor values. // An Alpha is an in-memory image of AlphaColor values.
type Alpha struct { type Alpha struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []AlphaColor
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -317,30 +362,41 @@ func (p *Alpha) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return AlphaColor{} return AlphaColor{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *Alpha) Set(x, y int, c Color) { func (p *Alpha) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toAlphaColor(c).(AlphaColor)
} }
func (p *Alpha) SetAlpha(x, y int, c AlphaColor) { func (p *Alpha) SetAlpha(x, y int, c AlphaColor) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *Alpha) SubImage(r Rectangle) Image { func (p *Alpha) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &Alpha{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &Alpha{ return &Alpha{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -349,8 +405,7 @@ func (p *Alpha) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xff { if c.A != 0xff {
@ -371,7 +426,8 @@ func NewAlpha(w, h int) *Alpha {
// An Alpha16 is an in-memory image of Alpha16Color values. // An Alpha16 is an in-memory image of Alpha16Color values.
type Alpha16 struct { type Alpha16 struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []Alpha16Color
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -386,30 +442,41 @@ func (p *Alpha16) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return Alpha16Color{} return Alpha16Color{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *Alpha16) Set(x, y int, c Color) { func (p *Alpha16) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toAlpha16Color(c).(Alpha16Color)
} }
func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) { func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *Alpha16) SubImage(r Rectangle) Image { func (p *Alpha16) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &Alpha16{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &Alpha16{ return &Alpha16{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -418,8 +485,7 @@ func (p *Alpha16) Opaque() bool {
if p.Rect.Empty() { if p.Rect.Empty() {
return true return true
} }
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
if c.A != 0xffff { if c.A != 0xffff {
@ -440,7 +506,8 @@ func NewAlpha16(w, h int) *Alpha16 {
// A Gray is an in-memory image of GrayColor values. // A Gray is an in-memory image of GrayColor values.
type Gray struct { type Gray struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []GrayColor
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -455,30 +522,41 @@ func (p *Gray) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return GrayColor{} return GrayColor{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *Gray) Set(x, y int, c Color) { func (p *Gray) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toGrayColor(c).(GrayColor)
} }
func (p *Gray) SetGray(x, y int, c GrayColor) { func (p *Gray) SetGray(x, y int, c GrayColor) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *Gray) SubImage(r Rectangle) Image { func (p *Gray) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &Gray{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &Gray{ return &Gray{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -495,7 +573,8 @@ func NewGray(w, h int) *Gray {
// A Gray16 is an in-memory image of Gray16Color values. // A Gray16 is an in-memory image of Gray16Color values.
type Gray16 struct { type Gray16 struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []Gray16Color
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -510,30 +589,41 @@ func (p *Gray16) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return Gray16Color{} return Gray16Color{}
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *Gray16) Set(x, y int, c Color) { func (p *Gray16) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = toGray16Color(c).(Gray16Color)
} }
func (p *Gray16) SetGray16(x, y int, c Gray16Color) { func (p *Gray16) SetGray16(x, y int, c Gray16Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = c i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = c
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *Gray16) SubImage(r Rectangle) Image { func (p *Gray16) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &Gray16{}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &Gray16{ return &Gray16{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: r,
} }
} }
@ -591,7 +681,8 @@ func (p PalettedColorModel) Index(c Color) int {
// A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel. // A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel.
type Paletted struct { type Paletted struct {
// Pix holds the image's pixels. The pixel at (x, y) is Pix[y*Stride+x]. // 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 []uint8
Stride int Stride int
// Rect is the image's bounds. // Rect is the image's bounds.
@ -611,35 +702,49 @@ func (p *Paletted) At(x, y int) Color {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return p.Palette[0] return p.Palette[0]
} }
return p.Palette[p.Pix[y*p.Stride+x]] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Palette[p.Pix[i]]
} }
func (p *Paletted) Set(x, y int, c Color) { func (p *Paletted) Set(x, y int, c Color) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = uint8(p.Palette.Index(c)) i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = uint8(p.Palette.Index(c))
} }
func (p *Paletted) ColorIndexAt(x, y int) uint8 { func (p *Paletted) ColorIndexAt(x, y int) uint8 {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return 0 return 0
} }
return p.Pix[y*p.Stride+x] i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
return p.Pix[i]
} }
func (p *Paletted) SetColorIndex(x, y int, index uint8) { func (p *Paletted) SetColorIndex(x, y int, index uint8) {
if !(Point{x, y}.In(p.Rect)) { if !(Point{x, y}.In(p.Rect)) {
return return
} }
p.Pix[y*p.Stride+x] = index i := (y-p.Rect.Min.Y)*p.Stride + (x - p.Rect.Min.X)
p.Pix[i] = index
} }
// SubImage returns an image representing the portion of the image p visible // SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image. // through r. The returned value shares pixels with the original image.
func (p *Paletted) SubImage(r Rectangle) Image { func (p *Paletted) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &Paletted{
Palette: p.Palette,
}
}
i := (r.Min.Y-p.Rect.Min.Y)*p.Stride + (r.Min.X - p.Rect.Min.X)
return &Paletted{ return &Paletted{
Pix: p.Pix, Pix: p.Pix[i:],
Stride: p.Stride, Stride: p.Stride,
Rect: p.Rect.Intersect(r), Rect: p.Rect.Intersect(r),
Palette: p.Palette, Palette: p.Palette,
@ -649,8 +754,7 @@ func (p *Paletted) SubImage(r Rectangle) Image {
// Opaque scans the entire image and returns whether or not it is fully opaque. // Opaque scans the entire image and returns whether or not it is fully opaque.
func (p *Paletted) Opaque() bool { func (p *Paletted) Opaque() bool {
var present [256]bool var present [256]bool
base := p.Rect.Min.Y * p.Stride i0, i1 := 0, p.Rect.Dx()
i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for _, c := range p.Pix[i0:i1] { for _, c := range p.Pix[i0:i1] {
present[c] = true present[c] = true

View File

@ -72,6 +72,11 @@ func TestImage(t *testing.T) {
t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3)) t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3))
continue continue
} }
// Test that taking an empty sub-image starting at a corner does not panic.
m.SubImage(Rect(0, 0, 0, 0))
m.SubImage(Rect(10, 0, 10, 0))
m.SubImage(Rect(0, 10, 0, 10))
m.SubImage(Rect(10, 10, 10, 10))
} }
} }

View File

@ -199,8 +199,8 @@ func (d *decoder) processDQT(n int) os.Error {
// makeImg allocates and initializes the destination image. // makeImg allocates and initializes the destination image.
func (d *decoder) makeImg(h0, v0, mxx, myy int) { func (d *decoder) makeImg(h0, v0, mxx, myy int) {
if d.nComp == nGrayComponent { if d.nComp == nGrayComponent {
d.img1 = image.NewGray(8*mxx, 8*myy) m := image.NewGray(8*mxx, 8*myy)
d.img1.Rect = image.Rect(0, 0, d.width, d.height) d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
return return
} }
var subsampleRatio ycbcr.SubsampleRatio var subsampleRatio ycbcr.SubsampleRatio

View File

@ -397,13 +397,13 @@ func rgbaToYCbCr(m *image.RGBA, p image.Point, yBlock, cbBlock, crBlock *block)
if sj > ymax { if sj > ymax {
sj = ymax sj = ymax
} }
yoff := sj * m.Stride offset := (sj-b.Min.Y)*m.Stride - b.Min.X
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
sx := p.X + i sx := p.X + i
if sx > xmax { if sx > xmax {
sx = xmax sx = xmax
} }
col := &m.Pix[yoff+sx] col := &m.Pix[offset+sx]
yy, cb, cr := ycbcr.RGBToYCbCr(col.R, col.G, col.B) yy, cb, cr := ycbcr.RGBToYCbCr(col.R, col.G, col.B)
yBlock[8*j+i] = int(yy) yBlock[8*j+i] = int(yy)
cbBlock[8*j+i] = int(cb) cbBlock[8*j+i] = int(cb)

View File

@ -324,8 +324,8 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
// We have previously verified that the alpha value is fully opaque. // We have previously verified that the alpha value is fully opaque.
cr0 := cr[0] cr0 := cr[0]
if rgba != nil { if rgba != nil {
yoff := y * rgba.Stride offset := (y - b.Min.Y) * rgba.Stride
for _, color := range rgba.Pix[yoff+b.Min.X : yoff+b.Max.X] { for _, color := range rgba.Pix[offset : offset+b.Dx()] {
cr0[i+0] = color.R cr0[i+0] = color.R
cr0[i+1] = color.G cr0[i+1] = color.G
cr0[i+2] = color.B cr0[i+2] = color.B
@ -341,8 +341,8 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
} }
} }
case cbP8: case cbP8:
rowOffset := y * paletted.Stride offset := (y - b.Min.Y) * paletted.Stride
copy(cr[0][1:], paletted.Pix[rowOffset+b.Min.X:rowOffset+b.Max.X]) copy(cr[0][1:], paletted.Pix[offset:offset+b.Dx()])
case cbTCA8: case cbTCA8:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for x := b.Min.X; x < b.Max.X; x++ { for x := b.Min.X; x < b.Max.X; x++ {

View File

@ -81,14 +81,14 @@ func TestWriter(t *testing.T) {
} }
} }
func TestSubimage(t *testing.T) { func TestSubImage(t *testing.T) {
m0 := image.NewRGBA(256, 256) m0 := image.NewRGBA(256, 256)
for y := 0; y < 256; y++ { for y := 0; y < 256; y++ {
for x := 0; x < 256; x++ { for x := 0; x < 256; x++ {
m0.Set(x, y, image.RGBAColor{uint8(x), uint8(y), 0, 255}) m0.Set(x, y, image.RGBAColor{uint8(x), uint8(y), 0, 255})
} }
} }
m0.Rect = image.Rect(50, 30, 250, 130) m0 = m0.SubImage(image.Rect(50, 30, 250, 130)).(*image.RGBA)
m1, err := encodeDecode(m0) m1, err := encodeDecode(m0)
if err != nil { if err != nil {
t.Error(err) t.Error(err)