Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for .world files in tmxrasterizer #2067

Merged
merged 5 commits into from
Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/libtiled/worldmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,14 @@ std::unique_ptr<World> WorldManager::privateLoadWorld(const QString &fileName,
/**
* Loads the world with the given \a fileName.
*
* \returns whether the world was loaded succesfully, optionally setting
* \returns the world if it was loaded succesfully, optionally setting
* \a errorString when not.
*/
bool WorldManager::loadWorld(const QString &fileName, QString *errorString)
World *WorldManager::loadWorld(const QString &fileName, QString *errorString)
{
auto world = privateLoadWorld(fileName, errorString);
if (!world)
return false;
return nullptr;

if (mWorlds.contains(fileName))
delete mWorlds.take(fileName);
Expand All @@ -189,7 +189,7 @@ bool WorldManager::loadWorld(const QString &fileName, QString *errorString)
mWorlds.insert(fileName, world.release());
emit worldsChanged();

return true;
return mWorlds.value(fileName);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/libtiled/worldmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class TILEDSHARED_EXPORT WorldManager : public QObject
static WorldManager &instance();
static void deleteInstance();

bool loadWorld(const QString &fileName, QString *errorString = nullptr);
World *loadWorld(const QString &fileName, QString *errorString = nullptr);
void unloadWorld(const QString &fileName);

const QMap<QString, World*> &worlds() const { return mWorlds; }
Expand Down
2 changes: 1 addition & 1 deletion src/tmxrasterizer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ int main(int argc, char *argv[])
app.setApplicationVersion(QLatin1String("1.0"));

QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::translate("main", "Renders a Tiled map (TMX format) to an image."));
parser.setApplicationDescription(QCoreApplication::translate("main", "Renders a Tiled map (TMX format) or a World map (WORLD format) to an image."));
parser.addHelpOption();
parser.addVersionOption();
parser.addOptions({
Expand Down
188 changes: 141 additions & 47 deletions src/tmxrasterizer/tmxrasterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "orthogonalrenderer.h"
#include "staggeredrenderer.h"
#include "tilelayer.h"
#include "worldmanager.h"

#include <QDebug>
#include <QImageWriter>
Expand All @@ -45,6 +46,21 @@

using namespace Tiled;

static std::unique_ptr<MapRenderer> createRenderer(Map &map)
{
switch (map.orientation()) {
case Map::Isometric:
return std::unique_ptr<MapRenderer>(new IsometricRenderer(&map));
case Map::Staggered:
return std::unique_ptr<MapRenderer>(new StaggeredRenderer(&map));
case Map::Hexagonal:
return std::unique_ptr<MapRenderer>(new HexagonalRenderer(&map));
case Map::Orthogonal:
default:
return std::unique_ptr<MapRenderer>(new OrthogonalRenderer(&map));
}
}

TmxRasterizer::TmxRasterizer():
mScale(1.0),
mTileSize(0),
Expand All @@ -55,6 +71,34 @@ TmxRasterizer::TmxRasterizer():
{
}

void TmxRasterizer::drawMapLayers(MapRenderer &renderer,
QPainter &painter,
Map &map,
QPoint mapOffset) const
{
// Perform a similar rendering than found in exportasimagedialog.cpp
LayerIterator iterator(&map);
while (const Layer *layer = iterator.next()) {
if (!shouldDrawLayer(layer))
continue;

const auto offset = layer->totalOffset() + mapOffset;
painter.setOpacity(layer->effectiveOpacity());
painter.translate(offset);

const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(layer);
const ImageLayer *imageLayer = dynamic_cast<const ImageLayer*>(layer);

if (tileLayer) {
renderer.drawTileLayer(&painter, tileLayer);
} else if (imageLayer) {
renderer.drawImageLayer(&painter, imageLayer);
}

painter.translate(-offset);
}
}

bool TmxRasterizer::shouldDrawLayer(const Layer *layer) const
{
if (layer->isObjectGroup() || layer->isGroupLayer())
Expand All @@ -69,8 +113,17 @@ bool TmxRasterizer::shouldDrawLayer(const Layer *layer) const
return !layer->isHidden();
}

int TmxRasterizer::render(const QString &mapFileName,
int TmxRasterizer::render(const QString &fileName,
const QString &imageFileName)
{
if (fileName.endsWith(".world", Qt::CaseInsensitive))
return renderWorld(fileName, imageFileName);
else
return renderMap(fileName, imageFileName);
}

int TmxRasterizer::renderMap(const QString &mapFileName,
const QString &imageFileName)
{
MapReader reader;
std::unique_ptr<Map> map { reader.readMap(mapFileName) };
Expand All @@ -81,36 +134,19 @@ int TmxRasterizer::render(const QString &mapFileName,
return 1;
}

std::unique_ptr<MapRenderer> renderer;

switch (map->orientation()) {
case Map::Isometric:
renderer.reset(new IsometricRenderer(map.get()));
break;
case Map::Staggered:
renderer.reset(new StaggeredRenderer(map.get()));
break;
case Map::Hexagonal:
renderer.reset(new HexagonalRenderer(map.get()));
break;
case Map::Orthogonal:
default:
renderer.reset(new OrthogonalRenderer(map.get()));
break;
}

std::unique_ptr<MapRenderer> renderer = createRenderer(*map);
QRect mapBoundingRect = renderer->mapBoundingRect();
QSize mapSize = mapBoundingRect.size();
QPoint mapOffset = mapBoundingRect.topLeft();
qreal xScale, yScale;

if (mSize > 0) {
xScale = (qreal) mSize / mapSize.width();
yScale = (qreal) mSize / mapSize.height();
xScale = static_cast<qreal>(mSize) / mapSize.width();
yScale = static_cast<qreal>(mSize) / mapSize.height();
xScale = yScale = qMin(1.0, qMin(xScale, yScale));
} else if (mTileSize > 0) {
xScale = (qreal) mTileSize / map->tileWidth();
yScale = (qreal) mTileSize / map->tileHeight();
xScale = static_cast<qreal>(mTileSize) / map->tileWidth();
yScale = static_cast<qreal>(mTileSize) / map->tileHeight();
} else {
xScale = yScale = mScale;
}
Expand All @@ -133,32 +169,15 @@ int TmxRasterizer::render(const QString &mapFileName,
painter.translate(margins.left(), margins.top());
painter.translate(-mapOffset);

// Perform a similar rendering than found in exportasimagedialog.cpp
LayerIterator iterator(map.get());
while (const Layer *layer = iterator.next()) {
if (!shouldDrawLayer(layer))
continue;

const auto offset = layer->totalOffset();

painter.setOpacity(layer->effectiveOpacity());
painter.translate(offset);

const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(layer);
const ImageLayer *imageLayer = dynamic_cast<const ImageLayer*>(layer);

if (tileLayer) {
renderer->drawTileLayer(&painter, tileLayer);
} else if (imageLayer) {
renderer->drawImageLayer(&painter, imageLayer);
}

painter.translate(-offset);
}

drawMapLayers(*renderer, painter, *map);
map.reset();
return saveImage(imageFileName, image);
}


// Save image
int TmxRasterizer::saveImage(const QString &imageFileName,
const QImage &image) const
{
QImageWriter imageWriter(imageFileName);

if (!imageWriter.canWrite())
Expand All @@ -173,3 +192,78 @@ int TmxRasterizer::render(const QString &mapFileName,

return 0;
}

int TmxRasterizer::renderWorld(const QString &worldFileName,
const QString &imageFileName)
{
WorldManager &worldManager = WorldManager::instance();
QString errorString;
const World *world = worldManager.loadWorld(worldFileName, &errorString);
if (!world) {
qWarning("Error loading the world file \"%s\":\n%s",
qUtf8Printable(worldFileName),
qUtf8Printable(errorString));
return 1;
}

auto const maps = world->allMaps();
RainingChain marked this conversation as resolved.
Show resolved Hide resolved
if (maps.isEmpty()) {
qWarning("Error: The world file to rasterize contains no maps : \"%s\"",
qUtf8Printable(worldFileName));
return 1;
}
QRect worldBoundingRect;
MapReader reader;
for (const World::MapEntry &mapEntry : maps) {
std::unique_ptr<Map> map { reader.readMap(mapEntry.fileName) };
if (!map) {
qWarning("Error while reading \"%s\":\n%s",
qUtf8Printable(mapEntry.fileName),
qUtf8Printable(reader.errorString()));
continue;
}
std::unique_ptr<MapRenderer> renderer = createRenderer(*map);
QRect mapBoundingRect = renderer->mapBoundingRect();
mapBoundingRect.translate(mapEntry.rect.topLeft());

worldBoundingRect = worldBoundingRect.united(mapBoundingRect);
}

QSize worldSize = worldBoundingRect.size();
qreal xScale, yScale;
if (mSize > 0) {
xScale = static_cast<qreal>(mSize) / worldSize.width();
yScale = static_cast<qreal>(mSize) / worldSize.height();
xScale = yScale = qMin(1.0, qMin(xScale, yScale));
} else {
xScale = yScale = mScale;
}

worldSize.rwidth() *= xScale;
worldSize.rheight() *= yScale;
QImage image(worldSize, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);

painter.setRenderHint(QPainter::Antialiasing, mUseAntiAliasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform, mSmoothImages);
painter.setTransform(QTransform::fromScale(xScale, yScale));

painter.translate(-worldBoundingRect.topLeft());

for (const World::MapEntry &mapEntry : maps) {
MapReader reader;
std::unique_ptr<Map> map { reader.readMap(mapEntry.fileName) };
if (!map) {
qWarning("Error while reading \"%s\":\n%s",
qUtf8Printable(mapEntry.fileName),
qUtf8Printable(reader.errorString()));
return 1;
}

std::unique_ptr<MapRenderer> renderer = createRenderer(*map);
drawMapLayers(*renderer, painter, *map, mapEntry.rect.topLeft());
}

return saveImage(imageFileName, image);
}
13 changes: 11 additions & 2 deletions src/tmxrasterizer/tmxrasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@

#include "layer.h"

#include "map.h"
#include "mapreader.h"
#include <QString>
#include <QStringList>

using namespace Tiled;

class QImage;
class QPainter;

class TmxRasterizer
{

Expand All @@ -46,7 +51,7 @@ class TmxRasterizer
int size() const { return mSize; }
bool useAntiAliasing() const { return mUseAntiAliasing; }
bool smoothImages() const { return mSmoothImages; }
bool IgnoreVisibility() const { return mIgnoreVisibility; }
bool ignoreVisibility() const { return mIgnoreVisibility; }

void setScale(qreal scale) { mScale = scale; }
void setTileSize(int tileSize) { mTileSize = tileSize; }
Expand All @@ -57,7 +62,7 @@ class TmxRasterizer

void setLayersToHide(QStringList layersToHide) { mLayersToHide = layersToHide; }

int render(const QString &mapFileName, const QString &imageFileName);
int render(const QString &fileName, const QString &imageFileName);

private:
qreal mScale;
Expand All @@ -68,5 +73,9 @@ class TmxRasterizer
bool mIgnoreVisibility;
QStringList mLayersToHide;

void drawMapLayers(MapRenderer &renderer, QPainter &painter, Map &map, QPoint mapOffset = QPoint(0, 0)) const;
int renderMap(const QString &mapFileName, const QString &imageFileName);
int renderWorld(const QString &worldFileName, const QString &imageFileName);
int saveImage(const QString &imageFileName, const QImage &image) const;
bool shouldDrawLayer(const Layer *layer) const;
};