Example #1
0
func warm(out, in draw.Image, opacity uint8, colors []color.Color) {
	draw.Draw(out, out.Bounds(), image.Transparent, image.ZP, draw.Src)
	bounds := in.Bounds()
	collen := float64(len(colors))
	wg := &sync.WaitGroup{}
	for x := bounds.Min.X; x < bounds.Max.X; x++ {
		wg.Add(1)
		go func(x int) {
			defer wg.Done()
			for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
				col := in.At(x, y)
				_, _, _, alpha := col.RGBA()
				if alpha > 0 {
					percent := float64(alpha) / float64(0xffff)
					template := colors[int((collen-1)*(1.0-percent))]
					tr, tg, tb, ta := template.RGBA()
					ta /= 256
					outalpha := uint8(float64(ta) *
						(float64(opacity) / 256.0))
					outcol := color.NRGBA{
						uint8(tr / 256),
						uint8(tg / 256),
						uint8(tb / 256),
						uint8(outalpha)}
					out.Set(x, y, outcol)
				}
			}
		}(x)
	}
	wg.Wait()
}
Example #2
0
func warm(out, in draw.Image, opacity uint8, colors []color.Color) {
	bounds := in.Bounds()
	collen := float64(len(colors))
	wg := sync.WaitGroup{}
	wg.Add(bounds.Dx())
	for x := bounds.Min.X; x < bounds.Max.X; x++ {
		go func(x int) {
			defer wg.Done()
			for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
				col := in.At(x, y)
				_, _, _, alpha := col.RGBA()
				percent := float64(alpha) / float64(0xffff)
				var outcol color.Color
				if percent == 0 {
					outcol = color.Transparent
				} else {
					template := colors[int((collen-1)*(1.0-percent))]
					tr, tg, tb, ta := template.RGBA()
					ta /= 256
					outalpha := uint8(float64(ta) *
						(float64(opacity) / 256.0))
					outcol = color.NRGBA{
						uint8(tr / 256),
						uint8(tg / 256),
						uint8(tb / 256),
						uint8(outalpha)}
				}
				out.Set(x, y, outcol)
			}
		}(x)
	}
	wg.Wait()
}
Example #3
0
// Apply the given color mapping to the specified image buffers.
func Apply(from, to *Rule, src, dst draw.Image) {
	var x, y int
	var r, g, b, a uint32
	var sc color.Color
	var dc color.RGBA
	var pix pixel

	rect := src.Bounds()

	for y = 0; y < rect.Dy(); y++ {
		for x = 0; x < rect.Dx(); x++ {
			sc = src.At(x, y)

			r, g, b, a = sc.RGBA()
			pix.r = uint8(r >> 8)
			pix.g = uint8(g >> 8)
			pix.b = uint8(b >> 8)
			pix.a = uint8(a >> 8)

			// Check if the pixel matches the filter rule.
			if !(match(pix.r, from.R) && match(pix.g, from.G) && match(pix.b, from.B) && match(pix.a, from.A)) {
				dst.Set(x, y, sc)
				continue
			}

			// Compute three different types of grayscale conversion.
			// These can be applied by named references.
			pix.average = uint8(((r + g + b) / 3) >> 8)
			pix.lightness = uint8(((min(min(r, g), b) + max(max(r, g), b)) / 2) >> 8)

			// For luminosity it is necessary to apply an inverse of the gamma
			// function for the color space before calculating the inner product.
			// Then you apply the gamma function to the reduced value. Failure to
			// incorporate the gamma function can result in errors of up to 20%.
			//
			// For typical computer stuff, the color space is sRGB. The right
			// numbers for sRGB are approx. 0.21, 0.72, 0.07. Gamma for sRGB
			// is a composite function that approximates exponentiation by 1/2.2
			//
			// This is a rather expensive operation, but gives a much more accurate
			// and satisfactory result than the average and lightness versions.
			pix.luminosity = gammaSRGB(
				0.212655*invGammaSRGB(pix.r) +
					0.715158*invGammaSRGB(pix.g) +
					0.072187*invGammaSRGB(pix.b))

			// Transform color.
			dc.R = transform(&pix, pix.r, to.R)
			dc.G = transform(&pix, pix.g, to.G)
			dc.B = transform(&pix, pix.b, to.B)
			dc.A = transform(&pix, pix.a, to.A)

			// Set new pixel.
			dst.Set(x, y, dc)
		}
	}
}
Example #4
0
// Blends src image (top layer) into dst image (bottom layer) using
// the BlendFunc provided by mode. BlendFunc is applied to each pixel
// where the src image overlaps the dst image and the result is stored
// in the original dst image, src image is unmutable.
func BlendImage(dst draw.Image, src image.Image, mode BlendFunc) {
	// Obtain the intersection of both images.
	inter := dst.Bounds().Intersect(src.Bounds())
	// Apply BlendFuc to each pixel in the intersection.
	for y := inter.Min.Y; y < inter.Max.Y; y++ {
		for x := inter.Min.X; x < inter.Max.X; x++ {
			dst.Set(x, y, mode(dst.At(x, y), src.At(x, y)))
		}
	}
}
Example #5
0
func (burkes) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
	quantError0 := make([][3]int32, r.Dx()+4)
	quantError1 := make([][3]int32, r.Dx()+4)

	out := color.RGBA64{A: 0xffff}
	for y := 0; y != r.Dy(); y++ {
		for x := 0; x != r.Dx(); x++ {
			sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA()
			er, eg, eb := int32(sr), int32(sg), int32(sb)

			er = clamp(er + quantError0[x+2][0]/32)
			eg = clamp(eg + quantError0[x+2][1]/32)
			eb = clamp(eb + quantError0[x+2][2]/32)

			out.R = uint16(er)
			out.G = uint16(eg)
			out.B = uint16(eb)
			dst.Set(r.Min.X+x, r.Min.Y+y, &out)

			sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
			er -= int32(sr)
			eg -= int32(sg)
			eb -= int32(sb)

			quantError0[x+3][0] += er * 8
			quantError0[x+3][1] += eg * 8
			quantError0[x+3][2] += eb * 8
			quantError0[x+4][0] += er * 4
			quantError0[x+4][1] += eg * 4
			quantError0[x+4][2] += eb * 4
			quantError1[x+0][0] += er * 2
			quantError1[x+0][1] += eg * 2
			quantError1[x+0][2] += eb * 2
			quantError1[x+1][0] += er * 4
			quantError1[x+1][1] += eg * 4
			quantError1[x+1][2] += eb * 4
			quantError1[x+2][0] += er * 8
			quantError1[x+2][1] += eg * 8
			quantError1[x+2][2] += eb * 8
			quantError1[x+3][0] += er * 4
			quantError1[x+3][1] += eg * 4
			quantError1[x+3][2] += eb * 4
			quantError1[x+4][0] += er * 2
			quantError1[x+4][1] += eg * 2
			quantError1[x+4][2] += eb * 2
		}

		// Recycle the quantization error buffers.
		quantError0, quantError1 = quantError1, quantError0
		for i := range quantError1 {
			quantError1[i] = [3]int32{}
		}
	}
}
Example #6
0
func noise(src draw.Image) {
	orig := src.Bounds()
	numToMod := (orig.Max.X * orig.Max.Y) / 2
	for i := 0; i < numToMod; i++ {
		x := rand.Intn(orig.Max.X)
		y := rand.Intn(orig.Max.Y)
		origColor := src.At(x, y).(color.RGBA)
		origColor.R += 30
		origColor.B += 30
		origColor.G += 30
		src.Set(x, y, origColor)
	}
}
func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) {
	bounds := src.Bounds()
	x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y)
	tr.TransformRectangle(&x0, &y0, &x1, &y1)
	var x, y, u, v float64
	var c1, c2, cr color.Color
	var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32
	var color color.RGBA
	for x = x0; x < x1; x++ {
		for y = y0; y < y1; y++ {
			u = x
			v = y
			tr.InverseTransform(&u, &v)
			if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) {
				c1 = dest.At(int(x), int(y))
				switch filter {
				case LinearFilter:
					c2 = src.At(int(u), int(v))
				case BilinearFilter:
					c2 = getColorBilinear(src, u, v)
				case BicubicFilter:
					c2 = getColorBicubic(src, u, v)
				}
				switch op {
				case draw.Over:
					r1, g1, b1, a1 = c1.RGBA()
					r2, g2, b2, a2 = c2.RGBA()
					ia = M - a2
					r = ((r1 * ia) / M) + r2
					g = ((g1 * ia) / M) + g2
					b = ((b1 * ia) / M) + b2
					a = ((a1 * ia) / M) + a2
					color.R = uint8(r >> 8)
					color.G = uint8(g >> 8)
					color.B = uint8(b >> 8)
					color.A = uint8(a >> 8)
					cr = color
				default:
					cr = c2
				}
				dest.Set(int(x), int(y), cr)
			}
		}
	}
}
Example #8
0
func (z *Rasterizer) rasterizeOpOver(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
	z.accumulateMask()
	out := color.RGBA64{}
	outc := color.Color(&out)
	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
			sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
			ma := z.bufU32[y*z.size.X+x]

			// This algorithm comes from the standard library's image/draw
			// package.
			dr, dg, db, da := dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
			a := 0xffff - (sa * ma / 0xffff)
			out.R = uint16((dr*a + sr*ma) / 0xffff)
			out.G = uint16((dg*a + sg*ma) / 0xffff)
			out.B = uint16((db*a + sb*ma) / 0xffff)
			out.A = uint16((da*a + sa*ma) / 0xffff)

			dst.Set(r.Min.X+x, r.Min.Y+y, outc)
		}
	}
}
func (falseFloydSteinberg) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
	quantErrorNext := make([][3]int32, r.Dx()+1)

	out := color.RGBA64{A: 0xffff}
	for y := 0; y != r.Dy(); y++ {
		quantError := [3]int32{}
		quantErrorNext[0] = [3]int32{}
		for x := 0; x != r.Dx(); x++ {
			sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA()
			er, eg, eb := int32(sr), int32(sg), int32(sb)

			er = clamp(er + (quantErrorNext[x][0]+quantError[0])/16)
			eg = clamp(eg + (quantErrorNext[x][1]+quantError[1])/16)
			eb = clamp(eb + (quantErrorNext[x][2]+quantError[2])/16)

			out.R = uint16(er)
			out.G = uint16(eg)
			out.B = uint16(eb)
			dst.Set(r.Min.X+x, r.Min.Y+y, &out)

			sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
			er -= int32(sr)
			eg -= int32(sg)
			eb -= int32(sb)
			quantError[0] = er * 3
			quantError[1] = eg * 3
			quantError[2] = eb * 3
			quantErrorNext[x+0][0] += er * 3
			quantErrorNext[x+0][1] += eg * 3
			quantErrorNext[x+0][2] += eb * 3
			quantErrorNext[x+1][0] = er * 2
			quantErrorNext[x+1][1] = eg * 2
			quantErrorNext[x+1][2] = eb * 2
		}
	}
}
Example #10
0
// Copy an images outer edge of pixels and "bleeds" them to the edge of the image
func bleed(im draw.Image, amount int) {
	// TODO refactor
	outer := im.Bounds()
	inner := image.Rect(outer.Min.X+amount, outer.Min.Y+amount, outer.Max.X-amount, outer.Max.Y-amount)

	// Top left
	col := im.At(inner.Min.X, inner.Min.Y)
	for x := 0; x < amount; x++ {
		for y := 0; y < amount; y++ {
			im.Set(x, y, col)
		}
	}
	// Top edge
	for x := inner.Min.X; x < inner.Max.X; x++ {
		col := im.At(x, amount)
		for y := 0; y < amount; y++ {
			im.Set(x, y, col)
		}
	}
	// Top right
	col = im.At(inner.Max.X-1, inner.Min.Y)
	for x := 1; x <= amount; x++ {
		for y := 0; y < amount; y++ {
			im.Set(outer.Max.X-x, y, col)
		}
	}
	// Right edge
	for y := amount; y < inner.Max.X; y++ {
		col := im.At(inner.Max.X-1, y)
		for x := 1; x <= amount; x++ {
			im.Set(outer.Max.X-x, y, col)
		}
	}
	// Bottom right
	col = im.At(inner.Max.X-1, inner.Max.Y-1)
	for x := 1; x <= amount; x++ {
		for y := 1; y <= amount; y++ {
			im.Set(outer.Max.X-x, outer.Max.Y-y, col)
		}
	}
	// Bottom edge
	for x := amount; x < inner.Max.X; x++ {
		col := im.At(x, inner.Max.Y-1)
		for y := 1; y <= amount; y++ {
			im.Set(x, outer.Max.Y-y, col)
		}
	}
	// Bottom left
	col = im.At(inner.Min.X, inner.Max.Y-1)
	for x := 0; x < amount; x++ {
		for y := 1; y <= amount; y++ {
			im.Set(x, outer.Max.Y-y, col)
		}
	}
	// Left edge
	for y := amount; y < inner.Max.Y; y++ {
		col := im.At(amount, y)
		for x := 0; x < amount; x++ {
			im.Set(x, y, col)
		}
	}
}