memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.08k stars 767 forks source link

Java2D, SVG shapes to NVG #403

Open MikOfClassX opened 6 years ago

MikOfClassX commented 6 years ago

Hello and thanks for NVG. I'm testing the LWJGL version and it's working quite well. I tried to write a small piece of code in order to convert a generic Java2D shape to a NVG path.

The problem (you guessed it) is the holes. Imagine to rasterize the "O" char. This also means that we will have the same problems with SVG shapes etc. Any hint ?

Mik

lieff commented 6 years ago

Did you try change nvgPathWinding between shapes when render "O" char?

olliwang commented 6 years ago

The holes should not be a problem. Check my project that converts SVG to NVG codes. https://github.com/ollix/svg2nvg

And this line deals with the hole: https://github.com/ollix/svg2nvg/blob/fd7203/svg2nvg/generator.py#L205

MikOfClassX commented 6 years ago

Lieff, Olliwang (svg2nvg is impressive), thanks for the replies.

nvgPathWinding() does the job, but my question is about general path rasterizing of intersecting paths i.e. the paths that define a generic glyph. I think nvidia-path-rendering is supporting this feature.

MikOfClassX commented 6 years ago

A little addendum: we could determine the polygon winding every nvgClosePath() and issue a nvgPathWinding() accordingly. See http://blog.element84.com/polygon-winding.html This may fix the problem. What about adding this feature to nvg ?

lieff commented 6 years ago

@MikOfClassX Hmm you are sure per-path nvgPathWinding not working? In any case you can simply reverse path points (nanovg does same in nvg__polyReverse).

MikOfClassX commented 6 years ago

@lieff for sure pathwinding is working. But NVG may auto-detect path winding of each path section (ie. caching the points until CLOSE_PATH is reached, determining and setting the path winding, submitting the points to NVG as usual, going ahead to the end of the path points). There's no need to reverse path points at api level. This will allow NVG to correctly render any generic path (i.e. a glyph ouline, etc) automatically.

lieff commented 6 years ago

It's impossible to autodetect pathwinding. There now way to know what application wants: draw all intersecting rects filled, or with holes at intersecting position. It's settable shape attribute (fill-rule="nonzero"/"evenodd" in svg).

memononen commented 6 years ago

The reason why the paths are always set CCW is that it simplifies the programmers job when doing visualisations using the API. You can specifically tell your intent, whether you're drawing a hole or not, and allows to combine solid shapes too. The pathwinding API allows to use the build in shapes as holes too *.

I think the readme has a mistake, it's using non-zero filling rule, not even-odd. Maybe it used to be different.

True type is using non-zero fill rule too, which means that you can determine the winding of a path before hand and pass it to NanoVG. Not all SVGs can be rendered correctly because of the fill rule.

Even-odd fill rule should be doable, it should be just matter of setting the stencil operation differently. I cannot remember why it is not in the API.

*) I think I got the API a bit wrong, it might make more sense to have it as draw state, like the fill color, not as a path command like it is now.

MikOfClassX commented 6 years ago

thanks @memononen . That's clarifying the whole lot. I'm now going through my signed-area code and I'll let you know. Are you going to improve NVG ?

As a side project I'm also investigating NVPathRendering. Apart from being NVidia-centric (problem), it's really fully featured and allows me to achieve arbitrary shape clipping (by stencils) and use the current GL states to do some other cool stuff.

MikOfClassX commented 6 years ago

@memononen I did it! using areas for CCW detection and some nice code (Java, using LWJGL). I wrote a little utility called Shape2NVG. It converts any Java2D shape into a NVG path. The conversion is very robust and I can easily render any shape, including complex glyphs. clipboard02

MikOfClassX commented 6 years ago

Now my suggestion for an api improvement is to have something like:

// batch a shape in one single call nvgPath(int codes, float vertices, int * winding);

codes: the shape segment type codes [NVG_MOVE_TO, NVG_LINE_TO, .... NVG_CLOSE] vertices: the vertices array {(x,y),... } winding: the winding array. one element for every NV_CLOSE [SOLID || HOLE ]

Just my 2cents..

MikOfClassX commented 6 years ago

my code here:

http://forum.lwjgl.org/index.php?topic=6594.0

AndreyArsov commented 6 years ago

I am hitting a problem with the approach above in the case when the font has glyphs (say "O") whose outside path CW and inside CCW. In this case the outside shape is marked as hole and the rendering is not correct. The rendered shape is thicker by a pixel and has artifacts around the edges sometimes. I guess this is expected, based on the nvg's operation, since it is not given the right information.

Looking for suggestions on how that can be fixed. In such a case the glyph paths have to be reversed, but I am not sure how this can be detected in the general case.

lieff commented 6 years ago

@AndreyArsov on what filling rule this shapes rely in correct case? It may be correct case and big hole must completely hide smaller filled shape. About artifacts: try disable anti-aliasing, with it, I'm getting artifacts too, especially when shapes scaled.

MikOfClassX commented 6 years ago

@AndreyArsov can you post your java test code ? My SGLNanoVGUtil is working as expected for me. There may be some unexpected issues however. Just create a test case and I'll give it a look.

AndreyArsov commented 6 years ago

here it is attached below. I have added some path processing to the example code and it is in C. There is a pathstrO that has both shapes of the O glyph reversed. I can swap them, but this is how it is generated from the font. The outside shape is wound CW, and the inside CCW. Hope you can compile it, for me it does on VS2013/2015.

@lieff, yes disabling antialiasing should work, but the paths look not so good.

example_gl2.zip

MikOfClassX commented 6 years ago

Cool code. Are you sure the path string is correctly rendered with another rasterizer (i.e. NVPathRendering) ?

AndreyArsov commented 6 years ago

I am not familiar with NVPathRendering, but at the moment am trying to render the same with Skia. Not as fun though trying to get skia working. Will report later.

@MikOfClassX, do you actually see the issue?

lieff commented 6 years ago

Here small sample of NVPathRendering https://github.com/lieff/lvg/blob/master/render/render_nvpr.c , Skia uses it internally too. It's fastest way on Nvidia cards.

AndreyArsov commented 6 years ago

I don't have nvidia card at the moment, and it looks like this code uses driver specific gl extension. On skia the paths render though, but maybe using the even-odd fill rule, will verify further.