# svgasm **Repository Path**: tidyfriday/svgasm ## Basic Information - **Project Name**: svgasm - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-01 - **Last Updated**: 2021-03-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # *svgasm* [![Sponsor][img_sponsor]][sponsor]   [![Build status][img_build_status]][build_status]   [![Language grade: C/C++][img_lgtm]][lgtm]   [![License][img_license]][license] [img_build_status]: https://github.com/tomkwok/svgasm/workflows/svgasm/badge.svg [build_status]: https://github.com/tomkwok/svgasm/actions?query=branch%3Amaster [img_lgtm]: https://img.shields.io/lgtm/grade/cpp/g/tomkwok/svgasm.svg?logo=lgtm&logoWidth=18 [lgtm]: https://lgtm.com/projects/g/tomkwok/svgasm/latest/files/ [img_license]: https://img.shields.io/github/license/tomkwok/svgasm?color=green&cacheSeconds=3600 [license]: LICENSE.md [img_sponsor]: readme/badge_sponsor_animation.svg [sponsor]: https://github.com/sponsors/tomkwok [***svgasm***](https://github.com/tomkwok/svgasm) is a proof-of-concept SVG assembler to generate a self-contained animated SVG file from multiple still SVG files with CSS keyframes animation to play frames in sequence. Steps listed in reverse order of execution: - Produces single animated SVG file that is viewable in Chrome, Safari, Firefox, Edge and IE 10+. - Executes SVG cleaner [*svgcleaner*](https://github.com/RazrFalcon/svgcleaner) (by default) or [*svgo*](https://github.com/svg/svgo) to minify each SVG file. - Executes bitmap tracer [*potrace*](http://potrace.sourceforge.net/) (by default) or [*autotrace*](https://github.com/autotrace/autotrace), or [*primitive*](https://github.com/fogleman/primitive) to convert raster image input to SVG. - Executes image processor [*graphicsmagick*](http://www.graphicsmagick.org) (by default) or [*imagemagick*](https://imagemagick.org/) to convert GIF animation to frames. ![Flow diagram of svgasm from animated GIF to animated SVG](readme/svgasm.1.svg) ## Examples ### SVG animation from single GIF using tracer If the two versions of animation below are out of sync, try reloading this page without refreshing by clicking [here](https://github.com/tomkwok/svgasm).
Show evolution of life GIF example

Note that colors are inverted with gm convert -negate to obtain black-on-white images before tracing with potrace. The SVG output is hence black-on-transparent but restyled to white-on-black to match the input GIF.

svgasm -t 'gm convert -negate "%s" pgm:- | mkbitmap -x -f 1 -s 1 - -o - | potrace -t 0.4 --svg -o -' -s 'svg {background-color: black} path {fill: white}' examples/_evolution_of_life.gif > examples/evolution_of_life.svg

Input GIF (6,843 KiB) Output SVG (3,490 KiB → 1,394 KiB gzipped)
Show rotating cross GIF example

svgasm examples/_rotating_cross.gif > examples/rotating_cross.svg (with potrace as tracer by default)

Input GIF (665 KiB) Output SVG (462 KiB → 119 KiB gzipped)
Show infinity spiral GIF example

svgasm -t 'mkbitmap -t 0.4 -s 1 "%s" -o - | potrace -t 4 --svg -o -' -s 'svg {background-color: white}' examples/_infinity_spiral.gif > examples/infinity_spiral.svg

Input GIF (349 KiB) Output SVG (715 KiB → 285 KiB gzipped)
Show NSFW cartoon GIF example

svgasm -t 'mkbitmap -x -t 0.44 -s 1 "%s" -o - | potrace --svg -o -' -s 'svg {background-color: #ddd}' examples/_mickey_mouse_nsfw.gif > examples/mickey_mouse_nsfw.svg

