Add a --tilesize option.

The produces tiled output way faster than repeatedly using --geometry
because it only reads the databse once.
This commit is contained in:
Martijn Versteegh 2018-11-14 12:34:52 +01:00 committed by Martijn Versteegh
parent 7025cfe3bf
commit fe5ba54fc9
7 changed files with 164 additions and 25 deletions

View File

@ -127,3 +127,9 @@ void Image::save(const std::string &filename)
fclose(f); fclose(f);
#endif #endif
} }
void Image::fill(Color &c)
{
gdImageFilledRectangle(m_image, 0, 0, m_width - 1 , m_height - 1, color2int(c));
}

View File

@ -98,6 +98,11 @@ backend:
geometry: geometry:
Limit area to specific geometry (*x:z+w+h* where x and z specify the lower left corner), e.g. ``--geometry -800:-800+1600+1600`` Limit area to specific geometry (*x:z+w+h* where x and z specify the lower left corner), e.g. ``--geometry -800:-800+1600+1600``
tilesize:
Don't output one big image, but output tiles of the specified size, e.g. "--tilesize 128x128". The sizes will be rounded to
a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y>
are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage.
zoom: zoom:
Apply zoom to drawn nodes by enlarging them to n*n squares, e.g. ``--zoom 4`` Apply zoom to drawn nodes by enlarging them to n*n squares, e.g. ``--zoom 4``

View File

