Aug. 11, 2008

Update: the article below is for an Inkscape extension I built a while back for doing animation. For a newer and much better solution for doing frame-by-frame animation in both Inkscape and Adobe Illustrator check out SVG Flipbook. Enjoy!

Inkscape is an awesome, free and open source vector graphics program. It's the free world's version of Adobe's Illustrator program. I can't comment on whether it's better or worse since I've never used Illustrator, but there are many things to love about Inkscape and it's very capable when it comes to vector graphics.

One thing that is hard to come by is a good, free and open source vector animation program. The Inkscape website says that they are looking at introducing animation into the program some time in the future, but who knows how long that will take. I needed to make animated vector graphics for my GameJam 0808 entry, which is a vector graphics platformer, so I knocked together a quick script to do layer based animation in Inkscape. Luckily Inkscape's extensions system is quite extensible, so I was able to write a small Python script that renders each layer separately using Inkscape itself, and then uses Imagemagick's "convert" program to mash the layers together into an animated GIF. Finally, the extension automatically launches the default browser to display the result. This currently only works under GNU/Linux systems with Imagemagick installed, but I'm sure it wouldn't be too big a stretch to get it working on other operating systems.

Here is the Inkscape extension XML file. Place this in your .inkscape/extensions/ directory in a file called gifanimate.inx:

<inkscape-extension>
  <_name>GIF Animate</_name>
  <id>org.ekips.filter.GIFAnimate</id>
  <dependency type="executable" location="extensions">gifanimate.py</dependency>
  <dependency type="executable" location="extensions">inkex.py</dependency>
  <!-- <param name="what" type="string" _gui-text="What would you like to greet?">World</param> -->
  <effect>
    <object-type>all</object-type>
    <effects-menu>
       <submenu _name="Animate"/>
    </effects-menu>
  </effect>
  <script>
    <command reldir="extensions" interpreter="python">gifanimate.py</command>
  </script>
</inkscape-extension>

Here is the Python script that does all the work. Put this in the same place, and name it 'gifanimate.py'.

#!/usr/bin/env python
import sys, os
sys.path.append('/usr/share/inkscape/extensions')

import inkex
from simplestyle import *

class GIFAnimate(inkex.Effect):
    def __init__(self):
        inkex.Effect.__init__(self)

    def effect(self):
        infile = sys.argv[-1]

        # Get access to main SVG document element and get its dimensions.
        from xml.dom.minidom import parse, parseString
        dom = parse(infile)
        svg = dom.getElementsByTagName('svg')[0]

        layers = svg.getElementsByTagName('g')
        pngcommand = "inkscape %s -C -i '%s' -j -e /tmp/%s.png 2>&1 > /dev/null"

        layerids = []
        delays = []
        for l in layers:
            bits = l.getAttribute('inkscape:label').split("#")
            if not "ignore" in bits:
                layerids.append(l.getAttribute('id'))
                delays.append(bits[0])

        [os.system(pngcommand % (infile, id, id)) for id in layerids]
        bits = " ".join(["-delay %d /tmp/%s.png" % (int(delays[x]), layerids[x]) for x in range(len(layerids))])
        gifcmd = "convert -loop 0 " + bits + " /tmp/inkscape-anim.gif"
        os.system(gifcmd)
        import webbrowser
        webbrowser.open("file:///tmp/inkscape-anim.gif")

# Create effect instance and apply it.
effect = GIFAnimate()
effect.affect()

To use it go to the 'effects' menu and choose 'Animate > GIF Animate'. Specify frame lengths (in milliseconds) by renaming the layer to the length of time that you'd like each frame to last. Inkscape will add a hash followed by a number for each duplicate entry, but my script will ignore that.

Main character walkcycle

Have fun!