Implement fetaure request issue #17

mtseeder and mtwebmapper got an option to set the background color where no nodes are generated, yet.
This commit is contained in:
Sascha L. Teichmann 2016-04-23 16:45:33 +02:00
parent 0030f7bc02
commit 0db9b519a6
10 changed files with 130 additions and 45 deletions

View File

@ -5,6 +5,7 @@
package main package main
import ( import (
"image/color"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -39,7 +40,7 @@ func createBaseLevel(
address string, address string,
xMin, yMin, zMin, xMax, yMax, zMax int, xMin, yMin, zMin, xMax, yMax, zMax int,
transparent bool, transparentDim float32, transparent bool, transparentDim float32,
colorsFile, outDir string, colorsFile string, bg color.RGBA, outDir string,
numWorkers int) (err error) { numWorkers int) (err error) {
var colors *common.Colors var colors *common.Colors
@ -65,7 +66,7 @@ func createBaseLevel(
} }
done.Add(1) done.Add(1)
btc := common.NewBaseTileCreator( btc := common.NewBaseTileCreator(
client, colors, client, colors, bg,
int16(yMin), int16(yMax), int16(yMin), int16(yMax),
transparent, baseDir, false) transparent, baseDir, false)
go createTiles(btc, jobs, &done) go createTiles(btc, jobs, &done)

View File

@ -7,6 +7,7 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"image/color"
"log" "log"
"bitbucket.org/s_l_teichmann/mtsatellite/common" "bitbucket.org/s_l_teichmann/mtsatellite/common"
@ -19,6 +20,7 @@ func main() {
xMin, yMin, zMin int xMin, yMin, zMin int
xMax, yMax, zMax int xMax, yMax, zMax int
colorsFile string colorsFile string
bgColor string
outDir string outDir string
numWorkers int numWorkers int
skipBaseLevel bool skipBaseLevel bool
@ -28,6 +30,8 @@ func main() {
version bool version bool
) )
defaultBgColor := common.ColorToHex(common.BackgroundColor)
flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "port", 6379, "port to of mtredisalize server")
flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)") flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)")
flag.StringVar(&host, "host", "localhost", "host to mtredisalize server") 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(&zMin, "zmin", -1933, "z min of the area to tile")
flag.IntVar(&zMax, "zmax", 1932, "z max 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(&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, "output-dir", "map", "directory with the resulting image tree")
flag.StringVar(&outDir, "o", "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") flag.IntVar(&numWorkers, "workers", 1, "number of workers")
@ -62,6 +68,13 @@ func main() {
common.PrintVersionAndExit() 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 { if !skipBaseLevel {
td := common.Clamp32f(float32(transparentDim/100.0), 0.0, 1.0) td := common.Clamp32f(float32(transparentDim/100.0), 0.0, 1.0)
address := fmt.Sprintf("%s:%d", host, port) address := fmt.Sprintf("%s:%d", host, port)
@ -69,14 +82,15 @@ func main() {
address, address,
xMin, yMin, zMin, xMax, yMax, zMax, xMin, yMin, zMin, xMax, yMax, zMax,
transparent, td, transparent, td,
colorsFile, colorsFile, bg,
outDir, outDir,
numWorkers); err != nil { numWorkers); err != nil {
log.Fatalf("Creating base level tiles failed: %s", err) log.Fatalf("Creating base level tiles failed: %s", err)
} }
} }
if !skipPyramid { 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) log.Fatalf("Creating pyramid tiles failed: %s", err)
} }
} }

View File

