1 /*
2 * tileset.cpp
3 * Copyright 2008-2015, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
4 * Copyright 2009, Edward Hutchins <eah1@yahoo.com>
5 *
6 * This file is part of libtiled.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 * EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "tileset.h"
31
32 #include "imagecache.h"
33 #include "tile.h"
34 #include "tilesetmanager.h"
35 #include "wangset.h"
36
37 #include <QBitmap>
38
39 #include "qtcompat_p.h"
40
41 namespace Tiled {
42
create(const QString & name,int tileWidth,int tileHeight,int tileSpacing,int margin)43 SharedTileset Tileset::create(const QString &name, int tileWidth, int tileHeight, int tileSpacing, int margin)
44 {
45 SharedTileset tileset(new Tileset(name, tileWidth, tileHeight,
46 tileSpacing, margin));
47 tileset->mWeakPointer = tileset;
48 TilesetManager::instance()->addTileset(tileset.data());
49 return tileset;
50 }
51
Tileset(QString name,int tileWidth,int tileHeight,int tileSpacing,int margin)52 Tileset::Tileset(QString name, int tileWidth, int tileHeight,
53 int tileSpacing, int margin):
54 Object(TilesetType),
55 mName(std::move(name)),
56 mTileWidth(tileWidth),
57 mTileHeight(tileHeight),
58 mTileSpacing(tileSpacing),
59 mMargin(margin),
60 mObjectAlignment(Unspecified),
61 mOrientation(Orthogonal),
62 mGridSize(tileWidth, tileHeight),
63 mColumnCount(0),
64 mExpectedColumnCount(0),
65 mExpectedRowCount(0),
66 mNextTileId(0),
67 mStatus(LoadingReady)
68 {
69 Q_ASSERT(tileSpacing >= 0);
70 Q_ASSERT(margin >= 0);
71 }
72
~Tileset()73 Tileset::~Tileset()
74 {
75 TilesetManager::instance()->removeTileset(this);
76 qDeleteAll(mTiles);
77 qDeleteAll(mWangSets);
78 }
79
setFormat(const QString & format)80 void Tileset::setFormat(const QString &format)
81 {
82 mFormat = format;
83 }
84
format() const85 QString Tileset::format() const
86 {
87 return mFormat;
88 }
89
90 /**
91 * Sets the tile size of this tileset. Affects how image is cut in loadImage.
92 *
93 * @warning Invalid for image collection tilesets!
94 */
setTileSize(QSize tileSize)95 void Tileset::setTileSize(QSize tileSize)
96 {
97 mTileWidth = tileSize.width();
98 mTileHeight = tileSize.height();
99 }
100
101 /**
102 * Sets the space in pixels between tiles in the tileset. Affects how image is
103 * cut in loadImage.
104 */
setTileSpacing(int tileSpacing)105 void Tileset::setTileSpacing(int tileSpacing)
106 {
107 Q_ASSERT(tileSpacing >= 0);
108 mTileSpacing = tileSpacing;
109 }
110
111 /**
112 * Sets the margin used by the tileset image. This is the number of pixels
113 * at the top-left border of the image that is skipped when cutting out tiles.
114 * Affects how image is cut in loadImage.
115 */
setMargin(int margin)116 void Tileset::setMargin(int margin)
117 {
118 Q_ASSERT(margin >= 0);
119 mMargin = margin;
120 }
121
122 /**
123 * Returns the location of the tile with the given ID.
124 */
findTileLocation(Tile * tile) const125 int Tileset::findTileLocation(Tile *tile) const
126 {
127 return mTiles.indexOf(tile);
128 }
129
130 /**
131 * Returns the tile with the given ID, creating it when it does not exist yet.
132 */
findOrCreateTile(int id)133 Tile *Tileset::findOrCreateTile(int id)
134 {
135 if (Tile *tile = mTilesById.value(id))
136 return tile;
137
138 mNextTileId = std::max(mNextTileId, id + 1);
139
140 auto tile = new Tile(id, this);
141 mTilesById[id] = tile;
142 mTiles.append(tile);
143
144 return tile;
145 }
146
147 /**
148 * Returns the number of tile rows in the tileset image.
149 */
rowCount() const150 int Tileset::rowCount() const
151 {
152 if (isCollection())
153 return 1;
154
155 return rowCountForHeight(mImageReference.size.height());
156 }
157
158 /**
159 * Sets the transparent color. Pixels with this color will be masked out
160 * when loadFromImage() is called.
161 */
setTransparentColor(const QColor & c)162 void Tileset::setTransparentColor(const QColor &c)
163 {
164 mImageReference.transparentColor = c;
165 }
166
167 /**
168 * Sets the image reference data for tileset image based tilesets.
169 *
170 * This function also sets the expected column count, which can be used later
171 * for automatic adjustment of tile indexes in case the tileset image width has
172 * changed.
173 */
setImageReference(const ImageReference & reference)174 void Tileset::setImageReference(const ImageReference &reference)
175 {
176 const QUrl oldImageSource = mImageReference.source;
177
178 mImageReference = reference;
179 mExpectedColumnCount = columnCountForWidth(mImageReference.size.width());
180 mExpectedRowCount = rowCountForHeight(mImageReference.size.height());
181
182 if (mImageReference.source != oldImageSource)
183 TilesetManager::instance()->tilesetImageSourceChanged(*this, oldImageSource);
184 }
185
186 /**
187 * Load this tileset from the given tileset \a image. This will replace
188 * existing tile images in this tileset with new ones. If the new image
189 * contains more tiles than exist in the tileset new tiles will be
190 * appended, if there are fewer tiles the excess images will be blanked.
191 *
192 * The tile width and height of this tileset must be higher than 0.
193 *
194 * @param image the image to load the tiles from
195 * @param source the url to the image, which will be remembered as the
196 * image source of this tileset.
197 * @return <code>true</code> if loading was successful, otherwise
198 * returns <code>false</code>
199 */
loadFromImage(const QImage & image,const QUrl & source)200 bool Tileset::loadFromImage(const QImage &image, const QUrl &source)
201 {
202 const QUrl oldImageSource = mImageReference.source;
203
204 mImageReference.source = source;
205
206 if (mImageReference.source != oldImageSource)
207 TilesetManager::instance()->tilesetImageSourceChanged(*this, oldImageSource);
208
209 if (image.isNull()) {
210 mImageReference.status = LoadingError;
211 return false;
212 }
213
214 const QSize tileSize = this->tileSize();
215 if (tileSize.isEmpty())
216 return false;
217
218 const int margin = this->margin();
219 const int spacing = this->tileSpacing();
220 const int stopWidth = image.width() - tileSize.width();
221 const int stopHeight = image.height() - tileSize.height();
222
223 int tileNum = 0;
224
225 for (int y = margin; y <= stopHeight; y += tileSize.height() + spacing) {
226 for (int x = margin; x <= stopWidth; x += tileSize.width() + spacing) {
227 const QImage tileImage = image.copy(x, y, tileSize.width(), tileSize.height());
228 QPixmap tilePixmap = QPixmap::fromImage(tileImage);
229 const QColor &transparent = mImageReference.transparentColor;
230
231 if (transparent.isValid()) {
232 const QImage mask = tileImage.createMaskFromColor(transparent.rgb());
233 tilePixmap.setMask(QBitmap::fromImage(mask));
234 }
235
236 auto it = mTilesById.find(tileNum);
237 if (it != mTilesById.end()) {
238 it.value()->setImage(tilePixmap);
239 } else {
240 auto tile = new Tile(tilePixmap, tileNum, this);
241 mTilesById.insert(tileNum, tile);
242 mTiles.insert(tileNum, tile);
243 }
244
245 ++tileNum;
246 }
247 }
248
249 // Blank out any remaining tiles to avoid confusion (todo: could be more clear)
250 for (Tile *tile : qAsConst(mTiles)) {
251 if (tile->id() >= tileNum) {
252 QPixmap tilePixmap = QPixmap(tileSize);
253 tilePixmap.fill();
254 tile->setImage(tilePixmap);
255 }
256 }
257
258 mNextTileId = std::max(mNextTileId, tileNum);
259
260 mImageReference.size = image.size();
261 mColumnCount = columnCountForWidth(mImageReference.size.width());
262 mImageReference.status = LoadingReady;
263
264 return true;
265 }
266
267 /**
268 * Exists only because the Python plugin interface does not handle QUrl (would
269 * be nice to add this). Assumes \a source is a local file when it would
270 * otherwise be a relative URL (without scheme).
271 *
272 * \sa setImageSource
273 */
loadFromImage(const QImage & image,const QString & source)274 bool Tileset::loadFromImage(const QImage &image, const QString &source)
275 {
276 return loadFromImage(image, Tiled::toUrl(source));
277 }
278
279 /**
280 * Convenience override that loads the image via the ImageCache.
281 */
loadFromImage(const QString & fileName)282 bool Tileset::loadFromImage(const QString &fileName)
283 {
284 const QUrl oldImageSource = mImageReference.source;
285 mImageReference.source = QUrl::fromLocalFile(fileName);
286 if (mImageReference.source != oldImageSource)
287 TilesetManager::instance()->tilesetImageSourceChanged(*this, oldImageSource);
288
289 return loadImage();
290 }
291
292 /**
293 * Tries to load the image this tileset is referring to.
294 *
295 * @return <code>true</code> if loading was successful, otherwise
296 * returns <code>false</code>
297 */
loadImage()298 bool Tileset::loadImage()
299 {
300 TilesheetParameters p;
301 p.fileName = Tiled::urlToLocalFileOrQrc(mImageReference.source);
302 p.tileWidth = mTileWidth;
303 p.tileHeight = mTileHeight;
304 p.spacing = mTileSpacing;
305 p.margin = mMargin;
306 p.transparentColor = mImageReference.transparentColor;
307
308 if (p.tileWidth <= 0 || p.tileHeight <= 0) {
309 mImageReference.status = LoadingError;
310 return false;
311 }
312
313 QImage image = ImageCache::loadImage(p.fileName);
314 if (image.isNull()) {
315 mImageReference.status = LoadingError;
316 return false;
317 }
318
319 auto tiles = ImageCache::cutTiles(p);
320
321 for (int tileNum = 0; tileNum < tiles.size(); ++tileNum) {
322 auto it = mTilesById.find(tileNum);
323 if (it != mTilesById.end()) {
324 it.value()->setImage(tiles.at(tileNum));
325 } else {
326 auto tile = new Tile(tiles.at(tileNum), tileNum, this);
327 mTilesById.insert(tileNum, tile);
328 mTiles.insert(tileNum, tile);
329 }
330 }
331
332 QPixmap blank;
333
334 // Blank out any remaining tiles to avoid confusion (todo: could be more clear)
335 for (Tile *tile : qAsConst(mTiles)) {
336 if (tile->id() >= tiles.size()) {
337 if (blank.isNull()) {
338 blank = QPixmap(mTileWidth, mTileHeight);
339 blank.fill();
340 }
341 tile->setImage(blank);
342 }
343 }
344
345 mNextTileId = std::max<int>(mNextTileId, tiles.size());
346
347 mImageReference.size = image.size();
348 mColumnCount = columnCountForWidth(mImageReference.size.width());
349 mImageReference.status = LoadingReady;
350
351 return true;
352 }
353
354 /**
355 * Returns whether the tiles in \a candidate use the same images as the ones
356 * in \a subject. Note that \a candidate is allowed to have additional tiles
357 * as well.
358 */
sameTileImages(const Tileset & subject,const Tileset & candidate)359 static bool sameTileImages(const Tileset &subject, const Tileset &candidate)
360 {
361 for (const Tile *subjectTile : subject.tiles()) {
362 const Tile *replacementTile = candidate.findTile(subjectTile->id());
363 if (!replacementTile)
364 return false;
365 if (subjectTile->imageSource() != replacementTile->imageSource())
366 return false;
367 }
368
369 return true;
370 }
371
372 /**
373 * This checks if there is a similar tileset in the given list.
374 * It is needed for replacing this tileset by its similar copy.
375 */
findSimilarTileset(const QVector<SharedTileset> & tilesets) const376 SharedTileset Tileset::findSimilarTileset(const QVector<SharedTileset> &tilesets) const
377 {
378 // The TilesetManager avoids loading the same external tileset twice, so
379 // for external tilesets we don't need to look for "similar" tilesets.
380 if (isExternal())
381 return SharedTileset();
382
383 for (const SharedTileset &candidate : tilesets) {
384 Q_ASSERT(candidate != this);
385
386 if (candidate->tileCount() != tileCount())
387 continue;
388 if (candidate->imageSource() != imageSource())
389 continue;
390 if (candidate->tileSize() != tileSize())
391 continue;
392 if (candidate->tileSpacing() != tileSpacing())
393 continue;
394 if (candidate->margin() != margin())
395 continue;
396 if (candidate->tileOffset() != tileOffset())
397 continue;
398
399 // For an image collection tileset, check the image sources
400 if (isCollection())
401 if (!sameTileImages(*this, *candidate))
402 continue;
403
404 return candidate;
405 }
406
407 return SharedTileset();
408 }
409
410 /**
411 * Changes the source of the tileset image.
412 *
413 * Only takes affect when loadImage is called.
414 */
setImageSource(const QUrl & imageSource)415 void Tileset::setImageSource(const QUrl &imageSource)
416 {
417 if (mImageReference.source != imageSource) {
418 const QUrl oldImageSource = mImageReference.source;
419 mImageReference.source = imageSource;
420 TilesetManager::instance()->tilesetImageSourceChanged(*this, oldImageSource);
421 }
422 }
423
424 /**
425 * Exists only because the Python plugin interface does not handle QUrl (would
426 * be nice to add this). Assumes \a source is a local file when it is either
427 * an absolute file path or would otherwise be a relative URL (without scheme).
428 *
429 * \sa loadFromImage
430 */
setImageSource(const QString & source)431 void Tileset::setImageSource(const QString &source)
432 {
433 setImageSource(Tiled::toUrl(source));
434 }
435
436 /**
437 * Returns the column count that this tileset would have if the tileset
438 * image would have the given \a width. This takes into account the tile
439 * size, margin and spacing.
440 */
columnCountForWidth(int width) const441 int Tileset::columnCountForWidth(int width) const
442 {
443 if (mTileWidth <= 0)
444 return 0;
445 return (width - mMargin + mTileSpacing) / (mTileWidth + mTileSpacing);
446 }
447
448 /**
449 * Returns the row count that this tileset would have if the tileset
450 * image would have the given \a width. This takes into account the tile
451 * size, margin and spacing.
452 */
rowCountForHeight(int height) const453 int Tileset::rowCountForHeight(int height) const
454 {
455 if (mTileHeight <= 0)
456 return 0;
457 return (height - mMargin + mTileSpacing) / (mTileHeight + mTileSpacing);
458 }
459
addWangSet(std::unique_ptr<WangSet> wangSet)460 void Tileset::addWangSet(std::unique_ptr<WangSet> wangSet)
461 {
462 Q_ASSERT(wangSet->tileset() == this);
463 mWangSets.append(wangSet.release());
464 }
465
466 /**
467 * Adds a wangSet.
468 */
insertWangSet(int index,std::unique_ptr<WangSet> wangSet)469 void Tileset::insertWangSet(int index, std::unique_ptr<WangSet> wangSet)
470 {
471 Q_ASSERT(wangSet->tileset() == this);
472 mWangSets.insert(index, wangSet.release());
473 }
474
475 /**
476 * @brief Tileset::takeWangSetAt Removes the wangset at a given index
477 * And returns it to the caller.
478 * @param index Index to take at.
479 * @return
480 */
takeWangSetAt(int index)481 std::unique_ptr<WangSet> Tileset::takeWangSetAt(int index)
482 {
483 return std::unique_ptr<WangSet>(mWangSets.takeAt(index));
484 }
485
486 /**
487 * Adds a new tile to the end of the tileset.
488 */
addTile(const QPixmap & image,const QUrl & source)489 Tile *Tileset::addTile(const QPixmap &image, const QUrl &source)
490 {
491 Tile *newTile = new Tile(takeNextTileId(), this);
492 newTile->setImage(image);
493 newTile->setImageSource(source);
494
495 mTilesById.insert(newTile->id(), newTile);
496 mTiles.append(newTile);
497 if (mTileHeight < image.height())
498 mTileHeight = image.height();
499 if (mTileWidth < image.width())
500 mTileWidth = image.width();
501 return newTile;
502 }
503
504 /**
505 * Adds the given list of tiles to this tileset.
506 *
507 * The tiles should already have unique tile IDs associated with them!
508 */
addTiles(const QList<Tile * > & tiles)509 void Tileset::addTiles(const QList<Tile *> &tiles)
510 {
511 for (Tile *tile : tiles) {
512 Q_ASSERT(tile->tileset() == this && !mTilesById.contains(tile->id()));
513 mTilesById.insert(tile->id(), tile);
514 mTiles.append(tile);
515 }
516
517 updateTileSize();
518 }
519
520 /**
521 * Removes the given list of tiles from this tileset.
522 *
523 * @warning The tiles are not deleted!
524 */
removeTiles(const QList<Tile * > & tiles)525 void Tileset::removeTiles(const QList<Tile *> &tiles)
526 {
527 for (Tile *tile : tiles) {
528 Q_ASSERT(tile->tileset() == this && mTilesById.contains(tile->id()));
529 mTilesById.remove(tile->id());
530 mTiles.removeOne(tile);
531 }
532
533 updateTileSize();
534 }
535
536 /**
537 * Removes the given tile from this set and deletes it.
538 */
deleteTile(int id)539 void Tileset::deleteTile(int id)
540 {
541 auto tile = mTilesById.take(id);
542 mTiles.removeOne(tile);
543 delete tile;
544 }
545
546 /**
547 * Moves the \a tiles from their current position the given \a position.
548 *
549 * Returns the previous locations of the moved tiles.
550 */
relocateTiles(const QList<Tile * > & tiles,int location)551 QList<int> Tileset::relocateTiles(const QList<Tile *> &tiles, int location)
552 {
553 QList<int> prevLocations;
554 for (Tile *tile : tiles) {
555 int fromIndex = mTiles.indexOf(tile);
556 mTiles.move(fromIndex, location);
557 if (fromIndex > location)
558 ++location; // insert the next tile after the previous one
559
560 prevLocations.append(fromIndex);
561 }
562 return prevLocations;
563 }
564
anyTileOutOfOrder() const565 bool Tileset::anyTileOutOfOrder() const
566 {
567 int tileId = 0;
568 for (const Tile *tile : mTiles) {
569 if (tile->id() != tileId)
570 return true;
571 ++tileId;
572 }
573 return false;
574 }
575
576 /**
577 * Sets the \a image to be used for the tile with the given \a id.
578 *
579 * This function makes sure the tile width and tile height properties of the
580 * tileset reflect the maximum size. It is only expected to be used for
581 * image collection tilesets.
582 */
setTileImage(Tile * tile,const QPixmap & image,const QUrl & source)583 void Tileset::setTileImage(Tile *tile,
584 const QPixmap &image,
585 const QUrl &source)
586 {
587 Q_ASSERT(isCollection());
588 Q_ASSERT(mTilesById.value(tile->id()) == tile);
589
590 const QSize previousImageSize = tile->image().size();
591 const QSize newImageSize = image.size();
592
593 tile->setImage(image);
594 tile->setImageSource(source);
595
596 if (previousImageSize != newImageSize) {
597 // Update our max. tile size
598 if (previousImageSize.height() == mTileHeight ||
599 previousImageSize.width() == mTileWidth) {
600 // This used to be the max image; we have to recompute
601 updateTileSize();
602 } else {
603 // Check if we have a new maximum
604 if (mTileHeight < newImageSize.height())
605 mTileHeight = newImageSize.height();
606 if (mTileWidth < newImageSize.width())
607 mTileWidth = newImageSize.width();
608 }
609 }
610 }
611
setOriginalTileset(const SharedTileset & original)612 void Tileset::setOriginalTileset(const SharedTileset &original)
613 {
614 mOriginalTileset = original;
615 }
616
617 /**
618 * When a tileset gets exported, a copy might be made to apply certain export
619 * options. In this case, the copy will have a (weak) pointer to the original
620 * tileset, to allow issues found during export to refer to this tileset.
621 */
originalTileset() const622 SharedTileset Tileset::originalTileset() const
623 {
624 SharedTileset original { mOriginalTileset };
625 if (!original)
626 original = sharedPointer();
627 return original;
628 }
629
swap(Tileset & other)630 void Tileset::swap(Tileset &other)
631 {
632 const Properties p = properties();
633 setProperties(other.properties());
634 other.setProperties(p);
635
636 std::swap(mFileName, other.mFileName);
637 std::swap(mImageReference, other.mImageReference);
638 std::swap(mTileWidth, other.mTileWidth);
639 std::swap(mTileHeight, other.mTileHeight);
640 std::swap(mTileSpacing, other.mTileSpacing);
641 std::swap(mMargin, other.mMargin);
642 std::swap(mTileOffset, other.mTileOffset);
643 std::swap(mObjectAlignment, other.mObjectAlignment);
644 std::swap(mOrientation, other.mOrientation);
645 std::swap(mGridSize, other.mGridSize);
646 std::swap(mColumnCount, other.mColumnCount);
647 std::swap(mExpectedColumnCount, other.mExpectedColumnCount);
648 std::swap(mExpectedRowCount, other.mExpectedRowCount);
649 std::swap(mTilesById, other.mTilesById);
650 std::swap(mTiles, other.mTiles);
651 std::swap(mNextTileId, other.mNextTileId);
652 std::swap(mWangSets, other.mWangSets);
653 std::swap(mStatus, other.mStatus);
654 std::swap(mBackgroundColor, other.mBackgroundColor);
655 std::swap(mFormat, other.mFormat);
656
657 // Don't swap mWeakPointer, since it's a reference to this.
658
659 // Update back references from tiles and Wang sets
660 for (auto tile : qAsConst(mTiles))
661 tile->mTileset = this;
662 for (auto wangSet : qAsConst(mWangSets))
663 wangSet->setTileset(this);
664
665 for (auto tile : qAsConst(other.mTiles))
666 tile->mTileset = &other;
667 for (auto wangSet : qAsConst(other.mWangSets))
668 wangSet->setTileset(&other);
669 }
670
clone() const671 SharedTileset Tileset::clone() const
672 {
673 SharedTileset c = create(mName, mTileWidth, mTileHeight, mTileSpacing, mMargin);
674 c->setProperties(properties());
675
676 // mFileName stays empty
677 c->mTileOffset = mTileOffset;
678 c->mObjectAlignment = mObjectAlignment;
679 c->mOrientation = mOrientation;
680 c->mGridSize = mGridSize;
681 c->mColumnCount = mColumnCount;
682 c->mNextTileId = mNextTileId;
683 c->mStatus = mStatus;
684 c->mBackgroundColor = mBackgroundColor;
685 c->mFormat = mFormat;
686 c->mTransformationFlags = mTransformationFlags;
687
688 for (auto tile : mTiles) {
689 const int id = tile->id();
690 Tile *clonedTile = tile->clone(c.data());
691
692 c->mTilesById.insert(id, clonedTile);
693 c->mTiles.append(clonedTile);
694 }
695
696 c->mWangSets.reserve(mWangSets.size());
697 for (WangSet *wangSet : mWangSets)
698 c->mWangSets.append(wangSet->clone(c.data()));
699
700 // Call setter to please TilesetManager, which starts watching the image of
701 // the tileset when it calls TilesetManager::tilesetImageSourceChanged.
702 c->setImageReference(mImageReference);
703
704 return c;
705 }
706
707 /**
708 * Sets tile size to the maximum size.
709 */
updateTileSize()710 void Tileset::updateTileSize()
711 {
712 int maxWidth = 0;
713 int maxHeight = 0;
714 for (Tile *tile : qAsConst(mTiles)) {
715 const QSize size = tile->size();
716 if (maxWidth < size.width())
717 maxWidth = size.width();
718 if (maxHeight < size.height())
719 maxHeight = size.height();
720 }
721 mTileWidth = maxWidth;
722 mTileHeight = maxHeight;
723 }
724
725
orientationToString(Tileset::Orientation orientation)726 QString Tileset::orientationToString(Tileset::Orientation orientation)
727 {
728 switch (orientation) {
729 case Tileset::Orthogonal:
730 return QStringLiteral("orthogonal");
731 case Tileset::Isometric:
732 return QStringLiteral("isometric");
733 }
734 return QString();
735 }
736
orientationFromString(const QString & string)737 Tileset::Orientation Tileset::orientationFromString(const QString &string)
738 {
739 Orientation orientation = Orthogonal;
740 if (string == QLatin1String("isometric"))
741 orientation = Isometric;
742 return orientation;
743 }
744
745 } // namespace Tiled
746