Пример #1
0
func (ri *RevisitImage) RevisitMsg() (*RevisitMsg, error) {
	buf := bytes.NewBuffer(nil)

	switch ri.ImgType {
	case "image/jpeg":
		err := jpeg.Encode(buf, image.Image(image.Image(&ri.Rgbas[0])), nil)
		if err != nil {
			return nil, err
		}

	case "image/png":
		err := png.Encode(buf, image.Image(&ri.Rgbas[0]))
		if err != nil {
			return nil, err
		}

	case "image/gif":
		g := &gif.GIF{
			Image:     make([]*image.Paletted, 0),
			LoopCount: ri.LoopCount,
			Delay:     make([]int, 0),
		}

		for index, src := range ri.Rgbas {
			b := src.Bounds()
			pal := image.NewPaletted(image.Rect(0, 0, b.Dx(), b.Dy()), ri.Palette[index])
			draw.Draw(pal, pal.Bounds(), image.Image(&src), b.Min, draw.Src)

			g.Image = append(g.Image, pal)
			g.Delay = append(g.Delay, ri.Delay[index])
		}

		buf := bytes.NewBuffer(nil)
		err := gif.EncodeAll(buf, g)
		if err != nil {
			return nil, err
		}

		dstImgBase64 := base64.StdEncoding.EncodeToString(buf.Bytes())
		return &RevisitMsg{
			Content: ImageData{
				Data: fmt.Sprintf("data:%s;base64,%s", ri.ImgType, dstImgBase64),
			},
		}, nil

	default:
		return nil, errors.New("invalid image type")
	}

	dstImgBase64 := base64.StdEncoding.EncodeToString(buf.Bytes())
	return &RevisitMsg{
		Content: ImageData{
			Data: fmt.Sprintf("data:%s;base64,%s", ri.ImgType, dstImgBase64),
		},
	}, nil
}
Пример #2
0
func ImageTransformByProfile(src_image image.Image, src_prof, dst_prof *Profile) (image.Image, error) {
	var dst_image image.Image
	rect := src_image.Bounds()
	width := rect.Dx()
	height := rect.Dy()
	colorModel := src_image.ColorModel()
	// 今のところ RGBA, YCbCr のみ対応
	if (colorModel != color.YCbCrModel) && (colorModel != color.RGBAModel) {
		return nil, fmt.Errorf("ImageTransformByProfile: Unsupported ColorModel(%d)", colorModel)
	}
	var src_rgba *image.RGBA
	var src_ycbcr *image.YCbCr
	if colorModel == color.YCbCrModel {
		// YCbCr の場合は RGB に変換する
		src_ycbcr = src_image.(*image.YCbCr)
		src_rgba = image.NewRGBA(rect)
		DrawYCbCr(src_rgba, rect, src_ycbcr, image.Pt(0, 0))
	} else {
		src_rgba = src_image.(*image.RGBA) // type assertions
	}
	transform := CreateTransform(src_prof, DATA_RGBA_8, dst_prof, DATA_RGBA_8)
	defer transform.DeleteTransform()
	if transform == nil {
		return nil, fmt.Errorf("ImageTransformByProfile: CreateTransform Failedl(%d)", colorModel)
	}
	dst_rgba := image.NewRGBA(rect)
	src_pix := src_rgba.Pix
	dst_pix := dst_rgba.Pix
	len_pix := len(src_pix)
	transform.DoTransform(src_pix, dst_pix, len_pix)
	// YCbCr の場合は RGB から戻す
	if colorModel == color.YCbCrModel {
		dst_ycbcr := image.NewYCbCr(rect, src_ycbcr.SubsampleRatio)
		var x int
		var y int
		for y = 0; y < height; y++ {
			for x = 0; x < width; x++ {
				r, g, b, _ := dst_rgba.At(x, y).RGBA()
				yy, cb, cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b))
				yi := dst_ycbcr.YOffset(x, y)
				ci := dst_ycbcr.COffset(x, y)
				dst_ycbcr.Y[yi] = yy
				dst_ycbcr.Cb[ci] = cb
				dst_ycbcr.Cr[ci] = cr
			}
		}
		dst_image = image.Image(dst_ycbcr)
	} else {
		dst_image = image.Image(dst_rgba)
	}

	return dst_image, nil
}
Пример #3
0
// Copy copies the part of the source image defined by src and sr and writes to
// the part of the destination image defined by dst and the translation of sr
// so that sr.Min translates to dp.
func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) {
	mask, mp, op := image.Image(nil), image.Point{}, Over
	if opts != nil {
		// TODO: set mask, mp and op.
	}
	dr := sr.Add(dp.Sub(sr.Min))
	DrawMask(dst, dr, src, sr.Min, mask, mp, op)
}
Пример #4
0
func testInterlacedFailWith(t *testing.T, rgb bool) {
	src := readImage(t, "testdata/lenna.jpg")
	dst := image.Image(image.NewYCbCr(image.Rect(0, 0, 640, 480), image.YCbCrSubsampleRatio420))
	if rgb {
		src = toRgb(src)
		dst = toRgb(dst)
	}
	convert(t, dst, src, false, true, NewBicubicFilter())
	convert(t, dst, src, true, true, NewBicubicFilter())
}
Пример #5
0
func analyse(settings CropSettings, img image.Image, cropWidth, cropHeight, realMinScale float64) (Crop, error) {
	o := image.Image(image.NewRGBA(img.Bounds()))

	now := time.Now()
	edgeDetect(img, o)
	log.Println("Time elapsed edge:", time.Since(now))
	debugOutput(settings.DebugMode, &o, "edge")

	now = time.Now()
	if settings.FaceDetection {
		err := faceDetect(settings, img, o)

		if err != nil {
			return Crop{}, err
		}

		log.Println("Time elapsed face:", time.Since(now))
		debugOutput(settings.DebugMode, &o, "face")
	} else {
		skinDetect(img, o)
		log.Println("Time elapsed skin:", time.Since(now))
		debugOutput(settings.DebugMode, &o, "skin")
	}

	now = time.Now()
	saturationDetect(img, o)
	log.Println("Time elapsed sat:", time.Since(now))
	debugOutput(settings.DebugMode, &o, "saturation")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(o, cropWidth, cropHeight, realMinScale)
	log.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		log.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	log.Println("Time elapsed score:", time.Since(now))

	if settings.DebugMode {
		drawDebugCrop(&topCrop, &o)
		debugOutput(true, &o, "final")
	}

	return topCrop, nil
}
Пример #6
0
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
	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))) {
		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
		}
	}

	sy := sp.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
	sx1 := sx0 + (x1 - x0)
	i0 := dst.PixOffset(x0, y0)
	di := dx * 4
	for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy {
		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()
			if op == Over {
				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
				// and similarly for dg, db and da, but instead we multiply a
				// (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

				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 {
				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)
			}
		}
		i0 += dy * dst.Stride
	}
}
Пример #7
0
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
	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))) {
		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
		}
	}

	sy := sp.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 {
		dpix := dst.Pix[i0:]
		for x, sx, mx := x0, sx0, mx0; x != x1; x, sx, mx = x+dx, 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, 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
				// and similarly for dg, db and da, but instead we multiply a
				// (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
			} else {
				dr = sr * ma / m
				dg = sg * ma / m
				db = sb * ma / m
				da = sa * ma / m
			}
			dpix[x-dst.Rect.Min.X] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
		}
		i0 += dy * dst.Stride
	}
}
Пример #8
0
func BenchmarkEdge(b *testing.B) {
	fname := "24391757.jpg"
	fi, _ := os.Open("./samples/" + fname)
	defer fi.Close()
	img, _, err := image.Decode(fi)
	if err != nil {
		b.Error(err)
	}
	o := image.Image(image.NewRGBA(img.Bounds()))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		edgeDetect(img, o)
	}
}
Пример #9
0
func analyse(img *image.Image, cropWidth, cropHeight, realMinScale float64) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	debugOutput(&o, "edge")

	now = time.Now()
	if useFaceDetection {
		faceDetect(img, &o)
		fmt.Println("Time elapsed face:", time.Since(now))
		debugOutput(&o, "face")
	} else {
		skinDetect(img, &o)
		fmt.Println("Time elapsed skin:", time.Since(now))
		debugOutput(&o, "skin")
	}

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	debugOutput(&o, "saturation")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o, cropWidth, cropHeight, realMinScale)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	if debug {
		drawDebugCrop(&topCrop, &o)
	}
	debugOutput(&o, "final")

	return topCrop
}
Пример #10
0
func analyse(img *image.Image, cropWidth, cropHeight, realMinScale float64) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	if debug {
		writeImageToPng(&o, "./smartcrop_edge.png")
	}

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	if debug {
		writeImageToPng(&o, "./smartcrop_skin.png")
	}

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	if debug {
		writeImageToPng(&o, "./smartcrop_sat.png")
	}

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o, cropWidth, cropHeight, realMinScale)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	if debug {
		drawDebugCrop(&topCrop, &o)
		writeImageToPng(&o, "./smartcrop_debug.png")
	}

	return topCrop
}
Пример #11
0
// imgを32x32のブロックの画像に変換する
func Cube(img *image.Image) image.Image {
	layer := image.Image(image.NewNRGBA(image.Rect(0, 0, 32, 32)))

	top := Rotate(img, 45)
	top = Resize(&top, 28, 15)
	left := Slide(img, 2, true, false)
	left = Resize(&left, 14, 24)
	right := Slide(img, 2, true, true)
	right = Resize(&right, 14, 24)

	Paste(&layer, 2, 0, &top)
	Paste(&layer, 2, 8, &left)
	Paste(&layer, 16, 6, &right)

	return layer
}
Пример #12
0
func resizeEmojiGif(gifImg *gif.GIF) *gif.GIF {
	// Create a new RGBA image to hold the incremental frames.
	firstFrame := gifImg.Image[0].Bounds()
	b := image.Rect(0, 0, firstFrame.Dx(), firstFrame.Dy())
	img := image.NewRGBA(b)

	resizedImage := image.Image(nil)
	// Resize each frame.
	for index, frame := range gifImg.Image {
		bounds := frame.Bounds()
		draw.Draw(img, bounds, frame, bounds.Min, draw.Over)
		resizedImage = resizeEmoji(img, firstFrame.Dx(), firstFrame.Dy())
		gifImg.Image[index] = imageToPaletted(resizedImage)
	}
	// Set new gif width and height
	gifImg.Config.Width = resizedImage.Bounds().Dx()
	gifImg.Config.Height = resizedImage.Bounds().Dy()
	return gifImg
}
Пример #13
0
// benchGlyph benchmarks rasterizing a TrueType glyph.
//
// Note that, compared to the github.com/google/font-go prototype, the height
// here is the height of the bounding box, not the pixels per em used to scale
// a glyph's vectors. A height of 64 corresponds to a ppem greater than 64.
func benchGlyph(b *testing.B, colorModel byte, loose bool, height int, op draw.Op) {
	width, data := scaledBenchmarkGlyphData(height)
	z := NewRasterizer(width, height)

	bounds := z.Bounds()
	if loose {
		bounds.Max.X++
	}
	dst, src := draw.Image(nil), image.Image(nil)
	switch colorModel {
	case 'A':
		dst = image.NewAlpha(bounds)
		src = image.Opaque
	case 'N':
		dst = image.NewNRGBA(bounds)
		src = image.NewUniform(color.NRGBA{0x40, 0x80, 0xc0, 0xff})
	case 'R':
		dst = image.NewRGBA(bounds)
		src = image.NewUniform(color.RGBA{0x40, 0x80, 0xc0, 0xff})
	default:
		b.Fatal("unsupported color model")
	}
	bounds = z.Bounds()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		z.Reset(width, height)
		z.DrawOp = op
		for _, d := range data {
			switch d.n {
			case 0:
				z.MoveTo(d.px, d.py)
			case 1:
				z.LineTo(d.px, d.py)
			case 2:
				z.QuadTo(d.px, d.py, d.qx, d.qy)
			}
		}
		z.Draw(dst, bounds, src, image.Point{})
	}
}
Пример #14
0
func analyse(img *image.Image) Crop {
	o := image.Image(image.NewRGBA((*img).Bounds()))

	now := time.Now()
	edgeDetect(img, &o)
	fmt.Println("Time elapsed edge:", time.Since(now))
	//	writeImageToJpeg(&o, "/tmp/smartcrop_step1.jpg")

	now = time.Now()
	skinDetect(img, &o)
	fmt.Println("Time elapsed skin:", time.Since(now))
	//	writeImageToJpeg(&o, "/tmp/smartcrop_step2.jpg")

	now = time.Now()
	saturationDetect(img, &o)
	fmt.Println("Time elapsed sat:", time.Since(now))
	//	writeImageToJpeg(&o, "/tmp/smartcrop_step3.jpg")

	now = time.Now()
	var topCrop Crop
	topScore := -1.0
	cs := crops(&o)
	fmt.Println("Time elapsed crops:", time.Since(now), len(cs))

	now = time.Now()
	for _, crop := range cs {
		//		nowIn := time.Now()
		crop.Score = score(&o, &crop)
		//		fmt.Println("Time elapsed single-score:", time.Since(nowIn))
		if crop.Score.Total > topScore {
			topCrop = crop
			topScore = crop.Score.Total
		}
	}
	fmt.Println("Time elapsed score:", time.Since(now))

	return topCrop
}
Пример #15
0
func windowOnClickHandler(me gxui.MouseEvent) {
	y := yMax - yMin // mandelbrot axis size
	x := xMax - xMin

	xp := float64(me.WindowPoint.X) // point clicked on screen in pixels
	yp := float64(me.WindowPoint.Y)

	// find point clicked in mandelbrot space
	xm := xMin + (xp/1024)*x
	ym := yMin + (yp/1024)*y

	// scale viewport of mandelbrot space
	if me.Button == gxui.MouseButtonLeft {
		x = x / 2
		y = y / 2
	} else {
		x = x * 2
		y = y * 2

	}

	yMax = ym + y/2
	yMin = ym - y/2
	xMax = xm + x/2
	xMin = xm - x/2

	//fmt.Print(xm, ym)

	//fmt.Print(yMax, yMin, xMax, xMin)

	source := image.Image(newMandelbrot())
	rgba := image.NewRGBA(source.Bounds())
	draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src)
	texture = d.CreateTexture(rgba, 1)
	img.SetTexture(texture)

	window.Redraw()
}
Пример #16
0
func appMain(driver gxui.Driver) {

	d = driver
	source := image.Image(newMandelbrot())

	theme := flags.CreateTheme(driver)

	mx := source.Bounds().Max

	img = theme.CreateImage()

	window = theme.CreateWindow(mx.X, mx.Y, "Image viewer")
	window.SetScale(flags.DefaultScaleFactor)
	window.AddChild(img)

	rgba := image.NewRGBA(source.Bounds())
	draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src)
	texture = driver.CreateTexture(rgba, 1)
	img.SetTexture(texture)

	window.OnClick(windowOnClickHandler)
	window.OnClose(driver.Terminate)
}
Пример #17
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)
			}
		}
	}
}
Пример #18
0
func do(c appengine.Context, key string) (*bytes.Buffer, error) {

	client := urlfetch.Client(c)
	resp, err := client.Get(key)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	c.Infof("HTTP GET returned status %v", resp.Status)

	img, _, err := image.Decode(resp.Body)
	if err != nil {
		return nil, err
	}

	bnds := img.Bounds()
	if bnds.Dx() > 1024 {
		c.Infof("Resizing image", bnds.Dx())
		img = resize.Resize(1024, 0, img, resize.Lanczos3)
	}

	faces, err := findFaces(c, &img)
	// todo: should I pass back by reference?
	if err != nil {
		return nil, err
	}

	bnds = img.Bounds()

	m := image.NewRGBA(image.Rect(0, 0, bnds.Dx(), bnds.Dy()))

	draw.Draw(m, bnds, img, image.Point{0, 0}, draw.Src)

	brd, err := getBeardCached(c)
	if err != nil {
		return nil, err
	}

	for _, face := range faces {
		brd_resized := resize.Resize(uint(face.Rectangle.Width*2), 0, brd, resize.Lanczos3)
		brd_bnds := brd_resized.Bounds()

		vert := (face.Landmarks.MouthLeft.Y+face.Landmarks.MouthRight.Y)/2 - float32(brd_bnds.Dy())*0.5

		rb := image.NewRGBA(image.Rect(0, 0, brd_bnds.Dx(), brd_bnds.Dy()))

		rad := float64(face.Attributes.Pose.Roll) * math.Pi / 180
		graphics.Rotate(rb, brd_resized, &graphics.RotateOptions{rad})

		mid := face.Rectangle.Left + face.Rectangle.Width/2
		lt := mid - (float32(brd_bnds.Dx()) / 2)
		sr := image.Rect(0, 0, brd_bnds.Dx()*4, brd_bnds.Dy()*4)
		dp := image.Point{int(float64(lt)), int(float64(vert))}
		rt := image.Rectangle{dp, dp.Add(sr.Size())}

		draw.Draw(m, rt, rb, sr.Min, draw.Over)
	}

	img_out := image.Image(m)

	buffer := new(bytes.Buffer)
	if err := jpeg.Encode(buffer, img_out, nil); err != nil {
		return nil, err
	}

	return buffer, nil
}
Пример #19
0
func TestRectDstMask(t *testing.T) {
	f, err := os.Open("../testdata/testpattern.png")
	if err != nil {
		t.Fatalf("Open: %v", err)
	}
	defer f.Close()
	src, _, err := image.Decode(f)
	if err != nil {
		t.Fatalf("Decode: %v", err)
	}
	m00 := transformMatrix(1, 0, 0)

	bounds := image.Rect(0, 0, 50, 50)
	dstOutside := image.NewRGBA(bounds)
	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			dstOutside.SetRGBA(x, y, color.RGBA{uint8(5 * x), uint8(5 * y), 0x00, 0xff})
		}
	}

	mk := func(q Transformer, dstMask image.Image, dstMaskP image.Point) *image.RGBA {
		m := image.NewRGBA(bounds)
		Copy(m, bounds.Min, dstOutside, bounds, Src, nil)
		q.Transform(m, m00, src, src.Bounds(), Over, &Options{
			DstMask:  dstMask,
			DstMaskP: dstMaskP,
		})
		return m
	}

	qs := []Interpolator{
		NearestNeighbor,
		ApproxBiLinear,
		CatmullRom,
	}
	dstMaskPs := []image.Point{
		{0, 0},
		{5, 7},
		{-3, 0},
	}
	rect := image.Rect(10, 10, 30, 40)
	for _, q := range qs {
		for _, dstMaskP := range dstMaskPs {
			dstInside := mk(q, nil, image.Point{})
			for _, wrap := range []bool{false, true} {
				// TODO: replace "rectImage(rect)" with "rect" once Go 1.5 is
				// released, where an image.Rectangle implements image.Image.
				dstMask := image.Image(rectImage(rect))
				if wrap {
					dstMask = srcWrapper{dstMask}
				}
				dst := mk(q, dstMask, dstMaskP)

				nError := 0
			loop:
				for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
					for x := bounds.Min.X; x < bounds.Max.X; x++ {
						which := dstOutside
						if (image.Point{x, y}).Add(dstMaskP).In(rect) {
							which = dstInside
						}
						if got, want := dst.RGBAAt(x, y), which.RGBAAt(x, y); got != want {
							if nError == 10 {
								t.Errorf("q=%T dmp=%v wrap=%v: ...and more errors", q, dstMaskP, wrap)
								break loop
							}
							nError++
							t.Errorf("q=%T dmp=%v wrap=%v: x=%3d y=%3d: got %v, want %v",
								q, dstMaskP, wrap, x, y, got, want)
						}
					}
				}
			}
		}
	}
}
Пример #20
0
// Resize an image to be w wide and h high
func resize(original image.Image, w, h int) (image.Image, error) {
	src, ok := original.(*image.RGBA)
	if ok == false {
		b := original.Bounds()
		src = image.NewRGBA(b)
		draw.Draw(src, b, original, b.Min, draw.Src)
	}

	width := src.Bounds().Dx()
	height := src.Bounds().Dy()

	if width < 1 || height < 1 {
		return image.Image(src), fmt.Errorf("Image dimensions invalid -- %v, %v", width, height)
	}

	if h == 0 {
		// Maintain aspect ratio
		h = int(float32(w) / (float32(width) / float32(height)))
	}
	if w < 1 || h < 1 {
		return image.Image(src), fmt.Errorf("Resize values invalid -- %v, %v", w, h)
	}

	dst := image.NewRGBA(image.Rect(0, 0, w, h))

	xRatio := float32(width) / float32(w)
	yRatio := float32(height) / float32(h)

	if width > w {
		// Blend pixels from larger source in smaller destination image
		b := src.Bounds()
		i := src.PixOffset(0, 0)
		checklist := make([]bool, len(src.Pix)>>2)

		for y := b.Min.Y; y < b.Max.Y; y++ {
			oy := int(float32(y) / yRatio)
			for x := b.Min.X; x < b.Max.X; x++ {
				ox := int(float32(x) / xRatio)
				o := dst.PixOffset(ox, oy)

				if !checklist[o>>2] {
					// Untouched pixel, do initial paint
					checklist[o>>2] = true
					dst.Pix[o+0] = src.Pix[i+0]
					dst.Pix[o+1] = src.Pix[i+1]
					dst.Pix[o+2] = src.Pix[i+2]
					dst.Pix[o+3] = src.Pix[i+3]
				} else {
					// Pixel already seen, paint with average blend
					dst.Pix[o+0] = uint8((uint64(dst.Pix[o+0]) + uint64(src.Pix[i+0])) >> 1)
					dst.Pix[o+1] = uint8((uint64(dst.Pix[o+1]) + uint64(src.Pix[i+1])) >> 1)
					dst.Pix[o+2] = uint8((uint64(dst.Pix[o+2]) + uint64(src.Pix[i+2])) >> 1)
					dst.Pix[o+3] = uint8((uint64(dst.Pix[o+3]) + uint64(src.Pix[i+3])) >> 1)
				}

				i += 4
			}
		}
	} else {
		// Destination image larger than source, no blend required
		b := dst.Bounds()
		i := dst.PixOffset(0, 0)

		for y := b.Min.Y; y < b.Max.Y; y++ {
			oy := int(float32(y) * yRatio)
			for x := b.Min.X; x < b.Max.X; x++ {
				ox := int(float32(x) * xRatio)
				o := src.PixOffset(ox, oy)

				dst.Pix[i+0] = src.Pix[o+0]
				dst.Pix[i+1] = src.Pix[o+1]
				dst.Pix[i+2] = src.Pix[o+2]
				dst.Pix[i+3] = src.Pix[o+3]

				i += 4
			}
		}
	}

	return dst, nil
}
Пример #21
0
func (d *decoder) decode(r io.Reader, full bool) error {
	if rr, ok := r.(reader); ok {
		d.r = rr
	} else {
		d.r = bufio.NewReader(r)
	}

	// Check for DDS magic number
	_, err := io.ReadFull(d.r, d.tmp[:4])
	if err != nil {
		return err
	}
	ident := string(d.tmp[0:4])
	if ident != "DDS " {
		return fmt.Errorf("dds: wrong magic number")
	}

	// Decode the DDS header
	err = d.decodeHeader()
	if err != nil {
		return err
	}

	// Check if it's a supported format
	// For now, we'll only support DXT1,DXT3,DXT5
	neededFlags := uint32(DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT)
	if d.h.Flags&neededFlags != neededFlags {
		return fmt.Errorf("dds: file header is missing necessary dds flags")
	}

	// Sanitize mipmap count
	if d.h.Flags&DDSD_MIPMAPCOUNT == 0 {
		d.h.MipMapCount = 1
	}

	if !full {
		return nil
	}

	switch {
	case d.h.Ddspf.Flags&DDPF_FOURCC != 0:
		switch d.h.Ddspf.FourCC {
		case FOURCC_DXT1:
			d.img = make([]image.Image, d.h.MipMapCount)
			w, h := int(d.h.Width), int(d.h.Height)
			for i := 0; i < int(d.h.MipMapCount); i++ {
				//fmt.Printf("mipmap %v is %vx%v\n", i, w, h)
				img := glimage.NewDxt1(image.Rect(0, 0, w, h))
				_, err = io.ReadFull(d.r, img.Pix)
				if err != nil {
					return err
				}
				d.img[i] = image.Image(img)
				w >>= 1
				h >>= 1
			}
		case FOURCC_DXT3:
			d.img = make([]image.Image, d.h.MipMapCount)
			w, h := int(d.h.Width), int(d.h.Height)
			for i := 0; i < int(d.h.MipMapCount); i++ {
				//fmt.Printf("mipmap %v is %vx%v\n", i, w, h)
				img := glimage.NewDxt3(image.Rect(0, 0, w, h))
				_, err = io.ReadFull(d.r, img.Pix)
				if err != nil {
					return err
				}
				d.img[i] = image.Image(img)
				w >>= 1
				h >>= 1
			}
		case FOURCC_DXT5:
			d.img = make([]image.Image, d.h.MipMapCount)
			w, h := int(d.h.Width), int(d.h.Height)
			for i := 0; i < int(d.h.MipMapCount); i++ {
				//fmt.Printf("mipmap %v is %vx%v\n", i, w, h)
				img := glimage.NewDxt5(image.Rect(0, 0, w, h))
				_, err = io.ReadFull(d.r, img.Pix)
				if err != nil {
					return err
				}
				d.img[i] = image.Image(img)
				w >>= 1
				h >>= 1
			}
		default:
			return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf)
		}
	case d.h.Ddspf.Flags&DDPF_RGB != 0:
		// Color formats
		if d.h.Ddspf.Flags&DDPF_ALPHAPIXELS != 0 {
			// Color formats with alpha
			switch {
			// A8R8G8B8
			case d.h.Ddspf.RBitMask == 0x00FF0000 && d.h.Ddspf.GBitMask == 0x0000FF00 &&
				d.h.Ddspf.BBitMask == 0x000000FF && d.h.Ddspf.ABitMask == 0xFF000000:
				d.img = make([]image.Image, d.h.MipMapCount)
				w, h := int(d.h.Width), int(d.h.Height)
				for i := 0; i < int(d.h.MipMapCount); i++ {
					img := glimage.NewBGRA(image.Rect(0, 0, w, h))
					_, err = io.ReadFull(d.r, img.Pix)
					if err != nil {
						return err
					}
					d.img[i] = image.Image(img)
					w >>= 1
					h >>= 1
				}
			// A4R4G4B4
			case d.h.Ddspf.RBitMask == 0x0F00 && d.h.Ddspf.GBitMask == 0x00F0 &&
				d.h.Ddspf.BBitMask == 0x000F && d.h.Ddspf.ABitMask == 0xF000:
				d.img = make([]image.Image, d.h.MipMapCount)
				w, h := int(d.h.Width), int(d.h.Height)
				for i := 0; i < int(d.h.MipMapCount); i++ {
					img := glimage.NewBGRA4444(image.Rect(0, 0, w, h))
					err = binary.Read(d.r, binary.LittleEndian, &img.Pix)
					if err != nil {
						return err
					}
					d.img[i] = image.Image(img)
					w >>= 1
					h >>= 1
				}
			// A1R5G5B5
			case d.h.Ddspf.RBitMask == 0x7C00 && d.h.Ddspf.GBitMask == 0x03E0 &&
				d.h.Ddspf.BBitMask == 0x001F && d.h.Ddspf.ABitMask == 0x8000:
				d.img = make([]image.Image, d.h.MipMapCount)
				w, h := int(d.h.Width), int(d.h.Height)
				for i := 0; i < int(d.h.MipMapCount); i++ {
					img := glimage.NewBGRA5551(image.Rect(0, 0, w, h))
					err = binary.Read(d.r, binary.LittleEndian, &img.Pix)
					if err != nil {
						return err
					}
					d.img[i] = image.Image(img)
					w >>= 1
					h >>= 1
				}
			default:
				return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf)
			}
		} else {
			// Color formats without alpha
			switch {
			// R5G6B5
			case d.h.Ddspf.RBitMask == 0xF800 && d.h.Ddspf.GBitMask == 0x07E0 &&
				d.h.Ddspf.BBitMask == 0x001F && d.h.Ddspf.ABitMask == 0x0000:
				d.img = make([]image.Image, d.h.MipMapCount)
				w, h := int(d.h.Width), int(d.h.Height)
				for i := 0; i < int(d.h.MipMapCount); i++ {
					img := glimage.NewBGR565(image.Rect(0, 0, w, h))
					err = binary.Read(d.r, binary.LittleEndian, &img.Pix)
					if err != nil {
						return err
					}
					d.img[i] = image.Image(img)
					w >>= 1
					h >>= 1
				}
			default:
				return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf)
			}
		}
	default:
		return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf)
	}

	return nil
}
Пример #22
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)
			}
		}
	}
}
Пример #23
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.
func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
	clip(dst, &r, src, &sp, mask, &mp)
	if r.Empty() {
		return
	}

	// 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 {
			if mask == nil {
				switch src0 := src.(type) {
				case *image.ColorImage:
					drawFillOver(dst0, r, src0)
					return
				case *image.RGBA:
					drawCopyOver(dst0, r, src0, sp)
					return
				case *image.NRGBA:
					drawNRGBAOver(dst0, r, src0, sp)
					return
				case *ycbcr.YCbCr:
					drawYCbCr(dst0, r, src0, sp)
					return
				}
			} else if mask0, ok := mask.(*image.Alpha); ok {
				switch src0 := src.(type) {
				case *image.ColorImage:
					drawGlyphOver(dst0, r, src0, mask0, mp)
					return
				}
			}
		} else {
			if mask == nil {
				switch src0 := src.(type) {
				case *image.ColorImage:
					drawFillSrc(dst0, r, src0)
					return
				case *image.RGBA:
					drawCopySrc(dst0, r, src0, sp)
					return
				case *image.NRGBA:
					drawNRGBASrc(dst0, r, src0, sp)
					return
				case *ycbcr.YCbCr:
					drawYCbCr(dst0, r, src0, sp)
					return
				}
			}
		}
		drawRGBA(dst0, 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 *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 {
			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(image.RGBA64Color)
				}
				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)
			}
		}
	}
}
Пример #24
0
// Convert a JSON Hyper-Schema compliant image media object into an image.Image.
// Returns the image, or a non-nil error if there was a problem.
func ObjectToImage(v interface{}) (image.Image, error) {
	return image.Image(image.NewGray(image.ZR)), nil
}
Пример #25
0
func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool {
	return image.Image(dst) == src &&
		r.Overlaps(r.Add(sp.Sub(r.Min))) &&
		(sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X))
}
Пример #26
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)
			}
		}
	}
}