Skip to content

Commit

Permalink
Tile sheet with different sized tiles mapeditor#1008
Browse files Browse the repository at this point in the history
  • Loading branch information
devnewton committed Apr 21, 2022
1 parent ea32425 commit 76b5be8
Show file tree
Hide file tree
Showing 21 changed files with 132 additions and 27 deletions.
2 changes: 2 additions & 0 deletions docs/reference/json-map-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ Tile (Definition)
animation, array, "Array of :ref:`Frames <json-frame>`"
id, int, "Local ID of the tile"
image, string, "Image representing this tile (optional)"
imagetop, int, "Horizontal offset of the tile image in pixels"
imageleft, int, "Vertical offset of the tile image in pixels"
imageheight, int, "Height of the tile image in pixels"
imagewidth, int, "Width of the tile image in pixels"
objectgroup, :ref:`json-layer`, "Layer with type ``objectgroup``, when collision shapes are specified (optional)"
Expand Down
9 changes: 5 additions & 4 deletions src/libtiled/imagereference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ bool ImageReference::hasImage() const

QPixmap ImageReference::create() const
{
QPixmap pixmap;
if (source.isLocalFile())
return ImageCache::loadPixmap(source.toLocalFile());
pixmap = ImageCache::loadPixmap(source.toLocalFile());
else if (source.scheme() == QLatin1String("qrc"))
return ImageCache::loadPixmap(QLatin1Char(':') + source.path());
pixmap = ImageCache::loadPixmap(QLatin1Char(':') + source.path());
else if (!data.isEmpty())
return QPixmap::fromImage(QImage::fromData(data, format));
pixmap = QPixmap::fromImage(QImage::fromData(data, format));

return QPixmap();
return pixmap.copy(this->topLeft.x(), this->topLeft.y(), this->size.width(), this->size.height());
}

} // namespace Tiled
1 change: 1 addition & 0 deletions src/libtiled/imagereference.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ImageReference

QUrl source;
QColor transparentColor;
QPoint topLeft;
QSize size;
QByteArray format;
QByteArray data;
Expand Down
4 changes: 3 additions & 1 deletion src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ void MapReaderPrivate::readTilesetTile(Tileset &tileset)
if (imageReference.source.isEmpty())
xml.raiseError(tr("Error reading embedded image for tile %1").arg(id));
}
tileset.setTileImage(tile, image, imageReference.source);
tileset.setTileImage(tile, image, imageReference.source, QRect(imageReference.topLeft, imageReference.size));
}
} else if (xml.name() == QLatin1String("objectgroup")) {
std::unique_ptr<ObjectGroup> objectGroup = readObjectGroup();
Expand Down Expand Up @@ -643,6 +643,8 @@ ImageReference MapReaderPrivate::readImage()
ImageReference image;
image.source = toUrl(source, mPath);
image.format = atts.value(QLatin1String("format")).toLatin1();
image.topLeft = QPoint(atts.value(QLatin1String("top")).toInt(),
atts.value(QLatin1String("left")).toInt());
image.size = QSize(atts.value(QLatin1String("width")).toInt(),
atts.value(QLatin1String("height")).toInt());

Expand Down
5 changes: 5 additions & 0 deletions src/libtiled/maptovariantconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ QVariant MapToVariantConverter::toVariant(const Tileset &tileset,
tileVariant[QStringLiteral("imagewidth")] = tileSize.width();
tileVariant[QStringLiteral("imageheight")] = tileSize.height();
}
const QPoint topLeft = tile->imageSourceRect().topLeft();
if (!topLeft.isNull()) {
tileVariant[QStringLiteral("imagetop")] = topLeft.y();
tileVariant[QStringLiteral("imageleft")] = topLeft.x();
}
}
if (tile->objectGroup())
tileVariant[QStringLiteral("objectgroup")] = toVariant(*tile->objectGroup());
Expand Down
8 changes: 8 additions & 0 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,14 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset &tileset,
QString::number(tileSize.height()));
}

const QPoint tileTopLeft = tile->imageSourceRect().topLeft();
if(!tileTopLeft.isNull()) {
w.writeAttribute(QStringLiteral("top"),
QString::number(tileTopLeft.y()));
w.writeAttribute(QStringLiteral("left"),
QString::number(tileTopLeft.x()));
}

