pytmx/tmxloader.py (view raw)
1import itertools
2import os
3import pygame
4import pytmx
5from .constants import *
6
7__all__ = ['load_pygame', 'load_tmx']
8
9
10def handle_transformation(tile, flags):
11 if flags:
12 fx = flags & TRANS_FLIPX == TRANS_FLIPX
13 fy = flags & TRANS_FLIPY == TRANS_FLIPY
14 r = flags & TRANS_ROT == TRANS_ROT
15
16 if r:
17 # not sure why the flip is required...but it is.
18 newtile = pygame.transform.rotate(tile, 270)
19 newtile = pygame.transform.flip(newtile, 1, 0)
20
21 if fx or fy:
22 newtile = pygame.transform.flip(newtile, fx, fy)
23
24 elif fx or fy:
25 newtile = pygame.transform.flip(tile, fx, fy)
26
27 return newtile
28
29 else:
30 return tile
31
32
33def smart_convert(original, colorkey, force_colorkey, pixelalpha):
34 """
35 this method does several tests on a surface to determine the optimal
36 flags and pixel format for each tile surface.
37
38 this is done for the best rendering speeds and removes the need to
39 convert() the images on your own
40 """
41 tile_size = original.get_size()
42
43 # count the number of pixels in the tile that are not transparent
44 px = pygame.mask.from_surface(original).count()
45
46 # there are no transparent pixels in the image
47 if px == tile_size[0] * tile_size[1]:
48 tile = original.convert()
49
50 # there are transparent pixels, and set to force a colorkey
51 elif force_colorkey:
52 tile = pygame.Surface(tile_size)
53 tile.fill(force_colorkey)
54 tile.blit(original, (0, 0))
55 tile.set_colorkey(force_colorkey, pygame.RLEACCEL)
56
57 # there are transparent pixels, and tiled set a colorkey
58 elif colorkey:
59 tile = original.convert()
60 tile.set_colorkey(colorkey, pygame.RLEACCEL)
61
62 # there are transparent pixels, and set for perpixel alpha
63 elif pixelalpha:
64 tile = original.convert_alpha()
65
66 # there are transparent pixels, and we won't handle them
67 else:
68 tile = original.convert()
69
70 return tile
71
72
73def _load_images_pygame(tmxdata, mapping, *args, **kwargs):
74 """
75 Utility function to load images.
76
77
78 due to the way the tiles are loaded, they will be in the same pixel format
79 as the display when it is loaded. take this into consideration if you
80 intend to support different screen pixel formats.
81
82 by default, the images will not have per-pixel alphas. this can be
83 changed by including "pixelalpha=True" in the keywords. this will result
84 in much slower blitting speeds.
85
86 if the tileset's image has colorkey transparency set in Tiled, the loader
87 will return images that have their transparency already set. using a
88 tileset with colorkey transparency will greatly increase the speed of
89 rendering the map.
90
91 optionally, you can force the loader to strip the alpha channel of the
92 tileset image and to fill in the missing areas with a color, then use that
93 new color as a colorkey. the resulting tiles will render much faster, but
94 will not preserve the transparency of the tile if it uses partial
95 transparency (which you shouldn't be doing anyway, this is SDL).
96
97 TL;DR:
98 Don't attempt to convert() or convert_alpha() the individual tiles. It is
99 already done for you.
100 """
101
102 pixelalpha = kwargs.get("pixelalpha", False)
103 force_colorkey = kwargs.get("force_colorkey", False)
104
105 if force_colorkey:
106 pixelalpha = True
107
108 if force_colorkey:
109 try:
110 force_colorkey = pygame.Color(*force_colorkey)
111 except:
112 msg = 'Cannot understand color: {0}'
113 print msg.format(force_colorkey)
114 raise ValueError
115
116 # change background color into something nice
117 if tmxdata.background_color:
118 tmxdata.background_color = pygame.Color(tmxdata.background_color)
119
120 # initialize the array of images
121 tmxdata.images = [0] * tmxdata.maxgid
122
123 for ts in tmxdata.tilesets:
124 path = os.path.join(os.path.dirname(tmxdata.filename), ts.source)
125 image = pygame.image.load(path)
126 w, h = image.get_size()
127
128 # margins and spacing
129 tilewidth = ts.tilewidth + ts.spacing
130 tileheight = ts.tileheight + ts.spacing
131 tile_size = ts.tilewidth, ts.tileheight
132
133 # some tileset images may be slightly larger than the tile area
134 # ie: may include a banner, copyright, ect. this compensates for that
135 width = int((((w - ts.margin * 2 + ts.spacing) / tilewidth) * tilewidth) - ts.spacing)
136 height = int((((h - ts.margin * 2 + ts.spacing) / tileheight) * tileheight) - ts.spacing)
137
138 # trim off any pixels on the right side that isn't a tile
139 # this happens if extra graphics are included on the left, but they are not actually part of the tileset
140 width -= (w - ts.margin) % tilewidth
141
142 # using product avoids the overhead of nested loops
143 p = itertools.product(xrange(ts.margin, height + ts.margin, tileheight),
144 xrange(ts.margin, width + ts.margin, tilewidth))
145
146 colorkey = getattr(ts, 'trans', None)
147 if colorkey:
148 colorkey = pygame.Color('#{0}'.format(colorkey))
149
150 for real_gid, (y, x) in enumerate(p, ts.firstgid):
151 if x + ts.tilewidth-ts.spacing > width:
152 continue
153
154 gids = tmxdata.map_gid(real_gid)
155
156 if gids:
157 original = image.subsurface(((x, y), tile_size))
158
159 for gid, flags in gids:
160 tile = handle_transformation(original, flags)
161 tile = smart_convert(tile, colorkey, force_colorkey, pixelalpha)
162 tmxdata.images[gid] = tile
163
164 # load image layer images
165 for layer in tmxdata.all_layers:
166 if isinstance(layer, pytmx.TiledImageLayer):
167 colorkey = getattr(layer, 'trans', None)
168 if colorkey:
169 colorkey = pygame.Color("#{0}".format(colorkey))
170
171 source = getattr(layer, 'source', None)
172 if source:
173 real_gid = len(tmxdata.images)
174 gid = tmxdata.register_gid(real_gid)
175 layer.gid = gid
176 path = os.path.join(os.path.dirname(tmxdata.filename), source)
177 image = pygame.image.load(path)
178 image = smart_convert(image, colorkey, force_colorkey, pixelalpha)
179 tmxdata.images.append(image)
180
181
182def load_pygame(filename, *args, **kwargs):
183 """
184 PYGAME USERS: Use me.
185
186 Load a TMX file, load the images, and return a TiledMap class that is ready to use.
187 """
188 tmxdata = pytmx.TiledMap(filename)
189 _load_images_pygame(tmxdata, None, *args, **kwargs)
190 return tmxdata
191
192
193load_tmx = pytmx.TiledMap