Skip to content

Commit

Permalink
Add Repeat X and Repeat Y properties to Image Layers
Browse files Browse the repository at this point in the history
Introduce the boolean properties "Repeat X" and "Repeat Y" to Image
Layers. For rendering inside Tiled, this repetition is limited to the
overall bounds of all tile layers.
As a result, drawing will be cut off when an offset is set along with
the repeating property for that axis.

The YY plugin will export these properties accordingly as
"Horizontal Tile" and "Vertical Tile".
For backwards compatibility however, the respective custom properties
"htiled" and "vtiled" take precedence over the built-in ones.
  • Loading branch information
krukai committed Dec 9, 2021
1 parent 3b0b9a5 commit 1d53803
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 10 deletions.
22 changes: 22 additions & 0 deletions src/libtiled/imagelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@ class TILEDSHARED_EXPORT ImageLayer : public Layer
*/
bool isEmpty() const override;

/**
* Returns true if the image of this layer repeats along the x axis.
*/
bool repeatX() const { return mRepeatX; }

/**
* Returns true if the image of this layer repeats along the y axis.
*/
bool repeatY() const { return mRepeatY; }

/**
* Sets whether the image of this layer repeats along the x axis.
*/
void setRepeatX(bool repeatX) { mRepeatX = repeatX; }

/**
* Sets whether the image of this layer repeats along the y axis.
*/
void setRepeatY(bool repeatY) { mRepeatY = repeatY; }

ImageLayer *clone() const override;

protected:
Expand All @@ -123,6 +143,8 @@ class TILEDSHARED_EXPORT ImageLayer : public Layer
QUrl mImageSource;
QColor mTransparentColor;
QPixmap mImage;
bool mRepeatX = false;
bool mRepeatY = false;
};

} // namespace Tiled
3 changes: 3 additions & 0 deletions src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,9 @@ std::unique_ptr<ImageLayer> MapReaderPrivate::readImageLayer()
auto imageLayer = std::make_unique<ImageLayer>(name, x, y);
readLayerAttributes(*imageLayer, atts);

imageLayer->setRepeatX(atts.value(QLatin1String("repeatx")).toInt());
imageLayer->setRepeatY(atts.value(QLatin1String("repeaty")).toInt());

// Image layer pixel position moved from x/y to offsetx/offsety for
// consistency with other layers. This is here for backwards compatibility.
if (!atts.hasAttribute(QLatin1String("offsetx")))
Expand Down
18 changes: 16 additions & 2 deletions src/libtiled/maprenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,18 @@ MapRenderer::~MapRenderer()

QRectF MapRenderer::boundingRect(const ImageLayer *imageLayer) const
{
return QRectF(QPointF(), imageLayer->image().size());
QRectF bounds = QRectF(QPointF(), imageLayer->image().size());

if (imageLayer->repeatX()) {
bounds.setLeft(INT_MIN / 512);
bounds.setRight(INT_MAX / 256);
}
if (imageLayer->repeatY()) {
bounds.setTop(INT_MIN / 512);
bounds.setBottom(INT_MAX / 256);
}

return bounds;
}

