Cleanup
[carla.git] / source / modules / dgl / src / nanovg / nanovg.c
blob0c75b17fe17a9edf1a4023937621d830887c1901
1 //
2 // Copyright (c) 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.
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <math.h>
22 #include <memory.h>
24 #include "nanovg.h"
25 #define FONTSTASH_IMPLEMENTATION
26 #include "fontstash.h"
28 #ifndef NVG_NO_STB
29 #define STB_IMAGE_IMPLEMENTATION
30 #include "stb_image.h"
31 #endif
33 #ifdef NVG_DISABLE_SKIPPING_WHITESPACE
34 #define NVG_SKIPPED_CHAR NVG_SPACE
35 #else
36 #define NVG_SKIPPED_CHAR NVG_CHAR
37 #endif
39 #ifndef NVG_FONT_TEXTURE_FLAGS
40 #define NVG_FONT_TEXTURE_FLAGS 0
41 #endif
43 #ifdef _MSC_VER
44 #pragma warning(disable: 4100) // unreferenced formal parameter
45 #pragma warning(disable: 4127) // conditional expression is constant
46 #pragma warning(disable: 4204) // nonstandard extension used : non-constant aggregate initializer
47 #pragma warning(disable: 4706) // assignment within conditional expression
48 #endif
50 #define NVG_INIT_FONTIMAGE_SIZE 512
51 #define NVG_MAX_FONTIMAGE_SIZE 2048
52 #define NVG_MAX_FONTIMAGES 4
54 #define NVG_INIT_COMMANDS_SIZE 256
55 #define NVG_INIT_POINTS_SIZE 128
56 #define NVG_INIT_PATHS_SIZE 16
57 #define NVG_INIT_VERTS_SIZE 256
58 #define NVG_MAX_STATES 32
60 #define NVG_KAPPA90 0.5522847493f // Length proportional to radius of a cubic bezier handle for 90deg arcs.
62 #define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr]))
65 enum NVGcommands {
66 NVG_MOVETO = 0,
67 NVG_LINETO = 1,
68 NVG_BEZIERTO = 2,
69 NVG_CLOSE = 3,
70 NVG_WINDING = 4,
73 enum NVGpointFlags
75 NVG_PT_CORNER = 0x01,
76 NVG_PT_LEFT = 0x02,
77 NVG_PT_BEVEL = 0x04,
78 NVG_PR_INNERBEVEL = 0x08,
81 struct NVGstate {
82 NVGcompositeOperationState compositeOperation;
83 int shapeAntiAlias;
84 NVGpaint fill;
85 NVGpaint stroke;
86 float strokeWidth;
87 float miterLimit;
88 int lineJoin;
89 int lineCap;
90 NVGcolor tint;
91 float xform[6];
92 NVGscissor scissor;
93 float fontSize;
94 float letterSpacing;
95 float lineHeight;
96 float fontBlur;
97 int textAlign;
98 int fontId;
100 typedef struct NVGstate NVGstate;
102 struct NVGpoint {
103 float x,y;
104 float dx, dy;
105 float len;
106 float dmx, dmy;
107 unsigned char flags;
109 typedef struct NVGpoint NVGpoint;
111 struct NVGpathCache {
112 NVGpoint* points;
113 int npoints;
114 int cpoints;
115 NVGpath* paths;
116 int npaths;
117 int cpaths;
118 NVGvertex* verts;
119 int nverts;
120 int cverts;
121 float bounds[4];
123 typedef struct NVGpathCache NVGpathCache;
125 struct NVGfontContext { // Fontstash context plus font images; shared between shared NanoVG contexts.
126 int refCount;
127 struct FONScontext* fs;
128 int fontImages[NVG_MAX_FONTIMAGES];
129 int fontImageIdx;
131 typedef struct NVGfontContext NVGfontContext;
133 struct NVGcontext {
134 NVGparams params;
135 float* commands;
136 int ccommands;
137 int ncommands;
138 float commandx, commandy;
139 NVGstate states[NVG_MAX_STATES];
140 int nstates;
141 NVGpathCache* cache;
142 float tessTol;
143 float distTol;
144 float fringeWidth;
145 float devicePxRatio;
146 NVGfontContext* fontContext;
147 int drawCallCount;
148 int fillTriCount;
149 int strokeTriCount;
150 int textTriCount;
153 static float nvg__sqrtf(float a) { return sqrtf(a); }
154 static float nvg__modf(float a, float b) { return fmodf(a, b); }
155 static float nvg__sinf(float a) { return sinf(a); }
156 static float nvg__cosf(float a) { return cosf(a); }
157 static float nvg__tanf(float a) { return tanf(a); }
158 static float nvg__atan2f(float a,float b) { return atan2f(a, b); }
159 static float nvg__acosf(float a) { return acosf(a); }
161 static int nvg__mini(int a, int b) { return a < b ? a : b; }
162 static int nvg__maxi(int a, int b) { return a > b ? a : b; }
163 static int nvg__clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); }
164 static float nvg__minf(float a, float b) { return a < b ? a : b; }
165 static float nvg__maxf(float a, float b) { return a > b ? a : b; }
166 static float nvg__absf(float a) { return a >= 0.0f ? a : -a; }
167 static float nvg__signf(float a) { return a >= 0.0f ? 1.0f : -1.0f; }
168 static float nvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
169 static float nvg__cross(float dx0, float dy0, float dx1, float dy1) { return dx1*dy0 - dx0*dy1; }
171 static float nvg__normalize(float *x, float* y)
173 float d = nvg__sqrtf((*x)*(*x) + (*y)*(*y));
174 if (d > 1e-6f) {
175 float id = 1.0f / d;
176 *x *= id;
177 *y *= id;
179 return d;
183 static void nvg__deletePathCache(NVGpathCache* c)
185 if (c == NULL) return;
186 if (c->points != NULL) free(c->points);
187 if (c->paths != NULL) free(c->paths);
188 if (c->verts != NULL) free(c->verts);
189 free(c);
192 static NVGpathCache* nvg__allocPathCache(void)
194 NVGpathCache* c = (NVGpathCache*)malloc(sizeof(NVGpathCache));
195 if (c == NULL) goto error;
196 memset(c, 0, sizeof(NVGpathCache));
198 c->points = (NVGpoint*)malloc(sizeof(NVGpoint)*NVG_INIT_POINTS_SIZE);
199 if (!c->points) goto error;
200 c->npoints = 0;
201 c->cpoints = NVG_INIT_POINTS_SIZE;
203 c->paths = (NVGpath*)malloc(sizeof(NVGpath)*NVG_INIT_PATHS_SIZE);
204 if (!c->paths) goto error;
205 c->npaths = 0;
206 c->cpaths = NVG_INIT_PATHS_SIZE;
208 c->verts = (NVGvertex*)malloc(sizeof(NVGvertex)*NVG_INIT_VERTS_SIZE);
209 if (!c->verts) goto error;
210 c->nverts = 0;
211 c->cverts = NVG_INIT_VERTS_SIZE;
213 return c;
214 error:
215 nvg__deletePathCache(c);
216 return NULL;
219 static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio)
221 ctx->tessTol = 0.25f / ratio;
222 ctx->distTol = 0.01f / ratio;
223 ctx->fringeWidth = 1.0f / ratio;
224 ctx->devicePxRatio = ratio;
227 static NVGcompositeOperationState nvg__compositeOperationState(int op)
229 int sfactor, dfactor;
231 if (op == NVG_SOURCE_OVER)
233 sfactor = NVG_ONE;
234 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
236 else if (op == NVG_SOURCE_IN)
238 sfactor = NVG_DST_ALPHA;
239 dfactor = NVG_ZERO;
241 else if (op == NVG_SOURCE_OUT)
243 sfactor = NVG_ONE_MINUS_DST_ALPHA;
244 dfactor = NVG_ZERO;
246 else if (op == NVG_ATOP)
248 sfactor = NVG_DST_ALPHA;
249 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
251 else if (op == NVG_DESTINATION_OVER)
253 sfactor = NVG_ONE_MINUS_DST_ALPHA;
254 dfactor = NVG_ONE;
256 else if (op == NVG_DESTINATION_IN)
258 sfactor = NVG_ZERO;
259 dfactor = NVG_SRC_ALPHA;
261 else if (op == NVG_DESTINATION_OUT)
263 sfactor = NVG_ZERO;
264 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
266 else if (op == NVG_DESTINATION_ATOP)
268 sfactor = NVG_ONE_MINUS_DST_ALPHA;
269 dfactor = NVG_SRC_ALPHA;
271 else if (op == NVG_LIGHTER)
273 sfactor = NVG_ONE;
274 dfactor = NVG_ONE;
276 else if (op == NVG_COPY)
278 sfactor = NVG_ONE;
279 dfactor = NVG_ZERO;
281 else if (op == NVG_XOR)
283 sfactor = NVG_ONE_MINUS_DST_ALPHA;
284 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
286 else
288 sfactor = NVG_ONE;
289 dfactor = NVG_ZERO;
292 NVGcompositeOperationState state;
293 state.srcRGB = sfactor;
294 state.dstRGB = dfactor;
295 state.srcAlpha = sfactor;
296 state.dstAlpha = dfactor;
297 return state;
300 static NVGstate* nvg__getState(NVGcontext* ctx)
302 return &ctx->states[ctx->nstates-1];
305 NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other) // Share the fonts and images of 'other' if it's non-NULL.
307 FONSparams fontParams;
308 NVGcontext* ctx = (NVGcontext*)malloc(sizeof(NVGcontext));
309 int i;
310 if (ctx == NULL) goto error;
311 memset(ctx, 0, sizeof(NVGcontext));
313 ctx->params = *params;
314 if (other) {
315 ctx->fontContext = other->fontContext;
316 ctx->fontContext->refCount++;
317 } else {
318 ctx->fontContext = (NVGfontContext*)malloc(sizeof(NVGfontContext));
319 if (ctx->fontContext == NULL) goto error;
320 for (i = 0; i < NVG_MAX_FONTIMAGES; i++)
321 ctx->fontContext->fontImages[i] = 0;
322 ctx->fontContext->refCount = 1;
325 ctx->commands = (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE);
326 if (!ctx->commands) goto error;
327 ctx->ncommands = 0;
328 ctx->ccommands = NVG_INIT_COMMANDS_SIZE;
330 ctx->cache = nvg__allocPathCache();
331 if (ctx->cache == NULL) goto error;
333 nvgSave(ctx);
334 nvgReset(ctx);
336 nvg__setDevicePixelRatio(ctx, 1.0f);
338 if (ctx->params.renderCreate(ctx->params.userPtr, other ? other->params.userPtr : NULL) == 0) goto error;
340 // Init font rendering
341 if (!other) {
342 memset(&fontParams, 0, sizeof(fontParams));
343 fontParams.width = NVG_INIT_FONTIMAGE_SIZE;
344 fontParams.height = NVG_INIT_FONTIMAGE_SIZE;
345 fontParams.flags = FONS_ZERO_TOPLEFT;
346 fontParams.renderCreate = NULL;
347 fontParams.renderUpdate = NULL;
348 fontParams.renderDraw = NULL;
349 fontParams.renderDelete = NULL;
350 fontParams.userPtr = NULL;
351 ctx->fontContext->fs = fonsCreateInternal(&fontParams);
352 if (ctx->fontContext->fs == NULL) goto error;
354 // Create font texture
355 ctx->fontContext->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr,
356 NVG_TEXTURE_ALPHA,
357 fontParams.width,
358 fontParams.height,
359 NVG_FONT_TEXTURE_FLAGS,
360 NULL);
361 if (ctx->fontContext->fontImages[0] == 0) goto error;
362 ctx->fontContext->fontImageIdx = 0;
365 return ctx;
367 error:
368 nvgDeleteInternal(ctx);
369 return 0;
372 NVGparams* nvgInternalParams(NVGcontext* ctx)
374 return &ctx->params;
377 void nvgDeleteInternal(NVGcontext* ctx)
379 int i;
380 if (ctx == NULL) return;
381 if (ctx->commands != NULL) free(ctx->commands);
382 if (ctx->cache != NULL) nvg__deletePathCache(ctx->cache);
384 if (ctx->fontContext != NULL && --ctx->fontContext->refCount == 0) {
385 if (ctx->fontContext->fs)
386 fonsDeleteInternal(ctx->fontContext->fs);
388 for (i = 0; i < NVG_MAX_FONTIMAGES; i++) {
389 if (ctx->fontContext->fontImages[i] != 0) {
390 nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]);
391 ctx->fontContext->fontImages[i] = 0;
395 free(ctx->fontContext);
398 if (ctx->params.renderDelete != NULL)
399 ctx->params.renderDelete(ctx->params.userPtr);
401 free(ctx);
404 void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio)
406 /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
407 ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
408 ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/
410 ctx->nstates = 0;
411 nvgSave(ctx);
412 nvgReset(ctx);
414 nvg__setDevicePixelRatio(ctx, devicePixelRatio);
416 ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio);
418 ctx->drawCallCount = 0;
419 ctx->fillTriCount = 0;
420 ctx->strokeTriCount = 0;
421 ctx->textTriCount = 0;
424 void nvgCancelFrame(NVGcontext* ctx)
426 ctx->params.renderCancel(ctx->params.userPtr);
429 void nvgEndFrame(NVGcontext* ctx)
431 ctx->params.renderFlush(ctx->params.userPtr);
432 if (ctx->fontContext->fontImageIdx != 0) {
433 int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
434 int i, j, iw, ih;
435 // delete images that smaller than current one
436 if (fontImage == 0)
437 return;
438 nvgImageSize(ctx, fontImage, &iw, &ih);
439 for (i = j = 0; i < ctx->fontContext->fontImageIdx; i++) {
440 if (ctx->fontContext->fontImages[i] != 0) {
441 int nw, nh;
442 nvgImageSize(ctx, ctx->fontContext->fontImages[i], &nw, &nh);
443 if (nw < iw || nh < ih)
444 nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]);
445 else
446 ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[i];
449 // make current font image to first
450 ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[0];
451 ctx->fontContext->fontImages[0] = fontImage;
452 ctx->fontContext->fontImageIdx = 0;
453 // clear all images after j
454 for (i = j; i < NVG_MAX_FONTIMAGES; i++)
455 ctx->fontContext->fontImages[i] = 0;
459 NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b)
461 return nvgRGBA(r,g,b,255);
464 NVGcolor nvgRGBf(float r, float g, float b)
466 return nvgRGBAf(r,g,b,1.0f);
469 NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
471 NVGcolor color;
472 // Use longer initialization to suppress warning.
473 color.r = r / 255.0f;
474 color.g = g / 255.0f;
475 color.b = b / 255.0f;
476 color.a = a / 255.0f;
477 return color;
480 NVGcolor nvgRGBAf(float r, float g, float b, float a)
482 NVGcolor color;
483 // Use longer initialization to suppress warning.
484 color.r = r;
485 color.g = g;
486 color.b = b;
487 color.a = a;
488 return color;
491 NVGcolor nvgTransRGBA(NVGcolor c, unsigned char a)
493 c.a = a / 255.0f;
494 return c;
497 NVGcolor nvgTransRGBAf(NVGcolor c, float a)
499 c.a = a;
500 return c;
503 NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
505 int i;
506 float oneminu;
507 NVGcolor cint = {{{0}}};
509 u = nvg__clampf(u, 0.0f, 1.0f);
510 oneminu = 1.0f - u;
511 for( i = 0; i <4; i++ )
513 cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u;
516 return cint;
519 NVGcolor nvgHSL(float h, float s, float l)
521 return nvgHSLA(h,s,l,255);
524 static float nvg__hue(float h, float m1, float m2)
526 if (h < 0) h += 1;
527 if (h > 1) h -= 1;
528 if (h < 1.0f/6.0f)
529 return m1 + (m2 - m1) * h * 6.0f;
530 else if (h < 3.0f/6.0f)
531 return m2;
532 else if (h < 4.0f/6.0f)
533 return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f;
534 return m1;
537 NVGcolor nvgHSLA(float h, float s, float l, unsigned char a)
539 float m1, m2;
540 NVGcolor col;
541 h = nvg__modf(h, 1.0f);
542 if (h < 0.0f) h += 1.0f;
543 s = nvg__clampf(s, 0.0f, 1.0f);
544 l = nvg__clampf(l, 0.0f, 1.0f);
545 m2 = l <= 0.5f ? (l * (1 + s)) : (l + s - l * s);
546 m1 = 2 * l - m2;
547 col.r = nvg__clampf(nvg__hue(h + 1.0f/3.0f, m1, m2), 0.0f, 1.0f);
548 col.g = nvg__clampf(nvg__hue(h, m1, m2), 0.0f, 1.0f);
549 col.b = nvg__clampf(nvg__hue(h - 1.0f/3.0f, m1, m2), 0.0f, 1.0f);
550 col.a = a/255.0f;
551 return col;
554 void nvgTransformIdentity(float* t)
556 t[0] = 1.0f; t[1] = 0.0f;
557 t[2] = 0.0f; t[3] = 1.0f;
558 t[4] = 0.0f; t[5] = 0.0f;
561 void nvgTransformTranslate(float* t, float tx, float ty)
563 t[0] = 1.0f; t[1] = 0.0f;
564 t[2] = 0.0f; t[3] = 1.0f;
565 t[4] = tx; t[5] = ty;
568 void nvgTransformScale(float* t, float sx, float sy)
570 t[0] = sx; t[1] = 0.0f;
571 t[2] = 0.0f; t[3] = sy;
572 t[4] = 0.0f; t[5] = 0.0f;
575 void nvgTransformRotate(float* t, float a)
577 float cs = nvg__cosf(a), sn = nvg__sinf(a);
578 t[0] = cs; t[1] = sn;
579 t[2] = -sn; t[3] = cs;
580 t[4] = 0.0f; t[5] = 0.0f;
583 void nvgTransformSkewX(float* t, float a)
585 t[0] = 1.0f; t[1] = 0.0f;
586 t[2] = nvg__tanf(a); t[3] = 1.0f;
587 t[4] = 0.0f; t[5] = 0.0f;
590 void nvgTransformSkewY(float* t, float a)
592 t[0] = 1.0f; t[1] = nvg__tanf(a);
593 t[2] = 0.0f; t[3] = 1.0f;
594 t[4] = 0.0f; t[5] = 0.0f;
597 void nvgTransformMultiply(float* t, const float* s)
599 float t0 = t[0] * s[0] + t[1] * s[2];
600 float t2 = t[2] * s[0] + t[3] * s[2];
601 float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
602 t[1] = t[0] * s[1] + t[1] * s[3];
603 t[3] = t[2] * s[1] + t[3] * s[3];
604 t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
605 t[0] = t0;
606 t[2] = t2;
607 t[4] = t4;
610 void nvgTransformPremultiply(float* t, const float* s)
612 float s2[6];
613 memcpy(s2, s, sizeof(float)*6);
614 nvgTransformMultiply(s2, t);
615 memcpy(t, s2, sizeof(float)*6);
618 int nvgTransformInverse(float* inv, const float* t)
620 double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
621 if (det > -1e-6 && det < 1e-6) {
622 nvgTransformIdentity(inv);
623 return 0;
625 invdet = 1.0 / det;
626 inv[0] = (float)(t[3] * invdet);
627 inv[2] = (float)(-t[2] * invdet);
628 inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
629 inv[1] = (float)(-t[1] * invdet);
630 inv[3] = (float)(t[0] * invdet);
631 inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
632 return 1;
635 void nvgTransformPoint(float* dx, float* dy, const float* t, float sx, float sy)
637 *dx = sx*t[0] + sy*t[2] + t[4];
638 *dy = sx*t[1] + sy*t[3] + t[5];
641 float nvgDegToRad(float deg)
643 return deg / 180.0f * NVG_PI;
646 float nvgRadToDeg(float rad)
648 return rad / NVG_PI * 180.0f;
651 static void nvg__setPaintColor(NVGpaint* p, NVGcolor color)
653 memset(p, 0, sizeof(*p));
654 nvgTransformIdentity(p->xform);
655 p->radius = 0.0f;
656 p->feather = 1.0f;
657 p->innerColor = color;
658 p->outerColor = color;
662 // State handling
663 void nvgSave(NVGcontext* ctx)
665 if (ctx->nstates >= NVG_MAX_STATES)
666 return;
667 if (ctx->nstates > 0)
668 memcpy(&ctx->states[ctx->nstates], &ctx->states[ctx->nstates-1], sizeof(NVGstate));
669 ctx->nstates++;
672 void nvgRestore(NVGcontext* ctx)
674 if (ctx->nstates <= 1)
675 return;
676 ctx->nstates--;
679 void nvgReset(NVGcontext* ctx)
681 NVGstate* state = nvg__getState(ctx);
682 memset(state, 0, sizeof(*state));
684 nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255));
685 nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255));
686 state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER);
687 state->shapeAntiAlias = 1;
688 state->strokeWidth = 1.0f;
689 state->miterLimit = 10.0f;
690 state->lineCap = NVG_BUTT;
691 state->lineJoin = NVG_MITER;
692 state->tint = nvgRGBAf(1, 1, 1, 1);
693 nvgTransformIdentity(state->xform);
695 state->scissor.extent[0] = -1.0f;
696 state->scissor.extent[1] = -1.0f;
698 state->fontSize = 16.0f;
699 state->letterSpacing = 0.0f;
700 state->lineHeight = 1.0f;
701 state->fontBlur = 0.0f;
702 state->textAlign = NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE;
703 state->fontId = 0;
706 // State setting
707 void nvgShapeAntiAlias(NVGcontext* ctx, int enabled)
709 NVGstate* state = nvg__getState(ctx);
710 state->shapeAntiAlias = enabled;
713 void nvgStrokeWidth(NVGcontext* ctx, float width)
715 NVGstate* state = nvg__getState(ctx);
716 state->strokeWidth = width;
719 void nvgMiterLimit(NVGcontext* ctx, float limit)
721 NVGstate* state = nvg__getState(ctx);
722 state->miterLimit = limit;
725 void nvgLineCap(NVGcontext* ctx, int cap)
727 NVGstate* state = nvg__getState(ctx);
728 state->lineCap = cap;
731 void nvgLineJoin(NVGcontext* ctx, int join)
733 NVGstate* state = nvg__getState(ctx);
734 state->lineJoin = join;
737 void nvgGlobalAlpha(NVGcontext* ctx, float alpha)
739 NVGstate* state = nvg__getState(ctx);
740 state->tint.a = alpha;
743 void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint)
745 NVGstate* state = nvg__getState(ctx);
746 state->tint = tint;
749 NVGcolor nvgGetGlobalTint(NVGcontext* ctx)
751 NVGstate* state = nvg__getState(ctx);
752 return state->tint;
755 void nvgAlpha(NVGcontext* ctx, float alpha)
757 NVGstate* state = nvg__getState(ctx);
758 state->tint.a *= alpha;
761 void nvgTint(NVGcontext* ctx, NVGcolor tint)
763 NVGstate* state = nvg__getState(ctx);
764 int i;
765 for (i = 0; i < 4; i++)
766 state->tint.rgba[i] *= tint.rgba[i];
769 void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f)
771 NVGstate* state = nvg__getState(ctx);
772 float t[6] = { a, b, c, d, e, f };
773 nvgTransformPremultiply(state->xform, t);
776 void nvgResetTransform(NVGcontext* ctx)
778 NVGstate* state = nvg__getState(ctx);
779 nvgTransformIdentity(state->xform);
782 void nvgTranslate(NVGcontext* ctx, float x, float y)
784 NVGstate* state = nvg__getState(ctx);
785 float t[6];
786 nvgTransformTranslate(t, x,y);
787 nvgTransformPremultiply(state->xform, t);
790 void nvgRotate(NVGcontext* ctx, float angle)
792 NVGstate* state = nvg__getState(ctx);
793 float t[6];
794 nvgTransformRotate(t, angle);
795 nvgTransformPremultiply(state->xform, t);
798 void nvgSkewX(NVGcontext* ctx, float angle)
800 NVGstate* state = nvg__getState(ctx);
801 float t[6];
802 nvgTransformSkewX(t, angle);
803 nvgTransformPremultiply(state->xform, t);
806 void nvgSkewY(NVGcontext* ctx, float angle)
808 NVGstate* state = nvg__getState(ctx);
809 float t[6];
810 nvgTransformSkewY(t, angle);
811 nvgTransformPremultiply(state->xform, t);
814 void nvgScale(NVGcontext* ctx, float x, float y)
816 NVGstate* state = nvg__getState(ctx);
817 float t[6];
818 nvgTransformScale(t, x,y);
819 nvgTransformPremultiply(state->xform, t);
822 void nvgCurrentTransform(NVGcontext* ctx, float* xform)
824 NVGstate* state = nvg__getState(ctx);
825 if (xform == NULL) return;
826 memcpy(xform, state->xform, sizeof(float)*6);
829 void nvgStrokeColor(NVGcontext* ctx, NVGcolor color)
831 NVGstate* state = nvg__getState(ctx);
832 nvg__setPaintColor(&state->stroke, color);
835 void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint)
837 NVGstate* state = nvg__getState(ctx);
838 state->stroke = paint;
839 nvgTransformMultiply(state->stroke.xform, state->xform);
842 void nvgFillColor(NVGcontext* ctx, NVGcolor color)
844 NVGstate* state = nvg__getState(ctx);
845 nvg__setPaintColor(&state->fill, color);
848 void nvgFillPaint(NVGcontext* ctx, NVGpaint paint)
850 NVGstate* state = nvg__getState(ctx);
851 state->fill = paint;
852 nvgTransformMultiply(state->fill.xform, state->xform);
855 #ifndef NVG_NO_STB
856 int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
858 int w, h, n, image;
859 unsigned char* img;
860 stbi_set_unpremultiply_on_load(1);
861 stbi_convert_iphone_png_to_rgb(1);
862 img = stbi_load(filename, &w, &h, &n, 4);
863 if (img == NULL) {
864 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
865 return 0;
867 image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
868 stbi_image_free(img);
869 return image;
872 int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata)
874 int w, h, n, image;
875 unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
876 if (img == NULL) {
877 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
878 return 0;
880 image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
881 stbi_image_free(img);
882 return image;
884 #endif
886 int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, NVGtexture format, const unsigned char* data)
888 return ctx->params.renderCreateTexture(ctx->params.userPtr, format, w, h, imageFlags, data);
891 int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data)
893 return nvgCreateImageRaw(ctx, w, h, imageFlags, NVG_TEXTURE_RGBA, data);
896 void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data)
898 int w, h;
899 ctx->params.renderGetTextureSize(ctx->params.userPtr, image, &w, &h);
900 ctx->params.renderUpdateTexture(ctx->params.userPtr, image, 0,0, w,h, data);
903 void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h)
905 ctx->params.renderGetTextureSize(ctx->params.userPtr, image, w, h);
908 void nvgDeleteImage(NVGcontext* ctx, int image)
910 ctx->params.renderDeleteTexture(ctx->params.userPtr, image);
913 NVGpaint nvgLinearGradient(NVGcontext* ctx,
914 float sx, float sy, float ex, float ey,
915 NVGcolor icol, NVGcolor ocol)
917 NVGpaint p;
918 float dx, dy, d;
919 const float large = 1e5;
920 NVG_NOTUSED(ctx);
921 memset(&p, 0, sizeof(p));
923 // Calculate transform aligned to the line
924 dx = ex - sx;
925 dy = ey - sy;
926 d = sqrtf(dx*dx + dy*dy);
927 if (d > 0.0001f) {
928 dx /= d;
929 dy /= d;
930 } else {
931 dx = 0;
932 dy = 1;
935 p.xform[0] = dy; p.xform[1] = -dx;
936 p.xform[2] = dx; p.xform[3] = dy;
937 p.xform[4] = sx - dx*large; p.xform[5] = sy - dy*large;
939 p.extent[0] = large;
940 p.extent[1] = large + d*0.5f;
942 p.radius = 0.0f;
944 p.feather = nvg__maxf(1.0f, d);
946 p.innerColor = icol;
947 p.outerColor = ocol;
949 return p;
952 NVGpaint nvgRadialGradient(NVGcontext* ctx,
953 float cx, float cy, float inr, float outr,
954 NVGcolor icol, NVGcolor ocol)
956 NVGpaint p;
957 float r = (inr+outr)*0.5f;
958 float f = (outr-inr);
959 NVG_NOTUSED(ctx);
960 memset(&p, 0, sizeof(p));
962 nvgTransformIdentity(p.xform);
963 p.xform[4] = cx;
964 p.xform[5] = cy;
966 p.extent[0] = r;
967 p.extent[1] = r;
969 p.radius = r;
971 p.feather = nvg__maxf(1.0f, f);
973 p.innerColor = icol;
974 p.outerColor = ocol;
976 return p;
979 NVGpaint nvgBoxGradient(NVGcontext* ctx,
980 float x, float y, float w, float h, float r, float f,
981 NVGcolor icol, NVGcolor ocol)
983 NVGpaint p;
984 NVG_NOTUSED(ctx);
985 memset(&p, 0, sizeof(p));
987 nvgTransformIdentity(p.xform);
988 p.xform[4] = x+w*0.5f;
989 p.xform[5] = y+h*0.5f;
991 p.extent[0] = w*0.5f;
992 p.extent[1] = h*0.5f;
994 p.radius = r;
996 p.feather = nvg__maxf(1.0f, f);
998 p.innerColor = icol;
999 p.outerColor = ocol;
1001 return p;
1005 NVGpaint nvgImagePattern(NVGcontext* ctx,
1006 float cx, float cy, float w, float h, float angle,
1007 int image, float alpha)
1009 NVGpaint p;
1010 NVG_NOTUSED(ctx);
1011 memset(&p, 0, sizeof(p));
1013 nvgTransformRotate(p.xform, angle);
1014 p.xform[4] = cx;
1015 p.xform[5] = cy;
1017 p.extent[0] = w;
1018 p.extent[1] = h;
1020 p.image = image;
1022 p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha);
1024 return p;
1027 // Scissoring
1028 void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h)
1030 NVGstate* state = nvg__getState(ctx);
1032 w = nvg__maxf(0.0f, w);
1033 h = nvg__maxf(0.0f, h);
1035 nvgTransformIdentity(state->scissor.xform);
1036 state->scissor.xform[4] = x+w*0.5f;
1037 state->scissor.xform[5] = y+h*0.5f;
1038 nvgTransformMultiply(state->scissor.xform, state->xform);
1040 state->scissor.extent[0] = w*0.5f;
1041 state->scissor.extent[1] = h*0.5f;
1044 static void nvg__isectRects(float* dst,
1045 float ax, float ay, float aw, float ah,
1046 float bx, float by, float bw, float bh)
1048 float minx = nvg__maxf(ax, bx);
1049 float miny = nvg__maxf(ay, by);
1050 float maxx = nvg__minf(ax+aw, bx+bw);
1051 float maxy = nvg__minf(ay+ah, by+bh);
1052 dst[0] = minx;
1053 dst[1] = miny;
1054 dst[2] = nvg__maxf(0.0f, maxx - minx);
1055 dst[3] = nvg__maxf(0.0f, maxy - miny);
1058 void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h)
1060 NVGstate* state = nvg__getState(ctx);
1061 float pxform[6], invxorm[6];
1062 float rect[4];
1063 float ex, ey, tex, tey;
1065 // If no previous scissor has been set, set the scissor as current scissor.
1066 if (state->scissor.extent[0] < 0) {
1067 nvgScissor(ctx, x, y, w, h);
1068 return;
1071 // Transform the current scissor rect into current transform space.
1072 // If there is difference in rotation, this will be approximation.
1073 memcpy(pxform, state->scissor.xform, sizeof(float)*6);
1074 ex = state->scissor.extent[0];
1075 ey = state->scissor.extent[1];
1076 nvgTransformInverse(invxorm, state->xform);
1077 nvgTransformMultiply(pxform, invxorm);
1078 tex = ex*nvg__absf(pxform[0]) + ey*nvg__absf(pxform[2]);
1079 tey = ex*nvg__absf(pxform[1]) + ey*nvg__absf(pxform[3]);
1081 // Intersect rects.
1082 nvg__isectRects(rect, pxform[4]-tex,pxform[5]-tey,tex*2,tey*2, x,y,w,h);
1084 nvgScissor(ctx, rect[0], rect[1], rect[2], rect[3]);
1087 void nvgResetScissor(NVGcontext* ctx)
1089 NVGstate* state = nvg__getState(ctx);
1090 memset(state->scissor.xform, 0, sizeof(state->scissor.xform));
1091 state->scissor.extent[0] = -1.0f;
1092 state->scissor.extent[1] = -1.0f;
1095 // Global composite operation.
1096 void nvgGlobalCompositeOperation(NVGcontext* ctx, int op)
1098 NVGstate* state = nvg__getState(ctx);
1099 state->compositeOperation = nvg__compositeOperationState(op);
1102 void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor)
1104 nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor);
1107 void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha)
1109 NVGcompositeOperationState op;
1110 op.srcRGB = srcRGB;
1111 op.dstRGB = dstRGB;
1112 op.srcAlpha = srcAlpha;
1113 op.dstAlpha = dstAlpha;
1115 NVGstate* state = nvg__getState(ctx);
1116 state->compositeOperation = op;
1119 static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
1121 float dx = x2 - x1;
1122 float dy = y2 - y1;
1123 return dx*dx + dy*dy < tol*tol;
1126 static float nvg__distPtSeg(float x, float y, float px, float py, float qx, float qy)
1128 float pqx, pqy, dx, dy, d, t;
1129 pqx = qx-px;
1130 pqy = qy-py;
1131 dx = x-px;
1132 dy = y-py;
1133 d = pqx*pqx + pqy*pqy;
1134 t = pqx*dx + pqy*dy;
1135 if (d > 0) t /= d;
1136 if (t < 0) t = 0;
1137 else if (t > 1) t = 1;
1138 dx = px + t*pqx - x;
1139 dy = py + t*pqy - y;
1140 return dx*dx + dy*dy;
1143 static void nvg__appendCommands(NVGcontext* ctx, float* vals, int nvals)
1145 NVGstate* state = nvg__getState(ctx);
1146 int i;
1148 if (ctx->ncommands+nvals > ctx->ccommands) {
1149 float* commands;
1150 int ccommands = ctx->ncommands+nvals + ctx->ccommands/2;
1151 commands = (float*)realloc(ctx->commands, sizeof(float)*ccommands);
1152 if (commands == NULL) return;
1153 ctx->commands = commands;
1154 ctx->ccommands = ccommands;
1157 if ((int)vals[0] != NVG_CLOSE && (int)vals[0] != NVG_WINDING) {
1158 ctx->commandx = vals[nvals-2];
1159 ctx->commandy = vals[nvals-1];
1162 // transform commands
1163 i = 0;
1164 while (i < nvals) {
1165 int cmd = (int)vals[i];
1166 switch (cmd) {
1167 case NVG_MOVETO:
1168 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1169 i += 3;
1170 break;
1171 case NVG_LINETO:
1172 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1173 i += 3;
1174 break;
1175 case NVG_BEZIERTO:
1176 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1177 nvgTransformPoint(&vals[i+3],&vals[i+4], state->xform, vals[i+3],vals[i+4]);
1178 nvgTransformPoint(&vals[i+5],&vals[i+6], state->xform, vals[i+5],vals[i+6]);
1179 i += 7;
1180 break;
1181 case NVG_CLOSE:
1182 i++;
1183 break;
1184 case NVG_WINDING:
1185 i += 2;
1186 break;
1187 default:
1188 i++;
1192 memcpy(&ctx->commands[ctx->ncommands], vals, nvals*sizeof(float));
1194 ctx->ncommands += nvals;
1198 static void nvg__clearPathCache(NVGcontext* ctx)
1200 ctx->cache->npoints = 0;
1201 ctx->cache->npaths = 0;
1204 static NVGpath* nvg__lastPath(NVGcontext* ctx)
1206 if (ctx->cache->npaths > 0)
1207 return &ctx->cache->paths[ctx->cache->npaths-1];
1208 return NULL;
1211 static void nvg__addPath(NVGcontext* ctx)
1213 NVGpath* path;
1214 if (ctx->cache->npaths+1 > ctx->cache->cpaths) {
1215 NVGpath* paths;
1216 int cpaths = ctx->cache->npaths+1 + ctx->cache->cpaths/2;
1217 paths = (NVGpath*)realloc(ctx->cache->paths, sizeof(NVGpath)*cpaths);
1218 if (paths == NULL) return;
1219 ctx->cache->paths = paths;
1220 ctx->cache->cpaths = cpaths;
1222 path = &ctx->cache->paths[ctx->cache->npaths];
1223 memset(path, 0, sizeof(*path));
1224 path->first = ctx->cache->npoints;
1225 path->winding = NVG_CCW;
1227 ctx->cache->npaths++;
1230 static NVGpoint* nvg__lastPoint(NVGcontext* ctx)
1232 if (ctx->cache->npoints > 0)
1233 return &ctx->cache->points[ctx->cache->npoints-1];
1234 return NULL;
1237 static void nvg__addPoint(NVGcontext* ctx, float x, float y, int flags)
1239 NVGpath* path = nvg__lastPath(ctx);
1240 NVGpoint* pt;
1241 if (path == NULL) return;
1243 if (path->count > 0 && ctx->cache->npoints > 0) {
1244 pt = nvg__lastPoint(ctx);
1245 if (nvg__ptEquals(pt->x,pt->y, x,y, ctx->distTol)) {
1246 pt->flags |= flags;
1247 return;
1251 if (ctx->cache->npoints+1 > ctx->cache->cpoints) {
1252 NVGpoint* points;
1253 int cpoints = ctx->cache->npoints+1 + ctx->cache->cpoints/2;
1254 points = (NVGpoint*)realloc(ctx->cache->points, sizeof(NVGpoint)*cpoints);
1255 if (points == NULL) return;
1256 ctx->cache->points = points;
1257 ctx->cache->cpoints = cpoints;
1260 pt = &ctx->cache->points[ctx->cache->npoints];
1261 memset(pt, 0, sizeof(*pt));
1262 pt->x = x;
1263 pt->y = y;
1264 pt->flags = (unsigned char)flags;
1266 ctx->cache->npoints++;
1267 path->count++;
1270 static void nvg__closePath(NVGcontext* ctx)
1272 NVGpath* path = nvg__lastPath(ctx);
1273 if (path == NULL) return;
1274 path->closed = 1;
1277 static void nvg__pathWinding(NVGcontext* ctx, int winding)
1279 NVGpath* path = nvg__lastPath(ctx);
1280 if (path == NULL) return;
1281 path->winding = winding;
1284 static float nvg__getAverageScale(float *t)
1286 float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
1287 float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
1288 return (sx + sy) * 0.5f;
1291 static NVGvertex* nvg__allocTempVerts(NVGcontext* ctx, int nverts)
1293 if (nverts > ctx->cache->cverts) {
1294 NVGvertex* verts;
1295 int cverts = (nverts + 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly.
1296 verts = (NVGvertex*)realloc(ctx->cache->verts, sizeof(NVGvertex)*cverts);
1297 if (verts == NULL) return NULL;
1298 ctx->cache->verts = verts;
1299 ctx->cache->cverts = cverts;
1302 return ctx->cache->verts;
1305 static float nvg__triarea2(float ax, float ay, float bx, float by, float cx, float cy)
1307 float abx = bx - ax;
1308 float aby = by - ay;
1309 float acx = cx - ax;
1310 float acy = cy - ay;
1311 return acx*aby - abx*acy;
1314 static float nvg__polyArea(NVGpoint* pts, int npts)
1316 int i;
1317 float area = 0;
1318 for (i = 2; i < npts; i++) {
1319 NVGpoint* a = &pts[0];
1320 NVGpoint* b = &pts[i-1];
1321 NVGpoint* c = &pts[i];
1322 area += nvg__triarea2(a->x,a->y, b->x,b->y, c->x,c->y);
1324 return area * 0.5f;
1327 static void nvg__polyReverse(NVGpoint* pts, int npts)
1329 NVGpoint tmp;
1330 int i = 0, j = npts-1;
1331 while (i < j) {
1332 tmp = pts[i];
1333 pts[i] = pts[j];
1334 pts[j] = tmp;
1335 i++;
1336 j--;
1341 static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
1343 vtx->x = x;
1344 vtx->y = y;
1345 vtx->u = u;
1346 vtx->v = v;
1349 static void nvg__tesselateBezier(NVGcontext* ctx,
1350 float x1, float y1, float x2, float y2,
1351 float x3, float y3, float x4, float y4,
1352 int level, int type)
1354 float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
1355 float dx,dy,d2,d3;
1357 if (level > 10) return;
1359 x12 = (x1+x2)*0.5f;
1360 y12 = (y1+y2)*0.5f;
1361 x23 = (x2+x3)*0.5f;
1362 y23 = (y2+y3)*0.5f;
1363 x34 = (x3+x4)*0.5f;
1364 y34 = (y3+y4)*0.5f;
1365 x123 = (x12+x23)*0.5f;
1366 y123 = (y12+y23)*0.5f;
1368 dx = x4 - x1;
1369 dy = y4 - y1;
1370 d2 = nvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
1371 d3 = nvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
1373 if ((d2 + d3)*(d2 + d3) < ctx->tessTol * (dx*dx + dy*dy)) {
1374 nvg__addPoint(ctx, x4, y4, type);
1375 return;
1378 /* if (nvg__absf(x1+x3-x2-x2) + nvg__absf(y1+y3-y2-y2) + nvg__absf(x2+x4-x3-x3) + nvg__absf(y2+y4-y3-y3) < ctx->tessTol) {
1379 nvg__addPoint(ctx, x4, y4, type);
1380 return;
1383 x234 = (x23+x34)*0.5f;
1384 y234 = (y23+y34)*0.5f;
1385 x1234 = (x123+x234)*0.5f;
1386 y1234 = (y123+y234)*0.5f;
1388 nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
1389 nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
1392 static void nvg__flattenPaths(NVGcontext* ctx)
1394 NVGpathCache* cache = ctx->cache;
1395 // NVGstate* state = nvg__getState(ctx);
1396 NVGpoint* last;
1397 NVGpoint* p0;
1398 NVGpoint* p1;
1399 NVGpoint* pts;
1400 NVGpath* path;
1401 int i, j;
1402 float* cp1;
1403 float* cp2;
1404 float* p;
1405 float area;
1407 if (cache->npaths > 0)
1408 return;
1410 // Flatten
1411 i = 0;
1412 while (i < ctx->ncommands) {
1413 int cmd = (int)ctx->commands[i];
1414 switch (cmd) {
1415 case NVG_MOVETO:
1416 nvg__addPath(ctx);
1417 p = &ctx->commands[i+1];
1418 nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER);
1419 i += 3;
1420 break;
1421 case NVG_LINETO:
1422 p = &ctx->commands[i+1];
1423 nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER);
1424 i += 3;
1425 break;
1426 case NVG_BEZIERTO:
1427 last = nvg__lastPoint(ctx);
1428 if (last != NULL) {
1429 cp1 = &ctx->commands[i+1];
1430 cp2 = &ctx->commands[i+3];
1431 p = &ctx->commands[i+5];
1432 nvg__tesselateBezier(ctx, last->x,last->y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, NVG_PT_CORNER);
1434 i += 7;
1435 break;
1436 case NVG_CLOSE:
1437 nvg__closePath(ctx);
1438 i++;
1439 break;
1440 case NVG_WINDING:
1441 nvg__pathWinding(ctx, (int)ctx->commands[i+1]);
1442 i += 2;
1443 break;
1444 default:
1445 i++;
1449 cache->bounds[0] = cache->bounds[1] = 1e6f;
1450 cache->bounds[2] = cache->bounds[3] = -1e6f;
1452 // Calculate the direction and length of line segments.
1453 for (j = 0; j < cache->npaths; j++) {
1454 path = &cache->paths[j];
1455 pts = &cache->points[path->first];
1457 // If the first and last points are the same, remove the last, mark as closed path.
1458 p0 = &pts[path->count-1];
1459 p1 = &pts[0];
1460 if (nvg__ptEquals(p0->x,p0->y, p1->x,p1->y, ctx->distTol)) {
1461 path->count--;
1462 p0 = &pts[path->count-1];
1463 path->closed = 1;
1466 // Enforce winding.
1467 if (path->count > 2) {
1468 area = nvg__polyArea(pts, path->count);
1469 if (path->winding == NVG_CCW && area < 0.0f)
1470 nvg__polyReverse(pts, path->count);
1471 if (path->winding == NVG_CW && area > 0.0f)
1472 nvg__polyReverse(pts, path->count);
1475 for(i = 0; i < path->count; i++) {
1476 // Calculate segment direction and length
1477 p0->dx = p1->x - p0->x;
1478 p0->dy = p1->y - p0->y;
1479 p0->len = nvg__normalize(&p0->dx, &p0->dy);
1480 // Update bounds
1481 cache->bounds[0] = nvg__minf(cache->bounds[0], p0->x);
1482 cache->bounds[1] = nvg__minf(cache->bounds[1], p0->y);
1483 cache->bounds[2] = nvg__maxf(cache->bounds[2], p0->x);
1484 cache->bounds[3] = nvg__maxf(cache->bounds[3], p0->y);
1485 // Advance
1486 p0 = p1++;
1491 static int nvg__curveDivs(float r, float arc, float tol)
1493 float da = acosf(r / (r + tol)) * 2.0f;
1494 return nvg__maxi(2, (int)ceilf(arc / da));
1497 static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w,
1498 float* x0, float* y0, float* x1, float* y1)
1500 if (bevel) {
1501 *x0 = p1->x + p0->dy * w;
1502 *y0 = p1->y - p0->dx * w;
1503 *x1 = p1->x + p1->dy * w;
1504 *y1 = p1->y - p1->dx * w;
1505 } else {
1506 *x0 = p1->x + p1->dmx * w;
1507 *y0 = p1->y + p1->dmy * w;
1508 *x1 = p1->x + p1->dmx * w;
1509 *y1 = p1->y + p1->dmy * w;
1513 static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
1514 float lw, float rw, float lu, float ru, int ncap,
1515 float fringe)
1517 int i, n;
1518 float dlx0 = p0->dy;
1519 float dly0 = -p0->dx;
1520 float dlx1 = p1->dy;
1521 float dly1 = -p1->dx;
1522 NVG_NOTUSED(fringe);
1524 if (p1->flags & NVG_PT_LEFT) {
1525 float lx0,ly0,lx1,ly1,a0,a1;
1526 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1);
1527 a0 = atan2f(-dly0, -dlx0);
1528 a1 = atan2f(-dly1, -dlx1);
1529 if (a1 > a0) a1 -= NVG_PI*2;
1531 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1532 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1534 n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap);
1535 for (i = 0; i < n; i++) {
1536 float u = i/(float)(n-1);
1537 float a = a0 + u*(a1-a0);
1538 float rx = p1->x + cosf(a) * rw;
1539 float ry = p1->y + sinf(a) * rw;
1540 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1541 nvg__vset(dst, rx, ry, ru,1); dst++;
1544 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1545 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1547 } else {
1548 float rx0,ry0,rx1,ry1,a0,a1;
1549 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1);
1550 a0 = atan2f(dly0, dlx0);
1551 a1 = atan2f(dly1, dlx1);
1552 if (a1 < a0) a1 += NVG_PI*2;
1554 nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1); dst++;
1555 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1557 n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap);
1558 for (i = 0; i < n; i++) {
1559 float u = i/(float)(n-1);
1560 float a = a0 + u*(a1-a0);
1561 float lx = p1->x + cosf(a) * lw;
1562 float ly = p1->y + sinf(a) * lw;
1563 nvg__vset(dst, lx, ly, lu,1); dst++;
1564 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1567 nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu,1); dst++;
1568 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1571 return dst;
1574 static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
1575 float lw, float rw, float lu, float ru, float fringe)
1577 float rx0,ry0,rx1,ry1;
1578 float lx0,ly0,lx1,ly1;
1579 float dlx0 = p0->dy;
1580 float dly0 = -p0->dx;
1581 float dlx1 = p1->dy;
1582 float dly1 = -p1->dx;
1583 NVG_NOTUSED(fringe);
1585 if (p1->flags & NVG_PT_LEFT) {
1586 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1);
1588 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1589 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1591 if (p1->flags & NVG_PT_BEVEL) {
1592 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1593 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1595 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1596 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1597 } else {
1598 rx0 = p1->x - p1->dmx * rw;
1599 ry0 = p1->y - p1->dmy * rw;
1601 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1602 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1604 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1605 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1607 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1608 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1611 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1612 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1614 } else {
1615 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1);
1617 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1618 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1620 if (p1->flags & NVG_PT_BEVEL) {
1621 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1622 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1624 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1625 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1626 } else {
1627 lx0 = p1->x + p1->dmx * lw;
1628 ly0 = p1->y + p1->dmy * lw;
1630 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1631 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1633 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1634 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1636 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1637 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1640 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1641 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1644 return dst;
1647 static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p,
1648 float dx, float dy, float w, float d,
1649 float aa, float u0, float u1)
1651 float px = p->x - dx*d;
1652 float py = p->y - dy*d;
1653 float dlx = dy;
1654 float dly = -dx;
1655 nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++;
1656 nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++;
1657 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1658 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1659 return dst;
1662 static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p,
1663 float dx, float dy, float w, float d,
1664 float aa, float u0, float u1)
1666 float px = p->x + dx*d;
1667 float py = p->y + dy*d;
1668 float dlx = dy;
1669 float dly = -dx;
1670 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1671 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1672 nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++;
1673 nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++;
1674 return dst;
1678 static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
1679 float dx, float dy, float w, int ncap,
1680 float aa, float u0, float u1)
1682 int i;
1683 float px = p->x;
1684 float py = p->y;
1685 float dlx = dy;
1686 float dly = -dx;
1687 NVG_NOTUSED(aa);
1688 for (i = 0; i < ncap; i++) {
1689 float a = i/(float)(ncap-1)*NVG_PI;
1690 float ax = cosf(a) * w, ay = sinf(a) * w;
1691 nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++;
1692 nvg__vset(dst, px, py, 0.5f,1); dst++;
1694 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1695 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1696 return dst;
1699 static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
1700 float dx, float dy, float w, int ncap,
1701 float aa, float u0, float u1)
1703 int i;
1704 float px = p->x;
1705 float py = p->y;
1706 float dlx = dy;
1707 float dly = -dx;
1708 NVG_NOTUSED(aa);
1709 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1710 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1711 for (i = 0; i < ncap; i++) {
1712 float a = i/(float)(ncap-1)*NVG_PI;
1713 float ax = cosf(a) * w, ay = sinf(a) * w;
1714 nvg__vset(dst, px, py, 0.5f,1); dst++;
1715 nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++;
1717 return dst;
1721 static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float miterLimit)
1723 NVGpathCache* cache = ctx->cache;
1724 int i, j;
1725 float iw = 0.0f;
1727 if (w > 0.0f) iw = 1.0f / w;
1729 // Calculate which joins needs extra vertices to append, and gather vertex count.
1730 for (i = 0; i < cache->npaths; i++) {
1731 NVGpath* path = &cache->paths[i];
1732 NVGpoint* pts = &cache->points[path->first];
1733 NVGpoint* p0 = &pts[path->count-1];
1734 NVGpoint* p1 = &pts[0];
1735 int nleft = 0;
1737 path->nbevel = 0;
1739 for (j = 0; j < path->count; j++) {
1740 float dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
1741 dlx0 = p0->dy;
1742 dly0 = -p0->dx;
1743 dlx1 = p1->dy;
1744 dly1 = -p1->dx;
1745 // Calculate extrusions
1746 p1->dmx = (dlx0 + dlx1) * 0.5f;
1747 p1->dmy = (dly0 + dly1) * 0.5f;
1748 dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
1749 if (dmr2 > 0.000001f) {
1750 float scale = 1.0f / dmr2;
1751 if (scale > 600.0f) {
1752 scale = 600.0f;
1754 p1->dmx *= scale;
1755 p1->dmy *= scale;
1758 // Clear flags, but keep the corner.
1759 p1->flags = (p1->flags & NVG_PT_CORNER) ? NVG_PT_CORNER : 0;
1761 // Keep track of left turns.
1762 cross = p1->dx * p0->dy - p0->dx * p1->dy;
1763 if (cross > 0.0f) {
1764 nleft++;
1765 p1->flags |= NVG_PT_LEFT;
1768 // Calculate if we should use bevel or miter for inner join.
1769 limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw);
1770 if ((dmr2 * limit*limit) < 1.0f)
1771 p1->flags |= NVG_PR_INNERBEVEL;
1773 // Check to see if the corner needs to be beveled.
1774 if (p1->flags & NVG_PT_CORNER) {
1775 if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
1776 p1->flags |= NVG_PT_BEVEL;
1780 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0)
1781 path->nbevel++;
1783 p0 = p1++;
1786 path->convex = (nleft == path->count) ? 1 : 0;
1791 static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit)
1793 NVGpathCache* cache = ctx->cache;
1794 NVGvertex* verts;
1795 NVGvertex* dst;
1796 int cverts, i, j;
1797 float aa = fringe;//ctx->fringeWidth;
1798 float u0 = 0.0f, u1 = 1.0f;
1799 int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle.
1801 w += aa * 0.5f;
1803 // Disable the gradient used for antialiasing when antialiasing is not used.
1804 if (aa == 0.0f) {
1805 u0 = 0.5f;
1806 u1 = 0.5f;
1809 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
1811 // Calculate max vertex usage.
1812 cverts = 0;
1813 for (i = 0; i < cache->npaths; i++) {
1814 NVGpath* path = &cache->paths[i];
1815 int loop = (path->closed == 0) ? 0 : 1;
1816 if (lineJoin == NVG_ROUND)
1817 cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop
1818 else
1819 cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop
1820 if (loop == 0) {
1821 // space for caps
1822 if (lineCap == NVG_ROUND) {
1823 cverts += (ncap*2 + 2)*2;
1824 } else {
1825 cverts += (3+3)*2;
1830 verts = nvg__allocTempVerts(ctx, cverts);
1831 if (verts == NULL) return 0;
1833 for (i = 0; i < cache->npaths; i++) {
1834 NVGpath* path = &cache->paths[i];
1835 NVGpoint* pts = &cache->points[path->first];
1836 NVGpoint* p0;
1837 NVGpoint* p1;
1838 int s, e, loop;
1839 float dx, dy;
1841 path->fill = 0;
1842 path->nfill = 0;
1844 // Calculate fringe or stroke
1845 loop = (path->closed == 0) ? 0 : 1;
1846 dst = verts;
1847 path->stroke = dst;
1849 if (loop) {
1850 // Looping
1851 p0 = &pts[path->count-1];
1852 p1 = &pts[0];
1853 s = 0;
1854 e = path->count;
1855 } else {
1856 // Add cap
1857 p0 = &pts[0];
1858 p1 = &pts[1];
1859 s = 1;
1860 e = path->count-1;
1863 if (loop == 0) {
1864 // Add cap
1865 dx = p1->x - p0->x;
1866 dy = p1->y - p0->y;
1867 nvg__normalize(&dx, &dy);
1868 if (lineCap == NVG_BUTT)
1869 dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1);
1870 else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
1871 dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1);
1872 else if (lineCap == NVG_ROUND)
1873 dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1);
1876 for (j = s; j < e; ++j) {
1877 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
1878 if (lineJoin == NVG_ROUND) {
1879 dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa);
1880 } else {
1881 dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa);
1883 } else {
1884 nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++;
1885 nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++;
1887 p0 = p1++;
1890 if (loop) {
1891 // Loop it
1892 nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++;
1893 nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++;
1894 } else {
1895 // Add cap
1896 dx = p1->x - p0->x;
1897 dy = p1->y - p0->y;
1898 nvg__normalize(&dx, &dy);
1899 if (lineCap == NVG_BUTT)
1900 dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1);
1901 else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
1902 dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1);
1903 else if (lineCap == NVG_ROUND)
1904 dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1);
1907 path->nstroke = (int)(dst - verts);
1909 verts = dst;
1912 return 1;
1915 static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLimit)
1917 NVGpathCache* cache = ctx->cache;
1918 NVGvertex* verts;
1919 NVGvertex* dst;
1920 int cverts, convex, i, j;
1921 float aa = ctx->fringeWidth;
1922 int fringe = w > 0.0f;
1924 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
1926 // Calculate max vertex usage.
1927 cverts = 0;
1928 for (i = 0; i < cache->npaths; i++) {
1929 NVGpath* path = &cache->paths[i];
1930 cverts += path->count + path->nbevel + 1;
1931 if (fringe)
1932 cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop
1935 verts = nvg__allocTempVerts(ctx, cverts);
1936 if (verts == NULL) return 0;
1938 convex = cache->npaths == 1 && cache->paths[0].convex;
1940 for (i = 0; i < cache->npaths; i++) {
1941 NVGpath* path = &cache->paths[i];
1942 NVGpoint* pts = &cache->points[path->first];
1943 NVGpoint* p0;
1944 NVGpoint* p1;
1945 float rw, lw, woff;
1946 float ru, lu;
1948 // Calculate shape vertices.
1949 woff = 0.5f*aa;
1950 dst = verts;
1951 path->fill = dst;
1953 if (fringe) {
1954 // Looping
1955 p0 = &pts[path->count-1];
1956 p1 = &pts[0];
1957 for (j = 0; j < path->count; ++j) {
1958 if (p1->flags & NVG_PT_BEVEL) {
1959 float dlx0 = p0->dy;
1960 float dly0 = -p0->dx;
1961 float dlx1 = p1->dy;
1962 float dly1 = -p1->dx;
1963 if (p1->flags & NVG_PT_LEFT) {
1964 float lx = p1->x + p1->dmx * woff;
1965 float ly = p1->y + p1->dmy * woff;
1966 nvg__vset(dst, lx, ly, 0.5f,1); dst++;
1967 } else {
1968 float lx0 = p1->x + dlx0 * woff;
1969 float ly0 = p1->y + dly0 * woff;
1970 float lx1 = p1->x + dlx1 * woff;
1971 float ly1 = p1->y + dly1 * woff;
1972 nvg__vset(dst, lx0, ly0, 0.5f,1); dst++;
1973 nvg__vset(dst, lx1, ly1, 0.5f,1); dst++;
1975 } else {
1976 nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1); dst++;
1978 p0 = p1++;
1980 } else {
1981 for (j = 0; j < path->count; ++j) {
1982 nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1);
1983 dst++;
1987 path->nfill = (int)(dst - verts);
1988 verts = dst;
1990 // Calculate fringe
1991 if (fringe) {
1992 lw = w + woff;
1993 rw = w - woff;
1994 lu = 0;
1995 ru = 1;
1996 dst = verts;
1997 path->stroke = dst;
1999 // Create only half a fringe for convex shapes so that
2000 // the shape can be rendered without stenciling.
2001 if (convex) {
2002 lw = woff; // This should generate the same vertex as fill inset above.
2003 lu = 0.5f; // Set outline fade at middle.
2006 // Looping
2007 p0 = &pts[path->count-1];
2008 p1 = &pts[0];
2010 for (j = 0; j < path->count; ++j) {
2011 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
2012 dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth);
2013 } else {
2014 nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1); dst++;
2015 nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1); dst++;
2017 p0 = p1++;
2020 // Loop it
2021 nvg__vset(dst, verts[0].x, verts[0].y, lu,1); dst++;
2022 nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++;
2024 path->nstroke = (int)(dst - verts);
2025 verts = dst;
2026 } else {
2027 path->stroke = NULL;
2028 path->nstroke = 0;
2032 return 1;
2036 // Draw
2037 void nvgBeginPath(NVGcontext* ctx)
2039 ctx->ncommands = 0;
2040 nvg__clearPathCache(ctx);
2043 void nvgMoveTo(NVGcontext* ctx, float x, float y)
2045 float vals[] = { NVG_MOVETO, x, y };
2046 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2049 void nvgLineTo(NVGcontext* ctx, float x, float y)
2051 float vals[] = { NVG_LINETO, x, y };
2052 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2055 void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y)
2057 float vals[] = { NVG_BEZIERTO, c1x, c1y, c2x, c2y, x, y };
2058 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2061 void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y)
2063 float x0 = ctx->commandx;
2064 float y0 = ctx->commandy;
2065 float vals[] = { NVG_BEZIERTO,
2066 x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0),
2067 x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y),
2068 x, y };
2069 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2072 void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius)
2074 float x0 = ctx->commandx;
2075 float y0 = ctx->commandy;
2076 float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
2077 int dir;
2079 if (ctx->ncommands == 0) {
2080 return;
2083 // Handle degenerate cases.
2084 if (nvg__ptEquals(x0,y0, x1,y1, ctx->distTol) ||
2085 nvg__ptEquals(x1,y1, x2,y2, ctx->distTol) ||
2086 nvg__distPtSeg(x1,y1, x0,y0, x2,y2) < ctx->distTol*ctx->distTol ||
2087 radius < ctx->distTol) {
2088 nvgLineTo(ctx, x1,y1);
2089 return;
2092 // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
2093 dx0 = x0-x1;
2094 dy0 = y0-y1;
2095 dx1 = x2-x1;
2096 dy1 = y2-y1;
2097 nvg__normalize(&dx0,&dy0);
2098 nvg__normalize(&dx1,&dy1);
2099 a = nvg__acosf(dx0*dx1 + dy0*dy1);
2100 d = radius / nvg__tanf(a/2.0f);
2102 // printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
2104 if (d > 10000.0f) {
2105 nvgLineTo(ctx, x1,y1);
2106 return;
2109 if (nvg__cross(dx0,dy0, dx1,dy1) > 0.0f) {
2110 cx = x1 + dx0*d + dy0*radius;
2111 cy = y1 + dy0*d + -dx0*radius;
2112 a0 = nvg__atan2f(dx0, -dy0);
2113 a1 = nvg__atan2f(-dx1, dy1);
2114 dir = NVG_CW;
2115 // printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2116 } else {
2117 cx = x1 + dx0*d + -dy0*radius;
2118 cy = y1 + dy0*d + dx0*radius;
2119 a0 = nvg__atan2f(-dx0, dy0);
2120 a1 = nvg__atan2f(dx1, -dy1);
2121 dir = NVG_CCW;
2122 // printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2125 nvgArc(ctx, cx, cy, radius, a0, a1, dir);
2128 void nvgClosePath(NVGcontext* ctx)
2130 float vals[] = { NVG_CLOSE };
2131 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2134 void nvgPathWinding(NVGcontext* ctx, int dir)
2136 float vals[] = { NVG_WINDING, (float)dir };
2137 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2140 void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir)
2142 float a = 0, da = 0, hda = 0, kappa = 0;
2143 float dx = 0, dy = 0, x = 0, y = 0, tanx = 0, tany = 0;
2144 float px = 0, py = 0, ptanx = 0, ptany = 0;
2145 float vals[3 + 5*7 + 100];
2146 int i, ndivs, nvals;
2147 int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;
2149 // Clamp angles
2150 da = a1 - a0;
2151 if (dir == NVG_CW) {
2152 if (nvg__absf(da) >= NVG_PI*2) {
2153 da = NVG_PI*2;
2154 } else {
2155 while (da < 0.0f) da += NVG_PI*2;
2157 } else {
2158 if (nvg__absf(da) >= NVG_PI*2) {
2159 da = -NVG_PI*2;
2160 } else {
2161 while (da > 0.0f) da -= NVG_PI*2;
2165 // Split arc into max 90 degree segments.
2166 ndivs = nvg__maxi(1, nvg__mini((int)(nvg__absf(da) / (NVG_PI*0.5f) + 0.5f), 5));
2167 hda = (da / (float)ndivs) / 2.0f;
2168 kappa = nvg__absf(4.0f / 3.0f * (1.0f - nvg__cosf(hda)) / nvg__sinf(hda));
2170 if (dir == NVG_CCW)
2171 kappa = -kappa;
2173 nvals = 0;
2174 for (i = 0; i <= ndivs; i++) {
2175 a = a0 + da * (i/(float)ndivs);
2176 dx = nvg__cosf(a);
2177 dy = nvg__sinf(a);
2178 x = cx + dx*r;
2179 y = cy + dy*r;
2180 tanx = -dy*r*kappa;
2181 tany = dx*r*kappa;
2183 if (i == 0) {
2184 vals[nvals++] = (float)move;
2185 vals[nvals++] = x;
2186 vals[nvals++] = y;
2187 } else {
2188 vals[nvals++] = NVG_BEZIERTO;
2189 vals[nvals++] = px+ptanx;
2190 vals[nvals++] = py+ptany;
2191 vals[nvals++] = x-tanx;
2192 vals[nvals++] = y-tany;
2193 vals[nvals++] = x;
2194 vals[nvals++] = y;
2196 px = x;
2197 py = y;
2198 ptanx = tanx;
2199 ptany = tany;
2202 nvg__appendCommands(ctx, vals, nvals);
2205 void nvgRect(NVGcontext* ctx, float x, float y, float w, float h)
2207 float vals[] = {
2208 NVG_MOVETO, x,y,
2209 NVG_LINETO, x,y+h,
2210 NVG_LINETO, x+w,y+h,
2211 NVG_LINETO, x+w,y,
2212 NVG_CLOSE
2214 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2217 void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r)
2219 nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r);
2222 void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft)
2224 if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
2225 nvgRect(ctx, x, y, w, h);
2226 return;
2227 } else {
2228 float halfw = nvg__absf(w)*0.5f;
2229 float halfh = nvg__absf(h)*0.5f;
2230 float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h);
2231 float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h);
2232 float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h);
2233 float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h);
2234 float vals[] = {
2235 NVG_MOVETO, x, y + ryTL,
2236 NVG_LINETO, x, y + h - ryBL,
2237 NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h,
2238 NVG_LINETO, x + w - rxBR, y + h,
2239 NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR,
2240 NVG_LINETO, x + w, y + ryTR,
2241 NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y,
2242 NVG_LINETO, x + rxTL, y,
2243 NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL,
2244 NVG_CLOSE
2246 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2250 void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry)
2252 float vals[] = {
2253 NVG_MOVETO, cx-rx, cy,
2254 NVG_BEZIERTO, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
2255 NVG_BEZIERTO, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
2256 NVG_BEZIERTO, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
2257 NVG_BEZIERTO, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
2258 NVG_CLOSE
2260 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2263 void nvgCircle(NVGcontext* ctx, float cx, float cy, float r)
2265 nvgEllipse(ctx, cx,cy, r,r);
2268 void nvgDebugDumpPathCache(NVGcontext* ctx)
2270 const NVGpath* path;
2271 int i, j;
2273 printf("Dumping %d cached paths\n", ctx->cache->npaths);
2274 for (i = 0; i < ctx->cache->npaths; i++) {
2275 path = &ctx->cache->paths[i];
2276 printf(" - Path %d\n", i);
2277 if (path->nfill) {
2278 printf(" - fill: %d\n", path->nfill);
2279 for (j = 0; j < path->nfill; j++)
2280 printf("%f\t%f\n", path->fill[j].x, path->fill[j].y);
2282 if (path->nstroke) {
2283 printf(" - stroke: %d\n", path->nstroke);
2284 for (j = 0; j < path->nstroke; j++)
2285 printf("%f\t%f\n", path->stroke[j].x, path->stroke[j].y);
2290 void nvgFill(NVGcontext* ctx)
2292 NVGstate* state = nvg__getState(ctx);
2293 const NVGpath* path;
2294 NVGpaint fillPaint = state->fill;
2295 int i;
2297 nvg__flattenPaths(ctx);
2298 if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
2299 nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f);
2300 else
2301 nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f);
2303 // Apply global tint
2304 for (i = 0; i < 4; i++) {
2305 fillPaint.innerColor.rgba[i] *= state->tint.rgba[i];
2306 fillPaint.outerColor.rgba[i] *= state->tint.rgba[i];
2309 ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
2310 ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths);
2312 // Count triangles
2313 for (i = 0; i < ctx->cache->npaths; i++) {
2314 path = &ctx->cache->paths[i];
2315 ctx->fillTriCount += path->nfill-2;
2316 ctx->fillTriCount += path->nstroke-2;
2317 ctx->drawCallCount += 2;
2321 void nvgStroke(NVGcontext* ctx)
2323 NVGstate* state = nvg__getState(ctx);
2324 float scale = nvg__getAverageScale(state->xform);
2325 float strokeWidth = nvg__clampf(state->strokeWidth * scale, 0.0f, 200.0f);
2326 NVGpaint strokePaint = state->stroke;
2327 const NVGpath* path;
2328 int i;
2331 if (strokeWidth < ctx->fringeWidth) {
2332 // If the stroke width is less than pixel size, use alpha to emulate coverage.
2333 // Since coverage is area, scale by alpha*alpha.
2334 float alpha = nvg__clampf(strokeWidth / ctx->fringeWidth, 0.0f, 1.0f);
2335 strokePaint.innerColor.a *= alpha*alpha;
2336 strokePaint.outerColor.a *= alpha*alpha;
2337 strokeWidth = ctx->fringeWidth;
2340 // Apply global tint
2341 for (i = 0; i < 4; i++) {
2342 strokePaint.innerColor.rgba[i] *= state->tint.rgba[i];
2343 strokePaint.outerColor.rgba[i] *= state->tint.rgba[i];
2346 nvg__flattenPaths(ctx);
2348 if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
2349 nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit);
2350 else
2351 nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit);
2353 ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
2354 strokeWidth, ctx->cache->paths, ctx->cache->npaths);
2356 // Count triangles
2357 for (i = 0; i < ctx->cache->npaths; i++) {
2358 path = &ctx->cache->paths[i];
2359 ctx->strokeTriCount += path->nstroke-2;
2360 ctx->drawCallCount++;
2364 // Add fonts
2365 int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename)
2367 return fonsAddFont(ctx->fontContext->fs, name, filename, 0);
2370 int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex)
2372 return fonsAddFont(ctx->fontContext->fs, name, filename, fontIndex);
2375 int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData)
2377 return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, 0);
2380 int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex)
2382 return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, fontIndex);
2385 int nvgFindFont(NVGcontext* ctx, const char* name)
2387 if (name == NULL) return -1;
2388 return fonsGetFontByName(ctx->fontContext->fs, name);
2392 int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont)
2394 if(baseFont == -1 || fallbackFont == -1) return 0;
2395 return fonsAddFallbackFont(ctx->fontContext->fs, baseFont, fallbackFont);
2398 int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont)
2400 return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont));
2403 void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont)
2405 fonsResetFallbackFont(ctx->fontContext->fs, baseFont);
2408 void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont)
2410 nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont));
2413 // State setting
2414 void nvgFontSize(NVGcontext* ctx, float size)
2416 NVGstate* state = nvg__getState(ctx);
2417 state->fontSize = size;
2420 void nvgFontBlur(NVGcontext* ctx, float blur)
2422 NVGstate* state = nvg__getState(ctx);
2423 state->fontBlur = blur;
2426 void nvgTextLetterSpacing(NVGcontext* ctx, float spacing)
2428 NVGstate* state = nvg__getState(ctx);
2429 state->letterSpacing = spacing;
2432 void nvgTextLineHeight(NVGcontext* ctx, float lineHeight)
2434 NVGstate* state = nvg__getState(ctx);
2435 state->lineHeight = lineHeight;
2438 void nvgTextAlign(NVGcontext* ctx, int align)
2440 NVGstate* state = nvg__getState(ctx);
2441 state->textAlign = align;
2444 void nvgFontFaceId(NVGcontext* ctx, int font)
2446 NVGstate* state = nvg__getState(ctx);
2447 state->fontId = font;
2450 void nvgFontFace(NVGcontext* ctx, const char* font)
2452 NVGstate* state = nvg__getState(ctx);
2453 state->fontId = fonsGetFontByName(ctx->fontContext->fs, font);
2456 static float nvg__quantize(float a, float d)
2458 return ((int)(a / d + 0.5f)) * d;
2461 static float nvg__getFontScale(NVGstate* state)
2463 return nvg__minf(nvg__quantize(nvg__getAverageScale(state->xform), 0.01f), 4.0f);
2466 static void nvg__flushTextTexture(NVGcontext* ctx)
2468 int dirty[4];
2470 if (fonsValidateTexture(ctx->fontContext->fs, dirty)) {
2471 int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
2472 // Update texture
2473 if (fontImage != 0) {
2474 int iw, ih;
2475 const unsigned char* data = fonsGetTextureData(ctx->fontContext->fs, &iw, &ih);
2476 int x = dirty[0];
2477 int y = dirty[1];
2478 int w = dirty[2] - dirty[0];
2479 int h = dirty[3] - dirty[1];
2480 ctx->params.renderUpdateTexture(ctx->params.userPtr, fontImage, x,y, w,h, data);
2485 static int nvg__allocTextAtlas(NVGcontext* ctx)
2487 int iw, ih;
2488 nvg__flushTextTexture(ctx);
2489 if (ctx->fontContext->fontImageIdx >= NVG_MAX_FONTIMAGES-1)
2490 return 0;
2491 // if next fontImage already have a texture
2492 if (ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] != 0)
2493 nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1], &iw, &ih);
2494 else { // calculate the new font image size and create it.
2495 nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx], &iw, &ih);
2496 if (iw > ih)
2497 ih *= 2;
2498 else
2499 iw *= 2;
2500 if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE)
2501 iw = ih = NVG_MAX_FONTIMAGE_SIZE;
2502 ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1]
2503 = ctx->params.renderCreateTexture(ctx->params.userPtr,
2504 NVG_TEXTURE_ALPHA, iw, ih, NVG_FONT_TEXTURE_FLAGS, NULL);
2506 ++ctx->fontContext->fontImageIdx;
2507 fonsResetAtlas(ctx->fontContext->fs, iw, ih);
2508 return 1;
2511 static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts)
2513 int i;
2514 NVGstate* state = nvg__getState(ctx);
2515 NVGpaint paint = state->fill;
2517 // Render triangles.
2518 paint.image = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
2520 // Apply global tint
2521 for (i = 0; i < 4; i++) {
2522 paint.innerColor.rgba[i] *= state->tint.rgba[i];
2523 paint.outerColor.rgba[i] *= state->tint.rgba[i];
2526 ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth);
2528 ctx->drawCallCount++;
2529 ctx->textTriCount += nverts/3;
2532 static int nvg__isTransformFlipped(const float *xform)
2534 float det = xform[0] * xform[3] - xform[2] * xform[1];
2535 return( det < 0);
2538 float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end)
2540 NVGstate* state = nvg__getState(ctx);
2541 FONStextIter iter, prevIter;
2542 FONSquad q;
2543 NVGvertex* verts;
2544 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2545 float invscale = 1.0f / scale;
2546 int cverts = 0;
2547 int nverts = 0;
2548 int isFlipped = nvg__isTransformFlipped(state->xform);
2550 if (end == NULL)
2551 end = string + strlen(string);
2553 if (state->fontId == FONS_INVALID) return x;
2555 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2556 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2557 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2558 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2559 fonsSetFont(ctx->fontContext->fs, state->fontId);
2561 cverts = nvg__maxi(2, (int)(end - string)) * 6; // conservative estimate.
2562 verts = nvg__allocTempVerts(ctx, cverts);
2563 if (verts == NULL) return x;
2565 fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED);
2566 prevIter = iter;
2567 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2568 float c[4*2];
2569 if (iter.prevGlyphIndex == -1) { // can not retrieve glyph?
2570 if (nverts != 0) {
2571 nvg__renderText(ctx, verts, nverts);
2572 nverts = 0;
2574 if (!nvg__allocTextAtlas(ctx))
2575 break; // no memory :(
2576 iter = prevIter;
2577 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2578 if (iter.prevGlyphIndex == -1) // still can not find glyph?
2579 break;
2581 prevIter = iter;
2582 if(isFlipped) {
2583 float tmp;
2585 tmp = q.y0; q.y0 = q.y1; q.y1 = tmp;
2586 tmp = q.t0; q.t0 = q.t1; q.t1 = tmp;
2588 // Transform corners.
2589 nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale);
2590 nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale);
2591 nvgTransformPoint(&c[4],&c[5], state->xform, q.x1*invscale, q.y1*invscale);
2592 nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale, q.y1*invscale);
2593 // Create triangles
2594 if (nverts+6 <= cverts) {
2595 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++;
2596 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++;
2597 nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); nverts++;
2598 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++;
2599 nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); nverts++;
2600 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++;
2604 // TODO: add back-end bit to do this just once per frame.
2605 nvg__flushTextTexture(ctx);
2607 nvg__renderText(ctx, verts, nverts);
2609 return iter.nextx / scale;
2612 void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end)
2614 NVGstate* state = nvg__getState(ctx);
2615 NVGtextRow rows[2];
2616 int nrows = 0, i;
2617 int oldAlign = state->textAlign;
2618 int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT);
2619 int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE);
2620 float lineh = 0;
2622 if (state->fontId == FONS_INVALID) return;
2624 nvgTextMetrics(ctx, NULL, NULL, &lineh);
2626 state->textAlign = NVG_ALIGN_LEFT | valign;
2628 while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) {
2629 for (i = 0; i < nrows; i++) {
2630 NVGtextRow* row = &rows[i];
2631 if (haling & NVG_ALIGN_LEFT)
2632 nvgText(ctx, x, y, row->start, row->end);
2633 else if (haling & NVG_ALIGN_CENTER)
2634 nvgText(ctx, x + breakRowWidth*0.5f - row->width*0.5f, y, row->start, row->end);
2635 else if (haling & NVG_ALIGN_RIGHT)
2636 nvgText(ctx, x + breakRowWidth - row->width, y, row->start, row->end);
2637 y += lineh * state->lineHeight;
2639 string = rows[nrows-1].next;
2642 state->textAlign = oldAlign;
2645 int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions)
2647 NVGstate* state = nvg__getState(ctx);
2648 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2649 float invscale = 1.0f / scale;
2650 FONStextIter iter, prevIter;
2651 FONSquad q;
2652 int npos = 0;
2654 if (state->fontId == FONS_INVALID) return 0;
2656 if (end == NULL)
2657 end = string + strlen(string);
2659 if (string == end)
2660 return 0;
2662 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2663 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2664 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2665 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2666 fonsSetFont(ctx->fontContext->fs, state->fontId);
2668 fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
2669 prevIter = iter;
2670 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2671 if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
2672 iter = prevIter;
2673 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2675 prevIter = iter;
2676 positions[npos].str = iter.str;
2677 positions[npos].x = iter.x * invscale;
2678 positions[npos].minx = nvg__minf(iter.x, q.x0) * invscale;
2679 positions[npos].maxx = nvg__maxf(iter.nextx, q.x1) * invscale;
2680 npos++;
2681 if (npos >= maxPositions)
2682 break;
2685 return npos;
2688 enum NVGcodepointType {
2689 NVG_SPACE,
2690 NVG_NEWLINE,
2691 NVG_CHAR,
2692 NVG_CJK_CHAR,
2695 int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows)
2697 NVGstate* state = nvg__getState(ctx);
2698 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2699 float invscale = 1.0f / scale;
2700 FONStextIter iter, prevIter;
2701 FONSquad q;
2702 int nrows = 0;
2703 float rowStartX = 0;
2704 float rowWidth = 0;
2705 float rowMinX = 0;
2706 float rowMaxX = 0;
2707 const char* rowStart = NULL;
2708 const char* rowEnd = NULL;
2709 const char* wordStart = NULL;
2710 float wordStartX = 0;
2711 float wordMinX = 0;
2712 const char* breakEnd = NULL;
2713 float breakWidth = 0;
2714 float breakMaxX = 0;
2715 int type = NVG_SPACE, ptype = NVG_SPACE;
2716 unsigned int pcodepoint = 0;
2718 if (maxRows == 0) return 0;
2719 if (state->fontId == FONS_INVALID) return 0;
2721 if (end == NULL)
2722 end = string + strlen(string);
2724 if (string == end) return 0;
2726 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2727 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2728 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2729 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2730 fonsSetFont(ctx->fontContext->fs, state->fontId);
2732 breakRowWidth *= scale;
2734 fonsTextIterInit(ctx->fontContext->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
2735 prevIter = iter;
2736 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2737 if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
2738 iter = prevIter;
2739 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2741 prevIter = iter;
2742 switch (iter.codepoint) {
2743 case 9: // \t
2744 case 11: // \v
2745 case 12: // \f
2746 case 32: // space
2747 case 0x00a0: // NBSP
2748 type = NVG_SPACE;
2749 break;
2750 case 10: // \n
2751 type = pcodepoint == 13 ? NVG_SPACE : NVG_NEWLINE;
2752 break;
2753 case 13: // \r
2754 type = pcodepoint == 10 ? NVG_SPACE : NVG_NEWLINE;
2755 break;
2756 case 0x0085: // NEL
2757 type = NVG_NEWLINE;
2758 break;
2759 default:
2760 if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) ||
2761 (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) ||
2762 (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) ||
2763 (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) ||
2764 (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) ||
2765 (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF))
2766 type = NVG_CJK_CHAR;
2767 else
2768 type = NVG_CHAR;
2769 break;
2772 if (type == NVG_NEWLINE) {
2773 // Always handle new lines.
2774 rows[nrows].start = rowStart != NULL ? rowStart : iter.str;
2775 rows[nrows].end = rowEnd != NULL ? rowEnd : iter.str;
2776 rows[nrows].width = rowWidth * invscale;
2777 rows[nrows].minx = rowMinX * invscale;
2778 rows[nrows].maxx = rowMaxX * invscale;
2779 rows[nrows].next = iter.next;
2780 nrows++;
2781 if (nrows >= maxRows)
2782 return nrows;
2783 // Set null break point
2784 breakEnd = rowStart;
2785 breakWidth = 0.0;
2786 breakMaxX = 0.0;
2787 // Indicate to skip the white space at the beginning of the row.
2788 rowStart = NULL;
2789 rowEnd = NULL;
2790 rowWidth = 0;
2791 rowMinX = rowMaxX = 0;
2792 } else {
2793 if (rowStart == NULL) {
2794 // Skip white space until the beginning of the line
2795 if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) {
2796 // The current char is the row so far
2797 rowStartX = iter.x;
2798 rowStart = iter.str;
2799 rowEnd = iter.next;
2800 rowWidth = iter.nextx - rowStartX;
2801 rowMinX = q.x0 - rowStartX;
2802 rowMaxX = q.x1 - rowStartX;
2803 wordStart = iter.str;
2804 wordStartX = iter.x;
2805 wordMinX = q.x0 - rowStartX;
2806 // Set null break point
2807 breakEnd = rowStart;
2808 breakWidth = 0.0;
2809 breakMaxX = 0.0;
2811 } else {
2812 float nextWidth = iter.nextx - rowStartX;
2814 // track last non-white space character
2815 if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) {
2816 rowEnd = iter.next;
2817 rowWidth = iter.nextx - rowStartX;
2818 rowMaxX = q.x1 - rowStartX;
2820 // track last end of a word
2821 if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) {
2822 breakEnd = iter.str;
2823 breakWidth = rowWidth;
2824 breakMaxX = rowMaxX;
2826 // track last beginning of a word
2827 if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) {
2828 wordStart = iter.str;
2829 wordStartX = iter.x;
2830 wordMinX = q.x0;
2833 // Break to new line when a character is beyond break width.
2834 if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) {
2835 // The run length is too long, need to break to new line.
2836 if (breakEnd == rowStart) {
2837 // The current word is longer than the row length, just break it from here.
2838 rows[nrows].start = rowStart;
2839 rows[nrows].end = iter.str;
2840 rows[nrows].width = rowWidth * invscale;
2841 rows[nrows].minx = rowMinX * invscale;
2842 rows[nrows].maxx = rowMaxX * invscale;
2843 rows[nrows].next = iter.str;
2844 nrows++;
2845 if (nrows >= maxRows)
2846 return nrows;
2847 rowStartX = iter.x;
2848 rowStart = iter.str;
2849 rowEnd = iter.next;
2850 rowWidth = iter.nextx - rowStartX;
2851 rowMinX = q.x0 - rowStartX;
2852 rowMaxX = q.x1 - rowStartX;
2853 wordStart = iter.str;
2854 wordStartX = iter.x;
2855 wordMinX = q.x0 - rowStartX;
2856 } else {
2857 // Break the line from the end of the last word, and start new line from the beginning of the new.
2858 rows[nrows].start = rowStart;
2859 rows[nrows].end = breakEnd;
2860 rows[nrows].width = breakWidth * invscale;
2861 rows[nrows].minx = rowMinX * invscale;
2862 rows[nrows].maxx = breakMaxX * invscale;
2863 rows[nrows].next = wordStart;
2864 nrows++;
2865 if (nrows >= maxRows)
2866 return nrows;
2867 // Update row
2868 rowStartX = wordStartX;
2869 rowStart = wordStart;
2870 rowEnd = iter.next;
2871 rowWidth = iter.nextx - rowStartX;
2872 rowMinX = wordMinX - rowStartX;
2873 rowMaxX = q.x1 - rowStartX;
2875 // Set null break point
2876 breakEnd = rowStart;
2877 breakWidth = 0.0;
2878 breakMaxX = 0.0;
2883 pcodepoint = iter.codepoint;
2884 ptype = type;
2887 // Break the line from the end of the last word, and start new line from the beginning of the new.
2888 if (rowStart != NULL) {
2889 rows[nrows].start = rowStart;
2890 rows[nrows].end = rowEnd;
2891 rows[nrows].width = rowWidth * invscale;
2892 rows[nrows].minx = rowMinX * invscale;
2893 rows[nrows].maxx = rowMaxX * invscale;
2894 rows[nrows].next = end;
2895 nrows++;
2898 return nrows;
2901 float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds)
2903 NVGstate* state = nvg__getState(ctx);
2904 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2905 float invscale = 1.0f / scale;
2906 float width;
2908 if (state->fontId == FONS_INVALID) return 0;
2910 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2911 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2912 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2913 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2914 fonsSetFont(ctx->fontContext->fs, state->fontId);
2916 width = fonsTextBounds(ctx->fontContext->fs, x*scale, y*scale, string, end, bounds);
2917 if (bounds != NULL) {
2918 // Use line bounds for height.
2919 fonsLineBounds(ctx->fontContext->fs, y*scale, &bounds[1], &bounds[3]);
2920 bounds[0] *= invscale;
2921 bounds[1] *= invscale;
2922 bounds[2] *= invscale;
2923 bounds[3] *= invscale;
2925 return width * invscale;
2928 void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds)
2930 NVGstate* state = nvg__getState(ctx);
2931 NVGtextRow rows[2];
2932 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2933 float invscale = 1.0f / scale;
2934 int nrows = 0, i;
2935 int oldAlign = state->textAlign;
2936 int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT);
2937 int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE);
2938 float lineh = 0, rminy = 0, rmaxy = 0;
2939 float minx, miny, maxx, maxy;
2941 if (state->fontId == FONS_INVALID) {
2942 if (bounds != NULL)
2943 bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0f;
2944 return;
2947 nvgTextMetrics(ctx, NULL, NULL, &lineh);
2949 state->textAlign = NVG_ALIGN_LEFT | valign;
2951 minx = maxx = x;
2952 miny = maxy = y;
2954 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2955 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2956 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2957 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2958 fonsSetFont(ctx->fontContext->fs, state->fontId);
2959 fonsLineBounds(ctx->fontContext->fs, 0, &rminy, &rmaxy);
2960 rminy *= invscale;
2961 rmaxy *= invscale;
2963 while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) {
2964 for (i = 0; i < nrows; i++) {
2965 NVGtextRow* row = &rows[i];
2966 float rminx, rmaxx, dx = 0;
2967 // Horizontal bounds
2968 if (haling & NVG_ALIGN_LEFT)
2969 dx = 0;
2970 else if (haling & NVG_ALIGN_CENTER)
2971 dx = breakRowWidth*0.5f - row->width*0.5f;
2972 else if (haling & NVG_ALIGN_RIGHT)
2973 dx = breakRowWidth - row->width;
2974 rminx = x + row->minx + dx;
2975 rmaxx = x + row->maxx + dx;
2976 minx = nvg__minf(minx, rminx);
2977 maxx = nvg__maxf(maxx, rmaxx);
2978 // Vertical bounds.
2979 miny = nvg__minf(miny, y + rminy);
2980 maxy = nvg__maxf(maxy, y + rmaxy);
2982 y += lineh * state->lineHeight;
2984 string = rows[nrows-1].next;
2987 state->textAlign = oldAlign;
2989 if (bounds != NULL) {
2990 bounds[0] = minx;
2991 bounds[1] = miny;
2992 bounds[2] = maxx;
2993 bounds[3] = maxy;
2997 void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh)
2999 NVGstate* state = nvg__getState(ctx);
3000 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
3001 float invscale = 1.0f / scale;
3003 if (state->fontId == FONS_INVALID) return;
3005 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
3006 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
3007 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
3008 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
3009 fonsSetFont(ctx->fontContext->fs, state->fontId);
3011 fonsVertMetrics(ctx->fontContext->fs, ascender, descender, lineh);
3012 if (ascender != NULL)
3013 *ascender *= invscale;
3014 if (descender != NULL)
3015 *descender *= invscale;
3016 if (lineh != NULL)
3017 *lineh *= invscale;
3019 // vim: ft=c nu noet ts=4