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}