Source code for engine.gfx.util

"""Utilities for graphics."""

import pygame as pg

from ..conf import conf
from .. import util


[docs]class Spritemap (object): """A wrapper for spritesheets. Spritemap(img[, ncols][, nrows][, sw][, sh], pad=0[, nsprites], pool=conf.DEFAULT_RESOURCE_POOL, res_mgr=conf.GAME.resources) :arg img: a surface or filename to load from; this is a grid of sprites with the same size. :arg ncols,sw: determines the the number of columns in the spritesheet; ``ncols`` is the number of columns and ``sw`` is the width of individual sprites, in pixels. Only one is required, and both may be omitted if the spritesheet is a single column. :arg nrows,sh: determines the the number of rows in the spritesheet; ``nrows`` is the number of rows and ``sh`` is the height of individual sprites. Only one is required, and both may be omitted if the spritesheet is a single row. :arg pad: padding in pixels between each sprite. This may be ``(col_gap, row_gap)``, or a single number for the same gap in both cases. :arg nsprites: the number of sprites in the spritesheet. If omitted, this is taken to be the maximum number of sprites that could fit on the spritesheet; if passed, and smaller than the maximum, the last sprites are ignored (see below for ordering). :arg pool: :class:`ResourceManager <engine.res.ResourceManager>` resource pool name to cache any loaded images in. :arg res_mgr: :class:`ResourceManager <engine.res.ResourceManager>` instance to use to load any images. A spritemap provides ``__len__`` and ``__getitem__`` to obtain sprites, and so iterating over all sprites is also supported. Sprites are obtained from top to bottom, left to right, in that order, and slices are as follows:: spritemap[sprite_index] -> sfc spritemap[col, row] -> sfc where ``sfc`` is a surface containing the sprite. (The latter form is an implicit ``tuple``, so ``spritemap[(col, row)]`` works as well.) """ def __init__ (self, img, ncols=None, nrows=None, sw=None, sh=None, pad=0, nsprites=None, pool=conf.DEFAULT_RESOURCE_POOL, res_mgr=None): if isinstance(img, basestring): if res_mgr is None: res_mgr = conf.GAME.resources img = res_mgr.img(img, pool=pool) img_sz = img.get_size() if isinstance(pad, int): pad = (pad, pad) if pad[0] < 0 or pad[1] < 0: raise ValueError('padding must be positive') # get number of columns and rows and sprite size ncells = [ncols, nrows] ss = [sw, sh] for axis in (0, 1): n = ncells[axis] s_sz = ss[axis] i_sz = img_sz[axis] p = pad[axis] if n is not None: if (i_sz + p) % n != 0: raise ValueError( 'invalid image size (dimension {0}): expected ' '({1}n-{2}), got {3}'.format(axis, n, p, i_sz) ) ss[axis] = (i_sz + p) // n - p elif s_sz is not None: if (i_sz + p) % (s_sz + p) != 0: raise ValueError( 'invalid image size (dimension {0}): expected ' '({1}n-{2}), got {3}'.format(axis, s_sz + p, p, i_sz) ) ncells[axis] = (i_sz + p) // (s_sz + p) else: ncells[axis] = 1 ss[axis] = i_sz self._ncells = ncells ncols, nrows = ncells ncells = ncols * nrows if nsprites is None or nsprites > ncells: nsprites = ncells #: The width of each sprite, in pixels. self.sprite_w = ss[0] #: The height of each sprite, in pixels. self.sprite_h = ss[1] #: ``(``:attr:`sprite_w` ``,`` :attr:`sprite_h` ``)``. self.sprite_size = tuple(ss) # copy to separate surfaces self._sfcs = sfcs = [] tile_rect = util.grid.Grid(ncells, ss, pad).tile_rect mk_sfc = util.blank_sfc if util.has_alpha(img) else pg.Surface for i in xrange(nsprites): rect = tile_rect(i % ncols, i // ncols) sfc = mk_sfc(rect.size) sfc.blit(img, (0, 0), rect) sfcs.append(sfc) def __len__ (self): return len(self._sfcs) def __getitem__ (self, i): ncols, nrows = self._ncells if not isinstance(i, int): col, row = i if col < 0: col += ncols if row < 0: row += nrows if (col < 0 or col >= ncols or row < 0 or row >= nrows): raise IndexError('spritemap index out of bounds') i = row * ncols + col return self._sfcs[i]