Skip to content

Commit 694257b

Browse files
committed
Added support for specifying a tile drawing offset
Each tileset can now define an offset that is applied when drawing its tiles. This is useful when combining tilesets with a different baseline, or lining up the base of the tiles with the tile grid. This new tileset property is used to improve the tile positions in the 'perspective walls' and 'isometric grass and water' examples. This closes mapeditor#16 Sponsored-by: Clint Bellanger
1 parent 058c752 commit 694257b

16 files changed

+392
-186
lines changed

examples/isometric_grass_and_water.tmx

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<!DOCTYPE map SYSTEM "http://mapeditor.org/dtd/1.0/map.dtd">
33
<map version="1.0" orientation="isometric" width="25" height="25" tilewidth="64" tileheight="32">
44
<tileset firstgid="1" name="isometric_grass_and_water" tilewidth="64" tileheight="64">
5+
<tileoffset x="0" y="16"/>
56
<image source="isometric_grass_and_water.png" width="256" height="384"/>
67
</tileset>
78
<layer name="Tile Layer 1" width="25" height="25">

examples/perspective_walls.tmx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<map version="1.0" orientation="orthogonal" width="32" height="32" tilewidth="32" tileheight="32">
2+
<map version="1.0" orientation="orthogonal" width="32" height="32" tilewidth="31" tileheight="31">
33
<tileset firstgid="1" source="perspective_walls.tsx"/>
44
<layer name="Walls" width="32" height="32">
55
<data encoding="base64" compression="zlib">

examples/perspective_walls.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<tileset name="perspective_walls" tilewidth="64" tileheight="64">
3+
<tileoffset x="-32" y="0"/>
34
<image source="perspective_walls.png"/>
45
<tile id="13">
56
<properties>

src/libtiled/isometricrenderer.cpp

+12-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "mapobject.h"
3333
#include "tile.h"
3434
#include "tilelayer.h"
35+
#include "tileset.h"
3536

3637
#include <cmath>
3738

@@ -164,10 +165,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter,
164165
if (rect.isNull())
165166
rect = boundingRect(layer->bounds());
166167

167-
const QSize maxTileSize = layer->maxTileSize();
168-
const int extraWidth = maxTileSize.width() - tileWidth;
169-
const int extraHeight = maxTileSize.height() - tileHeight;
170-
rect.adjust(-extraWidth, 0, 0, extraHeight);
168+
QMargins drawMargins = layer->drawMargins();
169+
drawMargins.setTop(drawMargins.top() - tileHeight);
170+
drawMargins.setRight(drawMargins.right() - tileWidth);
171+
172+
rect.adjust(-drawMargins.right(),
173+
-drawMargins.bottom(),
174+
drawMargins.left(),
175+
drawMargins.top());
171176

