diff --git a/docs/reference/json-map-format.rst b/docs/reference/json-map-format.rst index bd3822313c..223b48afd7 100644 --- a/docs/reference/json-map-format.rst +++ b/docs/reference/json-map-format.rst @@ -549,6 +549,8 @@ Tile (Definition) animation, array, "Array of :ref:`Frames `" 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)" diff --git a/src/libtiled/imagereference.cpp b/src/libtiled/imagereference.cpp index 06e176c69c..08ea699682 100644 --- a/src/libtiled/imagereference.cpp +++ b/src/libtiled/imagereference.cpp @@ -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 diff --git a/src/libtiled/imagereference.h b/src/libtiled/imagereference.h index 002c4538fa..ff5ba46b9b 100644 --- a/src/libtiled/imagereference.h +++ b/src/libtiled/imagereference.h @@ -35,6 +35,7 @@ class ImageReference QUrl source; QColor transparentColor; + QPoint topLeft; QSize size; QByteArray format; QByteArray data; diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index c62216d7c4..b64cf2d331 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -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 = readObjectGroup(); @@ -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()); diff --git a/src/libtiled/maptovariantconverter.cpp b/src/libtiled/maptovariantconverter.cpp index b770dc1b09..a0745b738c 100644 --- a/src/libtiled/maptovariantconverter.cpp +++ b/src/libtiled/maptovariantconverter.cpp @@ -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()); diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index 0300de6a50..b7186bf513 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -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")); diff --git a/src/libtiled/tile.cpp b/src/libtiled/tile.cpp index be4068f536..369bed6b22 100644 --- a/src/libtiled/tile.cpp +++ b/src/libtiled/tile.cpp @@ -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; diff --git a/src/libtiled/tile.h b/src/libtiled/tile.h index 7753774d75..3b77cfc7f2 100644 --- a/src/libtiled/tile.h +++ b/src/libtiled/tile.h @@ -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; @@ -112,6 +115,7 @@ class TILEDSHARED_EXPORT Tile : public Object Tileset *mTileset; QPixmap mImage; QUrl mImageSource; + QRect mImageSourceRect; LoadingStatus mImageStatus; QString mType; qreal mProbability; @@ -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. */ diff --git a/src/libtiled/tileset.cpp b/src/libtiled/tileset.cpp index 70d1614674..0f9a887e31 100644 --- a/src/libtiled/tileset.cpp +++ b/src/libtiled/tileset.cpp @@ -470,11 +470,12 @@ std::unique_ptr 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); @@ -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); @@ -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 diff --git a/src/libtiled/tileset.h b/src/libtiled/tileset.h index 76b9ad4654..cc0e9df6a4 100644 --- a/src/libtiled/tileset.h +++ b/src/libtiled/tileset.h @@ -190,7 +190,7 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThis wangSet); std::unique_ptr 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 &tiles); void removeTiles(const QList &tiles); void deleteTile(int id); @@ -204,7 +204,8 @@ class TILEDSHARED_EXPORT Tileset : public Object, public QEnableSharedFromThisimageSourceRect()); tilesetDocument->undoStack()->push(command); } @@ -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); } diff --git a/src/tiled/changetileimagesource.cpp b/src/tiled/changetileimagesource.cpp index 32666b18d6..fafca8667f 100644 --- a/src/tiled/changetileimagesource.cpp +++ b/src/tiled/changetileimagesource.cpp @@ -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 diff --git a/src/tiled/changetileimagesource.h b/src/tiled/changetileimagesource.h index f10c275321..044b557619 100644 --- a/src/tiled/changetileimagesource.h +++ b/src/tiled/changetileimagesource.h @@ -22,6 +22,7 @@ #include #include +#include namespace Tiled { @@ -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 diff --git a/src/tiled/editabletile.cpp b/src/tiled/editabletile.cpp index 2aff95f16f..79d6c692bf 100644 --- a/src/tiled/editabletile.cpp +++ b/src/tiled/editabletile.cpp @@ -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); } } diff --git a/src/tiled/editabletile.h b/src/tiled/editabletile.h index bed864ed38..4ce22bece5 100644 --- a/src/tiled/editabletile.h +++ b/src/tiled/editabletile.h @@ -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) @@ -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; @@ -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); @@ -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(); diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp index 6133d2baf8..0d04c6b305 100644 --- a/src/tiled/propertybrowser.cpp +++ b/src/tiled/propertybrowser.cpp @@ -908,10 +908,14 @@ void PropertyBrowser::addTileProperties() const Tile *tile = static_cast(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); @@ -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(); undoStack->push(new ChangeTileImageSource(mTilesetDocument, - tile, filePath.url)); + tile, filePath.url, tile->imageSourceRect())); break; } default: @@ -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: { diff --git a/src/tiled/propertybrowser.h b/src/tiled/propertybrowser.h index 7e8be3920f..dc6eb85e54 100644 --- a/src/tiled/propertybrowser.h +++ b/src/tiled/propertybrowser.h @@ -130,6 +130,7 @@ class PropertyBrowser : public QtTreePropertyBrowser RenderOrderProperty, LayerFormatProperty, ImageSourceProperty, + ImageSourceRectProperty, TilesetImageParametersProperty, FlippingProperty, DrawOrderProperty, diff --git a/src/tiled/tileanimationeditor.cpp b/src/tiled/tileanimationeditor.cpp index 2917a04759..c599de10b6 100644 --- a/src/tiled/tileanimationeditor.cpp +++ b/src/tiled/tileanimationeditor.cpp @@ -364,6 +364,9 @@ void TileAnimationEditor::setTilesetDocument(TilesetDocument *tilesetDocument) if (mTilesetDocument) { mUi->tilesetView->setModel(new TilesetModel(mTilesetDocument, mUi->tilesetView)); + connect(mTilesetDocument, &TilesetDocument::tilesetChanged, + this, &TileAnimationEditor::tilesetChanged); + connect(mTilesetDocument, &TilesetDocument::tileAnimationChanged, this, &TileAnimationEditor::tileAnimationChanged); @@ -428,6 +431,19 @@ void TileAnimationEditor::framesEdited() mFrameListModel->frames())); } +void TileAnimationEditor::tilesetChanged() +{ + auto *tilesetDocument = static_cast(sender()); + auto *tilesetView = mUi->tilesetView; + auto *model = tilesetView->tilesetModel(); + + if (tilesetDocument == mTilesetDocument) + setTile(nullptr); // It may be gone + + tilesetView->updateBackgroundColor(); + model->tilesetChanged(); +} + void TileAnimationEditor::setDefaultFrameTime(int duration) { mFrameListModel->setDefaultFrameTime(duration); diff --git a/src/tiled/tileanimationeditor.h b/src/tiled/tileanimationeditor.h index d513916a3c..fb6d744e67 100644 --- a/src/tiled/tileanimationeditor.h +++ b/src/tiled/tileanimationeditor.h @@ -64,6 +64,7 @@ public slots: private: void framesEdited(); + void tilesetChanged(); void tileAnimationChanged(Tile *tile); void currentObjectChanged(Object *object); diff --git a/src/tiled/tilesetdocument.cpp b/src/tiled/tilesetdocument.cpp index 899ea970f6..382d7a7203 100644 --- a/src/tiled/tilesetdocument.cpp +++ b/src/tiled/tilesetdocument.cpp @@ -409,11 +409,11 @@ void TilesetDocument::setTileType(Tile *tile, const QString &type) emit mapDocument->tileTypeChanged(tile); } -void TilesetDocument::setTileImage(Tile *tile, const QPixmap &image, const QUrl &source) +void TilesetDocument::setTileImage(Tile *tile, const QPixmap &image, const QUrl &source, const QRect& rect) { Q_ASSERT(tile->tileset() == mTileset.data()); - mTileset->setTileImage(tile, image, source); + mTileset->setTileImage(tile, image, source, rect); emit tileImageSourceChanged(tile); for (MapDocument *mapDocument : mapDocuments()) diff --git a/src/tiled/tilesetdocument.h b/src/tiled/tilesetdocument.h index 2716f9b94e..05ab1abae7 100644 --- a/src/tiled/tilesetdocument.h +++ b/src/tiled/tilesetdocument.h @@ -112,7 +112,7 @@ class TilesetDocument : public Document WangColorModel *wangColorModel(WangSet *wangSet); void setTileType(Tile *tile, const QString &type); - void setTileImage(Tile *tile, const QPixmap &image, const QUrl &source); + void setTileImage(Tile *tile, const QPixmap &image, const QUrl &source, const QRect& rect); void setTileProbability(Tile *tile, qreal probability); void swapTileObjectGroup(Tile *tile, std::unique_ptr &objectGroup);