-
Notifications
You must be signed in to change notification settings - Fork 10
/
main.go
136 lines (118 loc) · 3.74 KB
/
main.go
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package main
import (
"flag"
"fmt"
"image"
"image/color"
"image/png"
"log"
"os"
"runtime"
"strconv"
"time"
)
var imgConf ImageConfig
func main() {
parseImageConfigArgs()
generateImagesFromLocations(getLocations())
}
func parseImageConfigArgs() {
imgWidthPtr := flag.Int("width", 1920, "The width of the image in pixels.")
imgHeightPtr := flag.Int("height", 1024, "The height of the image in pixels.")
samplesPtr := flag.Int("samples", 5, "The number of samples")
maxIterPtr := flag.Int("iter", 500, "The max. number of iterations.")
OffsetPtr := flag.Float64("offset", 0.0, "The HSL offset in the range [0, 1)")
mixingPtr := flag.Bool("mixing", true, "Use linear color mixing.")
insideBlackPtr := flag.Bool("black", true, "Paint area inside in black.")
grayscalePtr := flag.Bool("grayscale", false, "Paint image in grayscale.")
flag.Parse()
imgConf = ImageConfig{
Width: *imgWidthPtr,
Height: *imgHeightPtr,
Samples: *samplesPtr,
MaxIter: *maxIterPtr,
Offset: *OffsetPtr,
Mixing: *mixingPtr,
InsideBlack: *insideBlackPtr,
Grayscale: *grayscalePtr,
RndGlobal: uint64(time.Now().UnixNano()),
}
}
func generateImagesFromLocations(locs LocationsFile) {
dirPath := fmt.Sprintf("results/%d", imgConf.MaxIter)
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
if err := os.MkdirAll(dirPath, 0755); err != nil {
panic(err)
}
}
for index, loc := range locs.Locations {
log.Println("Allocating and rendering image", index+1)
img := image.NewRGBA(image.Rect(0, 0, imgConf.Width, imgConf.Height))
renderImage(img, loc)
log.Println("Encoding image", index+1)
filename := "results/" + strconv.Itoa(imgConf.MaxIter) + "/" + strconv.Itoa(index+1)
f, err := os.Create(filename + ".png")
if err != nil {
panic(err)
}
err = png.Encode(f, img)
if err != nil {
panic(err)
}
}
}
func renderImage(img *image.RGBA, loc Location) {
jobs := make(chan int)
for i := 0; i < runtime.NumCPU(); i++ {
rndLocal := RandUint64(&imgConf.RndGlobal)
go func() {
for y := range jobs {
renderRow(loc, y, img, &rndLocal)
}
}()
}
for y := 0; y < imgConf.Height; y++ {
jobs <- y
fmt.Printf("\r%d/%d (%d%%)", y, imgConf.Height, int(100*(float64(y)/float64(imgConf.Height))))
}
fmt.Printf("\r%d/%[1]d (100%%)\n", imgConf.Height)
}
func renderRow(loc Location, y int, img *image.RGBA, rndLocal *uint64) {
for x := 0; x < imgConf.Width; x++ {
cr, cg, cb := getColorForPixel(loc, x, y, rndLocal)
img.SetRGBA(x, y, color.RGBA{R: cr, G: cg, B: cb, A: 255})
}
}
func getColorForPixel(loc Location, x int, y int, rndLocal *uint64) (uint8, uint8, uint8) {
var r, g, b int
for i := 0; i < imgConf.Samples; i++ {
c := getColorForComplexNr(convertPixelToComplexNr(loc, x, y, rndLocal))
if imgConf.Mixing {
r += int(RGBToLinear(c.R))
g += int(RGBToLinear(c.G))
b += int(RGBToLinear(c.B))
} else {
r += int(c.R)
g += int(c.G)
b += int(c.B)
}
}
var cr, cg, cb uint8
if imgConf.Mixing {
cr = LinearToRGB(uint16(float64(r) / float64(imgConf.Samples)))
cg = LinearToRGB(uint16(float64(g) / float64(imgConf.Samples)))
cb = LinearToRGB(uint16(float64(b) / float64(imgConf.Samples)))
} else {
cr = uint8(float64(r) / float64(imgConf.Samples))
cg = uint8(float64(g) / float64(imgConf.Samples))
cb = uint8(float64(b) / float64(imgConf.Samples))
}
return cr, cg, cb
}
func convertPixelToComplexNr(loc Location, x int, y int, rndLocal *uint64) complex128 {
ratio := float64(imgConf.Width) / float64(imgConf.Height)
// RandFloat64() is added for anti-aliasing
nx := (1/loc.Zoom)*ratio*((float64(x)+RandFloat64(rndLocal))/float64(imgConf.Width)-0.5) + loc.XCenter
ny := (1/loc.Zoom)*((float64(y)+RandFloat64(rndLocal))/float64(imgConf.Height)-0.5) - loc.YCenter
return complex(nx, ny)
}