all repos — Legends-RPG @ f2dca0d393a15a440a0a97993509345ca515d575

A fantasy mini-RPG built with Python and Pygame.

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