Skip to content

Commit

Permalink
New chunk based format for infinite maps (#1696)
Browse files Browse the repository at this point in the history
  • Loading branch information
ketanhwr authored and bjorn committed Aug 28, 2017
1 parent a4db8de commit 3f907f0
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 100 deletions.
41 changes: 18 additions & 23 deletions src/libtiled/gidmapper.cpp
Expand Up @@ -30,6 +30,7 @@

#include "compression.h"
#include "tile.h"
#include "tiled.h"
#include "tileset.h"

using namespace Tiled;
Expand Down Expand Up @@ -147,29 +148,20 @@ unsigned GidMapper::cellToGid(const Cell &cell) const
* without compression.
*/
QByteArray GidMapper::encodeLayerData(const TileLayer &tileLayer,
Map::LayerDataFormat format) const
Map::LayerDataFormat format,
QRect bounds) const
{
Q_ASSERT(format != Map::XML);
Q_ASSERT(format != Map::CSV);

int startX = 0;
int startY = 0;
int endX = tileLayer.width() - 1;
int endY = tileLayer.height() - 1;

if (tileLayer.map()->infinite()) {
QRect bounds = tileLayer.bounds().translated(-tileLayer.position());
startX = bounds.left();
startY = bounds.top();
endX = bounds.right();
endY = bounds.bottom();
}
if (bounds.isEmpty())
bounds = QRect(0, 0, tileLayer.width(), tileLayer.height());

QByteArray tileData;
tileData.reserve((endX - startX + 1) * (endY - startY + 1) * 4);
tileData.reserve(bounds.width() * bounds.height() * 4);

for (int y = startY; y <= endY; ++y) {
for (int x = startX; x <= endX; ++x) {
for (int y = bounds.top(); y <= bounds.bottom(); ++y) {
for (int x = bounds.left(); x <= bounds.right(); ++x) {
const unsigned gid = cellToGid(tileLayer.cellAt(x, y));
tileData.append((char) (gid));
tileData.append((char) (gid >> 8));
Expand All @@ -189,13 +181,16 @@ QByteArray GidMapper::encodeLayerData(const TileLayer &tileLayer,
GidMapper::DecodeError GidMapper::decodeLayerData(TileLayer &tileLayer,
const QByteArray &layerData,
Map::LayerDataFormat format,
int startX, int startY) const
QRect bounds) const
{
Q_ASSERT(format != Map::XML);
Q_ASSERT(format != Map::CSV);

if (bounds.isEmpty())
bounds = QRect(0, 0, tileLayer.width(), tileLayer.height());

QByteArray decodedData = QByteArray::fromBase64(layerData);
const int size = (tileLayer.width() * tileLayer.height()) * 4;
const int size = (bounds.width() * bounds.height()) * 4;

if (format == Map::Base64Gzip || format == Map::Base64Zlib)
decodedData = decompress(decodedData, size);
Expand All @@ -204,8 +199,8 @@ GidMapper::DecodeError GidMapper::decodeLayerData(TileLayer &tileLayer,
return CorruptLayerData;

const unsigned char *data = reinterpret_cast<const unsigned char*>(decodedData.constData());
int x = 0;
int y = 0;
int x = bounds.x();
int y = bounds.y();
bool ok;

for (int i = 0; i < size - 3; i += 4) {
Expand All @@ -220,11 +215,11 @@ GidMapper::DecodeError GidMapper::decodeLayerData(TileLayer &tileLayer,
return isEmpty() ? TileButNoTilesets : InvalidTile;
}

tileLayer.setCell(x + startX, y + startY, result);
tileLayer.setCell(x, y, result);

x++;
if (x == tileLayer.width()) {
x = 0;
if (x == bounds.right() + 1) {
x = bounds.x();
y++;
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/libtiled/gidmapper.h
Expand Up @@ -52,7 +52,8 @@ class TILEDSHARED_EXPORT GidMapper
unsigned cellToGid(const Cell &cell) const;

QByteArray encodeLayerData(const TileLayer &tileLayer,
Map::LayerDataFormat format) const;
Map::LayerDataFormat format,
QRect bounds = QRect()) const;

enum DecodeError {
NoError = 0,
Expand All @@ -64,7 +65,7 @@ class TILEDSHARED_EXPORT GidMapper
DecodeError decodeLayerData(TileLayer &tileLayer,
const QByteArray &layerData,
Map::LayerDataFormat format,
int startX, int startY) const;
QRect bounds = QRect()) const;

unsigned invalidTile() const;

Expand Down
90 changes: 59 additions & 31 deletions src/libtiled/mapreader.cpp
Expand Up @@ -98,16 +98,18 @@ class MapReaderPrivate
Layer *tryReadLayer();

TileLayer *readTileLayer();
void readTileLayerData(TileLayer &tileLayer, const int startX, const int startY);
void readTileLayerData(TileLayer &tileLayer);
void readTileLayerRect(TileLayer &tileLayer,
Map::LayerDataFormat layerDataFormat,
QStringRef encoding,
QRect bounds);
void decodeBinaryLayerData(TileLayer &tileLayer,
const QByteArray &data,
Map::LayerDataFormat format,
const int startX,
const int startY);
QRect bounds);
void decodeCSVLayerData(TileLayer &tileLayer,
QStringRef text,
const int startX,
const int startY);
QRect bounds);

/**
* Returns the cell for the given global tile ID. Errors are raised with
Expand Down Expand Up @@ -773,8 +775,6 @@ TileLayer *MapReaderPrivate::readTileLayer()
const int y = atts.value(QLatin1String("y")).toInt();
const int width = atts.value(QLatin1String("width")).toInt();
const int height = atts.value(QLatin1String("height")).toInt();
const int startX = atts.value(QLatin1String("startx")).toInt();
const int startY = atts.value(QLatin1String("starty")).toInt();

TileLayer *tileLayer = new TileLayer(name, x, y, width, height);
readLayerAttributes(*tileLayer, atts);
Expand All @@ -783,17 +783,15 @@ TileLayer *MapReaderPrivate::readTileLayer()
if (xml.name() == QLatin1String("properties"))
tileLayer->mergeProperties(readProperties());
else if (xml.name() == QLatin1String("data"))
readTileLayerData(*tileLayer, startX, startY);
readTileLayerData(*tileLayer);
else
readUnknownElement();
}

return tileLayer;
}

void MapReaderPrivate::readTileLayerData(TileLayer &tileLayer,
const int startX,
const int startY)
void MapReaderPrivate::readTileLayerData(TileLayer &tileLayer)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("data"));

Expand Down Expand Up @@ -825,26 +823,52 @@ void MapReaderPrivate::readTileLayerData(TileLayer &tileLayer,

mMap->setLayerDataFormat(layerDataFormat);

int x = 0;
int y = 0;
if (mMap->infinite()) {
while (xml.readNext() != QXmlStreamReader::Invalid) {
if (xml.isEndElement()) {
break;
} else if (xml.isStartElement()) {
if (xml.name() == QLatin1String("chunk")) {
const QXmlStreamAttributes atts = xml.attributes();
int x = atts.value(QLatin1String("x")).toInt();
int y = atts.value(QLatin1String("y")).toInt();
int width = atts.value(QLatin1String("width")).toInt();
int height = atts.value(QLatin1String("height")).toInt();

readTileLayerRect(tileLayer, layerDataFormat, encoding, QRect(x, y, width, height));
}
}
}
} else {
readTileLayerRect(tileLayer, layerDataFormat, encoding, QRect(0, 0, tileLayer.width(), tileLayer.height()));
}
}

void MapReaderPrivate::readTileLayerRect(TileLayer &tileLayer,
Map::LayerDataFormat layerDataFormat,
QStringRef encoding,
QRect bounds)
{
int x = bounds.x();
int y = bounds.y();

while (xml.readNext() != QXmlStreamReader::Invalid) {
if (xml.isEndElement()) {
break;
} else if (xml.isStartElement()) {
if (xml.name() == QLatin1String("tile")) {
if (y >= tileLayer.height()) {
if (y >= bounds.bottom() + 1) {
xml.raiseError(tr("Too many <tile> elements"));
continue;
}

const QXmlStreamAttributes atts = xml.attributes();
unsigned gid = atts.value(QLatin1String("gid")).toUInt();
tileLayer.setCell(x + startX, y + startY, cellForGid(gid));
tileLayer.setCell(x, y, cellForGid(gid));

x++;
if (x >= tileLayer.width()) {
x = 0;
if (x >= bounds.right() + 1) {
x = bounds.x();
y++;
}

Expand All @@ -857,10 +881,9 @@ void MapReaderPrivate::readTileLayerData(TileLayer &tileLayer,
decodeBinaryLayerData(tileLayer,
xml.text().toLatin1(),
layerDataFormat,
startX,
startY);
bounds);
} else if (encoding == QLatin1String("csv")) {
decodeCSVLayerData(tileLayer, xml.text(), startX, startY);
decodeCSVLayerData(tileLayer, xml.text(), bounds);
}
}
}
Expand All @@ -869,10 +892,11 @@ void MapReaderPrivate::readTileLayerData(TileLayer &tileLayer,
void MapReaderPrivate::decodeBinaryLayerData(TileLayer &tileLayer,
const QByteArray &data,
Map::LayerDataFormat format,
const int startX,
const int startY)
QRect bounds)
{
GidMapper::DecodeError error = mGidMapper.decodeLayerData(tileLayer, data, format, startX, startY);
GidMapper::DecodeError error;

error = mGidMapper.decodeLayerData(tileLayer, data, format, bounds);

switch (error) {
case GidMapper::CorruptLayerData:
Expand All @@ -891,30 +915,34 @@ void MapReaderPrivate::decodeBinaryLayerData(TileLayer &tileLayer,

void MapReaderPrivate::decodeCSVLayerData(TileLayer &tileLayer,
QStringRef text,
const int startX,
const int startY)
QRect bounds)
{
QString trimText = text.trimmed().toString();
QStringList tiles = trimText.split(QLatin1Char(','));

if (tiles.length() != tileLayer.width() * tileLayer.height()) {
int lengthCheck = bounds.width() * bounds.height();

if (tiles.length() != lengthCheck) {
xml.raiseError(tr("Corrupt layer data for layer '%1'")
.arg(tileLayer.name()));
return;
}

for (int y = 0; y < tileLayer.height(); y++) {
for (int x = 0; x < tileLayer.width(); x++) {
int currentTile = 0;

for (int y = bounds.top(); y <= bounds.bottom(); y++) {
for (int x = bounds.left(); x <= bounds.right(); x++) {
bool conversionOk;
const unsigned gid = tiles.at(y * tileLayer.width() + x)
.toUInt(&conversionOk);
const unsigned gid = tiles.at(currentTile++).toUInt(&conversionOk);

if (!conversionOk) {
xml.raiseError(
tr("Unable to parse tile at (%1,%2) on layer '%3'")
.arg(x + 1).arg(y + 1).arg(tileLayer.name()));
return;
}
tileLayer.setCell(x + startX, y + startY, cellForGid(gid));

tileLayer.setCell(x, y, cellForGid(gid));
}
}
}
Expand Down

0 comments on commit 3f907f0

Please sign in to comment.