Juce SVG Demo


#1

Here’s another demo of the latest SVG offerings in Juce. It’s a simple app that lets you drag and drop an SVG file onto itself for display. It works with almost all of the SVG clipart from openclipart.org. Most SVG files work perfectly. Only a small handful display with slight flaws and only the most intricate drawings crash the app.

As in my past demos, the project belongs in the Juce\Projects\ folder. The included project file is for Dev C++. There’s also a small clipboard svg graphic in the zip.

Get it from the following link:
http://www.esnips.com/web/JuceStuff


#2

If you’ve got any SVG examples that crash it, please send them to me and I’ll fix it!

Some of the mis-drawing might be because there are radial gradient fills with an off-centre focal point - I need to add that capability to juce’s graphics class before it can render those correctly.


#3

I thought it might the simplest route to give you direct links to some of the SVGs which crash the program.

http://openclipart.org/clipart//education/school_in_fall_abiclipar_01.svg
http://openclipart.org/clipart//food/food_leif_lodahl_01.svg
http://openclipart.org/clipart//food/fruitcollection_leif_lod_07.svg

The files are rather large in size, 2 of them being over 1MB. These are the only SVGs I’ve yet found on openclipart.org which don’t work.

Not only is your implimentation of SVG severely impressive, your support is just superb. This SVG thing is important to me because I intend on using SVGs for skinning purposes since they don’t suffer from pixelation when scaling, as do bitmaps. Thanks again.


#4

Ok, that was interesting. There’s only a couple of tweaks needed to make it work.

First, I forgot to support the switch tag, so this bit needs changing:

[code] void parseSubElements (const XmlElement& xml, DrawableComposite* const parentDrawable)
{
forEachXmlChildElement (xml, e)
{
Drawable* d = 0;

        if (e->hasTagName (T("g")))
            d = parseGroupElement (*e);
        else if (e->hasTagName (T("svg")))
            d = parseSVGElement (*e);
        else if (e->hasTagName (T("path")))
            d = parsePath (*e);
        else if (e->hasTagName (T("rect")))
            d = parseRect (*e);
        else if (e->hasTagName (T("circle")))
            d = parseCircle (*e);
        else if (e->hasTagName (T("ellipse")))
            d = parseEllipse (*e);
        else if (e->hasTagName (T("line")))
            d = parseLine (*e);
        else if (e->hasTagName (T("polyline")))
            d = parsePolygon (*e, true);
        else if (e->hasTagName (T("polygon")))
            d = parsePolygon (*e, false);
        else if (e->hasTagName (T("text")))
            d = parseText (*e);
        else if (e->hasTagName (T("switch")))
            d = parseSwitch (*e);
        else if (e->hasTagName (T("style")))
            parseCSSStyle (*e);

        parentDrawable->insertDrawable (d);
    }
}

DrawableComposite* parseSwitch (const XmlElement& xml)
{
    forEachXmlChildElement (xml, e)
    {
        if (e->hasTagName (T("g")))
            return parseGroupElement (*e);
    }

    return 0;
}[/code]

and the number parsing was a bit wrong (and very inefficient - this patch speeds things up dramatically)

[code] static bool parseNextNumber (const String& source, String& value, int& index, const bool allowUnits)
{
const tchar* const s = (const tchar*) source;

    while (CharacterFunctions::isWhitespace (s[index]) || s[index] == T(','))
        ++index;

    int start = index;

    if (CharacterFunctions::isDigit (s[index]) || s[index] == T('.') || s[index] == T('-'))
        ++index;

    while (CharacterFunctions::isDigit (s[index]) || s[index] == T('.'))
        ++index;

    if ((s[index] == T('e') || s[index] == T('E'))
         && (CharacterFunctions::isDigit (s[index + 1]) 
              || s[index + 1] == T('-')
              || s[index + 1] == T('+')))
    {
        index += 2;

        while (CharacterFunctions::isDigit (s[index]))
            ++index;
    }

    if (allowUnits)
    {
        while (CharacterFunctions::isLetter (s[index]))
            ++index;
    }

    if (index == start)
        return false;

    value = String (s + start, index - start);

    while (CharacterFunctions::isWhitespace (s[index]) || s[index] == T(','))
        ++index;

    return true;
}[/code]

Thanks - give me a shout if you find any more stuff it won’t handle!


#5

i dunno if it’s mentioned elsewhere, but i was just playing with the latest juce demo and the svg drawing stuff and the opacity slider effect seemed non-optimal to me. basically, if you have a complex svg with multiple layers like the shredder icon, any opacity but 100% doesn’t really read correctly because each layer goes transparent individually rather than the entire result being made transparent after the proper layering of fully opaque colors. with the shredder, the shredder paper is done with white lines over black ones and the black ones are also overlapping a bit themselves. once made transparent, the thing becomes a mess.

while it’s not exactly a bug, i doubt anybody really wants this behavior should they turnd down opacity with an svg object (altho i admit i haven’t actually played with the code for this stuff yet).


#6

[quote=“fathom”]i dunno if it’s mentioned elsewhere, but i was just playing with the latest juce demo and the svg drawing stuff and the opacity slider effect seemed non-optimal to me. basically, if you have a complex svg with multiple layers like the shredder icon, any opacity but 100% doesn’t really read correctly because each layer goes transparent individually rather than the entire result being made transparent after the proper layering of fully opaque colors. with the shredder, the shredder paper is done with white lines over black ones and the black ones are also overlapping a bit themselves. once made transparent, the thing becomes a mess.

while it’s not exactly a bug, i doubt anybody really wants this behavior should they turnd down opacity with an svg object (altho i admit i haven’t actually played with the code for this stuff yet).[/quote]

Yes, I know about this. The neatest way to do it properly would be to have intermediate layers that are rendered into, and then blended onto the target. Adding that ability to the graphics class is something I’d like to do eventually.

Of course in the meantime you could just render your SVG into an image and alpha-blend that, which amounts to the same thing.


#7

[quote=“jules”]Yes, I know about this. The neatest way to do it properly would be to have intermediate layers that are rendered into, and then blended onto the target. Adding that ability to the graphics class is something I’d like to do eventually.

Of course in the meantime you could just render your SVG into an image and alpha-blend that, which amounts to the same thing.[/quote]

yeah, i figured i wasn’t pointing out something you didn’t already know, but i figured it was worth a mention just in case.

altho the solution you suggested is probably a ton easier and not much more costly in terms of execution time, it seems to me that if you added some way to specifiy a layer level for a particular series of drawing commands and didn’t render everything immediately, you could probably render out complex transparencies without a temporary buffer. not sure this would make anything significantly better, but really i thought of it more as an interesting exercise.

the idea behind the layer level is to give priority to spans of higher (more foreground) layers. and of course, by delying the drawing you have an opportunity to build your complete scan conversion list before actually rendering it. altho i suppose that might break your current system where self-intersection causes holes… so i guess… um… nevermind…


#8

It’d be nice to be able to do the whole thing as a set of scanlines, but that gets tricky when you start throwing images into the mix…


#9