Source code for img2gb.gbtilemap

"""
The :class:`GBTilemap` class represents a GameBoy tilemap. It can map up to
32x32 tiles that are stored in a :class:`GBTileset` (if not given, a new empty
tileset is created.

Creating a new tilemap::

    from img2gb import GBTilemap, GBTileset, GBTile

    tileset = GBTileset()
    tilemap = GBTilemap(
            width=32,
            height=32,
            gbtileset=tilset
            )

    tile = GBTile()  # blank tile

    tilemap.put_tile(0, 0, tile)  # The tile will be added in the tileset


Creating a tilemap from an image::

    from img2gb import GBTilemap
    from PIL import Image

    image = Image.open("./my_tilemap.png")
    tilemap = tilemap.from_image(image)
"""

from .gbtile import GBTile
from .gbtileset import GBTileset
from .helpers import to_pil_rgb_image


[docs] class GBTilemap(object): """Stores and manipulates GameBoy tilemaps. :param int width: With of the tilemap in tile (default = ``32``). :param int height: Height of the tilemap in tile (default = ``32``). :param GBTileset gbtileset: The tileset to use (a new empty one will be created if set to ``None``, default = ``None``). """
[docs] @classmethod def from_image( Cls, pil_image, gbtileset=None, missing="append", replace=0, dedup=True ): """Generates the tilemap from the given image. The tileset can also be generated at the same time. :param PIL.Image.Image pil_image: The image that represents the tilemap. :param GBTileset gbtileset: The tileset that contains the tiles used in the tilemap (a new empty one is created if not provided). :param str missing: What to do if a tile is missing from the tileset: * ``"append"`` (default): append the tile to the tileset, * ``"error"``: raise an error, * ``"replace"``: relpace by an other tile (see the ``replace`` argument). :param int replace: The id of the replacement tile when ``missing="replace"``. :param bool dedup: Deduplicate tiles when ``missing="append"`` (default = ``True``). """ image = to_pil_rgb_image(pil_image) width, height = image.size if width % 8 or height % 8: raise ValueError("The input image width and height must be a multiple of 8") tilemap = Cls(width / 8, height / 8, gbtileset=gbtileset) for tile_y in range(0, height, 8): for tile_x in range(0, width, 8): tile = GBTile.from_image(image, tile_x, tile_y) tilemap.put_tile( tile_x / 8, tile_y / 8, tile, missing=missing, replace=replace, dedup=dedup, ) return tilemap
def __init__(self, width=32, height=32, gbtileset=None): self._tileset = gbtileset if gbtileset else GBTileset() self._map = [0x00] * int(width * height) self._width = width self._height = height @property def tileset(self): """The tileset that contains the tiles used by the tilemap. :type: GBTileset """ return self._tileset @property def width(self): """The width of the tilemap. :type: int """ return self._width @property def height(self): """The height of the tilemap. :type: int """ return self._height @property def size(self): """The with and height of the tilemap. :type: tuple of two int ``(width, height)`` """ return self._width, self._height @property def data(self): """Raw data of the tilemap. :type: list of int """ return self._map
[docs] def put_tile(self, x, y, gbtile, missing="append", replace=0, dedup=True): """Put a tile at the given position in the tilemap. :param int x: The x coordinate where to put the tile in the tilemap. :param int y: The y coordinate where to put the tile in the tilemap. :param GBTile gbtile: The tile to put in the tilemap. :param str missing: What to do if a tile is missing from the tileset: * ``"append"`` (default): append the tile to the tileset, * ``"error"``: raise an error, * ``"replace"``: relpace by an other tile (see the ``replace`` argument). :param int replace: The id of the replacement tile when ``missing="replace"``. :param bool dedup: Deduplicate tiles when ``missing="append"`` (default = ``True``). """ if x < 0 or y < 0: raise ValueError("x and y coordinates cannot be negative") if x >= self._width: raise ValueError( "The x coordinate is greater than the width of the tilemap" ) if y >= self._height: raise ValueError( "The y coordinate is greater than the height of the tilemap" ) if missing not in ("append", "error", "replace"): raise ValueError( "Wrong value '%s' for the missing argument. Authorised values are 'append', 'error' and 'replace'." ) if gbtile in self._tileset.tiles and dedup: tile_id = self._tileset.index(gbtile) else: if missing == "append": tile_id = self._tileset.add_tile(gbtile, dedup=dedup) elif missing == "error": raise ValueError("The given tile is missing from the tileset.") elif missing == "replace": tile_id = replace index = int(y * self._width + x) self._map[index] = tile_id
[docs] def to_hex_string(self): """Returns the tilemap as an hexadecimal-encoded string. :rtype: str e.g. (4x4 tiles):: 00 00 00 00 00 01 02 00 00 03 04 00 00 00 00 00 """ result = "" for index in range(len(self._map)): tile_id = self._map[index] result += "%02X" % tile_id if (index + 1) % self._width == 0: result += "\n" else: result += " " return result.strip()
[docs] def to_c_string(self, name="TILEMAP"): """Returns C code that represents the tilemap. :param str name: The name of the variable in the generated code (always converted to uppercase in the generated code, default = ``"TILEMAP"``) :rtype: str """ c = "const UINT8 %s[] = {\n" % name.upper() for index in range(len(self._map)): if index % self._width == 0: c += " " tile_id = self._map[index] c += "0x%02X," % tile_id if (index + 1) % self._width == 0: c += "\n" else: c += " " c += "};" return c
[docs] def to_c_header_string(self, name="TILEMAP"): """Returns the C header (.h) code for the tilemap. :param str name: The name of the variable in the generated code (always converted to uppercase in the generated code, default = ``"TILEMAP"``) :rtype: str """ h = "extern const UINT8 %s[];\n" % name.upper() h += "#define %s_WIDTH %i\n" % (name.upper(), self._width) h += "#define %s_HEIGHT %i" % (name.upper(), self._height) return h