This Rust library parses AND renders most of the SVG spec:
What is missing is animations. But all the features for static images are there. The author helpfully included code that compiles Rust to both static and dynamic libraries along with a C header file. Adapting the example.c
code from the c-api/examples/cairo/
folder, I was able to render SVG directly to juce::Image
like so:
#include <resvg.h>
// load SVG into internal tree representation
resvg_render_tree *tree;
resvg_init_log();
resvg_options *opt = resvg_options_create();
resvg_options_load_system_fonts(opt);
int err = resvg_parse_tree_from_file("/path/to/file.svg", opt, &tree);
resvg_options_destroy(opt);
if (err != RESVG_OK)
{
printf("Error id: %i\n", err);
abort();
}
// get dimensions, assume painting at origin
resvg_size size = resvg_get_image_size(tree);
int width = (int)size.width;
int height = (int)size.height;
int x = 0;
int y = 0;
// setup image to render to
juce::Image img (juce::Image::PixelFormat::ARGB, width, height, true);
juce::Image::BitmapData bmp (img, x, y, width, height);
// fit SVG parse tree to original, width, height, or zoom factor
resvg_fit_to fit_to = { RESVG_FIT_TO_TYPE_ORIGINAL, 0 };
// render directly into image data (no cairo surface necessary)
resvg_render(tree, fit_to, resvg_transform_identity(), width, height, (char*)bmp.data);
resvg_tree_destroy(tree);
// RGBA -> BGRA
for (int i = 0; i < width * height * 4; i += 4)
{
unsigned char r = bmp.data[i + 0];
bmp.data[i + 0] = bmp.data[i + 2];
bmp.data[i + 2] = r;
}
g.drawImageAt(img, x, y);
(Of course, in your component class you’ll probably want to keep the parse tree as a member variable and free it in the destructor.)
The downside to this approach is an extra ~9 MB to final executable size. And since it is in C there is a tiny bit of memory management to handle. Also, it isn’t “JUCE native” in the sense of converting to paths and fills.
But overall this is the simplest, easiest solution I have found for getting SVG effects into my JUCE project, and to link everything up it only took an extra 3 lines in CMake. I hope this is helpful! Took a bit of trial and error to figure this out, and I didn’t find any satisfactory solutions in the forums here or in the Audio Programmer Discord. The next step may be to wrap this library into a more user-friendly C++ API.
Here is an example render from SVG filter effects - Wikipedia
Textures: feTurbulence, feDiffuseLighting, feDistantLight, feComposite and feBlend
Shadows: feGaussianBlur, feOffset, feColorMatrix and feBlend