Embedded images & tilesets with individual tiles #261
Conversation
|
Just to make sure, since you're speaking of TMX format changes: are these changes compatible with Tiled Java, or did you devise a new way of storing embedded images and per-tile images? Actually I would like Tiled Qt to support individual tile images, but I wanted to leave out support for embedded tile images, because this approach didn't make much sense to me. Do you have a use-case for it? Btw: Thanks for putting your work into a pull request! :-) |
As compatible as practical. Tiled Java should be able to read this format. There is a restriction that Tiled Java doesn't have: the tiles have to be ordered by ID, starting at 0, with no gaps. (This is because tiles are loaded into a container that doesn't allow gaps.) The restriction can be lifted in the future, of course.
Yes, I do. I'm making a library for programmatic manipulation of maps, such as "baking" several partially transparent tiles into one, if they overlap each other in the map. It makes sense to embed autogenerated images such as this, rather than having to mess with inventing filenames and managing lots of files (e.g. adding them to Git whenever they change). |
|
To implement functionality similar to image objects (#235, #177), this would need:
Unfortunately my vacation is almost over so I probably won't get around to it soon :( |
| return image.toImage().copy( | ||
| -(mTileset->tileWidth() - image.width()) / 2, | ||
| -(mTileset->tileHeight() - image.height()), | ||
| mTileset->tileWidth(), mTileset->tileHeight()); |
bjorn
Aug 25, 2012
Member
Hmm, this is totally unacceptable for performance reasons. Converting the QPixmap to a QImage and then making another copy just to tweak the alignment. Instead, this should be implemented by the TileDelegate in tilesetview.cpp.
Hmm, this is totally unacceptable for performance reasons. Converting the QPixmap to a QImage and then making another copy just to tweak the alignment. Instead, this should be implemented by the TileDelegate in tilesetview.cpp.
| @@ -219,3 +219,60 @@ void Tileset::calculateTerrainDistances() | |||
| // Repeat while we are still making new connections (could take a number of iterations for distant terrain types to connect) | |||
| } while (bNewConnections); | |||
| } | |||
|
|
|||
| void Tileset::addTile(Tile *newTile) { | |||
bjorn
Aug 25, 2012
Member
Please keep with the coding style and place those { on a new line (same for the other functions added to Tileset).
Please keep with the coding style and place those { on a new line (same for the other functions added to Tileset).
| } | ||
| if (mTileWidth < image.width()) { | ||
| mTileWidth = image.width(); | ||
| } |
bjorn
Aug 25, 2012
Member
Please leave out { and } when you got only a single line body.
Please leave out { and } when you got only a single line body.
| readUnknownElement(); | ||
| } | ||
| } | ||
| }else{ |
bjorn
Aug 25, 2012
Member
Please include spaces here, like so: } else {
Please include spaces here, like so: } else {
| QString trans = atts.value(QLatin1String("trans")).toString(); | ||
| QString source = atts.value(QLatin1String("source")).toString(); |
bjorn
Aug 25, 2012
Member
Why change the order of these lines?
Why change the order of these lines?
| if (haveImage && (tileWidth == 0 || tileHeight == 0)) { | ||
| xml.raiseError(tr("Invalid tileset parameters for tileset" | ||
| " '%1'").arg(name)); | ||
| } |
bjorn
Aug 25, 2012
Member
This check needs to happen before readTilesetImage, or it needs to be moved inside that function. Otherwise you might hit the assertion at https://github.com/bjorn/tiled/blob/da6ece274e2cf18d8e4971e1f3b7f4708d1f622d/src/libtiled/tileset.cpp#L50
This check needs to happen before readTilesetImage, or it needs to be moved inside that function. Otherwise you might hit the assertion at https://github.com/bjorn/tiled/blob/da6ece274e2cf18d8e4971e1f3b7f4708d1f622d/src/libtiled/tileset.cpp#L50
| xml.raiseError(tr("Invalid tile ID: %1").arg(id)); | ||
| return; | ||
| } else if (id == tileset->tileCount()) { | ||
| tileset->addTile(new Tile(QPixmap(), id, tileset)); |
bjorn
Aug 25, 2012
Member
Rather than relying on the caller to specify the right tileset for the tile, I would prefer to have Tileset::addTile take care of creating the tile. So that this becomes:
tileset->addTile(id, QPixmap())
Rather than relying on the caller to specify the right tileset for the tile, I would prefer to have Tileset::addTile take care of creating the tile. So that this becomes:
tileset->addTile(id, QPixmap())
|
Rebased to current master, and addressed most of the comments. The hardest one still remains. |
| const QXmlStreamAttributes atts = xml.attributes(); | ||
| QString encoding = atts.value(QLatin1String("encoding")) | ||
| .toString(); | ||
| QByteArray data = xml.readElementText().toAscii(); |
bjorn
Aug 27, 2012
Member
The function toAscii was deprecated in Qt 5 because it doesn't always do what you'd think it does, since it depends on which codec is set for C strings. Please use QString::toLatin1 instead (also below).
The function toAscii was deprecated in Qt 5 because it doesn't always do what you'd think it does, since it depends on which codec is set for C strings. Please use QString::toLatin1 instead (also below).
|
|
||
| QBuffer buffer; | ||
| tile->image().save(&buffer, "png"); | ||
| w.writeCharacters(QString::fromAscii(buffer.data().toBase64())); |
bjorn
Aug 27, 2012
Member
Similarly, this should be QString::fromLatin1.
Similarly, this should be QString::fromLatin1.
| } | ||
| if (mTileWidth < newTile->image().width()) { | ||
| mTileWidth = newTile->image().width(); | ||
| } |
bjorn
Aug 27, 2012
Member
Too many braces here. Also, you can replace newTile->image().height() etc. by just image.height() here.
Too many braces here. Also, you can replace newTile->image().height() etc. by just image.height() here.
| } | ||
| if (maxHeight < tile->image().height()) { | ||
| maxHeight = tile->image().height(); | ||
| } |
bjorn
Aug 27, 2012
Member
Too many braces here. Also, the Tile class has width/height methods that forward to its image, please use those.
Too many braces here. Also, the Tile class has width/height methods that forward to its image, please use those.
|
Thanks for your time! Here are some updated patches again. |
|
Looks very good now, nothing left that I want to complain about! :) Just one question before I merge this. I noticed your first commit carries a different author email (@redhat.com). Was this intentional? |
|
Wow, you have a sharp eye! Not intentional, fixed. (I took my work computer to vacation, and didn't set up Tiled's repo properly) I also changed a comment and commit message in the last commit: the tiles are bottom-left aligned in their cells, instead of bottom-middle. This mimics how they appear on the map. The code already did this. |
| @@ -378,17 +388,48 @@ void MapReaderPrivate::readTilesetImage(Tileset *tileset) | |||
| tileset->setTransparentColor(QColor(trans)); | |||
| } | |||
|
|
|||
| source = p->resolveReference(source, mPath); | |||
|
|
|||
bjorn
Aug 30, 2012
Member
Sorry I noticed one remaining problem with this part of the patch. This source path is "resolved", which means if source was relative it will now have the map path prepended to it. It is important that the resolved version is passed to the tileset because the map path will be forgotten later, which can cause problems on saving (where the relative reference would stay the same, regardless of where the map is saved - which pretty much only works when saving the map to the same location).
This patch currently causes the unresolved version to be passed into Tileset::loadFromImage.
Sorry I noticed one remaining problem with this part of the patch. This source path is "resolved", which means if source was relative it will now have the map path prepended to it. It is important that the resolved version is passed to the tileset because the map path will be forgotten later, which can cause problems on saving (where the relative reference would stay the same, regardless of where the map is saved - which pretty much only works when saving the map to the same location).
This patch currently causes the unresolved version to be passed into Tileset::loadFromImage.
encukou
Aug 31, 2012
Author
Contributor
I guess that was a rebase artifact. Thanks for noticing.
I guess that was a rebase artifact. Thanks for noticing.
|
Now the path gets resolved twice, but performance-wise that's rather insignificant. Thanks for the patches! |
|
Thank you for the thorough review! |
This adds basic support for images embedded in the XML file, and tilesets where each tile is backed by an individual image.
No UI is added for creating such tilesets, and the way they're displayed in the dock leaves much to be desired, especially if tiles are of different sizes.
To be honest, the patch is more about adding this to the TMX format rather than the GUI :) Here are the format changes:
<image source="..." />, you can say<image format="png"><data encoding="base64">...</data></image>.This means there's a couple of options for the TMX layout:
With these patches, Tiled should be able to read all of these, but it will only save the first or fourth. It will only save base64-encoded PNGs.
The editor will always set a tileset's "tilewidth" and "tileheight" to the largest of the individual tiles.
Helps with #103