Subversion Repositories pentevo

Rev

Rev 1054 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1050 lvd 1
#!/usr/bin/env python3
2
 
3
"""
4
// ZX-Evo SDLoad Configuration (c) NedoPC 2023
5
//
6
// font generator: takes 6912-positioned font and generates font in internal FPGA format
7
 
8
/*
9
    This file is part of ZX-Evo Base Configuration firmware.
10
 
11
    ZX-Evo Base Configuration firmware is free software:
12
    you can redistribute it and/or modify it under the terms of
13
    the GNU General Public License as published by
14
    the Free Software Foundation, either version 3 of the License, or
15
    (at your option) any later version.
16
 
17
    ZX-Evo Base Configuration firmware is distributed in the hope that
18
    it will be useful, but WITHOUT ANY WARRANTY; without even
19
    the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20
    See the GNU General Public License for more details.
21
 
22
    You should have received a copy of the GNU General Public License
23
    along with ZX-Evo Base Configuration firmware.
24
    If not, see <http://www.gnu.org/licenses/>.
25
*/
26
"""
27
 
28
import argparse,os,sys
29
 
1051 lvd 30
class ZXPic:
1050 lvd 31
 
1051 lvd 32
        def __init__(self,filename):
33
 
34
                with open(filename,'rb') as file:
35
                        self.zxscr = bytes(file.read())
1050 lvd 36
 
1054 lvd 37
                if( len(self.zxscr)!=6144 and len(self.zxscr)!=6912 ):
1051 lvd 38
                        sys.exit('Wrong zx file <{}> size, must be 6144 or 6912'.format(filename))
1050 lvd 39
 
1051 lvd 40
                if( len(self.zxscr)==6912 ):
41
                        self.colored = True
42
                else:
43
                        self.colored = False
1050 lvd 44
 
1051 lvd 45
                self.pixels = bytes(self.zxscr[:6144])
1050 lvd 46
 
1051 lvd 47
                if( self.colored ):
48
                        self.attrs = bytes(self.zxscr[6144:])
49
                else:
50
                        self.attrs = None
51
 
1052 lvd 52
                self.sz_x = 256
53
                self.sz_y = 192
1051 lvd 54
 
1052 lvd 55
 
56
        def get_pix(self,x,y):
1051 lvd 57
 
1052 lvd 58
                if( x<0 or x>=self.sz_x or y<0 or y>=self.sz_y ):
59
                        sys.exit('x,y must be within 0..{} and 0..{} range!'.format(self.sz_x-1,self.sz_y-1))
1051 lvd 60
 
61
 
62
                bitnum = 7 - (x & 7)
63
 
64
                offset = (x>>3) + (y & 7)*256 + ((y & 0x38)>>3)*32 + ((y & 0xC0)>>6)*2048
65
 
66
                return True if self.pixels[offset] & (1<<bitnum) else False
67
 
68
 
1052 lvd 69
class CharSet:
1051 lvd 70
 
1052 lvd 71
        def __init__(self, num_els, first_idx, sz_x, sz_y):
1051 lvd 72
 
1059 lvd 73
                num_els   = int(num_els)
74
                first_idx = int(first_idx)
75
                sz_x      = int(sz_x)
76
                sz_y      = int(sz_y)
1051 lvd 77
 
1059 lvd 78
                # check arguments
79
                assert num_els>0, 'num_els must be positive!'
80
                assert first_idx>=0, 'first_idx must be non-negative!'
81
                assert sz_x>0, 'sz_x must be positive!'
82
                assert sz_y>0, 'sz_y must be positive!'
1051 lvd 83
 
1059 lvd 84
                self.num_els   = num_els
85
                self.first_idx = first_idx
86
                self.sz_x      = sz_x
87
                self.sz_y      = sz_y
1051 lvd 88
 
1052 lvd 89
                # generate empty characters
90
                self.charset = [None] * (self.first_idx + self.num_els)
91
 
92
                for char_idx in range(self.first_idx, self.first_idx + self.num_els):
93
 
94
                        char = [None] * self.sz_y
95
 
96
                        for char_y in range(self.sz_y):
97
 
98
                                line = [False] * self.sz_x;
99
 
100
                                char[char_y] = line
101
 
102
                        self.charset[char_idx] = char
103
 
104
 
105
        def set_pix(self, char_idx, char_y, char_x, value):
106
 
107
                self.charset[char_idx][char_y][char_x] = value
108
 
109
 
110
        def get_pix(self, char_idx, char_y, char_x):
111
 
112
                return self.charset[char_idx][char_y][char_x]
113
 
114
 
115
 
116
def generate_font(pic, start_cx=0, start_cy=0, blk_sx=8, blk_sy=8, box_offx=0, box_offy=0, box_sx=6, box_sy=6, first_idx=32, num_els=224):
117
# pic -- byte pic with sizes pic.sz_x, pic.sz_y and get_pix(x,y)
118
# blk_sx/y -- size of font blocks (bounding boxes), typical 8x8
119
# start_cx/cy -- coord of first element of font, in blocks (typical upper left, 0/0)
120
# box_offx/y -- offset of actual box with a letter inside block (typical 0/0)
121
# box_sx/y -- actual box size, 6/6 for 6x6 font etc.
122
# first_idx -- font index corresponding to start_cx/y position
123
# num_els -- how many font elements to parse
124
 
