Beispiel #1
0
// Encode writes the Image m to w in PNG format. Any Image may be encoded, but
// images that are not image.NRGBA might be encoded lossily.
func Encode(w io.Writer, m image.Image) os.Error {
	// Obviously, negative widths and heights are invalid. Furthermore, the PNG
	// spec section 11.2.2 says that zero is invalid. Excessively large images are
	// also rejected.
	mw, mh := int64(m.Width()), int64(m.Height())
	if mw <= 0 || mh <= 0 || mw >= 1<<32 || mh >= 1<<32 {
		return FormatError("invalid image size: " + strconv.Itoa64(mw) + "x" + strconv.Itoa64(mw))
	}

	var e encoder
	e.w = w
	e.m = m
	e.colorType = uint8(ctTrueColorAlpha)
	pal, _ := m.(*image.Paletted)
	if pal != nil {
		e.colorType = ctPaletted
	} else if opaque(m) {
		e.colorType = ctTrueColor
	}

	_, e.err = io.WriteString(w, pngHeader)
	e.writeIHDR()
	if pal != nil {
		e.writePLTE(pal.Palette)
	}
	e.writeIDATs()
	e.writeIEND()
	return e.err
}
Beispiel #2
0
func newSampler(img image.Image) *Sampler{
	s := new(Sampler);
	s.texture = img;
	s.tw = img.Width();
	s.th = img.Height();
	return s;
}
Beispiel #3
0
func makeGolden(dst image.Image, t drawTest) image.Image {
	// Since golden is a newly allocated image, we don't have to check if the
	// input source and mask images and the output golden image overlap.
	golden := image.NewRGBA(image.Rect(0, 0, dst.Width(), dst.Height()))
	for y := 0; y < golden.Height(); y++ {
		my, sy := y, y
		for x := 0; x < golden.Width(); x++ {
			mx, sx := x, x
			const M = 1<<16 - 1
			var dr, dg, db, da uint32
			if t.op == Over {
				dr, dg, db, da = dst.At(x, y).RGBA()
			}
			sr, sg, sb, sa := t.src.At(sx, sy).RGBA()
			ma := uint32(M)
			if t.mask != nil {
				_, _, _, ma = t.mask.At(mx, my).RGBA()
			}
			a := M - (sa * ma / M)
			golden.Set(x, y, color.RGBA64{
				uint16((dr*a + sr*ma) / M),
				uint16((dg*a + sg*ma) / M),
				uint16((db*a + sb*ma) / M),
				uint16((da*a + sa*ma) / M),
			})
		}
	}
	return golden
}
Beispiel #4
0
// Returns whether or not the image is fully opaque.
func opaque(m image.Image) bool {
	for y := 0; y < m.Height(); y++ {
		for x := 0; x < m.Width(); x++ {
			_, _, _, a := m.At(x, y).RGBA()
			if a != 0xffffffff {
				return false
			}
		}
	}
	return true
}
Beispiel #5
0
func diff(m0, m1 image.Image) os.Error {
	if m0.Width() != m1.Width() || m0.Height() != m1.Height() {
		return os.NewError(fmt.Sprintf("dimensions differ: %dx%d vs %dx%d", m0.Width(), m0.Height(), m1.Width(), m1.Height()))
	}
	for y := 0; y < m0.Height(); y++ {
		for x := 0; x < m0.Width(); x++ {
			r0, g0, b0, a0 := m0.At(x, y).RGBA()
			r1, g1, b1, a1 := m1.At(x, y).RGBA()
			if r0 != r1 || g0 != g1 || b0 != b1 || a0 != a1 {
				return os.NewError(fmt.Sprintf("colors differ at (%d, %d): %v vs %v", x, y, m0.At(x, y), m1.At(x, y)))
			}
		}
	}
	return nil
}
Beispiel #6
0
// Draw aligns r.Min in dst with pt in src and mask
// and then replaces the rectangle r in dst with the
// result of the Porter-Duff compositing operation
// ``(src in mask) over dst.''  If mask is nil, the operation
// simplifies to ``src over dst.''
// The implementation is simple and slow.
func Draw(dst Image, r Rectangle, src, mask image.Image, pt Point) {
	// Plenty of room for optimizations here.

	dx, dy := src.Width(), src.Height()
	if mask != nil {
		if dx > mask.Width() {
			dx = mask.Width()
		}
		if dy > mask.Width() {
			dy = mask.Width()
		}
	}
	dx -= pt.X
	dy -= pt.Y
	if r.Dx() > dx {
		r.Max.X = r.Min.X + dx
	}
	if r.Dy() > dy {
		r.Max.Y = r.Min.Y + dy
	}

	x0, x1, dx := r.Min.X, r.Max.X, 1
	y0, y1, dy := r.Min.Y, r.Max.Y, 1
	if image.Image(dst) == src && r.Overlaps(r.Add(pt.Sub(r.Min))) {
		// Rectangles overlap: process backward?
		if pt.Y < r.Min.Y || pt.Y == r.Min.Y && pt.X < r.Min.X {
			x0, x1, dx = x1-1, x0-1, -1
			y0, y1, dy = y1-1, y0-1, -1
		}
	}

	var out *image.RGBA64Color
	for y := y0; y != y1; y += dy {
		for x := x0; x != x1; x += dx {
			sx := pt.X + x - r.Min.X
			sy := pt.Y + y - r.Min.Y
			if mask == nil {
				dst.Set(x, y, src.At(sx, sy))
				continue
			}
			_, _, _, ma := mask.At(sx, sy).RGBA()
			switch ma {
			case 0:
				continue
			case 0xFFFFFFFF:
				dst.Set(x, y, src.At(sx, sy))
			default:
				dr, dg, db, da := dst.At(x, y).RGBA()
				dr >>= 16
				dg >>= 16
				db >>= 16
				da >>= 16
				sr, sg, sb, sa := src.At(sx, sy).RGBA()
				sr >>= 16
				sg >>= 16
				sb >>= 16
				sa >>= 16
				ma >>= 16
				const M = 1<<16 - 1
				a := sa * ma / M
				dr = (dr*(M-a) + sr*ma) / M
				dg = (dg*(M-a) + sg*ma) / M
				db = (db*(M-a) + sb*ma) / M
				da = (da*(M-a) + sa*ma) / M
				if out == nil {
					out = new(image.RGBA64Color)
				}
				out.R = uint16(dr)
				out.G = uint16(dg)
				out.B = uint16(db)
				out.A = uint16(da)
				dst.Set(x, y, out)
			}
		}
	}
}
Beispiel #7
0
func writeImage(w io.Writer, m image.Image, ct uint8) os.Error {
	zw, err := zlib.NewDeflater(w)
	if err != nil {
		return err
	}
	defer zw.Close()

	bpp := 0 // Bytes per pixel.
	var paletted *image.Paletted
	switch ct {
	case ctTrueColor:
		bpp = 3
	case ctPaletted:
		bpp = 1
		paletted = m.(*image.Paletted)
	case ctTrueColorAlpha:
		bpp = 4
	}
	// cr[*] and pr are the bytes for the current and previous row.
	// cr[0] is unfiltered (or equivalently, filtered with the ftNone filter).
	// cr[ft], for non-zero filter types ft, are buffers for transforming cr[0] under the
	// other PNG filter types. These buffers are allocated once and re-used for each row.
	// The +1 is for the per-row filter type, which is at cr[*][0].
	var cr [nFilter][]uint8
	for i := 0; i < len(cr); i++ {
		cr[i] = make([]uint8, 1+bpp*m.Width())
		cr[i][0] = uint8(i)
	}
	pr := make([]uint8, 1+bpp*m.Width())

	for y := 0; y < m.Height(); y++ {
		// Convert from colors to bytes.
		switch ct {
		case ctTrueColor:
			for x := 0; x < m.Width(); x++ {
				// We have previously verified that the alpha value is fully opaque.
				r, g, b, _ := m.At(x, y).RGBA()
				cr[0][3*x+1] = uint8(r >> 24)
				cr[0][3*x+2] = uint8(g >> 24)
				cr[0][3*x+3] = uint8(b >> 24)
			}
		case ctPaletted:
			for x := 0; x < m.Width(); x++ {
				cr[0][x+1] = paletted.ColorIndexAt(x, y)
			}
		case ctTrueColorAlpha:
			// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
			for x := 0; x < m.Width(); x++ {
				c := image.NRGBAColorModel.Convert(m.At(x, y)).(image.NRGBAColor)
				cr[0][4*x+1] = c.R
				cr[0][4*x+2] = c.G
				cr[0][4*x+3] = c.B
				cr[0][4*x+4] = c.A
			}
		}

		// Apply the filter.
		f := filter(cr[0:nFilter], pr, bpp)

		// Write the compressed bytes.
		_, err = zw.Write(cr[f])
		if err != nil {
			return err
		}

		// The current row for y is the previous row for y+1.
		pr, cr[0] = cr[0], pr
	}
	return nil
}
Beispiel #8
0
// An approximation of the sng command-line tool.
func sng(w io.WriteCloser, filename string, png image.Image) {
	defer w.Close()
	// For now, the go PNG parser only reads bitdepths of 8.
	bitdepth := 8

	// Write the filename and IHDR.
	io.WriteString(w, "#SNG: from "+filename+".png\nIHDR {\n")
	fmt.Fprintf(w, "    width: %d; height: %d; bitdepth: %d;\n", png.Width(), png.Height(), bitdepth)
	cm := png.ColorModel()
	var paletted *image.Paletted
	cpm, _ := cm.(image.PalettedColorModel)
	switch {
	case cm == image.RGBAColorModel:
		io.WriteString(w, "    using color;\n")
	case cm == image.NRGBAColorModel:
		io.WriteString(w, "    using color alpha;\n")
	case cpm != nil:
		io.WriteString(w, "    using color palette;\n")
		paletted = png.(*image.Paletted)
	default:
		io.WriteString(w, "unknown PNG decoder color model\n")
	}
	io.WriteString(w, "}\n")

	// We fake a gAMA output. The test files have a gAMA chunk but the go PNG parser ignores it
	// (the PNG spec section 11.3 says "Ancillary chunks may be ignored by a decoder").
	io.WriteString(w, "gAMA {1.0000}\n")

	// Write the PLTE (if applicable).
	if cpm != nil {
		io.WriteString(w, "PLTE {\n")
		for i := 0; i < len(cpm); i++ {
			r, g, b, _ := cpm[i].RGBA()
			r >>= 24
			g >>= 24
			b >>= 24
			fmt.Fprintf(w, "    (%3d,%3d,%3d)     # rgb = (0x%02x,0x%02x,0x%02x)\n", r, g, b, r, g, b)
		}
		io.WriteString(w, "}\n")
	}

	// Write the IMAGE.
	io.WriteString(w, "IMAGE {\n    pixels hex\n")
	for y := 0; y < png.Height(); y++ {
		switch {
		case cm == image.RGBAColorModel:
			for x := 0; x < png.Width(); x++ {
				rgba := png.At(x, y).(image.RGBAColor)
				fmt.Fprintf(w, "%02x%02x%02x ", rgba.R, rgba.G, rgba.B)
			}
		case cm == image.NRGBAColorModel:
			for x := 0; x < png.Width(); x++ {
				nrgba := png.At(x, y).(image.NRGBAColor)
				fmt.Fprintf(w, "%02x%02x%02x%02x ", nrgba.R, nrgba.G, nrgba.B, nrgba.A)
			}
		case cpm != nil:
			for x := 0; x < png.Width(); x++ {
				fmt.Fprintf(w, "%02x", paletted.ColorIndexAt(x, y))
			}
		}
		io.WriteString(w, "\n")
	}
	io.WriteString(w, "}\n")
}
Beispiel #9
0
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
// The implementation is simple and slow.
// TODO(nigeltao): Optimize this.
func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
	dx, dy := src.Width()-sp.X, src.Height()-sp.Y
	if mask != nil {
		if dx > mask.Width()-mp.X {
			dx = mask.Width() - mp.X
		}
		if dy > mask.Height()-mp.Y {
			dy = mask.Height() - mp.Y
		}
	}
	if r.Dx() > dx {
		r.Max.X = r.Min.X + dx
	}
	if r.Dy() > dy {
		r.Max.Y = r.Min.Y + dy
	}

	// TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
	// TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.

	// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
	if dst0, ok := dst.(*image.RGBA); ok {
		if op == Over {
			// TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha).
		} else {
			if mask == nil {
				if src0, ok := src.(image.ColorImage); ok {
					drawFill(dst0, r, src0)
					return
				}
				if src0, ok := src.(*image.RGBA); ok {
					if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) {
						// TODO(nigeltao): Implement a fast path for the overlapping case.
					} else {
						drawCopy(dst0, r, src0, sp)
						return
					}
				}
			}
		}
	}

	x0, x1, dx := r.Min.X, r.Max.X, 1
	y0, y1, dy := r.Min.Y, r.Max.Y, 1
	if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
		// Rectangles overlap: process backward?
		if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
			x0, x1, dx = x1-1, x0-1, -1
			y0, y1, dy = y1-1, y0-1, -1
		}
	}

	var out *image.RGBA64Color
	sy := sp.Y + y0 - r.Min.Y
	my := mp.Y + y0 - r.Min.Y
	for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
		sx := sp.X + x0 - r.Min.X
		mx := mp.X + x0 - r.Min.X
		for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
			// A nil mask is equivalent to a fully opaque, infinitely large mask.
			// We work in 16-bit color, so that multiplying two values does not overflow a uint32.
			const M = 1<<16 - 1
			ma := uint32(M)
			if mask != nil {
				_, _, _, ma = mask.At(mx, my).RGBA()
				ma >>= 16
			}
			switch {
			case ma == 0:
				if op == Over {
					// No-op.
				} else {
					dst.Set(x, y, zeroColor)
				}
			case ma == M && op == Src:
				dst.Set(x, y, src.At(sx, sy))
			default:
				sr, sg, sb, sa := src.At(sx, sy).RGBA()
				sr >>= 16
				sg >>= 16
				sb >>= 16
				sa >>= 16
				if out == nil {
					out = new(image.RGBA64Color)
				}
				if op == Over {
					dr, dg, db, da := dst.At(x, y).RGBA()
					dr >>= 16
					dg >>= 16
					db >>= 16
					da >>= 16
					a := M - (sa * ma / M)
					out.R = uint16((dr*a + sr*ma) / M)
					out.G = uint16((dg*a + sg*ma) / M)
					out.B = uint16((db*a + sb*ma) / M)
					out.A = uint16((da*a + sa*ma) / M)
				} else {
					out.R = uint16(sr * ma / M)
					out.G = uint16(sg * ma / M)
					out.B = uint16(sb * ma / M)
					out.A = uint16(sa * ma / M)
				}
				dst.Set(x, y, out)
			}
		}
	}
}
Beispiel #10
0
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r
// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque.
// The implementation is simple and slow.
// TODO(nigeltao): Optimize this.
func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) {
	dx, dy := src.Width()-sp.X, src.Height()-sp.Y
	if mask != nil {
		if dx > mask.Width()-mp.X {
			dx = mask.Width() - mp.X
		}
		if dy > mask.Height()-mp.Y {
			dy = mask.Height() - mp.Y
		}
	}
	if r.Dx() > dx {
		r.Max.X = r.Min.X + dx
	}
	if r.Dy() > dy {
		r.Max.Y = r.Min.Y + dy
	}

	// TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y.
	// TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y.

	// Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation.
	switch dst0 := dst.(type) {
	case *image.RGBA:
		if op == Over {
			if mask == nil {
				if src0, ok := src.(image.Uniform); ok {
					drawFillOver(dst0, r, src0)
					return
				}
				if src0, ok := src.(*image.RGBA); ok {
					if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) {
						// TODO(nigeltao): Implement a fast path for the overlapping case.
					} else {
						drawCopyOver(dst0, r, src0, sp)
						return
					}
				}
			} else if mask0, ok := mask.(*image.Alpha); ok {
				if src0, ok := src.(image.Uniform); ok {
					drawGlyphOver(dst0, r, src0, mask0, mp)
					return
				}
			}
		} else {
			if mask == nil {
				if src0, ok := src.(image.Uniform); ok {
					drawFillSrc(dst0, r, src0)
					return
				}
				if src0, ok := src.(*image.RGBA); ok {
					if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) {
						// TODO(nigeltao): Implement a fast path for the overlapping case.
					} else {
						drawCopySrc(dst0, r, src0, sp)
						return
					}
				}
			}
		}
		drawRGBA(dst0, r, src, sp, mask, mp, op)
		return
	case DrawMasker:
		// Destination might wish to perform the draw operation itself
		if dst0.DrawMask(r, src, sp, mask, mp, op) {
			return
		}
	}
	x0, x1, dx := r.Min.X, r.Max.X, 1
	y0, y1, dy := r.Min.Y, r.Max.Y, 1
	if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) {
		// Rectangles overlap: process backward?
		if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X {
			x0, x1, dx = x1-1, x0-1, -1
			y0, y1, dy = y1-1, y0-1, -1
		}
	}

	var out *color.RGBA64
	sy := sp.Y + y0 - r.Min.Y
	my := mp.Y + y0 - r.Min.Y
	for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
		sx := sp.X + x0 - r.Min.X
		mx := mp.X + x0 - r.Min.X
		for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx {
			ma := uint32(m)
			if mask != nil {
				_, _, _, ma = mask.At(mx, my).RGBA()
			}
			switch {
			case ma == 0:
				if op == Over {
					// No-op.
				} else {
					dst.Set(x, y, zeroColor)
				}
			case ma == m && op == Src:
				dst.Set(x, y, src.At(sx, sy))
			default:
				sr, sg, sb, sa := src.At(sx, sy).RGBA()
				if out == nil {
					out = new(color.RGBA64)
				}
				if op == Over {
					dr, dg, db, da := dst.At(x, y).RGBA()
					a := m - (sa * ma / m)
					out.R = uint16((dr*a + sr*ma) / m)
					out.G = uint16((dg*a + sg*ma) / m)
					out.B = uint16((db*a + sb*ma) / m)
					out.A = uint16((da*a + sa*ma) / m)
				} else {
					out.R = uint16(sr * ma / m)
					out.G = uint16(sg * ma / m)
					out.B = uint16(sb * ma / m)
					out.A = uint16(sa * ma / m)
				}
				dst.Set(x, y, out)
			}
		}
	}
}