From 8452a26fcdcb5d34858eff3ac860549f522aba3d Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 26 Oct 2014 18:36:47 +0100 Subject: [PATCH] Render transparent tiles if command line flag is set. --- cmd/mtseeder/baselevel.go | 3 +- cmd/mtseeder/main.go | 10 +- cmd/mttilemapper/main.go | 6 +- cmd/mtwebmapper/main.go | 21 ++-- cmd/mtwebmapper/tilesupdater.go | 11 +- common/basetilecreator.go | 5 +- common/colors.go | 4 +- common/renderer.go | 215 +++++++++++++++++++++++--------- 8 files changed, 198 insertions(+), 77 deletions(-) diff --git a/cmd/mtseeder/baselevel.go b/cmd/mtseeder/baselevel.go index 9f3faf9..8b13cdf 100644 --- a/cmd/mtseeder/baselevel.go +++ b/cmd/mtseeder/baselevel.go @@ -48,6 +48,7 @@ func order(a, b int) (int, int) { func createBaseLevel( address string, xMin, zMin, xMax, zMax int, + transparent bool, colorsFile, outDir string, numWorkers int) (err error) { @@ -71,7 +72,7 @@ func createBaseLevel( return } done.Add(1) - btc := common.NewBaseTileCreator(client, colors, baseDir, false) + btc := common.NewBaseTileCreator(client, colors, transparent, baseDir, false) go createTiles(btc, jobs, &done) } diff --git a/cmd/mtseeder/main.go b/cmd/mtseeder/main.go index 557506e..7b8b795 100644 --- a/cmd/mtseeder/main.go +++ b/cmd/mtseeder/main.go @@ -21,6 +21,7 @@ func main() { numWorkers int skipBaseLevel bool skipPyramid bool + transparent bool ) flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") @@ -39,6 +40,8 @@ func main() { flag.BoolVar(&skipBaseLevel, "sb", false, "Do not generate base level tiles (shorthand)") flag.BoolVar(&skipPyramid, "skip-pyramid", false, "Do not generate pyramid tiles") flag.BoolVar(&skipPyramid, "sp", false, "Do not generate pyramid tiles (shorthand)") + flag.BoolVar(&transparent, "transparent", false, "Render transparent blocks.") + flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).") flag.Parse() @@ -46,7 +49,12 @@ func main() { if !skipBaseLevel { address := fmt.Sprintf("%s:%d", host, port) if err = createBaseLevel( - address, xMin, zMin, xMax, zMax, colorsFile, outDir, numWorkers); err != nil { + address, + xMin, zMin, xMax, zMax, + transparent, + colorsFile, + outDir, + numWorkers); err != nil { log.Fatalf("Creating base level tiles failed: %s", err) } } diff --git a/cmd/mttilemapper/main.go b/cmd/mttilemapper/main.go index 6944bbc..87fea0c 100644 --- a/cmd/mttilemapper/main.go +++ b/cmd/mttilemapper/main.go @@ -23,6 +23,7 @@ func main() { colorsfile string outfile string shaded bool + transparent bool ) flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") @@ -41,6 +42,7 @@ func main() { 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") + flag.BoolVar(&transparent, "transparent", false, "render transparent blocks") flag.Parse() @@ -70,7 +72,7 @@ func main() { q1x, q1y, q1z := int16(x), int16(y), int16(z) q2x, q2y, q2z := q1x+int16(width)-1, q1y+int16(depth)-1, q1z+int16(height)-1 - renderer := common.NewRenderer(width, height) + renderer := common.NewRenderer(width, height, transparent) renderer.SetPos(q1x, q1z) yOrder := common.NewYOrder(renderer, 512) @@ -106,7 +108,7 @@ func main() { if shaded { image = renderer.CreateShadedImage( 16, 16, (width-2)*16, (height-2)*16, - colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) + colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) } else { image = renderer.CreateImage( colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) diff --git a/cmd/mtwebmapper/main.go b/cmd/mtwebmapper/main.go index d326dcb..a70ffd2 100644 --- a/cmd/mtwebmapper/main.go +++ b/cmd/mtwebmapper/main.go @@ -17,14 +17,15 @@ import ( func main() { var ( - webPort int - webHost string - webDir string - mapDir string - redisPort int - redisHost string - colorsFile string - workers int + webPort int + webHost string + webDir string + mapDir string + redisPort int + redisHost string + colorsFile string + workers int + transparent bool ) flag.IntVar(&webPort, "web-port", 8808, "port of the web server") flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)") @@ -41,6 +42,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.BoolVar(&transparent, "transparent", false, "Render transparent blocks.") + flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).") flag.Parse() @@ -56,7 +59,7 @@ func main() { log.Fatalf("ERROR: problem loading colors: %s", err) } redisAddress := fmt.Sprintf("%s:%d", redisHost, redisPort) - tu := newTileUpdater(mapDir, redisAddress, colors, workers) + tu := newTileUpdater(mapDir, redisAddress, colors, transparent, workers) go tu.doUpdates() router.Path("/update").Methods("POST").Handler(tu) } diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 0c4c65e..7047164 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -26,6 +26,7 @@ type tileUpdater struct { redisAddress string colors *common.Colors workers int + transparent bool cond *sync.Cond mu sync.Mutex } @@ -56,12 +57,18 @@ func (c xz) parent() xzm { Mask: 1 << (zr<<1 | xr)} } -func newTileUpdater(mapDir, redisAddress string, colors *common.Colors, workers int) *tileUpdater { +func newTileUpdater( + mapDir, redisAddress string, + colors *common.Colors, + transparent bool, + workers int) *tileUpdater { + tu := tileUpdater{ mapDir: mapDir, redisAddress: redisAddress, changes: map[xz]bool{}, colors: colors, + transparent: transparent, workers: workers} tu.cond = sync.NewCond(&tu.mu) return &tu @@ -113,7 +120,7 @@ func (tu *tileUpdater) doUpdates() { log.Printf("WARN: Cannot connect to redis server: %s", err) continue } - btc := common.NewBaseTileCreator(client, tu.colors, baseDir, true) + btc := common.NewBaseTileCreator(client, tu.colors, tu.transparent, baseDir, true) done.Add(1) go updateBaseTiles(jobs, btc, &done) } diff --git a/common/basetilecreator.go b/common/basetilecreator.go index c23464c..6f3affc 100644 --- a/common/basetilecreator.go +++ b/common/basetilecreator.go @@ -54,9 +54,10 @@ type BaseTileCreator struct { func NewBaseTileCreator( client *RedisClient, colors *Colors, + transparent bool, baseDir string, update bool) *BaseTileCreator { - renderer := NewRenderer(tileWidth, tileHeight) + renderer := NewRenderer(tileWidth, tileHeight, transparent) return &BaseTileCreator{ client: client, colors: colors, @@ -118,7 +119,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error { image := btc.renderer.CreateShadedImage( 16, 16, (tileWidth-2)*16, (tileHeight-2)*16, - btc.colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) + btc.colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) path := filepath.Join(btc.baseDir, strconv.Itoa(i), fmt.Sprintf("%d.png", j)) diff --git a/common/colors.go b/common/colors.go index eaf1b3f..5edef65 100644 --- a/common/colors.go +++ b/common/colors.go @@ -115,8 +115,8 @@ func (colors *Colors) BlendColors(span *Span, col color.RGBA, pos int32) color.R } const scale = float32(1) / float32(100) for ; curr != nil; curr = curr.Next { - // At least 20% attenuation + 5% extra for each depth meter. - factor := float32(min(100, 20+(curr.To-curr.From)*5)) * scale + // At least 35% attenuation + 5% extra for each depth meter. + factor := float32(min(100, 35+(curr.To-curr.From)*5)) * scale col = BlendColor(colors.Colors[curr.Value], col, factor) } return col diff --git a/common/renderer.go b/common/renderer.go index 49328af..29017c9 100644 --- a/common/renderer.go +++ b/common/renderer.go @@ -27,6 +27,8 @@ type Renderer struct { RejectedBlocks int SolidBlocks int TransparentBlocks int + spans *SpanPool + tBuffer []*Span } type YOrder struct { @@ -122,19 +124,29 @@ func (yo *YOrder) Pop() (x interface{}) { return x } -func NewRenderer(width, height int) (renderer *Renderer) { +func NewRenderer(width, height int, transparent bool) (renderer *Renderer) { dim := width * height pixSize := dim * 16 * 16 yBuffer := make([]int32, pixSize) cBuffer := make([]int32, pixSize) yMin := make([]int32, dim) + var tBuffer []*Span + var spans *SpanPool + + if transparent { + tBuffer = make([]*Span, pixSize) + spans = NewSpanPool() + } + renderer = &Renderer{ width: width, height: height, yBuffer: yBuffer, cBuffer: cBuffer, - yMin: yMin} + yMin: yMin, + tBuffer: tBuffer, + spans: spans} renderer.Reset() return @@ -153,6 +165,15 @@ func (r *Renderer) Reset() { for i, n := 0, len(r.yMin); i < n; i++ { r.yMin[i] = math.MinInt32 } + + if r.tBuffer != nil { + for i, t := range r.tBuffer { + if t != nil { + r.spans.FreeAll(t) + r.tBuffer[i] = nil + } + } + } } func (r *Renderer) IsFilled() bool { @@ -189,37 +210,64 @@ func (r *Renderer) RenderBlock(block *Block, colors *Colors) (err error) { return } - if db.Transparent { - r.TransparentBlocks++ - } else { - r.SolidBlocks++ - } - w := r.width << 4 ofs := int(bz)*w<<4 + int(bx)<<4 yB := r.yBuffer - yMin := int32(math.MaxInt32) - for z := 0; z < 16; z++ { - for x := 0; x < 16; x++ { - currentY := yB[ofs] - if currentY < blockY { - for y := 15; y >= 0; y-- { - if c, ok := db.Content(x, y, z); ok { - r.cBuffer[ofs] = c - currentY = blockY + int32(y) - yB[ofs] = currentY - break + + if db.Transparent && r.tBuffer != nil { + r.TransparentBlocks++ + + for z := 0; z < 16; z++ { + for x := 0; x < 16; x++ { + currentY := yB[ofs] + if currentY < blockY { + for y := 15; y >= 0; y-- { + if c, ok := db.Content(x, y, z); ok { + currentY = blockY + int32(y) + + if colors.IsTransparent(c) { + r.tBuffer[ofs] = r.spans.Insert(r.tBuffer[ofs], currentY, c) + } else { + r.cBuffer[ofs] = c + yB[ofs] = currentY + break + } + } } } + if currentY < yMin { + yMin = currentY + } + ofs++ } - if currentY < yMin { - yMin = currentY - } - ofs++ + ofs += w - 16 + } + + } else { + r.SolidBlocks++ + for z := 0; z < 16; z++ { + for x := 0; x < 16; x++ { + currentY := yB[ofs] + if currentY < blockY { + for y := 15; y >= 0; y-- { + if c, ok := db.Content(x, y, z); ok { + r.cBuffer[ofs] = c + currentY = blockY + int32(y) + yB[ofs] = currentY + break + } + } + } + if currentY < yMin { + yMin = currentY + } + ofs++ + } + ofs += w - 16 } - ofs += w - 16 } + r.yMin[pos] = yMin return @@ -349,13 +397,15 @@ func safeColor(x int32) uint8 { func (r *Renderer) CreateShadedImage( xOfs, zOfs, width, height int, - colors []color.RGBA, background color.RGBA) *image.RGBA { + cols *Colors, background color.RGBA) *image.RGBA { image := image.NewRGBA(image.Rect(0, 0, width, height)) pw := r.width << 4 - ofs, numCols := zOfs*pw+xOfs, int32(len(colors)) + cs := cols.Colors + + ofs, numCols := zOfs*pw+xOfs, int32(len(cs)) stride := pw - width @@ -365,42 +415,91 @@ func (r *Renderer) CreateShadedImage( pix := image.Pix - for z := height - 1; z >= 0; z-- { - for x := 0; x < width; x++ { - colIdx := r.cBuffer[ofs] - if colIdx < 0 || colIdx >= numCols { - pix[iofs] = background.R - pix[iofs+1] = background.G - pix[iofs+2] = background.B - pix[iofs+3] = 0xff - } else { - var y, y1, y2 int32 - y = r.yBuffer[ofs] - if x == 0 { - y1 = y + if r.tBuffer != nil { // Fast path for transparent images. + for z := height - 1; z >= 0; z-- { + for x := 0; x < width; x++ { + colIdx := r.cBuffer[ofs] + if colIdx < 0 || colIdx >= numCols { + pix[iofs] = background.R + pix[iofs+1] = background.G + pix[iofs+2] = background.B + pix[iofs+3] = 0xff } else { - y1 = r.yBuffer[ofs-1] + var y, y1, y2 int32 + y = r.yBuffer[ofs] + if x == 0 { + y1 = y + } else { + y1 = r.yBuffer[ofs-1] + } + if z == 0 { + y2 = y + } else { + y2 = r.yBuffer[ofs+pw] + } + d := ((y - y1) + (y - y2)) * 12 + if d > 36 { + d = 36 + } + col := cs[colIdx] + col = color.RGBA{ + R: safeColor(int32(col.R) + d), + G: safeColor(int32(col.G) + d), + B: safeColor(int32(col.B) + d), + A: 0xff} + if r.tBuffer[ofs] != nil { + col = cols.BlendColors(r.tBuffer[ofs], col, y) + } + pix[iofs] = col.R + pix[iofs+1] = col.G + pix[iofs+2] = col.B + pix[iofs+3] = col.A } - if z == 0 { - y2 = y - } else { - y2 = r.yBuffer[ofs+pw] - } - d := ((y - y1) + (y - y2)) * 12 - if d > 36 { - d = 36 - } - col := colors[colIdx] - pix[iofs] = safeColor(int32(col.R) + d) - pix[iofs+1] = safeColor(int32(col.G) + d) - pix[iofs+2] = safeColor(int32(col.B) + d) - pix[iofs+3] = 0xff + iofs += 4 + ofs++ } - iofs += 4 - ofs++ + ofs += stride + iofs -= istride + } + + } else { // Solid images. + for z := height - 1; z >= 0; z-- { + for x := 0; x < width; x++ { + colIdx := r.cBuffer[ofs] + if colIdx < 0 || colIdx >= numCols { + pix[iofs] = background.R + pix[iofs+1] = background.G + pix[iofs+2] = background.B + pix[iofs+3] = 0xff + } else { + var y, y1, y2 int32 + y = r.yBuffer[ofs] + if x == 0 { + y1 = y + } else { + y1 = r.yBuffer[ofs-1] + } + if z == 0 { + y2 = y + } else { + y2 = r.yBuffer[ofs+pw] + } + d := ((y - y1) + (y - y2)) * 12 + if d > 36 { + d = 36 + } + col := cs[colIdx] + pix[iofs] = safeColor(int32(col.R) + d) + pix[iofs+1] = safeColor(int32(col.G) + d) + pix[iofs+2] = safeColor(int32(col.B) + d) + pix[iofs+3] = 0xff + } + iofs += 4 + ofs++ + } + ofs += stride + iofs -= istride } - ofs += stride - iofs -= istride } return image }