Allow scaling a map to a smaller size while generating. (1/2, 1/4 .. 1/16)

Scaling will not average nodes across block boundaries, This implies
that the only scale factors supported are 1:2, 1:4, 1:8 and 1:16.
This commit is contained in:
Rogier 2015-02-14 14:31:25 +01:00
parent 045540e8f9
commit 5ce7ff4bce
5 changed files with 238 additions and 64 deletions

View File

@ -26,7 +26,7 @@ PixelAttributes::~PixelAttributes()
freeAttributes(); freeAttributes();
} }
void PixelAttributes::setParameters(int width, int lines, int nextY) void PixelAttributes::setParameters(int width, int lines, int nextY, int scale, bool defaultEmpty)
{ {
freeAttributes(); freeAttributes();
m_width = width + 1; // 1px gradient calculation m_width = width + 1; // 1px gradient calculation
@ -39,6 +39,7 @@ void PixelAttributes::setParameters(int width, int lines, int nextY)
m_nextY = nextY; m_nextY = nextY;
m_lastY = -1; m_lastY = -1;
m_firstUnshadedY = 0; m_firstUnshadedY = 0;
m_scale = scale;
m_pixelAttributes = new PixelAttribute *[m_lineCount]; m_pixelAttributes = new PixelAttribute *[m_lineCount];
if (!m_pixelAttributes) if (!m_pixelAttributes)
@ -52,7 +53,10 @@ void PixelAttributes::setParameters(int width, int lines, int nextY)
for (int i=0; i<m_lineCount; i++) for (int i=0; i<m_lineCount; i++)
for (int j=0; j<m_width; j++) { for (int j=0; j<m_width; j++) {
m_pixelAttributes[i][j].m_a=0; m_pixelAttributes[i][j].m_a=0;
m_pixelAttributes[i][j].next16Empty = (j - 1) % 16 == 0; if (defaultEmpty)
m_pixelAttributes[i][j].nextEmpty = (j - 1) % (16 / scale) == 0;
else
m_pixelAttributes[i][j].nextEmpty = false;
} }
} }
@ -108,13 +112,13 @@ static inline double colorSafeBounds(double color)
} }
void PixelAttributes::renderShading(bool drawAlpha) void PixelAttributes::renderShading(double emphasis, bool drawAlpha)
{ {
int y; int y;
for (y = yCoord2Line(m_firstUnshadedY); y <= yCoord2Line(m_lastY); y++) { for (y = yCoord2Line(m_firstUnshadedY); y <= yCoord2Line(m_lastY); y++) {
for (int x = 1; x < m_width; x++) { for (int x = 1; x < m_width; x++) {
if (m_pixelAttributes[y][x].next16Empty) { if (m_pixelAttributes[y][x].nextEmpty) {
x += 15; x += 16 / m_scale - 1;
continue; continue;
} }
if (!m_pixelAttributes[y][x].isNormalized()) if (!m_pixelAttributes[y][x].isNormalized())
@ -136,7 +140,7 @@ void PixelAttributes::renderShading(bool drawAlpha)
if (d > 3) { if (d > 3) {
d = 3; d = 3;
} }
d = d * 12 / 255; d = d * 12 / 255 * emphasis;
#define pixel (m_pixelAttributes[y][x]) #define pixel (m_pixelAttributes[y][x])
//PixelAttribute &pixel = m_pixelAttributes[y][x]; //PixelAttribute &pixel = m_pixelAttributes[y][x];
if (drawAlpha) if (drawAlpha)

View File

@ -27,11 +27,11 @@ public:
AlphaMixAverage = 0x04, AlphaMixAverage = 0x04,
}; };
static void setMixMode(AlphaMixingMode mode); static void setMixMode(AlphaMixingMode mode);
PixelAttribute(): next16Empty(true), m_n(0), m_h(NAN), m_t(0), m_a(0), m_r(0), m_g(0), m_b(0) {}; PixelAttribute(): nextEmpty(true), m_n(0), m_h(NAN), m_t(0), m_a(0), m_r(0), m_g(0), m_b(0) {};
// PixelAttribute(const PixelAttribute &p); // PixelAttribute(const PixelAttribute &p);
PixelAttribute(const Color &color, double height); PixelAttribute(const Color &color, double height);
PixelAttribute(const ColorEntry &entry, double height); PixelAttribute(const ColorEntry &entry, double height);
bool next16Empty; bool nextEmpty;
double h(void) const { return m_h / (m_n ? m_n : 1); } double h(void) const { return m_h / (m_n ? m_n : 1); }
double t(void) const { return m_t / (m_n ? m_n : 1); } double t(void) const { return m_t / (m_n ? m_n : 1); }
double a(void) const { return m_a / (m_n ? m_n : 1); } double a(void) const { return m_a / (m_n ? m_n : 1); }
@ -70,10 +70,10 @@ class PixelAttributes
public: public:
PixelAttributes(); PixelAttributes();
virtual ~PixelAttributes(); virtual ~PixelAttributes();
void setParameters(int width, int lines, int nextY); void setParameters(int width, int lines, int nextY, int scale, bool defaultEmpty);
void scroll(int keepY); void scroll(int keepY);
PixelAttribute &attribute(int y, int x); PixelAttribute &attribute(int y, int x);
void renderShading(bool drawAlpha); void renderShading(double emphasis, bool drawAlpha);
int getNextY(void) { return m_nextY; } int getNextY(void) { return m_nextY; }
void setLastY(int y); void setLastY(int y);
int getLastY(void) { return m_lastY; } int getLastY(void) { return m_lastY; }
@ -94,6 +94,7 @@ private:
int m_nextY; int m_nextY;
int m_lastY; int m_lastY;
int m_firstUnshadedY; int m_firstUnshadedY;
int m_scale;
}; };
inline void PixelAttributes::setLastY(int y) inline void PixelAttributes::setLastY(int y)
@ -133,13 +134,13 @@ inline PixelAttribute &PixelAttributes::attribute(int y, int x)
//} //}
inline PixelAttribute::PixelAttribute(const Color &color, double height) : inline PixelAttribute::PixelAttribute(const Color &color, double height) :
next16Empty(false), m_n(0), m_h(height), m_t(0), m_a(color.a/255.0), nextEmpty(false), m_n(0), m_h(height), m_t(0), m_a(color.a/255.0),
m_r(color.r/255.0), m_g(color.g/255.0), m_b(color.b/255.0) m_r(color.r/255.0), m_g(color.g/255.0), m_b(color.b/255.0)
{ {
} }
inline PixelAttribute::PixelAttribute(const ColorEntry &entry, double height) : inline PixelAttribute::PixelAttribute(const ColorEntry &entry, double height) :
next16Empty(false), m_n(0), m_h(height), m_t(entry.t/255.0), m_a(entry.a/255.0), nextEmpty(false), m_n(0), m_h(height), m_t(entry.t/255.0), m_a(entry.a/255.0),
m_r(entry.r/255.0), m_g(entry.g/255.0), m_b(entry.b/255.0) m_r(entry.r/255.0), m_g(entry.g/255.0), m_b(entry.b/255.0)
{ {
} }

View File

@ -6,7 +6,6 @@
* Company: LinuxOS.sk * Company: LinuxOS.sk
* ===================================================================== * =====================================================================
*/ */
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <climits> #include <climits>
@ -150,6 +149,7 @@ TileGenerator::TileGenerator():
m_backend(DEFAULT_BACKEND), m_backend(DEFAULT_BACKEND),
m_shrinkGeometry(true), m_shrinkGeometry(true),
m_blockGeometry(false), m_blockGeometry(false),
m_scaleFactor(1),
m_sqliteCacheWorldRow(false), m_sqliteCacheWorldRow(false),
m_chunkSize(0), m_chunkSize(0),
m_image(0), m_image(0),
@ -288,6 +288,11 @@ void TileGenerator::setTileCenter(int x, int y)
m_tileYCentered = true; m_tileYCentered = true;
} }
void TileGenerator::setScaleFactor(int f)
{
m_scaleFactor = f;
}
void TileGenerator::setDrawOrigin(bool drawOrigin) void TileGenerator::setDrawOrigin(bool drawOrigin)
{ {
m_drawOrigin = drawOrigin; m_drawOrigin = drawOrigin;
@ -348,8 +353,8 @@ void TileGenerator::setGeometry(const NodeCoord &corner1, const NodeCoord &corne
else { else {
m_reqZMin = (corner1.y - 15) / 16; m_reqZMin = (corner1.y - 15) / 16;
} }
m_mapXStartNodeOffset = corner1.x - m_reqXMin * 16; m_mapXStartNodeOffsetOrig = m_mapXStartNodeOffset = corner1.x - m_reqXMin * 16;
m_mapYEndNodeOffset = m_reqZMin * 16 - corner1.y; m_mapYEndNodeOffsetOrig = m_mapYEndNodeOffset = m_reqZMin * 16 - corner1.y;
if (corner2.x > 0) { if (corner2.x > 0) {
m_reqXMax = corner2.x / 16; m_reqXMax = corner2.x / 16;
@ -363,8 +368,8 @@ void TileGenerator::setGeometry(const NodeCoord &corner1, const NodeCoord &corne
else { else {
m_reqZMax = (corner2.y - 15) / 16; m_reqZMax = (corner2.y - 15) / 16;
} }
m_mapXEndNodeOffset = corner2.x - (m_reqXMax * 16 + 15); m_mapXEndNodeOffsetOrig = m_mapXEndNodeOffset = corner2.x - (m_reqXMax * 16 + 15);
m_mapYStartNodeOffset = (m_reqZMax * 16 + 15) - corner2.y; m_mapYStartNodeOffsetOrig = m_mapYStartNodeOffset = (m_reqZMax * 16 + 15) - corner2.y;
} }
void TileGenerator::setMinY(int y) void TileGenerator::setMinY(int y)
@ -437,6 +442,42 @@ void TileGenerator::setChunkSize(int size)
m_chunkSize = size; m_chunkSize = size;
} }
void TileGenerator::sanitizeParameters(void)
{
if (m_scaleFactor > 1) {
int dx0 = m_mapXStartNodeOffset % m_scaleFactor;
int dx1 = -m_mapXEndNodeOffset % m_scaleFactor;
int dy0 = m_mapYStartNodeOffset % m_scaleFactor;
int dy1 = -m_mapYEndNodeOffset % m_scaleFactor;
if (dx0 || dx1 || dy0 || dy1) {
m_mapXStartNodeOffsetOrig = m_mapXStartNodeOffset;
m_mapXEndNodeOffsetOrig = m_mapXEndNodeOffset;
m_mapYStartNodeOffsetOrig = m_mapYStartNodeOffset;
m_mapYEndNodeOffsetOrig = m_mapYEndNodeOffset;
m_mapXStartNodeOffset -= dx0;
m_mapXEndNodeOffset += dx1;
m_mapYStartNodeOffset -= dy0;
m_mapYEndNodeOffset += dy1;
std::cerr << "NOTE: rounding requested map boundaries to a multiple of the scale factor"
<< "(changes -" << dx0 << ",+" << dx1 << " x -" << dy1 << ",+" << dy0 << ")" << std::endl;
}
dx0 = dy0 = 0;
if (m_tileWidth != TILESIZE_CHUNK)
dx0 = m_tileWidth % m_scaleFactor;
if (dx0) dx0 = m_scaleFactor - dx0;
if (m_tileHeight != TILESIZE_CHUNK)
dy0 = m_tileHeight % m_scaleFactor;
if (dy0) dy0 = m_scaleFactor - dy0;
if (dx0 || dy0) {
m_tileWidth += dx0;
m_tileHeight += dy0;
std::cerr << "NOTE: rounding requested tile size up to nearest multiple of the scale factor: "
<< m_tileWidth << " x " << m_tileHeight << std::endl;
}
}
}
void TileGenerator::generate(const std::string &input, const std::string &output) void TileGenerator::generate(const std::string &input, const std::string &output)
{ {
string input_path = input; string input_path = input;
@ -445,6 +486,7 @@ void TileGenerator::generate(const std::string &input, const std::string &output
} }
openDb(input_path); openDb(input_path);
sanitizeParameters();
loadBlocks(); loadBlocks();
computeMapParameters(input); computeMapParameters(input);
createImage(); createImage();
@ -773,18 +815,20 @@ void TileGenerator::loadBlocks()
long long map_blocks; long long map_blocks;
if (verboseCoordinates >= 2) { if (verboseCoordinates >= 2) {
bool partialBlocks = (m_mapXStartNodeOffset || m_mapXEndNodeOffset || m_mapYStartNodeOffset || m_mapYEndNodeOffset); bool partialBlocks = (m_mapXStartNodeOffset || m_mapXEndNodeOffset || m_mapYStartNodeOffset || m_mapYEndNodeOffset);
if (partialBlocks || !m_blockGeometry) { bool adjustedGeom = (m_mapXStartNodeOffsetOrig != m_mapXStartNodeOffset || m_mapYEndNodeOffsetOrig != m_mapYEndNodeOffset
|| m_mapXEndNodeOffsetOrig != m_mapXEndNodeOffset || m_mapYStartNodeOffsetOrig != m_mapYStartNodeOffsetOrig);
if (partialBlocks || !m_blockGeometry || adjustedGeom) {
cout cout
<< std::setw(MESSAGE_WIDTH) << std::left << std::setw(MESSAGE_WIDTH) << std::left
<< (m_blockGeometry ? "Command-line Geometry:" : "Requested Geometry:") << (m_blockGeometry ? "Command-line Geometry:" : "Requested Geometry:")
<< std::right << std::right
<< std::setw(7) << m_reqXMin*16+m_mapXStartNodeOffset << "," << std::setw(7) << m_reqXMin*16+m_mapXStartNodeOffsetOrig << ","
<< std::setw(7) << m_reqYMin*16+m_reqYMinNode << "," << std::setw(7) << m_reqYMin*16+m_reqYMinNode << ","
<< std::setw(7) << m_reqZMin*16-m_mapYEndNodeOffset << std::setw(7) << m_reqZMin*16-m_mapYEndNodeOffsetOrig
<< " .. " << " .. "
<< std::setw(7) << m_reqXMax*16+15+m_mapXEndNodeOffset << "," << std::setw(7) << m_reqXMax*16+15+m_mapXEndNodeOffsetOrig << ","
<< std::setw(7) << m_reqYMax*16+m_reqYMaxNode << "," << std::setw(7) << m_reqYMax*16+m_reqYMaxNode << ","
<< std::setw(7) << m_reqZMax*16+15-m_mapYStartNodeOffset << std::setw(7) << m_reqZMax*16+15-m_mapYStartNodeOffsetOrig
<< " (" << " ("
<< std::setw(6) << m_reqXMin << "," << std::setw(6) << m_reqXMin << ","
<< std::setw(6) << m_reqYMin << "," << std::setw(6) << m_reqYMin << ","
@ -817,6 +861,28 @@ void TileGenerator::loadBlocks()
<< std::setw(6) << m_reqZMax << std::setw(6) << m_reqZMax
<< ")\n"; << ")\n";
} }
if (!m_blockGeometry && adjustedGeom) {
cout
<< std::setw(MESSAGE_WIDTH) << std::left
<< "Adjusted Geometry:"
<< std::right
<< std::setw(7) << m_reqXMin*16+m_mapXStartNodeOffset << ","
<< std::setw(7) << m_reqYMin*16+m_reqYMinNode << ","
<< std::setw(7) << m_reqZMin*16-m_mapYEndNodeOffset
<< " .. "
<< std::setw(7) << m_reqXMax*16+15+m_mapXEndNodeOffset << ","
<< std::setw(7) << m_reqYMax*16+m_reqYMaxNode << ","
<< std::setw(7) << m_reqZMax*16+15-m_mapYStartNodeOffset
<< " ("
<< std::setw(6) << m_reqXMin << ","
<< std::setw(6) << m_reqYMin << ","
<< std::setw(6) << m_reqZMin
<< " .. "
<< std::setw(6) << m_reqXMax << ","
<< std::setw(6) << m_reqYMax << ","
<< std::setw(6) << m_reqZMax
<< ")\n";
}
} }
if (m_blockGeometry) { if (m_blockGeometry) {
m_mapXStartNodeOffset = 0; m_mapXStartNodeOffset = 0;
@ -993,18 +1059,59 @@ void TileGenerator::loadBlocks()
#undef MESSAGE_WIDTH #undef MESSAGE_WIDTH
} }
void TileGenerator::pushPixelRows(int zPosLimit) { void TileGenerator::scalePixelRows(PixelAttributes &pixelAttributes, PixelAttributes &pixelAttributesScaled, int zPosLimit) {
if (m_shading)
m_blockPixelAttributes.renderShading(m_drawAlpha);
int y; int y;
for (y = m_blockPixelAttributes.getNextY(); y <= m_blockPixelAttributes.getLastY() && y < worldBlockZ2StoredY(m_zMin - 1) + m_mapYEndNodeOffset; y++) { for (y = pixelAttributes.getNextY(); y <= pixelAttributes.getLastY() && y < worldBlockZ2StoredY(m_zMin - 1) + m_mapYEndNodeOffset; y++) {
for (int x = m_mapXStartNodeOffset; x < worldBlockX2StoredX(m_xMax + 1) + m_mapXEndNodeOffset; x++) { for (int x = m_mapXStartNodeOffset; x < worldBlockX2StoredX(m_xMax + 1) + m_mapXEndNodeOffset; x++) {
#define pixel pixelAttributes.attribute(y, x)
//PixelAttribute &pixel = pixelAttributes.attribute(y, x);
if (pixel.nextEmpty) {
pixelAttributesScaled.attribute(y / m_scaleFactor, x/m_scaleFactor).nextEmpty = true;
x += 15;
continue;
}
#ifdef DEBUG
int mapX = x - m_mapXStartNodeOffset; int mapX = x - m_mapXStartNodeOffset;
int mapY = y - m_mapYStartNodeOffset; int mapY = y - m_mapYStartNodeOffset;
#define pixel m_blockPixelAttributes.attribute(y, x) { int ix = mapX2ImageX(mapX / m_scaleFactor); assert(ix - borderLeft() >= 0 && ix - borderLeft() - borderRight() < m_pictWidth); }
//PixelAttribute &pixel = m_blockPixelAttributes.attribute(y, x); { int iy = mapY2ImageY(mapY / m_scaleFactor); assert(iy - borderTop() >= 0 && iy - borderTop() - borderBottom() < m_pictHeight); }
if (pixel.next16Empty) { #endif
x += 15; if (pixel.is_valid() || pixel.color().to_uint())
pixelAttributesScaled.attribute(y / m_scaleFactor, x / m_scaleFactor).add(pixel);
#undef pixel
}
}
for (y = pixelAttributesScaled.getNextY(); y <= pixelAttributesScaled.getLastY(); y++) {
for (int x = m_mapXStartNodeOffset / m_scaleFactor; x < (worldBlockX2StoredX(m_xMax + 1) + m_mapXEndNodeOffset) / m_scaleFactor; x++) {
#define pixel pixelAttributesScaled.attribute(y, x)
if (pixel.nextEmpty) {
x += 16 / m_scaleFactor - 1;
continue;
}
if (pixel.is_valid() || pixel.color().to_uint())
pixel.normalize();
#undef pixel
}
}
int yLimit = worldBlockZ2StoredY(zPosLimit);
if (y <= yLimit) {
pixelAttributes.scroll(yLimit);
}
}
void TileGenerator::pushPixelRows(PixelAttributes &pixelAttributes, int zPosLimit) {
if (m_shading)
pixelAttributes.renderShading(m_scaleFactor < 3 ? 1 : 1 / sqrt(m_scaleFactor), m_drawAlpha);
int y;
for (y = pixelAttributes.getNextY(); y <= pixelAttributes.getLastY() && y < (worldBlockZ2StoredY(m_zMin - 1) + m_mapYEndNodeOffset) / m_scaleFactor; y++) {
for (int x = m_mapXStartNodeOffset / m_scaleFactor; x < (worldBlockX2StoredX(m_xMax + 1) + m_mapXEndNodeOffset) / m_scaleFactor; x++) {
int mapX = x - m_mapXStartNodeOffset / m_scaleFactor;
int mapY = y - m_mapYStartNodeOffset / m_scaleFactor;
#define pixel pixelAttributes.attribute(y, x)
//PixelAttribute &pixel = pixelAttributes.attribute(y, x);
//if (x < 2 && y < 2)
if (pixel.nextEmpty) {
x += 16 / m_scaleFactor - 1;
continue; continue;
} }
#ifdef DEBUG #ifdef DEBUG
@ -1016,9 +1123,9 @@ void TileGenerator::pushPixelRows(int zPosLimit) {
#undef pixel #undef pixel
} }
} }
int yLimit = worldBlockZ2StoredY(zPosLimit); int yLimit = worldBlockZ2StoredY(zPosLimit) / m_scaleFactor;
if (y <= yLimit) { if (y <= yLimit) {
m_blockPixelAttributes.scroll(yLimit); pixelAttributes.scroll(yLimit);
} }
} }
@ -1030,8 +1137,6 @@ void TileGenerator::computeTileParameters(
int mapEndNodeOffset, int mapEndNodeOffset,
int tileOrigin, int tileOrigin,
int tileSize, int tileSize,
// Input / Output parameters
int &pictSize,
// Output parameters // Output parameters
int &tileBorderCount, int &tileBorderCount,
int &tileMapStartOffset, int &tileMapStartOffset,
@ -1072,7 +1177,6 @@ void TileGenerator::computeTileParameters(
} }
tileMapStartOffset = (tileSize - start) % tileSize; tileMapStartOffset = (tileSize - start) % tileSize;
tileMapEndOffset = limit - ((tileBorderLimit-tileBorderStart) * tileSize); tileMapEndOffset = limit - ((tileBorderLimit-tileBorderStart) * tileSize);
pictSize += (tileBorderLimit - tileBorderStart) * m_tileBorderSize;
tileBorderCount = tileBorderLimit - tileBorderStart; tileBorderCount = tileBorderLimit - tileBorderStart;
} }
@ -1089,7 +1193,15 @@ void TileGenerator::computeMapParameters(const std::string &input)
m_storedHeight = (m_zMax - m_zMin + 1) * 16; m_storedHeight = (m_zMax - m_zMin + 1) * 16;
int mapWidth = m_storedWidth - m_mapXStartNodeOffset + m_mapXEndNodeOffset; int mapWidth = m_storedWidth - m_mapXStartNodeOffset + m_mapXEndNodeOffset;
int mapHeight = m_storedHeight - m_mapYStartNodeOffset + m_mapYEndNodeOffset; int mapHeight = m_storedHeight - m_mapYStartNodeOffset + m_mapYEndNodeOffset;
m_blockPixelAttributes.setParameters(m_storedWidth, 16, m_mapYStartNodeOffset); #ifdef DEBUG
assert(mapWidth % m_scaleFactor == 0);
assert(mapHeight % m_scaleFactor == 0);
#else
mapWidth += mapWidth % m_scaleFactor;
mapHeight += mapHeight % m_scaleFactor;
#endif
m_blockPixelAttributes.setParameters(m_storedWidth, 16, m_mapYStartNodeOffset, 1, true);
m_blockPixelAttributesScaled.setParameters(m_storedWidth / m_scaleFactor, 16 / m_scaleFactor, m_mapYStartNodeOffset / m_scaleFactor, m_scaleFactor, false);
// Set special values for origin (which depend on other paramters) // Set special values for origin (which depend on other paramters)
if (m_tileWidth) { if (m_tileWidth) {
@ -1114,6 +1226,10 @@ void TileGenerator::computeMapParameters(const std::string &input)
m_tileXOrigin -= m_tileWidth/2; m_tileXOrigin -= m_tileWidth/2;
break; break;
} }
if (m_tileXOrigin >= 0)
m_tileXOrigin -= m_tileXOrigin % m_scaleFactor;
else
m_tileXOrigin -= -m_tileXOrigin % m_scaleFactor;
} }
if (m_tileHeight) { if (m_tileHeight) {
switch (m_tileZOrigin) { switch (m_tileZOrigin) {
@ -1137,6 +1253,10 @@ void TileGenerator::computeMapParameters(const std::string &input)
m_tileZOrigin -= m_tileHeight / 2; m_tileZOrigin -= m_tileHeight / 2;
break; break;
} }
if (m_tileXOrigin >= 0)
m_tileZOrigin -= m_tileZOrigin % m_scaleFactor;
else
m_tileZOrigin -= -m_tileZOrigin % m_scaleFactor;
} }
// Compute adjustments for tiles. // Compute adjustments for tiles.
@ -1152,8 +1272,6 @@ void TileGenerator::computeMapParameters(const std::string &input)
m_mapXEndNodeOffset, m_mapXEndNodeOffset,
m_tileXOrigin, m_tileXOrigin,
m_tileWidth, m_tileWidth,
// Input / Output parameters
m_pictWidth,
// Output parameters // Output parameters
m_tileBorderXCount, m_tileBorderXCount,
m_tileMapXOffset, m_tileMapXOffset,
@ -1171,8 +1289,6 @@ void TileGenerator::computeMapParameters(const std::string &input)
-m_mapYStartNodeOffset, -m_mapYStartNodeOffset,
m_tileZOrigin, m_tileZOrigin,
m_tileHeight, m_tileHeight,
// Input / Output parameters
m_pictHeight,
// Output parameters // Output parameters
m_tileBorderYCount, m_tileBorderYCount,
tileMapYEndOffset, tileMapYEndOffset,
@ -1181,18 +1297,25 @@ void TileGenerator::computeMapParameters(const std::string &input)
false); false);
} }
m_pictWidth /= m_scaleFactor;
m_pictWidth += m_tileBorderXCount * m_tileBorderSize;
m_pictHeight /= m_scaleFactor;
m_pictHeight += m_tileBorderYCount * m_tileBorderSize;
// Print some useful messages in cases where it may not be possible to generate the image... // Print some useful messages in cases where it may not be possible to generate the image...
long long pixels = static_cast<long long>(m_pictWidth + borderLeft() + borderRight()) * (m_pictHeight + borderTop() + borderBottom()); long long pixels = static_cast<long long>(m_pictWidth + borderLeft() + borderRight()) * (m_pictHeight + borderTop() + borderBottom());
// Study the libgd code to known why the maximum is the following: // Study the libgd code to known why the maximum is the following:
long long max_pixels = INT_MAX - INT_MAX % m_pictHeight; long long max_pixels = INT_MAX - INT_MAX % m_pictHeight;
if (pixels > max_pixels) { if (pixels > max_pixels) {
cerr << "WARNING: Image will have " << pixels << " pixels; the PNG graphics library will refuse to handle more than approximately " << INT_MAX << std::endl; cerr << "WARNING: Image will have " << pixels << " pixels; the PNG graphics library will refuse to handle more than approximately " << INT_MAX << std::endl;
cerr << " (If map generation fails, consider using --scalefactor to reduce the image size by a factor 2)" << std::endl;
} }
// Estimated approximate maximum was determined by trial and error... // Estimated approximate maximum was determined by trial and error...
// (24100x24100 succeeded; 24200x24200 failed) // (24100x24100 succeeded; 24200x24200 failed)
#define ESTIMATED_MAX_PIXELS_32BIT (24100*24100L) #define ESTIMATED_MAX_PIXELS_32BIT (24100*24100L)
else if (sizeof(void *) == 4 && pixels > ESTIMATED_MAX_PIXELS_32BIT) { else if (sizeof(void *) == 4 && pixels > ESTIMATED_MAX_PIXELS_32BIT) {
cerr << "WARNING: Image will have " << pixels << " pixels; The maximum achievable on a 32-bit system is approximately " << ESTIMATED_MAX_PIXELS_32BIT << std::endl; cerr << "WARNING: Image will have " << pixels << " pixels; The maximum achievable on a 32-bit system is approximately " << ESTIMATED_MAX_PIXELS_32BIT << std::endl;
cerr << " (If map generation fails, consider using --scalefactor to reduce the image size by a factor 2 or 4)" << std::endl;
} }
#undef ESTIMATED_MAX_PIXELS_32BIT #undef ESTIMATED_MAX_PIXELS_32BIT
} }
@ -1215,9 +1338,9 @@ void TileGenerator::createImage()
if (m_tileWidth && m_tileBorderSize) { if (m_tileWidth && m_tileBorderSize) {
int borderColor = m_tileBorderColor.to_libgd(); int borderColor = m_tileBorderColor.to_libgd();
for (int i = 0; i < m_tileBorderXCount; i++) { for (int i = 0; i < m_tileBorderXCount; i++) {
int xPos = m_tileMapXOffset + i * (m_tileWidth + m_tileBorderSize); int xPos = m_tileMapXOffset / m_scaleFactor + i * (m_tileWidth / m_scaleFactor + m_tileBorderSize);
#ifdef DEBUG #ifdef DEBUG
int xPos2 = mapX2ImageX(m_tileMapXOffset + i * m_tileWidth) - borderLeft() - 1; int xPos2 = mapX2ImageX(m_tileMapXOffset / m_scaleFactor + i * m_tileWidth / m_scaleFactor) - borderLeft() - 1;
assert(xPos == xPos2); assert(xPos == xPos2);
#endif #endif
gdImageFilledRectangle(m_image, xPos + borderLeft(), borderTop(), xPos + (m_tileBorderSize-1) + borderLeft(), m_pictHeight + borderTop() - 1, borderColor); gdImageFilledRectangle(m_image, xPos + borderLeft(), borderTop(), xPos + (m_tileBorderSize-1) + borderLeft(), m_pictHeight + borderTop() - 1, borderColor);
@ -1226,9 +1349,9 @@ void TileGenerator::createImage()
if (m_tileHeight && m_tileBorderSize) { if (m_tileHeight && m_tileBorderSize) {
int borderColor = m_tileBorderColor.to_libgd(); int borderColor = m_tileBorderColor.to_libgd();
for (int i = 0; i < m_tileBorderYCount; i++) { for (int i = 0; i < m_tileBorderYCount; i++) {
int yPos = m_tileMapYOffset + i * (m_tileHeight + m_tileBorderSize); int yPos = m_tileMapYOffset / m_scaleFactor + i * (m_tileHeight / m_scaleFactor + m_tileBorderSize);
#ifdef DEBUG #ifdef DEBUG
int yPos2 = mapY2ImageY(m_tileMapYOffset + i * m_tileHeight) - borderTop() - 1; int yPos2 = mapY2ImageY(m_tileMapYOffset / m_scaleFactor + i * m_tileHeight / m_scaleFactor) - borderTop() - 1;
assert(yPos == yPos2); assert(yPos == yPos2);
#endif #endif
gdImageFilledRectangle(m_image, borderLeft(), yPos + borderTop(), m_pictWidth + borderLeft() - 1, yPos + (m_tileBorderSize-1) + borderTop(), borderColor); gdImageFilledRectangle(m_image, borderLeft(), yPos + borderTop(), m_pictWidth + borderLeft() - 1, yPos + (m_tileBorderSize-1) + borderTop(), borderColor);
@ -1350,7 +1473,14 @@ void TileGenerator::renderMap()
if (currentPos.x != pos.x || currentPos.z != pos.z) { if (currentPos.x != pos.x || currentPos.z != pos.z) {
area_rendered++; area_rendered++;
if (currentPos.z != pos.z) { if (currentPos.z != pos.z) {
pushPixelRows(pos.z); if (m_scaleFactor > 1) {
scalePixelRows(m_blockPixelAttributes, m_blockPixelAttributesScaled, pos.z);
pushPixelRows(m_blockPixelAttributesScaled, pos.z);
m_blockPixelAttributesScaled.setLastY(((m_zMax - pos.z) * 16 + 15) / m_scaleFactor);
}
else {
pushPixelRows(m_blockPixelAttributes, pos.z);
}
m_blockPixelAttributes.setLastY((m_zMax - pos.z) * 16 + 15); m_blockPixelAttributes.setLastY((m_zMax - pos.z) * 16 + 15);
if (progressIndicator) if (progressIndicator)
cout << "Processing Z-coordinate: " << std::setw(6) << pos.z*16 cout << "Processing Z-coordinate: " << std::setw(6) << pos.z*16
@ -1403,8 +1533,15 @@ void TileGenerator::renderMap()
throw(std::runtime_error("Too many block unpacking errors - bailing out")); throw(std::runtime_error("Too many block unpacking errors - bailing out"));
} }
} }
if (currentPos.z != INT_MIN) if (currentPos.z != INT_MIN) {
pushPixelRows(currentPos.z - 1); if (m_scaleFactor > 1) {
scalePixelRows(m_blockPixelAttributes, m_blockPixelAttributesScaled, currentPos.z - 1);
pushPixelRows(m_blockPixelAttributesScaled, currentPos.z - 1);
}
else {
pushPixelRows(m_blockPixelAttributes, currentPos.z - 1);
}
}
if (verboseStatistics) { if (verboseStatistics) {
cout << "Statistics" cout << "Statistics"
<< ": blocks read: " << m_db->getBlocksReadCount() << ": blocks read: " << m_db->getBlocksReadCount()
@ -1504,7 +1641,7 @@ inline void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPo
#undef pixel #undef pixel
} }
if (!rowIsEmpty) if (!rowIsEmpty)
m_blockPixelAttributes.attribute(zBegin + 15 - z,xBegin).next16Empty = false; m_blockPixelAttributes.attribute(zBegin + 15 - z,xBegin).nextEmpty = false;
} }
} }
@ -1519,7 +1656,7 @@ void TileGenerator::renderScale()
string scaleText; string scaleText;
if ((m_drawScale & DRAWSCALE_TOP)) { if ((m_drawScale & DRAWSCALE_TOP)) {
for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4) { for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4 * m_scaleFactor) {
stringstream buf1, buf2; stringstream buf1, buf2;
buf1 << i * 16; buf1 << i * 16;
buf2 << "(" << i << ")"; buf2 << "(" << i << ")";
@ -1534,7 +1671,7 @@ void TileGenerator::renderScale()
} }
if ((m_drawScale & DRAWSCALE_LEFT)) { if ((m_drawScale & DRAWSCALE_LEFT)) {
for (int i = (m_zMax / 4) * 4; i >= m_zMin; i -= 4) { for (int i = (m_zMax / 4) * 4; i >= m_zMin; i -= 4 * m_scaleFactor) {
stringstream buf1, buf2; stringstream buf1, buf2;
buf1 << i * 16; buf1 << i * 16;
buf2 << "(" << i << ")"; buf2 << "(" << i << ")";
@ -1797,33 +1934,29 @@ void TileGenerator::printUnknown()
inline int TileGenerator::mapX2ImageX(int val) const inline int TileGenerator::mapX2ImageX(int val) const
{ {
if (m_tileWidth && m_tileBorderSize) if (m_tileWidth && m_tileBorderSize)
val += ((val - m_tileMapXOffset + m_tileWidth) / m_tileWidth) * m_tileBorderSize + borderLeft(); val += ((val - m_tileMapXOffset / m_scaleFactor + m_tileWidth / m_scaleFactor) / (m_tileWidth / m_scaleFactor)) * m_tileBorderSize;
else return val + borderLeft();
val += borderLeft();
return val;
} }
// Adjust map coordinate for tiles and border // Adjust map coordinate for tiles and border
inline int TileGenerator::mapY2ImageY(int val) const inline int TileGenerator::mapY2ImageY(int val) const
{ {
if (m_tileHeight && m_tileBorderSize) if (m_tileHeight / m_scaleFactor && m_tileBorderSize)
val += ((val - m_tileMapYOffset + m_tileHeight) / m_tileHeight) * m_tileBorderSize + borderTop(); val += ((val - m_tileMapYOffset / m_scaleFactor + m_tileHeight / m_scaleFactor) / (m_tileHeight / m_scaleFactor)) * m_tileBorderSize;
else return val + borderTop();
val += borderTop();
return val;
} }
// Convert world coordinate to image coordinate // Convert world coordinate to image coordinate
inline int TileGenerator::worldX2ImageX(int val) const inline int TileGenerator::worldX2ImageX(int val) const
{ {
val = (val - m_xMin * 16) - m_mapXStartNodeOffset; val = (val - m_xMin * 16) - m_mapXStartNodeOffset;
return mapX2ImageX(val); return mapX2ImageX(val / m_scaleFactor);
} }
// Convert world coordinate to image coordinate // Convert world coordinate to image coordinate
inline int TileGenerator::worldZ2ImageY(int val) const inline int TileGenerator::worldZ2ImageY(int val) const
{ {
val = (m_zMax * 16 + 15 - val) - m_mapYStartNodeOffset; val = (m_zMax * 16 + 15 - val) - m_mapYStartNodeOffset;
return mapY2ImageY(val); return mapY2ImageY(val / m_scaleFactor);
} }

View File

@ -138,6 +138,7 @@ public:
void setTileSize(int width, int heigth); void setTileSize(int width, int heigth);
void setTileOrigin(int x, int y); void setTileOrigin(int x, int y);
void setTileCenter(int x, int y); void setTileCenter(int x, int y);
void setScaleFactor(int f);
void enableProgressIndicator(void); void enableProgressIndicator(void);
void parseNodeColorsFile(const std::string &fileName); void parseNodeColorsFile(const std::string &fileName);
void parseHeightMapNodesFile(const std::string &fileName); void parseHeightMapNodesFile(const std::string &fileName);
@ -151,6 +152,7 @@ private:
std::string getWorldDatabaseBackend(const std::string &input); std::string getWorldDatabaseBackend(const std::string &input);
int getMapChunkSize(const std::string &input); int getMapChunkSize(const std::string &input);
void openDb(const std::string &input); void openDb(const std::string &input);
void sanitizeParameters(void);
void loadBlocks(); void loadBlocks();
void createImage(); void createImage();
void computeMapParameters(const std::string &input); void computeMapParameters(const std::string &input);
@ -162,8 +164,6 @@ private:
int mapEndNodeOffset, int mapEndNodeOffset,
int tileOrigin, int tileOrigin,
int tileSize, int tileSize,
// Input / Output parameters
int &pictSize,
// Output parameters // Output parameters
int &tileBorderCount, int &tileBorderCount,
int &tileMapOffset, int &tileMapOffset,
@ -172,7 +172,8 @@ private:
bool ascending); bool ascending);
void renderMap(); void renderMap();
std::list<int> getZValueList() const; std::list<int> getZValueList() const;
void pushPixelRows(int zPosLimit); void pushPixelRows(PixelAttributes &pixelAttributes, int zPosLimit);
void scalePixelRows(PixelAttributes &pixelAttributes, PixelAttributes &pixelAttributesScaled, int zPosLimit);
void processMapBlock(const DB::Block &block); void processMapBlock(const DB::Block &block);
void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version); void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version);
void renderScale(); void renderScale();
@ -231,12 +232,14 @@ private:
std::string m_backend; std::string m_backend;
bool m_shrinkGeometry; bool m_shrinkGeometry;
bool m_blockGeometry; bool m_blockGeometry;
int m_scaleFactor;
bool m_sqliteCacheWorldRow; bool m_sqliteCacheWorldRow;
int m_chunkSize; int m_chunkSize;
DB *m_db; DB *m_db;
gdImagePtr m_image; gdImagePtr m_image;
PixelAttributes m_blockPixelAttributes; PixelAttributes m_blockPixelAttributes;
PixelAttributes m_blockPixelAttributesScaled;
int m_xMin; int m_xMin;
int m_xMax; int m_xMax;
int m_zMin; int m_zMin;
@ -257,6 +260,10 @@ private:
int m_mapYStartNodeOffset; int m_mapYStartNodeOffset;
int m_mapXEndNodeOffset; int m_mapXEndNodeOffset;
int m_mapYEndNodeOffset; int m_mapYEndNodeOffset;
int m_mapXStartNodeOffsetOrig;
int m_mapYStartNodeOffsetOrig;
int m_mapXEndNodeOffsetOrig;
int m_mapYEndNodeOffsetOrig;
int m_tileXOrigin; int m_tileXOrigin;
int m_tileZOrigin; int m_tileZOrigin;
int m_tileXCentered; int m_tileXCentered;

View File

@ -37,6 +37,7 @@ using namespace std;
#define OPT_HEIGHTMAPNODESFILE 0x8b #define OPT_HEIGHTMAPNODESFILE 0x8b
#define OPT_HEIGHTMAPCOLORSFILE 0x8c #define OPT_HEIGHTMAPCOLORSFILE 0x8c
#define OPT_DRAWHEIGHTSCALE 0x8d #define OPT_DRAWHEIGHTSCALE 0x8d
#define OPT_SCALEFACTOR 0x8e
// Will be replaced with the actual name and location of the executable (if found) // Will be replaced with the actual name and location of the executable (if found)
string executableName = "minetestmapper"; string executableName = "minetestmapper";
@ -116,6 +117,7 @@ void usage()
" --tiles <tilesize>[+<border>]|block|chunk\n" " --tiles <tilesize>[+<border>]|block|chunk\n"
" --tileorigin <x>,<y>|world|map\n" " --tileorigin <x>,<y>|world|map\n"
" --tilecenter <x>,<y>|world|map\n" " --tilecenter <x>,<y>|world|map\n"
" --scalefactor 1:<n>\n"
" --chunksize <size>\n" " --chunksize <size>\n"
" --verbose[=n]\n" " --verbose[=n]\n"
" --verbose-search-colors[=n]\n" " --verbose-search-colors[=n]\n"
@ -582,6 +584,7 @@ int main(int argc, char *argv[])
{"tileorigin", required_argument, 0, 'T'}, {"tileorigin", required_argument, 0, 'T'},
{"tilecenter", required_argument, 0, 'T'}, {"tilecenter", required_argument, 0, 'T'},
{"tilebordercolor", required_argument, 0, 'B'}, {"tilebordercolor", required_argument, 0, 'B'},
{"scalefactor", required_argument, 0, OPT_SCALEFACTOR},
{"chunksize", required_argument, 0, OPT_CHUNKSIZE}, {"chunksize", required_argument, 0, OPT_CHUNKSIZE},
{"verbose", optional_argument, 0, 'v'}, {"verbose", optional_argument, 0, 'v'},
{"verbose-search-colors", optional_argument, 0, OPT_VERBOSE_SEARCH_COLORS}, {"verbose-search-colors", optional_argument, 0, OPT_VERBOSE_SEARCH_COLORS},
@ -812,6 +815,32 @@ int main(int argc, char *argv[])
generator.setChunkSize(size); generator.setChunkSize(size);
} }
break; break;
case OPT_SCALEFACTOR: {
istringstream arg;
arg.str(optarg);
int one;
char colon;
int factor = 1;
arg >> one >> std::ws;
if (arg.fail() || one != 1) {
std::cerr << "Invalid scale factor specification (" << optarg << ") - expected: 1:<n>" << std::endl;
exit(1);
}
if (!arg.eof()) {
arg >> colon >> factor >> std::ws;
if (arg.fail() || colon != ':' || factor<0 || !arg.eof()) {
std::cerr << "Invalid scale factor specification (" << optarg << ") - expected: 1:<n>" << std::endl;
usage();
exit(1);
}
if (factor != 1 && factor != 2 && factor != 4 && factor != 8 && factor != 16) {
std::cerr << "Scale factor must be 1:1, 1:2, 1:4, 1:8 or 1:16" << std::endl;
exit(1);
}
}
generator.setScaleFactor(factor);
}
break;
case 't': { case 't': {
istringstream tilesize; istringstream tilesize;
tilesize.str(optarg); tilesize.str(optarg);