Skip to content

Commit d566dfc

Browse files
authored
Add parser support for Tiled hex colors as ui.Color (#52)
Adding direct support for parsing color hex strings as dart.ui.Color objects, which is the primary type the interoperates with Dart rendering libraries and most Canvas methods use. Also, should eventually replace _parseTiledColor in flame_tiled RenderableTileMap.
1 parent 6e7656a commit d566dfc

File tree

9 files changed

+135
-18
lines changed

9 files changed

+135
-18
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## [Next]
2+
* Add parser support for Tiled hex colors as `ui.Color`
3+
* BREAKING CHANGE: re-named string color fields to `colorHex` such as `layer.tintColorHex` and
4+
added a new `color` field paired with each `colorHex` field of type `ui.Color`.
5+
16
## 0.8.4
27
* Adding "class" attribute for Tiled 1.9's Unified Custom Types:
38
* On `Tile` accessible as `class_` or `type`, backwards compatible with "type" property

lib/src/layer.dart

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ abstract class Layer {
7575

7676
/// Hex-formatted tint color (#RRGGBB or #AARRGGBB) that is multiplied with
7777
/// any graphics drawn by this layer or any child layers (optional).
78-
String? tintColor;
78+
String? tintColorHex;
79+
80+
/// [Color] that is multiplied with any graphics drawn by this layer or any
81+
/// child layers (optional).
82+
///
83+
/// Parsed from [tintColorHex], will be null if parsing fails for any reason.
84+
Color? tintColor;
7985

8086
/// The opacity of the layer as a value from 0 to 1. Defaults to 1.
8187
double opacity;
@@ -99,6 +105,7 @@ abstract class Layer {
99105
this.parallaxY = 1,
100106
this.startX,
101107
this.startY,
108+
this.tintColorHex,
102109
this.tintColor,
103110
this.opacity = 1,
104111
this.visible = true,
@@ -122,7 +129,8 @@ abstract class Layer {
122129
final parallaxY = parser.getDouble('parallaxy', defaults: 1);
123130
final startX = parser.getIntOrNull('startx');
124131
final startY = parser.getIntOrNull('starty');
125-
final tintColor = parser.getStringOrNull('tintcolor');
132+
final tintColorHex = parser.getStringOrNull('tintcolor');
133+
final tintColor = parser.getColorOrNull('tintcolor');
126134
final opacity = parser.getDouble('opacity', defaults: 1);
127135
final visible = parser.getBool('visible', defaults: true);
128136
final properties = parser.getProperties();
@@ -159,6 +167,7 @@ abstract class Layer {
159167
parallaxY: parallaxY,
160168
startX: startX,
161169
startY: startY,
170+
tintColorHex: tintColorHex,
162171
tintColor: tintColor,
163172
opacity: opacity,
164173
visible: visible,
@@ -176,7 +185,10 @@ abstract class Layer {
176185
'draworder',
177186
defaults: DrawOrder.topDown,
178187
);
179-
final color = parser.getString('color', defaults: '#a0a0a4');
188+
final colorHex =
189+
parser.getString('color', defaults: ObjectGroup.defaultColorHex);
190+
final color =
191+
parser.getColor('color', defaults: ObjectGroup.defaultColor);
180192
final objects = parser.getChildrenAs('object', TiledObject.parse);
181193
layer = ObjectGroup(
182194
id: id,
@@ -190,17 +202,20 @@ abstract class Layer {
190202
parallaxY: parallaxY,
191203
startX: startX,
192204
startY: startY,
205+
tintColorHex: tintColorHex,
193206
tintColor: tintColor,
194207
opacity: opacity,
195208
visible: visible,
196209
properties: properties,
197210
drawOrder: drawOrder,
198211
objects: objects,
212+
colorHex: colorHex,
199213
color: color,
200214
);
201215
break;
202216
case LayerType.imageLayer:
203-
final transparentColor = parser.getStringOrNull('transparentcolor');
217+
final transparentColorHex = parser.getStringOrNull('transparentcolor');
218+
final transparentColor = parser.getColorOrNull('transparentcolor');
204219
final image = parser.getSingleChildAs('image', TiledImage.parse);
205220
final repeatX = parser.getBool('repeatx', defaults: false);
206221
final repeatY = parser.getBool('repeaty', defaults: false);
@@ -218,11 +233,13 @@ abstract class Layer {
218233
repeatY: repeatY,
219234
startX: startX,
220235
startY: startY,
236+
tintColorHex: tintColorHex,
221237
tintColor: tintColor,
222238
opacity: opacity,
223239
visible: visible,
224240
properties: properties,
225241
image: image,
242+
transparentColorHex: transparentColorHex,
226243
transparentColor: transparentColor,
227244
);
228245
break;
@@ -240,6 +257,7 @@ abstract class Layer {
240257
parallaxY: parallaxY,
241258
startX: startX,
242259
startY: startY,
260+
tintColorHex: tintColorHex,
243261
tintColor: tintColor,
244262
opacity: opacity,
245263
visible: visible,
@@ -374,7 +392,8 @@ class TileLayer extends Layer {
374392
double parallaxY = 1,
375393
int? startX,
376394
int? startY,
377-
String? tintColor,
395+
String? tintColorHex,
396+
Color? tintColor,
378397
double opacity = 1,
379398
bool visible = true,
380399
List<Property> properties = const [],
@@ -398,11 +417,13 @@ class TileLayer extends Layer {
398417
parallaxY: parallaxY,
399418
startX: startX,
400419
startY: startY,
420+
tintColorHex: tintColorHex,
401421
tintColor: tintColor,
402422
opacity: opacity,
403423
visible: visible,
404424
properties: properties,
405425
);
426+
406427
static List<List<Gid>>? maybeGenerate(
407428
List<int>? data,
408429
int width,
@@ -416,15 +437,25 @@ class TileLayer extends Layer {
416437
}
417438

418439
class ObjectGroup extends Layer {
440+
static const defaultColor = Color.fromARGB(255, 160, 160, 164);
441+
static const defaultColorHex = '%a0a0a4';
442+
419443
/// topdown (default) or index (indexOrder).
420444
DrawOrder drawOrder;
421445

422446
/// List of [TiledObject].
423447
List<TiledObject> objects;
424448

425-
/// The color used to display the objects in this group.
449+
/// Hex-formatted color (#RRGGBB or #AARRGGBB) used to display the objects in
450+
/// this group. (defaults to gray (“#a0a0a4”))
451+
String colorHex;
452+
453+
/// [Color] used to display the objects in this group.
426454
/// (defaults to gray (“#a0a0a4”))
427-
String color;
455+
///
456+
/// Parsed from [colorHex], will be fallback to [defaultColor] if parsing
457+
/// fails for any reason.
458+
Color color;
428459

429460
ObjectGroup({
430461
int? id,
@@ -438,13 +469,15 @@ class ObjectGroup extends Layer {
438469
double parallaxY = 1,
439470
int? startX,
440471
int? startY,
441-
String? tintColor,
472+
String? tintColorHex,
473+
Color? tintColor,
442474
double opacity = 1,
443475
bool visible = true,
444476
List<Property> properties = const [],
445477
this.drawOrder = DrawOrder.topDown,
446478
required this.objects,
447-
this.color = '#a0a0a4',
479+
this.colorHex = defaultColorHex,
480+
this.color = defaultColor,
448481
}) : super(
449482
id: id,
450483
name: name,
@@ -458,6 +491,7 @@ class ObjectGroup extends Layer {
458491
parallaxY: parallaxY,
459492
startX: startX,
460493
startY: startY,
494+
tintColorHex: tintColorHex,
461495
tintColor: tintColor,
462496
opacity: opacity,
463497
visible: visible,
@@ -469,8 +503,15 @@ class ImageLayer extends Layer {
469503
/// Image used by this layer.
470504
TiledImage image;
471505

472-
/// Hex-formatted color (#RRGGBB) (optional).
473-
String? transparentColor;
506+
/// Hex-formatted color (#RRGGBB or #AARRGGBB) to be rendered as transparent
507+
/// (optional).
508+
String? transparentColorHex;
509+
510+
/// [Color] to be rendered as transparent (optional).
511+
///
512+
/// Parsed from [transparentColorHex], will be null if parsing fails for any
513+
/// reason.
514+
Color? transparentColor;
474515

475516
/// Whether or not to repeat the image on the X-axis
476517
bool repeatX;
@@ -490,13 +531,15 @@ class ImageLayer extends Layer {
490531
double parallaxY = 1,
491532
int? startX,
492533
int? startY,
493-
String? tintColor,
534+
String? tintColorHex,
535+
Color? tintColor,
494536
double opacity = 1,
495537
bool visible = true,
496538
List<Property> properties = const [],
497539
required this.image,
498540
required this.repeatX,
499541
required this.repeatY,
542+
this.transparentColorHex,
500543
this.transparentColor,
501544
}) : super(
502545
id: id,
@@ -511,6 +554,7 @@ class ImageLayer extends Layer {
511554
parallaxY: parallaxY,
512555
startX: startX,
513556
startY: startY,
557+
tintColorHex: tintColorHex,
514558
tintColor: tintColor,
515559
opacity: opacity,
516560
visible: visible,
@@ -534,7 +578,8 @@ class Group extends Layer {
534578
double parallaxY = 1,
535579
int? startX,
536580
int? startY,
537-
String? tintColor,
581+
String? tintColorHex,
582+
Color? tintColor,
538583
double opacity = 1,
539584
bool visible = true,
540585
List<Property> properties = const [],
@@ -552,6 +597,7 @@ class Group extends Layer {
552597
parallaxY: parallaxY,
553598
startX: startX,
554599
startY: startY,
600+
tintColorHex: tintColorHex,
555601
tintColor: tintColor,
556602
opacity: opacity,
557603
visible: visible,

lib/src/parser.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,35 @@ abstract class Parser {
197197
return result;
198198
}
199199

200+
Color? getColorOrNull(String name, {Color? defaults}) {
201+
final tiledColor = getStringOrNull(name);
202+
203+
// Tiled colors are stored as either ARGB or RGB hex values, so we can
204+
// parse them as hex numbers with a little coercing.
205+
int? colorValue;
206+
if (tiledColor?.length == 7) {
207+
// parse '#rrbbgg' as hex '0xaarrggbb' with the alpha channel on full
208+
colorValue = int.tryParse(tiledColor!.replaceFirst('#', '0xff'));
209+
} else if (tiledColor?.length == 9) {
210+
// parse '#aarrbbgg' as hex '0xaarrggbb'
211+
colorValue = int.tryParse(tiledColor!.replaceFirst('#', '0x'));
212+
}
213+
214+
if (colorValue != null) {
215+
return Color(colorValue);
216+
} else {
217+
return defaults;
218+
}
219+
}
220+
221+
Color getColor(String name, {Color? defaults}) {
222+
final result = getColorOrNull(name, defaults: defaults);
223+
if (result == null) {
224+
throw ParsingException(name, null, 'Missing required color field');
225+
}
226+
return result;
227+
}
228+
200229
T? getRawEnumOrNull<T>(
201230
List<T> values,
202231
String Function(T) namer,

lib/src/tiled_map.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,17 @@ class TiledMap {
6969
List<Tileset> tilesets;
7070
List<Layer> layers;
7171

72-
String? backgroundColor;
72+
/// Hex-formatted color (#RRGGBB or #AARRGGBB) to be rendered as a solid color
73+
/// behind all other layers (optional).
74+
String? backgroundColorHex;
75+
76+
/// [Color] to be rendered as a solid color behind all other layers
77+
/// (optional).
78+
///
79+
/// Parsed from [backgroundColorHex], will be null if parsing fails for any
80+
/// reason.
81+
Color? backgroundColor;
82+
7383
int compressionLevel;
7484

7585
int? nextLayerId;
@@ -96,6 +106,7 @@ class TiledMap {
96106
required this.tileHeight,
97107
this.tilesets = const [],
98108
this.layers = const [],
109+
this.backgroundColorHex,
99110
this.backgroundColor,
100111
this.compressionLevel = -1,
101112
this.hexSideLength,
@@ -224,7 +235,8 @@ class TiledMap {
224235
}
225236

226237
static TiledMap parse(Parser parser, {List<TsxProvider>? tsxList}) {
227-
final backgroundColor = parser.getStringOrNull('backgroundcolor');
238+
final backgroundColorHex = parser.getStringOrNull('backgroundcolor');
239+
final backgroundColor = parser.getColorOrNull('backgroundcolor');
228240
final compressionLevel = parser.getInt('compressionlevel', defaults: -1);
229241
final height = parser.getInt('height');
230242
final hexSideLength = parser.getIntOrNull('hexsidelength');
@@ -280,6 +292,7 @@ class TiledMap {
280292
tileHeight: tileHeight,
281293
tilesets: tilesets,
282294
layers: layers,
295+
backgroundColorHex: backgroundColorHex,
283296
backgroundColor: backgroundColor,
284297
compressionLevel: compressionLevel,
285298
hexSideLength: hexSideLength,

lib/tiled.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ library tiled;
33
import 'dart:convert';
44
import 'dart:math' show Rectangle;
55
import 'dart:typed_data';
6+
import 'dart:ui';
67

78
import 'package:archive/archive.dart';
89
import 'package:meta/meta.dart';

test/fixtures/test.tmx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<map version="1.2" tiledversion="1.2.1" orientation="orthogonal" renderorder="right-down" width="10" height="10" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
2+
<map version="1.2" tiledversion="1.2.1" orientation="orthogonal" renderorder="right-down" width="10" height="10" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1" backgroundcolor="#ccddaaff">
33
<tileset firstgid="1" name="basketball" tilewidth="32" tileheight="32" tilecount="2" columns="1">
44
<properties>
55
<property name="test_property" value="test_value"/>
@@ -16,7 +16,7 @@
1616
</properties>
1717
</tile>
1818
</tileset>
19-
<layer id="1" name="Tile Layer 1" width="10" height="10" class="layer1Class">
19+
<layer id="1" name="Tile Layer 1" width="10" height="10" class="layer1Class" tintcolor="#ffaabb">
2020
<data encoding="base64" compression="zlib">
2121
eJxjZCAeMI6qpblaAAl0AAs=
2222
</data>

test/layer_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:async';
22
import 'dart:io';
3+
import 'dart:ui';
34

45
import 'package:test/test.dart';
56
import 'package:tiled/tiled.dart';
@@ -70,5 +71,13 @@ void main() {
7071
expect(row.length, equals(10));
7172
});
7273
});
74+
75+
test('parsed colors', () {
76+
expect(layer.tintColorHex, equals('#ffaabb'));
77+
expect(
78+
layer.tintColor,
79+
equals(Color(int.parse('0xffffaabb'))),
80+
);
81+
});
7382
});
7483
}

test/object_group_test.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:io';
2+
import 'dart:ui';
23

34
import 'package:test/test.dart';
45
import 'package:tiled/tiled.dart';
@@ -27,7 +28,11 @@ void main() {
2728
});
2829

2930
test('sets color', () {
30-
expect(objectGroup.color, equals('#555500'));
31+
expect(objectGroup.colorHex, equals('#555500'));
32+
expect(
33+
objectGroup.color,
34+
equals(Color(int.parse('0xff555500'))),
35+
);
3136
});
3237

3338
test('sets opacity', () {

0 commit comments

Comments
 (0)