seekia/imported/goeffects/grayscale.go

78 lines
1.9 KiB
Go

package goeffects
// Package copied from https://github.com/markdaws/go-effects
import "image"
import "runtime"
// GSAlgo the type of algorithm to use when converting an image to it's grayscale equivalent
type GSAlgo int
const (
// GSLIGHTNESS is the average of the min and max r,g,b value
GSLIGHTNESS GSAlgo = iota
// GSAVERAGE is the average of the r,g,b values of each pixel
GSAVERAGE
// GSLUMINOSITY used a weighting for r,g,b based on how the human eye perceives colors
GSLUMINOSITY
)
type grayscale struct {
algo GSAlgo
}
func (gs *grayscale) Apply(img *Image, numRoutines int) (*Image, error) {
if numRoutines == 0 {
numRoutines = runtime.GOMAXPROCS(0)
}
pf := func(ri, x, y, offset, inStride int, inPix, outPix []uint8) {
var r, g, b uint8 = inPix[offset], inPix[offset+1], inPix[offset+2]
switch gs.algo {
case GSLIGHTNESS:
maximum := float64(max(max(r, g), b))
minimum := float64(max(min(r, g), b))
r = uint8(maximum + minimum/2)
g = r
b = r
case GSAVERAGE:
r = (r + g + b) / 3
g = r
b = r
case GSLUMINOSITY:
r = uint8(0.21*float64(r) + 0.72*float64(g) + 0.07*float64(b))
g = r
b = r
}
outPix[offset] = r
outPix[offset+1] = g
outPix[offset+2] = b
outPix[offset+3] = 255
}
out := &Image{
img: image.NewRGBA(image.Rectangle{
Min: image.Point{X: 0, Y: 0},
Max: image.Point{X: img.Width, Y: img.Height},
}),
Width: img.Width,
Height: img.Height,
Bounds: Rect{
X: img.Bounds.X,
Y: img.Bounds.Y,
Width: img.Bounds.Width,
Height: img.Bounds.Height,
},
}
runParallel(numRoutines, img, out.Bounds, out, pf, 0)
return out, nil
}
// NewGrayscale renders the input image as a grayscale image. numRoutines specifies how many
// goroutines should be used to process the image in parallel, use 0 to let the library decide
func NewGrayscale(algo GSAlgo) Effect {
return &grayscale{algo: algo}
}