1 /*
2  * tileset.h
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 #pragma once
31 
32 #include "imagereference.h"
33 #include "object.h"
34 
35 #include <QColor>
36 #include <QList>
37 #include <QPixmap>
38 #include <QPoint>
39 #include <QSharedPointer>
40 #include <QString>
41 #include <QVector>
42 
43 #include <memory>
44 
45 class QImage;
46 
47 namespace Tiled {
48 
49 class Tile;
50 class Tileset;
51 class WangSet;
52 
53 typedef QSharedPointer<Tileset> SharedTileset;
54 
55 /**
56  * A tileset, representing a set of tiles.
57  *
58  * This class is meant to be used by either loading tiles from a tileset image
59  * (using loadFromImage) or by adding/removing individual tiles (using
60  * addTile, insertTiles and removeTiles). These two use-cases are not meant to
61  * be mixed.
62  */
63 class TILEDSHARED_EXPORT Tileset : public Object
64 {
65 public:
66     /**
67      * The orientation of the tileset determines the projection used in the
68      * TileCollisionDock and for the Wang color overlay of the TilesetView.
69      */
70     enum Orientation {
71         Orthogonal,
72         Isometric,
73     };
74 
75     /**
76      * Creates a new tileset with the given parameters. Using this function
77      * makes sure the internal weak pointer is initialized, which enables the
78      * sharedPointer() function.
79      *
80      * @param name        the name of the tileset
81      * @param tileWidth   the width of the tiles in the tileset
82      * @param tileHeight  the height of the tiles in the tileset
83      * @param tileSpacing the spacing between the tiles in the tileset image
84      * @param margin      the margin around the tiles in the tileset image
85      */
86     static SharedTileset create(const QString &name,
87                                 int tileWidth,
88                                 int tileHeight,
89                                 int tileSpacing = 0,
90                                 int margin = 0);
91 
92 private:
93     /**
94      * Private constructor. Use create() instead.
95      */
96     Tileset(QString name, int tileWidth, int tileHeight,
97             int tileSpacing = 0, int margin = 0);
98 
99 public:
100     QString exportFileName;
101     QString exportFormat;
102 
103     ~Tileset();
104 
105     const QString &name() const;
106     void setName(const QString &name);
107 
108     const QString &fileName() const;
109     void setFileName(const QString &fileName);
110     bool isExternal() const;
111 
112     void setFormat(const QString &format);
113     QString format() const;
114 
115     int tileWidth() const;
116     int tileHeight() const;
117 
118     QSize tileSize() const;
119     void setTileSize(QSize tileSize);
120 
121     int tileSpacing() const;
122     void setTileSpacing(int tileSpacing);
123 
124     int margin() const;
125     void setMargin(int margin);
126 
127     Alignment objectAlignment() const;
128     void setObjectAlignment(Alignment objectAlignment);
129 
130     QPoint tileOffset() const;
131     void setTileOffset(QPoint offset);
132 
133     Orientation orientation() const;
134     void setOrientation(Orientation orientation);
135 
136     QSize gridSize() const;
137     void setGridSize(QSize gridSize);
138 
139     const QMap<int, Tile*> &tilesById() const;
140     const QList<Tile*> &tiles() const;
141     inline Tile *findTile(int id) const;
tileAt(int id)142     Tile *tileAt(int id) const { return findTile(id); } // provided for Python
143     int findTileLocation(Tile *tile) const;
144     Tile *findOrCreateTile(int id);
145     int tileCount() const;
146 
147     int columnCount() const;
148     int rowCount() const;
149     void setColumnCount(int columnCount);
150     int expectedColumnCount() const;
151     int expectedRowCount() const;
152     void syncExpectedColumnsAndRows();
153 
154     int imageWidth() const;
155     int imageHeight() const;
156 
157     QColor transparentColor() const;
158     void setTransparentColor(const QColor &c);
159 
160     const QColor &backgroundColor() const;
161     void setBackgroundColor(QColor color);
162 
163     void setImageReference(const ImageReference &reference);
164 
165     bool loadFromImage(const QImage &image, const QUrl &source);
166     bool loadFromImage(const QImage &image, const QString &source);
167     bool loadFromImage(const QString &fileName);
168     bool loadImage();
169 
170     SharedTileset findSimilarTileset(const QVector<SharedTileset> &tilesets) const;
171 
172     const QUrl &imageSource() const;
173     void setImageSource(const QUrl &imageSource);
174     void setImageSource(const QString &url);
175     QString imageSourceString() const;
176 
177     bool isCollection() const;
178 
179     int columnCountForWidth(int width) const;
180     int rowCountForHeight(int height) const;
181 
182     const QList<WangSet*> &wangSets() const;
183     int wangSetCount() const;
184     WangSet *wangSet(int index) const;
185 
186     void addWangSet(std::unique_ptr<WangSet> wangSet);
187     void insertWangSet(int index, std::unique_ptr<WangSet> wangSet);
188     std::unique_ptr<WangSet> takeWangSetAt(int index);
189 
190     Tile *addTile(const QPixmap &image, const QUrl &source = QUrl());
191     void addTiles(const QList<Tile*> &tiles);
192     void removeTiles(const QList<Tile *> &tiles);
193     void deleteTile(int id);
194     QList<int> relocateTiles(const QList<Tile *> &tiles, int location);
195 
196     bool anyTileOutOfOrder() const;
197 
198     void setNextTileId(int nextId);
199     int nextTileId() const;
200     int takeNextTileId();
201 
202     void setTileImage(Tile *tile,
203                       const QPixmap &image,
204                       const QUrl &source = QUrl());
205 
206     SharedTileset sharedPointer() const;
207 
208     void setOriginalTileset(const SharedTileset &original);
209     SharedTileset originalTileset() const;
210 
211     void setStatus(LoadingStatus status);
212     void setImageStatus(LoadingStatus status);
213     LoadingStatus status() const;
214     LoadingStatus imageStatus() const;
215 
216     enum TransformationFlag {
217         NoTransformation        = 0,
218         AllowFlipHorizontally   = 1 << 0,
219         AllowFlipVertically     = 1 << 1,
220         AllowRotate             = 1 << 2,
221         PreferUntransformed     = 1 << 3,
222     };
223     Q_DECLARE_FLAGS(TransformationFlags, TransformationFlag)
224 
225     TransformationFlags transformationFlags() const;
226     void setTransformationFlags(TransformationFlags flags);
227 
228     void swap(Tileset &other);
229 
230     SharedTileset clone() const;
231 
232     /**
233      * Helper function that converts the tileset orientation to a string value.
234      * Useful for map writers.
235      *
236      * @return The tileset orientation as a lowercase string.
237      */
238     static QString orientationToString(Orientation);
239 
240     /**
241      * Helper function that converts a string to a tileset orientation enumerator.
242      * Useful for map readers.
243      *
244      * @return The tileset orientation matching the given string, or
245      *         Tileset::Orthogonal if the string is unrecognized.
246      */
247     static Orientation orientationFromString(const QString &);
248 
249 private:
250     void updateTileSize();
251 
252     QString mName;
253     QString mFileName;
254     ImageReference mImageReference;
255     int mTileWidth;
256     int mTileHeight;
257     int mTileSpacing;
258     int mMargin;
259     QPoint mTileOffset;
260     Alignment mObjectAlignment;
261     Orientation mOrientation;
262     QSize mGridSize;
263     int mColumnCount;
264     int mExpectedColumnCount;
265     int mExpectedRowCount;
266     int mNextTileId;
267     QMap<int, Tile*> mTilesById;
268     QList<Tile*> mTiles;
269     QList<WangSet*> mWangSets;
270     LoadingStatus mStatus;
271     QColor mBackgroundColor;
272     QString mFormat;
273     TransformationFlags mTransformationFlags;
274 
275     QWeakPointer<Tileset> mWeakPointer;
276     QWeakPointer<Tileset> mOriginalTileset;
277 };
278 
279 
280 /**
281  * Returns the name of this tileset.
282  */
name()283 inline const QString &Tileset::name() const
284 {
285     return mName;
286 }
287 
288 /**
289  * Sets the name of this tileset.
290  */
setName(const QString & name)291 inline void Tileset::setName(const QString &name)
292 {
293     mName = name;
294 }
295 
296 /**
297  * Returns the file name of this tileset. When the tileset isn't an
298  * external tileset, the file name is empty.
299  */
fileName()300 inline const QString &Tileset::fileName() const
301 {
302     return mFileName;
303 }
304 
305 /**
306  * Sets the filename of this tileset.
307  */
setFileName(const QString & fileName)308 inline void Tileset::setFileName(const QString &fileName)
309 {
310     mFileName = fileName;
311 }
312 
313 /**
314  * Returns whether this tileset is external.
315  */
isExternal()316 inline bool Tileset::isExternal() const
317 {
318     return !mFileName.isEmpty();
319 }
320 
321 /**
322  * Returns the maximum width of the tiles in this tileset.
323  */
tileWidth()324 inline int Tileset::tileWidth() const
325 {
326     return mTileWidth;
327 }
328 
329 /**
330  * Returns the maximum height of the tiles in this tileset.
331  */
tileHeight()332 inline int Tileset::tileHeight() const
333 {
334     return mTileHeight;
335 }
336 
337 /**
338  * Returns the maximum size of the tiles in this tileset.
339  */
tileSize()340 inline QSize Tileset::tileSize() const
341 {
342     return QSize(mTileWidth, mTileHeight);
343 }
344 
345 /**
346  * Returns the spacing between the tiles in the tileset image.
347  */
tileSpacing()348 inline int Tileset::tileSpacing() const
349 {
350     return mTileSpacing;
351 }
352 
353 /**
354  * Returns the margin around the tiles in the tileset image.
355  */
margin()356 inline int Tileset::margin() const
357 {
358     return mMargin;
359 }
360 
361 /**
362  * Returns the alignment to use for tile objects.
363  */
objectAlignment()364 inline Alignment Tileset::objectAlignment() const
365 {
366     return mObjectAlignment;
367 }
368 
369 /**
370  * @see objectAlignment
371  */
setObjectAlignment(Alignment objectAlignment)372 inline void Tileset::setObjectAlignment(Alignment objectAlignment)
373 {
374     mObjectAlignment = objectAlignment;
375 }
376 
377 /**
378  * Returns the offset that is applied when drawing the tiles in this
379  * tileset.
380  */
tileOffset()381 inline QPoint Tileset::tileOffset() const
382 {
383     return mTileOffset;
384 }
385 
386 /**
387  * @see tileOffset
388  */
setTileOffset(QPoint offset)389 inline void Tileset::setTileOffset(QPoint offset)
390 {
391     mTileOffset = offset;
392 }
393 
394 /**
395  * Returns the orientation of the tiles in this tileset.
396  */
orientation()397 inline Tileset::Orientation Tileset::orientation() const
398 {
399     return mOrientation;
400 }
401 
402 /**
403  * @see orientation
404  */
setOrientation(Orientation orientation)405 inline void Tileset::setOrientation(Orientation orientation)
406 {
407     mOrientation = orientation;
408 }
409 
410 /**
411  * Returns the grid size that is used when the tileset has Isometric
412  * orientation.
413  */
gridSize()414 inline QSize Tileset::gridSize() const
415 {
416     return mGridSize;
417 }
418 
419 /**
420  * @see gridSize
421  */
setGridSize(QSize gridSize)422 inline void Tileset::setGridSize(QSize gridSize)
423 {
424     mGridSize = gridSize;
425 }
426 
tilesById()427 inline const QMap<int, Tile *> &Tileset::tilesById() const
428 {
429     return mTilesById;
430 }
431 
432 /**
433  * Returns a const reference to the tiles in this tileset.
434  */
tiles()435 inline const QList<Tile*> &Tileset::tiles() const
436 {
437     return mTiles;
438 }
439 
440 /**
441  * Returns the tile with the given tile ID. The tile IDs are local to this
442  * tileset.
443  */
findTile(int id)444 inline Tile *Tileset::findTile(int id) const
445 {
446     return mTilesById.value(id);
447 }
448 
449 /**
450  * Returns the number of tiles in this tileset.
451  *
452  * Note that the tiles are not necessarily consecutive.
453  */
tileCount()454 inline int Tileset::tileCount() const
455 {
456     return mTiles.size();
457 }
458 
459 /**
460  * Returns the number of tile columns in the tileset image.
461  */
columnCount()462 inline int Tileset::columnCount() const
463 {
464     return mColumnCount;
465 }
466 
467 /**
468  * Sets the column count to use when displaying this tileset. For tileset image
469  * based tilesets, this reflects the number of tile columns in the image.
470  */
setColumnCount(int columnCount)471 inline void Tileset::setColumnCount(int columnCount)
472 {
473     mColumnCount = columnCount;
474 }
475 
476 /**
477  * Returns the number of tile columns expected to be in the tileset image. This
478  * may differ from the actual amount of columns encountered when loading the
479  * image, and can be used for automatically adjusting tile indexes.
480  */
expectedColumnCount()481 inline int Tileset::expectedColumnCount() const
482 {
483     return mExpectedColumnCount;
484 }
485 
486 /**
487  * Returns the number of tile rows expected to be in the tileset image. This
488  * may differ from the actual amount of rows encountered when loading the
489  * image, and is checked when automatically adjusting tile indexes.
490  */
expectedRowCount()491 inline int Tileset::expectedRowCount() const
492 {
493     return mExpectedRowCount;
494 }
495 
496 /**
497  * Sets the expected column and row count to the actual column count. Usually
498  * called after checking with the user whether he wants the map to be adjusted
499  * to a change to the tileset image size.
500  */
syncExpectedColumnsAndRows()501 inline void Tileset::syncExpectedColumnsAndRows()
502 {
503     mExpectedColumnCount = mColumnCount;
504     mExpectedRowCount = rowCount();
505 }
506 
507 /**
508  * Returns the width of the tileset image.
509  */
imageWidth()510 inline int Tileset::imageWidth() const
511 {
512     return mImageReference.size.width();
513 }
514 
515 /**
516  * Returns the height of the tileset image.
517  */
imageHeight()518 inline int Tileset::imageHeight() const
519 {
520     return mImageReference.size.height();
521 }
522 
523 /**
524  * Returns the transparent color, or an invalid color if no transparent
525  * color is used.
526  */
transparentColor()527 inline QColor Tileset::transparentColor() const
528 {
529     return mImageReference.transparentColor;
530 }
531 
532 /**
533  * Returns the background color of this tileset.
534  */
backgroundColor()535 inline const QColor &Tileset::backgroundColor() const
536 {
537     return mBackgroundColor;
538 }
539 
540 /**
541  * Sets the background color of this tileset.
542  */
setBackgroundColor(QColor color)543 inline void Tileset::setBackgroundColor(QColor color)
544 {
545     mBackgroundColor = color;
546 }
547 
548 /**
549  * Returns the URL of the external image that contains the tiles in
550  * this tileset. Is an empty string when this tileset doesn't have a
551  * tileset image.
552  */
imageSource()553 inline const QUrl &Tileset::imageSource() const
554 {
555     return mImageReference.source;
556 }
557 
558 /**
559  * QString-API for Python.
560  */
imageSourceString()561 inline QString Tileset::imageSourceString() const
562 {
563     const QUrl &url = imageSource();
564     return url.isLocalFile() ? url.toLocalFile() : url.toString();
565 }
566 
567 /**
568  * Returns whether this tileset is a collection of images. In this case, the
569  * tileset itself has no image source.
570  */
isCollection()571 inline bool Tileset::isCollection() const
572 {
573     return imageSource().isEmpty();
574 }
575 
wangSets()576 inline const QList<WangSet*> &Tileset::wangSets() const
577 {
578     return mWangSets;
579 }
580 
wangSetCount()581 inline int Tileset::wangSetCount() const
582 {
583     return mWangSets.size();
584 }
585 
wangSet(int index)586 inline WangSet *Tileset::wangSet(int index) const
587 {
588     return index >= 0 ? mWangSets[index] : nullptr;
589 }
590 
591 /**
592  * Sets the next id to be used for tiles in this tileset.
593  */
setNextTileId(int nextId)594 inline void Tileset::setNextTileId(int nextId)
595 {
596     Q_ASSERT(nextId > 0);
597     mNextTileId = nextId;
598 }
599 
600 /**
601  * Returns the next tile id for this tileset.
602  */
nextTileId()603 inline int Tileset::nextTileId() const
604 {
605     return mNextTileId;
606 }
607 
608 /**
609  * Returns the next tile id for this tileset and allocates a new one.
610  */
takeNextTileId()611 inline int Tileset::takeNextTileId()
612 {
613     return mNextTileId++;
614 }
615 
sharedPointer()616 inline SharedTileset Tileset::sharedPointer() const
617 {
618     return SharedTileset(mWeakPointer);
619 }
620 
621 /**
622  * Sets the status of this tileset.
623  */
setStatus(LoadingStatus status)624 inline void Tileset::setStatus(LoadingStatus status)
625 {
626     mStatus = status;
627 }
628 
629 /**
630  * Sets the loading status of this tileset's image.
631  */
setImageStatus(LoadingStatus status)632 inline void Tileset::setImageStatus(LoadingStatus status)
633 {
634     mImageReference.status = status;
635 }
636 
637 /**
638  * Returns the loading status of this tileset.
639  *
640  * Only valid for external tilesets (fileName() != empty).
641  */
status()642 inline LoadingStatus Tileset::status() const
643 {
644     return mStatus;
645 }
646 
647 /**
648  * Returns the loading status of this tileset's image.
649  *
650  * Only valid for tilesets based on a single image (imageSource() != empty).
651  */
imageStatus()652 inline LoadingStatus Tileset::imageStatus() const
653 {
654     return mImageReference.status;
655 }
656 
transformationFlags()657 inline Tileset::TransformationFlags Tileset::transformationFlags() const
658 {
659     return mTransformationFlags;
660 }
661 
setTransformationFlags(TransformationFlags flags)662 inline void Tileset::setTransformationFlags(TransformationFlags flags)
663 {
664     mTransformationFlags = flags;
665 }
666 
667 } // namespace Tiled
668 
669 Q_DECLARE_METATYPE(Tiled::Tileset*)
670 Q_DECLARE_METATYPE(Tiled::SharedTileset)
671 
672 Q_DECLARE_OPERATORS_FOR_FLAGS(Tiled::Tileset::TransformationFlags)
673