if (tile->imageSource().isEmpty()) {
w.writeAttribute(QStringLiteral("format"),
QLatin1String("png"));
Expand Down
1 change: 1 addition & 0 deletions src/libtiled/tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ Tile *Tile::clone(Tileset *tileset) const
c->setProperties(properties());

c->mImageSource = mImageSource;
c->mImageSourceRect = mImageSourceRect;
c->mImageStatus = mImageStatus;
c->mType = mType;
c->mProbability = mProbability;
Expand Down
15 changes: 15 additions & 0 deletions src/libtiled/tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class TILEDSHARED_EXPORT Tile : public Object
const QUrl &imageSource() const;
void setImageSource(const QUrl &imageSource);

const QRect &imageSourceRect() const;
void setImageSourceRect(const QRect& imageSourceRect);

int width() const;
int height() const;
QSize size() const;
Expand Down Expand Up @@ -112,6 +115,7 @@ class TILEDSHARED_EXPORT Tile : public Object
Tileset *mTileset;
QPixmap mImage;
QUrl mImageSource;
QRect mImageSourceRect;
LoadingStatus mImageStatus;
QString mType;
qreal mProbability;
Expand Down Expand Up @@ -172,6 +176,17 @@ inline void Tile::setImageSource(const QUrl &imageSource)
mImageSource = imageSource;
}

/**
* Return image source rect in pixels
*/
inline const QRect& Tile::imageSourceRect() const {
return mImageSourceRect;
}

inline void Tile::setImageSourceRect(const QRect& imageSourceRect) {
mImageSourceRect = imageSourceRect;
}

/**
* Returns the width of this tile.
*/
Expand Down
7 changes: 5 additions & 2 deletions src/libtiled/tileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,12 @@ std::unique_ptr<WangSet> Tileset::takeWangSetAt(int index)
/**
* Adds a new tile to the end of the tileset.
*/
Tile *Tileset::addTile(const QPixmap &image, const QUrl &source)
Tile *Tileset::addTile(const QPixmap &image, const QUrl &source, const QRect &rect)
{
Tile *newTile = new Tile(takeNextTileId(), this);
newTile->setImage(image);
newTile->setImageSource(source);
newTile->setImageSourceRect(rect);

mTilesById.insert(newTile->id(), newTile);
mTiles.append(newTile);
Expand Down Expand Up @@ -566,7 +567,8 @@ bool Tileset::anyTileOutOfOrder() const
*/
void Tileset::setTileImage(Tile *tile,
const QPixmap &image,
const QUrl &source)
const QUrl &source,
const QRect &rect)
{
Q_ASSERT(isCollection());
Q_ASSERT(mTilesById.value(tile->id()) == tile);
Expand All @@ -576,6 +578,7 @@ void Tileset::setTileImage(Tile *tile,

tile->setImage(image);
tile->setImageSource(source);
tile->setImageSourceRect(rect);

if (previousImageSize != newImageSize) {
// Update our max. tile size
Expand Down
5 changes: 3 additions & 2 deletions src/libtiled/tileset.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThis<T
void insertWangSet(int index, std::unique_ptr<WangSet> wangSet);
std::unique_ptr<WangSet> takeWangSetAt(int index);

Tile *addTile(const QPixmap &image, const QUrl &source = QUrl());
Tile *addTile(const QPixmap &image, const QUrl &source = QUrl(), const QRect &rect = QRect());
void addTiles(const QList<Tile*> &tiles);
void removeTiles(const QList<Tile *> &tiles);
void deleteTile(int id);
Expand All @@ -204,7 +204,8 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThis<T

void setTileImage(Tile *tile,
const QPixmap &image,
const QUrl &source = QUrl());
const QUrl &source = QUrl(),
const QRect &rect = QRect());
/**
* @deprecated Only kept around for the Python API!
*/
Expand Down
6 changes: 4 additions & 2 deletions src/tiled/brokenlinks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ void LinkFixer::tryFixLink(const BrokenLink &link)
} else {
auto command = new ChangeTileImageSource(tilesetDocument,
link._tile,
newFileUrl);
newFileUrl,
link._tile->imageSourceRect());

tilesetDocument->undoStack()->push(command);
}
Expand Down Expand Up @@ -629,7 +630,8 @@ bool LinkFixer::tryFixLink(const BrokenLink &link, const QString &newFilePath)
} else {
auto command = new ChangeTileImageSource(tilesetDocument,
link._tile,
newSource);
newSource,
link._tile->imageSourceRect());

