svg in Fl_Browser

86 views
Skip to first unread message

israel dahl

unread,
Jul 21, 2015, 10:37:42 AM7/21/15
to fltkg...@googlegroups.com
Hi I have been working on a program, and would like to use svg files as the icon for a browser.
I know there is no Fl_SVG_Image (at least in the 1.3 version I am working with).
I know from searching that I can use Cairo to implement this in some fashion.

So, my main questions are:
Has someone already tackled creating Fl_SVG_Image?
Can just simply download it, and include it and carry on much the same as Fl_PNG_Image?

Is using SVG in a browser not going to work at all?
If a browser wont work, should I use a grid of buttons with SVG icons (just an example)?

I really appreciate you guys and your awesome work here!!

MacArthur, Ian (Selex ES, UK)

unread,
Jul 21, 2015, 10:53:32 AM7/21/15
to fltkg...@googlegroups.com
> Hi I have been working on a program, and would like to use svg files as
> the icon for a browser.
> I know there is no Fl_SVG_Image (at least in the 1.3 version I am
> working with).
> I know from searching that I can use Cairo to implement this in some
> fashion.

Though if you just want to draw SVG images, pulling in all of Cairo may not be the most efficient option (Cairo is not a small library... There are surely smaller libs to render SVG images with!)

> So, my main questions are:
> Has someone already tackled creating Fl_SVG_Image?
> Can just simply download it, and include it and carry on much the same
> as Fl_PNG_Image?

I'm not aware of one, though that doesn't count for much. I have not looked...

Note that, at base, fltk doesn't care where the data comes from; really all it wants is a byte array of RGB[A] values it can blit onto the screen.

So if you have a lib that can render your SVG images into a suitably sized RGB array, then all should be well; that RGB array is all that fltk wants, to draw the image.

Cairo can certainly do that (I think I have code somewhere to do exactly that, though can not actually find it...) but I'd imagine there *must* be more compact options!


> Is using SVG in a browser not going to work at all?
> If a browser wont work, should I use a grid of buttons with SVG icons
> (just an example)?

The browser, or an array of buttons, of a tree-view; I'd imagine any of these could be used...



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

MacArthur, Ian (Selex ES, UK)

unread,
Jul 21, 2015, 11:38:27 AM7/21/15
to fltkg...@googlegroups.com

> Note that, at base, fltk doesn't care where the data comes from; really
> all it wants is a byte array of RGB[A] values it can blit onto the
> screen.
>
> So if you have a lib that can render your SVG images into a suitably
> sized RGB array, then all should be well; that RGB array is all that
> fltk wants, to draw the image.
>
> Cairo can certainly do that (I think I have code somewhere to do
> exactly that, though can not actually find it...) but I'd imagine there
> *must* be more compact options!


Had a quick look for compact SVG libs.

Turns out there are *many* SVG libs but a lot of them are large and complex, and/or have piles of dependencies...

Of the "small" ones I found, I'm quite taken with nanoSVG which I stumbled across on Github. It looks to be ideal for rendering little icon-like images.

Two things I'd say about nanosvg though:-

1) Um, I am *really* glad I didn’t write that code...

2) Blimey; it actually works!


I'll try a post a minimal fltk example using it in a little while, in case anyone cares.

israel dahl

unread,
Jul 21, 2015, 12:13:05 PM7/21/15
to fltkg...@googlegroups.com, ian.ma...@selex-es.com

I absolutely care!!

Thank you for such a quick reply!!!
I am particularly fond of SVG, since it is a plain text xml file, and can have lots of amazing programming done (such as generation on the fly, and manipulation as well)

I think the extremely helpful thing you conveyed was that FLTK doesn't care what I use as long as it is converted to a suitable RGB array.
Thank you!!  I will do some searching for some svg libs thanks

MacArthur, Ian (Selex ES, UK)

unread,
Jul 21, 2015, 12:20:19 PM7/21/15
to fltkg...@googlegroups.com
OK, coffee break hack... Here's the nanosvg worked example.

To be honest, this is really just the nanosvg "example2.c" file reworked as a fltk window.
Built and tested in the nanosvg examples folder.

Seems to Just Work, for the very limited set of SVG images I tried.

This code is far from ideal, but is probably fine as a PoC and a starting point for something better.



/************************************************************************/
// compile as:
// fltk-config --use-images --compile fl_svg.cxx
//

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Button.H>
#include <Fl/fl_draw.H>

#include <float.h>
#define NANOSVG_IMPLEMENTATION
#include "../src/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "../src/nanosvgrast.h"

