1 | // |
2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org |
3 | // |
4 | // This software is provided 'as-is', without any express or implied |
5 | // warranty. In no event will the authors be held liable for any damages |
6 | // arising from the use of this software. |
7 | // Permission is granted to anyone to use this software for any purpose, |
8 | // including commercial applications, and to alter it and redistribute it |
9 | // freely, subject to the following restrictions: |
10 | // 1. The origin of this software must not be misrepresented; you must not |
11 | // claim that you wrote the original software. If you use this software |
12 | // in a product, an acknowledgment in the product documentation would be |
13 | // appreciated but is not required. |
14 | // 2. Altered source versions must be plainly marked as such, and must not be |
15 | // misrepresented as being the original software. |
16 | // 3. This notice may not be removed or altered from any source distribution. |
17 | // |
18 | #ifndef NANOVG_GL_H |
19 | #define NANOVG_GL_H |
20 | |
21 | #ifdef __cplusplus |
22 | extern "C" { |
23 | #endif |
24 | |
25 | // Create flags |
26 | |
27 | enum NVGcreateFlags { |
28 | // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). |
29 | NVG_ANTIALIAS = 1<<0, |
30 | // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little |
31 | // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. |
32 | NVG_STENCIL_STROKES = 1<<1, |
33 | // Flag indicating that additional debug checks are done. |
34 | NVG_DEBUG = 1<<2, |
35 | }; |
36 | |
37 | #if defined NANOVG_GL2_IMPLEMENTATION |
38 | # define NANOVG_GL2 1 |
39 | # define NANOVG_GL_IMPLEMENTATION 1 |
40 | #elif defined NANOVG_GL3_IMPLEMENTATION |
41 | # define NANOVG_GL3 1 |
42 | # define NANOVG_GL_IMPLEMENTATION 1 |
43 | #if !defined(NANOVG_GL_NO_UNIFORMBUFFER) |
44 | # define NANOVG_GL_USE_UNIFORMBUFFER 1 |
45 | #endif |
46 | #elif defined NANOVG_GLES2_IMPLEMENTATION |
47 | # define NANOVG_GLES2 1 |
48 | # define NANOVG_GL_IMPLEMENTATION 1 |
49 | #elif defined NANOVG_GLES3_IMPLEMENTATION |
50 | # define NANOVG_GLES3 1 |
51 | # define NANOVG_GL_IMPLEMENTATION 1 |
52 | #endif |
53 | |
54 | #define NANOVG_GL_USE_STATE_FILTER (1) |
55 | |
56 | // Creates NanoVG contexts for different OpenGL (ES) versions. |
57 | // Flags should be combination of the create flags above. |
58 | |
59 | #if defined NANOVG_GL2 |
60 | |
61 | NVGcontext* nvgCreateGL2(int flags); |
62 | void nvgDeleteGL2(NVGcontext* ctx); |
63 | |
64 | int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); |
65 | GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); |
66 | |
67 | #endif |
68 | |
69 | #if defined NANOVG_GL3 |
70 | |
71 | NVGcontext* nvgCreateGL3(int flags); |
72 | void nvgDeleteGL3(NVGcontext* ctx); |
73 | |
74 | int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); |
75 | GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); |
76 | |
77 | #endif |
78 | |
79 | #if defined NANOVG_GLES2 |
80 | |
81 | NVGcontext* nvgCreateGLES2(int flags); |
82 | void nvgDeleteGLES2(NVGcontext* ctx); |
83 | |
84 | int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); |
85 | GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); |
86 | |
87 | #endif |
88 | |
89 | #if defined NANOVG_GLES3 |
90 | |
91 | NVGcontext* nvgCreateGLES3(int flags); |
92 | void nvgDeleteGLES3(NVGcontext* ctx); |
93 | |
94 | int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); |
95 | GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); |
96 | |
97 | #endif |
98 | |
99 | // These are additional flags on top of NVGimageFlags. |
100 | enum NVGimageFlagsGL { |
101 | NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. |
102 | }; |
103 | |
104 | #ifdef __cplusplus |
105 | } |
106 | #endif |
107 | |
108 | #endif /* NANOVG_GL_H */ |
109 | |
110 | #ifdef NANOVG_GL_IMPLEMENTATION |
111 | |
112 | #include <stdlib.h> |
113 | #include <stdio.h> |
114 | #include <string.h> |
115 | #include <math.h> |
116 | #include "nanovg.h" |
117 | |
118 | enum GLNVGuniformLoc { |
119 | GLNVG_LOC_VIEWSIZE, |
120 | GLNVG_LOC_TEX, |
121 | GLNVG_LOC_FRAG, |
122 | GLNVG_MAX_LOCS |
123 | }; |
124 | |
125 | enum GLNVGshaderType { |
126 | NSVG_SHADER_FILLGRAD, |
127 | NSVG_SHADER_FILLIMG, |
128 | NSVG_SHADER_SIMPLE, |
129 | NSVG_SHADER_IMG |
130 | }; |
131 | |
132 | #if NANOVG_GL_USE_UNIFORMBUFFER |
133 | enum GLNVGuniformBindings { |
134 | GLNVG_FRAG_BINDING = 0, |
135 | }; |
136 | #endif |
137 | |
138 | struct GLNVGshader { |
139 | GLuint prog; |
140 | GLuint frag; |
141 | GLuint vert; |
142 | GLint loc[GLNVG_MAX_LOCS]; |
143 | }; |
144 | typedef struct GLNVGshader GLNVGshader; |
145 | |
146 | struct GLNVGtexture { |
147 | int id; |
148 | GLuint tex; |
149 | int width, height; |
150 | int type; |
151 | int flags; |
152 | }; |
153 | typedef struct GLNVGtexture GLNVGtexture; |
154 | |
155 | struct GLNVGblend |
156 | { |
157 | GLenum srcRGB; |
158 | GLenum dstRGB; |
159 | GLenum srcAlpha; |
160 | GLenum dstAlpha; |
161 | }; |
162 | typedef struct GLNVGblend GLNVGblend; |
163 | |
164 | enum GLNVGcallType { |
165 | GLNVG_NONE = 0, |
166 | GLNVG_FILL, |
167 | GLNVG_CONVEXFILL, |
168 | GLNVG_STROKE, |
169 | GLNVG_TRIANGLES, |
170 | }; |
171 | |
172 | struct GLNVGcall { |
173 | int type; |
174 | int image; |
175 | int pathOffset; |
176 | int pathCount; |
177 | int triangleOffset; |
178 | int triangleCount; |
179 | int uniformOffset; |
180 | GLNVGblend blendFunc; |
181 | }; |
182 | typedef struct GLNVGcall GLNVGcall; |
183 | |
184 | struct GLNVGpath { |
185 | int fillOffset; |
186 | int fillCount; |
187 | int strokeOffset; |
188 | int strokeCount; |
189 | }; |
190 | typedef struct GLNVGpath GLNVGpath; |
191 | |
192 | struct GLNVGfragUniforms { |
193 | #if NANOVG_GL_USE_UNIFORMBUFFER |
194 | float scissorMat[12]; // matrices are actually 3 vec4s |
195 | float paintMat[12]; |
196 | struct NVGcolor innerCol; |
197 | struct NVGcolor outerCol; |
198 | float scissorExt[2]; |
199 | float scissorScale[2]; |
200 | float extent[2]; |
201 | float radius; |
202 | float feather; |
203 | float strokeMult; |
204 | float strokeThr; |
205 | int texType; |
206 | int type; |
207 | #else |
208 | // note: after modifying layout or size of uniform array, |
209 | // don't forget to also update the fragment shader source! |
210 | #define NANOVG_GL_UNIFORMARRAY_SIZE 11 |
211 | union { |
212 | struct { |
213 | float scissorMat[12]; // matrices are actually 3 vec4s |
214 | float paintMat[12]; |
215 | struct NVGcolor innerCol; |
216 | struct NVGcolor outerCol; |
217 | float scissorExt[2]; |
218 | float scissorScale[2]; |
219 | float extent[2]; |
220 | float radius; |
221 | float feather; |
222 | float strokeMult; |
223 | float strokeThr; |
224 | float texType; |
225 | float type; |
226 | }; |
227 | float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4]; |
228 | }; |
229 | #endif |
230 | }; |
231 | typedef struct GLNVGfragUniforms GLNVGfragUniforms; |
232 | |
233 | struct GLNVGcontext { |
234 | GLNVGshader shader; |
235 | GLNVGtexture* textures; |
236 | float view[2]; |
237 | int ntextures; |
238 | int ctextures; |
239 | int textureId; |
240 | GLuint vertBuf; |
241 | #if defined NANOVG_GL3 |
242 | GLuint vertArr; |
243 | #endif |
244 | #if NANOVG_GL_USE_UNIFORMBUFFER |
245 | GLuint fragBuf; |
246 | #endif |
247 | int fragSize; |
248 | int flags; |
249 | |
250 | // Per frame buffers |
251 | GLNVGcall* calls; |
252 | int ccalls; |
253 | int ncalls; |
254 | GLNVGpath* paths; |
255 | int cpaths; |
256 | int npaths; |
257 | struct NVGvertex* verts; |
258 | int cverts; |
259 | int nverts; |
260 | unsigned char* uniforms; |
261 | int cuniforms; |
262 | int nuniforms; |
263 | |
264 | // cached state |
265 | #if NANOVG_GL_USE_STATE_FILTER |
266 | GLuint boundTexture; |
267 | GLuint stencilMask; |
268 | GLenum stencilFunc; |
269 | GLint stencilFuncRef; |
270 | GLuint stencilFuncMask; |
271 | GLNVGblend blendFunc; |
272 | #endif |
273 | }; |
274 | typedef struct GLNVGcontext GLNVGcontext; |
275 | |
276 | static int glnvg__maxi(int a, int b) { return a > b ? a : b; } |
277 | |
278 | #ifdef NANOVG_GLES2 |
279 | static unsigned int glnvg__nearestPow2(unsigned int num) |
280 | { |
281 | unsigned n = num > 0 ? num - 1 : 0; |
282 | n |= n >> 1; |
283 | n |= n >> 2; |
284 | n |= n >> 4; |
285 | n |= n >> 8; |
286 | n |= n >> 16; |
287 | n++; |
288 | return n; |
289 | } |
290 | #endif |
291 | |
292 | static void glnvg__bindTexture(GLNVGcontext* gl, GLuint tex) |
293 | { |
294 | #if NANOVG_GL_USE_STATE_FILTER |
295 | if (gl->boundTexture != tex) { |
296 | gl->boundTexture = tex; |
297 | glBindTexture(GL_TEXTURE_2D, tex); |
298 | } |
299 | #else |
300 | glBindTexture(GL_TEXTURE_2D, tex); |
301 | #endif |
302 | } |
303 | |
304 | static void glnvg__stencilMask(GLNVGcontext* gl, GLuint mask) |
305 | { |
306 | #if NANOVG_GL_USE_STATE_FILTER |
307 | if (gl->stencilMask != mask) { |
308 | gl->stencilMask = mask; |
309 | glStencilMask(mask); |
310 | } |
311 | #else |
312 | glStencilMask(mask); |
313 | #endif |
314 | } |
315 | |
316 | static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint mask) |
317 | { |
318 | #if NANOVG_GL_USE_STATE_FILTER |
319 | if ((gl->stencilFunc != func) || |
320 | (gl->stencilFuncRef != ref) || |
321 | (gl->stencilFuncMask != mask)) { |
322 | |
323 | gl->stencilFunc = func; |
324 | gl->stencilFuncRef = ref; |
325 | gl->stencilFuncMask = mask; |
326 | glStencilFunc(func, ref, mask); |
327 | } |
328 | #else |
329 | glStencilFunc(func, ref, mask); |
330 | #endif |
331 | } |
332 | static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) |
333 | { |
334 | #if NANOVG_GL_USE_STATE_FILTER |
335 | if ((gl->blendFunc.srcRGB != blend->srcRGB) || |
336 | (gl->blendFunc.dstRGB != blend->dstRGB) || |
337 | (gl->blendFunc.srcAlpha != blend->srcAlpha) || |
338 | (gl->blendFunc.dstAlpha != blend->dstAlpha)) { |
339 | |
340 | gl->blendFunc = *blend; |
341 | glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); |
342 | } |
343 | #else |
344 | glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); |
345 | #endif |
346 | } |
347 | |
348 | static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) |
349 | { |
350 | GLNVGtexture* tex = NULL; |
351 | int i; |
352 | |
353 | for (i = 0; i < gl->ntextures; i++) { |
354 | if (gl->textures[i].id == 0) { |
355 | tex = &gl->textures[i]; |
356 | break; |
357 | } |
358 | } |
359 | if (tex == NULL) { |
360 | if (gl->ntextures+1 > gl->ctextures) { |
361 | GLNVGtexture* textures; |
362 | int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate |
363 | textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); |
364 | if (textures == NULL) return NULL; |
365 | gl->textures = textures; |
366 | gl->ctextures = ctextures; |
367 | } |
368 | tex = &gl->textures[gl->ntextures++]; |
369 | } |
370 | |
371 | memset(tex, 0, sizeof(*tex)); |
372 | tex->id = ++gl->textureId; |
373 | |
374 | return tex; |
375 | } |
376 | |
377 | static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) |
378 | { |
379 | int i; |
380 | for (i = 0; i < gl->ntextures; i++) |
381 | if (gl->textures[i].id == id) |
382 | return &gl->textures[i]; |
383 | return NULL; |
384 | } |
385 | |
386 | static int glnvg__deleteTexture(GLNVGcontext* gl, int id) |
387 | { |
388 | int i; |
389 | for (i = 0; i < gl->ntextures; i++) { |
390 | if (gl->textures[i].id == id) { |
391 | if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) |
392 | glDeleteTextures(1, &gl->textures[i].tex); |
393 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); |
394 | return 1; |
395 | } |
396 | } |
397 | return 0; |
398 | } |
399 | |
400 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) |
401 | { |
402 | GLchar str[512+1]; |
403 | GLsizei len = 0; |
404 | glGetShaderInfoLog(shader, 512, &len, str); |
405 | if (len > 512) len = 512; |
406 | str[len] = '\0'; |
407 | printf("Shader %s/%s error:\n%s\n" , name, type, str); |
408 | } |
409 | |
410 | static void glnvg__dumpProgramError(GLuint prog, const char* name) |
411 | { |
412 | GLchar str[512+1]; |
413 | GLsizei len = 0; |
414 | glGetProgramInfoLog(prog, 512, &len, str); |
415 | if (len > 512) len = 512; |
416 | str[len] = '\0'; |
417 | printf("Program %s error:\n%s\n" , name, str); |
418 | } |
419 | |
420 | static void glnvg__checkError(GLNVGcontext* gl, const char* str) |
421 | { |
422 | GLenum err; |
423 | if ((gl->flags & NVG_DEBUG) == 0) return; |
424 | err = glGetError(); |
425 | if (err != GL_NO_ERROR) { |
426 | printf("Error %08x after %s\n" , err, str); |
427 | return; |
428 | } |
429 | } |
430 | |
431 | static int glnvg__createShader(GLNVGshader* shader, const char* name, const char* , const char* opts, const char* vshader, const char* fshader) |
432 | { |
433 | GLint status; |
434 | GLuint prog, vert, frag; |
435 | const char* str[3]; |
436 | str[0] = header; |
437 | str[1] = opts != NULL ? opts : "" ; |
438 | |
439 | memset(shader, 0, sizeof(*shader)); |
440 | |
441 | prog = glCreateProgram(); |
442 | vert = glCreateShader(GL_VERTEX_SHADER); |
443 | frag = glCreateShader(GL_FRAGMENT_SHADER); |
444 | str[2] = vshader; |
445 | glShaderSource(vert, 3, str, 0); |
446 | str[2] = fshader; |
447 | glShaderSource(frag, 3, str, 0); |
448 | |
449 | glCompileShader(vert); |
450 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); |
451 | if (status != GL_TRUE) { |
452 | glnvg__dumpShaderError(vert, name, "vert" ); |
453 | return 0; |
454 | } |
455 | |
456 | glCompileShader(frag); |
457 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); |
458 | if (status != GL_TRUE) { |
459 | glnvg__dumpShaderError(frag, name, "frag" ); |
460 | return 0; |
461 | } |
462 | |
463 | glAttachShader(prog, vert); |
464 | glAttachShader(prog, frag); |
465 | |
466 | glBindAttribLocation(prog, 0, "vertex" ); |
467 | glBindAttribLocation(prog, 1, "tcoord" ); |
468 | |
469 | glLinkProgram(prog); |
470 | glGetProgramiv(prog, GL_LINK_STATUS, &status); |
471 | if (status != GL_TRUE) { |
472 | glnvg__dumpProgramError(prog, name); |
473 | return 0; |
474 | } |
475 | |
476 | shader->prog = prog; |
477 | shader->vert = vert; |
478 | shader->frag = frag; |
479 | |
480 | return 1; |
481 | } |
482 | |
483 | static void glnvg__deleteShader(GLNVGshader* shader) |
484 | { |
485 | if (shader->prog != 0) |
486 | glDeleteProgram(shader->prog); |
487 | if (shader->vert != 0) |
488 | glDeleteShader(shader->vert); |
489 | if (shader->frag != 0) |
490 | glDeleteShader(shader->frag); |
491 | } |
492 | |
493 | static void glnvg__getUniforms(GLNVGshader* shader) |
494 | { |
495 | shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize" ); |
496 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex" ); |
497 | |
498 | #if NANOVG_GL_USE_UNIFORMBUFFER |
499 | shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag" ); |
500 | #else |
501 | shader->loc[GLNVG_LOC_FRAG] = glGetUniformLocation(shader->prog, "frag" ); |
502 | #endif |
503 | } |
504 | |
505 | static int glnvg__renderCreate(void* uptr) |
506 | { |
507 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
508 | int align = 4; |
509 | |
510 | // TODO: mediump float may not be enough for GLES2 in iOS. |
511 | // see the following discussion: https://github.com/memononen/nanovg/issues/46 |
512 | static const char* = |
513 | #if defined NANOVG_GL2 |
514 | "#define NANOVG_GL2 1\n" |
515 | #elif defined NANOVG_GL3 |
516 | "#version 150 core\n" |
517 | "#define NANOVG_GL3 1\n" |
518 | #elif defined NANOVG_GLES2 |
519 | "#version 100\n" |
520 | "#define NANOVG_GL2 1\n" |
521 | #elif defined NANOVG_GLES3 |
522 | "#version 300 es\n" |
523 | "#define NANOVG_GL3 1\n" |
524 | #endif |
525 | |
526 | #if NANOVG_GL_USE_UNIFORMBUFFER |
527 | "#define USE_UNIFORMBUFFER 1\n" |
528 | #else |
529 | "#define UNIFORMARRAY_SIZE 11\n" |
530 | #endif |
531 | "\n" ; |
532 | |
533 | static const char* fillVertShader = |
534 | "#ifdef NANOVG_GL3\n" |
535 | " uniform vec2 viewSize;\n" |
536 | " in vec2 vertex;\n" |
537 | " in vec2 tcoord;\n" |
538 | " out vec2 ftcoord;\n" |
539 | " out vec2 fpos;\n" |
540 | "#else\n" |
541 | " uniform vec2 viewSize;\n" |
542 | " attribute vec2 vertex;\n" |
543 | " attribute vec2 tcoord;\n" |
544 | " varying vec2 ftcoord;\n" |
545 | " varying vec2 fpos;\n" |
546 | "#endif\n" |
547 | "void main(void) {\n" |
548 | " ftcoord = tcoord;\n" |
549 | " fpos = vertex;\n" |
550 | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" |
551 | "}\n" ; |
552 | |
553 | static const char* fillFragShader = |
554 | "#ifdef GL_ES\n" |
555 | "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" |
556 | " precision highp float;\n" |
557 | "#else\n" |
558 | " precision mediump float;\n" |
559 | "#endif\n" |
560 | "#endif\n" |
561 | "#ifdef NANOVG_GL3\n" |
562 | "#ifdef USE_UNIFORMBUFFER\n" |
563 | " layout(std140) uniform frag {\n" |
564 | " mat3 scissorMat;\n" |
565 | " mat3 paintMat;\n" |
566 | " vec4 innerCol;\n" |
567 | " vec4 outerCol;\n" |
568 | " vec2 scissorExt;\n" |
569 | " vec2 scissorScale;\n" |
570 | " vec2 extent;\n" |
571 | " float radius;\n" |
572 | " float feather;\n" |
573 | " float strokeMult;\n" |
574 | " float strokeThr;\n" |
575 | " int texType;\n" |
576 | " int type;\n" |
577 | " };\n" |
578 | "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER |
579 | " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" |
580 | "#endif\n" |
581 | " uniform sampler2D tex;\n" |
582 | " in vec2 ftcoord;\n" |
583 | " in vec2 fpos;\n" |
584 | " out vec4 outColor;\n" |
585 | "#else\n" // !NANOVG_GL3 |
586 | " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" |
587 | " uniform sampler2D tex;\n" |
588 | " varying vec2 ftcoord;\n" |
589 | " varying vec2 fpos;\n" |
590 | "#endif\n" |
591 | "#ifndef USE_UNIFORMBUFFER\n" |
592 | " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" |
593 | " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" |
594 | " #define innerCol frag[6]\n" |
595 | " #define outerCol frag[7]\n" |
596 | " #define scissorExt frag[8].xy\n" |
597 | " #define scissorScale frag[8].zw\n" |
598 | " #define extent frag[9].xy\n" |
599 | " #define radius frag[9].z\n" |
600 | " #define feather frag[9].w\n" |
601 | " #define strokeMult frag[10].x\n" |
602 | " #define strokeThr frag[10].y\n" |
603 | " #define texType int(frag[10].z)\n" |
604 | " #define type int(frag[10].w)\n" |
605 | "#endif\n" |
606 | "\n" |
607 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" |
608 | " vec2 ext2 = ext - vec2(rad,rad);\n" |
609 | " vec2 d = abs(pt) - ext2;\n" |
610 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" |
611 | "}\n" |
612 | "\n" |
613 | "// Scissoring\n" |
614 | "float scissorMask(vec2 p) {\n" |
615 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" |
616 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" |
617 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" |
618 | "}\n" |
619 | "#ifdef EDGE_AA\n" |
620 | "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" |
621 | "float strokeMask() {\n" |
622 | " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" |
623 | "}\n" |
624 | "#endif\n" |
625 | "\n" |
626 | "void main(void) {\n" |
627 | " vec4 result;\n" |
628 | " float scissor = scissorMask(fpos);\n" |
629 | "#ifdef EDGE_AA\n" |
630 | " float strokeAlpha = strokeMask();\n" |
631 | " if (strokeAlpha < strokeThr) discard;\n" |
632 | "#else\n" |
633 | " float strokeAlpha = 1.0;\n" |
634 | "#endif\n" |
635 | " if (type == 0) { // Gradient\n" |
636 | " // Calculate gradient color using box gradient\n" |
637 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" |
638 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" |
639 | " vec4 color = mix(innerCol,outerCol,d);\n" |
640 | " // Combine alpha\n" |
641 | " color *= strokeAlpha * scissor;\n" |
642 | " result = color;\n" |
643 | " } else if (type == 1) { // Image\n" |
644 | " // Calculate color fron texture\n" |
645 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" |
646 | "#ifdef NANOVG_GL3\n" |
647 | " vec4 color = texture(tex, pt);\n" |
648 | "#else\n" |
649 | " vec4 color = texture2D(tex, pt);\n" |
650 | "#endif\n" |
651 | " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" |
652 | " if (texType == 2) color = vec4(color.x);" |
653 | " // Apply color tint and alpha.\n" |
654 | " color *= innerCol;\n" |
655 | " // Combine alpha\n" |
656 | " color *= strokeAlpha * scissor;\n" |
657 | " result = color;\n" |
658 | " } else if (type == 2) { // Stencil fill\n" |
659 | " result = vec4(1,1,1,1);\n" |
660 | " } else if (type == 3) { // Textured tris\n" |
661 | "#ifdef NANOVG_GL3\n" |
662 | " vec4 color = texture(tex, ftcoord);\n" |
663 | "#else\n" |
664 | " vec4 color = texture2D(tex, ftcoord);\n" |
665 | "#endif\n" |
666 | " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" |
667 | " if (texType == 2) color = vec4(color.x);" |
668 | " color *= scissor;\n" |
669 | " result = color * innerCol;\n" |
670 | " }\n" |
671 | "#ifdef NANOVG_GL3\n" |
672 | " outColor = result;\n" |
673 | "#else\n" |
674 | " gl_FragColor = result;\n" |
675 | "#endif\n" |
676 | "}\n" ; |
677 | |
678 | glnvg__checkError(gl, "init" ); |
679 | |
680 | if (gl->flags & NVG_ANTIALIAS) { |
681 | if (glnvg__createShader(&gl->shader, "shader" , shaderHeader, "#define EDGE_AA 1\n" , fillVertShader, fillFragShader) == 0) |
682 | return 0; |
683 | } else { |
684 | if (glnvg__createShader(&gl->shader, "shader" , shaderHeader, NULL, fillVertShader, fillFragShader) == 0) |
685 | return 0; |
686 | } |
687 | |
688 | glnvg__checkError(gl, "uniform locations" ); |
689 | glnvg__getUniforms(&gl->shader); |
690 | |
691 | // Create dynamic vertex array |
692 | #if defined NANOVG_GL3 |
693 | glGenVertexArrays(1, &gl->vertArr); |
694 | #endif |
695 | glGenBuffers(1, &gl->vertBuf); |
696 | |
697 | #if NANOVG_GL_USE_UNIFORMBUFFER |
698 | // Create UBOs |
699 | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); |
700 | glGenBuffers(1, &gl->fragBuf); |
701 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); |
702 | #endif |
703 | gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; |
704 | |
705 | glnvg__checkError(gl, "create done" ); |
706 | |
707 | glFinish(); |
708 | |
709 | return 1; |
710 | } |
711 | |
712 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) |
713 | { |
714 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
715 | GLNVGtexture* tex = glnvg__allocTexture(gl); |
716 | |
717 | if (tex == NULL) return 0; |
718 | |
719 | #ifdef NANOVG_GLES2 |
720 | // Check for non-power of 2. |
721 | if (glnvg__nearestPow2(w) != (unsigned int)w || glnvg__nearestPow2(h) != (unsigned int)h) { |
722 | // No repeat |
723 | if ((imageFlags & NVG_IMAGE_REPEATX) != 0 || (imageFlags & NVG_IMAGE_REPEATY) != 0) { |
724 | printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n" , w, h); |
725 | imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); |
726 | } |
727 | // No mips. |
728 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { |
729 | printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n" , w, h); |
730 | imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; |
731 | } |
732 | } |
733 | #endif |
734 | |
735 | glGenTextures(1, &tex->tex); |
736 | tex->width = w; |
737 | tex->height = h; |
738 | tex->type = type; |
739 | tex->flags = imageFlags; |
740 | glnvg__bindTexture(gl, tex->tex); |
741 | |
742 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); |
743 | #ifndef NANOVG_GLES2 |
744 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); |
745 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
746 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
747 | #endif |
748 | |
749 | #if defined (NANOVG_GL2) |
750 | // GL 1.4 and later has support for generating mipmaps using a tex parameter. |
751 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { |
752 | glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); |
753 | } |
754 | #endif |
755 | |
756 | if (type == NVG_TEXTURE_RGBA) |
757 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); |
758 | else |
759 | #if defined(NANOVG_GLES2) || defined (NANOVG_GL2) |
760 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); |
761 | #elif defined(NANOVG_GLES3) |
762 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); |
763 | #else |
764 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); |
765 | #endif |
766 | |
767 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { |
768 | if (imageFlags & NVG_IMAGE_NEAREST) { |
769 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
770 | } else { |
771 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
772 | } |
773 | } else { |
774 | if (imageFlags & NVG_IMAGE_NEAREST) { |
775 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
776 | } else { |
777 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
778 | } |
779 | } |
780 | |
781 | if (imageFlags & NVG_IMAGE_NEAREST) { |
782 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
783 | } else { |
784 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
785 | } |
786 | |
787 | if (imageFlags & NVG_IMAGE_REPEATX) |
788 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
789 | else |
790 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
791 | |
792 | if (imageFlags & NVG_IMAGE_REPEATY) |
793 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
794 | else |
795 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
796 | |
797 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); |
798 | #ifndef NANOVG_GLES2 |
799 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
800 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
801 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
802 | #endif |
803 | |
804 | // The new way to build mipmaps on GLES and GL3 |
805 | #if !defined(NANOVG_GL2) |
806 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { |
807 | glGenerateMipmap(GL_TEXTURE_2D); |
808 | } |
809 | #endif |
810 | |
811 | glnvg__checkError(gl, "create tex" ); |
812 | glnvg__bindTexture(gl, 0); |
813 | |
814 | return tex->id; |
815 | } |
816 | |
817 | |
818 | static int glnvg__renderDeleteTexture(void* uptr, int image) |
819 | { |
820 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
821 | return glnvg__deleteTexture(gl, image); |
822 | } |
823 | |
824 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) |
825 | { |
826 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
827 | GLNVGtexture* tex = glnvg__findTexture(gl, image); |
828 | |
829 | if (tex == NULL) return 0; |
830 | glnvg__bindTexture(gl, tex->tex); |
831 | |
832 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); |
833 | |
834 | #ifndef NANOVG_GLES2 |
835 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); |
836 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); |
837 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); |
838 | #else |
839 | // No support for all of skip, need to update a whole row at a time. |
840 | if (tex->type == NVG_TEXTURE_RGBA) |
841 | data += y*tex->width*4; |
842 | else |
843 | data += y*tex->width; |
844 | x = 0; |
845 | w = tex->width; |
846 | #endif |
847 | |
848 | if (tex->type == NVG_TEXTURE_RGBA) |
849 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); |
850 | else |
851 | #if defined(NANOVG_GLES2) || defined(NANOVG_GL2) |
852 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); |
853 | #else |
854 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); |
855 | #endif |
856 | |
857 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); |
858 | #ifndef NANOVG_GLES2 |
859 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
860 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
861 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
862 | #endif |
863 | |
864 | glnvg__bindTexture(gl, 0); |
865 | |
866 | return 1; |
867 | } |
868 | |
869 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) |
870 | { |
871 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
872 | GLNVGtexture* tex = glnvg__findTexture(gl, image); |
873 | if (tex == NULL) return 0; |
874 | *w = tex->width; |
875 | *h = tex->height; |
876 | return 1; |
877 | } |
878 | |
879 | static void glnvg__xformToMat3x4(float* m3, float* t) |
880 | { |
881 | m3[0] = t[0]; |
882 | m3[1] = t[1]; |
883 | m3[2] = 0.0f; |
884 | m3[3] = 0.0f; |
885 | m3[4] = t[2]; |
886 | m3[5] = t[3]; |
887 | m3[6] = 0.0f; |
888 | m3[7] = 0.0f; |
889 | m3[8] = t[4]; |
890 | m3[9] = t[5]; |
891 | m3[10] = 1.0f; |
892 | m3[11] = 0.0f; |
893 | } |
894 | |
895 | static NVGcolor glnvg__premulColor(NVGcolor c) |
896 | { |
897 | c.r *= c.a; |
898 | c.g *= c.a; |
899 | c.b *= c.a; |
900 | return c; |
901 | } |
902 | |
903 | static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint, |
904 | NVGscissor* scissor, float width, float fringe, float strokeThr) |
905 | { |
906 | GLNVGtexture* tex = NULL; |
907 | float invxform[6]; |
908 | |
909 | memset(frag, 0, sizeof(*frag)); |
910 | |
911 | frag->innerCol = glnvg__premulColor(paint->innerColor); |
912 | frag->outerCol = glnvg__premulColor(paint->outerColor); |
913 | |
914 | if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { |
915 | memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); |
916 | frag->scissorExt[0] = 1.0f; |
917 | frag->scissorExt[1] = 1.0f; |
918 | frag->scissorScale[0] = 1.0f; |
919 | frag->scissorScale[1] = 1.0f; |
920 | } else { |
921 | nvgTransformInverse(invxform, scissor->xform); |
922 | glnvg__xformToMat3x4(frag->scissorMat, invxform); |
923 | frag->scissorExt[0] = scissor->extent[0]; |
924 | frag->scissorExt[1] = scissor->extent[1]; |
925 | frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; |
926 | frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; |
927 | } |
928 | |
929 | memcpy(frag->extent, paint->extent, sizeof(frag->extent)); |
930 | frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; |
931 | frag->strokeThr = strokeThr; |
932 | |
933 | if (paint->image != 0) { |
934 | tex = glnvg__findTexture(gl, paint->image); |
935 | if (tex == NULL) return 0; |
936 | if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { |
937 | float m1[6], m2[6]; |
938 | nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); |
939 | nvgTransformMultiply(m1, paint->xform); |
940 | nvgTransformScale(m2, 1.0f, -1.0f); |
941 | nvgTransformMultiply(m2, m1); |
942 | nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); |
943 | nvgTransformMultiply(m1, m2); |
944 | nvgTransformInverse(invxform, m1); |
945 | } else { |
946 | nvgTransformInverse(invxform, paint->xform); |
947 | } |
948 | frag->type = NSVG_SHADER_FILLIMG; |
949 | |
950 | #if NANOVG_GL_USE_UNIFORMBUFFER |
951 | if (tex->type == NVG_TEXTURE_RGBA) |
952 | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; |
953 | else |
954 | frag->texType = 2; |
955 | #else |
956 | if (tex->type == NVG_TEXTURE_RGBA) |
957 | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; |
958 | else |
959 | frag->texType = 2.0f; |
960 | #endif |
961 | // printf("frag->texType = %d\n", frag->texType); |
962 | } else { |
963 | frag->type = NSVG_SHADER_FILLGRAD; |
964 | frag->radius = paint->radius; |
965 | frag->feather = paint->feather; |
966 | nvgTransformInverse(invxform, paint->xform); |
967 | } |
968 | |
969 | glnvg__xformToMat3x4(frag->paintMat, invxform); |
970 | |
971 | return 1; |
972 | } |
973 | |
974 | static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); |
975 | |
976 | static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) |
977 | { |
978 | #if NANOVG_GL_USE_UNIFORMBUFFER |
979 | glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); |
980 | #else |
981 | GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset); |
982 | glUniform4fv(gl->shader.loc[GLNVG_LOC_FRAG], NANOVG_GL_UNIFORMARRAY_SIZE, &(frag->uniformArray[0][0])); |
983 | #endif |
984 | |
985 | if (image != 0) { |
986 | GLNVGtexture* tex = glnvg__findTexture(gl, image); |
987 | glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); |
988 | glnvg__checkError(gl, "tex paint tex" ); |
989 | } else { |
990 | glnvg__bindTexture(gl, 0); |
991 | } |
992 | } |
993 | |
994 | static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) |
995 | { |
996 | NVG_NOTUSED(devicePixelRatio); |
997 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
998 | gl->view[0] = width; |
999 | gl->view[1] = height; |
1000 | } |
1001 | |
1002 | static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) |
1003 | { |
1004 | GLNVGpath* paths = &gl->paths[call->pathOffset]; |
1005 | int i, npaths = call->pathCount; |
1006 | |
1007 | // Draw shapes |
1008 | glEnable(GL_STENCIL_TEST); |
1009 | glnvg__stencilMask(gl, 0xff); |
1010 | glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff); |
1011 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
1012 | |
1013 | // set bindpoint for solid loc |
1014 | glnvg__setUniforms(gl, call->uniformOffset, 0); |
1015 | glnvg__checkError(gl, "fill simple" ); |
1016 | |
1017 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); |
1018 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); |
1019 | glDisable(GL_CULL_FACE); |
1020 | for (i = 0; i < npaths; i++) |
1021 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); |
1022 | glEnable(GL_CULL_FACE); |
1023 | |
1024 | // Draw anti-aliased pixels |
1025 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
1026 | |
1027 | glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); |
1028 | glnvg__checkError(gl, "fill fill" ); |
1029 | |
1030 | if (gl->flags & NVG_ANTIALIAS) { |
1031 | glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); |
1032 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
1033 | // Draw fringes |
1034 | for (i = 0; i < npaths; i++) |
1035 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1036 | } |
1037 | |
1038 | // Draw fill |
1039 | glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); |
1040 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); |
1041 | glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); |
1042 | |
1043 | glDisable(GL_STENCIL_TEST); |
1044 | } |
1045 | |
1046 | static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) |
1047 | { |
1048 | GLNVGpath* paths = &gl->paths[call->pathOffset]; |
1049 | int i, npaths = call->pathCount; |
1050 | |
1051 | glnvg__setUniforms(gl, call->uniformOffset, call->image); |
1052 | glnvg__checkError(gl, "convex fill" ); |
1053 | |
1054 | for (i = 0; i < npaths; i++) { |
1055 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); |
1056 | // Draw fringes |
1057 | if (paths[i].strokeCount > 0) { |
1058 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1059 | } |
1060 | } |
1061 | } |
1062 | |
1063 | static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) |
1064 | { |
1065 | GLNVGpath* paths = &gl->paths[call->pathOffset]; |
1066 | int npaths = call->pathCount, i; |
1067 | |
1068 | if (gl->flags & NVG_STENCIL_STROKES) { |
1069 | |
1070 | glEnable(GL_STENCIL_TEST); |
1071 | glnvg__stencilMask(gl, 0xff); |
1072 | |
1073 | // Fill the stroke base without overlap |
1074 | glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff); |
1075 | glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); |
1076 | glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); |
1077 | glnvg__checkError(gl, "stroke fill 0" ); |
1078 | for (i = 0; i < npaths; i++) |
1079 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1080 | |
1081 | // Draw anti-aliased pixels. |
1082 | glnvg__setUniforms(gl, call->uniformOffset, call->image); |
1083 | glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); |
1084 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
1085 | for (i = 0; i < npaths; i++) |
1086 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1087 | |
1088 | // Clear stencil buffer. |
1089 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
1090 | glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); |
1091 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); |
1092 | glnvg__checkError(gl, "stroke fill 1" ); |
1093 | for (i = 0; i < npaths; i++) |
1094 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1095 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
1096 | |
1097 | glDisable(GL_STENCIL_TEST); |
1098 | |
1099 | // glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); |
1100 | |
1101 | } else { |
1102 | glnvg__setUniforms(gl, call->uniformOffset, call->image); |
1103 | glnvg__checkError(gl, "stroke fill" ); |
1104 | // Draw Strokes |
1105 | for (i = 0; i < npaths; i++) |
1106 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); |
1107 | } |
1108 | } |
1109 | |
1110 | static void glnvg__triangles(GLNVGcontext* gl, GLNVGcall* call) |
1111 | { |
1112 | glnvg__setUniforms(gl, call->uniformOffset, call->image); |
1113 | glnvg__checkError(gl, "triangles fill" ); |
1114 | |
1115 | glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); |
1116 | } |
1117 | |
1118 | static void glnvg__renderCancel(void* uptr) { |
1119 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1120 | gl->nverts = 0; |
1121 | gl->npaths = 0; |
1122 | gl->ncalls = 0; |
1123 | gl->nuniforms = 0; |
1124 | } |
1125 | |
1126 | static GLenum glnvg_convertBlendFuncFactor(int factor) |
1127 | { |
1128 | if (factor == NVG_ZERO) |
1129 | return GL_ZERO; |
1130 | if (factor == NVG_ONE) |
1131 | return GL_ONE; |
1132 | if (factor == NVG_SRC_COLOR) |
1133 | return GL_SRC_COLOR; |
1134 | if (factor == NVG_ONE_MINUS_SRC_COLOR) |
1135 | return GL_ONE_MINUS_SRC_COLOR; |
1136 | if (factor == NVG_DST_COLOR) |
1137 | return GL_DST_COLOR; |
1138 | if (factor == NVG_ONE_MINUS_DST_COLOR) |
1139 | return GL_ONE_MINUS_DST_COLOR; |
1140 | if (factor == NVG_SRC_ALPHA) |
1141 | return GL_SRC_ALPHA; |
1142 | if (factor == NVG_ONE_MINUS_SRC_ALPHA) |
1143 | return GL_ONE_MINUS_SRC_ALPHA; |
1144 | if (factor == NVG_DST_ALPHA) |
1145 | return GL_DST_ALPHA; |
1146 | if (factor == NVG_ONE_MINUS_DST_ALPHA) |
1147 | return GL_ONE_MINUS_DST_ALPHA; |
1148 | if (factor == NVG_SRC_ALPHA_SATURATE) |
1149 | return GL_SRC_ALPHA_SATURATE; |
1150 | return GL_INVALID_ENUM; |
1151 | } |
1152 | |
1153 | static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) |
1154 | { |
1155 | GLNVGblend blend; |
1156 | blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); |
1157 | blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); |
1158 | blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); |
1159 | blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); |
1160 | if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) |
1161 | { |
1162 | blend.srcRGB = GL_ONE; |
1163 | blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; |
1164 | blend.srcAlpha = GL_ONE; |
1165 | blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; |
1166 | } |
1167 | return blend; |
1168 | } |
1169 | |
1170 | static void glnvg__renderFlush(void* uptr) |
1171 | { |
1172 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1173 | int i; |
1174 | |
1175 | if (gl->ncalls > 0) { |
1176 | |
1177 | // Setup require GL state. |
1178 | glUseProgram(gl->shader.prog); |
1179 | |
1180 | glEnable(GL_CULL_FACE); |
1181 | glCullFace(GL_BACK); |
1182 | glFrontFace(GL_CCW); |
1183 | glEnable(GL_BLEND); |
1184 | glDisable(GL_DEPTH_TEST); |
1185 | glDisable(GL_SCISSOR_TEST); |
1186 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
1187 | glStencilMask(0xffffffff); |
1188 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
1189 | glStencilFunc(GL_ALWAYS, 0, 0xffffffff); |
1190 | glActiveTexture(GL_TEXTURE0); |
1191 | glBindTexture(GL_TEXTURE_2D, 0); |
1192 | #if NANOVG_GL_USE_STATE_FILTER |
1193 | gl->boundTexture = 0; |
1194 | gl->stencilMask = 0xffffffff; |
1195 | gl->stencilFunc = GL_ALWAYS; |
1196 | gl->stencilFuncRef = 0; |
1197 | gl->stencilFuncMask = 0xffffffff; |
1198 | gl->blendFunc.srcRGB = GL_INVALID_ENUM; |
1199 | gl->blendFunc.srcAlpha = GL_INVALID_ENUM; |
1200 | gl->blendFunc.dstRGB = GL_INVALID_ENUM; |
1201 | gl->blendFunc.dstAlpha = GL_INVALID_ENUM; |
1202 | #endif |
1203 | |
1204 | #if NANOVG_GL_USE_UNIFORMBUFFER |
1205 | // Upload ubo for frag shaders |
1206 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); |
1207 | glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW); |
1208 | #endif |
1209 | |
1210 | // Upload vertex data |
1211 | #if defined NANOVG_GL3 |
1212 | glBindVertexArray(gl->vertArr); |
1213 | #endif |
1214 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); |
1215 | glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(NVGvertex), gl->verts, GL_STREAM_DRAW); |
1216 | glEnableVertexAttribArray(0); |
1217 | glEnableVertexAttribArray(1); |
1218 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0); |
1219 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); |
1220 | |
1221 | // Set view and texture just once per frame. |
1222 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); |
1223 | glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view); |
1224 | |
1225 | #if NANOVG_GL_USE_UNIFORMBUFFER |
1226 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); |
1227 | #endif |
1228 | |
1229 | for (i = 0; i < gl->ncalls; i++) { |
1230 | GLNVGcall* call = &gl->calls[i]; |
1231 | glnvg__blendFuncSeparate(gl,&call->blendFunc); |
1232 | if (call->type == GLNVG_FILL) |
1233 | glnvg__fill(gl, call); |
1234 | else if (call->type == GLNVG_CONVEXFILL) |
1235 | glnvg__convexFill(gl, call); |
1236 | else if (call->type == GLNVG_STROKE) |
1237 | glnvg__stroke(gl, call); |
1238 | else if (call->type == GLNVG_TRIANGLES) |
1239 | glnvg__triangles(gl, call); |
1240 | } |
1241 | |
1242 | glDisableVertexAttribArray(0); |
1243 | glDisableVertexAttribArray(1); |
1244 | #if defined NANOVG_GL3 |
1245 | glBindVertexArray(0); |
1246 | #endif |
1247 | glDisable(GL_CULL_FACE); |
1248 | glBindBuffer(GL_ARRAY_BUFFER, 0); |
1249 | glUseProgram(0); |
1250 | glnvg__bindTexture(gl, 0); |
1251 | } |
1252 | |
1253 | // Reset calls |
1254 | gl->nverts = 0; |
1255 | gl->npaths = 0; |
1256 | gl->ncalls = 0; |
1257 | gl->nuniforms = 0; |
1258 | } |
1259 | |
1260 | static int glnvg__maxVertCount(const NVGpath* paths, int npaths) |
1261 | { |
1262 | int i, count = 0; |
1263 | for (i = 0; i < npaths; i++) { |
1264 | count += paths[i].nfill; |
1265 | count += paths[i].nstroke; |
1266 | } |
1267 | return count; |
1268 | } |
1269 | |
1270 | static GLNVGcall* glnvg__allocCall(GLNVGcontext* gl) |
1271 | { |
1272 | GLNVGcall* ret = NULL; |
1273 | if (gl->ncalls+1 > gl->ccalls) { |
1274 | GLNVGcall* calls; |
1275 | int ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls/2; // 1.5x Overallocate |
1276 | calls = (GLNVGcall*)realloc(gl->calls, sizeof(GLNVGcall) * ccalls); |
1277 | if (calls == NULL) return NULL; |
1278 | gl->calls = calls; |
1279 | gl->ccalls = ccalls; |
1280 | } |
1281 | ret = &gl->calls[gl->ncalls++]; |
1282 | memset(ret, 0, sizeof(GLNVGcall)); |
1283 | return ret; |
1284 | } |
1285 | |
1286 | static int glnvg__allocPaths(GLNVGcontext* gl, int n) |
1287 | { |
1288 | int ret = 0; |
1289 | if (gl->npaths+n > gl->cpaths) { |
1290 | GLNVGpath* paths; |
1291 | int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths/2; // 1.5x Overallocate |
1292 | paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths); |
1293 | if (paths == NULL) return -1; |
1294 | gl->paths = paths; |
1295 | gl->cpaths = cpaths; |
1296 | } |
1297 | ret = gl->npaths; |
1298 | gl->npaths += n; |
1299 | return ret; |
1300 | } |
1301 | |
1302 | static int glnvg__allocVerts(GLNVGcontext* gl, int n) |
1303 | { |
1304 | int ret = 0; |
1305 | if (gl->nverts+n > gl->cverts) { |
1306 | NVGvertex* verts; |
1307 | int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate |
1308 | verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts); |
1309 | if (verts == NULL) return -1; |
1310 | gl->verts = verts; |
1311 | gl->cverts = cverts; |
1312 | } |
1313 | ret = gl->nverts; |
1314 | gl->nverts += n; |
1315 | return ret; |
1316 | } |
1317 | |
1318 | static int glnvg__allocFragUniforms(GLNVGcontext* gl, int n) |
1319 | { |
1320 | int ret = 0, structSize = gl->fragSize; |
1321 | if (gl->nuniforms+n > gl->cuniforms) { |
1322 | unsigned char* uniforms; |
1323 | int cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate |
1324 | uniforms = (unsigned char*)realloc(gl->uniforms, structSize * cuniforms); |
1325 | if (uniforms == NULL) return -1; |
1326 | gl->uniforms = uniforms; |
1327 | gl->cuniforms = cuniforms; |
1328 | } |
1329 | ret = gl->nuniforms * structSize; |
1330 | gl->nuniforms += n; |
1331 | return ret; |
1332 | } |
1333 | |
1334 | static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i) |
1335 | { |
1336 | return (GLNVGfragUniforms*)&gl->uniforms[i]; |
1337 | } |
1338 | |
1339 | static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) |
1340 | { |
1341 | vtx->x = x; |
1342 | vtx->y = y; |
1343 | vtx->u = u; |
1344 | vtx->v = v; |
1345 | } |
1346 | |
1347 | static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, |
1348 | const float* bounds, const NVGpath* paths, int npaths) |
1349 | { |
1350 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1351 | GLNVGcall* call = glnvg__allocCall(gl); |
1352 | NVGvertex* quad; |
1353 | GLNVGfragUniforms* frag; |
1354 | int i, maxverts, offset; |
1355 | |
1356 | if (call == NULL) return; |
1357 | |
1358 | call->type = GLNVG_FILL; |
1359 | call->triangleCount = 4; |
1360 | call->pathOffset = glnvg__allocPaths(gl, npaths); |
1361 | if (call->pathOffset == -1) goto error; |
1362 | call->pathCount = npaths; |
1363 | call->image = paint->image; |
1364 | call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); |
1365 | |
1366 | if (npaths == 1 && paths[0].convex) |
1367 | { |
1368 | call->type = GLNVG_CONVEXFILL; |
1369 | call->triangleCount = 0; // Bounding box fill quad not needed for convex fill |
1370 | } |
1371 | |
1372 | // Allocate vertices for all the paths. |
1373 | maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; |
1374 | offset = glnvg__allocVerts(gl, maxverts); |
1375 | if (offset == -1) goto error; |
1376 | |
1377 | for (i = 0; i < npaths; i++) { |
1378 | GLNVGpath* copy = &gl->paths[call->pathOffset + i]; |
1379 | const NVGpath* path = &paths[i]; |
1380 | memset(copy, 0, sizeof(GLNVGpath)); |
1381 | if (path->nfill > 0) { |
1382 | copy->fillOffset = offset; |
1383 | copy->fillCount = path->nfill; |
1384 | memcpy(&gl->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); |
1385 | offset += path->nfill; |
1386 | } |
1387 | if (path->nstroke > 0) { |
1388 | copy->strokeOffset = offset; |
1389 | copy->strokeCount = path->nstroke; |
1390 | memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); |
1391 | offset += path->nstroke; |
1392 | } |
1393 | } |
1394 | |
1395 | // Setup uniforms for draw calls |
1396 | if (call->type == GLNVG_FILL) { |
1397 | // Quad |
1398 | call->triangleOffset = offset; |
1399 | quad = &gl->verts[call->triangleOffset]; |
1400 | glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); |
1401 | glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); |
1402 | glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); |
1403 | glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); |
1404 | |
1405 | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); |
1406 | if (call->uniformOffset == -1) goto error; |
1407 | // Simple shader for stencil |
1408 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); |
1409 | memset(frag, 0, sizeof(*frag)); |
1410 | frag->strokeThr = -1.0f; |
1411 | frag->type = NSVG_SHADER_SIMPLE; |
1412 | // Fill shader |
1413 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f); |
1414 | } else { |
1415 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); |
1416 | if (call->uniformOffset == -1) goto error; |
1417 | // Fill shader |
1418 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); |
1419 | } |
1420 | |
1421 | return; |
1422 | |
1423 | error: |
1424 | // We get here if call alloc was ok, but something else is not. |
1425 | // Roll back the last call to prevent drawing it. |
1426 | if (gl->ncalls > 0) gl->ncalls--; |
1427 | } |
1428 | |
1429 | static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, |
1430 | float strokeWidth, const NVGpath* paths, int npaths) |
1431 | { |
1432 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1433 | GLNVGcall* call = glnvg__allocCall(gl); |
1434 | int i, maxverts, offset; |
1435 | |
1436 | if (call == NULL) return; |
1437 | |
1438 | call->type = GLNVG_STROKE; |
1439 | call->pathOffset = glnvg__allocPaths(gl, npaths); |
1440 | if (call->pathOffset == -1) goto error; |
1441 | call->pathCount = npaths; |
1442 | call->image = paint->image; |
1443 | call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); |
1444 | |
1445 | // Allocate vertices for all the paths. |
1446 | maxverts = glnvg__maxVertCount(paths, npaths); |
1447 | offset = glnvg__allocVerts(gl, maxverts); |
1448 | if (offset == -1) goto error; |
1449 | |
1450 | for (i = 0; i < npaths; i++) { |
1451 | GLNVGpath* copy = &gl->paths[call->pathOffset + i]; |
1452 | const NVGpath* path = &paths[i]; |
1453 | memset(copy, 0, sizeof(GLNVGpath)); |
1454 | if (path->nstroke) { |
1455 | copy->strokeOffset = offset; |
1456 | copy->strokeCount = path->nstroke; |
1457 | memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); |
1458 | offset += path->nstroke; |
1459 | } |
1460 | } |
1461 | |
1462 | if (gl->flags & NVG_STENCIL_STROKES) { |
1463 | // Fill shader |
1464 | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); |
1465 | if (call->uniformOffset == -1) goto error; |
1466 | |
1467 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); |
1468 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); |
1469 | |
1470 | } else { |
1471 | // Fill shader |
1472 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); |
1473 | if (call->uniformOffset == -1) goto error; |
1474 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); |
1475 | } |
1476 | |
1477 | return; |
1478 | |
1479 | error: |
1480 | // We get here if call alloc was ok, but something else is not. |
1481 | // Roll back the last call to prevent drawing it. |
1482 | if (gl->ncalls > 0) gl->ncalls--; |
1483 | } |
1484 | |
1485 | static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, |
1486 | const NVGvertex* verts, int nverts) |
1487 | { |
1488 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1489 | GLNVGcall* call = glnvg__allocCall(gl); |
1490 | GLNVGfragUniforms* frag; |
1491 | |
1492 | if (call == NULL) return; |
1493 | |
1494 | call->type = GLNVG_TRIANGLES; |
1495 | call->image = paint->image; |
1496 | call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); |
1497 | |
1498 | // Allocate vertices for all the paths. |
1499 | call->triangleOffset = glnvg__allocVerts(gl, nverts); |
1500 | if (call->triangleOffset == -1) goto error; |
1501 | call->triangleCount = nverts; |
1502 | |
1503 | memcpy(&gl->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); |
1504 | |
1505 | // Fill shader |
1506 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); |
1507 | if (call->uniformOffset == -1) goto error; |
1508 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); |
1509 | glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); |
1510 | frag->type = NSVG_SHADER_IMG; |
1511 | |
1512 | return; |
1513 | |
1514 | error: |
1515 | // We get here if call alloc was ok, but something else is not. |
1516 | // Roll back the last call to prevent drawing it. |
1517 | if (gl->ncalls > 0) gl->ncalls--; |
1518 | } |
1519 | |
1520 | static void glnvg__renderDelete(void* uptr) |
1521 | { |
1522 | GLNVGcontext* gl = (GLNVGcontext*)uptr; |
1523 | int i; |
1524 | if (gl == NULL) return; |
1525 | |
1526 | glnvg__deleteShader(&gl->shader); |
1527 | |
1528 | #if NANOVG_GL3 |
1529 | #if NANOVG_GL_USE_UNIFORMBUFFER |
1530 | if (gl->fragBuf != 0) |
1531 | glDeleteBuffers(1, &gl->fragBuf); |
1532 | #endif |
1533 | if (gl->vertArr != 0) |
1534 | glDeleteVertexArrays(1, &gl->vertArr); |
1535 | #endif |
1536 | if (gl->vertBuf != 0) |
1537 | glDeleteBuffers(1, &gl->vertBuf); |
1538 | |
1539 | for (i = 0; i < gl->ntextures; i++) { |
1540 | if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) |
1541 | glDeleteTextures(1, &gl->textures[i].tex); |
1542 | } |
1543 | free(gl->textures); |
1544 | |
1545 | free(gl->paths); |
1546 | free(gl->verts); |
1547 | free(gl->uniforms); |
1548 | free(gl->calls); |
1549 | |
1550 | free(gl); |
1551 | } |
1552 | |
1553 | |
1554 | #if defined NANOVG_GL2 |
1555 | NVGcontext* nvgCreateGL2(int flags) |
1556 | #elif defined NANOVG_GL3 |
1557 | NVGcontext* nvgCreateGL3(int flags) |
1558 | #elif defined NANOVG_GLES2 |
1559 | NVGcontext* nvgCreateGLES2(int flags) |
1560 | #elif defined NANOVG_GLES3 |
1561 | NVGcontext* nvgCreateGLES3(int flags) |
1562 | #endif |
1563 | { |
1564 | NVGparams params; |
1565 | NVGcontext* ctx = NULL; |
1566 | GLNVGcontext* gl = (GLNVGcontext*)malloc(sizeof(GLNVGcontext)); |
1567 | if (gl == NULL) goto error; |
1568 | memset(gl, 0, sizeof(GLNVGcontext)); |
1569 | |
1570 | memset(¶ms, 0, sizeof(params)); |
1571 | params.renderCreate = glnvg__renderCreate; |
1572 | params.renderCreateTexture = glnvg__renderCreateTexture; |
1573 | params.renderDeleteTexture = glnvg__renderDeleteTexture; |
1574 | params.renderUpdateTexture = glnvg__renderUpdateTexture; |
1575 | params.renderGetTextureSize = glnvg__renderGetTextureSize; |
1576 | params.renderViewport = glnvg__renderViewport; |
1577 | params.renderCancel = glnvg__renderCancel; |
1578 | params.renderFlush = glnvg__renderFlush; |
1579 | params.renderFill = glnvg__renderFill; |
1580 | params.renderStroke = glnvg__renderStroke; |
1581 | params.renderTriangles = glnvg__renderTriangles; |
1582 | params.renderDelete = glnvg__renderDelete; |
1583 | params.userPtr = gl; |
1584 | params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; |
1585 | |
1586 | gl->flags = flags; |
1587 | |
1588 | ctx = nvgCreateInternal(¶ms); |
1589 | if (ctx == NULL) goto error; |
1590 | |
1591 | return ctx; |
1592 | |
1593 | error: |
1594 | // 'gl' is freed by nvgDeleteInternal. |
1595 | if (ctx != NULL) nvgDeleteInternal(ctx); |
1596 | return NULL; |
1597 | } |
1598 | |
1599 | #if defined NANOVG_GL2 |
1600 | void nvgDeleteGL2(NVGcontext* ctx) |
1601 | #elif defined NANOVG_GL3 |
1602 | void nvgDeleteGL3(NVGcontext* ctx) |
1603 | #elif defined NANOVG_GLES2 |
1604 | void nvgDeleteGLES2(NVGcontext* ctx) |
1605 | #elif defined NANOVG_GLES3 |
1606 | void nvgDeleteGLES3(NVGcontext* ctx) |
1607 | #endif |
1608 | { |
1609 | nvgDeleteInternal(ctx); |
1610 | } |
1611 | |
1612 | #if defined NANOVG_GL2 |
1613 | int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) |
1614 | #elif defined NANOVG_GL3 |
1615 | int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) |
1616 | #elif defined NANOVG_GLES2 |
1617 | int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) |
1618 | #elif defined NANOVG_GLES3 |
1619 | int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) |
1620 | #endif |
1621 | { |
1622 | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; |
1623 | GLNVGtexture* tex = glnvg__allocTexture(gl); |
1624 | |
1625 | if (tex == NULL) return 0; |
1626 | |
1627 | tex->type = NVG_TEXTURE_RGBA; |
1628 | tex->tex = textureId; |
1629 | tex->flags = imageFlags; |
1630 | tex->width = w; |
1631 | tex->height = h; |
1632 | |
1633 | return tex->id; |
1634 | } |
1635 | |
1636 | #if defined NANOVG_GL2 |
1637 | GLuint nvglImageHandleGL2(NVGcontext* ctx, int image) |
1638 | #elif defined NANOVG_GL3 |
1639 | GLuint nvglImageHandleGL3(NVGcontext* ctx, int image) |
1640 | #elif defined NANOVG_GLES2 |
1641 | GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image) |
1642 | #elif defined NANOVG_GLES3 |
1643 | GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image) |
1644 | #endif |
1645 | { |
1646 | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; |
1647 | GLNVGtexture* tex = glnvg__findTexture(gl, image); |
1648 | return tex->tex; |
1649 | } |
1650 | |
1651 | #endif /* NANOVG_GL_IMPLEMENTATION */ |
1652 | |