diff --git a/examples/isometric_grass_and_water.tmx b/examples/isometric_grass_and_water.tmx index e9a896dad8..93cfc52c76 100644 --- a/examples/isometric_grass_and_water.tmx +++ b/examples/isometric_grass_and_water.tmx @@ -2,6 +2,7 @@ + diff --git a/examples/perspective_walls.tmx b/examples/perspective_walls.tmx index bd4d2b25f7..858eea843f 100644 --- a/examples/perspective_walls.tmx +++ b/examples/perspective_walls.tmx @@ -1,5 +1,5 @@ - + diff --git a/examples/perspective_walls.tsx b/examples/perspective_walls.tsx index 45d763d57a..98cdbec615 100644 --- a/examples/perspective_walls.tsx +++ b/examples/perspective_walls.tsx @@ -1,5 +1,6 @@ + diff --git a/src/libtiled/isometricrenderer.cpp b/src/libtiled/isometricrenderer.cpp index 7031dec1b2..c846667e02 100644 --- a/src/libtiled/isometricrenderer.cpp +++ b/src/libtiled/isometricrenderer.cpp @@ -32,6 +32,7 @@ #include "mapobject.h" #include "tile.h" #include "tilelayer.h" +#include "tileset.h" #include @@ -164,10 +165,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter, if (rect.isNull()) rect = boundingRect(layer->bounds()); - const QSize maxTileSize = layer->maxTileSize(); - const int extraWidth = maxTileSize.width() - tileWidth; - const int extraHeight = maxTileSize.height() - tileHeight; - rect.adjust(-extraWidth, 0, 0, extraHeight); + QMargins drawMargins = layer->drawMargins(); + drawMargins.setTop(drawMargins.top() - tileHeight); + drawMargins.setRight(drawMargins.right() - tileWidth); + + rect.adjust(-drawMargins.right(), + -drawMargins.bottom(), + drawMargins.left(), + drawMargins.top()); // Determine the tile and pixel coordinates to start at QPointF tilePos = pixelToTileCoords(rect.x(), rect.y()); @@ -214,13 +219,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter, const Cell &cell = layer->cellAt(columnItr); if (!cell.isEmpty()) { const QPixmap &img = cell.tile->image(); + const QPoint offset = cell.tile->tileset()->tileOffset(); qreal m11 = 1; // Horizontal scaling factor qreal m12 = 0; // Vertical shearing factor qreal m21 = 0; // Horizontal shearing factor qreal m22 = 1; // Vertical scaling factor - qreal dx = x; - qreal dy = y - img.height(); + qreal dx = offset.x() + x; + qreal dy = offset.y() + y - img.height(); if (cell.flippedDiagonally) { // Use shearing to swap the X/Y axis diff --git a/src/libtiled/map.cpp b/src/libtiled/map.cpp index 10381b593d..a2393564b4 100644 --- a/src/libtiled/map.cpp +++ b/src/libtiled/map.cpp @@ -43,8 +43,7 @@ Map::Map(Orientation orientation, mWidth(width), mHeight(height), mTileWidth(tileWidth), - mTileHeight(tileHeight), - mMaxTileSize(tileWidth, tileHeight) + mTileHeight(tileHeight) { } @@ -53,12 +52,25 @@ Map::~Map() qDeleteAll(mLayers); } -void Map::adjustMaxTileSize(const QSize &size) +static QMargins maxMargins(const QMargins &a, + const QMargins &b) { - if (size.width() > mMaxTileSize.width()) - mMaxTileSize.setWidth(size.width()); - if (size.height() > mMaxTileSize.height()) - mMaxTileSize.setHeight(size.height()); + return QMargins(qMax(a.left(), b.left()), + qMax(a.top(), b.top()), + qMax(a.right(), b.right()), + qMax(a.bottom(), b.bottom())); +} + +void Map::adjustDrawMargins(const QMargins &margins) +{ + // The TileLayer includes the maximum tile size in its draw margins. So + // we need to subtract the tile size of the map, since that part does not + // contribute to additional margin. + mDrawMargins = maxMargins(QMargins(margins.left(), + margins.top() - mTileHeight, + margins.right() - mTileWidth, + margins.bottom()), + mDrawMargins); } int Map::tileLayerCount() const @@ -105,7 +117,7 @@ void Map::adoptLayer(Layer *layer) layer->setMap(this); if (TileLayer *tileLayer = dynamic_cast(layer)) - adjustMaxTileSize(tileLayer->maxTileSize()); + adjustDrawMargins(tileLayer->drawMargins()); } Layer *Map::takeLayerAt(int index) @@ -158,7 +170,7 @@ bool Map::isTilesetUsed(Tileset *tileset) const Map *Map::clone() const { Map *o = new Map(mOrientation, mWidth, mHeight, mTileWidth, mTileHeight); - o->mMaxTileSize = mMaxTileSize; + o->mDrawMargins = mDrawMargins; foreach (const Layer *layer, mLayers) o->addLayer(layer->clone()); o->mTilesets = mTilesets; diff --git a/src/libtiled/map.h b/src/libtiled/map.h index 6e1512f2c4..349e6fea39 100644 --- a/src/libtiled/map.h +++ b/src/libtiled/map.h @@ -34,6 +34,7 @@ #include "object.h" #include +#include #include namespace Tiled { @@ -128,26 +129,18 @@ class TILEDSHARED_EXPORT Map : public Object int tileHeight() const { return mTileHeight; } /** - * Returns the maximum tile size used by tile layers of this map. - * @see TileLayer::extraTileSize() + * Adjusts the draw margins to be at least as big as the given margins. + * Called from tile layers when their tiles change. */ - QSize maxTileSize() const { return mMaxTileSize; } + void adjustDrawMargins(const QMargins &margins); /** - * Adjusts the maximum tile size to be at least as much as the given - * size. Called from tile layers when their maximum tile size increases. - */ - void adjustMaxTileSize(const QSize &size); - - /** - * Convenience method for getting the extra tile size, which is the number - * of pixels that tiles may extend beyond the size of the tile grid. + * Returns the margins that have to be taken into account when figuring + * out which part of the map to repaint after changing some tiles. * - * @see maxTileSize() + * @see TileLayer::drawMargins */ - QSize extraTileSize() const - { return QSize(mMaxTileSize.width() - mTileWidth, - mMaxTileSize.height() - mTileHeight); } + QMargins drawMargins() const { return mDrawMargins; } /** * Returns the number of layers of this map. @@ -259,7 +252,7 @@ class TILEDSHARED_EXPORT Map : public Object int mHeight; int mTileWidth; int mTileHeight; - QSize mMaxTileSize; + QMargins mDrawMargins; QList mLayers; QList mTilesets; }; diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index fd994a1336..53f209e184 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -277,14 +277,21 @@ Tileset *MapReaderPrivate::readTileset() tileSpacing, margin); while (xml.readNextStartElement()) { - if (xml.name() == "tile") + if (xml.name() == "tile") { readTilesetTile(tileset); - else if (xml.name() == "properties") + } else if (xml.name() == "tileoffset") { + const QXmlStreamAttributes oa = xml.attributes(); + int x = oa.value(QLatin1String("x")).toString().toInt(); + int y = oa.value(QLatin1String("y")).toString().toInt(); + tileset->setTileOffset(QPoint(x, y)); + xml.skipCurrentElement(); + } else if (xml.name() == "properties") { tileset->mergeProperties(readProperties()); - else if (xml.name() == "image") + } else if (xml.name() == "image") { readTilesetImage(tileset); - else + } else { readUnknownElement(); + } } } } else { // External tileset diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index bde9f7f433..ca9231322e 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -234,6 +234,14 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset *tileset, if (margin != 0) w.writeAttribute(QLatin1String("margin"), QString::number(margin)); + const QPoint offset = tileset->tileOffset(); + if (!offset.isNull()) { + w.writeStartElement(QLatin1String("tileoffset")); + w.writeAttribute(QLatin1String("x"), QString::number(offset.x())); + w.writeAttribute(QLatin1String("y"), QString::number(offset.y())); + w.writeEndElement(); + } + // Write the tileset properties writeProperties(w, tileset->properties()); diff --git a/src/libtiled/orthogonalrenderer.cpp b/src/libtiled/orthogonalrenderer.cpp index 1186c4f480..9dad71fb8d 100644 --- a/src/libtiled/orthogonalrenderer.cpp +++ b/src/libtiled/orthogonalrenderer.cpp @@ -32,6 +32,7 @@ #include "mapobject.h" #include "tile.h" #include "tilelayer.h" +#include "tileset.h" #include @@ -192,10 +193,15 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter, int endY = layer->height(); if (!exposed.isNull()) { - const QSize maxTileSize = layer->maxTileSize(); - const int extraWidth = maxTileSize.width() - tileWidth; - const int extraHeight = maxTileSize.height() - tileHeight; - QRectF rect = exposed.adjusted(-extraWidth, 0, 0, extraHeight); + QMargins drawMargins = layer->drawMargins(); + drawMargins.setTop(drawMargins.top() - tileHeight); + drawMargins.setRight(drawMargins.right() - tileWidth); + + QRectF rect = exposed.adjusted(-drawMargins.right(), + -drawMargins.bottom(), + drawMargins.left(), + drawMargins.top()); + rect.translate(-layerPos); startX = qMax((int) rect.x() / tileWidth, 0); @@ -213,13 +219,14 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter, continue; const QPixmap &img = cell.tile->image(); + const QPoint offset = cell.tile->tileset()->tileOffset(); qreal m11 = 1; // Horizontal scaling factor qreal m12 = 0; // Vertical shearing factor qreal m21 = 0; // Horizontal shearing factor qreal m22 = 1; // Vertical scaling factor - qreal dx = x * tileWidth; - qreal dy = (y + 1) * tileHeight - img.height(); + qreal dx = offset.x() + x * tileWidth; + qreal dy = offset.y() + (y + 1) * tileHeight - img.height(); if (cell.flippedDiagonally) { // Use shearing to swap the X/Y axis diff --git a/src/libtiled/tilelayer.cpp b/src/libtiled/tilelayer.cpp index 4234c82e93..ea30f5fabd 100644 --- a/src/libtiled/tilelayer.cpp +++ b/src/libtiled/tilelayer.cpp @@ -67,6 +67,22 @@ QRegion TileLayer::region() const return region; } +static QSize maxSize(const QSize &a, + const QSize &b) +{ + return QSize(qMax(a.width(), b.width()), + qMax(a.height(), b.height())); +} + +static QMargins maxMargins(const QMargins &a, + const QMargins &b) +{ + return QMargins(qMax(a.left(), b.left()), + qMax(a.top(), b.top()), + qMax(a.right(), b.right()), + qMax(a.bottom(), b.bottom())); +} + void TileLayer::setCell(int x, int y, const Cell &cell) { Q_ASSERT(contains(x, y)); @@ -78,16 +94,17 @@ void TileLayer::setCell(int x, int y, const Cell &cell) if (cell.flippedDiagonally) std::swap(width, height); - if (width > mMaxTileSize.width()) { - mMaxTileSize.setWidth(width); - if (mMap) - mMap->adjustMaxTileSize(mMaxTileSize); - } - if (height > mMaxTileSize.height()) { - mMaxTileSize.setHeight(height); - if (mMap) - mMap->adjustMaxTileSize(mMaxTileSize); - } + const QPoint offset = cell.tile->tileset()->tileOffset(); + + mMaxTileSize = maxSize(QSize(width, height), mMaxTileSize); + mOffsetMargins = maxMargins(QMargins(-offset.x(), + -offset.y(), + offset.x(), + offset.y()), + mOffsetMargins); + + if (mMap) + mMap->adjustDrawMargins(drawMargins()); } mGrid[x + y * mWidth] = cell; @@ -403,5 +420,6 @@ TileLayer *TileLayer::initializeClone(TileLayer *clone) const Layer::initializeClone(clone); clone->mGrid = mGrid; clone->mMaxTileSize = mMaxTileSize; + clone->mOffsetMargins = mOffsetMargins; return clone; } diff --git a/src/libtiled/tilelayer.h b/src/libtiled/tilelayer.h index 7d67e07700..6671736e5d 100644 --- a/src/libtiled/tilelayer.h +++ b/src/libtiled/tilelayer.h @@ -34,6 +34,7 @@ #include "layer.h" +#include #include #include @@ -113,11 +114,24 @@ class TILEDSHARED_EXPORT TileLayer : public Layer TileLayer(const QString &name, int x, int y, int width, int height); /** - * Returns the maximum tile size of this layer. Used by the layer - * rendering code to determine the area that needs to be redrawn. + * Returns the maximum tile size of this layer. */ QSize maxTileSize() const { return mMaxTileSize; } + /** + * Returns the margins that have to be taken into account while drawing + * this tile layer. The margins depend on the maximum tile size and the + * offset applied to the tiles. + */ + QMargins drawMargins() const + { + return QMargins(mOffsetMargins.left(), + mOffsetMargins.top() + mMaxTileSize.height(), + mOffsetMargins.right() + mMaxTileSize.width(), + mOffsetMargins.bottom()); + } + + /** * Returns whether (x, y) is inside this map layer. */ @@ -256,6 +270,7 @@ class TILEDSHARED_EXPORT TileLayer : public Layer private: QSize mMaxTileSize; + QMargins mOffsetMargins; QVector mGrid; }; diff --git a/src/libtiled/tileset.h b/src/libtiled/tileset.h index 2f9b50aeb9..99c7ba67db 100644 --- a/src/libtiled/tileset.h +++ b/src/libtiled/tileset.h @@ -34,6 +34,7 @@ #include #include +#include #include class QImage; @@ -126,6 +127,17 @@ class TILEDSHARED_EXPORT Tileset : public Object */ int margin() const { return mMargin; } + /** + * Returns the offset that is applied when drawing the tiles in this + * tileset. + */ + QPoint tileOffset() const { return mTileOffset; } + + /** + * @see tileOffset + */ + void setTileOffset(QPoint offset) { mTileOffset = offset; } + /** * Returns the tile for the given tile ID. * The tile ID is local to this tileset, which means the IDs are in range @@ -210,6 +222,7 @@ class TILEDSHARED_EXPORT Tileset : public Object int mTileHeight; int mTileSpacing; int mMargin; + QPoint mTileOffset; int mImageWidth; int mImageHeight; int mColumnCount; diff --git a/src/tiled/brushitem.cpp b/src/tiled/brushitem.cpp index 9eed2df2ce..1b0c613534 100644 --- a/src/tiled/brushitem.cpp +++ b/src/tiled/brushitem.cpp @@ -136,11 +136,14 @@ void BrushItem::updateBoundingRect() // Adjust for amount of pixels tiles extend at the top and to the right if (mTileLayer) { const Map *map = mMapDocument->map(); - const int tileWidth = map->tileWidth(); - const int tileHeight = map->tileHeight(); - const QSize maxTileSize = mTileLayer->maxTileSize(); - const int extendTop = -qMax(0, maxTileSize.height() - tileHeight); - const int extendRight = qMax(0, maxTileSize.width() - tileWidth); - mBoundingRect.adjust(0, extendTop, extendRight, 0); + + QMargins drawMargins = mTileLayer->drawMargins(); + drawMargins.setTop(drawMargins.top() - map->tileHeight()); + drawMargins.setRight(drawMargins.right() - map->tileWidth()); + + mBoundingRect.adjust(-drawMargins.left(), + -drawMargins.top(), + drawMargins.right(), + drawMargins.bottom()); } } diff --git a/src/tiled/mapscene.cpp b/src/tiled/mapscene.cpp index 442be5e4c3..a50e82bdb0 100644 --- a/src/tiled/mapscene.cpp +++ b/src/tiled/mapscene.cpp @@ -232,11 +232,14 @@ void MapScene::updateCurrentLayerHighlight() void MapScene::repaintRegion(const QRegion ®ion) { const MapRenderer *renderer = mMapDocument->renderer(); - const QSize extra = mMapDocument->map()->extraTileSize(); + const QMargins margins = mMapDocument->map()->drawMargins(); - foreach (const QRect &r, region.rects()) - update(renderer->boundingRect(r) - .adjusted(0, -extra.height(), extra.width(), 0)); + foreach (const QRect &r, region.rects()) { + update(renderer->boundingRect(r).adjusted(-margins.left(), + -margins.top(), + margins.right(), + margins.bottom())); + } } void MapScene::enableSelectedTool() diff --git a/src/tiled/newtilesetdialog.cpp b/src/tiled/newtilesetdialog.cpp index b9526ea79e..aa59a06d54 100644 --- a/src/tiled/newtilesetdialog.cpp +++ b/src/tiled/newtilesetdialog.cpp @@ -37,6 +37,7 @@ static const char * const COLOR_ENABLED_KEY = "Tileset/UseTransparentColor"; static const char * const COLOR_KEY = "Tileset/TransparentColor"; static const char * const SPACING_KEY = "Tileset/Spacing"; static const char * const MARGIN_KEY = "Tileset/Margin"; +static const char * const OFFSET_KEY = "Tileset/Offset"; using namespace Tiled; using namespace Tiled::Internal; @@ -58,11 +59,14 @@ NewTilesetDialog::NewTilesetDialog(const QString &path, QWidget *parent) : QColor color = colorName.isEmpty() ? Qt::magenta : QColor(colorName); int spacing = s->value(QLatin1String(SPACING_KEY)).toInt(); int margin = s->value(QLatin1String(MARGIN_KEY)).toInt(); + QPoint offset = s->value(QLatin1String(OFFSET_KEY)).toPoint(); mUi->useTransparentColor->setChecked(colorEnabled); mUi->colorButton->setColor(color); mUi->spacing->setValue(spacing); mUi->margin->setValue(margin); + mUi->offsetX->setValue(offset.x()); + mUi->offsetY->setValue(offset.y()); connect(mUi->browseButton, SIGNAL(clicked()), SLOT(browse())); connect(mUi->name, SIGNAL(textEdited(QString)), SLOT(nameEdited(QString))); @@ -112,11 +116,15 @@ void NewTilesetDialog::tryAccept() const int tileHeight = mUi->tileHeight->value(); const int spacing = mUi->spacing->value(); const int margin = mUi->margin->value(); + const QPoint offset = QPoint(mUi->offsetX->value(), + mUi->offsetY->value()); std::auto_ptr tileset(new Tileset(name, tileWidth, tileHeight, spacing, margin)); + tileset->setTileOffset(offset); + if (useTransparentColor) tileset->setTransparentColor(transparentColor); @@ -141,6 +149,7 @@ void NewTilesetDialog::tryAccept() s->setValue(QLatin1String(COLOR_KEY), transparentColor.name()); s->setValue(QLatin1String(SPACING_KEY), spacing); s->setValue(QLatin1String(MARGIN_KEY), margin); + s->setValue(QLatin1String(OFFSET_KEY), offset); mNewTileset = tileset.release(); accept(); diff --git a/src/tiled/newtilesetdialog.ui b/src/tiled/newtilesetdialog.ui index d2ebf1c5fc..5f90c4d4ec 100644 --- a/src/tiled/newtilesetdialog.ui +++ b/src/tiled/newtilesetdialog.ui @@ -2,11 +2,19 @@ NewTilesetDialog + + + 0 + 0 + 342 + 369 + + New Tileset - - + + Tileset @@ -76,128 +84,230 @@ - - - - Tiles + + + + 0 - - - - - Tile width: - - - - - - - true - - - 1 - - - 9999 - - - 32 - - - - - - - Margin: - - - - - - - 0 - - - 99 - - - 0 - - - - - - - Tile height: - - - - - - - 1 - - - 9999 - - - 32 - - - - - - - Spacing: - - - - - - - 0 - - - 99 - - - 0 - - - - - - - Qt::Horizontal - - - QSizePolicy::MinimumExpanding - - - - 20 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - + + + + Tiles + + + + + + Tile width: + + + + + + + true + + + 1 + + + 9999 + + + 32 + + + + + + + Tile height: + + + + + + + 1 + + + 9999 + + + 32 + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 10 + 13 + + + + + + + + Margin: + + + + + + + 0 + + + 99 + + + 0 + + + + + + + Spacing: + + + + + + + 0 + + + 99 + + + 0 + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + + Drawing Offset + + + + + + X: + + + + + + + true + + + -9999 + + + 9999 + + + + + + + Y: + + + + + + + -9999 + + + 9999 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 20 + + + + + - + Qt::Horizontal