1 /*
2 * maptovariantconverter.cpp
3 * Copyright 2011, Porfírio José Pereira Ribeiro <porfirioribeiro@gmail.com>
4 * Copyright 2011-2015, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
5 *
6 * This file is part of Tiled.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the Free
10 * Software Foundation; either version 2 of the License, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "maptovariantconverter.h"
23
24 #include "grouplayer.h"
25 #include "imagelayer.h"
26 #include "map.h"
27 #include "mapobject.h"
28 #include "objectgroup.h"
29 #include "objecttemplate.h"
30 #include "properties.h"
31 #include "tile.h"
32 #include "tilelayer.h"
33 #include "tileset.h"
34 #include "wangset.h"
35
36 #include <QCoreApplication>
37
38 using namespace Tiled;
39
toVariant(const Map & map,const QDir & mapDir)40 QVariant MapToVariantConverter::toVariant(const Map &map, const QDir &mapDir)
41 {
42 mDir = mapDir;
43 mGidMapper.clear();
44
45 QVariantMap mapVariant;
46
47 mapVariant[QStringLiteral("type")] = QLatin1String("map");
48 if (mVersion == 2)
49 mapVariant[QStringLiteral("version")] = QStringLiteral("1.6");
50 else
51 mapVariant[QStringLiteral("version")] = 1.1;
52 mapVariant[QStringLiteral("tiledversion")] = QCoreApplication::applicationVersion();
53 mapVariant[QStringLiteral("orientation")] = orientationToString(map.orientation());
54 mapVariant[QStringLiteral("renderorder")] = renderOrderToString(map.renderOrder());
55 mapVariant[QStringLiteral("width")] = map.width();
56 mapVariant[QStringLiteral("height")] = map.height();
57 mapVariant[QStringLiteral("tilewidth")] = map.tileWidth();
58 mapVariant[QStringLiteral("tileheight")] = map.tileHeight();
59 mapVariant[QStringLiteral("infinite")] = map.infinite();
60 mapVariant[QStringLiteral("nextlayerid")] = map.nextLayerId();
61 mapVariant[QStringLiteral("nextobjectid")] = map.nextObjectId();
62 mapVariant[QStringLiteral("compressionlevel")] = map.compressionLevel();
63
64 if (map.chunkSize() != QSize(CHUNK_SIZE, CHUNK_SIZE) || !map.exportFileName.isEmpty() || !map.exportFormat.isEmpty()) {
65 QVariantMap editorSettingsVariant;
66
67 if (map.chunkSize() != QSize(CHUNK_SIZE, CHUNK_SIZE)) {
68 QVariantMap chunkSizeVariant;
69 chunkSizeVariant[QStringLiteral("width")] = map.chunkSize().width();
70 chunkSizeVariant[QStringLiteral("height")] = map.chunkSize().height();
71 editorSettingsVariant[QStringLiteral("chunksize")] = chunkSizeVariant;
72 }
73
74 if (!map.exportFileName.isEmpty() || !map.exportFormat.isEmpty()) {
75 QVariantMap exportVariant;
76 if (!map.exportFileName.isEmpty())
77 exportVariant[QStringLiteral("target")] = mDir.relativeFilePath(map.exportFileName);
78 if (!map.exportFormat.isEmpty())
79 exportVariant[QStringLiteral("format")] = map.exportFormat;
80 editorSettingsVariant[QStringLiteral("export")] = exportVariant;
81 }
82
83 mapVariant[QStringLiteral("editorsettings")] = editorSettingsVariant;
84 }
85
86 addProperties(mapVariant, map.properties());
87
88 if (map.orientation() == Map::Hexagonal) {
89 mapVariant[QStringLiteral("hexsidelength")] = map.hexSideLength();
90 }
91
92 if (map.orientation() == Map::Hexagonal || map.orientation() == Map::Staggered) {
93 mapVariant[QStringLiteral("staggeraxis")] = staggerAxisToString(map.staggerAxis());
94 mapVariant[QStringLiteral("staggerindex")] = staggerIndexToString(map.staggerIndex());
95 }
96
97 const QColor bgColor = map.backgroundColor();
98 if (bgColor.isValid())
99 mapVariant[QStringLiteral("backgroundcolor")] = colorToString(bgColor);
100
101 QVariantList tilesetVariants;
102
103 unsigned firstGid = 1;
104 for (const SharedTileset &tileset : map.tilesets()) {
105 tilesetVariants << toVariant(*tileset, firstGid);
106 mGidMapper.insert(firstGid, tileset);
107 firstGid += tileset->nextTileId();
108 }
109 mapVariant[QStringLiteral("tilesets")] = tilesetVariants;
110
111 mapVariant[QStringLiteral("layers")] = toVariant(map.layers(),
112 map.layerDataFormat(),
113 map.compressionLevel(),
114 map.chunkSize());
115
116 return mapVariant;
117 }
118
toVariant(const Tileset & tileset,const QDir & directory)119 QVariant MapToVariantConverter::toVariant(const Tileset &tileset,
120 const QDir &directory)
121 {
122 mDir = directory;
123 return toVariant(tileset, 0);
124 }
125
toVariant(const ObjectTemplate & objectTemplate,const QDir & directory)126 QVariant MapToVariantConverter::toVariant(const ObjectTemplate &objectTemplate,
127 const QDir &directory)
128 {
129 mDir = directory;
130 QVariantMap objectTemplateVariant;
131
132 objectTemplateVariant[QStringLiteral("type")] = QLatin1String("template");
133
134 mGidMapper.clear();
135 if (Tileset *tileset = objectTemplate.object()->cell().tileset()) {
136 unsigned firstGid = 1;
137 mGidMapper.insert(firstGid, tileset->sharedPointer());
138 objectTemplateVariant[QStringLiteral("tileset")] = toVariant(*tileset, firstGid);
139 }
140
141 objectTemplateVariant[QStringLiteral("object")] = toVariant(*objectTemplate.object());
142
143 return objectTemplateVariant;
144 }
145
toVariant(const Tileset & tileset,int firstGid) const146 QVariant MapToVariantConverter::toVariant(const Tileset &tileset,
147 int firstGid) const
148 {
149 QVariantMap tilesetVariant;
150
151 if (firstGid > 0) {
152 tilesetVariant[QStringLiteral("firstgid")] = firstGid;
153
154 const QString &fileName = tileset.fileName();
155 if (!fileName.isEmpty()) {
156 QString source = mDir.relativeFilePath(fileName);
157 tilesetVariant[QStringLiteral("source")] = source;
158
159 // Tileset is external, so no need to write any of the stuff below
160 return tilesetVariant;
161 }
162 } else {
163 // Include a 'type' property if we are writing the tileset to its own file
164 tilesetVariant[QStringLiteral("type")] = QLatin1String("tileset");
165
166 // Include version in external tilesets
167 if (mVersion == 2)
168 tilesetVariant[QStringLiteral("version")] = QStringLiteral("1.6");
169 else
170 tilesetVariant[QStringLiteral("version")] = 1.1;
171 tilesetVariant[QStringLiteral("tiledversion")] = QCoreApplication::applicationVersion();
172 }
173
174 tilesetVariant[QStringLiteral("name")] = tileset.name();
175 tilesetVariant[QStringLiteral("tilewidth")] = tileset.tileWidth();
176 tilesetVariant[QStringLiteral("tileheight")] = tileset.tileHeight();
177 tilesetVariant[QStringLiteral("spacing")] = tileset.tileSpacing();
178 tilesetVariant[QStringLiteral("margin")] = tileset.margin();
179 tilesetVariant[QStringLiteral("tilecount")] = tileset.tileCount();
180 tilesetVariant[QStringLiteral("columns")] = tileset.columnCount();
181
182 // Write editor settings when saving external tilesets
183 if (firstGid == 0) {
184 if (!tileset.exportFileName.isEmpty() || !tileset.exportFormat.isEmpty()) {
185 QVariantMap editorSettingsVariant;
186
187 QVariantMap exportVariant;
188 exportVariant[QStringLiteral("target")] = mDir.relativeFilePath(tileset.exportFileName);
189 exportVariant[QStringLiteral("format")] = tileset.exportFormat;
190 editorSettingsVariant[QStringLiteral("export")] = exportVariant;
191
192 tilesetVariant[QStringLiteral("editorsettings")] = editorSettingsVariant;
193 }
194 }
195
196 const QColor &backgroundColor = tileset.backgroundColor();
197 if (backgroundColor.isValid())
198 tilesetVariant[QStringLiteral("backgroundcolor")] = colorToString(backgroundColor);
199
200 if (tileset.objectAlignment() != Unspecified)
201 tilesetVariant[QStringLiteral("objectalignment")] = alignmentToString(tileset.objectAlignment());
202
203 addProperties(tilesetVariant, tileset.properties());
204
205 const QPoint offset = tileset.tileOffset();
206 if (!offset.isNull()) {
207 QVariantMap tileOffset;
208 tileOffset[QStringLiteral("x")] = offset.x();
209 tileOffset[QStringLiteral("y")] = offset.y();
210 tilesetVariant[QStringLiteral("tileoffset")] = tileOffset;
211 }
212
213 if (tileset.orientation() != Tileset::Orthogonal || tileset.gridSize() != tileset.tileSize()) {
214 QVariantMap grid;
215 grid[QStringLiteral("orientation")] = Tileset::orientationToString(tileset.orientation());
216 grid[QStringLiteral("width")] = tileset.gridSize().width();
217 grid[QStringLiteral("height")] = tileset.gridSize().height();
218 tilesetVariant[QStringLiteral("grid")] = grid;
219 }
220
221 // Write the image element
222 const QUrl &imageSource = tileset.imageSource();
223 if (!imageSource.isEmpty()) {
224 const QString rel = toFileReference(imageSource, mDir);
225
226 tilesetVariant[QStringLiteral("image")] = rel;
227
228 const QColor transColor = tileset.transparentColor();
229 if (transColor.isValid())
230 tilesetVariant[QStringLiteral("transparentcolor")] = transColor.name();
231
232 tilesetVariant[QStringLiteral("imagewidth")] = tileset.imageWidth();
233 tilesetVariant[QStringLiteral("imageheight")] = tileset.imageHeight();
234 }
235
236 const auto transformationFlags = tileset.transformationFlags();
237 if (transformationFlags) {
238 tilesetVariant[QStringLiteral("transformations")] = QVariantMap {
239 { QStringLiteral("hflip"), transformationFlags.testFlag(Tileset::AllowFlipHorizontally) },
240 { QStringLiteral("vflip"), transformationFlags.testFlag(Tileset::AllowFlipVertically) },
241 { QStringLiteral("rotate"), transformationFlags.testFlag(Tileset::AllowRotate) },
242 { QStringLiteral("preferuntransformed"), transformationFlags.testFlag(Tileset::PreferUntransformed) },
243 };
244 }
245
246 // Write the properties, external image, object group and animation for
247 // those tiles that have them.
248
249 // Used for version 1
250 QVariantMap tilePropertiesVariant;
251 QVariantMap tilePropertyTypesVariant;
252 QVariantMap tilesVariantMap;
253
254 // Used for version 2
255 QVariantList tilesVariant;
256
257 const bool includeAllTiles = mVersion != 1 && tileset.anyTileOutOfOrder();
258
259 for (const Tile *tile : tileset.tiles()) {
260 const Properties properties = tile->properties();
261 QVariantMap tileVariant;
262
263 if (mVersion == 1) {
264 if (!properties.isEmpty()) {
265 tilePropertiesVariant[QString::number(tile->id())] = toVariant(properties);
266 tilePropertyTypesVariant[QString::number(tile->id())] = propertyTypesToVariant(properties);
267 }
268 } else {
269 addProperties(tileVariant, properties);
270 }
271
272 if (!tile->type().isEmpty())
273 tileVariant[QStringLiteral("type")] = tile->type();
274 if (tile->probability() != 1.0)
275 tileVariant[QStringLiteral("probability")] = tile->probability();
276 if (!tile->imageSource().isEmpty()) {
277 const QString rel = toFileReference(tile->imageSource(), mDir);
278 tileVariant[QStringLiteral("image")] = rel;
279
280 const QSize tileSize = tile->size();
281 if (!tileSize.isNull()) {
282 tileVariant[QStringLiteral("imagewidth")] = tileSize.width();
283 tileVariant[QStringLiteral("imageheight")] = tileSize.height();
284 }
285 }
286 if (tile->objectGroup())
287 tileVariant[QStringLiteral("objectgroup")] = toVariant(*tile->objectGroup());
288 if (tile->isAnimated()) {
289 QVariantList frameVariants;
290 for (const Frame &frame : tile->frames()) {
291 QVariantMap frameVariant;
292 frameVariant[QStringLiteral("tileid")] = frame.tileId;
293 frameVariant[QStringLiteral("duration")] = frame.duration;
294 frameVariants.append(frameVariant);
295 }
296 tileVariant[QStringLiteral("animation")] = frameVariants;
297 }
298
299 if (includeAllTiles || !tileVariant.empty()) {
300 if (mVersion == 1) {
301 tilesVariantMap[QString::number(tile->id())] = tileVariant;
302 } else {
303 tileVariant[QStringLiteral("id")] = tile->id();
304 tilesVariant << tileVariant;
305 }
306 }
307 }
308
309 if (!tilePropertiesVariant.empty()) {
310 tilesetVariant[QStringLiteral("tileproperties")] = tilePropertiesVariant;
311 tilesetVariant[QStringLiteral("tilepropertytypes")] = tilePropertyTypesVariant;
312 }
313
314 if (!tilesVariantMap.empty())
315 tilesetVariant[QStringLiteral("tiles")] = tilesVariantMap;
316 else if (!tilesVariant.empty())
317 tilesetVariant[QStringLiteral("tiles")] = tilesVariant;
318
319 // Write the Wang sets
320 if (tileset.wangSetCount() > 0) {
321 QVariantList wangSetVariants;
322
323 for (const WangSet *wangSet : tileset.wangSets())
324 wangSetVariants.append(toVariant(*wangSet));
325
326 tilesetVariant[QStringLiteral("wangsets")] = wangSetVariants;
327 }
328
329 return tilesetVariant;
330 }
331
toVariant(const Properties & properties) const332 QVariant MapToVariantConverter::toVariant(const Properties &properties) const
333 {
334 QVariantMap variantMap;
335
336 Properties::const_iterator it = properties.constBegin();
337 Properties::const_iterator it_end = properties.constEnd();
338 for (; it != it_end; ++it) {
339 const QVariant value = toExportValue(it.value(), mDir);
340 variantMap[it.key()] = value;
341 }
342
343 return variantMap;
344 }
345
propertyTypesToVariant(const Properties & properties) const346 QVariant MapToVariantConverter::propertyTypesToVariant(const Properties &properties) const
347 {
348 QVariantMap variantMap;
349
350 Properties::const_iterator it = properties.constBegin();
351 Properties::const_iterator it_end = properties.constEnd();
352 for (; it != it_end; ++it)
353 variantMap[it.key()] = typeToName(it.value().userType());
354
355 return variantMap;
356 }
357
358
toVariant(const WangSet & wangSet) const359 QVariant MapToVariantConverter::toVariant(const WangSet &wangSet) const
360 {
361 QVariantMap wangSetVariant;
362
363 wangSetVariant[QStringLiteral("name")] = wangSet.name();
364 wangSetVariant[QStringLiteral("type")] = wangSetTypeToString(wangSet.type());
365 wangSetVariant[QStringLiteral("tile")] = wangSet.imageTileId();
366
367 QVariantList colorVariants;
368 for (int i = 1; i <= wangSet.colorCount(); ++i)
369 colorVariants.append(toVariant(*wangSet.colorAt(i)));
370 wangSetVariant[QStringLiteral("colors")] = colorVariants;
371
372 QVariantList wangTileVariants;
373 const auto wangTiles = wangSet.sortedWangTiles();
374 for (const WangTile &wangTile : wangTiles) {
375 QVariantMap wangTileVariant;
376
377 QVariantList wangIdVariant;
378 for (int i = 0; i < WangId::NumIndexes; ++i)
379 wangIdVariant.append(QVariant(wangTile.wangId().indexColor(i)));
380
381 wangTileVariant[QStringLiteral("wangid")] = wangIdVariant;
382 wangTileVariant[QStringLiteral("tileid")] = wangTile.tileId();
383
384 wangTileVariants.append(wangTileVariant);
385 }
386 wangSetVariant[QStringLiteral("wangtiles")] = wangTileVariants;
387
388 addProperties(wangSetVariant, wangSet.properties());
389
390 return wangSetVariant;
391 }
392
toVariant(const WangColor & wangColor) const393 QVariant MapToVariantConverter::toVariant(const WangColor &wangColor) const
394 {
395 QVariantMap colorVariant;
396 colorVariant[QStringLiteral("color")] = colorToString(wangColor.color());
397 colorVariant[QStringLiteral("name")] = wangColor.name();
398 colorVariant[QStringLiteral("probability")] = wangColor.probability();
399 colorVariant[QStringLiteral("tile")] = wangColor.imageId();
400 addProperties(colorVariant, wangColor.properties());
401 return colorVariant;
402 }
403
toVariant(const QList<Layer * > & layers,Map::LayerDataFormat format,int compressionLevel,QSize chunkSize) const404 QVariant MapToVariantConverter::toVariant(const QList<Layer *> &layers,
405 Map::LayerDataFormat format,
406 int compressionLevel,
407 QSize chunkSize) const
408 {
409 QVariantList layerVariants;
410
411 for (const Layer *layer : layers) {
412 switch (layer->layerType()) {
413 case Layer::TileLayerType:
414 layerVariants << toVariant(*static_cast<const TileLayer*>(layer), format, compressionLevel, chunkSize);
415 break;
416 case Layer::ObjectGroupType:
417 layerVariants << toVariant(*static_cast<const ObjectGroup*>(layer));
418 break;
419 case Layer::ImageLayerType:
420 layerVariants << toVariant(*static_cast<const ImageLayer*>(layer));
421 break;
422 case Layer::GroupLayerType:
423 layerVariants << toVariant(*static_cast<const GroupLayer*>(layer), format, compressionLevel, chunkSize);
424 }
425 }
426
427 return layerVariants;
428 }
429
toVariant(const TileLayer & tileLayer,Map::LayerDataFormat format,int compressionLevel,QSize chunkSize) const430 QVariant MapToVariantConverter::toVariant(const TileLayer &tileLayer,
431 Map::LayerDataFormat format,
432 int compressionLevel,
433 QSize chunkSize) const
434 {
435 QVariantMap tileLayerVariant;
436 tileLayerVariant[QStringLiteral("type")] = QLatin1String("tilelayer");
437
438 if (tileLayer.map()->infinite()) {
439 QRect bounds = tileLayer.localBounds();
440 tileLayerVariant[QStringLiteral("width")] = bounds.width();
441 tileLayerVariant[QStringLiteral("height")] = bounds.height();
442 tileLayerVariant[QStringLiteral("startx")] = bounds.left();
443 tileLayerVariant[QStringLiteral("starty")] = bounds.top();
444 } else {
445 tileLayerVariant[QStringLiteral("width")] = tileLayer.width();
446 tileLayerVariant[QStringLiteral("height")] = tileLayer.height();
447 }
448
449 addLayerAttributes(tileLayerVariant, tileLayer);
450
451 switch (format) {
452 case Map::XML:
453 case Map::CSV:
454 break;
455 case Map::Base64:
456 case Map::Base64Zlib:
457 case Map::Base64Gzip:
458 case Map::Base64Zstandard:
459 tileLayerVariant[QStringLiteral("encoding")] = QLatin1String("base64");
460 tileLayerVariant[QStringLiteral("compression")] = compressionToString(format);
461 break;
462 }
463
464 if (tileLayer.map()->infinite()) {
465 QVariantList chunkVariants;
466
467 const auto chunks = tileLayer.sortedChunksToWrite(chunkSize);
468 for (const QRect &rect : chunks) {
469 QVariantMap chunkVariant;
470
471 chunkVariant[QStringLiteral("x")] = rect.x();
472 chunkVariant[QStringLiteral("y")] = rect.y();
473 chunkVariant[QStringLiteral("width")] = rect.width();
474 chunkVariant[QStringLiteral("height")] = rect.height();
475
476 addTileLayerData(chunkVariant, tileLayer, format, compressionLevel, rect);
477
478 chunkVariants.append(chunkVariant);
479 }
480
481 tileLayerVariant[QStringLiteral("chunks")] = chunkVariants;
482 } else {
483 addTileLayerData(tileLayerVariant, tileLayer, format, compressionLevel,
484 QRect(0, 0, tileLayer.width(), tileLayer.height()));
485 }
486
487 return tileLayerVariant;
488 }
489
toVariant(const ObjectGroup & objectGroup) const490 QVariant MapToVariantConverter::toVariant(const ObjectGroup &objectGroup) const
491 {
492 QVariantMap objectGroupVariant;
493 objectGroupVariant[QStringLiteral("type")] = QLatin1String("objectgroup");
494
495 if (objectGroup.color().isValid())
496 objectGroupVariant[QStringLiteral("color")] = colorToString(objectGroup.color());
497
498 objectGroupVariant[QStringLiteral("draworder")] = drawOrderToString(objectGroup.drawOrder());
499
500 addLayerAttributes(objectGroupVariant, objectGroup);
501 QVariantList objectVariants;
502 for (const MapObject *object : objectGroup.objects())
503 objectVariants << toVariant(*object);
504
505 objectGroupVariant[QStringLiteral("objects")] = objectVariants;
506
507 return objectGroupVariant;
508 }
509
toVariant(const MapObject & object) const510 QVariant MapToVariantConverter::toVariant(const MapObject &object) const
511 {
512 QVariantMap objectVariant;
513 const QString &name = object.name();
514 const QString &type = object.type();
515
516 addProperties(objectVariant, object.properties());
517
518 if (const ObjectTemplate *objectTemplate = object.objectTemplate()) {
519 QString relativeFileName = mDir.relativeFilePath(objectTemplate->fileName());
520 objectVariant[QStringLiteral("template")] = relativeFileName;
521 }
522
523 bool notTemplateInstance = !object.isTemplateInstance();
524
525 int id = object.id();
526 if (id != 0)
527 objectVariant[QStringLiteral("id")] = id;
528
529 if (notTemplateInstance || object.propertyChanged(MapObject::NameProperty))
530 objectVariant[QStringLiteral("name")] = name;
531
532 if (notTemplateInstance || object.propertyChanged(MapObject::TypeProperty))
533 objectVariant[QStringLiteral("type")] = type;
534
535
536 if (notTemplateInstance || object.propertyChanged(MapObject::CellProperty))
537 if (!object.cell().isEmpty())
538 objectVariant[QStringLiteral("gid")] = mGidMapper.cellToGid(object.cell());
539
540 if (!object.isTemplateBase()) {
541 objectVariant[QStringLiteral("x")] = object.x();
542 objectVariant[QStringLiteral("y")] = object.y();
543 }
544
545 if (notTemplateInstance || object.propertyChanged(MapObject::SizeProperty)) {
546 objectVariant[QStringLiteral("width")] = object.width();
547 objectVariant[QStringLiteral("height")] = object.height();
548 }
549
550 if (notTemplateInstance || object.propertyChanged(MapObject::RotationProperty))
551 objectVariant[QStringLiteral("rotation")] = object.rotation();
552
553 if (notTemplateInstance || object.propertyChanged(MapObject::VisibleProperty))
554 objectVariant[QStringLiteral("visible")] = object.isVisible();
555
556 /* Polygons are stored in this format:
557 *
558 * "polygon/polyline": [
559 * { "x": 0, "y": 0 },
560 * { "x": 1, "y": 1 },
561 * ...
562 * ]
563 */
564 switch (object.shape()) {
565 case MapObject::Rectangle:
566 break;
567 case MapObject::Polygon:
568 case MapObject::Polyline: {
569 if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty)) {
570 QVariantList pointVariants;
571 for (const QPointF &point : object.polygon()) {
572 QVariantMap pointVariant;
573 pointVariant[QStringLiteral("x")] = point.x();
574 pointVariant[QStringLiteral("y")] = point.y();
575 pointVariants.append(pointVariant);
576 }
577
578 if (object.shape() == MapObject::Polygon)
579 objectVariant[QStringLiteral("polygon")] = pointVariants;
580 else
581 objectVariant[QStringLiteral("polyline")] = pointVariants;
582 }
583 break;
584 }
585 case MapObject::Ellipse:
586 if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty))
587 objectVariant[QStringLiteral("ellipse")] = true;
588 break;
589 case MapObject::Text:
590 if (notTemplateInstance || (object.propertyChanged(MapObject::TextProperty) ||
591 object.propertyChanged(MapObject::TextFontProperty) ||
592 object.propertyChanged(MapObject::TextAlignmentProperty) ||
593 object.propertyChanged(MapObject::TextWordWrapProperty) ||
594 object.propertyChanged(MapObject::TextColorProperty)))
595 objectVariant[QStringLiteral("text")] = toVariant(object.textData());
596 break;
597 case MapObject::Point:
598 if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty))
599 objectVariant[QStringLiteral("point")] = true;
600 break;
601 }
602
603 return objectVariant;
604 }
605
toVariant(const TextData & textData) const606 QVariant MapToVariantConverter::toVariant(const TextData &textData) const
607 {
608 QVariantMap textVariant;
609
610 textVariant[QStringLiteral("text")] = textData.text;
611
612 if (textData.font.family() != QLatin1String("sans-serif"))
613 textVariant[QStringLiteral("fontfamily")] = textData.font.family();
614 if (textData.font.pixelSize() >= 0 && textData.font.pixelSize() != 16)
615 textVariant[QStringLiteral("pixelsize")] = textData.font.pixelSize();
616 if (textData.wordWrap)
617 textVariant[QStringLiteral("wrap")] = textData.wordWrap;
618 if (textData.color != Qt::black)
619 textVariant[QStringLiteral("color")] = colorToString(textData.color);
620 if (textData.font.bold())
621 textVariant[QStringLiteral("bold")] = textData.font.bold();
622 if (textData.font.italic())
623 textVariant[QStringLiteral("italic")] = textData.font.italic();
624 if (textData.font.underline())
625 textVariant[QStringLiteral("underline")] = textData.font.underline();
626 if (textData.font.strikeOut())
627 textVariant[QStringLiteral("strikeout")] = textData.font.strikeOut();
628 if (!textData.font.kerning())
629 textVariant[QStringLiteral("kerning")] = textData.font.kerning();
630
631 if (!textData.alignment.testFlag(Qt::AlignLeft)) {
632 if (textData.alignment.testFlag(Qt::AlignHCenter))
633 textVariant[QStringLiteral("halign")] = QLatin1String("center");
634 else if (textData.alignment.testFlag(Qt::AlignRight))
635 textVariant[QStringLiteral("halign")] = QLatin1String("right");
636 else if (textData.alignment.testFlag(Qt::AlignJustify))
637 textVariant[QStringLiteral("halign")] = QLatin1String("justify");
638 }
639
640 if (!textData.alignment.testFlag(Qt::AlignTop)) {
641 if (textData.alignment.testFlag(Qt::AlignVCenter))
642 textVariant[QStringLiteral("valign")] = QLatin1String("center");
643 else if (textData.alignment.testFlag(Qt::AlignBottom))
644 textVariant[QStringLiteral("valign")] = QLatin1String("bottom");
645 }
646
647 return textVariant;
648 }
649
toVariant(const ImageLayer & imageLayer) const650 QVariant MapToVariantConverter::toVariant(const ImageLayer &imageLayer) const
651 {
652 QVariantMap imageLayerVariant;
653 imageLayerVariant[QStringLiteral("type")] = QLatin1String("imagelayer");
654
655 addLayerAttributes(imageLayerVariant, imageLayer);
656
657 const QString rel = toFileReference(imageLayer.imageSource(), mDir);
658 imageLayerVariant[QStringLiteral("image")] = rel;
659
660 const QColor transColor = imageLayer.transparentColor();
661 if (transColor.isValid())
662 imageLayerVariant[QStringLiteral("transparentcolor")] = transColor.name();
663
664 return imageLayerVariant;
665 }
666
toVariant(const GroupLayer & groupLayer,Map::LayerDataFormat format,int compressionLevel,QSize chunkSize) const667 QVariant MapToVariantConverter::toVariant(const GroupLayer &groupLayer,
668 Map::LayerDataFormat format,
669 int compressionLevel,
670 QSize chunkSize) const
671 {
672 QVariantMap groupLayerVariant;
673 groupLayerVariant[QStringLiteral("type")] = QLatin1String("group");
674
675 addLayerAttributes(groupLayerVariant, groupLayer);
676
677 groupLayerVariant[QStringLiteral("layers")] = toVariant(groupLayer.layers(),
678 format,
679 compressionLevel,
680 chunkSize);
681
682 return groupLayerVariant;
683 }
684
addTileLayerData(QVariantMap & variant,const TileLayer & tileLayer,Map::LayerDataFormat format,int compressionLevel,const QRect & bounds) const685 void MapToVariantConverter::addTileLayerData(QVariantMap &variant,
686 const TileLayer &tileLayer,
687 Map::LayerDataFormat format,
688 int compressionLevel,
689 const QRect &bounds) const
690 {
691 switch (format) {
692 case Map::XML:
693 case Map::CSV: {
694 QVariantList tileVariants;
695 for (int y = bounds.top(); y <= bounds.bottom(); ++y)
696 for (int x = bounds.left(); x <= bounds.right(); ++x)
697 tileVariants << mGidMapper.cellToGid(tileLayer.cellAt(x, y));
698
699 variant[QStringLiteral("data")] = tileVariants;
700 break;
701 }
702 case Map::Base64:
703 case Map::Base64Zlib:
704 case Map::Base64Gzip:
705 case Map::Base64Zstandard:{
706 QByteArray layerData = mGidMapper.encodeLayerData(tileLayer, format, bounds, compressionLevel);
707 variant[QStringLiteral("data")] = layerData;
708 break;
709 }
710 }
711 }
712
addLayerAttributes(QVariantMap & layerVariant,const Layer & layer) const713 void MapToVariantConverter::addLayerAttributes(QVariantMap &layerVariant,
714 const Layer &layer) const
715 {
716 if (layer.id() != 0)
717 layerVariant[QStringLiteral("id")] = layer.id();
718
719 layerVariant[QStringLiteral("name")] = layer.name();
720 layerVariant[QStringLiteral("x")] = layer.x();
721 layerVariant[QStringLiteral("y")] = layer.y();
722 layerVariant[QStringLiteral("visible")] = layer.isVisible();
723 layerVariant[QStringLiteral("opacity")] = layer.opacity();
724
725 const QPointF offset = layer.offset();
726 if (!offset.isNull()) {
727 layerVariant[QStringLiteral("offsetx")] = offset.x();
728 layerVariant[QStringLiteral("offsety")] = offset.y();
729 }
730
731 const QPointF parallaxFactor = layer.parallaxFactor();
732 if (parallaxFactor.x() != 1.0)
733 layerVariant[QStringLiteral("parallaxx")] = parallaxFactor.x();
734 if (parallaxFactor.y() != 1.0)
735 layerVariant[QStringLiteral("parallaxy")] = parallaxFactor.y();
736
737 if (layer.tintColor().isValid())
738 layerVariant[QStringLiteral("tintcolor")] = colorToString(layer.tintColor());
739
740 addProperties(layerVariant, layer.properties());
741 }
742
addProperties(QVariantMap & variantMap,const Properties & properties) const743 void MapToVariantConverter::addProperties(QVariantMap &variantMap,
744 const Properties &properties) const
745 {
746 if (properties.isEmpty())
747 return;
748
749 if (mVersion == 1) {
750 QVariantMap propertiesMap;
751 QVariantMap propertyTypesMap;
752
753 Properties::const_iterator it = properties.constBegin();
754 Properties::const_iterator it_end = properties.constEnd();
755 for (; it != it_end; ++it) {
756 int type = it.value().userType();
757 const QVariant value = toExportValue(it.value(), mDir);
758
759 propertiesMap[it.key()] = value;
760 propertyTypesMap[it.key()] = typeToName(type);
761 }
762
763 variantMap[QStringLiteral("properties")] = propertiesMap;
764 variantMap[QStringLiteral("propertytypes")] = propertyTypesMap;
765 } else {
766 QVariantList propertiesVariantList;
767
768 Properties::const_iterator it = properties.constBegin();
769 Properties::const_iterator it_end = properties.constEnd();
770 for (; it != it_end; ++it) {
771 int type = it.value().userType();
772 const QVariant value = toExportValue(it.value(), mDir);
773
774 QVariantMap propertyVariantMap;
775 propertyVariantMap[QStringLiteral("name")] = it.key();
776 propertyVariantMap[QStringLiteral("value")] = value;
777 propertyVariantMap[QStringLiteral("type")] = typeToName(type);
778 propertiesVariantList << propertyVariantMap;
779 }
780
781 variantMap[QStringLiteral("properties")] = propertiesVariantList;
782 }
783 }
784