effects.go

 1package gift
 2
 3import (
 4	"image"
 5	"image/draw"
 6)
 7
 8type pixelateFilter struct {
 9	size int
10}
11
12func (p *pixelateFilter) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangle) {
13	dstBounds = image.Rect(0, 0, srcBounds.Dx(), srcBounds.Dy())
14	return
15}
16
17func (p *pixelateFilter) Draw(dst draw.Image, src image.Image, options *Options) {
18	if options == nil {
19		options = &defaultOptions
20	}
21
22	blockSize := p.size
23	if blockSize <= 1 {
24		copyimage(dst, src, options)
25		return
26	}
27
28	srcb := src.Bounds()
29	dstb := dst.Bounds()
30
31	numBlocksX := srcb.Dx() / blockSize
32	if srcb.Dx()%blockSize > 0 {
33		numBlocksX++
34	}
35	numBlocksY := srcb.Dy() / blockSize
36	if srcb.Dy()%blockSize > 0 {
37		numBlocksY++
38	}
39
40	pixGetter := newPixelGetter(src)
41	pixSetter := newPixelSetter(dst)
42
43	parallelize(options.Parallelization, 0, numBlocksY, func(bmin, bmax int) {
44		for by := bmin; by < bmax; by++ {
45			for bx := 0; bx < numBlocksX; bx++ {
46				// calculate the block bounds
47				bb := image.Rect(bx*blockSize, by*blockSize, (bx+1)*blockSize, (by+1)*blockSize)
48				bbSrc := bb.Add(srcb.Min).Intersect(srcb)
49				bbDst := bbSrc.Sub(srcb.Min).Add(dstb.Min).Intersect(dstb)
50
51				// calculate average color of the block
52				var r, g, b, a float32
53				var cnt float32
54				for y := bbSrc.Min.Y; y < bbSrc.Max.Y; y++ {
55					for x := bbSrc.Min.X; x < bbSrc.Max.X; x++ {
56						px := pixGetter.getPixel(x, y)
57						r += px.r
58						g += px.g
59						b += px.b
60						a += px.a
61						cnt++
62					}
63				}
64				if cnt > 0 {
65					r /= cnt
66					g /= cnt
67					b /= cnt
68					a /= cnt
69				}
70
71				// set the calculated color for all pixels in the block
72				for y := bbDst.Min.Y; y < bbDst.Max.Y; y++ {
73					for x := bbDst.Min.X; x < bbDst.Max.X; x++ {
74						pixSetter.setPixel(x, y, pixel{r, g, b, a})
75					}
76				}
77			}
78		}
79	})
80}
81
82// Pixelate creates a filter that applies a pixelation effect to an image.
83func Pixelate(size int) Filter {
84	return &pixelateFilter{
85		size: size,
86	}
87}