Fl_Double_Window *main_win;
Fl_RGB_Image *img_3 = NULL;

/************************************************************************/
Fl_Box *box_1;

/************************************************************************/
void quit_cb(Fl_Button *, void *)
{
main_win->hide();
}

/************************************************************************/
int main(int argc, char **argv)
{
NSVGimage *svg_image = NULL;
NSVGrasterizer *rast = NULL;
unsigned char* img_data = NULL;
int width;
int height;
const int depth = 4;

fl_register_images();
Fl::get_system_colors();

// const char* filename = "../example/drawing.svg";
const char* filename = "../example/23.svg";

printf("parsing %s\n", filename);
svg_image = nsvgParseFromFile(filename, "px", 96.0f);
if (svg_image == NULL) {
printf("Could not open SVG image.\n");
return (-1);
}
width = (int)svg_image->width;
height = (int)svg_image->height;

rast = nsvgCreateRasterizer();
if (rast == NULL) {
printf("Could not init rasterizer.\n");
return (-1);
}

img_data = (unsigned char*)malloc(width * height * depth);
if (img_data == NULL) {
printf("Could not alloc image buffer.\n");
return (-1);
}

printf("rasterizing image %d x %d\n", width, height);
nsvgRasterize(rast, svg_image, 0, 0, 1, img_data, width, height, (width * depth));

main_win = new Fl_Double_Window((width + 20), (height + 20 + 50));

box_1 = new Fl_Box(10, 10, width, height);

Fl_Button *quit = new Fl_Button((main_win->w() - 70), (main_win->h() - 50), 60, 30);
quit->label("Quit");
quit->callback((Fl_Callback *)quit_cb);

main_win->end();
main_win->show();

img_3 = new Fl_RGB_Image(img_data, width, height, depth);
box_1->image(img_3);

int ret = Fl::run();

nsvgDeleteRasterizer(rast);
nsvgDelete(svg_image);

return ret;
} // main

/* End of File */

Fabien Costantini

unread,
Jul 21, 2015, 4:25:23 PM7/21/15
to fltkg...@googlegroups.com
I sense a  very straightforward possibilty of creating a useful, optional (assuming nano svg would not be incorporated in current versions) yet very convenient new widget:
    Fl_Svg_Image (probably derived from Fl_RGB_Image) ?
Note that in your example you use a box, but I think it should work fine with buttons as well ?
Excellent, compact  lib thanks for sharing !
Could really give the fltk look & feel some fresh taste again,

-Fabien


From: "MacArthur, Ian (Selex ES, UK)" <ian.ma...@selex-es.com>
To: "fltkg...@googlegroups.com" <fltkg...@googlegroups.com>
Sent: Tuesday, July 21, 2015 11:20 AM
Subject: RE: [fltk.general] svg in Fl_Browser
--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral+unsub...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


israel dahl

unread,
Jul 21, 2015, 4:52:28 PM7/21/15
to fltk.general, fab6...@shaw.ca
Hi I think nanosvg is ultimately a BIG NO.

I am just adverse to heavy usage of goto in code, (well any usage of goto makes me uneasy honestly... no offence if the FLTK codebase uses goto heavily :D I just use the API ).

I think that many other implementations could be done using something much nicer than nanosvg... So I think I might look around a bit more.

If I find something more useful than cairo or rsvg (those are the most common ones I have seen in GNU/Linux world) that is simple to use I will post it here.  But my initial searching did not come up with much that warrants posting.

Ian MacArthur

unread,
Jul 21, 2015, 5:20:44 PM7/21/15
to fltkg...@googlegroups.com
On Tue Jul 21 2015 21:25:20, 'Fabien Costantini' via fltk.general wrote:
> I sense a very straightforward possibilty of creating a useful, optional (assuming nano svg would not be incorporated in current versions) yet very convenient new widget:
> Fl_Svg_Image (probably derived from Fl_RGB_Image) ?


It looks like NanoSVG is licensed under a zlib-style licence, so may well be usable.
It’s also pretty small, about 2000 lines for the parser, and about 1100 for the rasterizer, all contained in just two files.
I don’t think it is a truly comprehensive SVG parser of course, but seems pretty good for what it is, and *considerably* smaller than Cairo.


> Note that in your example you use a box, but I think it should work fine with buttons as well ?

Sure, any fltk widget that you can set the image() for should work just the same way; though if you want fancy resizing behaviour you’d likely have to derive a widget and have the draw() method re-run the rasterizer when the widget is resized or etc...

