Skip to content
This repository has been archived by the owner on Dec 2, 2019. It is now read-only.

Support for subpixel correct drawing ? #425

Open
LennertSchneider opened this issue May 3, 2017 · 7 comments
Open

Support for subpixel correct drawing ? #425

LennertSchneider opened this issue May 3, 2017 · 7 comments

Comments

@LennertSchneider
Copy link

Hi,

I'm trying to render a waveform using nk_stroke_line, but it looks quite horrible, because all the float precision in the supplied coords is lost due to internal conversion to short.
I have the same problem with some other custom controls where I want to use lines and circles to construct them.
Is there a reason this has been done ? Is this really necessary to abandon the float precision internally ?

Cheers.

@vurtun
Copy link
Owner

vurtun commented May 3, 2017

Before I start converting draw commands internal position type from short to float could you test something for me. nk_convert_config has parameters:

struct nk_convert_config {
    /* ... */
    enum nk_anti_aliasing line_AA;
    unsigned circle_segment_count;
    unsigned arc_segment_count; 
    unsigned curve_segment_count;
    /* ... */
};

First could try turning line_AA anti-aliasing on and off. If that does not help please try to set higher values for each of the segment counts. These basically dictate out of how many triangles shapes/lines generated out of. Also for any curved shapes or lines I would highly recommend overriding NK_SIN and NK_COS to standard library functions sin and cos.

If your problem still persist then I will change internal command types from short to float. As for the reason why they are short. This library started as an UI for X11 which only uses short. So at that time it made sense and took less memory. Especially since I at that time did not know how much memory would, could and should be consumed.

@LennertSchneider
Copy link
Author

I think I might not have been clear with my statement. The problem is not in using the predefined circle/arc/curve-primitives, but with doing my own out of multiple lines. I first stumbled across this problem when trying to draw an arc without the begin/and strokes from the center (really JUST the arc). Since there was no primitive for this I tried to draw a bunch of lines whose coords I calc myself (using the precise stdlib sin/cos). However the resulting arc looks horrorbly deformed due to each start and endpoint of each line segment snapping to fill pixels, because of the internal short conversion.

@itsuhane
Copy link

Sorry to bring this issue up again...I'm also facing a similar situation where I would like to have some custom widget displaying a function plot.

Below is a screenshot of my attempt drawing sin(x).
image
Like @LennertSchneider did, I sampled the function with different x, then plot the curve with nk_stroke_polyline.

Internally, Nuklear does float-to-short conversion:

nuklear/src/nuklear_draw.c

Lines 390 to 391 in 8f5c1be

cmd->points[i].x = (short)points[i*2];
cmd->points[i].y = (short)points[i*2+1];

The conversion results in heavily aliased curve, as shown in my screenshot.

As a proposal, there could be a global macro like NK_VERTEX_VALUE_TYPE, which defaults to short, but allow user to override with float to support subpixel drawing.
Current draw routines will keep the casting to make sure they don't broke any pixel-perfect drawing for UI elements. Meanwhile, they will have subpixel counterparts, nk_stroke_polyline_subpixel for example, for any user who want advanced control on their drawing.

@dumblob
Copy link
Contributor

dumblob commented Jun 14, 2019

@LennertSchneider @itsuhane could you maybe first try disabling the conversion from float to short in nuklear.h and post your results here (i.e. pictures and the backends you've used)? I'm asking as I don't expect it to be much better as at the end of the day the same color will be used for all affected pixels, so I'm afraid you might anyway need some more advanced form of anti aliasing.

@itsuhane
Copy link

Sure, I've tried some PoC. Please see the screenshot below:

image

Unfortunately, most of the commands are using short type internally, simply removing the conversion will only introduce compile warnings (narrowing from float to short).

To make the float version in the screenshot work, I had to make following changes:

In the definition of struct nk_command_polyline, use struct nk_vec2 for points[1].

nuklear/src/nuklear.h

Lines 4234 to 4240 in 181cfd8

struct nk_command_polyline {
struct nk_command header;
struct nk_color color;
unsigned short line_thickness;
unsigned short point_count;
struct nk_vec2i points[1];
};

Then I had a subpixel version for nk_stroke_polyline:

NK_API void
nk_stroke_polyline_subpixel(struct nk_command_buffer *b, float *points, int point_count,
    float line_thickness, struct nk_color col)
{
    int i;
    nk_size size = 0;
    struct nk_command_polyline *cmd;

    NK_ASSERT(b);
    if (!b || col.a == 0 || line_thickness <= 0) return;
    size = sizeof(*cmd) + sizeof(float) * 2 * (nk_size)point_count; // has to be sizeof(float)
    cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size);
    if (!cmd) return;
    cmd->color = col;
    cmd->point_count = (unsigned short)point_count;
    cmd->line_thickness = line_thickness;
    for (i = 0; i < point_count; ++i) {
        cmd->points[i].x = points[i*2]; // no type-casting
        cmd->points[i].y = points[i*2+1];
    }
}

@itsuhane
Copy link

itsuhane commented Jun 14, 2019

My changes are on top of version 509c75b.
The backend is OpenGL 3 (GLFW+glbinding).

@dumblob
Copy link
Contributor

dumblob commented Jun 14, 2019

@itsuhane thank you for the reaction. Now I'm thinking again about your proposal:

As a proposal, there could be a global macro like NK_VERTEX_VALUE_TYPE, which defaults to short, but allow user to override with float to support subpixel drawing.
Current draw routines will keep the casting to make sure they don't broke any pixel-perfect drawing for UI elements. Meanwhile, they will have subpixel counterparts, nk_stroke_polyline_subpixel for example, for any user who want advanced control on their drawing.

As that actually seems to be the way to go. Make a pull request and I'll merge it 😉.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants