123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- import os
- import sys
- import array
- import struct
- import zlib
- import textwrap
- import wave
- import audioop
- import Image
- import ImageFont
- import ImageDraw
- import gameduino2 as gd2
- import gameduino2.convert
- from gameduino2.imbytes import imbytes
- def stretch(im):
- d = imbytes(im)
- # print min(d), max(d)
- r = max(d) - min(d)
- return im.point(lambda x: (x - min(d)) * 255 / r)
- def getalpha(im):
- return im.split()[3]
- def tile(tw, th, im):
- tiles = []
- for y in range(0, im.size[1], th):
- for x in range(0, im.size[0], tw):
- tiles.append(im.crop((x, y, x + tw, y + th)))
- o = Image.new(im.mode, (tw, th * len(tiles)))
- for i,t in enumerate(tiles):
- o.paste(t, (0, i * th))
- return o
- def split(tw, th, im):
- tiles = []
- for y in range(0, im.size[1], th):
- for x in range(0, im.size[0], tw):
- tiles.append(im.crop((x, y, x + tw, y + th)))
- return tiles
- def join(tiles):
- (tw, th) = tiles[0].size
- o = Image.new(tiles[0].mode, (tw, th * len(tiles)))
- for i,t in enumerate(tiles):
- o.paste(t, (0, i * th))
- return o
- def setwidth(im, w):
- e = Image.new(im.mode, (w, im.size[1]))
- e.paste(im, (0, 0))
- return e
- def even(im):
- w = im.size[0]
- if (w % 2) == 0:
- return im
- else:
- return setwidth(im, w + 1)
- def extents(im):
- """ find pixel extents of im, as a box """
- w,h = im.size
- cols = [set(imbytes(im.crop((i, 0, i + 1, im.size[1])))) != set([0]) for i in range(w)]
- rows = [set(imbytes(im.crop((0, i, im.size[0], i + 1)))) != set([0]) for i in range(h)]
- if not True in cols:
- return (0, 0, 0, 0)
- else:
- x0 = cols.index(True)
- y0 = rows.index(True)
- while not cols[-1]:
- cols.pop()
- while not rows[-1]:
- rows.pop()
- return (x0, y0, len(cols), len(rows))
- def ul(x):
- return str(x) + "UL"
- def preview(np, fmt, size, data):
- def chan(x):
- return Image.fromstring("L", size, (255 * x).astype(np.uint8))
- if fmt == gd2.L1:
- r = Image.fromstring("1", size, data)
- elif fmt == gd2.L8:
- r = Image.fromstring("L", size, data)
- else:
- d8 = np.array(data)
- a = np.ones(size[0] * size[1])
- (r, g, b) = (a, a, a)
- if fmt == gd2.ARGB4:
- d16 = np.array(array.array('H', data.tostring()))
- a = (15 & (d16 >> 12)) / 15.
- r = (15 & (d16 >> 8)) / 15.
- g = (15 & (d16 >> 4)) / 15.
- b = (15 & (d16 >> 0)) / 15.
- elif fmt == gd2.RGB565:
- d16 = np.array(array.array('H', data.tostring()))
- r = (31 & (d16 >> 11)) / 31.
- g = (63 & (d16 >> 5)) / 63.
- b = (31 & (d16 >> 0)) / 31.
- elif fmt == gd2.ARGB1555:
- d16 = np.array(array.array('H', data.tostring()))
- a = (1 & (d16 >> 15))
- r = (31 & (d16 >> 10)) / 31.
- g = (31 & (d16 >> 5)) / 31.
- b = (31 & (d16 >> 0)) / 31.
- elif fmt == gd2.ARGB2:
- a = (3 & (d8 >> 6)) / 3.
- r = (3 & (d8 >> 4)) / 3.
- g = (3 & (d8 >> 2)) / 3.
- b = (3 & (d8 >> 0)) / 3.
- elif fmt == gd2.RGB332:
- r = (7 & (d8 >> 5)) / 7.
- g = (7 & (d8 >> 2)) / 7.
- b = (3 & (d8 >> 0)) / 3.
- elif fmt == gd2.L4:
- hi = d8 >> 4
- lo = d8 & 15
- d4 = np.column_stack((hi, lo)).flatten()
- r = d4 / 15.
- g = r
- b = r
- o = Image.merge("RGB", [chan(c) for c in (r, g, b)])
- bg = Image.new("RGB", size, (128, 128, 128))
- r = Image.composite(o, bg, chan(a))
- r = r.resize((r.size[0] * 5, r.size[1] * 5), Image.NEAREST)
- return r
- import gameduino2.base
- class AssetBin(gameduino2.base.GD2):
- asset_file = None
- prefix = ""
- previews = False
- def __init__(self):
- self.alldata = ""
- self.commands = ""
- self.defines = []
- self.inits = []
- self.handle = 0
- self.np = None
- self.bitmaps = []
- # Set defaults for FT800. target_810() modifies these
- self.device = 'GD2'
- self.maxram = 256 * 1024
- self.maxhandles = 15
-
- def target_810(self):
- self.device = 'GD3'
- self.maxram = 1024 * 1024
- self.maxhandles = 32
- def define(self, n, v):
- self.defines.append((self.prefix + n, v))
- def add(self, name, s):
- if name:
- self.defines.append((name, ul(len(self.alldata))))
- self.alldata += s
- def c(self, s):
- self.commands += s
- def addim(self, name, im, fmt, dither = False):
- (_, imgdata) = gameduino2.convert.convert(im, dither, fmt = fmt)
- self.add(name, imgdata.tostring())
- (w, h) = im.size
- if name:
- self.defines.append(("%s_WIDTH" % name, w))
- self.defines.append(("%s_HEIGHT" % name, h))
- def align(self, n):
- while (len(self.alldata) % n) != 0:
- self.alldata += chr(0)
- def load_handle(self, name, images, fmt,
- dither = False,
- filter = gd2.NEAREST,
- scale = 1,
- rotating = False):
- if self.maxhandles <= self.handle:
- print "Error: too many bitmap handles used, limit is %d" % self.maxhandles
- sys.exit(1)
- (w, h) = images[0].size
- self.define("%s_HANDLE" % name, self.handle)
- name = self.prefix + name
- self.defines.append(("%s_WIDTH" % name, w))
- self.defines.append(("%s_HEIGHT" % name, h))
- self.defines.append(("%s_CELLS" % name, len(images)))
- self.align(2)
- self.bitmaps.append((name.lower(), w, h, w / 2, h / 2, len(self.alldata), fmt, self.handle))
- self.BitmapHandle(self.handle);
- self.BitmapSource(len(self.alldata));
- if not rotating:
- (vw, vh) = (scale * w, scale * h)
- vsz = 0
- else:
- vsz = int(scale * max(w, h))
- self.define("%s_SIZE" % name, vsz)
- (vw, vh) = (vsz, vsz)
- self.BitmapSize(filter, gd2.BORDER, gd2.BORDER, vw, vh);
- self.inits.append("static const shape_t %s_SHAPE = {%d, %d, %d, %d};" % (name, self.handle, w, h, vsz))
- # aw is aligned width
- # For L1, L2, L4 formats the width must be a whole number of bytes
- if fmt == gd2.L1:
- aw = (w + 7) & ~7
- elif fmt == gd2.L2:
- aw = (w + 3) & ~3
- elif fmt == gd2.L4:
- aw = (w + 1) & ~1
- else:
- aw = w
- bpl = {
- gd2.ARGB1555 : 2 * aw,
- gd2.L1 : aw / 8,
- gd2.L2 : aw / 4,
- gd2.L4 : aw / 2,
- gd2.L8 : aw,
- gd2.RGB332 : aw,
- gd2.ARGB2 : aw,
- gd2.ARGB4 : 2 * aw,
- gd2.RGB565 : 2 * aw,
- gd2.PALETTED : aw}[fmt]
- self.BitmapLayout(fmt, bpl, h);
- for i,im in enumerate(images):
- if aw != w:
- im = setwidth(im, aw)
- if hasattr(im, "imgdata"):
- imgdata = im.imgdata
- else:
- (_, imgdata) = gameduino2.convert.convert(im, dither, fmt = fmt)
- """
- if self.previews:
- if not self.np:
- import numpy
- self.np = numpy
- preview(self.np, fmt, im.size, imgdata).save("previews/%s-%s-%02d.png" % (self.name, name, i))
- """
- self.alldata += imgdata.tostring()
- self.handle += 1
- def load_font(self, name, ims, widths, fmt, **args):
- trim0 = 0
- while ims[trim0] is None:
- trim0 += 1
- p0 = len(self.alldata)
- h = self.handle
- tims = ims[trim0:]
- self.load_handle(name, tims, fmt, **args)
- self.align(4)
- p1 = len(self.alldata)
- # Compute memory required by one char
- onechar = (p1 - p0) / len(tims)
- # print name, 'font requires', (p1 - p0), 'bytes'
- sz = ims[trim0].size
- self.BitmapSource(p0 - (onechar * trim0));
- widths = [max(0, w) for w in widths]
- dblock = array.array('B', widths).tostring() + struct.pack("<5i", fmt, 1, sz[0], sz[1], p0 - (onechar * trim0))
- self.alldata += dblock
- self.cmd_setfont(h, p1);
- def load_ttf(self, name, ttfname, size, format):
- font = ImageFont.truetype(ttfname, size)
- sizes = [font.getsize(chr(c)) for c in range(32, 128)]
- fw = max([w for (w, _) in sizes])
- fh = max([h for (_, h) in sizes])
- # print fw, fh
- alle = {}
- for i in range(1, 96):
- im = Image.new("L", (fw+8, fh))
- dr = ImageDraw.Draw(im)
- dr.text((8,0), chr(32 + i), font=font, fill=255)
- alle[i] = gd2.prep.extents(im)
- fw = max([(x1 - x0) for (x0, y0, x1, y1) in alle.values()])
- ims = ([None] * 32) + [Image.new("L", (fw, fh)) for i in range(32, 128)]
- for i in range(33, 127):
- dr = ImageDraw.Draw(ims[i])
- (x0, y0, x1, y1) = alle[i - 32]
- x = max(0, 8 - x0)
- if x > 0:
- sizes[i - 32] = (sizes[i - 32][0] - x, sizes[i - 32][1])
- dr.text((x, 0), chr(i), font=font, fill=255)
- # imgtools.view(im)
- widths = ([0] * 32) + [w for (w, _) in sizes]
- self.load_font(name, ims, widths, format)
- """
- def dxt1(self, imagefile):
- import numpy
- im = Image.open(imagefile).resize((480,272), Image.ANTIALIAS)
- dxt = "%s.dxt" % imagefile
- if not os.access(dxt, os.R_OK):
- im.save("tmp.png")
- assert os.system("squishpng tmp.png %s" % dxt) == 0
- sz = (480 / 4, 272 / 4)
- def rgb(cs):
- r = (cs >> 11) << 3
- g = 0xff & ((cs >> 5) << 2)
- b = 0xff & (cs << 3)
- return (r,g,b)
- def rgbim(cs):
- return Image.merge("RGB", [Image.fromarray(c.astype(numpy.uint8).reshape(*sz)) for c in rgb(cs)])
- def morton1(x):
- v = x & 0x55555555
- v = (v | (v >> 1)) & 0x33333333;
- v = (v | (v >> 2)) & 0x0F0F0F0F;
- v = (v | (v >> 4)) & 0x00FF00FF;
- v = (v | (v >> 8)) & 0x0000FFFF;
- return v.astype(numpy.uint16)
- h = open(dxt)
- h.read(8)
- c0s = []
- c1s = []
- bits = []
- for i in range(sz[0] * sz[1]):
- tile = h.read(8)
- c0,c1,bit = struct.unpack("2HI", tile)
- b0 = bit & 0x55555555
- b1 = (bit >> 1) & 0x55555555
- is0 = ~b1 & ~b0
- is1 = ~b1 & b0
- is2 = b1 & ~b0
- is3 = b1 & b0
- if c0<=c1:
- if bit == 0xaaaaaaaa:
- # print c0<c1,hex(bit)
- r0,g0,b0 = rgb(c0)
- r1,g1,b1 = rgb(c1)
- r = (r0 + r1) / 2
- g = (g0 + g1) / 2
- b = (b0 + b1) / 2
- # r,g,b = (255,0,255)
- c0 = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)
- c1 = c0
- bit = 0
- else:
- if 0:
- for i in range(0, 32, 2):
- fld = (3 & (bit >> i))
- if 3 == fld:
- print c0, c1, hex(bit), i
- assert 0
- # Map as follows:
- # 0 -> 0
- # 1 -> 3
- # 2 -> 1
- bit = (is1 * 3) + (is2 * 1)
- assert is3 == 0
- else:
- # 0 -> 0
- # 1 -> 3
- # 2 -> 1
- # 3 -> 2
- bit = (is1 * 3) + (is2 * 1) + (is3 * 2)
- if 0:
- c0 = 63 << 5 # green
- c1 = 31 << 11 # red
- bit = 0x0f0f0f0f
- c0s.append(c0)
- c1s.append(c1)
- bits.append(bit)
- c0s = numpy.array(c0s, numpy.uint16)
- c1s = numpy.array(c1s, numpy.uint16)
- bits = numpy.array(bits, numpy.uint32)
- bits0 = morton1(bits)
- bits1 = morton1(bits >> 1)
- if 1:
- def im44(v):
- im = Image.new("1", (4,4))
- pix = im.load()
- for i in range(4):
- for j in range(4):
- if v & (1 << (4 * i + j)):
- pix[j,i] = 255
- return im
- ims = [im44(i) for i in range(65536)]
- b0im = Image.new("1", (480, 272))
- b1im = Image.new("1", (480, 272))
- xys = [(x,y) for y in range(0, 272, 4) for x in range(0, 480, 4)]
- for i,(x,y) in enumerate(xys):
- b0im.paste(ims[bits0[i]], (x, y))
- b1im.paste(ims[bits1[i]], (x, y))
- class MockImage:
- def __init__(self, s, size):
- self.imgdata = s
- self.size = size
- self.load_handle("BACKGROUND_COLOR", [MockImage(c0s, sz), MockImage(c1s, sz)], gd2.RGB565, scale = 4)
- self.load_handle("BACKGROUND_BITS", [b0im, b1im], gd2.L1)
- """
- def load_sample(self, name, filename):
- f = wave.open(filename, "rb")
- if f.getnchannels() != 1:
- print "Sorry - .wav file must be mono"
- sys.exit(1)
- if f.getsampwidth() != 2:
- print "Sorry - .wav file must be 16-bit"
- sys.exit(1)
- freq = f.getframerate()
- pcm16 = f.readframes(f.getnframes())
- (adpcm, _) = audioop.lin2adpcm(pcm16, f.getsampwidth(), (0,0))
- adpcm = adpcm[:len(adpcm) & ~7]
- da = array.array('B', [((ord(c) >> 4) | ((15 & ord(c)) << 4)) for c in adpcm])
- self.align(8)
- self.add(name, da.tostring())
- self.define(name + "_LENGTH", len(da))
- self.define(name + "_FREQ", freq)
- header = None
- header_intro = ""
- def make(self):
- if self.header is None:
- name = self.__class__.__name__.lower() + "_assets.h"
- else:
- name = self.header
- self.name = name
- self.addall()
- if len(self.alldata) > self.maxram:
- print "Error: The data (%d bytes) is larger the the %s RAM (%d)" % (len(self.alldata), self.device, self.maxram)
- sys.exit(1)
- self.defines.append((self.prefix + "ASSETS_END", ul(len(self.alldata))))
- self.cmd_inflate(0)
- calldata = zlib.compress(self.alldata, 9)
- print 'Assets report'
- print '-------------'
- print 'Header file: %s' % self.header
- print '%s RAM used: %d' % (self.device, len(self.alldata))
- if not self.asset_file:
- print 'Flash used: %d' % len(calldata)
- else:
- print 'Output file: %s' % self.asset_file
- print 'File size: %d' % len(calldata)
- commandblock = self.commands + calldata
- hh = open(name, "w")
- hh.write(self.header_intro)
- for (nm,v) in self.defines:
- print >>hh, "#define %s %s" % (nm, v)
- p = self.prefix
- if self.asset_file is None:
- print >>hh, "static const PROGMEM uint8_t %s__assets[%d] = {" % (p, len(commandblock))
- print >>hh, textwrap.fill(", ".join(["%d" % ord(c) for c in commandblock]))
- print >>hh, "};"
- print >>hh, "#define %sLOAD_ASSETS() (GD.copy(%s__assets, sizeof(%s__assets)), GD.loadptr = %sASSETS_END)" % (p, p, p, p)
- else:
- open(self.asset_file, "wb").write(commandblock)
- print >>hh, '#define %sLOAD_ASSETS() (GD.safeload("%s"), GD.loadptr = %sASSETS_END)' % (p, self.asset_file, p)
- print >>hh
- for i in self.inits:
- print >>hh, i
- self.dump_bitmaps(hh)
- self.extras(hh)
- def dump_bitmaps(self, hh):
- hh.write("struct {\n")
- hh.write("".join([" Bitmap %s;\n" % bm[0] for bm in self.bitmaps]))
- hh.write("} bitmaps = {\n")
- fmt = " /* %16s */ {{%3d, %3d}, {%3d, %3d}, %#8xUL, %2d, %2d}"
- hh.write(",\n".join([fmt % bm for bm in self.bitmaps]))
- hh.write("\n};\n")
-
- def addall(self):
- pass
- def extras(self, hh):
- pass
- class ForthAssetBin(AssetBin):
- def make(self):
- if self.header is None:
- name = self.__class__.__name__.lower() + "_assets.fs"
- else:
- name = self.header
- self.name = name
- self.addall()
- if len(self.alldata) > self.maxram:
- print "Error: The data (%d bytes) is larger the the GD2 RAM" % len(self.alldata)
- sys.exit(1)
- self.defines.append((self.prefix + "ASSETS_END", ul(len(self.alldata))))
- self.cmd_inflate(0)
- calldata = zlib.compress(self.alldata)
- print 'Assets report'
- print '-------------'
- print 'Header file: %s' % self.header
- print 'GD2 RAM used: %d' % len(self.alldata)
- if not self.asset_file:
- print 'Flash used: %d' % len(calldata)
- else:
- print 'Output file: %s' % self.asset_file
- print 'File size: %d' % len(calldata)
- commandblock = self.commands + calldata
- commandblock += chr(0) * ((-len(commandblock)) & 3)
- commandblock32 = array.array('I', commandblock)
- hh = open(name, "w")
- print >>hh, "base @"
- print >>hh, "hex"
- if self.asset_file is None:
- print >>hh, textwrap.fill(" ".join(["%08x GD.c" % c for c in commandblock32]))
- else:
- open(self.asset_file, "wb").write(commandblock)
- print >>hh, "decimal"
- for (nm,v) in self.defines:
- print >>hh, "%-8s constant %s" % (str(v).replace('UL', ''), nm)
- print >>hh, "base !"
- self.extras(hh)
|