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