package goeffects // Package copied from https://github.com/markdaws/go-effects import "image" import "runtime" type oilPainting struct { filterSize int levels int } // NewOilPainting renders the input image as if it was painted like an oil painting. numRoutines specifies how many // goroutines should be used to process the image in parallel, use 0 to let the library decide. filterSize specifies // how bold the image should look, larger numbers equate to larger strokes, levels specifies how many buckets colors // will be grouped in to, start with values 5,30 to see how that works. func NewOilPainting(filterSize, levels int) Effect { return &oilPainting{filterSize: filterSize, levels: levels} } func (op *oilPainting) Apply(img *Image, numRoutines int) (*Image, error) { levels := op.levels - 1 filterOffset := (op.filterSize - 1) / 2 if numRoutines == 0 { numRoutines = runtime.GOMAXPROCS(0) } var iBin, rBin, gBin, bBin [][]int iBin = make([][]int, numRoutines) rBin = make([][]int, numRoutines) gBin = make([][]int, numRoutines) bBin = make([][]int, numRoutines) for ri := 0; ri < numRoutines; ri++ { iBin[ri] = make([]int, levels+1) rBin[ri] = make([]int, levels+1) gBin[ri] = make([]int, levels+1) bBin[ri] = make([]int, levels+1) } pf := func(ri, x, y, offset, inStride int, inPix, outPix []uint8) { reset(iBin[ri]) reset(rBin[ri]) reset(gBin[ri]) reset(bBin[ri]) var maxIntensity int var maxIndex int for fy := -filterOffset; fy <= filterOffset; fy++ { for fx := -filterOffset; fx <= filterOffset; fx++ { fOffset := offset + (fx*4 + fy*inStride) r := inPix[fOffset] g := inPix[fOffset+1] b := inPix[fOffset+2] ci := int(roundToInt32((float64(r+g+b) / 3.0 * float64(levels)) / 255.0)) iBin[ri][ci]++ rBin[ri][ci] += int(r) gBin[ri][ci] += int(g) bBin[ri][ci] += int(b) if iBin[ri][ci] > maxIntensity { maxIntensity = iBin[ri][ci] maxIndex = ci } } } outPix[offset] = uint8(rBin[ri][maxIndex] / maxIntensity) outPix[offset+1] = uint8(gBin[ri][maxIndex] / maxIntensity) outPix[offset+2] = uint8(bBin[ri][maxIndex] / maxIntensity) 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 + filterOffset, Y: img.Bounds.Y + filterOffset, Width: img.Bounds.Width - 2*filterOffset, Height: img.Bounds.Height - 2*filterOffset, }, } runParallel(numRoutines, img, out.Bounds, out, pf, 0) return out, nil }