> Excellent, compact lib thanks for sharing !

Just stumbled on it by accident, but I’m genuinely impressed with how well it does what it does.

> Could really give the fltk look & feel some fresh taste again,

And lighter than Cairo.

I have no view on how fast it is; but then Cairo isn’t all that quick anyway, so...






Ian MacArthur

unread,
Jul 21, 2015, 5:46:55 PM7/21/15
to fltkg...@googlegroups.com
On Tue Jul 21 2015 21:52:28, israel dahl wrote:

> Hi I think nanosvg is ultimately a BIG NO.


OK; though I’m curious as to why - based on what you said you need to do, nanosvg looks almost perfect for the job.

I’d be surprised if you find anything much more compact, for example.

>
> I am just adverse to heavy usage of goto in code, (well any usage of goto makes me uneasy honestly... no offence if the FLTK codebase uses goto heavily :D I just use the API ).

Not sure I’m following your reasoning here - if you are only using the API, then you likely don’t care what happens under the covers, so long as it works.
Yes fltk has goto’s in it.
Most libs do, once you dig in to the meat...

I’m not sure I’d describe 8 uses of goto in 3500 lines of C code as “heavy usage” either.
It’s straight C, and the gotos are all being used to trap error conditions, and a quick visual inspection makes it quite clear there’s nothing heinous going on there. In the absence of exceptions and so forth it is a fairly commonplace way to deal with error conditions in C.
It may not be a style you like, but it’s pretty good code nonetheless.
It’s actually pretty well structured.

I dunno, maybe you are worried that Uncle Edsger will be annoyed at us?

If so, the Dijkstra quote I favour in this case is:-

"Please don't fall into the trap of believing that I am terribly dogmatic about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a simple trick, by a simple form of coding discipline!”

Dijkstra (1973) in personal communication to Donald Knuth, quoted in Knuth's "Structured Programming with go to Statements".


Though, I guess it’s only fair to also quote:-

"I mean, if 10 years from now, when you are doing something quick and dirty, you suddenly visualize that I am looking over your shoulders and say to yourself "Dijkstra would not have liked this", well, that would be enough immortality for me.” Dijkstra (1995)



> I think that many other implementations could be done using something much nicer than nanosvg... So I think I might look around a bit more.
>
> If I find something more useful than cairo or rsvg (those are the most common ones I have seen in GNU/Linux world) that is simple to use I will post it here. But my initial searching did not come up with much that warrants posting.

Note that librsvg is basically Cairo anyway (it uses Cairo for it’s back end rendering now.) Cairo itself has a lot of dependencies, though *if* you are on a Linux box it is probable they are already in place.

Using Cairo on OSX or Win32 is less fun... And you probably need to static link it if you are going to distribute your exe’s, as it is very likely that the users will not have the necessary DLL's / SO's / dylib’s in place either.
Been there. Done that. Bloody awful.





Greg Ercolano

unread,
Jul 21, 2015, 5:57:31 PM7/21/15
to fltkg...@googlegroups.com
On 07/21/15 13:52, israel dahl wrote:
> I am just adverse to heavy usage of goto in code [..]

Then don't look at the jpeg, png, and zlib libraries ;)

