This commit is contained in:
Moved everything over to gitlab 2019-10-17 15:17:38 +00:00 committed by GitHub
commit 0c80bd7c85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 213 additions and 40 deletions

View File

@ -10,11 +10,16 @@
#include "Image.h" #include "Image.h"
#ifndef NDEBUG #ifndef NDEBUG
#define SIZECHECK(x, y) check_bounds((x), (y), m_width, m_height) // allow x,y to be 256 pixels outside of the image.
// this allows stuff like the playername labels to be drawn somewhat outside of the
// image (relying on gd's built in clipping) while still catching probable
// buggy draw operations
#define SIZECHECK_FUZZY(x, y) check_bounds((x), (y), m_width, m_height, 256)
#else #else
#define SIZECHECK(x, y) do {} while(0) #define SIZECHECK_FUZZY(x, y) do {} while(0)
#endif #endif
// ARGB but with inverted alpha // ARGB but with inverted alpha
static inline int color2int(Color c) static inline int color2int(Color c)
@ -35,18 +40,18 @@ static inline Color int2color(int c)
return c2; return c2;
} }
static inline void check_bounds(int x, int y, int width, int height) static inline void check_bounds(int x, int y, int width, int height, int border)
{ {
if(x < 0 || x >= width) { if(x < -border || x >= width + border) {
std::ostringstream oss; std::ostringstream oss;
oss << "Access outside image bounds (x), 0 < " oss << "Access outside image bounds (x), 0 < "
<< x << " < " << width << " is false."; << x << " < " << width << " is false.";
throw std::out_of_range(oss.str()); throw std::out_of_range(oss.str());
} }
if(y < 0 || y >= height) { if(y < -border || y >= height + border) {
std::ostringstream oss; std::ostringstream oss;
oss << "Access outside image bounds (y), 0 < " oss << "Access outside image bounds (y)," << -border << " 0 < "
<< y << " < " << height << " is false."; << y << " < " << height+border << " is false.";
throw std::out_of_range(oss.str()); throw std::out_of_range(oss.str());
} }
} }
@ -65,39 +70,39 @@ Image::~Image()
void Image::setPixel(int x, int y, const Color &c) void Image::setPixel(int x, int y, const Color &c)
{ {
SIZECHECK(x, y); SIZECHECK_FUZZY(x, y);
m_image->tpixels[y][x] = color2int(c); m_image->tpixels[y][x] = color2int(c);
} }
Color Image::getPixel(int x, int y) Color Image::getPixel(int x, int y)
{ {
SIZECHECK(x, y); SIZECHECK_FUZZY(x, y);
return int2color(m_image->tpixels[y][x]); return int2color(m_image->tpixels[y][x]);
} }
void Image::drawLine(int x1, int y1, int x2, int y2, const Color &c) void Image::drawLine(int x1, int y1, int x2, int y2, const Color &c)
{ {
SIZECHECK(x1, y1); SIZECHECK_FUZZY(x1, y1);
SIZECHECK(x2, y2); SIZECHECK_FUZZY(x2, y2);
gdImageLine(m_image, x1, y1, x2, y2, color2int(c)); gdImageLine(m_image, x1, y1, x2, y2, color2int(c));
} }
void Image::drawText(int x, int y, const std::string &s, const Color &c) void Image::drawText(int x, int y, const std::string &s, const Color &c)
{ {
SIZECHECK(x, y); SIZECHECK_FUZZY(x, y);
gdImageString(m_image, gdFontGetMediumBold(), x, y, (unsigned char*) s.c_str(), color2int(c)); gdImageString(m_image, gdFontGetMediumBold(), x, y, (unsigned char*) s.c_str(), color2int(c));
} }
void Image::drawFilledRect(int x, int y, int w, int h, const Color &c) void Image::drawFilledRect(int x, int y, int w, int h, const Color &c)
{ {
SIZECHECK(x, y); SIZECHECK_FUZZY(x, y);
SIZECHECK(x + w - 1, y + h - 1); SIZECHECK_FUZZY(x + w - 1, y + h - 1);
gdImageFilledRectangle(m_image, x, y, x + w - 1, y + h - 1, color2int(c)); gdImageFilledRectangle(m_image, x, y, x + w - 1, y + h - 1, color2int(c));
} }
void Image::drawCircle(int x, int y, int diameter, const Color &c) void Image::drawCircle(int x, int y, int diameter, const Color &c)
{ {
SIZECHECK(x, y); SIZECHECK_FUZZY(x, y);
gdImageArc(m_image, x, y, diameter, diameter, 0, 360, color2int(c)); gdImageArc(m_image, x, y, diameter, diameter, 0, 360, color2int(c));
} }
@ -122,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,95 @@ void TileGenerator::generate(const std::string &input, const std::string &output
} }
createImage(); createImage();
renderMap();
if (m_tileW < INT_MAX || m_tileH < INT_MAX)
{
m_xMin = round_multiple_nosign(m_xMin, m_tileW);
m_zMin = round_multiple_nosign(m_zMin, m_tileH);
int minTileX = m_xMin / m_tileW;
int minTileY = m_zMin / m_tileH;
sortPositionsIntoTiles();
int trueXMin = m_xMin;
int trueZMin = m_zMin;
// write info about the number of tiles and the tile sizes to a text file
// which can be used by another utility to generate zoom pyramids and/or
// add map annotations for a specific viewer.
std::ostringstream mfn;
mfn << "metadata_" << output << ".txt";
std::ofstream mf;
mf.open(mfn.str().c_str());
if (mf.is_open())
{
mf << "BaseName: " << output << std::endl;
mf << "NumTiles: " << m_numTilesX << " " << m_numTilesY << std::endl;
mf << "MinTile: " << minTileX << " " << minTileY << std::endl;
mf << "TileSize: " << (m_tileW*16) << " " << (m_tileH*16) << std::endl;
mf << "Zoom: " << m_zoom << std::endl;
mf.close();
}
else
{
std::cerr << "Warning: could not write to '" << mfn.str() << "'!" << std::endl;
}
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 << "tile_" << (x + minTileX) << '_' << (y + minTileY) << '_' << 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 +465,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_tileW < INT_MAX)
m_mapWidth = m_tileW;
if (m_tileH < INT_MAX)
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 +495,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 +731,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 +748,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 +762,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 +792,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::sortPositionsIntoTiles()
{
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 sortPositionsIntoTiles();
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
Don't render the image, just print the extent of the map that would be generated, in the same format as the geometry above. Don't render the image, just print the extent of the map that would be generated, in the same format as the geometry above.