QPainterPath MapRenderer::pointShape(const QPointF &position) const
Expand Down Expand Up @@ -115,7 +126,10 @@ void MapRenderer::drawImageLayer(QPainter *painter,
{
Q_UNUSED(exposed)

painter->drawPixmap(QPointF(), tinted(imageLayer->image(), imageLayer->effectiveTintColor()));
painter->save();
painter->setBrush(tinted(imageLayer->image(), imageLayer->effectiveTintColor()));
painter->drawRect(boundingRect(imageLayer));
painter->restore();
}

void MapRenderer::drawPointObject(QPainter *painter, const QColor &color) const
Expand Down
3 changes: 3 additions & 0 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,9 @@ void MapWriterPrivate::writeImageLayer(QXmlStreamWriter &w,
w.writeStartElement(QStringLiteral("imagelayer"));
writeLayerAttributes(w, imageLayer);

w.writeAttribute(QStringLiteral("repeatx"), QString::number(imageLayer.repeatX()));
w.writeAttribute(QStringLiteral("repeaty"), QString::number(imageLayer.repeatY()));

// Write the image element
const QUrl &imageSource = imageLayer.imageSource();
if (!imageSource.isEmpty()) {
Expand Down
6 changes: 4 additions & 2 deletions src/plugins/yy/yyplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1164,8 +1164,10 @@ static std::unique_ptr<GMRLayer> processImageLayer(const ImageLayer *imageLayer)
gmrBackgroundLayer->x = layerOffset.x();
gmrBackgroundLayer->y = layerOffset.y();

gmrBackgroundLayer->htiled = optionalProperty(imageLayer, "htiled", gmrBackgroundLayer->htiled);
gmrBackgroundLayer->vtiled = optionalProperty(imageLayer, "vtiled", gmrBackgroundLayer->vtiled);
// Give custom properties priority to ensure backwards compatibility
gmrBackgroundLayer->htiled = optionalProperty(imageLayer, "htiled", imageLayer->repeatX());
gmrBackgroundLayer->vtiled = optionalProperty(imageLayer, "vtiled", imageLayer->repeatY());

gmrBackgroundLayer->hspeed = optionalProperty(imageLayer, "hspeed", gmrBackgroundLayer->hspeed);
gmrBackgroundLayer->vspeed = optionalProperty(imageLayer, "vspeed", gmrBackgroundLayer->vspeed);
gmrBackgroundLayer->stretch = optionalProperty(imageLayer, "stretch", gmrBackgroundLayer->stretch);
Expand Down
14 changes: 13 additions & 1 deletion src/tiled/changeimagelayerproperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ ChangeImageLayerProperties::ChangeImageLayerProperties(
MapDocument *mapDocument,
ImageLayer *imageLayer,
const QColor &color,
const QUrl &newSource)
const QUrl &newSource,
bool newRepeatX,
bool newRepeatY)
: QUndoCommand(
QCoreApplication::translate(
"Undo Commands", "Change Image Layer Properties"))
Expand All @@ -43,6 +45,10 @@ ChangeImageLayerProperties::ChangeImageLayerProperties(
, mRedoColor(color)
, mUndoSource(imageLayer->imageSource())
, mRedoSource(newSource)
, mUndoRepeatX(imageLayer->repeatX())
, mRedoRepeatX(newRepeatX)
, mUndoRepeatY(imageLayer->repeatY())
, mRedoRepeatY(newRepeatY)
{
}

Expand All @@ -55,6 +61,9 @@ void ChangeImageLayerProperties::redo()
else
mImageLayer->loadFromImage(mRedoSource);

mImageLayer->setRepeatX(mRedoRepeatX);
mImageLayer->setRepeatY(mRedoRepeatY);

emit mMapDocument->imageLayerChanged(mImageLayer);
}

Expand All @@ -67,6 +76,9 @@ void ChangeImageLayerProperties::undo()
else
mImageLayer->loadFromImage(mUndoSource);

mImageLayer->setRepeatX(mUndoRepeatX);
mImageLayer->setRepeatY(mUndoRepeatY);

emit mMapDocument->imageLayerChanged(mImageLayer);
}

10 changes: 9 additions & 1 deletion src/tiled/changeimagelayerproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ class ChangeImageLayerProperties : public QUndoCommand
* @param imageLayer the image layer to modify
* @param newColor the new transparent color to apply
* @param newSource the new image source to apply
* @param newRepeatX the new repetition along the x axis to apply
* @param newRepeatY the new repetition along the y axis to apply
*/
ChangeImageLayerProperties(MapDocument *mapDocument,
ImageLayer *imageLayer,
const QColor &newColor,
const QUrl &newSource);
const QUrl &newSource,
bool newRepeatX,
bool newRepeatY);

void undo() override;
void redo() override;
Expand All @@ -58,6 +62,10 @@ class ChangeImageLayerProperties : public QUndoCommand
const QColor mRedoColor;
const QUrl mUndoSource;
const QUrl mRedoSource;
const bool mUndoRepeatX;
const bool mRedoRepeatX;
const bool mUndoRepeatY;
const bool mRedoRepeatY;
};

} // namespace Tiled
35 changes: 33 additions & 2 deletions src/tiled/editableimagelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ void EditableImageLayer::setTransparentColor(const QColor &transparentColor)
asset()->push(new ChangeImageLayerProperties(doc,
imageLayer(),
transparentColor,
imageSource()));
imageSource(),
repeatX(),
repeatY()));
} else {
imageLayer()->setTransparentColor(transparentColor);
if (!imageSource().isEmpty())
Expand All @@ -56,7 +58,9 @@ void EditableImageLayer::setImageSource(const QUrl &imageSource)
asset()->push(new ChangeImageLayerProperties(doc,
imageLayer(),
transparentColor(),
imageSource));
imageSource,
repeatX(),
repeatY()));
} else {
if (imageSource.isEmpty())
imageLayer()->resetImage();
Expand All @@ -71,6 +75,33 @@ void EditableImageLayer::setImage(ScriptImage *image, const QUrl &source)
imageLayer()->loadFromImage(QPixmap::fromImage(image->image()), source);
}

void EditableImageLayer::setRepeatX(bool repeatX)
{
if (auto doc = mapDocument()) {
asset()->push(new ChangeImageLayerProperties(doc,
imageLayer(),
transparentColor(),
imageSource(),
repeatX,
repeatY()));
} else {
imageLayer()->setRepeatX(repeatX);
}
}

void EditableImageLayer::setRepeatY(bool repeatY)
{
if (auto doc = mapDocument()) {
asset()->push(new ChangeImageLayerProperties(doc,
imageLayer(),
transparentColor(),
imageSource(),
repeatX(),
repeatY));
} else {
imageLayer()->setRepeatY(repeatY);
}
}
} // namespace Tiled