$ grep goto png/*.c | wc
20 100 974
$ grep goto jpeg/*.c | wc
38 222 1848
$ grep goto zlib/*.c | wc
26 170 1568
$ grep goto src/*.cxx | wc
51 329 3411



Fabien Costantini

unread,
Jul 21, 2015, 6:03:40 PM7/21/15
to fltkg...@googlegroups.com
Exactly Ian, that immediately rang the same bell on me, compact & fast, isn't it at the image our our toolkit (as opposed maybe to cairo) ?
Cairo deals with much more (i.e. the surfaces in different renderers and nice path and otehr 2D API if i remember well) but only this svg capability could be huge in our toolkit and after all, we already have decent 2D API (albeit not as fancy in terms of rendering).

@Israel:
Now to answer the goto thing, yes it is usually evil to use them, but I would not care too much if i don't need to maintain this lib  and more importantly:
Just to be the devil advocate for one second, I would say whenever where try/catch/finally exceptions handling is prohibited
 (for many reasons, like compatibility with older compiler platforms that 1.x like to support and also performance penalty simply as it was sought for FLTK) , 
then goto is the only 'clean' way to deal with error handling code that needs to release resources with no code duplication.
Think about it and tell me if it wouldn't be worse to duplicate the error handling post processing code that releases the resources already allocated (you don't want memory leaks right ?) ;-)
BTW, if you are interested how code  is generated a the assembly level, goto's are exactly what try/catch/finally generates in assembly code.

That said, I did not look at the details of the code yet and it might very well be that it is a bit loose!

Kind Regards,
-Fabien


From: Ian MacArthur <imaca...@gmail.com>
To: fltkg...@googlegroups.com
Sent: Tuesday, July 21, 2015 4:47 PM
Subject: Re: [fltk.general] svg in Fl_Browser

israel dahl

unread,
Jul 21, 2015, 10:11:24 PM7/21/15
to fltk.general, fab6...@shaw.ca
Ok, you all convinced me :)
I just have religiously avoided goto, as I use C++ and generally call a function where these goto statements call a goto error.
You are right though looking through it rather than just glancing.
I will give it a whirl, and I will not complain if this is included in a later FLTK version.
It would be really nice to have a premade Fl_SVG_Image, and if it is small fast and useful my objections to my style preferences have been easily pushed aside by all your rational arguments.

Also, I am on Linux (*buntu/Fedora/Debian/Puppy mostly) so librsvg and cairo are installed (actually I built JWM with svg support using cairo, so it would not be anything extra for what I am using it for).

Thanks everyone for the help and extremely nice education on goto :)

MacArthur, Ian (Selex ES, UK)

unread,
Jul 22, 2015, 10:29:55 AM7/22/15
to fltkg...@googlegroups.com
Got carried away, wrote a basic SVG viewer.
Should be able to pass it multiple files, and it should deal with resizing "on the fly" and stuff. Seems to work.

Longer than I'd intended (about 250 lines), but I thought I'd post it anyway in case others wanted to improve upon it...


/************************************************************************/
// compile as:
// fltk-config --use-images --compile fl_svg.cxx
// Needs to have the NanoSVG headers available.
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <float.h>

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Group.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_RGB_Image.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Output.H>
#include <FL/fl_draw.H>

#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of colour keywords
#define NANOSVG_IMPLEMENTATION // Implement NanoSVG implementation
#include "../src/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "../src/nanosvgrast.h"

// FLTK assets we need
static Fl_Double_Window *main_win = NULL;
static Fl_RGB_Image *img_3 = NULL;
static Fl_Output *stat_bar = NULL;

// NanoSVG assets
static NSVGimage *svg_image = NULL;
static NSVGrasterizer *raster_engine = NULL;

// Raster image data buffer
static unsigned char *img_data = NULL;
static const int depth = 4; // Assume always RGBA for now

// Hack to read multiple files from the command line...
static int first_arg = 0;
static int arg_count = 0;
static char **arg_list;
static int next_arg = 0;

static int view_width;
static int view_height;

static void rasterize_image(void);

/************************************************************************/
class vw_box : public Fl_Box
{
public:
vw_box(int X, int Y, int W, int H, const char *L=0) :
Fl_Box(X,Y,W,H,L),
wo(W),
ho(H) {}

// If the box is resized, we may need to re-rasterize the image to fit
void resize(int x, int y, int w, int h)
{
Fl_Box::resize(x,y,w,h);
if ((wo != w) || (ho != h)) // box has changed
{
if (svg_image) rasterize_image(); // trigger a resizing of the raster image
wo = w;
ho = h;
}
}
private:
int wo, ho;
};

static vw_box *box_1 = NULL;

/************************************************************************/
static void reload_svg(const char *filename)
{
// Release any previous image
if (svg_image) nsvgDelete(svg_image);
svg_image = nsvgParseFromFile(filename, "px", 96.0f);
} // reload_svg

