Example #1
0
// cb is backdrop colour, cs is source (blend layer) colour
func BlendPixel(cb, cs color.Color, f Blender) color.Color {
	// Uses methods described in "PDF Reference, Third Edition" from Adobe
	//  see: http://www.adobe.com/devnet/pdf/pdf_reference_archive.html

	// result colour
	cr := f(cb, cs)

	rb, gb, bb, ab := utils.RatioRGBA(cb)
	rs, gs, bs, as := utils.RatioRGBA(cs)
	rr, gr, br, _ := utils.RatioRGBA(cr)

	// Color compositing formula, expanded form. (Section 7.2.5)
	red := ((1 - as) * ab * rb) + ((1 - ab) * as * rs) + (ab * as * rr)
	green := ((1 - as) * ab * gb) + ((1 - ab) * as * gs) + (ab * as * gr)
	blue := ((1 - as) * ab * bb) + ((1 - ab) * as * bs) + (ab * as * br)

	// Union function. (Section 7.2.6)
	alpha := ab + as - (ab * as)

	return color.RGBA{
		uint8(utils.Truncatef(red * 255)),
		uint8(utils.Truncatef(green * 255)),
		uint8(utils.Truncatef(blue * 255)),
		uint8(utils.Truncatef(alpha * 255)),
	}
}
Example #2
0
// Dissolve randomly selects pixels from the blend image, depending on their
// opacity. Blend pixels with higher opacities are more likely to be displayed.
func Dissolve(a, b image.Image) image.Image {
	ba := a.Bounds()
	bb := b.Bounds()
	width := int(utils.Min(uint32(ba.Dx()), uint32(bb.Dx())))
	height := int(utils.Min(uint32(ba.Dy()), uint32(bb.Dy())))

	result := image.NewRGBA(image.Rect(0, 0, width, height))

	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			// base colour
			rb, gb, bb, ab := utils.RatioRGBA(a.At(x, y))
			// blend colour
			rs, gs, bs, as := utils.RatioRGBA(b.At(x, y))

			toPaint := ratioNRGBA(rb, gb, bb, ab)

			if rand.Float64() < as {
				toPaint = ratioNRGBA(rs, gs, bs, 1)
			}

			result.Set(x, y, toPaint)
		}
	}

	return result
}
Example #3
0
// Lighter chooses the lightest colour by comparing the sum of the colour
// channels.
func Lighter(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, _ := utils.RatioRGBA(c)
		m, n, o, _ := utils.RatioRGBA(d)

		if i+j+k > m+n+o {
			return c
		}
		return d
	})
}
Example #4
0
// Dodge brightens the base colour to reflect the blend colour.
func Dodge(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := i / (1 - m)
		g := j / (1 - n)
		b := k / (1 - o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #5
0
// Screen multiplies the complements of the base and blend colour channel
// values, then complements the result.
func Screen(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := 1 - ((1 - i) * (1 - m))
		g := 1 - ((1 - j) * (1 - n))
		b := 1 - ((1 - k) * (1 - o))
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #6
0
// Lightne selects the lighter of each pixels' colour channels.
func Lighten(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := utils.Maxf(i, m)
		g := utils.Maxf(j, n)
		b := utils.Maxf(k, o)
		a := utils.Maxf(l, p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #7
0
// LinearBurn adds the values of each colour channel together, then subtracts
// white to produce a darker image.
func LinearBurn(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := i + m - 1
		g := j + n - 1
		b := k + o - 1
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #8
0
// Burn darkens the base colour to reflect the blend colour.
func Burn(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := 1 - ((1 - i) / m)
		g := 1 - ((1 - j) / n)
		b := 1 - ((1 - k) / o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #9
0
// Multiply multiplies the base and blend image colour channels.
func Multiply(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := i * m
		g := j * n
		b := k * o
		a := l * p

		return ratioNRGBA(r, g, b, a)
	})
}
Example #10
0
// Exclusion creates an effect similar to, but lower in contrast than,
// difference.
func Exclusion(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := m + i - (2 * m * i)
		g := n + j - (2 * n * j)
		b := o + k - (2 * o * k)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #11
0
// Difference finds the absolute difference between the base and blend colours.
func Difference(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		r := math.Abs(m - i)
		g := math.Abs(n - j)
		b := math.Abs(o - k)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #12
0
// BlendPixels takes the base and blend images and applies the given Blender to
// each of their pixel pairs.
func BlendPixels(a, b image.Image, f Blender) image.Image {
	ba := a.Bounds()
	bb := b.Bounds()
	width := int(utils.Min(uint32(ba.Dx()), uint32(bb.Dx())))
	height := int(utils.Min(uint32(ba.Dy()), uint32(bb.Dy())))

	result := image.NewRGBA(image.Rect(0, 0, width, height))

	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			// Uses methods described in "PDF Reference, Third Edition" from Adobe
			//  see: http://www.adobe.com/devnet/pdf/pdf_reference_archive.html

			// backdrop colour
			cb := a.At(x, y)
			// source colour
			cs := b.At(x, y)
			// result colour
			cr := f(cb, cs)

			rb, gb, bb, ab := utils.RatioRGBA(cb)
			rs, gs, bs, as := utils.RatioRGBA(cs)
			rr, gr, br, _ := utils.RatioRGBA(cr)

			// Color compositing formula, expanded form. (Section 7.2.5)
			red := ((1 - as) * ab * rb) + ((1 - ab) * as * rs) + (ab * as * rr)
			green := ((1 - as) * ab * gb) + ((1 - ab) * as * gs) + (ab * as * gr)
			blue := ((1 - as) * ab * bb) + ((1 - ab) * as * bs) + (ab * as * br)

			// Union function. (Section 7.2.6)
			alpha := ab + as - (ab * as)

			result.Set(x, y, color.RGBA{
				uint8(utils.Truncatef(red * 255)),
				uint8(utils.Truncatef(green * 255)),
				uint8(utils.Truncatef(blue * 255)),
				uint8(utils.Truncatef(alpha * 255)),
			})
		}
	}

	return result
}
Example #13
0
// HardMix adds the red, green and blue channel values of the blend colour to
// the RGB values of the base colour. It sets any values greater than 255 to
// 255, and anything less to 0. This therefore makes all pixels either red,
// green, blue, white or black.
func HardMix(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		f := func(i, j float64) float64 {
			if j < 1-i {
				return 0
			}
			return 1
		}

		r := f(i, m)
		g := f(j, n)
		b := f(k, o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #14
0
// LinearLight lightens or darkens the image by changing the brightness. If the
// blend colour is lighter, the image is lightened; if the blend colour is
// darker, the image is darkened. It uses linear burn and linear dodge to darken
// or lighten.
func LinearLight(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		f := func(i, j float64) float64 {
			if j > 0.5 {
				return i + 2*(j-0.5)
			}
			return i + 2*j - 1
		}

		r := f(i, m)
		g := f(j, n)
		b := f(k, o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #15
0
// VividLight combines Dodge and Burn. Dodge applies to lighter colours, and
// Burn to darker.
func VividLight(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		f := func(i, j float64) float64 {
			if j > 0.5 {
				return i / (2 * (1 - j))
			}
			return 1 - (1-i)/(2*j)
		}

		r := f(i, m)
		g := f(j, n)
		b := f(k, o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #16
0
func AdjustC(value float64) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)

		r = utils.Truncatef(math.Pow(r, 1/value) * 255)
		g = utils.Truncatef(math.Pow(g, 1/value) * 255)
		b = utils.Truncatef(math.Pow(b, 1/value) * 255)

		return color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a * 255)}
	}
}
Example #17
0
// UnsharpMask sharpens the given Image using the unsharp mask technique.
// Basically the image is blurred, then subtracted from the original for
// differences above the threshold value.
func UnsharpMask(in image.Image, radius int, sigma, amount, threshold float64) image.Image {
	blurred := blur.Gaussian(in, radius, sigma, blur.IGNORE)
	bounds := in.Bounds()
	out := image.NewRGBA(bounds)

	// Absolute difference between a and b, returns float64 between 0 and 1.
	diff := func(a, b float64) float64 {
		if a > b {
			return a - b
		}
		return b - a
	}

	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			ar, ag, ab, aa := utils.RatioRGBA(in.At(x, y))
			br, bg, bb, _ := utils.RatioRGBA(blurred.At(x, y))

			if diff(ar, br) >= threshold {
				ar = amount*(ar-br) + ar
			}

			if diff(ag, bg) >= threshold {
				ag = amount*(ag-bg) + ag
			}

			if diff(ab, bb) >= threshold {
				ab = amount*(ab-bb) + ab
			}

			out.Set(x, y, color.NRGBA{
				uint8(utils.Truncatef(ar * 255)),
				uint8(utils.Truncatef(ag * 255)),
				uint8(utils.Truncatef(ab * 255)),
				uint8(aa * 255),
			})
		}
	}

	return out
}
Example #18
0
func AlphaC(adj utils.Adjuster) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)
		a = adj(a)
		if a > 1 {
			a = 1
		} else if a < 0 {
			a = 0
		}
		return color.NRGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), uint8(a * 255)}
	}
}
Example #19
0
func BlueC(adj utils.Adjuster) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)
		b = adj(b)
		if b > 1 {
			b = 1
		} else if b < 0 {
			b = 0
		}
		return color.NRGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), uint8(a * 255)}
	}
}
Example #20
0
func GreenC(adj utils.Adjuster) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)
		g = adj(g)
		if g > 1 {
			g = 1
		} else if g < 0 {
			g = 0
		}
		return color.NRGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), uint8(a * 255)}
	}
}
Example #21
0
func RedC(adj utils.Adjuster) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)
		r = adj(r)
		if r > 1 {
			r = 1
		} else if r < 0 {
			r = 0
		}
		return color.NRGBA{uint8(r * 255), uint8(g * 255), uint8(b * 255), uint8(a * 255)}
	}
}
Example #22
0
func AdjustC(value float64) utils.Composable {
	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)

		r = utils.Truncatef((((r - 0.5) * value) + 0.5) * 255)
		g = utils.Truncatef((((g - 0.5) * value) + 0.5) * 255)
		b = utils.Truncatef((((b - 0.5) * value) + 0.5) * 255)
		a = a * 255

		return color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
	}
}
Example #23
0
// PinLight replaces the colours, depending on the blend colour.
func PinLight(a, b image.Image) image.Image {
	return BlendPixels(a, b, func(c, d color.Color) color.Color {
		i, j, k, l := utils.RatioRGBA(c)
		m, n, o, p := utils.RatioRGBA(d)

		f := func(i, j float64) float64 {
			if i < 2*j-1 {
				return 2*j - 1
			} else if i > 2*j {
				return 2 * j
			}
			return i
		}

		r := f(i, m)
		g := f(j, n)
		b := f(k, o)
		a := p + l*(1-p)

		return ratioNRGBA(r, g, b, a)
	})
}
Example #24
0
func hsiaModel(c color.Color) color.Color {
	if _, ok := c.(HSIA); ok {
		return c
	}

	r, g, b, a := utils.RatioRGBA(c)

	maxi := utils.Maxf(r, g, b)
	mini := utils.Minf(r, g, b)
	chroma := maxi - mini

	// Work out hue
	hdash := 0.0
	if chroma == 0 {
		hdash = 0
	} else if maxi == r {
		hdash = math.Mod((g-b)/chroma, 6)
	} else if maxi == g {
		hdash = (b-r)/chroma + 2.0
	} else if maxi == b {
		hdash = (r-g)/chroma + 4.0
	}

	hue := hdash * 60

	if chroma == 0 {
		hue = 0
	}

	// Work out intensity
	intensity := (r + g + b) / 3

	// Work out saturation
	saturation := 0.0
	if chroma != 0 {
		saturation = 1 - mini/intensity
	}

	// prefer positive hues
	if hue < 0 {
		hue += 360
	}

	return HSIA{hue, saturation, intensity, a}
}
Example #25
0
func hsvaModel(c color.Color) color.Color {
	if _, ok := c.(HSVA); ok {
		return c
	}

	r, g, b, a := utils.RatioRGBA(c)

	maxi := utils.Maxf(r, g, b)
	mini := utils.Minf(r, g, b)
	chroma := maxi - mini

	// Work out hue
	hdash := 0.0
	if chroma == 0 {
		hdash = 0
	} else if maxi == r {
		hdash = math.Mod((g-b)/chroma, 6)
	} else if maxi == g {
		hdash = (b-r)/chroma + 2.0
	} else if maxi == b {
		hdash = (r-g)/chroma + 4.0
	}

	hue := hdash * 60

	if chroma == 0 {
		hue = 0
	}

	// Work out value
	value := maxi

	// Work out saturation
	saturation := 0.0
	if chroma != 0 {
		saturation = chroma / value
	}

	return HSVA{hue, saturation, value, a}
}
Example #26
0
func hslaModel(c color.Color) color.Color {
	if _, ok := c.(HSLA); ok {
		return c
	}

	r, g, b, a := utils.RatioRGBA(c)

	maxi := utils.Maxf(r, g, b)
	mini := utils.Minf(r, g, b)
	chroma := maxi - mini

	// Work out hue
	hdash := 0.0
	if chroma == 0 {
		hdash = 0
	} else if maxi == r {
		hdash = math.Mod((g-b)/chroma, 6)
	} else if maxi == g {
		hdash = (b-r)/chroma + 2.0
	} else if maxi == b {
		hdash = (r-g)/chroma + 4.0
	}

	hue := hdash * 60

	if chroma == 0 {
		hue = 0
	}

	// Work out lightness
	lightness := 0.5 * (maxi + mini)

	// Work out saturation
	saturation := 0.0
	if chroma != 0 {
		saturation = chroma / (1 - math.Abs(2*lightness-1))
	}

	return HSLA{hue, saturation, lightness, a}
}
Example #27
0
func SigmoidalC(factor, midpoint float64) utils.Composable {
	alpha := midpoint
	beta := factor

	f := func(u float64) float64 {
		return (1.0/(1.0+math.Exp(beta*(alpha-u))) -
			1.0/(1.0+math.Exp(beta))) /
			(1.0/(1.0+math.Exp(beta*(alpha-1))) -
				1.0/(1.0+math.Exp(beta*alpha)))
	}

	return func(c color.Color) color.Color {
		r, g, b, a := utils.RatioRGBA(c)

		r = utils.Truncatef(f(r) * 255)
		g = utils.Truncatef(f(g) * 255)
		b = utils.Truncatef(f(b) * 255)
		a = a * 255

		return color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
	}
}
Example #28
0
func getAlpha(c color.Color) float64 {
	_, _, _, a := utils.RatioRGBA(c)
	return a
}
Example #29
0
func getBlue(c color.Color) float64 {
	_, _, b, _ := utils.RatioRGBA(c)
	return b
}
Example #30
0
func getGreen(c color.Color) float64 {
	_, g, _, _ := utils.RatioRGBA(c)
	return g
}