@ -95,6 +95,8 @@ TileGenerator::TileGenerator():
m_geomY(-2048), m_geomY(-2048),
m_geomX2(2048), m_geomX2(2048),
m_geomY2(2048), m_geomY2(2048),
m_tileW(INT_MAX),
m_tileH(INT_MAX),
m_zoom(1), m_zoom(1),
m_scales(SCALE_LEFT | SCALE_TOP) m_scales(SCALE_LEFT | SCALE_TOP)
{ {
@ -190,6 +192,13 @@ void TileGenerator::setGeometry(int x, int y, int w, int h)
m_geomY2 = round_multiple_nosign(y + h, 16) / 16; m_geomY2 = round_multiple_nosign(y + h, 16) / 16;
} }
void TileGenerator::setTileSize(int w, int h)
{
m_tileW = round_multiple_nosign(w, 16) / 16;
m_tileH = round_multiple_nosign(h, 16) / 16;
}
void TileGenerator::setMinY(int y) void TileGenerator::setMinY(int y)
{ {
m_yMin = y; m_yMin = y;
@ -251,19 +260,66 @@ void TileGenerator::generate(const std::string &input, const std::string &output
} }
createImage(); createImage();
renderMap();
if (m_tileW < INT_MAX || m_tileH < INT_MAX)
{
tilePositions();
int trueXMin = m_xMin;
int trueZMin = m_zMin;
for (int x = 0; x < m_numTilesX; x++)
{
for (int y = 0; y < m_numTilesY; y++)
{
TileMap::iterator t = m_tiles.find(x + (y << 16));
m_xMin = trueXMin + x * m_tileW;
m_zMin = trueZMin + y * m_tileH;
m_xMax = m_xMin + m_tileW - 1;
m_zMax = m_zMin + m_tileH -1;
if (t != m_tiles.end() || !m_dontWriteEmpty)
{
m_image->fill(m_bgColor);
if (t != m_tiles.end())
renderMap(t->second);
if (m_drawScale) {
renderScale();
}
if (m_drawOrigin) {
renderOrigin();
}
if (m_drawPlayers) {
renderPlayers(input_path);
}
ostringstream fn;
fn << x << '_' << y << '_' << output;
writeImage(fn.str());
}
}
}
}
else
{
m_image->fill(m_bgColor);
renderMap(m_positions);
if (m_drawScale) {
renderScale();
}
if (m_drawOrigin) {
renderOrigin();
}
if (m_drawPlayers) {
renderPlayers(input_path);
}
writeImage(output);
}
closeDatabase(); closeDatabase();
if (m_drawScale) {
renderScale();
}
if (m_drawOrigin) {
renderOrigin();
}
if (m_drawPlayers) {
renderPlayers(input_path);
}
writeImage(output);
printUnknown(); printUnknown();
delete m_image;
m_image = NULL;
} }
void TileGenerator::parseColorsStream(std::istream &in) void TileGenerator::parseColorsStream(std::istream &in)
@ -380,8 +436,17 @@ void TileGenerator::createImage()
m_zMax = m_geomY2-1; m_zMax = m_geomY2-1;
} }
m_mapWidth = (m_xMax - m_xMin + 1) * 16; m_mapWidth = (m_xMax - m_xMin + 1);
m_mapHeight = (m_zMax - m_zMin + 1) * 16; m_mapHeight = (m_zMax - m_zMin + 1);
if (m_mapWidth > m_tileW)
m_mapWidth = m_tileW;
if (m_mapHeight > m_tileH)
m_mapHeight = m_tileH;
m_mapWidth *= 16;
m_mapHeight *= 16;
m_xBorder = (m_scales & SCALE_LEFT) ? scale_d : 0; m_xBorder = (m_scales & SCALE_LEFT) ? scale_d : 0;
m_yBorder = (m_scales & SCALE_TOP) ? scale_d : 0; m_yBorder = (m_scales & SCALE_TOP) ? scale_d : 0;
@ -401,15 +466,15 @@ void TileGenerator::createImage()
m_image->drawFilledRect(0, 0, image_width, image_height, m_bgColor); // Background m_image->drawFilledRect(0, 0, image_width, image_height, m_bgColor); // Background
} }
void TileGenerator::renderMap() void TileGenerator::renderMap(PositionsList &positions)
{ {
BlockDecoder blk; BlockDecoder blk;
std::list<int> zlist = getZValueList(); std::list<int> zlist = getZValueList(positions);
for (std::list<int>::iterator zPosition = zlist.begin(); zPosition != zlist.end(); ++zPosition) { for (std::list<int>::iterator zPosition = zlist.begin(); zPosition != zlist.end(); ++zPosition) {
int zPos = *zPosition; int zPos = *zPosition;
std::map<int16_t, BlockList> blocks; std::map<int16_t, BlockList> blocks;
m_db->getBlocksOnZ(blocks, zPos); m_db->getBlocksOnZ(blocks, zPos);
for (std::list<std::pair<int, int> >::const_iterator position = m_positions.begin(); position != m_positions.end(); ++position) { for (PositionsList::const_iterator position = positions.begin(); position != positions.end(); ++position) {
if (position->second != zPos) if (position->second != zPos)
continue; continue;
@ -637,9 +702,12 @@ void TileGenerator::renderPlayers(const std::string &inputPath)
{ {
PlayerAttributes players(inputPath); PlayerAttributes players(inputPath);
for (PlayerAttributes::Players::iterator player = players.begin(); player != players.end(); ++player) { for (PlayerAttributes::Players::iterator player = players.begin(); player != players.end(); ++player) {
if (player->x < m_xMin * 16 || player->x > m_xMax * 16 || if (player->x < m_xMin*16 || player->x > m_xMax * 16 ||
player->z < m_zMin * 16 || player->z > m_zMax * 16) player->z < m_zMin*16 || player->z > m_zMax * 16 )
{
continue; continue;
}
if (player->y < m_yMin || player->y > m_yMax) if (player->y < m_yMin || player->y > m_yMax)
continue; continue;
int imageX = getImageX(player->x, true), int imageX = getImageX(player->x, true),
@ -651,10 +719,10 @@ void TileGenerator::renderPlayers(const std::string &inputPath)
} }
} }
inline std::list<int> TileGenerator::getZValueList() const inline std::list<int> TileGenerator::getZValueList(PositionsList &positions) const
{ {
std::list<int> zlist; std::list<int> zlist;
for (std::list<std::pair<int, int> >::const_iterator position = m_positions.begin(); position != m_positions.end(); ++position) for (PositionsList::const_iterator position = positions.begin(); position != positions.end(); ++position)
zlist.push_back(position->second); zlist.push_back(position->second);
zlist.sort(); zlist.sort();
zlist.unique(); zlist.unique();
@ -665,8 +733,7 @@ inline std::list<int> TileGenerator::getZValueList() const
void TileGenerator::writeImage(const std::string &output) void TileGenerator::writeImage(const std::string &output)
{ {
m_image->save(output); m_image->save(output);
delete m_image; cout << "wrote image:" << output << endl;
m_image = NULL;
} }
void TileGenerator::printUnknown() void TileGenerator::printUnknown()
@ -696,3 +763,30 @@ inline void TileGenerator::setZoomed(int x, int y, Color color)
{ {
m_image->drawFilledRect(getImageX(x), getImageY(y), m_zoom, m_zoom, color); m_image->drawFilledRect(getImageX(x), getImageY(y), m_zoom, m_zoom, color);
} }
void TileGenerator::tilePositions()
{
m_numTilesX = round_multiple_nosign(m_xMax - m_xMin + 1, m_tileW) / m_tileW;
m_numTilesY = round_multiple_nosign(m_zMax - m_zMin + 1, m_tileH) / m_tileH;
for (PositionsList::iterator p = m_positions.begin(); p != m_positions.end(); p++)
{
int xtile = (p->first - m_xMin) / m_tileW;
int ytile = (p->second - m_zMin) / m_tileH;
int key = xtile + (ytile << 16);
TileMap::iterator t = m_tiles.find(key);
if (t == m_tiles.end())
{
PositionsList l;
m_tiles.insert(std::pair<int, PositionsList>(key, l));
t = m_tiles.find(key);
}
t->second.push_back(std::pair<int, int>(p->first, p->second));
}
}

View File

@ -26,6 +26,7 @@ public:
void drawFilledRect(int x, int y, int w, int h, const Color &c); void drawFilledRect(int x, int y, int w, int h, const Color &c);
void drawCircle(int x, int y, int diameter, const Color &c); void drawCircle(int x, int y, int diameter, const Color &c);
void save(const std::string &filename); void save(const std::string &filename);
void fill(Color &c);
private: private:
Image(const Image&); Image(const Image&);

View File

@ -56,6 +56,8 @@ struct BitmapThing { // 16x16 bitmap
uint16_t val[16]; uint16_t val[16];
}; };
typedef std::list<std::pair<int, int> > PositionsList;
class TileGenerator class TileGenerator
{ {
@ -63,9 +65,11 @@ private:
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
typedef std::unordered_map<std::string, ColorEntry> ColorMap; typedef std::unordered_map<std::string, ColorEntry> ColorMap;
typedef std::unordered_set<std::string> NameSet; typedef std::unordered_set<std::string> NameSet;
typedef std::unordered_map<int, PositionsList> TileMap;
#else #else
typedef std::map<std::string, ColorEntry> ColorMap; typedef std::map<std::string, ColorEntry> ColorMap;
typedef std::set<std::string> NameSet; typedef std::set<std::string> NameSet;
typedef std::map<int, PositionsList> TileMap;
#endif #endif
public: public:
@ -81,6 +85,7 @@ public:
void setDrawAlpha(bool drawAlpha); void setDrawAlpha(bool drawAlpha);
void setShading(bool shading); void setShading(bool shading);
void setGeometry(int x, int y, int w, int h); void setGeometry(int x, int y, int w, int h);
void setTileSize(int w, int h);
void setMinY(int y); void setMinY(int y);
void setMaxY(int y); void setMaxY(int y);
void parseColorsFile(const std::string &fileName); void parseColorsFile(const std::string &fileName);
@ -90,6 +95,7 @@ public:
void setZoom(int zoom); void setZoom(int zoom);
void setScales(uint flags); void setScales(uint flags);
void setDontWriteEmpty(bool f); void setDontWriteEmpty(bool f);
void tilePositions();
private: private:
void parseColorsStream(std::istream &in); void parseColorsStream(std::istream &in);
@ -97,8 +103,8 @@ private:
void closeDatabase(); void closeDatabase();
void loadBlocks(); void loadBlocks();
void createImage(); void createImage();
void renderMap(); void renderMap(PositionsList &positions);
std::list<int> getZValueList() const; std::list<int> getZValueList(PositionsList &positions) const;
void renderMapBlock(const BlockDecoder &blk, const BlockPos &pos); void renderMapBlock(const BlockDecoder &blk, const BlockPos &pos);
void renderMapBlockBottom(const BlockPos &pos); void renderMapBlockBottom(const BlockPos &pos);
void renderShading(int zPos); void renderShading(int zPos);
@ -138,9 +144,11 @@ private:
int m_geomY; int m_geomY;
int m_geomX2; int m_geomX2;
int m_geomY2; int m_geomY2;
int m_tileW;
int m_tileH;
int m_mapWidth; int m_mapWidth;
int m_mapHeight; int m_mapHeight;
std::list<std::pair<int, int> > m_positions;
ColorMap m_colorMap; ColorMap m_colorMap;
BitmapThing m_readPixels; BitmapThing m_readPixels;
BitmapThing m_readInfo; BitmapThing m_readInfo;
@ -148,6 +156,11 @@ private:
Color m_color[16][16]; Color m_color[16][16];
uint8_t m_thickness[16][16]; uint8_t m_thickness[16][16];
PositionsList m_positions;
TileMap m_tiles;
int m_numTilesX, m_numTilesY;
int m_zoom; int m_zoom;
uint m_scales; uint m_scales;
}; // class TileGenerator }; // class TileGenerator

View File

@ -29,6 +29,7 @@ void usage()
" --max-y <y>\n" " --max-y <y>\n"
" --backend <backend>\n" " --backend <backend>\n"
" --geometry x:y+w+h\n" " --geometry x:y+w+h\n"
" --tilesize wxh\n"
" --extent\n" " --extent\n"
" --zoom <zoomlevel>\n" " --zoom <zoomlevel>\n"
" --colors <colors.txt>\n" " --colors <colors.txt>\n"
@ -82,6 +83,7 @@ int main(int argc, char *argv[])
{"noshading", no_argument, 0, 'H'}, {"noshading", no_argument, 0, 'H'},
{"backend", required_argument, 0, 'd'}, {"backend", required_argument, 0, 'd'},
{"geometry", required_argument, 0, 'g'}, {"geometry", required_argument, 0, 'g'},
{"tilesize", required_argument, 0, 't'},
{"extent", no_argument, 0, 'E'}, {"extent", no_argument, 0, 'E'},
{"min-y", required_argument, 0, 'a'}, {"min-y", required_argument, 0, 'a'},
{"max-y", required_argument, 0, 'c'}, {"max-y", required_argument, 0, 'c'},
@ -174,6 +176,18 @@ int main(int argc, char *argv[])
generator.setGeometry(x, y, w, h); generator.setGeometry(x, y, w, h);
} }
break; break;
case 't': {
std::istringstream geometry(optarg);
int w, h;
char c;
geometry >> w >> c >> h;
if (geometry.fail() || c != 'x' || w < 1 || h < 1) {
usage();
exit(1);
}
generator.setTileSize(w, h);
}
break;
case 'f': { case 'f': {
uint flags = 0; uint flags = 0;
if(strchr(optarg, 't') != NULL) if(strchr(optarg, 't') != NULL)

View File

@ -76,6 +76,12 @@ Use specific map backend; supported: *sqlite3*, *leveldb*, *redis*, *postgresql*
.BR \-\-geometry " " \fIgeometry\fR .BR \-\-geometry " " \fIgeometry\fR
Limit area to specific geometry (*x:y+w+h* where x and y specify the lower left corner), e.g. "--geometry -800:-800+1600+1600" Limit area to specific geometry (*x:y+w+h* where x and y specify the lower left corner), e.g. "--geometry -800:-800+1600+1600"
.TP
.BR \-\-tilesize " " \fItilesize\fR
Don't output one big image, but output tiles of the specified size, e.g. "--tilesize 128x128". The sizes will be rounded to
a multiple of 16. The filenames will be created in the form <x>_<y>_<filename>, where <x> and <y>
are the tile numbers and <filename> is the name specified with -o. Skip empty tiles by also specifying --noemptyimage.
.TP .TP
.BR \-\-extent " " \fIextent\fR .BR \-\-extent " " \fIextent\fR
Dont render the image, just print the extent of the map that would be generated, in the same format as the geometry above. Dont render the image, just print the extent of the map that would be generated, in the same format as the geometry above.