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