/************************************************************************/
static void rasterize_image(void)
{
// determine the "natural" size of the SVG image
int svg_width = (int)svg_image->width;
int svg_height = (int)svg_image->height;

// Do we scale the image to fit our window?
// Specifically: Do we need to shrink the SVG image to fit the view?
int img_width = view_width = box_1->w();
int img_height = view_height = box_1->h();

double w_scale = (double)view_width / (double)svg_width;
double h_scale = (double)view_height / (double)svg_height;
double i_scale;

if ((w_scale >= 1.0) && (h_scale >= 1.0))
{
// image fits entirely inside the view
img_width = svg_width;
img_height = svg_height;
i_scale = 1.0;
}
else if (w_scale >= h_scale) // H as the dominant axis
{
i_scale = h_scale;
img_height = view_height;
img_width = (int)(svg_width * i_scale);
}
else // W as the dominant axis
{
i_scale = w_scale;
img_width = view_width;
img_height = (int)(svg_height * i_scale);
}

// allocate the image data buffer at a suitable size
if (img_data) free (img_data); // discard any previous image data
img_data = (unsigned char*)malloc(view_width * view_height * depth);
if (img_data == NULL)
{
fprintf(stderr, "Could not allocate view image buffer.\n");
fflush(stderr);
return;
}

// Now rasterize the SVG image at the size we want
nsvgRasterize(raster_engine, svg_image, // image to rasterize
0, 0, // x,y offset
(float)i_scale, // scale factor
img_data, // image data array to fill
img_width, img_height, // image size
(img_width * depth)); // image line stride

// assign the image data to the view box
if (img_3) delete img_3; // release any previous FL_RGB_Image we had
img_3 = new Fl_RGB_Image(img_data, img_width, img_height, depth);
box_1->image(img_3);

// fill in the status bar
static char stat_msg[256];
if ((svg_width == 0) && (svg_height == 0))
{
snprintf(stat_msg, 256, "Input file has no SVG image I can read");
}
else
{
snprintf(stat_msg, 256, "SVG %dx%d, IMG %dx%d, Scale %.3f", svg_width, svg_height,
img_width, img_height,
i_scale);
}
stat_bar->value(stat_msg);
main_win->redraw();
} // rasterize_image

/************************************************************************/
static void next_cb(Fl_Button *btn, void *)
{
if (next_arg >= arg_count) // time to quit, run out of files to view
{
if (svg_image) nsvgDelete(svg_image);
svg_image = NULL;
if (main_win) main_win->hide();
}
else
{
// load the SVG vector image data
reload_svg(arg_list[next_arg]);
++next_arg;

// update the button label accordingly
if (next_arg < arg_count)
{
btn->label("Next");
}
else
{
btn->label("Quit");
}

if (!svg_image)
{
if (main_win) main_win->hide();
return;
}
if (svg_image) rasterize_image();
}
} // next_cb

/************************************************************************/
int main(int argc, char **argv)
{
// Parse the command line arguments
first_arg = 0;
Fl::args(argc, argv, first_arg);
arg_count = argc;
arg_list = argv;
next_arg = first_arg;

// Initialize the SVG rasterizer engine
raster_engine = nsvgCreateRasterizer();
if (raster_engine == NULL)
{
fprintf(stderr, "Could not initialize the SVG rasterizer.\n");
fflush(stderr);
return (-1);
}

// Set a size for our view window
view_width = 512;
view_height = 512;

// create the fltk elements
main_win = new Fl_Double_Window((view_width + 20), (view_height + 20 + 40));

box_1 = new vw_box(10, 10, view_width, view_height);
box_1->box(FL_BORDER_BOX);

Fl_Group *stat_grp = new Fl_Group(1, (view_height + 11), (view_width + 18), 38);
stat_grp->begin();

stat_bar = new Fl_Output(10, (main_win->h() - 40), (main_win->w() - 90), 30);
stat_bar->clear_visible_focus();

Fl_Button *btn = new Fl_Button((main_win->w() - 70), (main_win->h() - 40), 60, 30, "Quit");
btn->callback((Fl_Callback *)next_cb);

stat_grp->end();
stat_grp->resizable(stat_bar);

main_win->end();
main_win->resizable(box_1);

// trigger loading of the first image
next_cb(btn, NULL);

// show the window
main_win->show(argc, argv);
int ret = Fl::run();

// clean-up the image resources
if (img_data) free (img_data);
if (raster_engine) nsvgDeleteRasterizer(raster_engine);
if (svg_image) nsvgDelete(svg_image);

Manfred Herr

unread,
Jul 24, 2015, 12:09:36 PM7/24/15
to fltk.general, ian.ma...@selex-es.com
Couldn't resist trying. Sorry for damping your excitement. In 7 little SVG files I found 2 giving strange results. See attachments.
yinyang.svg
matrix.svg

Ian MacArthur

unread,
Jul 24, 2015, 5:31:54 PM7/24/15
to fltkg...@googlegroups.com
On Fri Jul 24 2015 17:09:36, Manfred Herr wrote:
> Couldn't resist trying. Sorry for damping your excitement. In 7 little SVG files I found 2 giving strange results. See attachments.

Yup; definitely broken - look fine in another viewer though.

Probably worthwhile passing a bug report to the nanoSVG project, along with your files that exhibit the issues.

Still think that nanoSVG is good for what it is; it’s certainly fine for little svg icons files and so forth, for example; just so long as they don’t use any svg features it doesn’t support!




Reply all
Reply to author
Forward
0 new messages