diff --git a/cuburn/genome/convert.py b/cuburn/genome/convert.py index 5868268..7cf6d62 100644 --- a/cuburn/genome/convert.py +++ b/cuburn/genome/convert.py @@ -1,6 +1,7 @@ #!/usr/bin/env python2 import base64 +import binascii import warnings import xml.parsers.expat import numpy as np @@ -28,9 +29,17 @@ class XMLGenomeParser(object): assert self._flame is None self._flame = dict(attrs) self._flame['xforms'] = [] - self._flame['palette'] = np.ones((256, 4), dtype=np.float32) + if attrs.get('palette'): + pal = XMLPaletteParser.lookup(int(attrs['palette'])) + else: + pal = np.ones((256, 4), dtype=np.float32) + self._flame['palette'] = pal elif name == 'xform': + if 'color' in attrs: + # Color sometimes has an extra param that is unused by flam3 + attrs['color'] = attrs['color'].strip().split()[0] self._flame['xforms'].append(dict(attrs)) + self._flame['xforms'] elif name == 'finalxform': self._flame['finalxform'] = dict(attrs) elif name == 'color': @@ -50,6 +59,58 @@ class XMLGenomeParser(object): parser.parser.Parse(src, True) return parser.flames +class XMLPaletteParser(object): + _names, _numbers = None, None + + _locations = [ + '/usr/local/share/flam3/flam3-palettes.xml', + '/usr/share/flam3/flam3-palettes.xml', + ] + + def __init__(self, src): + self.names, self.numbers = {}, {} + self.parser = xml.parsers.expat.ParserCreate() + self.parser.StartElementHandler = self.start_element + self.parser.EndElementHandler = self.end_element + self.parser.Parse(src, True) + + def start_element(self, name, attrs): + if name == 'palette': + data = binascii.a2b_hex( + attrs['data'].replace('\n', '').replace(' ', '')) + pal = np.fromstring(data, 'u1').reshape((256, 4)) / 255.0 + if 'number' in attrs: + self.numbers[int(attrs['number'])] = pal + if 'name' in attrs: + self.names[attrs['name']] = pal + + def end_element(self, name): + pass + + @classmethod + def _load(cls): + src = None + for loc in cls._locations: + try: + with open(loc) as fp: + src = fp.read() + break + except: + pass + if not src: + raise IOError("Couldn't find a palettes XML file") + parser = cls(src) + cls._names, cls._numbers = parser.names, parser.numbers + + @classmethod + def lookup(cls, key, isname=False): + if not cls._names: + cls._load() + if isname: + return np.array(cls._names[key]) + else: + return np.array(cls._numbers[key]) + def convert_affine(aff, animate=False): xx, yx, xy, yy, xo, yo = vals = map(float, aff.split()) if vals == [1, 0, 0, 1, 0, 0]: return None diff --git a/cuburn/genome/tests/__init__.py b/cuburn/genome/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cuburn/genome/tests/test_convert.py b/cuburn/genome/tests/test_convert.py new file mode 100644 index 0000000..e9e2da4 --- /dev/null +++ b/cuburn/genome/tests/test_convert.py @@ -0,0 +1,81 @@ +import unittest + +import binascii +import numpy as np + +from cuburn.genome import convert + +def _make_palette_src(): + values = np.zeros((256,4), 'u1') + values[:,0] = range(256) + values[:,1] = 1 + values[:,2] = 2 + values[:,3] = 3 + # leave a newline in to make sure those get stripped + return """""" % binascii.b2a_hex(values.tostring()) + +def _make_genome_src(palette=False): + src = """ + + {color} + +""" + args = {'paletteidx': '', 'color': ''} + if palette: + args['paletteidx'] = 'palette="0"' + else: + args['color'] = '' + return src.format(**args) + +class XMLPaletteParserTest(unittest.TestCase): + def test_parse(self): + parser = convert.XMLPaletteParser(_make_palette_src()) + self.assertIn('synthetic', parser.names) + self.assertIn(0, parser.numbers) + self.assertEquals([0,1/255.,2/255.,3/255.], list(parser.numbers[0][0])) + self.assertEquals([1,1/255.,2/255.,3/255.], list(parser.numbers[0][255])) + +class ConversionTest(unittest.TestCase): + def test_parse(self): + parsed = convert.XMLGenomeParser.parse(_make_genome_src()) + converted = convert.flam3_to_node(parsed[0]) + palette = converted.pop('palette') + self.maxDiff = None + self.assertEquals(dict( + type='node', + author=dict(url='http://test.com', name='strobe'), + camera=dict(dither_width=1.0, scale=0.03125, + center=dict(x=0.01, y=0.02)), + filters=dict( + logscale=dict(brightness=4.0), + colorclip=dict(gamma=4.0)), + xforms={ + '0': dict( + color=0.0, + variations=dict(hyperbolic=dict(weight=0.1)), + pre_affine=dict( + spread=32.220017414088105, + angle=[20.91008494006789, -360], + magnitude=dict(x=1.019803902718557, y=0.5), + offset=dict(x=-0.5, y=-0.6)), + weight=0.1) + }), converted) + self.assertEquals('rgb8', palette[0]) + self.assertEquals('AQID////', palette[1][:8]) + + def test_parse_stock_palette(self): + try: + convert.XMLPaletteParser.lookup(0) + except: + # No system palettes installed, just skip the test + raise + parsed = convert.XMLGenomeParser.parse(_make_genome_src(True)) + converted = convert.flam3_to_node(parsed[0]) + palette = converted['palette'] + self.assertEquals('rgb8', palette[0]) + self.assertEquals('ALnqAMHu', palette[1][:8]) +