No, seriously, hear me out!
I needed vector graphics for a Zig project and I figured that everyone inluding myself is using SVG. So I wanted to use SVG. What it learned in the process that implementing a new SVG library is hard.
First of all, SVG is built on top of several other technologies:
For the purpose of static graphics, we can ignore the scripting portion of that. But even without that, implementing both a good XML and CSS parser is a lot of work.
Then SVG on top. SVG is so complex that different implementations of SVG disagree how an image should look. Especially if that image contains
<text>, as it depends on the system. What some of you probably learned by now is that if you want to have a redistributable SVG file, you have to have your authoring file in Inkscape, Illustrator, Batik, ... and you have your final file which has all texts converted to paths. Otherwise, the file will look very different on each machine. An example:
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="150" viewBox="0 0 400 150"> <rect x="50" y="50" width="300" height="50" fill="red" /> <text x="50" y="100" style="font-family: monospace; font-size: 50;">Hello</text> </svg>
How will this look in different browsers? Let's test!
|Windows 10, Edge||Void Linux, Chrome||Void Linux, Firefox|
🤔 That didn't go as expected. I thought that at least both files on my Linux machine look the same, but it seems like Firefox doesn't like the
font-size specification, while Chrome and Edge do.
And there are more and more edge cases which make implementation SVG too complex. Files can be pulled in from external sources via
xlink, file contents could be built by complex scripts, and so on. But even simpler files might break.
#FF000080 isn't a valid SVG color, but it's a valid HTML color. Chrome, Firefox and Edge show us a half-transparent red, while Inkscape and QtSvg show a black square.
All of this stuff made me realize: XML is meant to be an authoring file for vector graphics like
psd which contains not only the final graphic information, but also how that graphic is constructed piece by piece.
What we really need is a format like PNG for vector graphics. Compact, versatile and simple to implement. What most of us don't need are vector graphic animations or vector graphic applications. What we definitly don't need is a vector graphic format that can do raw sockets.
After the reasearch I did to implement SVG in Zig, I was disappointed and angry that stuff like vector graphics is so complex and in my stubbornness I decided:
I created a new format called Tiny Vector Graphics or short TinyVG that tries to have 95% of the features we actually need from SVG (so shapes, colors, gradients, scalability) but not have all the things we typically don't need (animations, scripting, external resources, hierarchical document structure, ...).
So before implementing a new graphics format, I looked at the things we have today:
- SVG is abundant. It is used nearly everywhere in Web and UI. Complexity is over the top.
- HVIF seems promising, but the lack of documentation is shying me away
- DXF is mostly used in CAD, but doesn't seem to have widespread support outside of that realm.
Looking at these formats made me set my design goals:
- Compact binary encoding (Make it smaller than SVG)
- Suitable for different platforms
- GPU / Games
- CPU / Desktop Applications
- Support a reasonable subset of SVG
- Suitable for different use cases
- Application/toolbar icons
- Graphs and diagrams
- Comics, mangas, pictures and other kinds of art
- Low implementation complexity (Make it simpler than SVG)
But then Dominic Szablewski published the The Quite OK Image Format and showed me that there was interest in simpler formats. It inspired and motivated me to push for TinyVG completion and I got back to the project.
The project was already in an OK state, but I wanted to go better. I refactored pretty much everything, streamlined the design of the format and got some things right.
This work made TinyVG smaller and more flexible than before and running benchmarks with a SVG converter showed me that I was on the right path.
I got the file size down to 40% compared to optimized SVG while retaining visual fidelity:
The three images you see here are:
- (left) The original SVG file from the Papirus icon theme
- (middle) My TinyVG software renderer output
- (right) The TinyVG data rendered as SVG again
There are tiny differences in the files, but overall I'm very happy with the conversion process.
With this day, I release the first draft of the specification into the wild and open for review.
With the reference implementation is done and the first draft of the specification written, you can already go and try TinyVG!
On the website, you can find the following things:
- The current specification
- A description of the text format
- Example files
- Tooling to work with TinyVG, including a offline renderer and conversion tools.
- A SDK containing a native library, a zig package and a polyfill
- The polyfill demonstration
- Benchmark Results including comparison renderings and evaluation
So let's recap if we succeeded in the goals set at the project start:
The benchmark shows that TinyVG is between 20% and 60% of an equivalent, optimized SVG file. This means the mission was a success in that regard. ✅
So the planned use cases can be covered with TinyVG. ✅
This one is easily shown: We successfully converted a huge load of SVG files in the benchmark (roughly 3500 files) without much problems.
There's both a CPU renderer implementation as well as a polyfill. This means we already have covered classic desktop applications as well as web content. There also is a Vulkan renderer implemented (not open source) for a older version of TinyVG, so GPU rendering also is covered.
But what about memory and cpu restricted environments like embedded platforms?
Let a photo speak for itself:
This goal: Achieved! ✅
Make it simpler than SVG. Well, that's an easy one. But let's compare some real numbers!
The TinyVG SDK in total has roughly 4000 lines of Zig and 2000 lines of C#. The C# portion only implements only the SVG to TinyVG conversion, utilizing the
System.Xml.XmlSerializer class for parsing XML. This means that there is low semantic checking and zero rendering involved. The Zig implementation though implements parsing the text and binary representation of TinyVG, and provides a software renderer as well. The code also includes the command line tools and native bindings.
To compare this to a SVG implementation, I've chosen libresvg, a Rust library to render SVG files. RazrFalcon claims that the whole SVG implementatio is roughly 50kLOC with all dependencies.
This shows that TinyVG can be fully implemented in a lower level language (Zig) with a low amount of code, while SVG requires substantially more code in languages with even a higher language of abstraction (C#, Rust).
Also the TinyVG spec is 14 pages long and won't grow by a lot if done more precise while the SVG 1.1 spec alone is 260 pages long, excluding the specifications for CSS, XML and ECMA-Script.
So I'd say: This is definitly a success! ✅
If you are interested in TinyVG, you have the following option:
- Drop a comment here
- Write me an email at firstname.lastname@example.org
- Join the Discord community
- Fork and contribute on GitHub
Did you have problems implementing TinyVG?
Spoiler: This section isn't really a FAQ, but I wanted to share these images anyways