mkpieces.py (3543B)
1 #!/usr/bin/env python3 2 """Genera gfx/pieces.c: sprites de las piezas a partir de los glifos de ajedrez de 3 DejaVu Sans. Cada pixel usa 2 bits: 0 transparente, 1 cuerpo, 2 contorno/detalle. 4 Uso: python3 tools/mkpieces.py > gfx/pieces.c (con --png ARCHIVO hace una muestra)""" 5 import sys 6 from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageChops 7 8 FONT = '/usr/share/fonts/TTF/DejaVuSans.ttf' 9 SIZES = [48, 32] 10 ORDER = 'PNBRQK' 11 FILLED = {'K': '♚', 'Q': '♛', 'R': '♜', 'B': '♝', 'N': '♞', 'P': '♟'} 12 HOLLOW = {'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙'} 13 14 def glyph(ch, size, box): 15 f = ImageFont.truetype(FONT, box) 16 im = Image.new('L', (size * 2, size * 2), 0) 17 ImageDraw.Draw(im).text((size, size), ch, font=f, fill=255, anchor='mm') 18 return im 19 20 def sprite(p, size): 21 box = int(size * 1.02) 22 full = glyph(FILLED[p], size, box) 23 hollow = glyph(HOLLOW[p], size, box) 24 bb = full.getbbox() 25 cx, cy = (bb[0] + bb[2]) // 2, (bb[1] + bb[3]) // 2 26 off = (cx - size // 2, cy - size // 2 - (size // 24)) 27 crop = lambda im: im.crop((off[0], off[1], off[0] + size, off[1] + size)) 28 full, hollow = crop(full), crop(hollow) 29 body = full.point(lambda v: 255 if v > 110 else 0) 30 # cuerpo = silueta rellena: el glifo hueco relleno por dentro coincide con el lleno 31 outer = body.filter(ImageFilter.MaxFilter(3)) 32 outline = ImageChops.subtract(outer, body) 33 # detalle interior: las lineas del glifo hueco que caen dentro del cuerpo 34 detail = hollow.point(lambda v: 255 if v > 140 else 0) 35 inner = body.filter(ImageFilter.MinFilter(3)) 36 detail = ImageChops.multiply(detail, inner) 37 px = [] 38 for y in range(size): 39 for x in range(size): 40 if outline.getpixel((x, y)) or detail.getpixel((x, y)): px.append(2) 41 elif body.getpixel((x, y)): px.append(1) 42 else: px.append(0) 43 return px 44 45 def main(): 46 if len(sys.argv) > 2 and sys.argv[1] == '--png': 47 W = sum(SIZES) + 8 48 im = Image.new('RGB', (len(ORDER) * 2 * (SIZES[0] + 4), W * 1), (120, 140, 90)) 49 x = 0 50 for col, (b, o) in enumerate([((240, 240, 230), (20, 20, 20)), ((30, 30, 34), (200, 200, 200))]): 51 for p in ORDER: 52 y = 0 53 for s in SIZES: 54 px = sprite(p, s) 55 for i, v in enumerate(px): 56 if v: im.putpixel((x + i % s, y + i // s), b if v == 1 else o) 57 y += s + 4 58 x += SIZES[0] + 4 59 im.resize((im.width * 3, im.height * 3), Image.NEAREST).save(sys.argv[2]) 60 return 61 print('/* pieces.c - sprites de las piezas, generado por tools/mkpieces.py (glifos de DejaVu Sans). No editar. */') 62 print('#include "chess_gfx.h"\n') 63 for s in SIZES: 64 for p in ORDER: 65 px = sprite(p, s) 66 data = [] 67 for i in range(0, len(px), 4): 68 b = 0 69 for k in range(4): 70 b |= px[i + k] << (2 * k) 71 data.append(b) 72 print('static const uint8_t P%d_%s[%d] = {' % (s, p, len(data))) 73 for i in range(0, len(data), 24): 74 print(' ' + ', '.join('0x%02x' % v for v in data[i:i + 24]) + ',') 75 print('};') 76 print('\nconst PieceSet PIECES[%d] = {' % len(SIZES)) 77 for s in SIZES: 78 print(' { %d, { 0, %s } },' % (s, ', '.join('P%d_%s' % (s, p) for p in ORDER))) 79 print('};') 80 print('const int NPIECESETS = %d;' % len(SIZES)) 81 82 main()