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])
+