diff --git a/cmd/mtseeder/baselevel.go b/cmd/mtseeder/baselevel.go index 2f7fba6..cbd8ebf 100644 --- a/cmd/mtseeder/baselevel.go +++ b/cmd/mtseeder/baselevel.go @@ -5,6 +5,7 @@ package main import ( + "image/color" "log" "os" "path/filepath" @@ -39,7 +40,7 @@ func createBaseLevel( address string, xMin, yMin, zMin, xMax, yMax, zMax int, transparent bool, transparentDim float32, - colorsFile, outDir string, + colorsFile string, bg color.RGBA, outDir string, numWorkers int) (err error) { var colors *common.Colors @@ -65,7 +66,7 @@ func createBaseLevel( } done.Add(1) btc := common.NewBaseTileCreator( - client, colors, + client, colors, bg, int16(yMin), int16(yMax), transparent, baseDir, false) go createTiles(btc, jobs, &done) diff --git a/cmd/mtseeder/main.go b/cmd/mtseeder/main.go index acb9a24..cee37dd 100644 --- a/cmd/mtseeder/main.go +++ b/cmd/mtseeder/main.go @@ -7,6 +7,7 @@ package main import ( "flag" "fmt" + "image/color" "log" "bitbucket.org/s_l_teichmann/mtsatellite/common" @@ -19,6 +20,7 @@ func main() { xMin, yMin, zMin int xMax, yMax, zMax int colorsFile string + bgColor string outDir string numWorkers int skipBaseLevel bool @@ -28,6 +30,8 @@ func main() { version bool ) + defaultBgColor := common.ColorToHex(common.BackgroundColor) + flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)") flag.StringVar(&host, "host", "localhost", "host to mtredisalize server") @@ -38,6 +42,8 @@ func main() { flag.IntVar(&zMin, "zmin", -1933, "z min of the area to tile") flag.IntVar(&zMax, "zmax", 1932, "z max of the area to tile") flag.StringVar(&colorsFile, "colors", "colors.txt", "definition of colors") + flag.StringVar(&bgColor, "background", defaultBgColor, "background color") + flag.StringVar(&bgColor, "bg", defaultBgColor, "background color (shorthand)") flag.StringVar(&outDir, "output-dir", "map", "directory with the resulting image tree") flag.StringVar(&outDir, "o", "map", "directory with the resulting image tree") flag.IntVar(&numWorkers, "workers", 1, "number of workers") @@ -62,6 +68,13 @@ func main() { common.PrintVersionAndExit() } + var bg color.RGBA + var err error + if bg, err = common.ParseColor(bgColor); err != nil { + log.Printf("WARN: Cannot parse background color '%s': %s\n", bgColor, err) + bg = common.BackgroundColor + } + if !skipBaseLevel { td := common.Clamp32f(float32(transparentDim/100.0), 0.0, 1.0) address := fmt.Sprintf("%s:%d", host, port) @@ -69,14 +82,15 @@ func main() { address, xMin, yMin, zMin, xMax, yMax, zMax, transparent, td, - colorsFile, + colorsFile, bg, outDir, numWorkers); err != nil { log.Fatalf("Creating base level tiles failed: %s", err) } } if !skipPyramid { - if err := createPyramid(outDir, numWorkers); err != nil { + pc := pyramidCreator{numWorkers: numWorkers, outDir: outDir, bg: bg} + if err := pc.create(); err != nil { log.Fatalf("Creating pyramid tiles failed: %s", err) } } diff --git a/cmd/mtseeder/pyramid.go b/cmd/mtseeder/pyramid.go index ffb0349..2426ec1 100644 --- a/cmd/mtseeder/pyramid.go +++ b/cmd/mtseeder/pyramid.go @@ -6,6 +6,7 @@ package main import ( "image" + "image/color" "image/draw" "io/ioutil" "log" @@ -21,6 +22,12 @@ import ( "github.com/bamiaux/rez" ) +type pyramidCreator struct { + numWorkers int + outDir string + bg color.RGBA +} + func findMaxDir(files []os.FileInfo) (min, max int) { min, max = math.MaxInt32, math.MinInt32 for _, file := range files { @@ -64,7 +71,10 @@ type pyramidJob struct { dst string } -func createParentLevel(oldDir string, jobs chan pyramidJob) (newDir string, err error) { +func (pc *pyramidCreator) createParentLevel( + oldDir string, + jobs chan pyramidJob) (newDir string, err error) { + oldName := filepath.Base(oldDir) var oldLevel int @@ -155,11 +165,14 @@ var dps = [4]image.Point{ image.Pt(256, 256), image.Pt(256, 0)} -func fuseTile(scratch, resized *image.RGBA, conv rez.Converter, job *pyramidJob) error { +func (pc *pyramidCreator) fuseTile( + scratch, resized *image.RGBA, + conv rez.Converter, + job *pyramidJob) error { for i, path := range job.src { - img := common.LoadPNG(path) + img := common.LoadPNG(path, pc.bg) sr := clipRect(img.Bounds()) r := sr.Sub(sr.Min).Add(dps[i]) @@ -176,7 +189,7 @@ func fuseTile(scratch, resized *image.RGBA, conv rez.Converter, job *pyramidJob) return common.SaveAsPNG(job.dst, resized) } -func fuseTiles(jobs chan pyramidJob, done *sync.WaitGroup) { +func (pc *pyramidCreator) fuseTiles(jobs chan pyramidJob, done *sync.WaitGroup) { defer done.Done() scratch := image.NewRGBA(image.Rect(0, 0, 512, 512)) resized := image.NewRGBA(image.Rect(0, 0, 256, 256)) @@ -194,41 +207,41 @@ func fuseTiles(jobs chan pyramidJob, done *sync.WaitGroup) { } for job := range jobs { - if err := fuseTile(scratch, resized, conv, &job); err != nil { + if err := pc.fuseTile(scratch, resized, conv, &job); err != nil { log.Printf("WARN: Writing image failed: %s\n", err) } } } -func createPyramid(outDir string, numWorker int) (err error) { +func (pc *pyramidCreator) create() (err error) { - for oldDir := filepath.Join(outDir, baseLevelDir); oldDir != ""; { - if oldDir, err = createLevel(oldDir, numWorker); err != nil { + for oldDir := filepath.Join(pc.outDir, baseLevelDir); oldDir != ""; { + if oldDir, err = pc.createLevel(oldDir); err != nil { return } } return } -func createLevel(oldDir string, numWorker int) (newDir string, err error) { +func (pc *pyramidCreator) createLevel(oldDir string) (string, error) { jobs := make(chan pyramidJob) var done sync.WaitGroup - for i := 0; i < numWorker; i++ { + for i := 0; i < pc.numWorkers; i++ { done.Add(1) - go fuseTiles(jobs, &done) + go pc.fuseTiles(jobs, &done) } - newDir, err = createParentLevel(oldDir, jobs) + newDir, err := pc.createParentLevel(oldDir, jobs) close(jobs) if err != nil { - return + return newDir, err } done.Wait() - return + return newDir, err } diff --git a/cmd/mttilemapper/main.go b/cmd/mttilemapper/main.go index e1a4d83..103739b 100644 --- a/cmd/mttilemapper/main.go +++ b/cmd/mttilemapper/main.go @@ -23,6 +23,7 @@ func main() { x, y, z int width, height, depth int colorsfile string + bgColor string outfile string shaded bool transparent bool @@ -31,6 +32,8 @@ func main() { version bool ) + defaultBgColor := common.ColorToHex(common.BackgroundColor) + flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)") flag.StringVar(&host, "host", "localhost", "host to mtredisalize server") @@ -44,6 +47,8 @@ func main() { flag.IntVar(&height, "h", 16, "height of query cuboid (shorthand)") flag.IntVar(&depth, "d", 150, "depth of query cuboid (shorthand)") flag.StringVar(&colorsfile, "colors", "colors.txt", "definition of colors") + flag.StringVar(&bgColor, "background", defaultBgColor, "background color") + flag.StringVar(&bgColor, "bg", defaultBgColor, "background color (shorthand)") flag.StringVar(&outfile, "output", "out.png", "image file of result") flag.StringVar(&outfile, "o", "out.png", "image file of result (shorthand)") flag.BoolVar(&shaded, "shaded", true, "draw relief") @@ -60,6 +65,13 @@ func main() { common.PrintVersionAndExit() } + var bg color.RGBA + var err error + if bg, err = common.ParseColor(bgColor); err != nil { + log.Printf("WARN: Cannot parse background color '%s': %s\n", bgColor, err) + bg = common.BackgroundColor + } + if cpuProfile != "" { f, err := os.Create(cpuProfile) if err != nil { @@ -69,9 +81,7 @@ func main() { defer pprof.StopCPUProfile() } - var err error var colors *common.Colors - if colors, err = common.ParseColors(colorsfile); err != nil { log.Fatalf("Cannot open color file: %s", err) } @@ -134,10 +144,9 @@ func main() { if shaded { image = renderer.CreateShadedImage( 16, 16, (width-2)*16, (height-2)*16, - colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) + colors, bg) } else { - image = renderer.CreateImage( - colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) + image = renderer.CreateImage(colors.Colors, bg) } if err = common.SaveAsPNG(outfile, image); err != nil { diff --git a/cmd/mtwebmapper/main.go b/cmd/mtwebmapper/main.go index 7d4b420..4cf3305 100644 --- a/cmd/mtwebmapper/main.go +++ b/cmd/mtwebmapper/main.go @@ -7,6 +7,7 @@ package main import ( "flag" "fmt" + "image/color" "log" "net" "net/http" @@ -25,6 +26,7 @@ func main() { redisPort int redisHost string colorsFile string + bgColor string workers int transparent bool transparentDim float64 @@ -35,6 +37,9 @@ func main() { yMin int yMax int ) + + defaultBgColor := common.ColorToHex(common.BackgroundColor) + flag.IntVar(&webPort, "web-port", 8808, "port of the web server") flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)") flag.StringVar(&webHost, "web-host", "localhost", "address to bind web server") @@ -54,6 +59,8 @@ func main() { flag.IntVar(&workers, "workers", 1, "number of workers to render tiles") flag.StringVar(&colorsFile, "colors", "colors.txt", "colors used to render map tiles.") flag.StringVar(&colorsFile, "c", "colors.txt", "colors used to render map tiles (shorthand).") + flag.StringVar(&bgColor, "background", defaultBgColor, "background color") + flag.StringVar(&bgColor, "bg", defaultBgColor, "background color (shorthand)") flag.BoolVar(&transparent, "transparent", false, "Render transparent blocks.") flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).") flag.Float64Var(&transparentDim, @@ -76,9 +83,16 @@ func main() { common.PrintVersionAndExit() } + var bg color.RGBA + var err error + if bg, err = common.ParseColor(bgColor); err != nil { + log.Printf("WARN: Cannot parse background color '%s': %s\n", bgColor, err) + bg = common.BackgroundColor + } + router := mux.NewRouter() - subBaseLine := newSubBaseLine(mapDir) + subBaseLine := newSubBaseLine(mapDir, bg) router.Path("/map/{z:[0-9]+}/{x:[0-9]+}/{y:[0-9]+}.png").Handler(subBaseLine) var btu baseTilesUpdates @@ -116,7 +130,7 @@ func main() { mapDir, redisAddress, allowedUpdateIps, - colors, + colors, bg, yMin, yMax, transparent, workers, diff --git a/cmd/mtwebmapper/subbaseline.go b/cmd/mtwebmapper/subbaseline.go index c63f7f6..0ef6279 100644 --- a/cmd/mtwebmapper/subbaseline.go +++ b/cmd/mtwebmapper/subbaseline.go @@ -7,6 +7,7 @@ package main import ( "fmt" "image" + "image/color" "image/png" "log" "net/http" @@ -21,10 +22,11 @@ import ( type subBaseLine struct { mapDir string + bg color.RGBA } -func newSubBaseLine(mapDir string) *subBaseLine { - return &subBaseLine{mapDir: mapDir} +func newSubBaseLine(mapDir string, bg color.RGBA) *subBaseLine { + return &subBaseLine{mapDir: mapDir, bg: bg} } func (sb *subBaseLine) ServeHTTP(rw http.ResponseWriter, r *http.Request) { @@ -77,7 +79,7 @@ func (sb *subBaseLine) ServeHTTP(rw http.ResponseWriter, r *http.Request) { xo := w * rx yo := w * (parts - 1 - ry) - img := common.LoadPNG(baseTile) + img := common.LoadPNG(baseTile, sb.bg) type subImage interface { SubImage(image.Rectangle) image.Image diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 0a4e6c8..1e91a3a 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -7,6 +7,7 @@ package main import ( "encoding/json" "image" + "image/color" "image/draw" "log" "net" @@ -34,6 +35,7 @@ type tileUpdater struct { redisAddress string ips []net.IP colors *common.Colors + bg color.RGBA yMin, yMax int16 workers int transparent bool @@ -71,6 +73,7 @@ func newTileUpdater( mapDir, redisAddress string, ips []net.IP, colors *common.Colors, + bg color.RGBA, yMin, yMax int, transparent bool, workers int, @@ -83,6 +86,7 @@ func newTileUpdater( ips: ips, changes: map[xz]bool{}, colors: colors, + bg: bg, yMin: int16(yMin), yMax: int16(yMax), transparent: transparent, @@ -171,11 +175,11 @@ func (tu *tileUpdater) doUpdates() { continue } btc := common.NewBaseTileCreator( - client, tu.colors, + client, tu.colors, tu.bg, tu.yMin, tu.yMax, tu.transparent, baseDir, true) done.Add(1) - go updateBaseTiles(jobs, btc, &done) + go tu.updateBaseTiles(jobs, btc, &done) } parentJobs := make(map[xz]uint16) @@ -193,7 +197,7 @@ func (tu *tileUpdater) doUpdates() { pJobs := make(chan xzm) for i, n := 0, common.Min(len(parentJobs), tu.workers); i < n; i++ { done.Add(1) - go updatePyramidTiles(level, tu.mapDir, pJobs, &done) + go tu.updatePyramidTiles(level, pJobs, &done) } ppJobs := make(map[xz]uint16) for c, mask := range parentJobs { @@ -212,13 +216,15 @@ func (tu *tileUpdater) doUpdates() { } } -func updatePyramidTiles(level int, baseDir string, jobs chan xzm, done *sync.WaitGroup) { +func (tu *tileUpdater) updatePyramidTiles( + level int, jobs chan xzm, done *sync.WaitGroup) { + defer done.Done() scratch := image.NewRGBA(image.Rect(0, 0, 256, 256)) resized := image.NewRGBA(image.Rect(0, 0, 128, 128)) for job := range jobs { - if err := updatePyramidTile(scratch, resized, level, baseDir, job); err != nil { + if err := tu.updatePyramidTile(scratch, resized, level, job); err != nil { log.Printf("Updating pyramid tile failed: %s\n", err) } } @@ -244,28 +250,29 @@ var ofs = [4][2]int{ var windowSize = image.Pt(128, 128) -func updatePyramidTile(scratch, resized *image.RGBA, level int, baseDir string, j xzm) error { +func (tu *tileUpdater) updatePyramidTile(scratch, resized *image.RGBA, level int, j xzm) error { var orig image.Image origPath := filepath.Join( - baseDir, + tu.mapDir, strconv.Itoa(level), strconv.Itoa(int(j.P.X)), strconv.Itoa(int(j.P.Z))+".png") sr := resized.Bounds() + levelDir := strconv.Itoa(level + 1) for i := uint16(0); i < 4; i++ { if j.Mask&(1<> 16), + G: uint8(rgb >> 8), + B: uint8(rgb), + A: 0xff}, nil +} + +func ColorToHex(col color.RGBA) string { + return fmt.Sprintf("#%02x%02x%02x", col.R, col.G, col.B) +} diff --git a/common/image.go b/common/image.go index 03c10ea..dcf3211 100644 --- a/common/image.go +++ b/common/image.go @@ -9,6 +9,7 @@ import ( "bytes" "errors" "image" + "image/color" "image/png" "log" "os" @@ -94,18 +95,18 @@ func SaveAsPNGAtomic(path string, img image.Image) (err error) { return os.Rename(tmpPath, path) } -func LoadPNG(path string) image.Image { +func LoadPNG(path string, bg color.RGBA) image.Image { var err error var file *os.File if file, err = os.Open(path); err != nil { - return image.White + return image.NewUniform(bg) } defer file.Close() reader := bufio.NewReader(file) var img image.Image if img, err = png.Decode(reader); err != nil { log.Printf("WARN: decoding '%s' failed: %s\n", path, err) - return image.White + return image.NewUniform(bg) } return img }