memononen / nanovg

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

Gradients on android #178

Open sonicdebris opened 9 years ago

sonicdebris commented 9 years ago

I'm trying to use nanovg on android using ndk, putting together a test app to share back (really appreciated what you did here). I was able to compile and launch the renderDemo function. It basically works, but not everything. I am getting an egl surface and context in c++ from a java surface, and drawing in it using nanovg:

const EGLint configAttribs[] = { 
        EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,    
        EGL_SURFACE_TYPE,EGL_WINDOW_BIT,
        EGL_BLUE_SIZE,8, EGL_GREEN_SIZE,8, EGL_RED_SIZE,8, EGL_ALPHA_SIZE,8,
        EGL_STENCIL_SIZE,8, EGL_NONE };
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,2, EGL_NONE };
const EGLint surfaceAttribs[] = {EGL_RENDER_BUFFER,EGL_BACK_BUFFER, EGL_NONE };

EGLint numConfigs, format;
EGLConfig config;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
eglChooseConfig(display, configAttribs, &config, 1, &numConfigs);
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow* window = ANativeWindow_fromSurface(e, jsurface);
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
EGLSurface surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
eglMakeCurrent(display, surface, surface, context);
int width,height;
eglQuerySurface(display, surface, EGL_WIDTH, &width);
eglQuerySurface(display, surface, EGL_HEIGHT, &height);
glViewport(0, 0, width,height);

NVGcontext* vg = nvgCreateGLES2(NVG_ANTIALIAS | NVG_STENCIL_STROKES);
float pxRatio = 1.0;
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

nvgBeginFrame(vg, width, height, pxRatio);
renderDemo(vg, 200,200, width,height,5,0,0);
nvgEndFrame(vg);

eglSwapBuffers(display, surface);

And I get this: nanovg_android_test

I am ignoring the text and images absence for now (it's just a matter of loading the resources using android's APIs). I'm concerned about gradients, as you can see from the color wheel.

I'm totally new to opengl, so I don't know what that could be, I'm searching but maybe it's something trivial that I overlooked. Any idea/hint?

cmaughan commented 9 years ago

Try changing the fragment precision from 'mediump' to 'highp' in nanovg_gl.h... I think there's a define for this too...

memononen commented 9 years ago

The easiest way to do that is to add a line here: https://github.com/memononen/nanovg/blob/master/src/nanovg_gl.h#L433

Change:

#elif defined NANOVG_GLES2
        "#version 100\n"
        "#define NANOVG_GL2 1\n"

to

#elif defined NANOVG_GLES2
        "#version 100\n"
        "#define NANOVG_GL2 1\n"
        "precision highp float;\n"

If that does not work, remove this around line 467:

        "#ifdef GL_ES\n"
        "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"
        " precision highp float;\n"
        "#else\n"
        " precision mediump float;\n"
        "#endif\n"
        "#endif\n"
sonicdebris commented 9 years ago

tried both, but nothing, the wheel is still "segmented". I tried something simpler, substituting the call to renderDemo with a rect filled with a gradient:

NVGpaint bg = nvgLinearGradient(vg, 0,0,0, height,nvgRGBA(0,0,255,255), nvgRGBA(255,0,0,255));
nvgRect(vg,0,0,width,height);
nvgFillPaint(vg, bg);
nvgFill(vg);

with this result:

nanovg_android_gradient

this happens both with and without the modifications you suggested.

memononen commented 9 years ago

The wheel is drawn using linear gradients in multiple segments. For some reason some radial gradients work just fine. All gradients use the same code, but different parameters to achieve radial or linear gradient.

Can you try to make the gradient start at some other location, say 100,100 -> width-100, height-100? I'm just trying to get the idea what might be wrong.

sonicdebris commented 9 years ago

Yeah, I noticed that the radial gradient in the eyes looks ok. here's the result for (100,100)->(w-100,h-100): nanovg_android_gradient_100_100_w-100_h-100

I also tried ( w/2 , 100 ) -> (w/2, h-100), and I noticed that the gradient looks different switching between portrait and landscape:

nanovg_android_gradient_w2_100_w2_h-100_horiz

nanovg_android_gradient_w2_100_w2_h-100

sonicdebris commented 9 years ago

I just tried again the test app I made, after downloading a "fresh" zip of the git repo. The gradient is now working!

I will perform further tests and I'd like to find a way to publish a polished test app (time to learn how to use git I suppose...), but since this might not happen very soon here's a zip with the current state of the app: https://www.dropbox.com/s/saspblk3yxanw60/nanoVGTest_android.zip?dl=0

I tested the app with a motorola moto g (stock android 4.4), and a Lg 4X (cyanogen 4.4).

memononen commented 9 years ago

All gradients are done using box gradient, maybe choosing smaller largecould fix it? https://github.com/memononen/nanovg/blob/master/src/nanovg.c

The precision issues is quite sad... if someone has good tips how to solve it (maybe change the data), I'd like to hear about it.