tilesetDocument->undoStack()->push(command);
}
Expand Down
12 changes: 8 additions & 4 deletions src/tiled/changetileimagesource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,26 @@ namespace Tiled {

ChangeTileImageSource::ChangeTileImageSource(TilesetDocument *tilesetDocument,
Tile *tile,
const QUrl &imageSource)
const QUrl &imageSource,
const QRect &imageRect)
: mTilesetDocument(tilesetDocument)
, mTile(tile)
, mOldImageSource(tile->imageSource())
, mNewImageSource(imageSource)
, mOldImageRect(tile->imageSourceRect())
, mNewImageRect(imageRect)
{
setText(QCoreApplication::translate("Undo Commands",
"Change Tile Image"));
}

void ChangeTileImageSource::apply(const QUrl &imageSource)
void ChangeTileImageSource::apply(const QUrl &imageSource, const QRect& imageRect)
{
// todo: make sure remote source loading is triggered
mTilesetDocument->setTileImage(mTile,
ImageCache::loadPixmap(imageSource.toLocalFile()),
imageSource);
ImageCache::loadPixmap(imageSource.toLocalFile()).copy(imageRect),
imageSource,
imageRect);
}

} // namespace Tiled
12 changes: 8 additions & 4 deletions src/tiled/changetileimagesource.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <QUndoCommand>
#include <QUrl>
#include <QRect>

namespace Tiled {

Expand All @@ -34,18 +35,21 @@ class ChangeTileImageSource : public QUndoCommand
public:
ChangeTileImageSource(TilesetDocument *tilesetDocument,
Tile *tile,
const QUrl &imageSource);
const QUrl &imageSource,
const QRect &imageRect);

void undo() override { apply(mOldImageSource); }
void redo() override { apply(mNewImageSource); }
void undo() override { apply(mOldImageSource, mOldImageRect); }
void redo() override { apply(mNewImageSource, mNewImageRect); }

private:
void apply(const QUrl &imageSource);
void apply(const QUrl &imageSource, const QRect &imageRect);

TilesetDocument *mTilesetDocument;
Tile *mTile;
QUrl mOldImageSource;
QUrl mNewImageSource;
QRect mOldImageRect;
QRect mNewImageRect;
};

} // namespace Tiled
24 changes: 22 additions & 2 deletions src/tiled/editabletile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,30 @@ void EditableTile::setImageFileName(const QString &fileName)
}

asset()->push(new ChangeTileImageSource(doc, tile(),
QUrl::fromLocalFile(fileName)));
QUrl::fromLocalFile(fileName),
imageRect()));
} else if (!checkReadOnly()) {
tile()->setImage(ImageCache::loadPixmap(fileName));
tile()->setImage(ImageCache::loadPixmap(fileName).copy(imageRect()));
tile()->setImageSource(QUrl::fromLocalFile(fileName));
tile()->setImageSourceRect(imageRect());
}
}

