comparison DPF-Prymula-audioplugins/dpf/dgl/src/nanovg/nanovg.c @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
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.
17 //
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <math.h>
22 #include <memory.h>
23
24 #include "nanovg.h"
25 #define FONTSTASH_IMPLEMENTATION
26 #define stbtt_fontinfo dpf_nvg_stbtt_fontinfo
27 #define stbrp_context dpf_nvg_stbrp_context
28 #define stbrp_rect dpf_nvg_stbrp_rect
29 #define stbrp_node dpf_nvg_stbrp_node
30 #define stbrp_coord dpf_nvg_stbrp_coord
31 #include "fontstash.h"
32
33 #ifndef NVG_NO_STB
34 #define STB_IMAGE_IMPLEMENTATION
35 #define stbi_convert_iphone_png_to_rgb dpf_stbi_convert_iphone_png_to_rgb
36 #define stbi_failure_reason dpf_stbi_failure_reason
37 #define stbi_hdr_to_ldr_gamma dpf_stbi_hdr_to_ldr_gamma
38 #define stbi_hdr_to_ldr_scale dpf_stbi_hdr_to_ldr_scale
39 #define stbi_image_free dpf_stbi_image_free
40 #define stbi_info dpf_stbi_info
41 #define stbi_info_from_callbacks dpf_stbi_info_from_callbacks
42 #define stbi_info_from_file dpf_stbi_info_from_file
43 #define stbi_info_from_memory dpf_stbi_info_from_memory
44 #define stbi_is_hdr dpf_stbi_is_hdr
45 #define stbi_is_hdr_from_callbacks dpf_stbi_is_hdr_from_callbacks
46 #define stbi_is_hdr_from_file dpf_stbi_is_hdr_from_file
47 #define stbi_is_hdr_from_memory dpf_stbi_is_hdr_from_memory
48 #define stbi_ldr_to_hdr_gamma dpf_stbi_ldr_to_hdr_gamma
49 #define stbi_ldr_to_hdr_scale dpf_stbi_ldr_to_hdr_scale
50 #define stbi_load dpf_stbi_load
51 #define stbi_load_from_callbacks dpf_stbi_load_from_callbacks
52 #define stbi_load_from_file dpf_stbi_load_from_file
53 #define stbi_load_from_memory dpf_stbi_load_from_memory
54 #define stbi_loadf dpf_stbi_loadf
55 #define stbi_loadf_from_callbacks dpf_stbi_loadf_from_callbacks
56 #define stbi_loadf_from_file dpf_stbi_loadf_from_file
57 #define stbi_loadf_from_memory dpf_stbi_loadf_from_memory
58 #define stbi_set_flip_vertically_on_load dpf_stbi_set_flip_vertically_on_load
59 #define stbi_set_unpremultiply_on_load dpf_stbi_set_unpremultiply_on_load
60 #define stbi_zlib_decode_buffer dpf_stbi_zlib_decode_buffer
61 #define stbi_zlib_decode_malloc dpf_stbi_zlib_decode_malloc
62 #define stbi_zlib_decode_malloc_guesssize dpf_stbi_zlib_decode_malloc_guesssize
63 #define stbi_zlib_decode_malloc_guesssize_headerflag dpf_stbi_zlib_decode_malloc_guesssize_headerflag
64 #define stbi_zlib_decode_noheader_buffer dpf_stbi_zlib_decode_noheader_buffer
65 #define stbi_zlib_decode_noheader_malloc dpf_stbi_zlib_decode_noheader_malloc
66 #include "stb_image.h"
67 #endif
68
69 #ifdef NVG_DISABLE_SKIPPING_WHITESPACE
70 #define NVG_SKIPPED_CHAR NVG_SPACE
71 #else
72 #define NVG_SKIPPED_CHAR NVG_CHAR
73 #endif
74
75 #ifndef NVG_FONT_TEXTURE_FLAGS
76 #define NVG_FONT_TEXTURE_FLAGS 0
77 #endif
78
79 #ifdef _MSC_VER
80 #pragma warning(disable: 4100) // unreferenced formal parameter
81 #pragma warning(disable: 4127) // conditional expression is constant
82 #pragma warning(disable: 4204) // nonstandard extension used : non-constant aggregate initializer
83 #pragma warning(disable: 4706) // assignment within conditional expression
84 #endif
85
86 #define NVG_INIT_FONTIMAGE_SIZE 512
87 #define NVG_MAX_FONTIMAGE_SIZE 2048
88 #define NVG_MAX_FONTIMAGES 4
89
90 #define NVG_INIT_COMMANDS_SIZE 256
91 #define NVG_INIT_POINTS_SIZE 128
92 #define NVG_INIT_PATHS_SIZE 16
93 #define NVG_INIT_VERTS_SIZE 256
94 #define NVG_MAX_STATES 32
95
96 #define NVG_KAPPA90 0.5522847493f // Length proportional to radius of a cubic bezier handle for 90deg arcs.
97
98 #define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr]))
99
100
101 enum NVGcommands {
102 NVG_MOVETO = 0,
103 NVG_LINETO = 1,
104 NVG_BEZIERTO = 2,
105 NVG_CLOSE = 3,
106 NVG_WINDING = 4,
107 };
108
109 enum NVGpointFlags
110 {
111 NVG_PT_CORNER = 0x01,
112 NVG_PT_LEFT = 0x02,
113 NVG_PT_BEVEL = 0x04,
114 NVG_PR_INNERBEVEL = 0x08,
115 };
116
117 struct NVGstate {
118 NVGcompositeOperationState compositeOperation;
119 int shapeAntiAlias;
120 NVGpaint fill;
121 NVGpaint stroke;
122 float strokeWidth;
123 float miterLimit;
124 int lineJoin;
125 int lineCap;
126 NVGcolor tint;
127 float xform[6];
128 NVGscissor scissor;
129 float fontSize;
130 float letterSpacing;
131 float lineHeight;
132 float fontBlur;
133 int textAlign;
134 int fontId;
135 };
136 typedef struct NVGstate NVGstate;
137
138 struct NVGpoint {
139 float x,y;
140 float dx, dy;
141 float len;
142 float dmx, dmy;
143 unsigned char flags;
144 };
145 typedef struct NVGpoint NVGpoint;
146
147 struct NVGpathCache {
148 NVGpoint* points;
149 int npoints;
150 int cpoints;
151 NVGpath* paths;
152 int npaths;
153 int cpaths;
154 NVGvertex* verts;
155 int nverts;
156 int cverts;
157 float bounds[4];
158 };
159 typedef struct NVGpathCache NVGpathCache;
160
161 struct NVGfontContext { // Fontstash context plus font images; shared between shared NanoVG contexts.
162 int refCount;
163 struct FONScontext* fs;
164 int fontImages[NVG_MAX_FONTIMAGES];
165 int fontImageIdx;
166 };
167 typedef struct NVGfontContext NVGfontContext;
168
169 struct NVGcontext {
170 NVGparams params;
171 float* commands;
172 int ccommands;
173 int ncommands;
174 float commandx, commandy;
175 NVGstate states[NVG_MAX_STATES];
176 int nstates;
177 NVGpathCache* cache;
178 float tessTol;
179 float distTol;
180 float fringeWidth;
181 float devicePxRatio;
182 NVGfontContext* fontContext;
183 int drawCallCount;
184 int fillTriCount;
185 int strokeTriCount;
186 int textTriCount;
187 };
188
189 static float nvg__sqrtf(float a) { return sqrtf(a); }
190 static float nvg__modf(float a, float b) { return fmodf(a, b); }
191 static float nvg__sinf(float a) { return sinf(a); }
192 static float nvg__cosf(float a) { return cosf(a); }
193 static float nvg__tanf(float a) { return tanf(a); }
194 static float nvg__atan2f(float a,float b) { return atan2f(a, b); }
195 static float nvg__acosf(float a) { return acosf(a); }
196
197 static int nvg__mini(int a, int b) { return a < b ? a : b; }
198 static int nvg__maxi(int a, int b) { return a > b ? a : b; }
199 static int nvg__clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); }
200 static float nvg__minf(float a, float b) { return a < b ? a : b; }
201 static float nvg__maxf(float a, float b) { return a > b ? a : b; }
202 static float nvg__absf(float a) { return a >= 0.0f ? a : -a; }
203 static float nvg__signf(float a) { return a >= 0.0f ? 1.0f : -1.0f; }
204 static float nvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
205 static float nvg__cross(float dx0, float dy0, float dx1, float dy1) { return dx1*dy0 - dx0*dy1; }
206
207 static float nvg__normalize(float *x, float* y)
208 {
209 float d = nvg__sqrtf((*x)*(*x) + (*y)*(*y));
210 if (d > 1e-6f) {
211 float id = 1.0f / d;
212 *x *= id;
213 *y *= id;
214 }
215 return d;
216 }
217
218
219 static void nvg__deletePathCache(NVGpathCache* c)
220 {
221 if (c == NULL) return;
222 if (c->points != NULL) free(c->points);
223 if (c->paths != NULL) free(c->paths);
224 if (c->verts != NULL) free(c->verts);
225 free(c);
226 }
227
228 static NVGpathCache* nvg__allocPathCache(void)
229 {
230 NVGpathCache* c = (NVGpathCache*)malloc(sizeof(NVGpathCache));
231 if (c == NULL) goto error;
232 memset(c, 0, sizeof(NVGpathCache));
233
234 c->points = (NVGpoint*)malloc(sizeof(NVGpoint)*NVG_INIT_POINTS_SIZE);
235 if (!c->points) goto error;
236 c->npoints = 0;
237 c->cpoints = NVG_INIT_POINTS_SIZE;
238
239 c->paths = (NVGpath*)malloc(sizeof(NVGpath)*NVG_INIT_PATHS_SIZE);
240 if (!c->paths) goto error;
241 c->npaths = 0;
242 c->cpaths = NVG_INIT_PATHS_SIZE;
243
244 c->verts = (NVGvertex*)malloc(sizeof(NVGvertex)*NVG_INIT_VERTS_SIZE);
245 if (!c->verts) goto error;
246 c->nverts = 0;
247 c->cverts = NVG_INIT_VERTS_SIZE;
248
249 return c;
250 error:
251 nvg__deletePathCache(c);
252 return NULL;
253 }
254
255 static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio)
256 {
257 ctx->tessTol = 0.25f / ratio;
258 ctx->distTol = 0.01f / ratio;
259 ctx->fringeWidth = 1.0f / ratio;
260 ctx->devicePxRatio = ratio;
261 }
262
263 static NVGcompositeOperationState nvg__compositeOperationState(int op)
264 {
265 int sfactor, dfactor;
266
267 if (op == NVG_SOURCE_OVER)
268 {
269 sfactor = NVG_ONE;
270 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
271 }
272 else if (op == NVG_SOURCE_IN)
273 {
274 sfactor = NVG_DST_ALPHA;
275 dfactor = NVG_ZERO;
276 }
277 else if (op == NVG_SOURCE_OUT)
278 {
279 sfactor = NVG_ONE_MINUS_DST_ALPHA;
280 dfactor = NVG_ZERO;
281 }
282 else if (op == NVG_ATOP)
283 {
284 sfactor = NVG_DST_ALPHA;
285 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
286 }
287 else if (op == NVG_DESTINATION_OVER)
288 {
289 sfactor = NVG_ONE_MINUS_DST_ALPHA;
290 dfactor = NVG_ONE;
291 }
292 else if (op == NVG_DESTINATION_IN)
293 {
294 sfactor = NVG_ZERO;
295 dfactor = NVG_SRC_ALPHA;
296 }
297 else if (op == NVG_DESTINATION_OUT)
298 {
299 sfactor = NVG_ZERO;
300 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
301 }
302 else if (op == NVG_DESTINATION_ATOP)
303 {
304 sfactor = NVG_ONE_MINUS_DST_ALPHA;
305 dfactor = NVG_SRC_ALPHA;
306 }
307 else if (op == NVG_LIGHTER)
308 {
309 sfactor = NVG_ONE;
310 dfactor = NVG_ONE;
311 }
312 else if (op == NVG_COPY)
313 {
314 sfactor = NVG_ONE;
315 dfactor = NVG_ZERO;
316 }
317 else if (op == NVG_XOR)
318 {
319 sfactor = NVG_ONE_MINUS_DST_ALPHA;
320 dfactor = NVG_ONE_MINUS_SRC_ALPHA;
321 }
322 else
323 {
324 sfactor = NVG_ONE;
325 dfactor = NVG_ZERO;
326 }
327
328 NVGcompositeOperationState state;
329 state.srcRGB = sfactor;
330 state.dstRGB = dfactor;
331 state.srcAlpha = sfactor;
332 state.dstAlpha = dfactor;
333 return state;
334 }
335
336 static NVGstate* nvg__getState(NVGcontext* ctx)
337 {
338 return &ctx->states[ctx->nstates-1];
339 }
340
341 NVGcontext* nvgCreateInternal(NVGparams* params, NVGcontext* other) // Share the fonts and images of 'other' if it's non-NULL.
342 {
343 FONSparams fontParams;
344 NVGcontext* ctx = (NVGcontext*)malloc(sizeof(NVGcontext));
345 int i;
346 if (ctx == NULL) goto error;
347 memset(ctx, 0, sizeof(NVGcontext));
348
349 ctx->params = *params;
350 if (other) {
351 ctx->fontContext = other->fontContext;
352 ctx->fontContext->refCount++;
353 } else {
354 ctx->fontContext = (NVGfontContext*)malloc(sizeof(NVGfontContext));
355 if (ctx->fontContext == NULL) goto error;
356 for (i = 0; i < NVG_MAX_FONTIMAGES; i++)
357 ctx->fontContext->fontImages[i] = 0;
358 ctx->fontContext->refCount = 1;
359 }
360
361 ctx->commands = (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE);
362 if (!ctx->commands) goto error;
363 ctx->ncommands = 0;
364 ctx->ccommands = NVG_INIT_COMMANDS_SIZE;
365
366 ctx->cache = nvg__allocPathCache();
367 if (ctx->cache == NULL) goto error;
368
369 nvgSave(ctx);
370 nvgReset(ctx);
371
372 nvg__setDevicePixelRatio(ctx, 1.0f);
373
374 if (ctx->params.renderCreate(ctx->params.userPtr, other ? other->params.userPtr : NULL) == 0) goto error;
375
376 // Init font rendering
377 if (!other) {
378 memset(&fontParams, 0, sizeof(fontParams));
379 fontParams.width = NVG_INIT_FONTIMAGE_SIZE;
380 fontParams.height = NVG_INIT_FONTIMAGE_SIZE;
381 fontParams.flags = FONS_ZERO_TOPLEFT;
382 fontParams.renderCreate = NULL;
383 fontParams.renderUpdate = NULL;
384 fontParams.renderDraw = NULL;
385 fontParams.renderDelete = NULL;
386 fontParams.userPtr = NULL;
387 ctx->fontContext->fs = fonsCreateInternal(&fontParams);
388 if (ctx->fontContext->fs == NULL) goto error;
389
390 // Create font texture
391 ctx->fontContext->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr,
392 NVG_TEXTURE_ALPHA,
393 fontParams.width,
394 fontParams.height,
395 NVG_FONT_TEXTURE_FLAGS,
396 NULL);
397 if (ctx->fontContext->fontImages[0] == 0) goto error;
398 ctx->fontContext->fontImageIdx = 0;
399 }
400
401 return ctx;
402
403 error:
404 nvgDeleteInternal(ctx);
405 return 0;
406 }
407
408 NVGparams* nvgInternalParams(NVGcontext* ctx)
409 {
410 return &ctx->params;
411 }
412
413 void nvgDeleteInternal(NVGcontext* ctx)
414 {
415 int i;
416 if (ctx == NULL) return;
417 if (ctx->commands != NULL) free(ctx->commands);
418 if (ctx->cache != NULL) nvg__deletePathCache(ctx->cache);
419
420 if (ctx->fontContext != NULL && --ctx->fontContext->refCount == 0) {
421 if (ctx->fontContext->fs)
422 fonsDeleteInternal(ctx->fontContext->fs);
423
424 for (i = 0; i < NVG_MAX_FONTIMAGES; i++) {
425 if (ctx->fontContext->fontImages[i] != 0) {
426 nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]);
427 ctx->fontContext->fontImages[i] = 0;
428 }
429 }
430
431 free(ctx->fontContext);
432 }
433
434 if (ctx->params.renderDelete != NULL)
435 ctx->params.renderDelete(ctx->params.userPtr);
436
437 free(ctx);
438 }
439
440 void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio)
441 {
442 /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
443 ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
444 ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/
445
446 ctx->nstates = 0;
447 nvgSave(ctx);
448 nvgReset(ctx);
449
450 nvg__setDevicePixelRatio(ctx, devicePixelRatio);
451
452 ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio);
453
454 ctx->drawCallCount = 0;
455 ctx->fillTriCount = 0;
456 ctx->strokeTriCount = 0;
457 ctx->textTriCount = 0;
458 }
459
460 void nvgCancelFrame(NVGcontext* ctx)
461 {
462 ctx->params.renderCancel(ctx->params.userPtr);
463 }
464
465 void nvgEndFrame(NVGcontext* ctx)
466 {
467 ctx->params.renderFlush(ctx->params.userPtr);
468 if (ctx->fontContext->fontImageIdx != 0) {
469 int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
470 int i, j, iw, ih;
471 // delete images that smaller than current one
472 if (fontImage == 0)
473 return;
474 nvgImageSize(ctx, fontImage, &iw, &ih);
475 for (i = j = 0; i < ctx->fontContext->fontImageIdx; i++) {
476 if (ctx->fontContext->fontImages[i] != 0) {
477 int nw, nh;
478 nvgImageSize(ctx, ctx->fontContext->fontImages[i], &nw, &nh);
479 if (nw < iw || nh < ih)
480 nvgDeleteImage(ctx, ctx->fontContext->fontImages[i]);
481 else
482 ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[i];
483 }
484 }
485 // make current font image to first
486 ctx->fontContext->fontImages[j++] = ctx->fontContext->fontImages[0];
487 ctx->fontContext->fontImages[0] = fontImage;
488 ctx->fontContext->fontImageIdx = 0;
489 // clear all images after j
490 for (i = j; i < NVG_MAX_FONTIMAGES; i++)
491 ctx->fontContext->fontImages[i] = 0;
492 }
493 }
494
495 NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b)
496 {
497 return nvgRGBA(r,g,b,255);
498 }
499
500 NVGcolor nvgRGBf(float r, float g, float b)
501 {
502 return nvgRGBAf(r,g,b,1.0f);
503 }
504
505 NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
506 {
507 NVGcolor color;
508 // Use longer initialization to suppress warning.
509 color.r = r / 255.0f;
510 color.g = g / 255.0f;
511 color.b = b / 255.0f;
512 color.a = a / 255.0f;
513 return color;
514 }
515
516 NVGcolor nvgRGBAf(float r, float g, float b, float a)
517 {
518 NVGcolor color;
519 // Use longer initialization to suppress warning.
520 color.r = r;
521 color.g = g;
522 color.b = b;
523 color.a = a;
524 return color;
525 }
526
527 NVGcolor nvgTransRGBA(NVGcolor c, unsigned char a)
528 {
529 c.a = a / 255.0f;
530 return c;
531 }
532
533 NVGcolor nvgTransRGBAf(NVGcolor c, float a)
534 {
535 c.a = a;
536 return c;
537 }
538
539 NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
540 {
541 int i;
542 float oneminu;
543 NVGcolor cint = {{{0}}};
544
545 u = nvg__clampf(u, 0.0f, 1.0f);
546 oneminu = 1.0f - u;
547 for( i = 0; i <4; i++ )
548 {
549 cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u;
550 }
551
552 return cint;
553 }
554
555 NVGcolor nvgHSL(float h, float s, float l)
556 {
557 return nvgHSLA(h,s,l,255);
558 }
559
560 static float nvg__hue(float h, float m1, float m2)
561 {
562 if (h < 0) h += 1;
563 if (h > 1) h -= 1;
564 if (h < 1.0f/6.0f)
565 return m1 + (m2 - m1) * h * 6.0f;
566 else if (h < 3.0f/6.0f)
567 return m2;
568 else if (h < 4.0f/6.0f)
569 return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f;
570 return m1;
571 }
572
573 NVGcolor nvgHSLA(float h, float s, float l, unsigned char a)
574 {
575 float m1, m2;
576 NVGcolor col;
577 h = nvg__modf(h, 1.0f);
578 if (h < 0.0f) h += 1.0f;
579 s = nvg__clampf(s, 0.0f, 1.0f);
580 l = nvg__clampf(l, 0.0f, 1.0f);
581 m2 = l <= 0.5f ? (l * (1 + s)) : (l + s - l * s);
582 m1 = 2 * l - m2;
583 col.r = nvg__clampf(nvg__hue(h + 1.0f/3.0f, m1, m2), 0.0f, 1.0f);
584 col.g = nvg__clampf(nvg__hue(h, m1, m2), 0.0f, 1.0f);
585 col.b = nvg__clampf(nvg__hue(h - 1.0f/3.0f, m1, m2), 0.0f, 1.0f);
586 col.a = a/255.0f;
587 return col;
588 }
589
590 void nvgTransformIdentity(float* t)
591 {
592 t[0] = 1.0f; t[1] = 0.0f;
593 t[2] = 0.0f; t[3] = 1.0f;
594 t[4] = 0.0f; t[5] = 0.0f;
595 }
596
597 void nvgTransformTranslate(float* t, float tx, float ty)
598 {
599 t[0] = 1.0f; t[1] = 0.0f;
600 t[2] = 0.0f; t[3] = 1.0f;
601 t[4] = tx; t[5] = ty;
602 }
603
604 void nvgTransformScale(float* t, float sx, float sy)
605 {
606 t[0] = sx; t[1] = 0.0f;
607 t[2] = 0.0f; t[3] = sy;
608 t[4] = 0.0f; t[5] = 0.0f;
609 }
610
611 void nvgTransformRotate(float* t, float a)
612 {
613 float cs = nvg__cosf(a), sn = nvg__sinf(a);
614 t[0] = cs; t[1] = sn;
615 t[2] = -sn; t[3] = cs;
616 t[4] = 0.0f; t[5] = 0.0f;
617 }
618
619 void nvgTransformSkewX(float* t, float a)
620 {
621 t[0] = 1.0f; t[1] = 0.0f;
622 t[2] = nvg__tanf(a); t[3] = 1.0f;
623 t[4] = 0.0f; t[5] = 0.0f;
624 }
625
626 void nvgTransformSkewY(float* t, float a)
627 {
628 t[0] = 1.0f; t[1] = nvg__tanf(a);
629 t[2] = 0.0f; t[3] = 1.0f;
630 t[4] = 0.0f; t[5] = 0.0f;
631 }
632
633 void nvgTransformMultiply(float* t, const float* s)
634 {
635 float t0 = t[0] * s[0] + t[1] * s[2];
636 float t2 = t[2] * s[0] + t[3] * s[2];
637 float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
638 t[1] = t[0] * s[1] + t[1] * s[3];
639 t[3] = t[2] * s[1] + t[3] * s[3];
640 t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
641 t[0] = t0;
642 t[2] = t2;
643 t[4] = t4;
644 }
645
646 void nvgTransformPremultiply(float* t, const float* s)
647 {
648 float s2[6];
649 memcpy(s2, s, sizeof(float)*6);
650 nvgTransformMultiply(s2, t);
651 memcpy(t, s2, sizeof(float)*6);
652 }
653
654 int nvgTransformInverse(float* inv, const float* t)
655 {
656 double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
657 if (det > -1e-6 && det < 1e-6) {
658 nvgTransformIdentity(inv);
659 return 0;
660 }
661 invdet = 1.0 / det;
662 inv[0] = (float)(t[3] * invdet);
663 inv[2] = (float)(-t[2] * invdet);
664 inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
665 inv[1] = (float)(-t[1] * invdet);
666 inv[3] = (float)(t[0] * invdet);
667 inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
668 return 1;
669 }
670
671 void nvgTransformPoint(float* dx, float* dy, const float* t, float sx, float sy)
672 {
673 *dx = sx*t[0] + sy*t[2] + t[4];
674 *dy = sx*t[1] + sy*t[3] + t[5];
675 }
676
677 float nvgDegToRad(float deg)
678 {
679 return deg / 180.0f * NVG_PI;
680 }
681
682 float nvgRadToDeg(float rad)
683 {
684 return rad / NVG_PI * 180.0f;
685 }
686
687 static void nvg__setPaintColor(NVGpaint* p, NVGcolor color)
688 {
689 memset(p, 0, sizeof(*p));
690 nvgTransformIdentity(p->xform);
691 p->radius = 0.0f;
692 p->feather = 1.0f;
693 p->innerColor = color;
694 p->outerColor = color;
695 }
696
697
698 // State handling
699 void nvgSave(NVGcontext* ctx)
700 {
701 if (ctx->nstates >= NVG_MAX_STATES)
702 return;
703 if (ctx->nstates > 0)
704 memcpy(&ctx->states[ctx->nstates], &ctx->states[ctx->nstates-1], sizeof(NVGstate));
705 ctx->nstates++;
706 }
707
708 void nvgRestore(NVGcontext* ctx)
709 {
710 if (ctx->nstates <= 1)
711 return;
712 ctx->nstates--;
713 }
714
715 void nvgReset(NVGcontext* ctx)
716 {
717 NVGstate* state = nvg__getState(ctx);
718 memset(state, 0, sizeof(*state));
719
720 nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255));
721 nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255));
722 state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER);
723 state->shapeAntiAlias = 1;
724 state->strokeWidth = 1.0f;
725 state->miterLimit = 10.0f;
726 state->lineCap = NVG_BUTT;
727 state->lineJoin = NVG_MITER;
728 state->tint = nvgRGBAf(1, 1, 1, 1);
729 nvgTransformIdentity(state->xform);
730
731 state->scissor.extent[0] = -1.0f;
732 state->scissor.extent[1] = -1.0f;
733
734 state->fontSize = 16.0f;
735 state->letterSpacing = 0.0f;
736 state->lineHeight = 1.0f;
737 state->fontBlur = 0.0f;
738 state->textAlign = NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE;
739 state->fontId = 0;
740 }
741
742 // State setting
743 void nvgShapeAntiAlias(NVGcontext* ctx, int enabled)
744 {
745 NVGstate* state = nvg__getState(ctx);
746 state->shapeAntiAlias = enabled;
747 }
748
749 void nvgStrokeWidth(NVGcontext* ctx, float width)
750 {
751 NVGstate* state = nvg__getState(ctx);
752 state->strokeWidth = width;
753 }
754
755 void nvgMiterLimit(NVGcontext* ctx, float limit)
756 {
757 NVGstate* state = nvg__getState(ctx);
758 state->miterLimit = limit;
759 }
760
761 void nvgLineCap(NVGcontext* ctx, int cap)
762 {
763 NVGstate* state = nvg__getState(ctx);
764 state->lineCap = cap;
765 }
766
767 void nvgLineJoin(NVGcontext* ctx, int join)
768 {
769 NVGstate* state = nvg__getState(ctx);
770 state->lineJoin = join;
771 }
772
773 void nvgGlobalAlpha(NVGcontext* ctx, float alpha)
774 {
775 NVGstate* state = nvg__getState(ctx);
776 state->tint.a = alpha;
777 }
778
779 void nvgGlobalTint(NVGcontext* ctx, NVGcolor tint)
780 {
781 NVGstate* state = nvg__getState(ctx);
782 state->tint = tint;
783 }
784
785 NVGcolor nvgGetGlobalTint(NVGcontext* ctx)
786 {
787 NVGstate* state = nvg__getState(ctx);
788 return state->tint;
789 }
790
791 void nvgAlpha(NVGcontext* ctx, float alpha)
792 {
793 NVGstate* state = nvg__getState(ctx);
794 state->tint.a *= alpha;
795 }
796
797 void nvgTint(NVGcontext* ctx, NVGcolor tint)
798 {
799 NVGstate* state = nvg__getState(ctx);
800 int i;
801 for (i = 0; i < 4; i++)
802 state->tint.rgba[i] *= tint.rgba[i];
803 }
804
805 void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f)
806 {
807 NVGstate* state = nvg__getState(ctx);
808 float t[6] = { a, b, c, d, e, f };
809 nvgTransformPremultiply(state->xform, t);
810 }
811
812 void nvgResetTransform(NVGcontext* ctx)
813 {
814 NVGstate* state = nvg__getState(ctx);
815 nvgTransformIdentity(state->xform);
816 }
817
818 void nvgTranslate(NVGcontext* ctx, float x, float y)
819 {
820 NVGstate* state = nvg__getState(ctx);
821 float t[6];
822 nvgTransformTranslate(t, x,y);
823 nvgTransformPremultiply(state->xform, t);
824 }
825
826 void nvgRotate(NVGcontext* ctx, float angle)
827 {
828 NVGstate* state = nvg__getState(ctx);
829 float t[6];
830 nvgTransformRotate(t, angle);
831 nvgTransformPremultiply(state->xform, t);
832 }
833
834 void nvgSkewX(NVGcontext* ctx, float angle)
835 {
836 NVGstate* state = nvg__getState(ctx);
837 float t[6];
838 nvgTransformSkewX(t, angle);
839 nvgTransformPremultiply(state->xform, t);
840 }
841
842 void nvgSkewY(NVGcontext* ctx, float angle)
843 {
844 NVGstate* state = nvg__getState(ctx);
845 float t[6];
846 nvgTransformSkewY(t, angle);
847 nvgTransformPremultiply(state->xform, t);
848 }
849
850 void nvgScale(NVGcontext* ctx, float x, float y)
851 {
852 NVGstate* state = nvg__getState(ctx);
853 float t[6];
854 nvgTransformScale(t, x,y);
855 nvgTransformPremultiply(state->xform, t);
856 }
857
858 void nvgCurrentTransform(NVGcontext* ctx, float* xform)
859 {
860 NVGstate* state = nvg__getState(ctx);
861 if (xform == NULL) return;
862 memcpy(xform, state->xform, sizeof(float)*6);
863 }
864
865 void nvgStrokeColor(NVGcontext* ctx, NVGcolor color)
866 {
867 NVGstate* state = nvg__getState(ctx);
868 nvg__setPaintColor(&state->stroke, color);
869 }
870
871 void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint)
872 {
873 NVGstate* state = nvg__getState(ctx);
874 state->stroke = paint;
875 nvgTransformMultiply(state->stroke.xform, state->xform);
876 }
877
878 void nvgFillColor(NVGcontext* ctx, NVGcolor color)
879 {
880 NVGstate* state = nvg__getState(ctx);
881 nvg__setPaintColor(&state->fill, color);
882 }
883
884 void nvgFillPaint(NVGcontext* ctx, NVGpaint paint)
885 {
886 NVGstate* state = nvg__getState(ctx);
887 state->fill = paint;
888 nvgTransformMultiply(state->fill.xform, state->xform);
889 }
890
891 #ifndef NVG_NO_STB
892 int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
893 {
894 int w, h, n, image;
895 unsigned char* img;
896 stbi_set_unpremultiply_on_load(1);
897 stbi_convert_iphone_png_to_rgb(1);
898 img = stbi_load(filename, &w, &h, &n, 4);
899 if (img == NULL) {
900 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
901 return 0;
902 }
903 image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
904 stbi_image_free(img);
905 return image;
906 }
907
908 int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata)
909 {
910 int w, h, n, image;
911 unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
912 if (img == NULL) {
913 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
914 return 0;
915 }
916 image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img);
917 stbi_image_free(img);
918 return image;
919 }
920 #endif
921
922 int nvgCreateImageRaw(NVGcontext* ctx, int w, int h, int imageFlags, NVGtexture format, const unsigned char* data)
923 {
924 return ctx->params.renderCreateTexture(ctx->params.userPtr, format, w, h, imageFlags, data);
925 }
926
927 int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data)
928 {
929 return nvgCreateImageRaw(ctx, w, h, imageFlags, NVG_TEXTURE_RGBA, data);
930 }
931
932 void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data)
933 {
934 int w, h;
935 ctx->params.renderGetTextureSize(ctx->params.userPtr, image, &w, &h);
936 ctx->params.renderUpdateTexture(ctx->params.userPtr, image, 0,0, w,h, data);
937 }
938
939 void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h)
940 {
941 ctx->params.renderGetTextureSize(ctx->params.userPtr, image, w, h);
942 }
943
944 void nvgDeleteImage(NVGcontext* ctx, int image)
945 {
946 ctx->params.renderDeleteTexture(ctx->params.userPtr, image);
947 }
948
949 NVGpaint nvgLinearGradient(NVGcontext* ctx,
950 float sx, float sy, float ex, float ey,
951 NVGcolor icol, NVGcolor ocol)
952 {
953 NVGpaint p;
954 float dx, dy, d;
955 const float large = 1e5;
956 NVG_NOTUSED(ctx);
957 memset(&p, 0, sizeof(p));
958
959 // Calculate transform aligned to the line
960 dx = ex - sx;
961 dy = ey - sy;
962 d = sqrtf(dx*dx + dy*dy);
963 if (d > 0.0001f) {
964 dx /= d;
965 dy /= d;
966 } else {
967 dx = 0;
968 dy = 1;
969 }
970
971 p.xform[0] = dy; p.xform[1] = -dx;
972 p.xform[2] = dx; p.xform[3] = dy;
973 p.xform[4] = sx - dx*large; p.xform[5] = sy - dy*large;
974
975 p.extent[0] = large;
976 p.extent[1] = large + d*0.5f;
977
978 p.radius = 0.0f;
979
980 p.feather = nvg__maxf(1.0f, d);
981
982 p.innerColor = icol;
983 p.outerColor = ocol;
984
985 return p;
986 }
987
988 NVGpaint nvgRadialGradient(NVGcontext* ctx,
989 float cx, float cy, float inr, float outr,
990 NVGcolor icol, NVGcolor ocol)
991 {
992 NVGpaint p;
993 float r = (inr+outr)*0.5f;
994 float f = (outr-inr);
995 NVG_NOTUSED(ctx);
996 memset(&p, 0, sizeof(p));
997
998 nvgTransformIdentity(p.xform);
999 p.xform[4] = cx;
1000 p.xform[5] = cy;
1001
1002 p.extent[0] = r;
1003 p.extent[1] = r;
1004
1005 p.radius = r;
1006
1007 p.feather = nvg__maxf(1.0f, f);
1008
1009 p.innerColor = icol;
1010 p.outerColor = ocol;
1011
1012 return p;
1013 }
1014
1015 NVGpaint nvgBoxGradient(NVGcontext* ctx,
1016 float x, float y, float w, float h, float r, float f,
1017 NVGcolor icol, NVGcolor ocol)
1018 {
1019 NVGpaint p;
1020 NVG_NOTUSED(ctx);
1021 memset(&p, 0, sizeof(p));
1022
1023 nvgTransformIdentity(p.xform);
1024 p.xform[4] = x+w*0.5f;
1025 p.xform[5] = y+h*0.5f;
1026
1027 p.extent[0] = w*0.5f;
1028 p.extent[1] = h*0.5f;
1029
1030 p.radius = r;
1031
1032 p.feather = nvg__maxf(1.0f, f);
1033
1034 p.innerColor = icol;
1035 p.outerColor = ocol;
1036
1037 return p;
1038 }
1039
1040
1041 NVGpaint nvgImagePattern(NVGcontext* ctx,
1042 float cx, float cy, float w, float h, float angle,
1043 int image, float alpha)
1044 {
1045 NVGpaint p;
1046 NVG_NOTUSED(ctx);
1047 memset(&p, 0, sizeof(p));
1048
1049 nvgTransformRotate(p.xform, angle);
1050 p.xform[4] = cx;
1051 p.xform[5] = cy;
1052
1053 p.extent[0] = w;
1054 p.extent[1] = h;
1055
1056 p.image = image;
1057
1058 p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha);
1059
1060 return p;
1061 }
1062
1063 // Scissoring
1064 void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h)
1065 {
1066 NVGstate* state = nvg__getState(ctx);
1067
1068 w = nvg__maxf(0.0f, w);
1069 h = nvg__maxf(0.0f, h);
1070
1071 nvgTransformIdentity(state->scissor.xform);
1072 state->scissor.xform[4] = x+w*0.5f;
1073 state->scissor.xform[5] = y+h*0.5f;
1074 nvgTransformMultiply(state->scissor.xform, state->xform);
1075
1076 state->scissor.extent[0] = w*0.5f;
1077 state->scissor.extent[1] = h*0.5f;
1078 }
1079
1080 static void nvg__isectRects(float* dst,
1081 float ax, float ay, float aw, float ah,
1082 float bx, float by, float bw, float bh)
1083 {
1084 float minx = nvg__maxf(ax, bx);
1085 float miny = nvg__maxf(ay, by);
1086 float maxx = nvg__minf(ax+aw, bx+bw);
1087 float maxy = nvg__minf(ay+ah, by+bh);
1088 dst[0] = minx;
1089 dst[1] = miny;
1090 dst[2] = nvg__maxf(0.0f, maxx - minx);
1091 dst[3] = nvg__maxf(0.0f, maxy - miny);
1092 }
1093
1094 void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h)
1095 {
1096 NVGstate* state = nvg__getState(ctx);
1097 float pxform[6], invxorm[6];
1098 float rect[4];
1099 float ex, ey, tex, tey;
1100
1101 // If no previous scissor has been set, set the scissor as current scissor.
1102 if (state->scissor.extent[0] < 0) {
1103 nvgScissor(ctx, x, y, w, h);
1104 return;
1105 }
1106
1107 // Transform the current scissor rect into current transform space.
1108 // If there is difference in rotation, this will be approximation.
1109 memcpy(pxform, state->scissor.xform, sizeof(float)*6);
1110 ex = state->scissor.extent[0];
1111 ey = state->scissor.extent[1];
1112 nvgTransformInverse(invxorm, state->xform);
1113 nvgTransformMultiply(pxform, invxorm);
1114 tex = ex*nvg__absf(pxform[0]) + ey*nvg__absf(pxform[2]);
1115 tey = ex*nvg__absf(pxform[1]) + ey*nvg__absf(pxform[3]);
1116
1117 // Intersect rects.
1118 nvg__isectRects(rect, pxform[4]-tex,pxform[5]-tey,tex*2,tey*2, x,y,w,h);
1119
1120 nvgScissor(ctx, rect[0], rect[1], rect[2], rect[3]);
1121 }
1122
1123 void nvgResetScissor(NVGcontext* ctx)
1124 {
1125 NVGstate* state = nvg__getState(ctx);
1126 memset(state->scissor.xform, 0, sizeof(state->scissor.xform));
1127 state->scissor.extent[0] = -1.0f;
1128 state->scissor.extent[1] = -1.0f;
1129 }
1130
1131 // Global composite operation.
1132 void nvgGlobalCompositeOperation(NVGcontext* ctx, int op)
1133 {
1134 NVGstate* state = nvg__getState(ctx);
1135 state->compositeOperation = nvg__compositeOperationState(op);
1136 }
1137
1138 void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor)
1139 {
1140 nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor);
1141 }
1142
1143 void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha)
1144 {
1145 NVGcompositeOperationState op;
1146 op.srcRGB = srcRGB;
1147 op.dstRGB = dstRGB;
1148 op.srcAlpha = srcAlpha;
1149 op.dstAlpha = dstAlpha;
1150
1151 NVGstate* state = nvg__getState(ctx);
1152 state->compositeOperation = op;
1153 }
1154
1155 static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
1156 {
1157 float dx = x2 - x1;
1158 float dy = y2 - y1;
1159 return dx*dx + dy*dy < tol*tol;
1160 }
1161
1162 static float nvg__distPtSeg(float x, float y, float px, float py, float qx, float qy)
1163 {
1164 float pqx, pqy, dx, dy, d, t;
1165 pqx = qx-px;
1166 pqy = qy-py;
1167 dx = x-px;
1168 dy = y-py;
1169 d = pqx*pqx + pqy*pqy;
1170 t = pqx*dx + pqy*dy;
1171 if (d > 0) t /= d;
1172 if (t < 0) t = 0;
1173 else if (t > 1) t = 1;
1174 dx = px + t*pqx - x;
1175 dy = py + t*pqy - y;
1176 return dx*dx + dy*dy;
1177 }
1178
1179 static void nvg__appendCommands(NVGcontext* ctx, float* vals, int nvals)
1180 {
1181 NVGstate* state = nvg__getState(ctx);
1182 int i;
1183
1184 if (ctx->ncommands+nvals > ctx->ccommands) {
1185 float* commands;
1186 int ccommands = ctx->ncommands+nvals + ctx->ccommands/2;
1187 commands = (float*)realloc(ctx->commands, sizeof(float)*ccommands);
1188 if (commands == NULL) return;
1189 ctx->commands = commands;
1190 ctx->ccommands = ccommands;
1191 }
1192
1193 if ((int)vals[0] != NVG_CLOSE && (int)vals[0] != NVG_WINDING) {
1194 ctx->commandx = vals[nvals-2];
1195 ctx->commandy = vals[nvals-1];
1196 }
1197
1198 // transform commands
1199 i = 0;
1200 while (i < nvals) {
1201 int cmd = (int)vals[i];
1202 switch (cmd) {
1203 case NVG_MOVETO:
1204 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1205 i += 3;
1206 break;
1207 case NVG_LINETO:
1208 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1209 i += 3;
1210 break;
1211 case NVG_BEZIERTO:
1212 nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]);
1213 nvgTransformPoint(&vals[i+3],&vals[i+4], state->xform, vals[i+3],vals[i+4]);
1214 nvgTransformPoint(&vals[i+5],&vals[i+6], state->xform, vals[i+5],vals[i+6]);
1215 i += 7;
1216 break;
1217 case NVG_CLOSE:
1218 i++;
1219 break;
1220 case NVG_WINDING:
1221 i += 2;
1222 break;
1223 default:
1224 i++;
1225 }
1226 }
1227
1228 memcpy(&ctx->commands[ctx->ncommands], vals, nvals*sizeof(float));
1229
1230 ctx->ncommands += nvals;
1231 }
1232
1233
1234 static void nvg__clearPathCache(NVGcontext* ctx)
1235 {
1236 ctx->cache->npoints = 0;
1237 ctx->cache->npaths = 0;
1238 }
1239
1240 static NVGpath* nvg__lastPath(NVGcontext* ctx)
1241 {
1242 if (ctx->cache->npaths > 0)
1243 return &ctx->cache->paths[ctx->cache->npaths-1];
1244 return NULL;
1245 }
1246
1247 static void nvg__addPath(NVGcontext* ctx)
1248 {
1249 NVGpath* path;
1250 if (ctx->cache->npaths+1 > ctx->cache->cpaths) {
1251 NVGpath* paths;
1252 int cpaths = ctx->cache->npaths+1 + ctx->cache->cpaths/2;
1253 paths = (NVGpath*)realloc(ctx->cache->paths, sizeof(NVGpath)*cpaths);
1254 if (paths == NULL) return;
1255 ctx->cache->paths = paths;
1256 ctx->cache->cpaths = cpaths;
1257 }
1258 path = &ctx->cache->paths[ctx->cache->npaths];
1259 memset(path, 0, sizeof(*path));
1260 path->first = ctx->cache->npoints;
1261 path->winding = NVG_CCW;
1262
1263 ctx->cache->npaths++;
1264 }
1265
1266 static NVGpoint* nvg__lastPoint(NVGcontext* ctx)
1267 {
1268 if (ctx->cache->npoints > 0)
1269 return &ctx->cache->points[ctx->cache->npoints-1];
1270 return NULL;
1271 }
1272
1273 static void nvg__addPoint(NVGcontext* ctx, float x, float y, int flags)
1274 {
1275 NVGpath* path = nvg__lastPath(ctx);
1276 NVGpoint* pt;
1277 if (path == NULL) return;
1278
1279 if (path->count > 0 && ctx->cache->npoints > 0) {
1280 pt = nvg__lastPoint(ctx);
1281 if (nvg__ptEquals(pt->x,pt->y, x,y, ctx->distTol)) {
1282 pt->flags |= flags;
1283 return;
1284 }
1285 }
1286
1287 if (ctx->cache->npoints+1 > ctx->cache->cpoints) {
1288 NVGpoint* points;
1289 int cpoints = ctx->cache->npoints+1 + ctx->cache->cpoints/2;
1290 points = (NVGpoint*)realloc(ctx->cache->points, sizeof(NVGpoint)*cpoints);
1291 if (points == NULL) return;
1292 ctx->cache->points = points;
1293 ctx->cache->cpoints = cpoints;
1294 }
1295
1296 pt = &ctx->cache->points[ctx->cache->npoints];
1297 memset(pt, 0, sizeof(*pt));
1298 pt->x = x;
1299 pt->y = y;
1300 pt->flags = (unsigned char)flags;
1301
1302 ctx->cache->npoints++;
1303 path->count++;
1304 }
1305
1306 static void nvg__closePath(NVGcontext* ctx)
1307 {
1308 NVGpath* path = nvg__lastPath(ctx);
1309 if (path == NULL) return;
1310 path->closed = 1;
1311 }
1312
1313 static void nvg__pathWinding(NVGcontext* ctx, int winding)
1314 {
1315 NVGpath* path = nvg__lastPath(ctx);
1316 if (path == NULL) return;
1317 path->winding = winding;
1318 }
1319
1320 static float nvg__getAverageScale(float *t)
1321 {
1322 float sx = sqrtf(t[0]*t[0] + t[2]*t[2]);
1323 float sy = sqrtf(t[1]*t[1] + t[3]*t[3]);
1324 return (sx + sy) * 0.5f;
1325 }
1326
1327 static NVGvertex* nvg__allocTempVerts(NVGcontext* ctx, int nverts)
1328 {
1329 if (nverts > ctx->cache->cverts) {
1330 NVGvertex* verts;
1331 int cverts = (nverts + 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly.
1332 verts = (NVGvertex*)realloc(ctx->cache->verts, sizeof(NVGvertex)*cverts);
1333 if (verts == NULL) return NULL;
1334 ctx->cache->verts = verts;
1335 ctx->cache->cverts = cverts;
1336 }
1337
1338 return ctx->cache->verts;
1339 }
1340
1341 static float nvg__triarea2(float ax, float ay, float bx, float by, float cx, float cy)
1342 {
1343 float abx = bx - ax;
1344 float aby = by - ay;
1345 float acx = cx - ax;
1346 float acy = cy - ay;
1347 return acx*aby - abx*acy;
1348 }
1349
1350 static float nvg__polyArea(NVGpoint* pts, int npts)
1351 {
1352 int i;
1353 float area = 0;
1354 for (i = 2; i < npts; i++) {
1355 NVGpoint* a = &pts[0];
1356 NVGpoint* b = &pts[i-1];
1357 NVGpoint* c = &pts[i];
1358 area += nvg__triarea2(a->x,a->y, b->x,b->y, c->x,c->y);
1359 }
1360 return area * 0.5f;
1361 }
1362
1363 static void nvg__polyReverse(NVGpoint* pts, int npts)
1364 {
1365 NVGpoint tmp;
1366 int i = 0, j = npts-1;
1367 while (i < j) {
1368 tmp = pts[i];
1369 pts[i] = pts[j];
1370 pts[j] = tmp;
1371 i++;
1372 j--;
1373 }
1374 }
1375
1376
1377 static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
1378 {
1379 vtx->x = x;
1380 vtx->y = y;
1381 vtx->u = u;
1382 vtx->v = v;
1383 }
1384
1385 static void nvg__tesselateBezier(NVGcontext* ctx,
1386 float x1, float y1, float x2, float y2,
1387 float x3, float y3, float x4, float y4,
1388 int level, int type)
1389 {
1390 float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
1391 float dx,dy,d2,d3;
1392
1393 if (level > 10) return;
1394
1395 x12 = (x1+x2)*0.5f;
1396 y12 = (y1+y2)*0.5f;
1397 x23 = (x2+x3)*0.5f;
1398 y23 = (y2+y3)*0.5f;
1399 x34 = (x3+x4)*0.5f;
1400 y34 = (y3+y4)*0.5f;
1401 x123 = (x12+x23)*0.5f;
1402 y123 = (y12+y23)*0.5f;
1403
1404 dx = x4 - x1;
1405 dy = y4 - y1;
1406 d2 = nvg__absf(((x2 - x4) * dy - (y2 - y4) * dx));
1407 d3 = nvg__absf(((x3 - x4) * dy - (y3 - y4) * dx));
1408
1409 if ((d2 + d3)*(d2 + d3) < ctx->tessTol * (dx*dx + dy*dy)) {
1410 nvg__addPoint(ctx, x4, y4, type);
1411 return;
1412 }
1413
1414 /* 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) {
1415 nvg__addPoint(ctx, x4, y4, type);
1416 return;
1417 }*/
1418
1419 x234 = (x23+x34)*0.5f;
1420 y234 = (y23+y34)*0.5f;
1421 x1234 = (x123+x234)*0.5f;
1422 y1234 = (y123+y234)*0.5f;
1423
1424 nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0);
1425 nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type);
1426 }
1427
1428 static void nvg__flattenPaths(NVGcontext* ctx)
1429 {
1430 NVGpathCache* cache = ctx->cache;
1431 // NVGstate* state = nvg__getState(ctx);
1432 NVGpoint* last;
1433 NVGpoint* p0;
1434 NVGpoint* p1;
1435 NVGpoint* pts;
1436 NVGpath* path;
1437 int i, j;
1438 float* cp1;
1439 float* cp2;
1440 float* p;
1441 float area;
1442
1443 if (cache->npaths > 0)
1444 return;
1445
1446 // Flatten
1447 i = 0;
1448 while (i < ctx->ncommands) {
1449 int cmd = (int)ctx->commands[i];
1450 switch (cmd) {
1451 case NVG_MOVETO:
1452 nvg__addPath(ctx);
1453 p = &ctx->commands[i+1];
1454 nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER);
1455 i += 3;
1456 break;
1457 case NVG_LINETO:
1458 p = &ctx->commands[i+1];
1459 nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER);
1460 i += 3;
1461 break;
1462 case NVG_BEZIERTO:
1463 last = nvg__lastPoint(ctx);
1464 if (last != NULL) {
1465 cp1 = &ctx->commands[i+1];
1466 cp2 = &ctx->commands[i+3];
1467 p = &ctx->commands[i+5];
1468 nvg__tesselateBezier(ctx, last->x,last->y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, NVG_PT_CORNER);
1469 }
1470 i += 7;
1471 break;
1472 case NVG_CLOSE:
1473 nvg__closePath(ctx);
1474 i++;
1475 break;
1476 case NVG_WINDING:
1477 nvg__pathWinding(ctx, (int)ctx->commands[i+1]);
1478 i += 2;
1479 break;
1480 default:
1481 i++;
1482 }
1483 }
1484
1485 cache->bounds[0] = cache->bounds[1] = 1e6f;
1486 cache->bounds[2] = cache->bounds[3] = -1e6f;
1487
1488 // Calculate the direction and length of line segments.
1489 for (j = 0; j < cache->npaths; j++) {
1490 path = &cache->paths[j];
1491 pts = &cache->points[path->first];
1492
1493 // If the first and last points are the same, remove the last, mark as closed path.
1494 p0 = &pts[path->count-1];
1495 p1 = &pts[0];
1496 if (nvg__ptEquals(p0->x,p0->y, p1->x,p1->y, ctx->distTol)) {
1497 path->count--;
1498 p0 = &pts[path->count-1];
1499 path->closed = 1;
1500 }
1501
1502 // Enforce winding.
1503 if (path->count > 2) {
1504 area = nvg__polyArea(pts, path->count);
1505 if (path->winding == NVG_CCW && area < 0.0f)
1506 nvg__polyReverse(pts, path->count);
1507 if (path->winding == NVG_CW && area > 0.0f)
1508 nvg__polyReverse(pts, path->count);
1509 }
1510
1511 for(i = 0; i < path->count; i++) {
1512 // Calculate segment direction and length
1513 p0->dx = p1->x - p0->x;
1514 p0->dy = p1->y - p0->y;
1515 p0->len = nvg__normalize(&p0->dx, &p0->dy);
1516 // Update bounds
1517 cache->bounds[0] = nvg__minf(cache->bounds[0], p0->x);
1518 cache->bounds[1] = nvg__minf(cache->bounds[1], p0->y);
1519 cache->bounds[2] = nvg__maxf(cache->bounds[2], p0->x);
1520 cache->bounds[3] = nvg__maxf(cache->bounds[3], p0->y);
1521 // Advance
1522 p0 = p1++;
1523 }
1524 }
1525 }
1526
1527 static int nvg__curveDivs(float r, float arc, float tol)
1528 {
1529 float da = acosf(r / (r + tol)) * 2.0f;
1530 return nvg__maxi(2, (int)ceilf(arc / da));
1531 }
1532
1533 static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w,
1534 float* x0, float* y0, float* x1, float* y1)
1535 {
1536 if (bevel) {
1537 *x0 = p1->x + p0->dy * w;
1538 *y0 = p1->y - p0->dx * w;
1539 *x1 = p1->x + p1->dy * w;
1540 *y1 = p1->y - p1->dx * w;
1541 } else {
1542 *x0 = p1->x + p1->dmx * w;
1543 *y0 = p1->y + p1->dmy * w;
1544 *x1 = p1->x + p1->dmx * w;
1545 *y1 = p1->y + p1->dmy * w;
1546 }
1547 }
1548
1549 static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
1550 float lw, float rw, float lu, float ru, int ncap,
1551 float fringe)
1552 {
1553 int i, n;
1554 float dlx0 = p0->dy;
1555 float dly0 = -p0->dx;
1556 float dlx1 = p1->dy;
1557 float dly1 = -p1->dx;
1558 NVG_NOTUSED(fringe);
1559
1560 if (p1->flags & NVG_PT_LEFT) {
1561 float lx0,ly0,lx1,ly1,a0,a1;
1562 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1);
1563 a0 = atan2f(-dly0, -dlx0);
1564 a1 = atan2f(-dly1, -dlx1);
1565 if (a1 > a0) a1 -= NVG_PI*2;
1566
1567 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1568 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1569
1570 n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap);
1571 for (i = 0; i < n; i++) {
1572 float u = i/(float)(n-1);
1573 float a = a0 + u*(a1-a0);
1574 float rx = p1->x + cosf(a) * rw;
1575 float ry = p1->y + sinf(a) * rw;
1576 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1577 nvg__vset(dst, rx, ry, ru,1); dst++;
1578 }
1579
1580 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1581 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1582
1583 } else {
1584 float rx0,ry0,rx1,ry1,a0,a1;
1585 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1);
1586 a0 = atan2f(dly0, dlx0);
1587 a1 = atan2f(dly1, dlx1);
1588 if (a1 < a0) a1 += NVG_PI*2;
1589
1590 nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1); dst++;
1591 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1592
1593 n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap);
1594 for (i = 0; i < n; i++) {
1595 float u = i/(float)(n-1);
1596 float a = a0 + u*(a1-a0);
1597 float lx = p1->x + cosf(a) * lw;
1598 float ly = p1->y + sinf(a) * lw;
1599 nvg__vset(dst, lx, ly, lu,1); dst++;
1600 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1601 }
1602
1603 nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu,1); dst++;
1604 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1605
1606 }
1607 return dst;
1608 }
1609
1610 static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
1611 float lw, float rw, float lu, float ru, float fringe)
1612 {
1613 float rx0,ry0,rx1,ry1;
1614 float lx0,ly0,lx1,ly1;
1615 float dlx0 = p0->dy;
1616 float dly0 = -p0->dx;
1617 float dlx1 = p1->dy;
1618 float dly1 = -p1->dx;
1619 NVG_NOTUSED(fringe);
1620
1621 if (p1->flags & NVG_PT_LEFT) {
1622 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1);
1623
1624 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1625 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1626
1627 if (p1->flags & NVG_PT_BEVEL) {
1628 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1629 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1630
1631 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1632 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1633 } else {
1634 rx0 = p1->x - p1->dmx * rw;
1635 ry0 = p1->y - p1->dmy * rw;
1636
1637 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1638 nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++;
1639
1640 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1641 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1642
1643 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1644 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1645 }
1646
1647 nvg__vset(dst, lx1, ly1, lu,1); dst++;
1648 nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++;
1649
1650 } else {
1651 nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1);
1652
1653 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1654 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1655
1656 if (p1->flags & NVG_PT_BEVEL) {
1657 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1658 nvg__vset(dst, rx0, ry0, ru,1); dst++;
1659
1660 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1661 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1662 } else {
1663 lx0 = p1->x + p1->dmx * lw;
1664 ly0 = p1->y + p1->dmy * lw;
1665
1666 nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++;
1667 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1668
1669 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1670 nvg__vset(dst, lx0, ly0, lu,1); dst++;
1671
1672 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1673 nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++;
1674 }
1675
1676 nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++;
1677 nvg__vset(dst, rx1, ry1, ru,1); dst++;
1678 }
1679
1680 return dst;
1681 }
1682
1683 static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p,
1684 float dx, float dy, float w, float d,
1685 float aa, float u0, float u1)
1686 {
1687 float px = p->x - dx*d;
1688 float py = p->y - dy*d;
1689 float dlx = dy;
1690 float dly = -dx;
1691 nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++;
1692 nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++;
1693 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1694 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1695 return dst;
1696 }
1697
1698 static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p,
1699 float dx, float dy, float w, float d,
1700 float aa, float u0, float u1)
1701 {
1702 float px = p->x + dx*d;
1703 float py = p->y + dy*d;
1704 float dlx = dy;
1705 float dly = -dx;
1706 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1707 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1708 nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++;
1709 nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++;
1710 return dst;
1711 }
1712
1713
1714 static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
1715 float dx, float dy, float w, int ncap,
1716 float aa, float u0, float u1)
1717 {
1718 int i;
1719 float px = p->x;
1720 float py = p->y;
1721 float dlx = dy;
1722 float dly = -dx;
1723 NVG_NOTUSED(aa);
1724 for (i = 0; i < ncap; i++) {
1725 float a = i/(float)(ncap-1)*NVG_PI;
1726 float ax = cosf(a) * w, ay = sinf(a) * w;
1727 nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++;
1728 nvg__vset(dst, px, py, 0.5f,1); dst++;
1729 }
1730 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1731 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1732 return dst;
1733 }
1734
1735 static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
1736 float dx, float dy, float w, int ncap,
1737 float aa, float u0, float u1)
1738 {
1739 int i;
1740 float px = p->x;
1741 float py = p->y;
1742 float dlx = dy;
1743 float dly = -dx;
1744 NVG_NOTUSED(aa);
1745 nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++;
1746 nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++;
1747 for (i = 0; i < ncap; i++) {
1748 float a = i/(float)(ncap-1)*NVG_PI;
1749 float ax = cosf(a) * w, ay = sinf(a) * w;
1750 nvg__vset(dst, px, py, 0.5f,1); dst++;
1751 nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++;
1752 }
1753 return dst;
1754 }
1755
1756
1757 static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float miterLimit)
1758 {
1759 NVGpathCache* cache = ctx->cache;
1760 int i, j;
1761 float iw = 0.0f;
1762
1763 if (w > 0.0f) iw = 1.0f / w;
1764
1765 // Calculate which joins needs extra vertices to append, and gather vertex count.
1766 for (i = 0; i < cache->npaths; i++) {
1767 NVGpath* path = &cache->paths[i];
1768 NVGpoint* pts = &cache->points[path->first];
1769 NVGpoint* p0 = &pts[path->count-1];
1770 NVGpoint* p1 = &pts[0];
1771 int nleft = 0;
1772
1773 path->nbevel = 0;
1774
1775 for (j = 0; j < path->count; j++) {
1776 float dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
1777 dlx0 = p0->dy;
1778 dly0 = -p0->dx;
1779 dlx1 = p1->dy;
1780 dly1 = -p1->dx;
1781 // Calculate extrusions
1782 p1->dmx = (dlx0 + dlx1) * 0.5f;
1783 p1->dmy = (dly0 + dly1) * 0.5f;
1784 dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy;
1785 if (dmr2 > 0.000001f) {
1786 float scale = 1.0f / dmr2;
1787 if (scale > 600.0f) {
1788 scale = 600.0f;
1789 }
1790 p1->dmx *= scale;
1791 p1->dmy *= scale;
1792 }
1793
1794 // Clear flags, but keep the corner.
1795 p1->flags = (p1->flags & NVG_PT_CORNER) ? NVG_PT_CORNER : 0;
1796
1797 // Keep track of left turns.
1798 cross = p1->dx * p0->dy - p0->dx * p1->dy;
1799 if (cross > 0.0f) {
1800 nleft++;
1801 p1->flags |= NVG_PT_LEFT;
1802 }
1803
1804 // Calculate if we should use bevel or miter for inner join.
1805 limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw);
1806 if ((dmr2 * limit*limit) < 1.0f)
1807 p1->flags |= NVG_PR_INNERBEVEL;
1808
1809 // Check to see if the corner needs to be beveled.
1810 if (p1->flags & NVG_PT_CORNER) {
1811 if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
1812 p1->flags |= NVG_PT_BEVEL;
1813 }
1814 }
1815
1816 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0)
1817 path->nbevel++;
1818
1819 p0 = p1++;
1820 }
1821
1822 path->convex = (nleft == path->count) ? 1 : 0;
1823 }
1824 }
1825
1826
1827 static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit)
1828 {
1829 NVGpathCache* cache = ctx->cache;
1830 NVGvertex* verts;
1831 NVGvertex* dst;
1832 int cverts, i, j;
1833 float aa = fringe;//ctx->fringeWidth;
1834 float u0 = 0.0f, u1 = 1.0f;
1835 int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle.
1836
1837 w += aa * 0.5f;
1838
1839 // Disable the gradient used for antialiasing when antialiasing is not used.
1840 if (aa == 0.0f) {
1841 u0 = 0.5f;
1842 u1 = 0.5f;
1843 }
1844
1845 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
1846
1847 // Calculate max vertex usage.
1848 cverts = 0;
1849 for (i = 0; i < cache->npaths; i++) {
1850 NVGpath* path = &cache->paths[i];
1851 int loop = (path->closed == 0) ? 0 : 1;
1852 if (lineJoin == NVG_ROUND)
1853 cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop
1854 else
1855 cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop
1856 if (loop == 0) {
1857 // space for caps
1858 if (lineCap == NVG_ROUND) {
1859 cverts += (ncap*2 + 2)*2;
1860 } else {
1861 cverts += (3+3)*2;
1862 }
1863 }
1864 }
1865
1866 verts = nvg__allocTempVerts(ctx, cverts);
1867 if (verts == NULL) return 0;
1868
1869 for (i = 0; i < cache->npaths; i++) {
1870 NVGpath* path = &cache->paths[i];
1871 NVGpoint* pts = &cache->points[path->first];
1872 NVGpoint* p0;
1873 NVGpoint* p1;
1874 int s, e, loop;
1875 float dx, dy;
1876
1877 path->fill = 0;
1878 path->nfill = 0;
1879
1880 // Calculate fringe or stroke
1881 loop = (path->closed == 0) ? 0 : 1;
1882 dst = verts;
1883 path->stroke = dst;
1884
1885 if (loop) {
1886 // Looping
1887 p0 = &pts[path->count-1];
1888 p1 = &pts[0];
1889 s = 0;
1890 e = path->count;
1891 } else {
1892 // Add cap
1893 p0 = &pts[0];
1894 p1 = &pts[1];
1895 s = 1;
1896 e = path->count-1;
1897 }
1898
1899 if (loop == 0) {
1900 // Add cap
1901 dx = p1->x - p0->x;
1902 dy = p1->y - p0->y;
1903 nvg__normalize(&dx, &dy);
1904 if (lineCap == NVG_BUTT)
1905 dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1);
1906 else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
1907 dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1);
1908 else if (lineCap == NVG_ROUND)
1909 dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1);
1910 }
1911
1912 for (j = s; j < e; ++j) {
1913 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
1914 if (lineJoin == NVG_ROUND) {
1915 dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa);
1916 } else {
1917 dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa);
1918 }
1919 } else {
1920 nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++;
1921 nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++;
1922 }
1923 p0 = p1++;
1924 }
1925
1926 if (loop) {
1927 // Loop it
1928 nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++;
1929 nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++;
1930 } else {
1931 // Add cap
1932 dx = p1->x - p0->x;
1933 dy = p1->y - p0->y;
1934 nvg__normalize(&dx, &dy);
1935 if (lineCap == NVG_BUTT)
1936 dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1);
1937 else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
1938 dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1);
1939 else if (lineCap == NVG_ROUND)
1940 dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1);
1941 }
1942
1943 path->nstroke = (int)(dst - verts);
1944
1945 verts = dst;
1946 }
1947
1948 return 1;
1949 }
1950
1951 static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLimit)
1952 {
1953 NVGpathCache* cache = ctx->cache;
1954 NVGvertex* verts;
1955 NVGvertex* dst;
1956 int cverts, convex, i, j;
1957 float aa = ctx->fringeWidth;
1958 int fringe = w > 0.0f;
1959
1960 nvg__calculateJoins(ctx, w, lineJoin, miterLimit);
1961
1962 // Calculate max vertex usage.
1963 cverts = 0;
1964 for (i = 0; i < cache->npaths; i++) {
1965 NVGpath* path = &cache->paths[i];
1966 cverts += path->count + path->nbevel + 1;
1967 if (fringe)
1968 cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop
1969 }
1970
1971 verts = nvg__allocTempVerts(ctx, cverts);
1972 if (verts == NULL) return 0;
1973
1974 convex = cache->npaths == 1 && cache->paths[0].convex;
1975
1976 for (i = 0; i < cache->npaths; i++) {
1977 NVGpath* path = &cache->paths[i];
1978 NVGpoint* pts = &cache->points[path->first];
1979 NVGpoint* p0;
1980 NVGpoint* p1;
1981 float rw, lw, woff;
1982 float ru, lu;
1983
1984 // Calculate shape vertices.
1985 woff = 0.5f*aa;
1986 dst = verts;
1987 path->fill = dst;
1988
1989 if (fringe) {
1990 // Looping
1991 p0 = &pts[path->count-1];
1992 p1 = &pts[0];
1993 for (j = 0; j < path->count; ++j) {
1994 if (p1->flags & NVG_PT_BEVEL) {
1995 float dlx0 = p0->dy;
1996 float dly0 = -p0->dx;
1997 float dlx1 = p1->dy;
1998 float dly1 = -p1->dx;
1999 if (p1->flags & NVG_PT_LEFT) {
2000 float lx = p1->x + p1->dmx * woff;
2001 float ly = p1->y + p1->dmy * woff;
2002 nvg__vset(dst, lx, ly, 0.5f,1); dst++;
2003 } else {
2004 float lx0 = p1->x + dlx0 * woff;
2005 float ly0 = p1->y + dly0 * woff;
2006 float lx1 = p1->x + dlx1 * woff;
2007 float ly1 = p1->y + dly1 * woff;
2008 nvg__vset(dst, lx0, ly0, 0.5f,1); dst++;
2009 nvg__vset(dst, lx1, ly1, 0.5f,1); dst++;
2010 }
2011 } else {
2012 nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1); dst++;
2013 }
2014 p0 = p1++;
2015 }
2016 } else {
2017 for (j = 0; j < path->count; ++j) {
2018 nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1);
2019 dst++;
2020 }
2021 }
2022
2023 path->nfill = (int)(dst - verts);
2024 verts = dst;
2025
2026 // Calculate fringe
2027 if (fringe) {
2028 lw = w + woff;
2029 rw = w - woff;
2030 lu = 0;
2031 ru = 1;
2032 dst = verts;
2033 path->stroke = dst;
2034
2035 // Create only half a fringe for convex shapes so that
2036 // the shape can be rendered without stenciling.
2037 if (convex) {
2038 lw = woff; // This should generate the same vertex as fill inset above.
2039 lu = 0.5f; // Set outline fade at middle.
2040 }
2041
2042 // Looping
2043 p0 = &pts[path->count-1];
2044 p1 = &pts[0];
2045
2046 for (j = 0; j < path->count; ++j) {
2047 if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
2048 dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth);
2049 } else {
2050 nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1); dst++;
2051 nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1); dst++;
2052 }
2053 p0 = p1++;
2054 }
2055
2056 // Loop it
2057 nvg__vset(dst, verts[0].x, verts[0].y, lu,1); dst++;
2058 nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++;
2059
2060 path->nstroke = (int)(dst - verts);
2061 verts = dst;
2062 } else {
2063 path->stroke = NULL;
2064 path->nstroke = 0;
2065 }
2066 }
2067
2068 return 1;
2069 }
2070
2071
2072 // Draw
2073 void nvgBeginPath(NVGcontext* ctx)
2074 {
2075 ctx->ncommands = 0;
2076 nvg__clearPathCache(ctx);
2077 }
2078
2079 void nvgMoveTo(NVGcontext* ctx, float x, float y)
2080 {
2081 float vals[] = { NVG_MOVETO, x, y };
2082 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2083 }
2084
2085 void nvgLineTo(NVGcontext* ctx, float x, float y)
2086 {
2087 float vals[] = { NVG_LINETO, x, y };
2088 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2089 }
2090
2091 void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y)
2092 {
2093 float vals[] = { NVG_BEZIERTO, c1x, c1y, c2x, c2y, x, y };
2094 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2095 }
2096
2097 void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y)
2098 {
2099 float x0 = ctx->commandx;
2100 float y0 = ctx->commandy;
2101 float vals[] = { NVG_BEZIERTO,
2102 x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0),
2103 x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y),
2104 x, y };
2105 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2106 }
2107
2108 void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius)
2109 {
2110 float x0 = ctx->commandx;
2111 float y0 = ctx->commandy;
2112 float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
2113 int dir;
2114
2115 if (ctx->ncommands == 0) {
2116 return;
2117 }
2118
2119 // Handle degenerate cases.
2120 if (nvg__ptEquals(x0,y0, x1,y1, ctx->distTol) ||
2121 nvg__ptEquals(x1,y1, x2,y2, ctx->distTol) ||
2122 nvg__distPtSeg(x1,y1, x0,y0, x2,y2) < ctx->distTol*ctx->distTol ||
2123 radius < ctx->distTol) {
2124 nvgLineTo(ctx, x1,y1);
2125 return;
2126 }
2127
2128 // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
2129 dx0 = x0-x1;
2130 dy0 = y0-y1;
2131 dx1 = x2-x1;
2132 dy1 = y2-y1;
2133 nvg__normalize(&dx0,&dy0);
2134 nvg__normalize(&dx1,&dy1);
2135 a = nvg__acosf(dx0*dx1 + dy0*dy1);
2136 d = radius / nvg__tanf(a/2.0f);
2137
2138 // printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
2139
2140 if (d > 10000.0f) {
2141 nvgLineTo(ctx, x1,y1);
2142 return;
2143 }
2144
2145 if (nvg__cross(dx0,dy0, dx1,dy1) > 0.0f) {
2146 cx = x1 + dx0*d + dy0*radius;
2147 cy = y1 + dy0*d + -dx0*radius;
2148 a0 = nvg__atan2f(dx0, -dy0);
2149 a1 = nvg__atan2f(-dx1, dy1);
2150 dir = NVG_CW;
2151 // printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2152 } else {
2153 cx = x1 + dx0*d + -dy0*radius;
2154 cy = y1 + dy0*d + dx0*radius;
2155 a0 = nvg__atan2f(-dx0, dy0);
2156 a1 = nvg__atan2f(dx1, -dy1);
2157 dir = NVG_CCW;
2158 // printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2159 }
2160
2161 nvgArc(ctx, cx, cy, radius, a0, a1, dir);
2162 }
2163
2164 void nvgClosePath(NVGcontext* ctx)
2165 {
2166 float vals[] = { NVG_CLOSE };
2167 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2168 }
2169
2170 void nvgPathWinding(NVGcontext* ctx, int dir)
2171 {
2172 float vals[] = { NVG_WINDING, (float)dir };
2173 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2174 }
2175
2176 void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir)
2177 {
2178 float a = 0, da = 0, hda = 0, kappa = 0;
2179 float dx = 0, dy = 0, x = 0, y = 0, tanx = 0, tany = 0;
2180 float px = 0, py = 0, ptanx = 0, ptany = 0;
2181 float vals[3 + 5*7 + 100];
2182 int i, ndivs, nvals;
2183 int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;
2184
2185 // Clamp angles
2186 da = a1 - a0;
2187 if (dir == NVG_CW) {
2188 if (nvg__absf(da) >= NVG_PI*2) {
2189 da = NVG_PI*2;
2190 } else {
2191 while (da < 0.0f) da += NVG_PI*2;
2192 }
2193 } else {
2194 if (nvg__absf(da) >= NVG_PI*2) {
2195 da = -NVG_PI*2;
2196 } else {
2197 while (da > 0.0f) da -= NVG_PI*2;
2198 }
2199 }
2200
2201 // Split arc into max 90 degree segments.
2202 ndivs = nvg__maxi(1, nvg__mini((int)(nvg__absf(da) / (NVG_PI*0.5f) + 0.5f), 5));
2203 hda = (da / (float)ndivs) / 2.0f;
2204 kappa = nvg__absf(4.0f / 3.0f * (1.0f - nvg__cosf(hda)) / nvg__sinf(hda));
2205
2206 if (dir == NVG_CCW)
2207 kappa = -kappa;
2208
2209 nvals = 0;
2210 for (i = 0; i <= ndivs; i++) {
2211 a = a0 + da * (i/(float)ndivs);
2212 dx = nvg__cosf(a);
2213 dy = nvg__sinf(a);
2214 x = cx + dx*r;
2215 y = cy + dy*r;
2216 tanx = -dy*r*kappa;
2217 tany = dx*r*kappa;
2218
2219 if (i == 0) {
2220 vals[nvals++] = (float)move;
2221 vals[nvals++] = x;
2222 vals[nvals++] = y;
2223 } else {
2224 vals[nvals++] = NVG_BEZIERTO;
2225 vals[nvals++] = px+ptanx;
2226 vals[nvals++] = py+ptany;
2227 vals[nvals++] = x-tanx;
2228 vals[nvals++] = y-tany;
2229 vals[nvals++] = x;
2230 vals[nvals++] = y;
2231 }
2232 px = x;
2233 py = y;
2234 ptanx = tanx;
2235 ptany = tany;
2236 }
2237
2238 nvg__appendCommands(ctx, vals, nvals);
2239 }
2240
2241 void nvgRect(NVGcontext* ctx, float x, float y, float w, float h)
2242 {
2243 float vals[] = {
2244 NVG_MOVETO, x,y,
2245 NVG_LINETO, x,y+h,
2246 NVG_LINETO, x+w,y+h,
2247 NVG_LINETO, x+w,y,
2248 NVG_CLOSE
2249 };
2250 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2251 }
2252
2253 void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r)
2254 {
2255 nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r);
2256 }
2257
2258 void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft)
2259 {
2260 if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {
2261 nvgRect(ctx, x, y, w, h);
2262 return;
2263 } else {
2264 float halfw = nvg__absf(w)*0.5f;
2265 float halfh = nvg__absf(h)*0.5f;
2266 float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h);
2267 float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h);
2268 float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h);
2269 float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h);
2270 float vals[] = {
2271 NVG_MOVETO, x, y + ryTL,
2272 NVG_LINETO, x, y + h - ryBL,
2273 NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h,
2274 NVG_LINETO, x + w - rxBR, y + h,
2275 NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR,
2276 NVG_LINETO, x + w, y + ryTR,
2277 NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y,
2278 NVG_LINETO, x + rxTL, y,
2279 NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL,
2280 NVG_CLOSE
2281 };
2282 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2283 }
2284 }
2285
2286 void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry)
2287 {
2288 float vals[] = {
2289 NVG_MOVETO, cx-rx, cy,
2290 NVG_BEZIERTO, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry,
2291 NVG_BEZIERTO, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy,
2292 NVG_BEZIERTO, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry,
2293 NVG_BEZIERTO, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy,
2294 NVG_CLOSE
2295 };
2296 nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
2297 }
2298
2299 void nvgCircle(NVGcontext* ctx, float cx, float cy, float r)
2300 {
2301 nvgEllipse(ctx, cx,cy, r,r);
2302 }
2303
2304 void nvgDebugDumpPathCache(NVGcontext* ctx)
2305 {
2306 const NVGpath* path;
2307 int i, j;
2308
2309 printf("Dumping %d cached paths\n", ctx->cache->npaths);
2310 for (i = 0; i < ctx->cache->npaths; i++) {
2311 path = &ctx->cache->paths[i];
2312 printf(" - Path %d\n", i);
2313 if (path->nfill) {
2314 printf(" - fill: %d\n", path->nfill);
2315 for (j = 0; j < path->nfill; j++)
2316 printf("%f\t%f\n", path->fill[j].x, path->fill[j].y);
2317 }
2318 if (path->nstroke) {
2319 printf(" - stroke: %d\n", path->nstroke);
2320 for (j = 0; j < path->nstroke; j++)
2321 printf("%f\t%f\n", path->stroke[j].x, path->stroke[j].y);
2322 }
2323 }
2324 }
2325
2326 void nvgFill(NVGcontext* ctx)
2327 {
2328 NVGstate* state = nvg__getState(ctx);
2329 const NVGpath* path;
2330 NVGpaint fillPaint = state->fill;
2331 int i;
2332
2333 nvg__flattenPaths(ctx);
2334 if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
2335 nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f);
2336 else
2337 nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f);
2338
2339 // Apply global tint
2340 for (i = 0; i < 4; i++) {
2341 fillPaint.innerColor.rgba[i] *= state->tint.rgba[i];
2342 fillPaint.outerColor.rgba[i] *= state->tint.rgba[i];
2343 }
2344
2345 ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
2346 ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths);
2347
2348 // Count triangles
2349 for (i = 0; i < ctx->cache->npaths; i++) {
2350 path = &ctx->cache->paths[i];
2351 ctx->fillTriCount += path->nfill-2;
2352 ctx->fillTriCount += path->nstroke-2;
2353 ctx->drawCallCount += 2;
2354 }
2355 }
2356
2357 void nvgStroke(NVGcontext* ctx)
2358 {
2359 NVGstate* state = nvg__getState(ctx);
2360 float scale = nvg__getAverageScale(state->xform);
2361 float strokeWidth = nvg__clampf(state->strokeWidth * scale, 0.0f, 200.0f);
2362 NVGpaint strokePaint = state->stroke;
2363 const NVGpath* path;
2364 int i;
2365
2366
2367 if (strokeWidth < ctx->fringeWidth) {
2368 // If the stroke width is less than pixel size, use alpha to emulate coverage.
2369 // Since coverage is area, scale by alpha*alpha.
2370 float alpha = nvg__clampf(strokeWidth / ctx->fringeWidth, 0.0f, 1.0f);
2371 strokePaint.innerColor.a *= alpha*alpha;
2372 strokePaint.outerColor.a *= alpha*alpha;
2373 strokeWidth = ctx->fringeWidth;
2374 }
2375
2376 // Apply global tint
2377 for (i = 0; i < 4; i++) {
2378 strokePaint.innerColor.rgba[i] *= state->tint.rgba[i];
2379 strokePaint.outerColor.rgba[i] *= state->tint.rgba[i];
2380 }
2381
2382 nvg__flattenPaths(ctx);
2383
2384 if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
2385 nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit);
2386 else
2387 nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit);
2388
2389 ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
2390 strokeWidth, ctx->cache->paths, ctx->cache->npaths);
2391
2392 // Count triangles
2393 for (i = 0; i < ctx->cache->npaths; i++) {
2394 path = &ctx->cache->paths[i];
2395 ctx->strokeTriCount += path->nstroke-2;
2396 ctx->drawCallCount++;
2397 }
2398 }
2399
2400 // Add fonts
2401 int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename)
2402 {
2403 return fonsAddFont(ctx->fontContext->fs, name, filename, 0);
2404 }
2405
2406 int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex)
2407 {
2408 return fonsAddFont(ctx->fontContext->fs, name, filename, fontIndex);
2409 }
2410
2411 int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData)
2412 {
2413 return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, 0);
2414 }
2415
2416 int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex)
2417 {
2418 return fonsAddFontMem(ctx->fontContext->fs, name, data, ndata, freeData, fontIndex);
2419 }
2420
2421 int nvgFindFont(NVGcontext* ctx, const char* name)
2422 {
2423 if (name == NULL) return -1;
2424 return fonsGetFontByName(ctx->fontContext->fs, name);
2425 }
2426
2427
2428 int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont)
2429 {
2430 if(baseFont == -1 || fallbackFont == -1) return 0;
2431 return fonsAddFallbackFont(ctx->fontContext->fs, baseFont, fallbackFont);
2432 }
2433
2434 int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont)
2435 {
2436 return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont));
2437 }
2438
2439 void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont)
2440 {
2441 fonsResetFallbackFont(ctx->fontContext->fs, baseFont);
2442 }
2443
2444 void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont)
2445 {
2446 nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont));
2447 }
2448
2449 // State setting
2450 void nvgFontSize(NVGcontext* ctx, float size)
2451 {
2452 NVGstate* state = nvg__getState(ctx);
2453 state->fontSize = size;
2454 }
2455
2456 void nvgFontBlur(NVGcontext* ctx, float blur)
2457 {
2458 NVGstate* state = nvg__getState(ctx);
2459 state->fontBlur = blur;
2460 }
2461
2462 void nvgTextLetterSpacing(NVGcontext* ctx, float spacing)
2463 {
2464 NVGstate* state = nvg__getState(ctx);
2465 state->letterSpacing = spacing;
2466 }
2467
2468 void nvgTextLineHeight(NVGcontext* ctx, float lineHeight)
2469 {
2470 NVGstate* state = nvg__getState(ctx);
2471 state->lineHeight = lineHeight;
2472 }
2473
2474 void nvgTextAlign(NVGcontext* ctx, int align)
2475 {
2476 NVGstate* state = nvg__getState(ctx);
2477 state->textAlign = align;
2478 }
2479
2480 void nvgFontFaceId(NVGcontext* ctx, int font)
2481 {
2482 NVGstate* state = nvg__getState(ctx);
2483 state->fontId = font;
2484 }
2485
2486 void nvgFontFace(NVGcontext* ctx, const char* font)
2487 {
2488 NVGstate* state = nvg__getState(ctx);
2489 state->fontId = fonsGetFontByName(ctx->fontContext->fs, font);
2490 }
2491
2492 static float nvg__quantize(float a, float d)
2493 {
2494 return ((int)(a / d + 0.5f)) * d;
2495 }
2496
2497 static float nvg__getFontScale(NVGstate* state)
2498 {
2499 return nvg__minf(nvg__quantize(nvg__getAverageScale(state->xform), 0.01f), 4.0f);
2500 }
2501
2502 static void nvg__flushTextTexture(NVGcontext* ctx)
2503 {
2504 int dirty[4];
2505
2506 if (fonsValidateTexture(ctx->fontContext->fs, dirty)) {
2507 int fontImage = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
2508 // Update texture
2509 if (fontImage != 0) {
2510 int iw, ih;
2511 const unsigned char* data = fonsGetTextureData(ctx->fontContext->fs, &iw, &ih);
2512 int x = dirty[0];
2513 int y = dirty[1];
2514 int w = dirty[2] - dirty[0];
2515 int h = dirty[3] - dirty[1];
2516 ctx->params.renderUpdateTexture(ctx->params.userPtr, fontImage, x,y, w,h, data);
2517 }
2518 }
2519 }
2520
2521 static int nvg__allocTextAtlas(NVGcontext* ctx)
2522 {
2523 int iw, ih;
2524 nvg__flushTextTexture(ctx);
2525 if (ctx->fontContext->fontImageIdx >= NVG_MAX_FONTIMAGES-1)
2526 return 0;
2527 // if next fontImage already have a texture
2528 if (ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1] != 0)
2529 nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1], &iw, &ih);
2530 else { // calculate the new font image size and create it.
2531 nvgImageSize(ctx, ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx], &iw, &ih);
2532 if (iw > ih)
2533 ih *= 2;
2534 else
2535 iw *= 2;
2536 if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE)
2537 iw = ih = NVG_MAX_FONTIMAGE_SIZE;
2538 ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx+1]
2539 = ctx->params.renderCreateTexture(ctx->params.userPtr,
2540 NVG_TEXTURE_ALPHA, iw, ih, NVG_FONT_TEXTURE_FLAGS, NULL);
2541 }
2542 ++ctx->fontContext->fontImageIdx;
2543 fonsResetAtlas(ctx->fontContext->fs, iw, ih);
2544 return 1;
2545 }
2546
2547 static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts)
2548 {
2549 int i;
2550 NVGstate* state = nvg__getState(ctx);
2551 NVGpaint paint = state->fill;
2552
2553 // Render triangles.
2554 paint.image = ctx->fontContext->fontImages[ctx->fontContext->fontImageIdx];
2555
2556 // Apply global tint
2557 for (i = 0; i < 4; i++) {
2558 paint.innerColor.rgba[i] *= state->tint.rgba[i];
2559 paint.outerColor.rgba[i] *= state->tint.rgba[i];
2560 }
2561
2562 ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth);
2563
2564 ctx->drawCallCount++;
2565 ctx->textTriCount += nverts/3;
2566 }
2567
2568 static int nvg__isTransformFlipped(const float *xform)
2569 {
2570 float det = xform[0] * xform[3] - xform[2] * xform[1];
2571 return( det < 0);
2572 }
2573
2574 float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end)
2575 {
2576 NVGstate* state = nvg__getState(ctx);
2577 FONStextIter iter, prevIter;
2578 FONSquad q;
2579 NVGvertex* verts;
2580 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2581 float invscale = 1.0f / scale;
2582 int cverts = 0;
2583 int nverts = 0;
2584 int isFlipped = nvg__isTransformFlipped(state->xform);
2585
2586 if (end == NULL)
2587 end = string + strlen(string);
2588
2589 if (state->fontId == FONS_INVALID) return x;
2590
2591 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2592 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2593 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2594 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2595 fonsSetFont(ctx->fontContext->fs, state->fontId);
2596
2597 cverts = nvg__maxi(2, (int)(end - string)) * 6; // conservative estimate.
2598 verts = nvg__allocTempVerts(ctx, cverts);
2599 if (verts == NULL) return x;
2600
2601 fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED);
2602 prevIter = iter;
2603 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2604 float c[4*2];
2605 if (iter.prevGlyphIndex == -1) { // can not retrieve glyph?
2606 if (nverts != 0) {
2607 nvg__renderText(ctx, verts, nverts);
2608 nverts = 0;
2609 }
2610 if (!nvg__allocTextAtlas(ctx))
2611 break; // no memory :(
2612 iter = prevIter;
2613 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2614 if (iter.prevGlyphIndex == -1) // still can not find glyph?
2615 break;
2616 }
2617 prevIter = iter;
2618 if(isFlipped) {
2619 float tmp;
2620
2621 tmp = q.y0; q.y0 = q.y1; q.y1 = tmp;
2622 tmp = q.t0; q.t0 = q.t1; q.t1 = tmp;
2623 }
2624 // Transform corners.
2625 nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale);
2626 nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale);
2627 nvgTransformPoint(&c[4],&c[5], state->xform, q.x1*invscale, q.y1*invscale);
2628 nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale, q.y1*invscale);
2629 // Create triangles
2630 if (nverts+6 <= cverts) {
2631 #if NVG_FONT_TEXTURE_FLAGS
2632 // align font kerning to integer pixel positions
2633 for (int i = 0; i < 8; ++i)
2634 c[i] = (int)(c[i] + 0.5f);
2635 #endif
2636 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++;
2637 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++;
2638 nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); nverts++;
2639 nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++;
2640 nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); nverts++;
2641 nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++;
2642 }
2643 }
2644
2645 // TODO: add back-end bit to do this just once per frame.
2646 nvg__flushTextTexture(ctx);
2647
2648 nvg__renderText(ctx, verts, nverts);
2649
2650 return iter.nextx / scale;
2651 }
2652
2653 void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end)
2654 {
2655 NVGstate* state = nvg__getState(ctx);
2656 NVGtextRow rows[2];
2657 int nrows = 0, i;
2658 int oldAlign = state->textAlign;
2659 int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT);
2660 int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE);
2661 float lineh = 0;
2662
2663 if (state->fontId == FONS_INVALID) return;
2664
2665 nvgTextMetrics(ctx, NULL, NULL, &lineh);
2666
2667 state->textAlign = NVG_ALIGN_LEFT | valign;
2668
2669 while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) {
2670 for (i = 0; i < nrows; i++) {
2671 NVGtextRow* row = &rows[i];
2672 if (haling & NVG_ALIGN_LEFT)
2673 nvgText(ctx, x, y, row->start, row->end);
2674 else if (haling & NVG_ALIGN_CENTER)
2675 nvgText(ctx, x + breakRowWidth*0.5f - row->width*0.5f, y, row->start, row->end);
2676 else if (haling & NVG_ALIGN_RIGHT)
2677 nvgText(ctx, x + breakRowWidth - row->width, y, row->start, row->end);
2678 y += lineh * state->lineHeight;
2679 }
2680 string = rows[nrows-1].next;
2681 }
2682
2683 state->textAlign = oldAlign;
2684 }
2685
2686 int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions)
2687 {
2688 NVGstate* state = nvg__getState(ctx);
2689 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2690 float invscale = 1.0f / scale;
2691 FONStextIter iter, prevIter;
2692 FONSquad q;
2693 int npos = 0;
2694
2695 if (state->fontId == FONS_INVALID) return 0;
2696
2697 if (end == NULL)
2698 end = string + strlen(string);
2699
2700 if (string == end)
2701 return 0;
2702
2703 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2704 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2705 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2706 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2707 fonsSetFont(ctx->fontContext->fs, state->fontId);
2708
2709 fonsTextIterInit(ctx->fontContext->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
2710 prevIter = iter;
2711 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2712 if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
2713 iter = prevIter;
2714 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2715 }
2716 prevIter = iter;
2717 positions[npos].str = iter.str;
2718 positions[npos].x = iter.x * invscale;
2719 positions[npos].minx = nvg__minf(iter.x, q.x0) * invscale;
2720 positions[npos].maxx = nvg__maxf(iter.nextx, q.x1) * invscale;
2721 npos++;
2722 if (npos >= maxPositions)
2723 break;
2724 }
2725
2726 return npos;
2727 }
2728
2729 enum NVGcodepointType {
2730 NVG_SPACE,
2731 NVG_NEWLINE,
2732 NVG_CHAR,
2733 NVG_CJK_CHAR,
2734 };
2735
2736 int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows)
2737 {
2738 NVGstate* state = nvg__getState(ctx);
2739 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2740 float invscale = 1.0f / scale;
2741 FONStextIter iter, prevIter;
2742 FONSquad q;
2743 int nrows = 0;
2744 float rowStartX = 0;
2745 float rowWidth = 0;
2746 float rowMinX = 0;
2747 float rowMaxX = 0;
2748 const char* rowStart = NULL;
2749 const char* rowEnd = NULL;
2750 const char* wordStart = NULL;
2751 float wordStartX = 0;
2752 float wordMinX = 0;
2753 const char* breakEnd = NULL;
2754 float breakWidth = 0;
2755 float breakMaxX = 0;
2756 int type = NVG_SPACE, ptype = NVG_SPACE;
2757 unsigned int pcodepoint = 0;
2758
2759 if (maxRows == 0) return 0;
2760 if (state->fontId == FONS_INVALID) return 0;
2761
2762 if (end == NULL)
2763 end = string + strlen(string);
2764
2765 if (string == end) return 0;
2766
2767 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2768 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2769 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2770 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2771 fonsSetFont(ctx->fontContext->fs, state->fontId);
2772
2773 breakRowWidth *= scale;
2774
2775 fonsTextIterInit(ctx->fontContext->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
2776 prevIter = iter;
2777 while (fonsTextIterNext(ctx->fontContext->fs, &iter, &q)) {
2778 if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
2779 iter = prevIter;
2780 fonsTextIterNext(ctx->fontContext->fs, &iter, &q); // try again
2781 }
2782 prevIter = iter;
2783 switch (iter.codepoint) {
2784 case 9: // \t
2785 case 11: // \v
2786 case 12: // \f
2787 case 32: // space
2788 case 0x00a0: // NBSP
2789 type = NVG_SPACE;
2790 break;
2791 case 10: // \n
2792 type = pcodepoint == 13 ? NVG_SPACE : NVG_NEWLINE;
2793 break;
2794 case 13: // \r
2795 type = pcodepoint == 10 ? NVG_SPACE : NVG_NEWLINE;
2796 break;
2797 case 0x0085: // NEL
2798 type = NVG_NEWLINE;
2799 break;
2800 default:
2801 if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) ||
2802 (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) ||
2803 (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) ||
2804 (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) ||
2805 (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) ||
2806 (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF))
2807 type = NVG_CJK_CHAR;
2808 else
2809 type = NVG_CHAR;
2810 break;
2811 }
2812
2813 if (type == NVG_NEWLINE) {
2814 // Always handle new lines.
2815 rows[nrows].start = rowStart != NULL ? rowStart : iter.str;
2816 rows[nrows].end = rowEnd != NULL ? rowEnd : iter.str;
2817 rows[nrows].width = rowWidth * invscale;
2818 rows[nrows].minx = rowMinX * invscale;
2819 rows[nrows].maxx = rowMaxX * invscale;
2820 rows[nrows].next = iter.next;
2821 nrows++;
2822 if (nrows >= maxRows)
2823 return nrows;
2824 // Set null break point
2825 breakEnd = rowStart;
2826 breakWidth = 0.0;
2827 breakMaxX = 0.0;
2828 // Indicate to skip the white space at the beginning of the row.
2829 rowStart = NULL;
2830 rowEnd = NULL;
2831 rowWidth = 0;
2832 rowMinX = rowMaxX = 0;
2833 } else {
2834 if (rowStart == NULL) {
2835 // Skip white space until the beginning of the line
2836 if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) {
2837 // The current char is the row so far
2838 rowStartX = iter.x;
2839 rowStart = iter.str;
2840 rowEnd = iter.next;
2841 rowWidth = iter.nextx - rowStartX;
2842 rowMinX = q.x0 - rowStartX;
2843 rowMaxX = q.x1 - rowStartX;
2844 wordStart = iter.str;
2845 wordStartX = iter.x;
2846 wordMinX = q.x0 - rowStartX;
2847 // Set null break point
2848 breakEnd = rowStart;
2849 breakWidth = 0.0;
2850 breakMaxX = 0.0;
2851 }
2852 } else {
2853 float nextWidth = iter.nextx - rowStartX;
2854
2855 // track last non-white space character
2856 if (type == NVG_CHAR || type == NVG_CJK_CHAR || type == NVG_SKIPPED_CHAR) {
2857 rowEnd = iter.next;
2858 rowWidth = iter.nextx - rowStartX;
2859 rowMaxX = q.x1 - rowStartX;
2860 }
2861 // track last end of a word
2862 if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) {
2863 breakEnd = iter.str;
2864 breakWidth = rowWidth;
2865 breakMaxX = rowMaxX;
2866 }
2867 // track last beginning of a word
2868 if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) {
2869 wordStart = iter.str;
2870 wordStartX = iter.x;
2871 wordMinX = q.x0;
2872 }
2873
2874 // Break to new line when a character is beyond break width.
2875 if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) {
2876 // The run length is too long, need to break to new line.
2877 if (breakEnd == rowStart) {
2878 // The current word is longer than the row length, just break it from here.
2879 rows[nrows].start = rowStart;
2880 rows[nrows].end = iter.str;
2881 rows[nrows].width = rowWidth * invscale;
2882 rows[nrows].minx = rowMinX * invscale;
2883 rows[nrows].maxx = rowMaxX * invscale;
2884 rows[nrows].next = iter.str;
2885 nrows++;
2886 if (nrows >= maxRows)
2887 return nrows;
2888 rowStartX = iter.x;
2889 rowStart = iter.str;
2890 rowEnd = iter.next;
2891 rowWidth = iter.nextx - rowStartX;
2892 rowMinX = q.x0 - rowStartX;
2893 rowMaxX = q.x1 - rowStartX;
2894 wordStart = iter.str;
2895 wordStartX = iter.x;
2896 wordMinX = q.x0 - rowStartX;
2897 } else {
2898 // Break the line from the end of the last word, and start new line from the beginning of the new.
2899 rows[nrows].start = rowStart;
2900 rows[nrows].end = breakEnd;
2901 rows[nrows].width = breakWidth * invscale;
2902 rows[nrows].minx = rowMinX * invscale;
2903 rows[nrows].maxx = breakMaxX * invscale;
2904 rows[nrows].next = wordStart;
2905 nrows++;
2906 if (nrows >= maxRows)
2907 return nrows;
2908 // Update row
2909 rowStartX = wordStartX;
2910 rowStart = wordStart;
2911 rowEnd = iter.next;
2912 rowWidth = iter.nextx - rowStartX;
2913 rowMinX = wordMinX - rowStartX;
2914 rowMaxX = q.x1 - rowStartX;
2915 }
2916 // Set null break point
2917 breakEnd = rowStart;
2918 breakWidth = 0.0;
2919 breakMaxX = 0.0;
2920 }
2921 }
2922 }
2923
2924 pcodepoint = iter.codepoint;
2925 ptype = type;
2926 }
2927
2928 // Break the line from the end of the last word, and start new line from the beginning of the new.
2929 if (rowStart != NULL) {
2930 rows[nrows].start = rowStart;
2931 rows[nrows].end = rowEnd;
2932 rows[nrows].width = rowWidth * invscale;
2933 rows[nrows].minx = rowMinX * invscale;
2934 rows[nrows].maxx = rowMaxX * invscale;
2935 rows[nrows].next = end;
2936 nrows++;
2937 }
2938
2939 return nrows;
2940 }
2941
2942 float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds)
2943 {
2944 NVGstate* state = nvg__getState(ctx);
2945 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2946 float invscale = 1.0f / scale;
2947 float width;
2948
2949 if (state->fontId == FONS_INVALID) return 0;
2950
2951 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2952 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2953 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2954 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2955 fonsSetFont(ctx->fontContext->fs, state->fontId);
2956
2957 width = fonsTextBounds(ctx->fontContext->fs, x*scale, y*scale, string, end, bounds);
2958 if (bounds != NULL) {
2959 // Use line bounds for height.
2960 fonsLineBounds(ctx->fontContext->fs, y*scale, &bounds[1], &bounds[3]);
2961 bounds[0] *= invscale;
2962 bounds[1] *= invscale;
2963 bounds[2] *= invscale;
2964 bounds[3] *= invscale;
2965 }
2966 return width * invscale;
2967 }
2968
2969 void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds)
2970 {
2971 NVGstate* state = nvg__getState(ctx);
2972 NVGtextRow rows[2];
2973 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
2974 float invscale = 1.0f / scale;
2975 int nrows = 0, i;
2976 int oldAlign = state->textAlign;
2977 int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT);
2978 int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE);
2979 float lineh = 0, rminy = 0, rmaxy = 0;
2980 float minx, miny, maxx, maxy;
2981
2982 if (state->fontId == FONS_INVALID) {
2983 if (bounds != NULL)
2984 bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0f;
2985 return;
2986 }
2987
2988 nvgTextMetrics(ctx, NULL, NULL, &lineh);
2989
2990 state->textAlign = NVG_ALIGN_LEFT | valign;
2991
2992 minx = maxx = x;
2993 miny = maxy = y;
2994
2995 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
2996 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
2997 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
2998 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
2999 fonsSetFont(ctx->fontContext->fs, state->fontId);
3000 fonsLineBounds(ctx->fontContext->fs, 0, &rminy, &rmaxy);
3001 rminy *= invscale;
3002 rmaxy *= invscale;
3003
3004 while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) {
3005 for (i = 0; i < nrows; i++) {
3006 NVGtextRow* row = &rows[i];
3007 float rminx, rmaxx, dx = 0;
3008 // Horizontal bounds
3009 if (haling & NVG_ALIGN_LEFT)
3010 dx = 0;
3011 else if (haling & NVG_ALIGN_CENTER)
3012 dx = breakRowWidth*0.5f - row->width*0.5f;
3013 else if (haling & NVG_ALIGN_RIGHT)
3014 dx = breakRowWidth - row->width;
3015 rminx = x + row->minx + dx;
3016 rmaxx = x + row->maxx + dx;
3017 minx = nvg__minf(minx, rminx);
3018 maxx = nvg__maxf(maxx, rmaxx);
3019 // Vertical bounds.
3020 miny = nvg__minf(miny, y + rminy);
3021 maxy = nvg__maxf(maxy, y + rmaxy);
3022
3023 y += lineh * state->lineHeight;
3024 }
3025 string = rows[nrows-1].next;
3026 }
3027
3028 state->textAlign = oldAlign;
3029
3030 if (bounds != NULL) {
3031 bounds[0] = minx;
3032 bounds[1] = miny;
3033 bounds[2] = maxx;
3034 bounds[3] = maxy;
3035 }
3036 }
3037
3038 void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh)
3039 {
3040 NVGstate* state = nvg__getState(ctx);
3041 float scale = nvg__getFontScale(state) * ctx->devicePxRatio;
3042 float invscale = 1.0f / scale;
3043
3044 if (state->fontId == FONS_INVALID) return;
3045
3046 fonsSetSize(ctx->fontContext->fs, state->fontSize*scale);
3047 fonsSetSpacing(ctx->fontContext->fs, state->letterSpacing*scale);
3048 fonsSetBlur(ctx->fontContext->fs, state->fontBlur*scale);
3049 fonsSetAlign(ctx->fontContext->fs, state->textAlign);
3050 fonsSetFont(ctx->fontContext->fs, state->fontId);
3051
3052 fonsVertMetrics(ctx->fontContext->fs, ascender, descender, lineh);
3053 if (ascender != NULL)
3054 *ascender *= invscale;
3055 if (descender != NULL)
3056 *descender *= invscale;
3057 if (lineh != NULL)
3058 *lineh *= invscale;
3059 }
3060 // vim: ft=c nu noet ts=4