172177
// Determine the tile and pixel coordinates to start at
173178
QPointF tilePos = pixelToTileCoords(rect.x(), rect.y());
@@ -214,13 +219,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter,
214219
const Cell &cell = layer->cellAt(columnItr);
215220
if (!cell.isEmpty()) {
216221
const QPixmap &img = cell.tile->image();
222+
const QPoint offset = cell.tile->tileset()->tileOffset();
217223

218224
qreal m11 = 1; // Horizontal scaling factor
219225
qreal m12 = 0; // Vertical shearing factor
220226
qreal m21 = 0; // Horizontal shearing factor
221227
qreal m22 = 1; // Vertical scaling factor
222-
qreal dx = x;
223-
qreal dy = y - img.height();
228+
qreal dx = offset.x() + x;
229+
qreal dy = offset.y() + y - img.height();
224230

225231
if (cell.flippedDiagonally) {
226232
// Use shearing to swap the X/Y axis

src/libtiled/map.cpp

+21-9
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ Map::Map(Orientation orientation,
4343
mWidth(width),
4444
mHeight(height),
4545
mTileWidth(tileWidth),
46-
mTileHeight(tileHeight),
47-
mMaxTileSize(tileWidth, tileHeight)
46+
mTileHeight(tileHeight)
4847
{
4948
}
5049

@@ -53,12 +52,25 @@ Map::~Map()
5352
qDeleteAll(mLayers);
5453
}
5554

56-
void Map::adjustMaxTileSize(const QSize &size)
55+
static QMargins maxMargins(const QMargins &a,
56+
const QMargins &b)
5757
{
58-
if (size.width() > mMaxTileSize.width())
59-
mMaxTileSize.setWidth(size.width());
60-
if (size.height() > mMaxTileSize.height())
61-
mMaxTileSize.setHeight(size.height());
58+
return QMargins(qMax(a.left(), b.left()),
59+
qMax(a.top(), b.top()),
60+
qMax(a.right(), b.right()),
61+
qMax(a.bottom(), b.bottom()));
62+
}
63+
64+
void Map::adjustDrawMargins(const QMargins &margins)
65+
{
66+
// The TileLayer includes the maximum tile size in its draw margins. So
67+
// we need to subtract the tile size of the map, since that part does not
68+
// contribute to additional margin.
69+
mDrawMargins = maxMargins(QMargins(margins.left(),
70+
margins.top() - mTileHeight,
71+
margins.right() - mTileWidth,
72+
margins.bottom()),
73+
mDrawMargins);
6274
}
6375

6476
int Map::tileLayerCount() const
@@ -105,7 +117,7 @@ void Map::adoptLayer(Layer *layer)
105117
layer->setMap(this);
106118

107119
if (TileLayer *tileLayer = dynamic_cast<TileLayer*>(layer))
108-
adjustMaxTileSize(tileLayer->maxTileSize());
120+
adjustDrawMargins(tileLayer->drawMargins());
109121
}
110122

111123
Layer *Map::takeLayerAt(int index)
@@ -158,7 +170,7 @@ bool Map::isTilesetUsed(Tileset *tileset) const
158170
Map *Map::clone() const
159171
{
160172
Map *o = new Map(mOrientation, mWidth, mHeight, mTileWidth, mTileHeight);
161-
o->mMaxTileSize = mMaxTileSize;
173+
o->mDrawMargins = mDrawMargins;
162174
foreach (const Layer *layer, mLayers)
163175
o->addLayer(layer->clone());
164176
o->mTilesets = mTilesets;

src/libtiled/map.h

+9-16
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "object.h"
3535

3636
#include <QList>
37+
#include <QMargins>
3738
#include <QSize>
3839

3940
namespace Tiled {
@@ -128,26 +129,18 @@ class TILEDSHARED_EXPORT Map : public Object
128129
int tileHeight() const { return mTileHeight; }
129130

130131
/**
131-
* Returns the maximum tile size used by tile layers of this map.
132-
* @see TileLayer::extraTileSize()
132+
* Adjusts the draw margins to be at least as big as the given margins.
133+
* Called from tile layers when their tiles change.
133134
*/
134-
QSize maxTileSize() const { return mMaxTileSize; }
135+
void adjustDrawMargins(const QMargins &margins);
135136

136137
/**
137-
* Adjusts the maximum tile size to be at least as much as the given
138-
* size. Called from tile layers when their maximum tile size increases.
139-
*/
140-
void adjustMaxTileSize(const QSize &size);
141-
142-
/**
143-
* Convenience method for getting the extra tile size, which is the number
144-
* of pixels that tiles may extend beyond the size of the tile grid.
138+
* Returns the margins that have to be taken into account when figuring
139+
* out which part of the map to repaint after changing some tiles.
145140
*
146-
* @see maxTileSize()
141+
* @see TileLayer::drawMargins
147142
*/
148-
QSize extraTileSize() const
149-
{ return QSize(mMaxTileSize.width() - mTileWidth,
150-
mMaxTileSize.height() - mTileHeight); }
143+
QMargins drawMargins() const { return mDrawMargins; }
151144

152145
/**
153146
* Returns the number of layers of this map.
@@ -259,7 +252,7 @@ class TILEDSHARED_EXPORT Map : public Object
259252
int mHeight;
260253
int mTileWidth;
261254
int mTileHeight;
262-
QSize mMaxTileSize;
255+
QMargins mDrawMargins;
263256
QList<Layer*> mLayers;
264257
QList<Tileset*> mTilesets;
265258
};

src/libtiled/mapreader.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,21 @@ Tileset *MapReaderPrivate::readTileset()
277277
tileSpacing, margin);
278278

279279
while (xml.readNextStartElement()) {
280-
if (xml.name() == "tile")
280+
if (xml.name() == "tile") {
281281
readTilesetTile(tileset);
282-
else if (xml.name() == "properties")
282+
} else if (xml.name() == "tileoffset") {
283+
const QXmlStreamAttributes oa = xml.attributes();
284+
int x = oa.value(QLatin1String("x")).toString().toInt();
285+
int y = oa.value(QLatin1String("y")).toString().toInt();
286+
tileset->setTileOffset(QPoint(x, y));
287+
xml.skipCurrentElement();
288+
} else if (xml.name() == "properties") {
283289
tileset->mergeProperties(readProperties());
284-
else if (xml.name() == "image")
290+
} else if (xml.name() == "image") {
285291
readTilesetImage(tileset);
286-
else
292+
} else {
287293
readUnknownElement();
294+
}
288295
}
289296
}
290297
} else { // External tileset

src/libtiled/mapwriter.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset *tileset,
234234
if (margin != 0)
235235
w.writeAttribute(QLatin1String("margin"), QString::number(margin));
236236

237+
const QPoint offset = tileset->tileOffset();
238+
if (!offset.isNull()) {
239+
w.writeStartElement(QLatin1String("tileoffset"));
240+
w.writeAttribute(QLatin1String("x"), QString::number(offset.x()));
241+
w.writeAttribute(QLatin1String("y"), QString::number(offset.y()));
242+
w.writeEndElement();
243+
}
244+
237245
// Write the tileset properties
238246
writeProperties(w, tileset->properties());
239247

src/libtiled/orthogonalrenderer.cpp

+13-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "mapobject.h"
3333
#include "tile.h"
3434
#include "tilelayer.h"
35+
#include "tileset.h"
3536

3637
#include <cmath>
3738

@@ -192,10 +193,15 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter,
192193
int endY = layer->height();
193194

194195
if (!exposed.isNull()) {
195-
const QSize maxTileSize = layer->maxTileSize();
196-
const int extraWidth = maxTileSize.width() - tileWidth;
197-
const int extraHeight = maxTileSize.height() - tileHeight;
198-
QRectF rect = exposed.adjusted(-extraWidth, 0, 0, extraHeight);
196+
QMargins drawMargins = layer->drawMargins();
197+
drawMargins.setTop(drawMargins.top() - tileHeight);
198+
drawMargins.setRight(drawMargins.right() - tileWidth);
199+
200+
QRectF rect = exposed.adjusted(-drawMargins.right(),
201+
-drawMargins.bottom(),
202+
drawMargins.left(),
203+
drawMargins.top());
204+
199205
rect.translate(-layerPos);
200206

201207
startX = qMax((int) rect.x() / tileWidth, 0);
@@ -213,13 +219,14 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter,
213219
continue;
214220

215221
const QPixmap &img = cell.tile->image();
222+
const QPoint offset = cell.tile->tileset()->tileOffset();
216223

217224
qreal m11 = 1; // Horizontal scaling factor
218225
qreal m12 = 0; // Vertical shearing factor
219226
qreal m21 = 0; // Horizontal shearing factor
220227
qreal m22 = 1; // Vertical scaling factor
221-
qreal dx = x * tileWidth;
222-
qreal dy = (y + 1) * tileHeight - img.height();
228+
qreal dx = offset.x() + x * tileWidth;
229+
qreal dy = offset.y() + (y + 1) * tileHeight - img.height();
223230

224231
if (cell.flippedDiagonally) {
225232
// Use shearing to swap the X/Y axis

src/libtiled/tilelayer.cpp

+28-10
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ QRegion TileLayer::region() const
6767
return region;
6868
}
6969

70+
static QSize maxSize(const QSize &a,
71+
const QSize &b)
72+
{
73+
return QSize(qMax(a.width(), b.width()),
74+
qMax(a.height(), b.height()));
75+
}
76+
77+
static QMargins maxMargins(const QMargins &a,
78+
const QMargins &b)
79+
{
80+
return QMargins(qMax(a.left(), b.left()),
81+
qMax(a.top(), b.top()),
82+
qMax(a.right(), b.right()),
83+
qMax(a.bottom(), b.bottom()));
84+
}
85+
7086
void TileLayer::setCell(int x, int y, const Cell &cell)
7187
{
7288
Q_ASSERT(contains(x, y));
@@ -78,16 +94,17 @@ void TileLayer::setCell(int x, int y, const Cell &cell)
7894
if (cell.flippedDiagonally)
7995
std::swap(width, height);
8096

81-
if (width > mMaxTileSize.width()) {
82-
mMaxTileSize.setWidth(width);
83-
if (mMap)
84-
mMap->adjustMaxTileSize(mMaxTileSize);
85-
}
86-
if (height > mMaxTileSize.height()) {
87-
mMaxTileSize.setHeight(height);
88-
if (mMap)
89-
mMap->adjustMaxTileSize(mMaxTileSize);
90-
}
97+
const QPoint offset = cell.tile->tileset()->tileOffset();
98+
99+
mMaxTileSize = maxSize(QSize(width, height), mMaxTileSize);
100+
mOffsetMargins = maxMargins(QMargins(-offset.x(),
101+
-offset.y(),
102+
offset.x(),
103+
offset.y()),
104+
mOffsetMargins);
105+
106+
if (mMap)
107+
mMap->adjustDrawMargins(drawMargins());
91108
}
92109

93110
mGrid[x + y * mWidth] = cell;
@@ -403,5 +420,6 @@ TileLayer *TileLayer::initializeClone(TileLayer *clone) const
403420
Layer::initializeClone(clone);
404421
clone->mGrid = mGrid;
405422
clone->mMaxTileSize = mMaxTileSize;
423+
clone->mOffsetMargins = mOffsetMargins;
406424
return clone;
407425
}

src/libtiled/tilelayer.h

+17-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "layer.h"
3636

37+
#include <QMargins>
3738
#include <QString>
3839
#include <QVector>
3940

@@ -113,11 +114,24 @@ class TILEDSHARED_EXPORT TileLayer : public Layer
113114
TileLayer(const QString &name, int x, int y, int width, int height);
114115

115116
/**
116-
* Returns the maximum tile size of this layer. Used by the layer
117-
* rendering code to determine the area that needs to be redrawn.
117+
* Returns the maximum tile size of this layer.
118118
*/
119119
QSize maxTileSize() const { return mMaxTileSize; }
120120

121+
/**
122+
* Returns the margins that have to be taken into account while drawing
123+
* this tile layer. The margins depend on the maximum tile size and the
124+
* offset applied to the tiles.
125+
*/
126+
QMargins drawMargins() const
127+
{
128+
return QMargins(mOffsetMargins.left(),
129+
mOffsetMargins.top() + mMaxTileSize.height(),
130+
mOffsetMargins.right() + mMaxTileSize.width(),
131+
mOffsetMargins.bottom());
132+
}
133+
134+
121135
/**
122136
* Returns whether (x, y) is inside this map layer.
123137
*/
@@ -256,6 +270,7 @@ class TILEDSHARED_EXPORT TileLayer : public Layer
256270

257271
private:
258272
QSize mMaxTileSize;
273+
QMargins mOffsetMargins;
259274
QVector<Cell> mGrid;
260275
};
261276

src/libtiled/tileset.h

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include <QColor>
3636
#include <QList>
37+
#include <QPoint>
3738
#include <QString>
3839

3940
class QImage;
@@ -126,6 +127,17 @@ class TILEDSHARED_EXPORT Tileset : public Object
126127
*/
127128
int margin() const { return mMargin; }
128129

130+
/**
131+
* Returns the offset that is applied when drawing the tiles in this
132+
* tileset.
133+
*/
134+
QPoint tileOffset() const { return mTileOffset; }
135+
136+
/**
137+
* @see tileOffset
138+
*/
139+
void setTileOffset(QPoint offset) { mTileOffset = offset; }
140+
129141
/**
130142
* Returns the tile for the given tile ID.
131143
* 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
210222
int mTileHeight;
211223
int mTileSpacing;
212224
int mMargin;
225+
QPoint mTileOffset;
213226
int mImageWidth;
214227
int mImageHeight;
215228
int mColumnCount;

0 commit comments

Comments
 (0)