125
        # check args
126
        start_cx = int(start_cx)
127
        start_cy = int(start_cy)
128
        blk_sx = int(blk_sx)
129
        blk_sy = int(blk_sy)
130
        box_offx = int(box_offx)
131
        box_offy = int(box_offy)
132
        box_sx = int(box_sx)
133
        box_sy = int(box_sy)
134
        first_idx = int(first_idx)
135
        num_els = int(num_els)
136
 
137
        assert first_idx>=0
138
        assert num_els>0
139
 
140
        assert blk_sx>0
141
        assert blk_sy>0
142
 
143
        assert start_cx>=0
144
        assert start_cy>=0
145
 
146
        assert blk_sx*(start_cx+1) <= pic.sz_x
147
        assert blk_sy*(start_cy+1) <= pic.sz_y
148
 
149
        assert box_offx>=0
150
        assert box_offy>=0
151
 
152
        assert box_sx>0
153
        assert box_sy>0
154
 
1054 lvd 155
        assert box_offx+box_sx <= blk_sx, 'box_offx+box_sx > blk_sx!'
156
        assert box_offy+box_sy <= blk_sy, 'box_offy+box_sy > blk_sy!'
1052 lvd 157
 
158
 
159
        # create empty font
160
        font = CharSet(num_els, first_idx, box_sx, box_sy)
161
 
162
        # load font data
163
        curr_blk_x = start_cx
164
        curr_blk_y = start_cy
165
 
166
        pic_overflow = False
167
 
168
        for char_idx in range(first_idx, first_idx + num_els):
169
 
170
                assert not pic_overflow
171
 
172
                # x/y of upper left part of the box
173
                x_origin = curr_blk_x*blk_sx + box_offx
174
                y_origin = curr_blk_y*blk_sy + box_offy
175
 
176
                # copy pixels
177
                for y in range(box_sy):
178
                        for x in range(box_sx):
1054 lvd 179
                                font.set_pix(char_idx, y, x, pic.get_pix(x_origin + x, y_origin + y))
1052 lvd 180
 
181
                # step to next char in bitmap
182
                curr_blk_x = curr_blk_x + 1
183
                if( curr_blk_x*blk_sx >= pic.sz_x ):
184
                        curr_blk_x = 0
185
                        curr_blk_y = curr_blk_y + 1
186
                        if( curr_blk_y*blk_sy >= pic.sz_y ):
187
                                curr_blk_y = 0
188
                                pic_overflow = True
189
 
190
 
191
        return font
192
 
193
 
194
 
195
 
1054 lvd 196
def gen_binary(font):
197
 
198
        binary = bytearray(1024) #zeroed
199
 
200
        for i in range(32,256):
201
                for y in range(6):
202
                        for x in range(6):             
1059 lvd 203
 
204
                                # actual layout of the resulting font is defined here
1054 lvd 205
                                offs = ((i>>3)*36 + x + y*6) & 0x3FF
1059 lvd 206
                                #
1054 lvd 207
                                bit = 1<<(7-(i&7))
208
 
209
                                if font.get_pix(i,y,x):
210
                                        binary[offs] = binary[offs] | bit
211
 
212
        return binary
213
 
214
 
215
 
1050 lvd 216
def main():
217
 
1051 lvd 218
        # parse arguments
219
        p = argparse.ArgumentParser()
220
        #
1052 lvd 221
        p.add_argument('--scr', '-s',           action='store', required=True, help='Filename of 6912 or 6144 ZX screen with font')
222
        p.add_argument('--out', '-o',           action='store', required=True, help='Filename prefix for resulting file(s). Extensions will be added as needed')
223
        p.add_argument(         '-x', type=int, action='store', default=0,     help='Initial X position of 8x8 block with first symbol (that must be a space)')
224
        p.add_argument(         '-y', type=int, action='store', default=0,     help='Initial Y position of 8x8 block with first symbol (that must be a space)')
1051 lvd 225
        #
226
        args = p.parse_args()
1050 lvd 227
 
1051 lvd 228
 
1052 lvd 229
        pic = ZXPic(args.scr)
230
 
1054 lvd 231
        font = generate_font(pic=pic,
232
                             start_cx=0, start_cy=1,
233
                             first_idx=32, num_els=224)
1052 lvd 234
 
1054 lvd 235
        binary = gen_binary(font)
1052 lvd 236
 
237
 
1054 lvd 238
        bin_name = args.out + ".bin"
1052 lvd 239
 
1054 lvd 240
        with open(bin_name,"wb") as wrbin:
241
                wrbin.write(binary)
242
 
243
 
1050 lvd 244
if __name__=="__main__":
245
        main()
246