Chlumsky / msdfgen

Multi-channel signed distance field generator
MIT License
3.9k stars 404 forks source link

Cannot render SVG msdf sprites #151

Closed Journeyman1337 closed 2 years ago

Journeyman1337 commented 2 years ago

I can't get SVG paths to render no matter what I try. The resulting image is always black. I am stumped to what the issue is.

Here is my code:

msdfgen::Shape shape;

if (msdfgen::loadSvgShape(shape, "blob.svg", 0))
{
    shape.normalize();
    msdfgen::Bitmap<float, 3> msdf(32 , 32);
    msdfgen::edgeColoringSimple(shape, 3.0);
    msdfgen::generateMSDF(msdf, shape, 4.0, 1.0, msdfgen::Vector2(4.0, 4.0));
    msdfgen::savePng(msdf, "output.png");
}

It doesn't work even for the example file Chumsky provided in this issue: https://github.com/Chlumsky/msdfgen/issues/13#issuecomment-224969159

Chlumsky commented 2 years ago

Probably because you're drawing an empty part. You have to set translation and scaling properly based on the actual layout of the SVG.

Journeyman1337 commented 2 years ago

Probably because you're drawing an empty part. You have to set translation and scaling properly based on the actual layout of the SVG.

What do you mean by translation and scaling?

Chlumsky commented 2 years ago

Well you are passing translation and scale as arguments to generateMSDF in what you claim is your code, so what isn't clear?

Journeyman1337 commented 2 years ago

Well you are passing translation and scale as arguments to generateMSDF in what you claim is your code, so what isn't clear?

How does the translation and scale relate to the svg?

Chlumsky commented 2 years ago

You are requesting an SDF bitmap with dimensions 32 x 32. If any coordinates in the SVG are larger than this (they are in this case), they will be outside of the rendered area, unless you set the scale correctly to make sure the SVG coordinates are transformed to fit into the output area.

Journeyman1337 commented 2 years ago

I think what I am looking for is the autoFrame command line argument but in the library api. I have to implement that myself?

Chlumsky commented 2 years ago

Yes, you either have to implement your own version, or you can adapt this code: https://github.com/Chlumsky/msdfgen/blob/3300ab6869ecda13d88774e90e97b0e1f0db4848/main.cpp#L918-L946

Journeyman1337 commented 2 years ago

Thank you I think I can figure it out now.

Journeyman1337 commented 2 years ago

For future reference in case anyone else wants to do this:

const double padding = 4;
const double output_pixel_width = 32;
const double output_pixel_height = 32;
const double pixel_range = 4;

msdfgen::Shape shape;
if (msdfgen::loadSvgShape(shape, "my_file.svg", 0))
{
    shape.normalize();
    msdfgen::edgeColoringSimple(shape, 3.0);
    msdfgen::Shape::Bounds bounds = shape.getBounds();
    double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
    if (l >= r || b >= t)
    {
        l = 0, b = 0, r = 1, t = 1;
    }
    msdfgen::Vector2 svg_dimensions(r - l, t - b);
    // pixel area not including padding
    msdfgen::Vector2 pixel_frame(output_pixel_width - (padding * 2), output_pixel_height - (padding * 2));
    // initial translation vector with pixel padding change
    const double svg_units_per_pixel_wide = svg_dimensions.x / output_pixel_width;
    const double svg_units_per_pixel_tall = svg_dimensions.y / output_pixel_height;
    msdfgen::Vector2 translate(svg_units_per_pixel_wide * padding, svg_units_per_pixel_tall * padding);
    double scale = 1.0;
    double range = 1.0;
    // fit svg unit range in pixel range while preserving aspect ratio and not cropping the svg
    if (svg_dimensions.x * pixel_frame.y < svg_dimensions.y * pixel_frame.x)
    {
        // add extra space to left and right
        translate.x += .5 * (pixel_frame.x / pixel_frame.y * svg_dimensions.y - svg_dimensions.x) - l;
        translate.y += -b;
        scale = pixel_frame.y / svg_dimensions.y;
        range = svg_units_per_pixel_wide * pixel_range;
    }
    else
    {
        // add extra space to top and bottom
        translate.x += -l;
        translate.y += .5 * (pixel_frame.y / pixel_frame.x * svg_dimensions.x - svg_dimensions.y) - b;
        scale = pixel_frame.x / svg_dimensions.x;
        range = svg_units_per_pixel_tall * pixel_range;
    }
    msdfgen::Bitmap<float, 3> msdf(output_pixel_width, output_pixel_height);
    msdfgen::generateMSDF(msdf, shape, range, scale, translate);
}