SVG rendering, dashed strokes


#1

I'm just trying out some SVG stuff and I have found that I can't render any strokes that are not continuous, can JUCE deal with different stroke types? In Inkscape I have created a plain svg which looks like dashStroke.png, yet is renders as a solid line(see renderedStroke.png). I couldn't attach the .svg file so I've included it inline.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   version="1.1"
   width="150"
   height="150"
   id="svg2">
  <defs
     id="defs4">
    <linearGradient
       id="linearGradient3893">
      <stop
         id="stop3895"
         style="stop-color:#009500;stop-opacity:1"
         offset="0" />
      <stop
         id="stop3897"
         style="stop-color:#000000;stop-opacity:0"
         offset="1" />
    </linearGradient>
  </defs>
  <metadata
     id="metadata7">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <path
     d="m 634.23883,673.15393 a 389.26828,379.20099 0 1 1 -778.53656,0 389.26828,379.20099 0 1 1 778.53656,0 z"
     transform="matrix(0.14343489,0,0,0.14724289,39.862678,-24.117129)"
     id="path3787"
     style="fill:#00ff00;fill-opacity:1;stroke:#000000;stroke-width:71.08824158;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:142.17648142, 71.08824068;stroke-dashoffset:142.17648142" />
</svg>

Any ideas?


#2

Sorry - that's a part of the SVG spec I didn't implement yet.


#3

Am I right to assume I should look at SVGState::getStrokeFor(), or will I end up chasing pink elephents?


#4

yes.. but the PathStrokeType class doesn't contain info about dashes, you'd need to deal with that separately, and call a different method to create the stroke.


#5

Hi Jules. I took a look at this. As you can see from the screenshot something is not quite right! Is it Ok to create the dashed path in the parsePath() method? As you can see I'm losing the solid fill, and the width of the dashed lines are too large. Can you spot any obvious problems?

 

    Drawable* parsePath (const XmlPath& xml) const
    {
        Path path;    
        parsePathString (path, xml->getStringAttribute ("d"));

        if (getStyleAttribute (xml, "fill-rule").trim().equalsIgnoreCase ("evenodd"))
            path.setUsingNonZeroWinding (false);

        //create dashed lines if needed
        const String strokeDashStyle (getStyleAttribute (xml, "stroke-dasharray"));
        const float strokeWidth (getStyleAttribute(xml, "stroke-width").getFloatValue());
        Array<float> dashes;
        StringArray dashArray;

        if(!strokeDashStyle.equalsIgnoreCase("none")){
            dashArray.addTokens(strokeDashStyle, ",", "");
            for(int i=0;i<dashArray.size();i++){
                dashes.add(dashArray[i].getFloatValue());
            }                    
            PathStrokeType (strokeWidth).createDashedStroke (path, path, dashes.getRawDataPointer(), dashArray.size());
        }

        return parseShape (xml, path);
    }

#6

Well no.. that's not going to work. Having a very quick look, it'd probably require additions to the DrawableShape class to support dashes for it to actually work correctly. At the moment that class can only have a solid outline.


#7

Thanks Jules. What I did was add an Array<float> to DrawableShape. In SVGParser::parseShape() I check for dashed strokes. If they exists I parse the data and set the float array in DrawableShape. Then in the DrawableShape::Paint() method I do this:

void DrawableShape::strokeChanged()
{
    strokePath.clear();
    strokeType.createStrokedPath(strokePath, path, AffineTransform::identity, 4.0f);
    setBoundsToEnclose (getDrawableBounds());
 
//if stroke contains dashes..
    if(dashArray.size()>0)
       strokeType.createDashedStroke (strokePath, path, dashArray.getRawDataPointer(), dashArray.size());
    repaint();
}

As you can see from the screenshot it seems to work Ok, but I'm sure you can think of more elegant solutions that mine.


#8

I've also need dashes support for the SVGs I get from my graphic designer.

* Here's my attempt at adding a dashLengths property to DrawableShape: https://github.com/soundradix/JUCE/commit/fd9fe5207a6ae4a097dc27575252bec698371cc1

* Here's the parsing of dashes from SVG: https://github.com/soundradix/JUCE/commit/2d1e2a603f25f40aa67123cf5b759734e9c6dadd