nuklear
nuklear copied to clipboard
Support for subpixel correct drawing ?
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.
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.
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.
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)
.
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: https://github.com/vurtun/nuklear/blob/8f5c1bef9c5fd844b584c00953c64ae238a06f2d/src/nuklear_draw.c#L390-L391
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.
@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.
Sure, I've tried some PoC. Please see the screenshot below:
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]
.
https://github.com/vurtun/nuklear/blob/181cfd86c47ae83eceabaf4e640587b844e613b6/src/nuklear.h#L4234-L4240
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];
}
}
My changes are on top of version 509c75b. The backend is OpenGL 3 (GLFW+glbinding).
@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 toshort
, but allow user to override withfloat
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 :wink:.