1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
/*
Copyright (C) 2019 Leo Tenenbaum
This file is part of AutoDistortion.
AutoDistortion is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
AutoDistortion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with AutoDistortion. If not, see <https://www.gnu.org/licenses/>.
*/
package autodistortion
import (
"image"
"image/color"
"github.com/pommicket/autodistortion/autoutils"
)
func mod(a int, b int) int {
// Because a % b is the remainder, not modulus!
return ((a % b) + b) % b
}
type workInfo struct {
img image.Image
xfunction autoutils.Function
yfunction autoutils.Function
}
func DistortRows(info *workInfo, out [][]color.RGBA, yFrom int, yTo int,
done chan struct{}) {
minX := info.img.Bounds().Min.X
maxX := info.img.Bounds().Max.X
minY := info.img.Bounds().Min.Y
maxY := info.img.Bounds().Max.Y
width := maxX - minX
height := maxY - minY
fwidth := float64(width)
fheight := float64(height)
xy := make([]float64, 2)
for y := yFrom; y < yTo; y++ {
xy[1] = (float64(y) - float64(minY)) / fheight
for x := 0; x < width; x++ {
xy[0] = float64(x) / fwidth
srcX := mod(int(info.xfunction.Evaluate(xy) * fwidth), width) + minX
srcY := mod(int(info.yfunction.Evaluate(xy) * fheight), height) + minY
r, g, b, a := info.img.At(int(srcX), int(srcY)).RGBA()
out[y][x] = color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8),
uint8(a >> 8)}
}
}
done <- struct{}{}
}
func Distort(img image.Image, functionLen int, nThreads int) image.Image {
rgba := image.NewRGBA(img.Bounds())
var xfunction autoutils.Function
var yfunction autoutils.Function
xfunction.Generate(2, functionLen)
yfunction.Generate(2, functionLen)
minY := img.Bounds().Min.Y
maxY := img.Bounds().Max.Y
minX := img.Bounds().Min.X
maxX := img.Bounds().Max.X
width := maxX - minX
height := maxY - minY
if width == 0 || height == 0 {
// I don't even know if this is possible, but just in case
return rgba
}
info := workInfo{img, xfunction, yfunction}
// Make a slice of pixels (image.RGBA.Set isn't thread safe ): )
pixels := make([][]color.RGBA, height)
done := make(chan struct{})
for y := 0; y < height; y++ {
pixels[y] = make([]color.RGBA, width)
}
if nThreads > height {
nThreads = height // Don't make more than one thread per row
}
for t := 0; t < nThreads; t++ {
yFrom := t * (height / nThreads) + minY
var yTo int
if t == nThreads - 1 { // Deal with final thread
yTo = maxY // (go to end of image)
} else {
yTo = (t+1) * (height / nThreads) + minY
}
go DistortRows(&info, pixels, yFrom, yTo, done)
}
for y := 0; y < nThreads; y++ {
<-done // Wait for all goroutines to finish
}
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
rgba.Set(x, y, pixels[y][x])
}
}
return rgba
}
|