@ -6,6 +6,7 @@ package main
import ( import (
"image" "image"
"image/color"
"image/draw" "image/draw"
"io/ioutil" "io/ioutil"
"log" "log"
@ -21,6 +22,12 @@ import (
"github.com/bamiaux/rez" "github.com/bamiaux/rez"
) )
type pyramidCreator struct {
numWorkers int
outDir string
bg color.RGBA
}
func findMaxDir(files []os.FileInfo) (min, max int) { func findMaxDir(files []os.FileInfo) (min, max int) {
min, max = math.MaxInt32, math.MinInt32 min, max = math.MaxInt32, math.MinInt32
for _, file := range files { for _, file := range files {
@ -64,7 +71,10 @@ type pyramidJob struct {
dst string 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) oldName := filepath.Base(oldDir)
var oldLevel int var oldLevel int
@ -155,11 +165,14 @@ var dps = [4]image.Point{
image.Pt(256, 256), image.Pt(256, 256),
image.Pt(256, 0)} 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 { for i, path := range job.src {
img := common.LoadPNG(path) img := common.LoadPNG(path, pc.bg)
sr := clipRect(img.Bounds()) sr := clipRect(img.Bounds())
r := sr.Sub(sr.Min).Add(dps[i]) 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) 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() defer done.Done()
scratch := image.NewRGBA(image.Rect(0, 0, 512, 512)) scratch := image.NewRGBA(image.Rect(0, 0, 512, 512))
resized := image.NewRGBA(image.Rect(0, 0, 256, 256)) 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 { 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) 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 != ""; { for oldDir := filepath.Join(pc.outDir, baseLevelDir); oldDir != ""; {
if oldDir, err = createLevel(oldDir, numWorker); err != nil { if oldDir, err = pc.createLevel(oldDir); err != nil {
return return
} }
} }
return return
} }
func createLevel(oldDir string, numWorker int) (newDir string, err error) { func (pc *pyramidCreator) createLevel(oldDir string) (string, error) {
jobs := make(chan pyramidJob) jobs := make(chan pyramidJob)
var done sync.WaitGroup var done sync.WaitGroup
for i := 0; i < numWorker; i++ { for i := 0; i < pc.numWorkers; i++ {
done.Add(1) 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) close(jobs)
if err != nil { if err != nil {
return return newDir, err
} }
done.Wait() done.Wait()
return return newDir, err
} }

View File

@ -23,6 +23,7 @@ func main() {
x, y, z int x, y, z int
width, height, depth int width, height, depth int
colorsfile string colorsfile string
bgColor string
outfile string outfile string
shaded bool shaded bool
transparent bool transparent bool
@ -31,6 +32,8 @@ func main() {
version bool version bool
) )
defaultBgColor := common.ColorToHex(common.BackgroundColor)
flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "port", 6379, "port to of mtredisalize server")
flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)") flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)")
flag.StringVar(&host, "host", "localhost", "host to mtredisalize server") 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(&height, "h", 16, "height of query cuboid (shorthand)")
flag.IntVar(&depth, "d", 150, "depth 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(&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, "output", "out.png", "image file of result")
flag.StringVar(&outfile, "o", "out.png", "image file of result (shorthand)") flag.StringVar(&outfile, "o", "out.png", "image file of result (shorthand)")
flag.BoolVar(&shaded, "shaded", true, "draw relief") flag.BoolVar(&shaded, "shaded", true, "draw relief")
@ -60,6 +65,13 @@ func main() {
common.PrintVersionAndExit() 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 != "" { if cpuProfile != "" {
f, err := os.Create(cpuProfile) f, err := os.Create(cpuProfile)
if err != nil { if err != nil {
@ -69,9 +81,7 @@ func main() {
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
} }
var err error
var colors *common.Colors var colors *common.Colors
if colors, err = common.ParseColors(colorsfile); err != nil { if colors, err = common.ParseColors(colorsfile); err != nil {
log.Fatalf("Cannot open color file: %s", err) log.Fatalf("Cannot open color file: %s", err)
} }
@ -134,10 +144,9 @@ func main() {
if shaded { if shaded {
image = renderer.CreateShadedImage( image = renderer.CreateShadedImage(
16, 16, (width-2)*16, (height-2)*16, 16, 16, (width-2)*16, (height-2)*16,
colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) colors, bg)
} else { } else {
image = renderer.CreateImage( image = renderer.CreateImage(colors.Colors, bg)
colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
} }
if err = common.SaveAsPNG(outfile, image); err != nil { if err = common.SaveAsPNG(outfile, image); err != nil {

View File

@ -7,6 +7,7 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"image/color"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -25,6 +26,7 @@ func main() {
redisPort int redisPort int
redisHost string redisHost string
colorsFile string colorsFile string
bgColor string
workers int workers int
transparent bool transparent bool
transparentDim float64 transparentDim float64
@ -35,6 +37,9 @@ func main() {
yMin int yMin int
yMax int yMax int
) )
defaultBgColor := common.ColorToHex(common.BackgroundColor)
flag.IntVar(&webPort, "web-port", 8808, "port of the web server") flag.IntVar(&webPort, "web-port", 8808, "port of the web server")
flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)") flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)")
flag.StringVar(&webHost, "web-host", "localhost", "address to bind web server") 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.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, "colors", "colors.txt", "colors used to render map tiles.")
flag.StringVar(&colorsFile, "c", "colors.txt", "colors used to render map tiles (shorthand).") 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, "transparent", false, "Render transparent blocks.")
flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).") flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).")
flag.Float64Var(&transparentDim, flag.Float64Var(&transparentDim,
@ -76,9 +83,16 @@ func main() {
common.PrintVersionAndExit() 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() 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) router.Path("/map/{z:[0-9]+}/{x:[0-9]+}/{y:[0-9]+}.png").Handler(subBaseLine)
var btu baseTilesUpdates var btu baseTilesUpdates
@ -116,7 +130,7 @@ func main() {
mapDir, mapDir,
redisAddress, redisAddress,
allowedUpdateIps, allowedUpdateIps,
colors, colors, bg,
yMin, yMax, yMin, yMax,
transparent, transparent,
workers, workers,

View File

@ -7,6 +7,7 @@ package main
import ( import (
"fmt" "fmt"
"image" "image"
"image/color"
"image/png" "image/png"
"log" "log"
"net/http" "net/http"
@ -21,10 +22,11 @@ import (
type subBaseLine struct { type subBaseLine struct {
mapDir string mapDir string
bg color.RGBA
} }
func newSubBaseLine(mapDir string) *subBaseLine { func newSubBaseLine(mapDir string, bg color.RGBA) *subBaseLine {
return &subBaseLine{mapDir: mapDir} return &subBaseLine{mapDir: mapDir, bg: bg}
} }
func (sb *subBaseLine) ServeHTTP(rw http.ResponseWriter, r *http.Request) { 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 xo := w * rx
yo := w * (parts - 1 - ry) yo := w * (parts - 1 - ry)
img := common.LoadPNG(baseTile) img := common.LoadPNG(baseTile, sb.bg)
type subImage interface { type subImage interface {
SubImage(image.Rectangle) image.Image SubImage(image.Rectangle) image.Image

View File

@ -7,6 +7,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"image" "image"
"image/color"
"image/draw" "image/draw"
"log" "log"
"net" "net"
@ -34,6 +35,7 @@ type tileUpdater struct {
redisAddress string redisAddress string
ips []net.IP ips []net.IP
colors *common.Colors colors *common.Colors
bg color.RGBA
yMin, yMax int16 yMin, yMax int16
workers int workers int
transparent bool transparent bool
@ -71,6 +73,7 @@ func newTileUpdater(
mapDir, redisAddress string, mapDir, redisAddress string,
ips []net.IP, ips []net.IP,
colors *common.Colors, colors *common.Colors,
bg color.RGBA,
yMin, yMax int, yMin, yMax int,
transparent bool, transparent bool,
workers int, workers int,
@ -83,6 +86,7 @@ func newTileUpdater(
ips: ips, ips: ips,
changes: map[xz]bool{}, changes: map[xz]bool{},
colors: colors, colors: colors,
bg: bg,
yMin: int16(yMin), yMin: int16(yMin),
yMax: int16(yMax), yMax: int16(yMax),
transparent: transparent, transparent: transparent,
@ -171,11 +175,11 @@ func (tu *tileUpdater) doUpdates() {
continue continue
} }
btc := common.NewBaseTileCreator( btc := common.NewBaseTileCreator(
client, tu.colors, client, tu.colors, tu.bg,
tu.yMin, tu.yMax, tu.yMin, tu.yMax,
tu.transparent, baseDir, true) tu.transparent, baseDir, true)
done.Add(1) done.Add(1)
go updateBaseTiles(jobs, btc, &done) go tu.updateBaseTiles(jobs, btc, &done)
} }
parentJobs := make(map[xz]uint16) parentJobs := make(map[xz]uint16)
@ -193,7 +197,7 @@ func (tu *tileUpdater) doUpdates() {
pJobs := make(chan xzm) pJobs := make(chan xzm)
for i, n := 0, common.Min(len(parentJobs), tu.workers); i < n; i++ { for i, n := 0, common.Min(len(parentJobs), tu.workers); i < n; i++ {
done.Add(1) done.Add(1)
go updatePyramidTiles(level, tu.mapDir, pJobs, &done) go tu.updatePyramidTiles(level, pJobs, &done)
} }
ppJobs := make(map[xz]uint16) ppJobs := make(map[xz]uint16)
for c, mask := range parentJobs { 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() defer done.Done()
scratch := image.NewRGBA(image.Rect(0, 0, 256, 256)) scratch := image.NewRGBA(image.Rect(0, 0, 256, 256))
resized := image.NewRGBA(image.Rect(0, 0, 128, 128)) resized := image.NewRGBA(image.Rect(0, 0, 128, 128))
for job := range jobs { 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) log.Printf("Updating pyramid tile failed: %s\n", err)
} }
} }
@ -244,28 +250,29 @@ var ofs = [4][2]int{
var windowSize = image.Pt(128, 128) 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 var orig image.Image
origPath := filepath.Join( origPath := filepath.Join(
baseDir, tu.mapDir,
strconv.Itoa(level), strconv.Itoa(level),
strconv.Itoa(int(j.P.X)), strconv.Itoa(int(j.P.X)),
strconv.Itoa(int(j.P.Z))+".png") strconv.Itoa(int(j.P.Z))+".png")
sr := resized.Bounds() sr := resized.Bounds()
levelDir := strconv.Itoa(level + 1)
for i := uint16(0); i < 4; i++ { for i := uint16(0); i < 4; i++ {
if j.Mask&(1<<i) != 0 { if j.Mask&(1<<i) != 0 {
//log.Printf("level %d: modified %d\n", level, i) //log.Printf("level %d: modified %d\n", level, i)
o := ofs[i] o := ofs[i]
bx, bz := int(2*j.P.X), int(2*j.P.Z) bx, bz := int(2*j.P.X), int(2*j.P.Z)
path := filepath.Join( path := filepath.Join(
baseDir, tu.mapDir,
strconv.Itoa(level+1), levelDir,
strconv.Itoa(bx+o[0]), strconv.Itoa(bx+o[0]),
strconv.Itoa(bz+o[1])+".png") strconv.Itoa(bz+o[1])+".png")
img := common.LoadPNG(path) img := common.LoadPNG(path, tu.bg)
if err := rez.Convert(resized, img, common.ResizeFilter); err != nil { if err := rez.Convert(resized, img, common.ResizeFilter); err != nil {
return err return err
} }
@ -274,7 +281,7 @@ func updatePyramidTile(scratch, resized *image.RGBA, level int, baseDir string,
} else { } else {
// Load lazy // Load lazy
if orig == nil { if orig == nil {
orig = common.LoadPNG(origPath) orig = common.LoadPNG(origPath, tu.bg)
} }
//log.Printf("level %d: copied %d\n", level, i) //log.Printf("level %d: copied %d\n", level, i)
min := orig.Bounds().Min.Add(dps[i]) min := orig.Bounds().Min.Add(dps[i])
@ -286,7 +293,10 @@ func updatePyramidTile(scratch, resized *image.RGBA, level int, baseDir string,
return common.SaveAsPNGAtomic(origPath, scratch) return common.SaveAsPNGAtomic(origPath, scratch)
} }
func updateBaseTiles(jobs chan xz, btc *common.BaseTileCreator, done *sync.WaitGroup) { func (tu *tileUpdater) updateBaseTiles(
jobs chan xz,
btc *common.BaseTileCreator, done *sync.WaitGroup) {
defer btc.Close() defer btc.Close()
defer done.Done() defer done.Done()
for job := range jobs { for job := range jobs {

View File

@ -59,11 +59,13 @@ type BaseTileCreator struct {
baseDir string baseDir string
update bool update bool
emptyImage []byte emptyImage []byte
bg color.RGBA
} }
func NewBaseTileCreator( func NewBaseTileCreator(
client *RedisClient, client *RedisClient,
colors *Colors, colors *Colors,
bg color.RGBA,
yMin, yMax int16, yMin, yMax int16,
transparent bool, transparent bool,
baseDir string, baseDir string,
@ -73,6 +75,7 @@ func NewBaseTileCreator(
return &BaseTileCreator{ return &BaseTileCreator{
client: client, client: client,
colors: colors, colors: colors,
bg: bg,
renderer: renderer, renderer: renderer,
yOrder: NewYOrder(renderer, yOrderCapacity), yOrder: NewYOrder(renderer, yOrderCapacity),
yMin: yMin, yMin: yMin,
@ -141,7 +144,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error {
// To avoid redundant encoding cache the resulting empty image. // To avoid redundant encoding cache the resulting empty image.
if btc.emptyImage == nil { if btc.emptyImage == nil {
var err error var err error
m := BackgroundImage((tileWidth-2)*16, (tileHeight-2)*16, BackgroundColor) m := BackgroundImage((tileWidth-2)*16, (tileHeight-2)*16, btc.bg)
if btc.emptyImage, err = EncodeToMem(m); err != nil { if btc.emptyImage, err = EncodeToMem(m); err != nil {
return err return err
} }
@ -152,7 +155,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error {
image := btc.renderer.CreateShadedImage( image := btc.renderer.CreateShadedImage(
16, 16, (tileWidth-2)*16, (tileHeight-2)*16, 16, 16, (tileWidth-2)*16, (tileHeight-2)*16,
btc.colors, BackgroundColor) btc.colors, btc.bg)
log.Printf("Writing (%d, %d) to file %s\n", x, z, path) log.Printf("Writing (%d, %d) to file %s\n", x, z, path)

View File

@ -10,6 +10,7 @@ import (
"image/color" "image/color"
"os" "os"
"sort" "sort"
"strconv"
"strings" "strings"
) )
@ -121,3 +122,20 @@ func (colors *Colors) BlendColors(span *Span, col color.RGBA, pos int32) color.R
} }
return col return col
} }
func ParseColor(col string) (color.RGBA, error) {
col = strings.TrimLeft(col, "#")
rgb, err := strconv.ParseUint(col, 16, 32)
if err != nil {
return color.RGBA{}, err
}
return color.RGBA{
R: uint8(rgb >> 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)
}

View File

@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"image" "image"
"image/color"
"image/png" "image/png"
"log" "log"
"os" "os"
@ -94,18 +95,18 @@ func SaveAsPNGAtomic(path string, img image.Image) (err error) {
return os.Rename(tmpPath, path) return os.Rename(tmpPath, path)
} }
func LoadPNG(path string) image.Image { func LoadPNG(path string, bg color.RGBA) image.Image {
var err error var err error
var file *os.File var file *os.File
if file, err = os.Open(path); err != nil { if file, err = os.Open(path); err != nil {
return image.White return image.NewUniform(bg)
} }
defer file.Close() defer file.Close()
reader := bufio.NewReader(file) reader := bufio.NewReader(file)
var img image.Image var img image.Image
if img, err = png.Decode(reader); err != nil { if img, err = png.Decode(reader); err != nil {
log.Printf("WARN: decoding '%s' failed: %s\n", path, err) log.Printf("WARN: decoding '%s' failed: %s\n", path, err)
return image.White return image.NewUniform(bg)
} }
return img return img
} }