void EditableTile::setImageRect(const QRect &rect)
{
if (TilesetDocument *doc = tilesetDocument()) {
if (!tileset()->tileset()->isCollection()) {
ScriptManager::instance().throwError(QCoreApplication::translate("Script Errors", "Tileset needs to be an image collection"));
return;
}

asset()->push(new ChangeTileImageSource(doc, tile(),
QUrl::fromLocalFile(imageFileName()),
rect));
} else if (!checkReadOnly()) {
tile()->setImage(ImageCache::loadPixmap(imageFileName()).copy(rect));
tile()->setImageSource(QUrl::fromLocalFile(imageFileName()));
tile()->setImageSourceRect(rect);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/tiled/editabletile.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class EditableTile : public EditableObject
Q_PROPERTY(QSize size READ size)
Q_PROPERTY(QString type READ type WRITE setType)
Q_PROPERTY(QString imageFileName READ imageFileName WRITE setImageFileName)
Q_PROPERTY(QRect imageRect READ imageRect WRITE setImageRect)
Q_PROPERTY(qreal probability READ probability WRITE setProbability)
Q_PROPERTY(Tiled::EditableObjectGroup *objectGroup READ objectGroup WRITE setObjectGroup)
Q_PROPERTY(QJSValue frames READ frames WRITE setFrames)
Expand Down Expand Up @@ -76,6 +77,7 @@ class EditableTile : public EditableObject
QSize size() const;
const QString &type() const;
QString imageFileName() const;
QRect imageRect() const;
qreal probability() const;
EditableObjectGroup *objectGroup() const;
QJSValue frames() const;
Expand All @@ -95,6 +97,7 @@ class EditableTile : public EditableObject
public slots:
void setType(const QString &type);
void setImageFileName(const QString &fileName);
void setImageRect(const QRect &rect);
void setProbability(qreal probability);
void setObjectGroup(EditableObjectGroup *editableObjectGroup);
void setFrames(QJSValue value);
Expand Down Expand Up @@ -137,6 +140,11 @@ inline QString EditableTile::imageFileName() const
return tile()->imageSource().toString(QUrl::PreferLocalFile);
}

inline QRect EditableTile::imageRect() const
{
return tile()->imageSourceRect();
}

inline qreal EditableTile::probability() const
{
return tile()->probability();
Expand Down
16 changes: 13 additions & 3 deletions src/tiled/propertybrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -908,10 +908,14 @@ void PropertyBrowser::addTileProperties()

const Tile *tile = static_cast<const Tile*>(mObject);
if (!tile->imageSource().isEmpty()) {
QtVariantProperty *imageSourceRectProperty = addProperty(ImageSourceRectProperty,
QMetaType::QRect,
tr("Image rect"), groupProperty);
imageSourceRectProperty->setEnabled(mTilesetDocument);

QtVariantProperty *imageSourceProperty = addProperty(ImageSourceProperty,
filePathTypeId(),
tr("Image"), groupProperty);

imageSourceProperty->setAttribute(QLatin1String("filter"),
Utils::readableImageFormatsFilter());
imageSourceProperty->setEnabled(mTilesetDocument);
Expand Down Expand Up @@ -1433,10 +1437,14 @@ void PropertyBrowser::applyTileValue(PropertyId id, const QVariant &val)
mTilesetDocument->selectedTiles(),
val.toFloat()));
break;
case ImageSourceRectProperty:
undoStack->push(new ChangeTileImageSource(mTilesetDocument,
tile, tile->imageSource(), val.toRect()));
break;
case ImageSourceProperty: {
const FilePath filePath = val.value<FilePath>();
undoStack->push(new ChangeTileImageSource(mTilesetDocument,
tile, filePath.url));
tile, filePath.url, tile->imageSourceRect()));
break;
}
default:
Expand Down Expand Up @@ -1811,8 +1819,10 @@ void PropertyBrowser::updateProperties()
mIdToProperty[WidthProperty]->setValue(tileSize.width());
mIdToProperty[HeightProperty]->setValue(tileSize.height());
mIdToProperty[TileProbabilityProperty]->setValue(tile->probability());
if (QtVariantProperty *imageSourceProperty = mIdToProperty.value(ImageSourceProperty))
if (QtVariantProperty *imageSourceProperty = mIdToProperty.value(ImageSourceProperty)) {
imageSourceProperty->setValue(QVariant::fromValue(FilePath { tile->imageSource() }));
mIdToProperty[ImageSourceRectProperty]->setValue(tile->imageSourceRect());
}
break;
}
case Object::WangSetType: {
Expand Down
1 change: 1 addition & 0 deletions src/tiled/propertybrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class PropertyBrowser : public QtTreePropertyBrowser
RenderOrderProperty,
LayerFormatProperty,
ImageSourceProperty,
ImageSourceRectProperty,
TilesetImageParametersProperty,
FlippingProperty,
DrawOrderProperty,
Expand Down
Loading

0 comments on commit 76b5be8

Please sign in to comment.