summaryrefslogtreecommitdiff
path: root/gbimg.py
blob: 5fda2f068b111761ed6562d42afa67b5261ffe46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#! /usr/bin/env python3
##
##  gbimg - turn PC image files into Game Boy tilesets
##  Copyright (C) 2016 Delwink, LLC
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU Affero General Public License as published by
##  the Free Software Foundation, version 3 only.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU Affero General Public License for more details.
##
##  You should have received a copy of the GNU Affero General Public License
##  along with this program.  If not, see <http://www.gnu.org/licenses/>.
##

from wand.image import Image

_PALETTE = {
    0:   3,
    85:  2,
    170: 1,
    255: 0
}

def map_palette(color):
    if color not in _PALETTE:
        raise ValueError('{} is not a valid color level'.format(color))

    return _PALETTE[color]

class Tileset:
    def __init__(self, img, data, bank=None):
        self._img = img
        self._data = None
        self._data_var = data

        if type(bank) is str:
            self._bank_var = bank
            self._bank_num = None
        elif type(bank) is int:
            self._bank_var = None
            self._bank_num = bank
        elif bank is None:
            self._bank_var = None
            self._bank_num = None
        else:
            raise TypeError('bank must be str, int, or None')

    def set_bank_num(self, num):
        self._bank_num = num

    def _convert(self):
        with Image(filename=self._img) as img:
            width, height = img.size
            if height != 8:
                raise ValueError('{} height is {}'.format(self, height))

            num_tiles = width / 8
            if num_tiles % 1 != 0:
                raise ValueError('Irregular tile width in {}'.format(self))

            num_tiles = int(num_tiles)
            img.depth = 8
            blob = img.make_blob(format='RGB')

        encoded = []
        for tile in range(num_tiles):
            encoded_tile = []

            for i in range(0, width * 8 * 3, width * 3):
                byte1 = 0
                byte2 = 0
                bit = 0x80

                for j in range(0, 8 * 3, 3):
                    color = map_palette(blob[(i + j) + ((8 * 3) * tile)])

                    if color & 0x01:
                        byte1 |= bit
                    if color & 0x02:
                        byte2 |= bit

                    bit >>= 1

                encoded_tile.append(byte1)
                encoded_tile.append(byte2)

            encoded += encoded_tile

        return bytes(encoded)

    @property
    def data(self):
        if not self._data:
            self._data = self._convert()

        return self._data

    @property
    def image_file(self):
        return self._img

    @property
    def data_var(self):
        return self._data_var

    @property
    def bank_var(self):
        return self._bank_var

    @property
    def bank_num(self):
        return self._bank_num

    def __len__(self):
        return len(self.data)

    def __str__(self):
        return 'Tileset ' + self.image_file