#include "moc_editableimagelayer.cpp"
16 changes: 16 additions & 0 deletions src/tiled/editableimagelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class EditableImageLayer : public EditableLayer

Q_PROPERTY(QColor transparentColor READ transparentColor WRITE setTransparentColor)
Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource)
Q_PROPERTY(bool repeatX READ repeatX WRITE setRepeatX)
Q_PROPERTY(bool repeatY READ repeatY WRITE setRepeatY)

public:
Q_INVOKABLE explicit EditableImageLayer(const QString &name = QString(),
Expand All @@ -44,9 +46,13 @@ class EditableImageLayer : public EditableLayer

const QColor &transparentColor() const;
const QUrl &imageSource() const;
bool repeatX() const;
bool repeatY() const;

void setTransparentColor(const QColor &transparentColor);
void setImageSource(const QUrl &imageSource);
void setRepeatX(bool repeatX);
void setRepeatY(bool repeatY);

Q_INVOKABLE void setImage(Tiled::ScriptImage *image, const QUrl &source = QUrl());

Expand All @@ -65,6 +71,16 @@ inline const QUrl &EditableImageLayer::imageSource() const
return imageLayer()->imageSource();
}

inline bool EditableImageLayer::repeatX() const
{
return imageLayer()->repeatX();
}

inline bool EditableImageLayer::repeatY() const
{
return imageLayer()->repeatY();
}

inline ImageLayer *EditableImageLayer::imageLayer() const
{
return static_cast<ImageLayer*>(layer());
Expand Down
43 changes: 41 additions & 2 deletions src/tiled/propertybrowser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,9 @@ void PropertyBrowser::addImageLayerProperties()

addProperty(ColorProperty, QMetaType::QColor, tr("Transparent Color"), groupProperty);

addProperty(RepeatXProperty, QMetaType::Bool, tr("Repeat X"), groupProperty);
addProperty(RepeatYProperty, QMetaType::Bool, tr("Repeat Y"), groupProperty);

addProperty(groupProperty);
}

Expand Down Expand Up @@ -1278,19 +1281,53 @@ QUndoCommand *PropertyBrowser::applyImageLayerValueTo(PropertyId id, const QVari
case ImageSourceProperty: {
const FilePath imageSource = val.value<FilePath>();
const QColor &color = imageLayer->transparentColor();
const bool repeatX = imageLayer->repeatX();
const bool repeatY = imageLayer->repeatY();
command = new ChangeImageLayerProperties(mMapDocument,
imageLayer,
color,
imageSource.url);
imageSource.url,
repeatX,
repeatY);
break;
}
case ColorProperty: {
const QColor color = val.value<QColor>();
const QUrl &imageSource = imageLayer->imageSource();
const bool repeatX = imageLayer->repeatX();
const bool repeatY = imageLayer->repeatY();
command = new ChangeImageLayerProperties(mMapDocument,
imageLayer,
color,
imageSource,
repeatX,
repeatY);
break;
}
case RepeatXProperty: {
const bool repeatX = val.toBool();
const QUrl &imageSource = imageLayer->imageSource();
const QColor &color = imageLayer->transparentColor();
const bool repeatY = imageLayer->repeatY();
command = new ChangeImageLayerProperties(mMapDocument,
imageLayer,
color,
imageSource,
repeatX,
repeatY);
break;
}
case RepeatYProperty: {
const bool repeatY = val.toBool();
const QUrl &imageSource = imageLayer->imageSource();
const QColor &color = imageLayer->transparentColor();
const bool repeatX = imageLayer->repeatX();
command = new ChangeImageLayerProperties(mMapDocument,
imageLayer,
color,
imageSource);
imageSource,
repeatX,
repeatY);
break;
}
default:
Expand Down Expand Up @@ -1766,6 +1803,8 @@ void PropertyBrowser::updateProperties()
const ImageLayer *imageLayer = static_cast<const ImageLayer*>(layer);
mIdToProperty[ImageSourceProperty]->setValue(QVariant::fromValue(FilePath { imageLayer->imageSource() }));
mIdToProperty[ColorProperty]->setValue(imageLayer->transparentColor());
mIdToProperty[RepeatXProperty]->setValue(imageLayer->repeatX());
mIdToProperty[RepeatYProperty]->setValue(imageLayer->repeatY());
break;
}
case Layer::GroupLayerType:
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/propertybrowser.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ class PropertyBrowser : public QtTreePropertyBrowser
OffsetXProperty,
OffsetYProperty,
ParallaxFactorProperty,
RepeatXProperty,
RepeatYProperty,
ColorProperty,
BackgroundColorProperty,
TileWidthProperty,
Expand Down

0 comments on commit 1d53803

Please sign in to comment.