Input GIF (912 KiB) Output SVG (1,164 KiB → 473 KiB gzipped)
Note that *potrace* [only natively handles two-valued images](http://potrace.sourceforge.net/faq.html#features). The color values in the SVG output can be specified with extra CSS styles definitions in the `-s` argument to the ***svgasm*** tool as in the command for the evolution of life GIF example. The colors do not have to be black or white or colors in grayscale used in the above examples. All of the above examples traced with *potrace* can also be traced with *autotrace* with less configuration but also less accurate (sometimes funky) shapes in results, which can be viewed in [examples/autotrace\_results.ipynb](examples/autotrace_results.ipynb). Use of multiple colors in SVG output is supported natively in *autotrace* but gradients are hard for any tracer.
Show Spongebob GIF example

svgasm -t 'autotrace --output-format svg --color-count 64 --despeckle-level 10 --despeckle-tightness 0.8 --remove-adjacent-corners "%s"' examples/_spongebob.gif > examples/spongebob.svg

Input GIF (426 KiB) Output SVG (888 KiB → 259 KiB gzipped)
Show anime GIF example

svgasm -t 'autotrace --output-format svg --color-count 120 --despeckle-level 16 --despeckle-tightness 1.5 --tangent-surround 1 --remove-adjacent-corners "%s"' -s 'svg {background-color: gray}' examples/_anime.gif > examples/anime.svg

Input GIF (1,616 KiB) Output SVG (1,268 KiB → 496 KiB gzipped)
While *primitive* is not a tracer, it can be used for raster-to-vector conversion with aesthetically pleasing results.
Show lions GIF example

svgasm -t 'gm convert "%s" png:- | primitive -i - -o - -n 250' examples/_lions.gif > examples/lions.svg

Input GIF (2,180 KiB) Output SVG (673 KiB → 193 KiB gzipped)
### SVG animation from multiple still SVGs
Show build status badge example

An example of a 2-fps 2-frame animated build status badge for this project is generated using svgasm from 2 GitHub Workflows status badge SVG files.

Build status badge animation example

Show calendar plot example

A worked example of a 1-fps 2-frame animated calendar plot in examples/calplot_animation.ipynb (Jupyter Notebook) is generated using calplot and svgasm.

Calplot animation example

Show contour plot example

While the above two examples can be created with CSS animation with some effort without the help of svgasm, the following example is not so easy. The following example is a 30-fps 41-frame animated contour plot of Fβ score from a sequence of 41 plots pre-generated using matplotlib animated with svgasm.

Contour plot animation example

Notably, ***svgasm*** does not use JavaScript in SVG output. (An alternative approach would be to re-program plot generation with a JavaScript library like [D3.js](https://github.com/d3/d3-contour) to have the browser generate the values of text, paths, gradients, etc.) ## Usage ``` svgasm [options] infilepath... Options: -d animation time delay in seconds (default: 0.1) -o path to SVG animation output file or - for stdout (default: -) -p prefix added to element IDs (default: _) -i animation iteration count (default: infinite) -e index of frame to stop at in last iteration if not infinite (default: -1) -l loading text in output or '' to turn off (default: 'Loading ...') -s extra CSS styles definition in output (default: '') -c command for SVG cleaner with "%s" (default: 'svgcleaner --multipass -c "%s"') -t command for tracer for non-SVG still image with "%s" (default: 'gm convert +matte "%s" ppm:- | potrace --svg -o -') -m command for magick program for GIF animation with %s (default: 'gm %s') -q silence verbose standard error output -h print help information ``` ## Usage examples - `svgasm -d 2 -i 5 -e 0 -o animation.svg input1.svg input2.svg input3.svg` Generates output *animation.svg* from *input1.svg*, *input2.svg* and *input3.svg* that animates with a delay of 2 seconds per frame, iterates 5 times, and stops at the first frame in the last iteration. - `svgasm -d 1/30 -l '' intro.jpg frame*.png > animation.svg` Generates output *animation.svg* from *intro.jpg* and wild card *frame\*.png* that animates with 30 frames per second, iterates infinitely, and with loading text turned off. - `svgasm animation1.gif animation2.gif > animation.svg` Generates output *animation.svg* from *animation1.gif* and *animation2.gif* that animates with the same time delay and iteration count as the first GIF file. ## Installing on macOS To install and run a pre-compiled binary of ***svgasm*** on macOS with [Homebrew](https://brew.sh/) installed, run the following commands, which would also install dependencies *svgcleaner*, *potrace* and *graphicsmagick*: ```sh brew install tomkwok/tap/svgasm svgasm ``` ## Building on macOS or Linux To clone this repository and build ***svgasm*** with a C++98 complier installed, run the following commands: ```sh git clone https://github.com/tomkwok/svgasm cd svgasm/ make ./svgasm ``` ***svgasm*** can be executed as a standalone program. However, the only working feature without dependencies is `svgasm [options] input*.svg > output.svg` (with automatic fallback to `cat` as dummy cleaner program). For more features, install runtime dependencies. An example instruction is provided for [Arch Linux](https://archlinux.org/) as follows: ```sh sudo pacman -S svgcleaner potrace graphicsmagick ``` To download the example files in this repository with `git-lfs` installed, run the following commands: ```sh git lfs install git lfs pull ``` ## Installing optional runtime dependencies - [*svgo*](https://github.com/svg/svgo): running the command `npm install -g svgo` or `yarn global add svgo` - [*autotrace*](https://github.com/autotrace/autotrace): downloading a release binary from the [release page of autotrace](https://github.com/autotrace/autotrace/releases) or install with a package manager - [*primitive*](https://github.com/fogleman/primitive): running the command `go get -u github.com/fogleman/primitive` ## Benchmark with different SVG cleaners ***svgasm*** has been tested to work with the following cleaners: - ***cat*** can be specified as a dummy cleaner program for most SVG files. (In particular, ensure that there are no unnecessary white-spaces in tags.) *cat* is the fallback program if *svgcleaner* is not installed on the system. - ***svgcleaner*** is the default cleaner program. *svgcleaner* is chosen as the default cleaner because it is generally very fast and produces small output. - ***svgo*** can be specified as the cleaner program. *svgo* is typically over an order of magnitude slower than *svgcleaner*. Nonetheless, *svgo* can sometimes produce smaller files than *svgcleaner* does. The following are the results of using ***svgasm*** to produce the 41-frame contour plot animation example above on an Intel Core i5 processor with different cleaners. The size of output after compression with [gzip](https://www.gzip.org/) at the best level `-9` is provided in brackets for reference since SVG files are usually served via HTTP which supports compression. | Command executed | Output SVG size (gzip) | Real elapsed time | |:------------------------------------------------- | --------------------------- | -----------------:| | `svgasm -c 'cat "%s"' [...]` | 3,143 KiB (1,145 KiB) | **0.265 s ± 0.006 s** | | `svgasm -c 'svgcleaner --multipass -c "%s"' [...]` | 2,232 KiB (992 KiB) | 0.844 s ± 0.041 s | | `svgasm -c 'svgo --multipass -o - "%s"' [...]` | **2,054 KiB (884 KiB)** | 34.839 s ± 0.292 s | ## Benchmark with GraphicsMagick and ImageMagick GraphicsMagick is a fork of ImageMagick, and it is reportedly faster in [benchmarks](http://www.graphicsmagick.org/benchmarks.html). The following are the results of using ***svgasm*** to produce the 60-frame rotating cross animation example above on an Intel Core i5 processor with GraphicsMagick and ImageMagick specified as the magick command. Note that the magick program is also present in the default tracer command for *potrace*. Identical output is obtained with the two utility programs. | | Command executed | Real elapsed time | | --------------- | :----------------------------------------------------------- | -----------------:| | GraphicsMagick | `svgasm -c 'cat "%s"' -m 'gm %s' -t 'gm convert "%s" [...]'` | **3.205 s ± 0.037 s** | | ImageMagick | `svgasm -c 'cat "%s"' -m '%s' -t 'convert "%s" [...]'` | 5.493 s ± 0.041 s | ## How ***svgasm*** is implemented - Refer to an [example](https://stackoverflow.com/questions/48893587/simple-animate-multiple-svgs-in-sequence-like-a-looping-gif) on Stack Overflow of animating a sequence of hand-drawn vector graphics. The principal method employed in the ***svgasm*** tool of playing SVGs in sequence with CSS animation is credited to its author. - Implementation is fine-tuned in the ***svgasm*** tool to better accommodate browser behavior. In particular, since Chrome renders elements on the fly and starts timing animations on elements as SVG file loads, attention was given to styles placement to prevent flickering in Chrome due to mismatching animation start times for different group elements for frames. See inline comments in [src/svgasm.cpp](src/svgasm.cpp) for details. Note that such flickering is only apparent in an animation with a high fps value. (It is not clear in the [CSS Animations specification](https://www.w3.org/TR/css-animations-1/#animations) whether a style rule is considered resolved or not when the styles are parsed but the elements referenced are not yet in the DOM. The answer is apparently no in Chrome, so CSS animations need to be moved to the end for animation of frames to start at the same time.) - An SVG file cleaner is executed to pre-process SVG files to ensure that ***svgasm*** can successfully parse input SVG files. For simplicity, ***svgasm*** uses string operations in standard library instead of an XML parser. The animated SVG file output generated does not seem to be further minifiable using *svgcleaner* or *svgo*. Currently, *svgo* gives an empty SVG when given the output of ***svgasm***. - The `` tag including its attributes (such as `width`, `height` and `viewbox`) in the first SVG file in the sequence of command-line arguments to the ***svgasm*** tool is copied to the output. It is assumed that all frames have the same size and viewport bounds, or all frames are effectively cropped to the size of the first frame. This approach is taken for simplicity in this proof-of-concept implementation and can be improved. - The ***svgasm*** tool generates a self-contained output file with a configurable loading text, the content of all SVG frames and CSS animation styles in `