3838namespace Tiled {
3939
4040Tileset::Tileset (QString name, int tileWidth, int tileHeight,
41- int tileSpacing, int margin)
41+ int tileSpacing, int margin, bool isAtlas )
4242 : Object(TilesetType)
4343 , mName (std::move(name))
4444 , mTileWidth (tileWidth)
4545 , mTileHeight (tileHeight)
4646 , mTileSpacing (tileSpacing)
4747 , mMargin (margin)
4848 , mGridSize (tileWidth, tileHeight)
49+ , mAtlas (isAtlas)
4950{
5051 Q_ASSERT (tileSpacing >= 0 );
5152 Q_ASSERT (margin >= 0 );
@@ -253,40 +254,58 @@ bool Tileset::initializeTilesetTiles()
253254 if (mImageReference .transparentColor .isValid ())
254255 mImage .setMask (mImage .createMaskFromColor (mImageReference .transparentColor ));
255256
256- QVector<QRect> tileRects;
257-
258- for (int y = mMargin ; y <= mImage .height () - mTileHeight ; y += mTileHeight + mTileSpacing )
259- for (int x = mMargin ; x <= mImage .width () - mTileWidth ; x += mTileWidth + mTileSpacing )
260- tileRects.append (QRect (x, y, mTileWidth , mTileHeight ));
261-
262- for (int tileNum = 0 ; tileNum < tileRects.size (); ++tileNum) {
263- auto it = mTilesById .find (tileNum);
264- if (it != mTilesById .end ()) {
265- it.value ()->setImage (QPixmap ()); // make sure it uses the tileset's image
266- it.value ()->setImageRect (tileRects.at (tileNum));
267- } else {
268- auto tile = new Tile (tileNum, this );
269- tile->setImageRect (tileRects.at (tileNum));
270- mTilesById .insert (tileNum, tile);
271- mTiles .insert (tileNum, tile);
257+ bool needsRectGeneration = true ;
258+ if (isAtlas ()) {
259+ for (Tile *tile : std::as_const (mTiles )) {
260+ if (!tile->imageRect ().isNull ()) {
261+ needsRectGeneration = false ;
262+ break ;
263+ }
272264 }
273265 }
274266
275- QPixmap blank;
267+ if (needsRectGeneration) {
268+ QVector<QRect> tileRects;
269+
270+ for (int y = mMargin ; y <= mImage .height () - mTileHeight ; y += mTileHeight + mTileSpacing )
271+ for (int x = mMargin ; x <= mImage .width () - mTileWidth ; x += mTileWidth + mTileSpacing )
272+ tileRects.append (QRect (x, y, mTileWidth , mTileHeight ));
273+
274+ for (int tileNum = 0 ; tileNum < tileRects.size (); ++tileNum) {
275+ QRect rect = tileRects.at (tileNum);
276+ const int tileId = isAtlas () ? generateTileId (rect.x (), rect.y (), true ) : tileNum;
277+ auto it = mTilesById .find (tileId);
278+ if (it != mTilesById .end ()) {
279+ it.value ()->setImage (QPixmap ()); // make sure it uses the tileset's image
280+ it.value ()->setImageRect (tileRects.at (tileNum));
281+ } else {
282+ auto tile = new Tile (tileId, this );
283+ tile->setImageRect (rect);
284+ mTilesById .insert (tileId, tile);
285+ mTiles .insert (tileNum, tile);
286+ }
287+ }
276288
277- // Blank out any remaining tiles to avoid confusion (todo: could be more clear)
278- for (Tile *tile : std::as_const (mTiles )) {
279- if (tile->id () >= tileRects.size ()) {
280- if (blank.isNull ()) {
281- blank = QPixmap (mTileWidth , mTileHeight );
282- blank.fill ();
289+ QPixmap blank;
290+
291+ // Blank out any remaining tiles to avoid confusion (todo: could be more clear)
292+ if (!isAtlas ()) {
293+ for (Tile *tile : std::as_const (mTiles )) {
294+ if (tile->id () >= tileRects.size ()) {
295+ if (blank.isNull ()) {
296+ blank = QPixmap (mTileWidth , mTileHeight );
297+ blank.fill ();
298+ }
299+ tile->setImage (blank);
300+ tile->setImageRect (QRect (0 , 0 , mTileWidth , mTileHeight ));
301+ }
283302 }
284- tile->setImage (blank);
285- tile->setImageRect (QRect (0 , 0 , mTileWidth , mTileHeight ));
286303 }
287304 }
288305
289- mNextTileId = std::max<int >(mNextTileId , tileRects.size ());
306+ for (Tile *tile : std::as_const (mTiles )) {
307+ mNextTileId = std::max (mNextTileId , tile->id () + 1 );
308+ }
290309
291310 mImageReference .size = mImage .size ();
292311 mColumnCount = columnCountForWidth (mImageReference .size .width ());
@@ -453,6 +472,9 @@ void Tileset::addTiles(const QList<Tile *> &tiles)
453472 Q_ASSERT (tile->tileset () == this && !mTilesById .contains (tile->id ()));
454473 mTilesById .insert (tile->id (), tile);
455474 mTiles .append (tile);
475+ if (isAtlas ()) {
476+ setNextTileId (std::max (nextTileId (), tile->id () + 1 ));
477+ }
456478 }
457479
458480 updateTileSize ();
@@ -469,6 +491,9 @@ void Tileset::removeTiles(const QList<Tile *> &tiles)
469491 Q_ASSERT (tile->tileset () == this && mTilesById .contains (tile->id ()));
470492 mTilesById .remove (tile->id ());
471493 mTiles .removeOne (tile);
494+ if (isAtlas ()) {
495+ setNextTileId (std::max (nextTileId (), tile->id () + 1 ));
496+ }
472497 }
473498
474499 updateTileSize ();
@@ -554,6 +579,9 @@ void Tileset::setTileImageRect(Tile *tile, const QRect &imageRect)
554579
555580void Tileset::maybeUpdateTileSize (QSize previousTileSize, QSize newTileSize)
556581{
582+ if (isAtlas ())
583+ return ;
584+
557585 if (previousTileSize == newTileSize)
558586 return ;
559587
@@ -681,6 +709,9 @@ SharedTileset Tileset::clone() const
681709 */
682710void Tileset::updateTileSize ()
683711{
712+ if (isAtlas ())
713+ return ;
714+
684715 int maxWidth = 0 ;
685716 int maxHeight = 0 ;
686717 for (Tile *tile : std::as_const (mTiles )) {
@@ -694,6 +725,18 @@ void Tileset::updateTileSize()
694725 mTileHeight = maxHeight;
695726}
696727
728+ int Tileset::generateTileId (int x, int y, bool absolute) const {
729+ if (absolute) {
730+ // Normalize x and y to tile coordinates
731+ x = (x - mMargin ) / (mTileWidth + mTileSpacing );
732+ y = (y - mMargin ) / (mTileHeight + mTileSpacing );
733+ }
734+
735+ // Use 12+12 bit spatial hash (4096x4096 coordinates limit)
736+ x &= 0xFFF ;
737+ y &= 0xFFF ;
738+ return (x << 12 ) | y; // 24 bits total
739+ }
697740
698741QString Tileset::orientationToString (Tileset::Orientation orientation)
699742{
0 commit comments