2 // Copyright (c) 2013 Mikko Mononen memon@inside.org
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.
25 #define FONTSTASH_IMPLEMENTATION
26 #include "fontstash.h"
29 #define STB_IMAGE_IMPLEMENTATION
30 #include "stb_image.h"
33 #ifdef NVG_DISABLE_SKIPPING_WHITESPACE
34 #define NVG_SKIPPED_CHAR NVG_SPACE
36 #define NVG_SKIPPED_CHAR NVG_CHAR
39 #ifndef NVG_FONT_TEXTURE_FLAGS
40 #define NVG_FONT_TEXTURE_FLAGS 0
44 #pragma warning(disable: 4100) // unreferenced formal parameter
45 #pragma warning(disable: 4127) // conditional expression is constant
46 #pragma warning(disable: 4204) // nonstandard extension used : non-constant aggregate initializer
47 #pragma warning(disable: 4706) // assignment within conditional expression
50 #define NVG_INIT_FONTIMAGE_SIZE 512
51 #define NVG_MAX_FONTIMAGE_SIZE 2048
52 #define NVG_MAX_FONTIMAGES 4
54 #define NVG_INIT_COMMANDS_SIZE 256
55 #define NVG_INIT_POINTS_SIZE 128
56 #define NVG_INIT_PATHS_SIZE 16
57 #define NVG_INIT_VERTS_SIZE 256
58 #define NVG_MAX_STATES 32
60 #define NVG_KAPPA90 0.5522847493f // Length proportional to radius of a cubic bezier handle for 90deg arcs.
62 #define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr]))
78 NVG_PR_INNERBEVEL
= 0x08,
82 NVGcompositeOperationState compositeOperation
;
100 typedef struct NVGstate NVGstate
;
109 typedef struct NVGpoint NVGpoint
;
111 struct NVGpathCache
{
123 typedef struct NVGpathCache NVGpathCache
;
125 struct NVGfontContext
{ // Fontstash context plus font images; shared between shared NanoVG contexts.
127 struct FONScontext
* fs
;
128 int fontImages
[NVG_MAX_FONTIMAGES
];
131 typedef struct NVGfontContext NVGfontContext
;
138 float commandx
, commandy
;
139 NVGstate states
[NVG_MAX_STATES
];
146 NVGfontContext
* fontContext
;
153 static float nvg__sqrtf(float a
) { return sqrtf(a
); }
154 static float nvg__modf(float a
, float b
) { return fmodf(a
, b
); }
155 static float nvg__sinf(float a
) { return sinf(a
); }
156 static float nvg__cosf(float a
) { return cosf(a
); }
157 static float nvg__tanf(float a
) { return tanf(a
); }
158 static float nvg__atan2f(float a
,float b
) { return atan2f(a
, b
); }
159 static float nvg__acosf(float a
) { return acosf(a
); }
161 static int nvg__mini(int a
, int b
) { return a
< b
? a
: b
; }
162 static int nvg__maxi(int a
, int b
) { return a
> b
? a
: b
; }
163 static int nvg__clampi(int a
, int mn
, int mx
) { return a
< mn
? mn
: (a
> mx
? mx
: a
); }
164 static float nvg__minf(float a
, float b
) { return a
< b
? a
: b
; }
165 static float nvg__maxf(float a
, float b
) { return a
> b
? a
: b
; }
166 static float nvg__absf(float a
) { return a
>= 0.0f
? a
: -a
; }
167 static float nvg__signf(float a
) { return a
>= 0.0f
? 1.0f
: -1.0f
; }
168 static float nvg__clampf(float a
, float mn
, float mx
) { return a
< mn
? mn
: (a
> mx
? mx
: a
); }
169 static float nvg__cross(float dx0
, float dy0
, float dx1
, float dy1
) { return dx1
*dy0
- dx0
*dy1
; }
171 static float nvg__normalize(float *x
, float* y
)
173 float d
= nvg__sqrtf((*x
)*(*x
) + (*y
)*(*y
));
183 static void nvg__deletePathCache(NVGpathCache
* c
)
185 if (c
== NULL
) return;
186 if (c
->points
!= NULL
) free(c
->points
);
187 if (c
->paths
!= NULL
) free(c
->paths
);
188 if (c
->verts
!= NULL
) free(c
->verts
);
192 static NVGpathCache
* nvg__allocPathCache(void)
194 NVGpathCache
* c
= (NVGpathCache
*)malloc(sizeof(NVGpathCache
));
195 if (c
== NULL
) goto error
;
196 memset(c
, 0, sizeof(NVGpathCache
));
198 c
->points
= (NVGpoint
*)malloc(sizeof(NVGpoint
)*NVG_INIT_POINTS_SIZE
);
199 if (!c
->points
) goto error
;
201 c
->cpoints
= NVG_INIT_POINTS_SIZE
;
203 c
->paths
= (NVGpath
*)malloc(sizeof(NVGpath
)*NVG_INIT_PATHS_SIZE
);
204 if (!c
->paths
) goto error
;
206 c
->cpaths
= NVG_INIT_PATHS_SIZE
;
208 c
->verts
= (NVGvertex
*)malloc(sizeof(NVGvertex
)*NVG_INIT_VERTS_SIZE
);
209 if (!c
->verts
) goto error
;
211 c
->cverts
= NVG_INIT_VERTS_SIZE
;
215 nvg__deletePathCache(c
);
219 static void nvg__setDevicePixelRatio(NVGcontext
* ctx
, float ratio
)
221 ctx
->tessTol
= 0.25f
/ ratio
;
222 ctx
->distTol
= 0.01f
/ ratio
;
223 ctx
->fringeWidth
= 1.0f
/ ratio
;
224 ctx
->devicePxRatio
= ratio
;
227 static NVGcompositeOperationState
nvg__compositeOperationState(int op
)
229 int sfactor
, dfactor
;
231 if (op
== NVG_SOURCE_OVER
)
234 dfactor
= NVG_ONE_MINUS_SRC_ALPHA
;
236 else if (op
== NVG_SOURCE_IN
)
238 sfactor
= NVG_DST_ALPHA
;
241 else if (op
== NVG_SOURCE_OUT
)
243 sfactor
= NVG_ONE_MINUS_DST_ALPHA
;
246 else if (op
== NVG_ATOP
)
248 sfactor
= NVG_DST_ALPHA
;
249 dfactor
= NVG_ONE_MINUS_SRC_ALPHA
;
251 else if (op
== NVG_DESTINATION_OVER
)
253 sfactor
= NVG_ONE_MINUS_DST_ALPHA
;
256 else if (op
== NVG_DESTINATION_IN
)
259 dfactor
= NVG_SRC_ALPHA
;
261 else if (op
== NVG_DESTINATION_OUT
)
264 dfactor
= NVG_ONE_MINUS_SRC_ALPHA
;
266 else if (op
== NVG_DESTINATION_ATOP
)
268 sfactor
= NVG_ONE_MINUS_DST_ALPHA
;
269 dfactor
= NVG_SRC_ALPHA
;
271 else if (op
== NVG_LIGHTER
)
276 else if (op
== NVG_COPY
)
281 else if (op
== NVG_XOR
)
283 sfactor
= NVG_ONE_MINUS_DST_ALPHA
;
284 dfactor
= NVG_ONE_MINUS_SRC_ALPHA
;
292 NVGcompositeOperationState state
;
293 state
.srcRGB
= sfactor
;
294 state
.dstRGB
= dfactor
;
295 state
.srcAlpha
= sfactor
;
296 state
.dstAlpha
= dfactor
;
300 static NVGstate
* nvg__getState(NVGcontext
* ctx
)
302 return &ctx
->states
[ctx
->nstates
-1];
305 NVGcontext
* nvgCreateInternal(NVGparams
* params
, NVGcontext
* other
) // Share the fonts and images of 'other' if it's non-NULL.
307 FONSparams fontParams
;
308 NVGcontext
* ctx
= (NVGcontext
*)malloc(sizeof(NVGcontext
));
310 if (ctx
== NULL
) goto error
;
311 memset(ctx
, 0, sizeof(NVGcontext
));
313 ctx
->params
= *params
;
315 ctx
->fontContext
= other
->fontContext
;
316 ctx
->fontContext
->refCount
++;
318 ctx
->fontContext
= (NVGfontContext
*)malloc(sizeof(NVGfontContext
));
319 if (ctx
->fontContext
== NULL
) goto error
;
320 for (i
= 0; i
< NVG_MAX_FONTIMAGES
; i
++)
321 ctx
->fontContext
->fontImages
[i
] = 0;
322 ctx
->fontContext
->refCount
= 1;
325 ctx
->commands
= (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE
);
326 if (!ctx
->commands
) goto error
;
328 ctx
->ccommands
= NVG_INIT_COMMANDS_SIZE
;
330 ctx
->cache
= nvg__allocPathCache();
331 if (ctx
->cache
== NULL
) goto error
;
336 nvg__setDevicePixelRatio(ctx
, 1.0f
);
338 if (ctx
->params
.renderCreate(ctx
->params
.userPtr
, other
? other
->params
.userPtr
: NULL
) == 0) goto error
;
340 // Init font rendering
342 memset(&fontParams
, 0, sizeof(fontParams
));
343 fontParams
.width
= NVG_INIT_FONTIMAGE_SIZE
;
344 fontParams
.height
= NVG_INIT_FONTIMAGE_SIZE
;
345 fontParams
.flags
= FONS_ZERO_TOPLEFT
;
346 fontParams
.renderCreate
= NULL
;
347 fontParams
.renderUpdate
= NULL
;
348 fontParams
.renderDraw
= NULL
;
349 fontParams
.renderDelete
= NULL
;
350 fontParams
.userPtr
= NULL
;
351 ctx
->fontContext
->fs
= fonsCreateInternal(&fontParams
);
352 if (ctx
->fontContext
->fs
== NULL
) goto error
;
354 // Create font texture
355 ctx
->fontContext
->fontImages
[0] = ctx
->params
.renderCreateTexture(ctx
->params
.userPtr
,
359 NVG_FONT_TEXTURE_FLAGS
,
361 if (ctx
->fontContext
->fontImages
[0] == 0) goto error
;
362 ctx
->fontContext
->fontImageIdx
= 0;
368 nvgDeleteInternal(ctx
);
372 NVGparams
* nvgInternalParams(NVGcontext
* ctx
)
377 void nvgDeleteInternal(NVGcontext
* ctx
)
380 if (ctx
== NULL
) return;
381 if (ctx
->commands
!= NULL
) free(ctx
->commands
);
382 if (ctx
->cache
!= NULL
) nvg__deletePathCache(ctx
->cache
);
384 if (ctx
->fontContext
!= NULL
&& --ctx
->fontContext
->refCount
== 0) {
385 if (ctx
->fontContext
->fs
)
386 fonsDeleteInternal(ctx
->fontContext
->fs
);
388 for (i
= 0; i
< NVG_MAX_FONTIMAGES
; i
++) {
389 if (ctx
->fontContext
->fontImages
[i
] != 0) {
390 nvgDeleteImage(ctx
, ctx
->fontContext
->fontImages
[i
]);
391 ctx
->fontContext
->fontImages
[i
] = 0;
395 free(ctx
->fontContext
);
398 if (ctx
->params
.renderDelete
!= NULL
)
399 ctx
->params
.renderDelete(ctx
->params
.userPtr
);
404 void nvgBeginFrame(NVGcontext
* ctx
, float windowWidth
, float windowHeight
, float devicePixelRatio
)
406 /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
407 ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
408 ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/
414 nvg__setDevicePixelRatio(ctx
, devicePixelRatio
);
416 ctx
->params
.renderViewport(ctx
->params
.userPtr
, windowWidth
, windowHeight
, devicePixelRatio
);
418 ctx
->drawCallCount
= 0;
419 ctx
->fillTriCount
= 0;
420 ctx
->strokeTriCount
= 0;
421 ctx
->textTriCount
= 0;
424 void nvgCancelFrame(NVGcontext
* ctx
)
426 ctx
->params
.renderCancel(ctx
->params
.userPtr
);
429 void nvgEndFrame(NVGcontext
* ctx
)
431 ctx
->params
.renderFlush(ctx
->params
.userPtr
);
432 if (ctx
->fontContext
->fontImageIdx
!= 0) {
433 int fontImage
= ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
];
435 // delete images that smaller than current one
438 nvgImageSize(ctx
, fontImage
, &iw
, &ih
);
439 for (i
= j
= 0; i
< ctx
->fontContext
->fontImageIdx
; i
++) {
440 if (ctx
->fontContext
->fontImages
[i
] != 0) {
442 nvgImageSize(ctx
, ctx
->fontContext
->fontImages
[i
], &nw
, &nh
);
443 if (nw
< iw
|| nh
< ih
)
444 nvgDeleteImage(ctx
, ctx
->fontContext
->fontImages
[i
]);
446 ctx
->fontContext
->fontImages
[j
++] = ctx
->fontContext
->fontImages
[i
];
449 // make current font image to first
450 ctx
->fontContext
->fontImages
[j
++] = ctx
->fontContext
->fontImages
[0];
451 ctx
->fontContext
->fontImages
[0] = fontImage
;
452 ctx
->fontContext
->fontImageIdx
= 0;
453 // clear all images after j
454 for (i
= j
; i
< NVG_MAX_FONTIMAGES
; i
++)
455 ctx
->fontContext
->fontImages
[i
] = 0;
459 NVGcolor
nvgRGB(unsigned char r
, unsigned char g
, unsigned char b
)
461 return nvgRGBA(r
,g
,b
,255);
464 NVGcolor
nvgRGBf(float r
, float g
, float b
)
466 return nvgRGBAf(r
,g
,b
,1.0f
);
469 NVGcolor
nvgRGBA(unsigned char r
, unsigned char g
, unsigned char b
, unsigned char a
)
472 // Use longer initialization to suppress warning.
473 color
.r
= r
/ 255.0f
;
474 color
.g
= g
/ 255.0f
;
475 color
.b
= b
/ 255.0f
;
476 color
.a
= a
/ 255.0f
;
480 NVGcolor
nvgRGBAf(float r
, float g
, float b
, float a
)
483 // Use longer initialization to suppress warning.
491 NVGcolor
nvgTransRGBA(NVGcolor c
, unsigned char a
)
497 NVGcolor
nvgTransRGBAf(NVGcolor c
, float a
)
503 NVGcolor
nvgLerpRGBA(NVGcolor c0
, NVGcolor c1
, float u
)
507 NVGcolor cint
= {{{0}}};
509 u
= nvg__clampf(u
, 0.0f
, 1.0f
);
511 for( i
= 0; i
<4; i
++ )
513 cint
.rgba
[i
] = c0
.rgba
[i
] * oneminu
+ c1
.rgba
[i
] * u
;
519 NVGcolor
nvgHSL(float h
, float s
, float l
)
521 return nvgHSLA(h
,s
,l
,255);
524 static float nvg__hue(float h
, float m1
, float m2
)
529 return m1
+ (m2
- m1
) * h
* 6.0f
;
530 else if (h
< 3.0f
/6.0f
)
532 else if (h
< 4.0f
/6.0f
)
533 return m1
+ (m2
- m1
) * (2.0f
/3.0f
- h
) * 6.0f
;
537 NVGcolor
nvgHSLA(float h
, float s
, float l
, unsigned char a
)
541 h
= nvg__modf(h
, 1.0f
);
542 if (h
< 0.0f
) h
+= 1.0f
;
543 s
= nvg__clampf(s
, 0.0f
, 1.0f
);
544 l
= nvg__clampf(l
, 0.0f
, 1.0f
);
545 m2
= l
<= 0.5f
? (l
* (1 + s
)) : (l
+ s
- l
* s
);
547 col
.r
= nvg__clampf(nvg__hue(h
+ 1.0f
/3.0f
, m1
, m2
), 0.0f
, 1.0f
);
548 col
.g
= nvg__clampf(nvg__hue(h
, m1
, m2
), 0.0f
, 1.0f
);
549 col
.b
= nvg__clampf(nvg__hue(h
- 1.0f
/3.0f
, m1
, m2
), 0.0f
, 1.0f
);
554 void nvgTransformIdentity(float* t
)
556 t
[0] = 1.0f
; t
[1] = 0.0f
;
557 t
[2] = 0.0f
; t
[3] = 1.0f
;
558 t
[4] = 0.0f
; t
[5] = 0.0f
;
561 void nvgTransformTranslate(float* t
, float tx
, float ty
)
563 t
[0] = 1.0f
; t
[1] = 0.0f
;
564 t
[2] = 0.0f
; t
[3] = 1.0f
;
565 t
[4] = tx
; t
[5] = ty
;
568 void nvgTransformScale(float* t
, float sx
, float sy
)
570 t
[0] = sx
; t
[1] = 0.0f
;
571 t
[2] = 0.0f
; t
[3] = sy
;
572 t
[4] = 0.0f
; t
[5] = 0.0f
;
575 void nvgTransformRotate(float* t
, float a
)
577 float cs
= nvg__cosf(a
), sn
= nvg__sinf(a
);
578 t
[0] = cs
; t
[1] = sn
;
579 t
[2] = -sn
; t
[3] = cs
;
580 t
[4] = 0.0f
; t
[5] = 0.0f
;
583 void nvgTransformSkewX(float* t
, float a
)
585 t
[0] = 1.0f
; t
[1] = 0.0f
;
586 t
[2] = nvg__tanf(a
); t
[3] = 1.0f
;
587 t
[4] = 0.0f
; t
[5] = 0.0f
;
590 void nvgTransformSkewY(float* t
, float a
)
592 t
[0] = 1.0f
; t
[1] = nvg__tanf(a
);
593 t
[2] = 0.0f
; t
[3] = 1.0f
;
594 t
[4] = 0.0f
; t
[5] = 0.0f
;
597 void nvgTransformMultiply(float* t
, const float* s
)
599 float t0
= t
[0] * s
[0] + t
[1] * s
[2];
600 float t2
= t
[2] * s
[0] + t
[3] * s
[2];
601 float t4
= t
[4] * s
[0] + t
[5] * s
[2] + s
[4];
602 t
[1] = t
[0] * s
[1] + t
[1] * s
[3];
603 t
[3] = t
[2] * s
[1] + t
[3] * s
[3];
604 t
[5] = t
[4] * s
[1] + t
[5] * s
[3] + s
[5];
610 void nvgTransformPremultiply(float* t
, const float* s
)
613 memcpy(s2
, s
, sizeof(float)*6);
614 nvgTransformMultiply(s2
, t
);
615 memcpy(t
, s2
, sizeof(float)*6);
618 int nvgTransformInverse(float* inv
, const float* t
)
620 double invdet
, det
= (double)t
[0] * t
[3] - (double)t
[2] * t
[1];
621 if (det
> -1e-6 && det
< 1e-6) {
622 nvgTransformIdentity(inv
);
626 inv
[0] = (float)(t
[3] * invdet
);
627 inv
[2] = (float)(-t
[2] * invdet
);
628 inv
[4] = (float)(((double)t
[2] * t
[5] - (double)t
[3] * t
[4]) * invdet
);
629 inv
[1] = (float)(-t
[1] * invdet
);
630 inv
[3] = (float)(t
[0] * invdet
);
631 inv
[5] = (float)(((double)t
[1] * t
[4] - (double)t
[0] * t
[5]) * invdet
);
635 void nvgTransformPoint(float* dx
, float* dy
, const float* t
, float sx
, float sy
)
637 *dx
= sx
*t
[0] + sy
*t
[2] + t
[4];
638 *dy
= sx
*t
[1] + sy
*t
[3] + t
[5];
641 float nvgDegToRad(float deg
)
643 return deg
/ 180.0f
* NVG_PI
;
646 float nvgRadToDeg(float rad
)
648 return rad
/ NVG_PI
* 180.0f
;
651 static void nvg__setPaintColor(NVGpaint
* p
, NVGcolor color
)
653 memset(p
, 0, sizeof(*p
));
654 nvgTransformIdentity(p
->xform
);
657 p
->innerColor
= color
;
658 p
->outerColor
= color
;
663 void nvgSave(NVGcontext
* ctx
)
665 if (ctx
->nstates
>= NVG_MAX_STATES
)
667 if (ctx
->nstates
> 0)
668 memcpy(&ctx
->states
[ctx
->nstates
], &ctx
->states
[ctx
->nstates
-1], sizeof(NVGstate
));
672 void nvgRestore(NVGcontext
* ctx
)
674 if (ctx
->nstates
<= 1)
679 void nvgReset(NVGcontext
* ctx
)
681 NVGstate
* state
= nvg__getState(ctx
);
682 memset(state
, 0, sizeof(*state
));
684 nvg__setPaintColor(&state
->fill
, nvgRGBA(255,255,255,255));
685 nvg__setPaintColor(&state
->stroke
, nvgRGBA(0,0,0,255));
686 state
->compositeOperation
= nvg__compositeOperationState(NVG_SOURCE_OVER
);
687 state
->shapeAntiAlias
= 1;
688 state
->strokeWidth
= 1.0f
;
689 state
->miterLimit
= 10.0f
;
690 state
->lineCap
= NVG_BUTT
;
691 state
->lineJoin
= NVG_MITER
;
692 state
->tint
= nvgRGBAf(1, 1, 1, 1);
693 nvgTransformIdentity(state
->xform
);
695 state
->scissor
.extent
[0] = -1.0f
;
696 state
->scissor
.extent
[1] = -1.0f
;
698 state
->fontSize
= 16.0f
;
699 state
->letterSpacing
= 0.0f
;
700 state
->lineHeight
= 1.0f
;
701 state
->fontBlur
= 0.0f
;
702 state
->textAlign
= NVG_ALIGN_LEFT
| NVG_ALIGN_BASELINE
;
707 void nvgShapeAntiAlias(NVGcontext
* ctx
, int enabled
)
709 NVGstate
* state
= nvg__getState(ctx
);
710 state
->shapeAntiAlias
= enabled
;
713 void nvgStrokeWidth(NVGcontext
* ctx
, float width
)
715 NVGstate
* state
= nvg__getState(ctx
);
716 state
->strokeWidth
= width
;
719 void nvgMiterLimit(NVGcontext
* ctx
, float limit
)
721 NVGstate
* state
= nvg__getState(ctx
);
722 state
->miterLimit
= limit
;
725 void nvgLineCap(NVGcontext
* ctx
, int cap
)
727 NVGstate
* state
= nvg__getState(ctx
);
728 state
->lineCap
= cap
;
731 void nvgLineJoin(NVGcontext
* ctx
, int join
)
733 NVGstate
* state
= nvg__getState(ctx
);
734 state
->lineJoin
= join
;
737 void nvgGlobalAlpha(NVGcontext
* ctx
, float alpha
)
739 NVGstate
* state
= nvg__getState(ctx
);
740 state
->tint
.a
= alpha
;
743 void nvgGlobalTint(NVGcontext
* ctx
, NVGcolor tint
)
745 NVGstate
* state
= nvg__getState(ctx
);
749 NVGcolor
nvgGetGlobalTint(NVGcontext
* ctx
)
751 NVGstate
* state
= nvg__getState(ctx
);
755 void nvgAlpha(NVGcontext
* ctx
, float alpha
)
757 NVGstate
* state
= nvg__getState(ctx
);
758 state
->tint
.a
*= alpha
;
761 void nvgTint(NVGcontext
* ctx
, NVGcolor tint
)
763 NVGstate
* state
= nvg__getState(ctx
);
765 for (i
= 0; i
< 4; i
++)
766 state
->tint
.rgba
[i
] *= tint
.rgba
[i
];
769 void nvgTransform(NVGcontext
* ctx
, float a
, float b
, float c
, float d
, float e
, float f
)
771 NVGstate
* state
= nvg__getState(ctx
);
772 float t
[6] = { a
, b
, c
, d
, e
, f
};
773 nvgTransformPremultiply(state
->xform
, t
);
776 void nvgResetTransform(NVGcontext
* ctx
)
778 NVGstate
* state
= nvg__getState(ctx
);
779 nvgTransformIdentity(state
->xform
);
782 void nvgTranslate(NVGcontext
* ctx
, float x
, float y
)
784 NVGstate
* state
= nvg__getState(ctx
);
786 nvgTransformTranslate(t
, x
,y
);
787 nvgTransformPremultiply(state
->xform
, t
);
790 void nvgRotate(NVGcontext
* ctx
, float angle
)
792 NVGstate
* state
= nvg__getState(ctx
);
794 nvgTransformRotate(t
, angle
);
795 nvgTransformPremultiply(state
->xform
, t
);
798 void nvgSkewX(NVGcontext
* ctx
, float angle
)
800 NVGstate
* state
= nvg__getState(ctx
);
802 nvgTransformSkewX(t
, angle
);
803 nvgTransformPremultiply(state
->xform
, t
);
806 void nvgSkewY(NVGcontext
* ctx
, float angle
)
808 NVGstate
* state
= nvg__getState(ctx
);
810 nvgTransformSkewY(t
, angle
);
811 nvgTransformPremultiply(state
->xform
, t
);
814 void nvgScale(NVGcontext
* ctx
, float x
, float y
)
816 NVGstate
* state
= nvg__getState(ctx
);
818 nvgTransformScale(t
, x
,y
);
819 nvgTransformPremultiply(state
->xform
, t
);
822 void nvgCurrentTransform(NVGcontext
* ctx
, float* xform
)
824 NVGstate
* state
= nvg__getState(ctx
);
825 if (xform
== NULL
) return;
826 memcpy(xform
, state
->xform
, sizeof(float)*6);
829 void nvgStrokeColor(NVGcontext
* ctx
, NVGcolor color
)
831 NVGstate
* state
= nvg__getState(ctx
);
832 nvg__setPaintColor(&state
->stroke
, color
);
835 void nvgStrokePaint(NVGcontext
* ctx
, NVGpaint paint
)
837 NVGstate
* state
= nvg__getState(ctx
);
838 state
->stroke
= paint
;
839 nvgTransformMultiply(state
->stroke
.xform
, state
->xform
);
842 void nvgFillColor(NVGcontext
* ctx
, NVGcolor color
)
844 NVGstate
* state
= nvg__getState(ctx
);
845 nvg__setPaintColor(&state
->fill
, color
);
848 void nvgFillPaint(NVGcontext
* ctx
, NVGpaint paint
)
850 NVGstate
* state
= nvg__getState(ctx
);
852 nvgTransformMultiply(state
->fill
.xform
, state
->xform
);
856 int nvgCreateImage(NVGcontext
* ctx
, const char* filename
, int imageFlags
)
860 stbi_set_unpremultiply_on_load(1);
861 stbi_convert_iphone_png_to_rgb(1);
862 img
= stbi_load(filename
, &w
, &h
, &n
, 4);
864 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
867 image
= nvgCreateImageRGBA(ctx
, w
, h
, imageFlags
, img
);
868 stbi_image_free(img
);
872 int nvgCreateImageMem(NVGcontext
* ctx
, int imageFlags
, unsigned char* data
, int ndata
)
875 unsigned char* img
= stbi_load_from_memory(data
, ndata
, &w
, &h
, &n
, 4);
877 // printf("Failed to load %s - %s\n", filename, stbi_failure_reason());
880 image
= nvgCreateImageRGBA(ctx
, w
, h
, imageFlags
, img
);
881 stbi_image_free(img
);
886 int nvgCreateImageRaw(NVGcontext
* ctx
, int w
, int h
, int imageFlags
, NVGtexture format
, const unsigned char* data
)
888 return ctx
->params
.renderCreateTexture(ctx
->params
.userPtr
, format
, w
, h
, imageFlags
, data
);
891 int nvgCreateImageRGBA(NVGcontext
* ctx
, int w
, int h
, int imageFlags
, const unsigned char* data
)
893 return nvgCreateImageRaw(ctx
, w
, h
, imageFlags
, NVG_TEXTURE_RGBA
, data
);
896 void nvgUpdateImage(NVGcontext
* ctx
, int image
, const unsigned char* data
)
899 ctx
->params
.renderGetTextureSize(ctx
->params
.userPtr
, image
, &w
, &h
);
900 ctx
->params
.renderUpdateTexture(ctx
->params
.userPtr
, image
, 0,0, w
,h
, data
);
903 void nvgImageSize(NVGcontext
* ctx
, int image
, int* w
, int* h
)
905 ctx
->params
.renderGetTextureSize(ctx
->params
.userPtr
, image
, w
, h
);
908 void nvgDeleteImage(NVGcontext
* ctx
, int image
)
910 ctx
->params
.renderDeleteTexture(ctx
->params
.userPtr
, image
);
913 NVGpaint
nvgLinearGradient(NVGcontext
* ctx
,
914 float sx
, float sy
, float ex
, float ey
,
915 NVGcolor icol
, NVGcolor ocol
)
919 const float large
= 1e5
;
921 memset(&p
, 0, sizeof(p
));
923 // Calculate transform aligned to the line
926 d
= sqrtf(dx
*dx
+ dy
*dy
);
935 p
.xform
[0] = dy
; p
.xform
[1] = -dx
;
936 p
.xform
[2] = dx
; p
.xform
[3] = dy
;
937 p
.xform
[4] = sx
- dx
*large
; p
.xform
[5] = sy
- dy
*large
;
940 p
.extent
[1] = large
+ d
*0.5f
;
944 p
.feather
= nvg__maxf(1.0f
, d
);
952 NVGpaint
nvgRadialGradient(NVGcontext
* ctx
,
953 float cx
, float cy
, float inr
, float outr
,
954 NVGcolor icol
, NVGcolor ocol
)
957 float r
= (inr
+outr
)*0.5f
;
958 float f
= (outr
-inr
);
960 memset(&p
, 0, sizeof(p
));
962 nvgTransformIdentity(p
.xform
);
971 p
.feather
= nvg__maxf(1.0f
, f
);
979 NVGpaint
nvgBoxGradient(NVGcontext
* ctx
,
980 float x
, float y
, float w
, float h
, float r
, float f
,
981 NVGcolor icol
, NVGcolor ocol
)
985 memset(&p
, 0, sizeof(p
));
987 nvgTransformIdentity(p
.xform
);
988 p
.xform
[4] = x
+w
*0.5f
;
989 p
.xform
[5] = y
+h
*0.5f
;
991 p
.extent
[0] = w
*0.5f
;
992 p
.extent
[1] = h
*0.5f
;
996 p
.feather
= nvg__maxf(1.0f
, f
);
1005 NVGpaint
nvgImagePattern(NVGcontext
* ctx
,
1006 float cx
, float cy
, float w
, float h
, float angle
,
1007 int image
, float alpha
)
1011 memset(&p
, 0, sizeof(p
));
1013 nvgTransformRotate(p
.xform
, angle
);
1022 p
.innerColor
= p
.outerColor
= nvgRGBAf(1,1,1,alpha
);
1028 void nvgScissor(NVGcontext
* ctx
, float x
, float y
, float w
, float h
)
1030 NVGstate
* state
= nvg__getState(ctx
);
1032 w
= nvg__maxf(0.0f
, w
);
1033 h
= nvg__maxf(0.0f
, h
);
1035 nvgTransformIdentity(state
->scissor
.xform
);
1036 state
->scissor
.xform
[4] = x
+w
*0.5f
;
1037 state
->scissor
.xform
[5] = y
+h
*0.5f
;
1038 nvgTransformMultiply(state
->scissor
.xform
, state
->xform
);
1040 state
->scissor
.extent
[0] = w
*0.5f
;
1041 state
->scissor
.extent
[1] = h
*0.5f
;
1044 static void nvg__isectRects(float* dst
,
1045 float ax
, float ay
, float aw
, float ah
,
1046 float bx
, float by
, float bw
, float bh
)
1048 float minx
= nvg__maxf(ax
, bx
);
1049 float miny
= nvg__maxf(ay
, by
);
1050 float maxx
= nvg__minf(ax
+aw
, bx
+bw
);
1051 float maxy
= nvg__minf(ay
+ah
, by
+bh
);
1054 dst
[2] = nvg__maxf(0.0f
, maxx
- minx
);
1055 dst
[3] = nvg__maxf(0.0f
, maxy
- miny
);
1058 void nvgIntersectScissor(NVGcontext
* ctx
, float x
, float y
, float w
, float h
)
1060 NVGstate
* state
= nvg__getState(ctx
);
1061 float pxform
[6], invxorm
[6];
1063 float ex
, ey
, tex
, tey
;
1065 // If no previous scissor has been set, set the scissor as current scissor.
1066 if (state
->scissor
.extent
[0] < 0) {
1067 nvgScissor(ctx
, x
, y
, w
, h
);
1071 // Transform the current scissor rect into current transform space.
1072 // If there is difference in rotation, this will be approximation.
1073 memcpy(pxform
, state
->scissor
.xform
, sizeof(float)*6);
1074 ex
= state
->scissor
.extent
[0];
1075 ey
= state
->scissor
.extent
[1];
1076 nvgTransformInverse(invxorm
, state
->xform
);
1077 nvgTransformMultiply(pxform
, invxorm
);
1078 tex
= ex
*nvg__absf(pxform
[0]) + ey
*nvg__absf(pxform
[2]);
1079 tey
= ex
*nvg__absf(pxform
[1]) + ey
*nvg__absf(pxform
[3]);
1082 nvg__isectRects(rect
, pxform
[4]-tex
,pxform
[5]-tey
,tex
*2,tey
*2, x
,y
,w
,h
);
1084 nvgScissor(ctx
, rect
[0], rect
[1], rect
[2], rect
[3]);
1087 void nvgResetScissor(NVGcontext
* ctx
)
1089 NVGstate
* state
= nvg__getState(ctx
);
1090 memset(state
->scissor
.xform
, 0, sizeof(state
->scissor
.xform
));
1091 state
->scissor
.extent
[0] = -1.0f
;
1092 state
->scissor
.extent
[1] = -1.0f
;
1095 // Global composite operation.
1096 void nvgGlobalCompositeOperation(NVGcontext
* ctx
, int op
)
1098 NVGstate
* state
= nvg__getState(ctx
);
1099 state
->compositeOperation
= nvg__compositeOperationState(op
);
1102 void nvgGlobalCompositeBlendFunc(NVGcontext
* ctx
, int sfactor
, int dfactor
)
1104 nvgGlobalCompositeBlendFuncSeparate(ctx
, sfactor
, dfactor
, sfactor
, dfactor
);
1107 void nvgGlobalCompositeBlendFuncSeparate(NVGcontext
* ctx
, int srcRGB
, int dstRGB
, int srcAlpha
, int dstAlpha
)
1109 NVGcompositeOperationState op
;
1112 op
.srcAlpha
= srcAlpha
;
1113 op
.dstAlpha
= dstAlpha
;
1115 NVGstate
* state
= nvg__getState(ctx
);
1116 state
->compositeOperation
= op
;
1119 static int nvg__ptEquals(float x1
, float y1
, float x2
, float y2
, float tol
)
1123 return dx
*dx
+ dy
*dy
< tol
*tol
;
1126 static float nvg__distPtSeg(float x
, float y
, float px
, float py
, float qx
, float qy
)
1128 float pqx
, pqy
, dx
, dy
, d
, t
;
1133 d
= pqx
*pqx
+ pqy
*pqy
;
1134 t
= pqx
*dx
+ pqy
*dy
;
1137 else if (t
> 1) t
= 1;
1138 dx
= px
+ t
*pqx
- x
;
1139 dy
= py
+ t
*pqy
- y
;
1140 return dx
*dx
+ dy
*dy
;
1143 static void nvg__appendCommands(NVGcontext
* ctx
, float* vals
, int nvals
)
1145 NVGstate
* state
= nvg__getState(ctx
);
1148 if (ctx
->ncommands
+nvals
> ctx
->ccommands
) {
1150 int ccommands
= ctx
->ncommands
+nvals
+ ctx
->ccommands
/2;
1151 commands
= (float*)realloc(ctx
->commands
, sizeof(float)*ccommands
);
1152 if (commands
== NULL
) return;
1153 ctx
->commands
= commands
;
1154 ctx
->ccommands
= ccommands
;
1157 if ((int)vals
[0] != NVG_CLOSE
&& (int)vals
[0] != NVG_WINDING
) {
1158 ctx
->commandx
= vals
[nvals
-2];
1159 ctx
->commandy
= vals
[nvals
-1];
1162 // transform commands
1165 int cmd
= (int)vals
[i
];
1168 nvgTransformPoint(&vals
[i
+1],&vals
[i
+2], state
->xform
, vals
[i
+1],vals
[i
+2]);
1172 nvgTransformPoint(&vals
[i
+1],&vals
[i
+2], state
->xform
, vals
[i
+1],vals
[i
+2]);
1176 nvgTransformPoint(&vals
[i
+1],&vals
[i
+2], state
->xform
, vals
[i
+1],vals
[i
+2]);
1177 nvgTransformPoint(&vals
[i
+3],&vals
[i
+4], state
->xform
, vals
[i
+3],vals
[i
+4]);
1178 nvgTransformPoint(&vals
[i
+5],&vals
[i
+6], state
->xform
, vals
[i
+5],vals
[i
+6]);
1192 memcpy(&ctx
->commands
[ctx
->ncommands
], vals
, nvals
*sizeof(float));
1194 ctx
->ncommands
+= nvals
;
1198 static void nvg__clearPathCache(NVGcontext
* ctx
)
1200 ctx
->cache
->npoints
= 0;
1201 ctx
->cache
->npaths
= 0;
1204 static NVGpath
* nvg__lastPath(NVGcontext
* ctx
)
1206 if (ctx
->cache
->npaths
> 0)
1207 return &ctx
->cache
->paths
[ctx
->cache
->npaths
-1];
1211 static void nvg__addPath(NVGcontext
* ctx
)
1214 if (ctx
->cache
->npaths
+1 > ctx
->cache
->cpaths
) {
1216 int cpaths
= ctx
->cache
->npaths
+1 + ctx
->cache
->cpaths
/2;
1217 paths
= (NVGpath
*)realloc(ctx
->cache
->paths
, sizeof(NVGpath
)*cpaths
);
1218 if (paths
== NULL
) return;
1219 ctx
->cache
->paths
= paths
;
1220 ctx
->cache
->cpaths
= cpaths
;
1222 path
= &ctx
->cache
->paths
[ctx
->cache
->npaths
];
1223 memset(path
, 0, sizeof(*path
));
1224 path
->first
= ctx
->cache
->npoints
;
1225 path
->winding
= NVG_CCW
;
1227 ctx
->cache
->npaths
++;
1230 static NVGpoint
* nvg__lastPoint(NVGcontext
* ctx
)
1232 if (ctx
->cache
->npoints
> 0)
1233 return &ctx
->cache
->points
[ctx
->cache
->npoints
-1];
1237 static void nvg__addPoint(NVGcontext
* ctx
, float x
, float y
, int flags
)
1239 NVGpath
* path
= nvg__lastPath(ctx
);
1241 if (path
== NULL
) return;
1243 if (path
->count
> 0 && ctx
->cache
->npoints
> 0) {
1244 pt
= nvg__lastPoint(ctx
);
1245 if (nvg__ptEquals(pt
->x
,pt
->y
, x
,y
, ctx
->distTol
)) {
1251 if (ctx
->cache
->npoints
+1 > ctx
->cache
->cpoints
) {
1253 int cpoints
= ctx
->cache
->npoints
+1 + ctx
->cache
->cpoints
/2;
1254 points
= (NVGpoint
*)realloc(ctx
->cache
->points
, sizeof(NVGpoint
)*cpoints
);
1255 if (points
== NULL
) return;
1256 ctx
->cache
->points
= points
;
1257 ctx
->cache
->cpoints
= cpoints
;
1260 pt
= &ctx
->cache
->points
[ctx
->cache
->npoints
];
1261 memset(pt
, 0, sizeof(*pt
));
1264 pt
->flags
= (unsigned char)flags
;
1266 ctx
->cache
->npoints
++;
1270 static void nvg__closePath(NVGcontext
* ctx
)
1272 NVGpath
* path
= nvg__lastPath(ctx
);
1273 if (path
== NULL
) return;
1277 static void nvg__pathWinding(NVGcontext
* ctx
, int winding
)
1279 NVGpath
* path
= nvg__lastPath(ctx
);
1280 if (path
== NULL
) return;
1281 path
->winding
= winding
;
1284 static float nvg__getAverageScale(float *t
)
1286 float sx
= sqrtf(t
[0]*t
[0] + t
[2]*t
[2]);
1287 float sy
= sqrtf(t
[1]*t
[1] + t
[3]*t
[3]);
1288 return (sx
+ sy
) * 0.5f
;
1291 static NVGvertex
* nvg__allocTempVerts(NVGcontext
* ctx
, int nverts
)
1293 if (nverts
> ctx
->cache
->cverts
) {
1295 int cverts
= (nverts
+ 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly.
1296 verts
= (NVGvertex
*)realloc(ctx
->cache
->verts
, sizeof(NVGvertex
)*cverts
);
1297 if (verts
== NULL
) return NULL
;
1298 ctx
->cache
->verts
= verts
;
1299 ctx
->cache
->cverts
= cverts
;
1302 return ctx
->cache
->verts
;
1305 static float nvg__triarea2(float ax
, float ay
, float bx
, float by
, float cx
, float cy
)
1307 float abx
= bx
- ax
;
1308 float aby
= by
- ay
;
1309 float acx
= cx
- ax
;
1310 float acy
= cy
- ay
;
1311 return acx
*aby
- abx
*acy
;
1314 static float nvg__polyArea(NVGpoint
* pts
, int npts
)
1318 for (i
= 2; i
< npts
; i
++) {
1319 NVGpoint
* a
= &pts
[0];
1320 NVGpoint
* b
= &pts
[i
-1];
1321 NVGpoint
* c
= &pts
[i
];
1322 area
+= nvg__triarea2(a
->x
,a
->y
, b
->x
,b
->y
, c
->x
,c
->y
);
1327 static void nvg__polyReverse(NVGpoint
* pts
, int npts
)
1330 int i
= 0, j
= npts
-1;
1341 static void nvg__vset(NVGvertex
* vtx
, float x
, float y
, float u
, float v
)
1349 static void nvg__tesselateBezier(NVGcontext
* ctx
,
1350 float x1
, float y1
, float x2
, float y2
,
1351 float x3
, float y3
, float x4
, float y4
,
1352 int level
, int type
)
1354 float x12
,y12
,x23
,y23
,x34
,y34
,x123
,y123
,x234
,y234
,x1234
,y1234
;
1357 if (level
> 10) return;
1365 x123
= (x12
+x23
)*0.5f
;
1366 y123
= (y12
+y23
)*0.5f
;
1370 d2
= nvg__absf(((x2
- x4
) * dy
- (y2
- y4
) * dx
));
1371 d3
= nvg__absf(((x3
- x4
) * dy
- (y3
- y4
) * dx
));
1373 if ((d2
+ d3
)*(d2
+ d3
) < ctx
->tessTol
* (dx
*dx
+ dy
*dy
)) {
1374 nvg__addPoint(ctx
, x4
, y4
, type
);
1378 /* if (nvg__absf(x1+x3-x2-x2) + nvg__absf(y1+y3-y2-y2) + nvg__absf(x2+x4-x3-x3) + nvg__absf(y2+y4-y3-y3) < ctx->tessTol) {
1379 nvg__addPoint(ctx, x4, y4, type);
1383 x234
= (x23
+x34
)*0.5f
;
1384 y234
= (y23
+y34
)*0.5f
;
1385 x1234
= (x123
+x234
)*0.5f
;
1386 y1234
= (y123
+y234
)*0.5f
;
1388 nvg__tesselateBezier(ctx
, x1
,y1
, x12
,y12
, x123
,y123
, x1234
,y1234
, level
+1, 0);
1389 nvg__tesselateBezier(ctx
, x1234
,y1234
, x234
,y234
, x34
,y34
, x4
,y4
, level
+1, type
);
1392 static void nvg__flattenPaths(NVGcontext
* ctx
)
1394 NVGpathCache
* cache
= ctx
->cache
;
1395 // NVGstate* state = nvg__getState(ctx);
1407 if (cache
->npaths
> 0)
1412 while (i
< ctx
->ncommands
) {
1413 int cmd
= (int)ctx
->commands
[i
];
1417 p
= &ctx
->commands
[i
+1];
1418 nvg__addPoint(ctx
, p
[0], p
[1], NVG_PT_CORNER
);
1422 p
= &ctx
->commands
[i
+1];
1423 nvg__addPoint(ctx
, p
[0], p
[1], NVG_PT_CORNER
);
1427 last
= nvg__lastPoint(ctx
);
1429 cp1
= &ctx
->commands
[i
+1];
1430 cp2
= &ctx
->commands
[i
+3];
1431 p
= &ctx
->commands
[i
+5];
1432 nvg__tesselateBezier(ctx
, last
->x
,last
->y
, cp1
[0],cp1
[1], cp2
[0],cp2
[1], p
[0],p
[1], 0, NVG_PT_CORNER
);
1437 nvg__closePath(ctx
);
1441 nvg__pathWinding(ctx
, (int)ctx
->commands
[i
+1]);
1449 cache
->bounds
[0] = cache
->bounds
[1] = 1e6f
;
1450 cache
->bounds
[2] = cache
->bounds
[3] = -1e6f
;
1452 // Calculate the direction and length of line segments.
1453 for (j
= 0; j
< cache
->npaths
; j
++) {
1454 path
= &cache
->paths
[j
];
1455 pts
= &cache
->points
[path
->first
];
1457 // If the first and last points are the same, remove the last, mark as closed path.
1458 p0
= &pts
[path
->count
-1];
1460 if (nvg__ptEquals(p0
->x
,p0
->y
, p1
->x
,p1
->y
, ctx
->distTol
)) {
1462 p0
= &pts
[path
->count
-1];
1467 if (path
->count
> 2) {
1468 area
= nvg__polyArea(pts
, path
->count
);
1469 if (path
->winding
== NVG_CCW
&& area
< 0.0f
)
1470 nvg__polyReverse(pts
, path
->count
);
1471 if (path
->winding
== NVG_CW
&& area
> 0.0f
)
1472 nvg__polyReverse(pts
, path
->count
);
1475 for(i
= 0; i
< path
->count
; i
++) {
1476 // Calculate segment direction and length
1477 p0
->dx
= p1
->x
- p0
->x
;
1478 p0
->dy
= p1
->y
- p0
->y
;
1479 p0
->len
= nvg__normalize(&p0
->dx
, &p0
->dy
);
1481 cache
->bounds
[0] = nvg__minf(cache
->bounds
[0], p0
->x
);
1482 cache
->bounds
[1] = nvg__minf(cache
->bounds
[1], p0
->y
);
1483 cache
->bounds
[2] = nvg__maxf(cache
->bounds
[2], p0
->x
);
1484 cache
->bounds
[3] = nvg__maxf(cache
->bounds
[3], p0
->y
);
1491 static int nvg__curveDivs(float r
, float arc
, float tol
)
1493 float da
= acosf(r
/ (r
+ tol
)) * 2.0f
;
1494 return nvg__maxi(2, (int)ceilf(arc
/ da
));
1497 static void nvg__chooseBevel(int bevel
, NVGpoint
* p0
, NVGpoint
* p1
, float w
,
1498 float* x0
, float* y0
, float* x1
, float* y1
)
1501 *x0
= p1
->x
+ p0
->dy
* w
;
1502 *y0
= p1
->y
- p0
->dx
* w
;
1503 *x1
= p1
->x
+ p1
->dy
* w
;
1504 *y1
= p1
->y
- p1
->dx
* w
;
1506 *x0
= p1
->x
+ p1
->dmx
* w
;
1507 *y0
= p1
->y
+ p1
->dmy
* w
;
1508 *x1
= p1
->x
+ p1
->dmx
* w
;
1509 *y1
= p1
->y
+ p1
->dmy
* w
;
1513 static NVGvertex
* nvg__roundJoin(NVGvertex
* dst
, NVGpoint
* p0
, NVGpoint
* p1
,
1514 float lw
, float rw
, float lu
, float ru
, int ncap
,
1518 float dlx0
= p0
->dy
;
1519 float dly0
= -p0
->dx
;
1520 float dlx1
= p1
->dy
;
1521 float dly1
= -p1
->dx
;
1522 NVG_NOTUSED(fringe
);
1524 if (p1
->flags
& NVG_PT_LEFT
) {
1525 float lx0
,ly0
,lx1
,ly1
,a0
,a1
;
1526 nvg__chooseBevel(p1
->flags
& NVG_PR_INNERBEVEL
, p0
, p1
, lw
, &lx0
,&ly0
, &lx1
,&ly1
);
1527 a0
= atan2f(-dly0
, -dlx0
);
1528 a1
= atan2f(-dly1
, -dlx1
);
1529 if (a1
> a0
) a1
-= NVG_PI
*2;
1531 nvg__vset(dst
, lx0
, ly0
, lu
,1); dst
++;
1532 nvg__vset(dst
, p1
->x
- dlx0
*rw
, p1
->y
- dly0
*rw
, ru
,1); dst
++;
1534 n
= nvg__clampi((int)ceilf(((a0
- a1
) / NVG_PI
) * ncap
), 2, ncap
);
1535 for (i
= 0; i
< n
; i
++) {
1536 float u
= i
/(float)(n
-1);
1537 float a
= a0
+ u
*(a1
-a0
);
1538 float rx
= p1
->x
+ cosf(a
) * rw
;
1539 float ry
= p1
->y
+ sinf(a
) * rw
;
1540 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1541 nvg__vset(dst
, rx
, ry
, ru
,1); dst
++;
1544 nvg__vset(dst
, lx1
, ly1
, lu
,1); dst
++;
1545 nvg__vset(dst
, p1
->x
- dlx1
*rw
, p1
->y
- dly1
*rw
, ru
,1); dst
++;
1548 float rx0
,ry0
,rx1
,ry1
,a0
,a1
;
1549 nvg__chooseBevel(p1
->flags
& NVG_PR_INNERBEVEL
, p0
, p1
, -rw
, &rx0
,&ry0
, &rx1
,&ry1
);
1550 a0
= atan2f(dly0
, dlx0
);
1551 a1
= atan2f(dly1
, dlx1
);
1552 if (a1
< a0
) a1
+= NVG_PI
*2;
1554 nvg__vset(dst
, p1
->x
+ dlx0
*rw
, p1
->y
+ dly0
*rw
, lu
,1); dst
++;
1555 nvg__vset(dst
, rx0
, ry0
, ru
,1); dst
++;
1557 n
= nvg__clampi((int)ceilf(((a1
- a0
) / NVG_PI
) * ncap
), 2, ncap
);
1558 for (i
= 0; i
< n
; i
++) {
1559 float u
= i
/(float)(n
-1);
1560 float a
= a0
+ u
*(a1
-a0
);
1561 float lx
= p1
->x
+ cosf(a
) * lw
;
1562 float ly
= p1
->y
+ sinf(a
) * lw
;
1563 nvg__vset(dst
, lx
, ly
, lu
,1); dst
++;
1564 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1567 nvg__vset(dst
, p1
->x
+ dlx1
*rw
, p1
->y
+ dly1
*rw
, lu
,1); dst
++;
1568 nvg__vset(dst
, rx1
, ry1
, ru
,1); dst
++;
1574 static NVGvertex
* nvg__bevelJoin(NVGvertex
* dst
, NVGpoint
* p0
, NVGpoint
* p1
,
1575 float lw
, float rw
, float lu
, float ru
, float fringe
)
1577 float rx0
,ry0
,rx1
,ry1
;
1578 float lx0
,ly0
,lx1
,ly1
;
1579 float dlx0
= p0
->dy
;
1580 float dly0
= -p0
->dx
;
1581 float dlx1
= p1
->dy
;
1582 float dly1
= -p1
->dx
;
1583 NVG_NOTUSED(fringe
);
1585 if (p1
->flags
& NVG_PT_LEFT
) {
1586 nvg__chooseBevel(p1
->flags
& NVG_PR_INNERBEVEL
, p0
, p1
, lw
, &lx0
,&ly0
, &lx1
,&ly1
);
1588 nvg__vset(dst
, lx0
, ly0
, lu
,1); dst
++;
1589 nvg__vset(dst
, p1
->x
- dlx0
*rw
, p1
->y
- dly0
*rw
, ru
,1); dst
++;
1591 if (p1
->flags
& NVG_PT_BEVEL
) {
1592 nvg__vset(dst
, lx0
, ly0
, lu
,1); dst
++;
1593 nvg__vset(dst
, p1
->x
- dlx0
*rw
, p1
->y
- dly0
*rw
, ru
,1); dst
++;
1595 nvg__vset(dst
, lx1
, ly1
, lu
,1); dst
++;
1596 nvg__vset(dst
, p1
->x
- dlx1
*rw
, p1
->y
- dly1
*rw
, ru
,1); dst
++;
1598 rx0
= p1
->x
- p1
->dmx
* rw
;
1599 ry0
= p1
->y
- p1
->dmy
* rw
;
1601 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1602 nvg__vset(dst
, p1
->x
- dlx0
*rw
, p1
->y
- dly0
*rw
, ru
,1); dst
++;
1604 nvg__vset(dst
, rx0
, ry0
, ru
,1); dst
++;
1605 nvg__vset(dst
, rx0
, ry0
, ru
,1); dst
++;
1607 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1608 nvg__vset(dst
, p1
->x
- dlx1
*rw
, p1
->y
- dly1
*rw
, ru
,1); dst
++;
1611 nvg__vset(dst
, lx1
, ly1
, lu
,1); dst
++;
1612 nvg__vset(dst
, p1
->x
- dlx1
*rw
, p1
->y
- dly1
*rw
, ru
,1); dst
++;
1615 nvg__chooseBevel(p1
->flags
& NVG_PR_INNERBEVEL
, p0
, p1
, -rw
, &rx0
,&ry0
, &rx1
,&ry1
);
1617 nvg__vset(dst
, p1
->x
+ dlx0
*lw
, p1
->y
+ dly0
*lw
, lu
,1); dst
++;
1618 nvg__vset(dst
, rx0
, ry0
, ru
,1); dst
++;
1620 if (p1
->flags
& NVG_PT_BEVEL
) {
1621 nvg__vset(dst
, p1
->x
+ dlx0
*lw
, p1
->y
+ dly0
*lw
, lu
,1); dst
++;
1622 nvg__vset(dst
, rx0
, ry0
, ru
,1); dst
++;
1624 nvg__vset(dst
, p1
->x
+ dlx1
*lw
, p1
->y
+ dly1
*lw
, lu
,1); dst
++;
1625 nvg__vset(dst
, rx1
, ry1
, ru
,1); dst
++;
1627 lx0
= p1
->x
+ p1
->dmx
* lw
;
1628 ly0
= p1
->y
+ p1
->dmy
* lw
;
1630 nvg__vset(dst
, p1
->x
+ dlx0
*lw
, p1
->y
+ dly0
*lw
, lu
,1); dst
++;
1631 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1633 nvg__vset(dst
, lx0
, ly0
, lu
,1); dst
++;
1634 nvg__vset(dst
, lx0
, ly0
, lu
,1); dst
++;
1636 nvg__vset(dst
, p1
->x
+ dlx1
*lw
, p1
->y
+ dly1
*lw
, lu
,1); dst
++;
1637 nvg__vset(dst
, p1
->x
, p1
->y
, 0.5f
,1); dst
++;
1640 nvg__vset(dst
, p1
->x
+ dlx1
*lw
, p1
->y
+ dly1
*lw
, lu
,1); dst
++;
1641 nvg__vset(dst
, rx1
, ry1
, ru
,1); dst
++;
1647 static NVGvertex
* nvg__buttCapStart(NVGvertex
* dst
, NVGpoint
* p
,
1648 float dx
, float dy
, float w
, float d
,
1649 float aa
, float u0
, float u1
)
1651 float px
= p
->x
- dx
*d
;
1652 float py
= p
->y
- dy
*d
;
1655 nvg__vset(dst
, px
+ dlx
*w
- dx
*aa
, py
+ dly
*w
- dy
*aa
, u0
,0); dst
++;
1656 nvg__vset(dst
, px
- dlx
*w
- dx
*aa
, py
- dly
*w
- dy
*aa
, u1
,0); dst
++;
1657 nvg__vset(dst
, px
+ dlx
*w
, py
+ dly
*w
, u0
,1); dst
++;
1658 nvg__vset(dst
, px
- dlx
*w
, py
- dly
*w
, u1
,1); dst
++;
1662 static NVGvertex
* nvg__buttCapEnd(NVGvertex
* dst
, NVGpoint
* p
,
1663 float dx
, float dy
, float w
, float d
,
1664 float aa
, float u0
, float u1
)
1666 float px
= p
->x
+ dx
*d
;
1667 float py
= p
->y
+ dy
*d
;
1670 nvg__vset(dst
, px
+ dlx
*w
, py
+ dly
*w
, u0
,1); dst
++;
1671 nvg__vset(dst
, px
- dlx
*w
, py
- dly
*w
, u1
,1); dst
++;
1672 nvg__vset(dst
, px
+ dlx
*w
+ dx
*aa
, py
+ dly
*w
+ dy
*aa
, u0
,0); dst
++;
1673 nvg__vset(dst
, px
- dlx
*w
+ dx
*aa
, py
- dly
*w
+ dy
*aa
, u1
,0); dst
++;
1678 static NVGvertex
* nvg__roundCapStart(NVGvertex
* dst
, NVGpoint
* p
,
1679 float dx
, float dy
, float w
, int ncap
,
1680 float aa
, float u0
, float u1
)
1688 for (i
= 0; i
< ncap
; i
++) {
1689 float a
= i
/(float)(ncap
-1)*NVG_PI
;
1690 float ax
= cosf(a
) * w
, ay
= sinf(a
) * w
;
1691 nvg__vset(dst
, px
- dlx
*ax
- dx
*ay
, py
- dly
*ax
- dy
*ay
, u0
,1); dst
++;
1692 nvg__vset(dst
, px
, py
, 0.5f
,1); dst
++;
1694 nvg__vset(dst
, px
+ dlx
*w
, py
+ dly
*w
, u0
,1); dst
++;
1695 nvg__vset(dst
, px
- dlx
*w
, py
- dly
*w
, u1
,1); dst
++;
1699 static NVGvertex
* nvg__roundCapEnd(NVGvertex
* dst
, NVGpoint
* p
,
1700 float dx
, float dy
, float w
, int ncap
,
1701 float aa
, float u0
, float u1
)
1709 nvg__vset(dst
, px
+ dlx
*w
, py
+ dly
*w
, u0
,1); dst
++;
1710 nvg__vset(dst
, px
- dlx
*w
, py
- dly
*w
, u1
,1); dst
++;
1711 for (i
= 0; i
< ncap
; i
++) {
1712 float a
= i
/(float)(ncap
-1)*NVG_PI
;
1713 float ax
= cosf(a
) * w
, ay
= sinf(a
) * w
;
1714 nvg__vset(dst
, px
, py
, 0.5f
,1); dst
++;
1715 nvg__vset(dst
, px
- dlx
*ax
+ dx
*ay
, py
- dly
*ax
+ dy
*ay
, u0
,1); dst
++;
1721 static void nvg__calculateJoins(NVGcontext
* ctx
, float w
, int lineJoin
, float miterLimit
)
1723 NVGpathCache
* cache
= ctx
->cache
;
1727 if (w
> 0.0f
) iw
= 1.0f
/ w
;
1729 // Calculate which joins needs extra vertices to append, and gather vertex count.
1730 for (i
= 0; i
< cache
->npaths
; i
++) {
1731 NVGpath
* path
= &cache
->paths
[i
];
1732 NVGpoint
* pts
= &cache
->points
[path
->first
];
1733 NVGpoint
* p0
= &pts
[path
->count
-1];
1734 NVGpoint
* p1
= &pts
[0];
1739 for (j
= 0; j
< path
->count
; j
++) {
1740 float dlx0
, dly0
, dlx1
, dly1
, dmr2
, cross
, limit
;
1745 // Calculate extrusions
1746 p1
->dmx
= (dlx0
+ dlx1
) * 0.5f
;
1747 p1
->dmy
= (dly0
+ dly1
) * 0.5f
;
1748 dmr2
= p1
->dmx
*p1
->dmx
+ p1
->dmy
*p1
->dmy
;
1749 if (dmr2
> 0.000001f
) {
1750 float scale
= 1.0f
/ dmr2
;
1751 if (scale
> 600.0f
) {
1758 // Clear flags, but keep the corner.
1759 p1
->flags
= (p1
->flags
& NVG_PT_CORNER
) ? NVG_PT_CORNER
: 0;
1761 // Keep track of left turns.
1762 cross
= p1
->dx
* p0
->dy
- p0
->dx
* p1
->dy
;
1765 p1
->flags
|= NVG_PT_LEFT
;
1768 // Calculate if we should use bevel or miter for inner join.
1769 limit
= nvg__maxf(1.01f
, nvg__minf(p0
->len
, p1
->len
) * iw
);
1770 if ((dmr2
* limit
*limit
) < 1.0f
)
1771 p1
->flags
|= NVG_PR_INNERBEVEL
;
1773 // Check to see if the corner needs to be beveled.
1774 if (p1
->flags
& NVG_PT_CORNER
) {
1775 if ((dmr2
* miterLimit
*miterLimit
) < 1.0f
|| lineJoin
== NVG_BEVEL
|| lineJoin
== NVG_ROUND
) {
1776 p1
->flags
|= NVG_PT_BEVEL
;
1780 if ((p1
->flags
& (NVG_PT_BEVEL
| NVG_PR_INNERBEVEL
)) != 0)
1786 path
->convex
= (nleft
== path
->count
) ? 1 : 0;
1791 static int nvg__expandStroke(NVGcontext
* ctx
, float w
, float fringe
, int lineCap
, int lineJoin
, float miterLimit
)
1793 NVGpathCache
* cache
= ctx
->cache
;
1797 float aa
= fringe
;//ctx->fringeWidth;
1798 float u0
= 0.0f
, u1
= 1.0f
;
1799 int ncap
= nvg__curveDivs(w
, NVG_PI
, ctx
->tessTol
); // Calculate divisions per half circle.
1803 // Disable the gradient used for antialiasing when antialiasing is not used.
1809 nvg__calculateJoins(ctx
, w
, lineJoin
, miterLimit
);
1811 // Calculate max vertex usage.
1813 for (i
= 0; i
< cache
->npaths
; i
++) {
1814 NVGpath
* path
= &cache
->paths
[i
];
1815 int loop
= (path
->closed
== 0) ? 0 : 1;
1816 if (lineJoin
== NVG_ROUND
)
1817 cverts
+= (path
->count
+ path
->nbevel
*(ncap
+2) + 1) * 2; // plus one for loop
1819 cverts
+= (path
->count
+ path
->nbevel
*5 + 1) * 2; // plus one for loop
1822 if (lineCap
== NVG_ROUND
) {
1823 cverts
+= (ncap
*2 + 2)*2;
1830 verts
= nvg__allocTempVerts(ctx
, cverts
);
1831 if (verts
== NULL
) return 0;
1833 for (i
= 0; i
< cache
->npaths
; i
++) {
1834 NVGpath
* path
= &cache
->paths
[i
];
1835 NVGpoint
* pts
= &cache
->points
[path
->first
];
1844 // Calculate fringe or stroke
1845 loop
= (path
->closed
== 0) ? 0 : 1;
1851 p0
= &pts
[path
->count
-1];
1867 nvg__normalize(&dx
, &dy
);
1868 if (lineCap
== NVG_BUTT
)
1869 dst
= nvg__buttCapStart(dst
, p0
, dx
, dy
, w
, -aa
*0.5f
, aa
, u0
, u1
);
1870 else if (lineCap
== NVG_BUTT
|| lineCap
== NVG_SQUARE
)
1871 dst
= nvg__buttCapStart(dst
, p0
, dx
, dy
, w
, w
-aa
, aa
, u0
, u1
);
1872 else if (lineCap
== NVG_ROUND
)
1873 dst
= nvg__roundCapStart(dst
, p0
, dx
, dy
, w
, ncap
, aa
, u0
, u1
);
1876 for (j
= s
; j
< e
; ++j
) {
1877 if ((p1
->flags
& (NVG_PT_BEVEL
| NVG_PR_INNERBEVEL
)) != 0) {
1878 if (lineJoin
== NVG_ROUND
) {
1879 dst
= nvg__roundJoin(dst
, p0
, p1
, w
, w
, u0
, u1
, ncap
, aa
);
1881 dst
= nvg__bevelJoin(dst
, p0
, p1
, w
, w
, u0
, u1
, aa
);
1884 nvg__vset(dst
, p1
->x
+ (p1
->dmx
* w
), p1
->y
+ (p1
->dmy
* w
), u0
,1); dst
++;
1885 nvg__vset(dst
, p1
->x
- (p1
->dmx
* w
), p1
->y
- (p1
->dmy
* w
), u1
,1); dst
++;
1892 nvg__vset(dst
, verts
[0].x
, verts
[0].y
, u0
,1); dst
++;
1893 nvg__vset(dst
, verts
[1].x
, verts
[1].y
, u1
,1); dst
++;
1898 nvg__normalize(&dx
, &dy
);
1899 if (lineCap
== NVG_BUTT
)
1900 dst
= nvg__buttCapEnd(dst
, p1
, dx
, dy
, w
, -aa
*0.5f
, aa
, u0
, u1
);
1901 else if (lineCap
== NVG_BUTT
|| lineCap
== NVG_SQUARE
)
1902 dst
= nvg__buttCapEnd(dst
, p1
, dx
, dy
, w
, w
-aa
, aa
, u0
, u1
);
1903 else if (lineCap
== NVG_ROUND
)
1904 dst
= nvg__roundCapEnd(dst
, p1
, dx
, dy
, w
, ncap
, aa
, u0
, u1
);
1907 path
->nstroke
= (int)(dst
- verts
);
1915 static int nvg__expandFill(NVGcontext
* ctx
, float w
, int lineJoin
, float miterLimit
)
1917 NVGpathCache
* cache
= ctx
->cache
;
1920 int cverts
, convex
, i
, j
;
1921 float aa
= ctx
->fringeWidth
;
1922 int fringe
= w
> 0.0f
;
1924 nvg__calculateJoins(ctx
, w
, lineJoin
, miterLimit
);
1926 // Calculate max vertex usage.
1928 for (i
= 0; i
< cache
->npaths
; i
++) {
1929 NVGpath
* path
= &cache
->paths
[i
];
1930 cverts
+= path
->count
+ path
->nbevel
+ 1;
1932 cverts
+= (path
->count
+ path
->nbevel
*5 + 1) * 2; // plus one for loop
1935 verts
= nvg__allocTempVerts(ctx
, cverts
);
1936 if (verts
== NULL
) return 0;
1938 convex
= cache
->npaths
== 1 && cache
->paths
[0].convex
;
1940 for (i
= 0; i
< cache
->npaths
; i
++) {
1941 NVGpath
* path
= &cache
->paths
[i
];
1942 NVGpoint
* pts
= &cache
->points
[path
->first
];
1948 // Calculate shape vertices.
1955 p0
= &pts
[path
->count
-1];
1957 for (j
= 0; j
< path
->count
; ++j
) {
1958 if (p1
->flags
& NVG_PT_BEVEL
) {
1959 float dlx0
= p0
->dy
;
1960 float dly0
= -p0
->dx
;
1961 float dlx1
= p1
->dy
;
1962 float dly1
= -p1
->dx
;
1963 if (p1
->flags
& NVG_PT_LEFT
) {
1964 float lx
= p1
->x
+ p1
->dmx
* woff
;
1965 float ly
= p1
->y
+ p1
->dmy
* woff
;
1966 nvg__vset(dst
, lx
, ly
, 0.5f
,1); dst
++;
1968 float lx0
= p1
->x
+ dlx0
* woff
;
1969 float ly0
= p1
->y
+ dly0
* woff
;
1970 float lx1
= p1
->x
+ dlx1
* woff
;
1971 float ly1
= p1
->y
+ dly1
* woff
;
1972 nvg__vset(dst
, lx0
, ly0
, 0.5f
,1); dst
++;
1973 nvg__vset(dst
, lx1
, ly1
, 0.5f
,1); dst
++;
1976 nvg__vset(dst
, p1
->x
+ (p1
->dmx
* woff
), p1
->y
+ (p1
->dmy
* woff
), 0.5f
,1); dst
++;
1981 for (j
= 0; j
< path
->count
; ++j
) {
1982 nvg__vset(dst
, pts
[j
].x
, pts
[j
].y
, 0.5f
,1);
1987 path
->nfill
= (int)(dst
- verts
);
1999 // Create only half a fringe for convex shapes so that
2000 // the shape can be rendered without stenciling.
2002 lw
= woff
; // This should generate the same vertex as fill inset above.
2003 lu
= 0.5f
; // Set outline fade at middle.
2007 p0
= &pts
[path
->count
-1];
2010 for (j
= 0; j
< path
->count
; ++j
) {
2011 if ((p1
->flags
& (NVG_PT_BEVEL
| NVG_PR_INNERBEVEL
)) != 0) {
2012 dst
= nvg__bevelJoin(dst
, p0
, p1
, lw
, rw
, lu
, ru
, ctx
->fringeWidth
);
2014 nvg__vset(dst
, p1
->x
+ (p1
->dmx
* lw
), p1
->y
+ (p1
->dmy
* lw
), lu
,1); dst
++;
2015 nvg__vset(dst
, p1
->x
- (p1
->dmx
* rw
), p1
->y
- (p1
->dmy
* rw
), ru
,1); dst
++;
2021 nvg__vset(dst
, verts
[0].x
, verts
[0].y
, lu
,1); dst
++;
2022 nvg__vset(dst
, verts
[1].x
, verts
[1].y
, ru
,1); dst
++;
2024 path
->nstroke
= (int)(dst
- verts
);
2027 path
->stroke
= NULL
;
2037 void nvgBeginPath(NVGcontext
* ctx
)
2040 nvg__clearPathCache(ctx
);
2043 void nvgMoveTo(NVGcontext
* ctx
, float x
, float y
)
2045 float vals
[] = { NVG_MOVETO
, x
, y
};
2046 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2049 void nvgLineTo(NVGcontext
* ctx
, float x
, float y
)
2051 float vals
[] = { NVG_LINETO
, x
, y
};
2052 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2055 void nvgBezierTo(NVGcontext
* ctx
, float c1x
, float c1y
, float c2x
, float c2y
, float x
, float y
)
2057 float vals
[] = { NVG_BEZIERTO
, c1x
, c1y
, c2x
, c2y
, x
, y
};
2058 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2061 void nvgQuadTo(NVGcontext
* ctx
, float cx
, float cy
, float x
, float y
)
2063 float x0
= ctx
->commandx
;
2064 float y0
= ctx
->commandy
;
2065 float vals
[] = { NVG_BEZIERTO
,
2066 x0
+ 2.0f
/3.0f
*(cx
- x0
), y0
+ 2.0f
/3.0f
*(cy
- y0
),
2067 x
+ 2.0f
/3.0f
*(cx
- x
), y
+ 2.0f
/3.0f
*(cy
- y
),
2069 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2072 void nvgArcTo(NVGcontext
* ctx
, float x1
, float y1
, float x2
, float y2
, float radius
)
2074 float x0
= ctx
->commandx
;
2075 float y0
= ctx
->commandy
;
2076 float dx0
,dy0
, dx1
,dy1
, a
, d
, cx
,cy
, a0
,a1
;
2079 if (ctx
->ncommands
== 0) {
2083 // Handle degenerate cases.
2084 if (nvg__ptEquals(x0
,y0
, x1
,y1
, ctx
->distTol
) ||
2085 nvg__ptEquals(x1
,y1
, x2
,y2
, ctx
->distTol
) ||
2086 nvg__distPtSeg(x1
,y1
, x0
,y0
, x2
,y2
) < ctx
->distTol
*ctx
->distTol
||
2087 radius
< ctx
->distTol
) {
2088 nvgLineTo(ctx
, x1
,y1
);
2092 // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
2097 nvg__normalize(&dx0
,&dy0
);
2098 nvg__normalize(&dx1
,&dy1
);
2099 a
= nvg__acosf(dx0
*dx1
+ dy0
*dy1
);
2100 d
= radius
/ nvg__tanf(a
/2.0f
);
2102 // printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d);
2105 nvgLineTo(ctx
, x1
,y1
);
2109 if (nvg__cross(dx0
,dy0
, dx1
,dy1
) > 0.0f
) {
2110 cx
= x1
+ dx0
*d
+ dy0
*radius
;
2111 cy
= y1
+ dy0
*d
+ -dx0
*radius
;
2112 a0
= nvg__atan2f(dx0
, -dy0
);
2113 a1
= nvg__atan2f(-dx1
, dy1
);
2115 // printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2117 cx
= x1
+ dx0
*d
+ -dy0
*radius
;
2118 cy
= y1
+ dy0
*d
+ dx0
*radius
;
2119 a0
= nvg__atan2f(-dx0
, dy0
);
2120 a1
= nvg__atan2f(dx1
, -dy1
);
2122 // printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
2125 nvgArc(ctx
, cx
, cy
, radius
, a0
, a1
, dir
);
2128 void nvgClosePath(NVGcontext
* ctx
)
2130 float vals
[] = { NVG_CLOSE
};
2131 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2134 void nvgPathWinding(NVGcontext
* ctx
, int dir
)
2136 float vals
[] = { NVG_WINDING
, (float)dir
};
2137 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2140 void nvgArc(NVGcontext
* ctx
, float cx
, float cy
, float r
, float a0
, float a1
, int dir
)
2142 float a
= 0, da
= 0, hda
= 0, kappa
= 0;
2143 float dx
= 0, dy
= 0, x
= 0, y
= 0, tanx
= 0, tany
= 0;
2144 float px
= 0, py
= 0, ptanx
= 0, ptany
= 0;
2145 float vals
[3 + 5*7 + 100];
2146 int i
, ndivs
, nvals
;
2147 int move
= ctx
->ncommands
> 0 ? NVG_LINETO
: NVG_MOVETO
;
2151 if (dir
== NVG_CW
) {
2152 if (nvg__absf(da
) >= NVG_PI
*2) {
2155 while (da
< 0.0f
) da
+= NVG_PI
*2;
2158 if (nvg__absf(da
) >= NVG_PI
*2) {
2161 while (da
> 0.0f
) da
-= NVG_PI
*2;
2165 // Split arc into max 90 degree segments.
2166 ndivs
= nvg__maxi(1, nvg__mini((int)(nvg__absf(da
) / (NVG_PI
*0.5f
) + 0.5f
), 5));
2167 hda
= (da
/ (float)ndivs
) / 2.0f
;
2168 kappa
= nvg__absf(4.0f
/ 3.0f
* (1.0f
- nvg__cosf(hda
)) / nvg__sinf(hda
));
2174 for (i
= 0; i
<= ndivs
; i
++) {
2175 a
= a0
+ da
* (i
/(float)ndivs
);
2184 vals
[nvals
++] = (float)move
;
2188 vals
[nvals
++] = NVG_BEZIERTO
;
2189 vals
[nvals
++] = px
+ptanx
;
2190 vals
[nvals
++] = py
+ptany
;
2191 vals
[nvals
++] = x
-tanx
;
2192 vals
[nvals
++] = y
-tany
;
2202 nvg__appendCommands(ctx
, vals
, nvals
);
2205 void nvgRect(NVGcontext
* ctx
, float x
, float y
, float w
, float h
)
2210 NVG_LINETO
, x
+w
,y
+h
,
2214 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2217 void nvgRoundedRect(NVGcontext
* ctx
, float x
, float y
, float w
, float h
, float r
)
2219 nvgRoundedRectVarying(ctx
, x
, y
, w
, h
, r
, r
, r
, r
);
2222 void nvgRoundedRectVarying(NVGcontext
* ctx
, float x
, float y
, float w
, float h
, float radTopLeft
, float radTopRight
, float radBottomRight
, float radBottomLeft
)
2224 if(radTopLeft
< 0.1f
&& radTopRight
< 0.1f
&& radBottomRight
< 0.1f
&& radBottomLeft
< 0.1f
) {
2225 nvgRect(ctx
, x
, y
, w
, h
);
2228 float halfw
= nvg__absf(w
)*0.5f
;
2229 float halfh
= nvg__absf(h
)*0.5f
;
2230 float rxBL
= nvg__minf(radBottomLeft
, halfw
) * nvg__signf(w
), ryBL
= nvg__minf(radBottomLeft
, halfh
) * nvg__signf(h
);
2231 float rxBR
= nvg__minf(radBottomRight
, halfw
) * nvg__signf(w
), ryBR
= nvg__minf(radBottomRight
, halfh
) * nvg__signf(h
);
2232 float rxTR
= nvg__minf(radTopRight
, halfw
) * nvg__signf(w
), ryTR
= nvg__minf(radTopRight
, halfh
) * nvg__signf(h
);
2233 float rxTL
= nvg__minf(radTopLeft
, halfw
) * nvg__signf(w
), ryTL
= nvg__minf(radTopLeft
, halfh
) * nvg__signf(h
);
2235 NVG_MOVETO
, x
, y
+ ryTL
,
2236 NVG_LINETO
, x
, y
+ h
- ryBL
,
2237 NVG_BEZIERTO
, x
, y
+ h
- ryBL
*(1 - NVG_KAPPA90
), x
+ rxBL
*(1 - NVG_KAPPA90
), y
+ h
, x
+ rxBL
, y
+ h
,
2238 NVG_LINETO
, x
+ w
- rxBR
, y
+ h
,
2239 NVG_BEZIERTO
, x
+ w
- rxBR
*(1 - NVG_KAPPA90
), y
+ h
, x
+ w
, y
+ h
- ryBR
*(1 - NVG_KAPPA90
), x
+ w
, y
+ h
- ryBR
,
2240 NVG_LINETO
, x
+ w
, y
+ ryTR
,
2241 NVG_BEZIERTO
, x
+ w
, y
+ ryTR
*(1 - NVG_KAPPA90
), x
+ w
- rxTR
*(1 - NVG_KAPPA90
), y
, x
+ w
- rxTR
, y
,
2242 NVG_LINETO
, x
+ rxTL
, y
,
2243 NVG_BEZIERTO
, x
+ rxTL
*(1 - NVG_KAPPA90
), y
, x
, y
+ ryTL
*(1 - NVG_KAPPA90
), x
, y
+ ryTL
,
2246 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2250 void nvgEllipse(NVGcontext
* ctx
, float cx
, float cy
, float rx
, float ry
)
2253 NVG_MOVETO
, cx
-rx
, cy
,
2254 NVG_BEZIERTO
, cx
-rx
, cy
+ry
*NVG_KAPPA90
, cx
-rx
*NVG_KAPPA90
, cy
+ry
, cx
, cy
+ry
,
2255 NVG_BEZIERTO
, cx
+rx
*NVG_KAPPA90
, cy
+ry
, cx
+rx
, cy
+ry
*NVG_KAPPA90
, cx
+rx
, cy
,
2256 NVG_BEZIERTO
, cx
+rx
, cy
-ry
*NVG_KAPPA90
, cx
+rx
*NVG_KAPPA90
, cy
-ry
, cx
, cy
-ry
,
2257 NVG_BEZIERTO
, cx
-rx
*NVG_KAPPA90
, cy
-ry
, cx
-rx
, cy
-ry
*NVG_KAPPA90
, cx
-rx
, cy
,
2260 nvg__appendCommands(ctx
, vals
, NVG_COUNTOF(vals
));
2263 void nvgCircle(NVGcontext
* ctx
, float cx
, float cy
, float r
)
2265 nvgEllipse(ctx
, cx
,cy
, r
,r
);
2268 void nvgDebugDumpPathCache(NVGcontext
* ctx
)
2270 const NVGpath
* path
;
2273 printf("Dumping %d cached paths\n", ctx
->cache
->npaths
);
2274 for (i
= 0; i
< ctx
->cache
->npaths
; i
++) {
2275 path
= &ctx
->cache
->paths
[i
];
2276 printf(" - Path %d\n", i
);
2278 printf(" - fill: %d\n", path
->nfill
);
2279 for (j
= 0; j
< path
->nfill
; j
++)
2280 printf("%f\t%f\n", path
->fill
[j
].x
, path
->fill
[j
].y
);
2282 if (path
->nstroke
) {
2283 printf(" - stroke: %d\n", path
->nstroke
);
2284 for (j
= 0; j
< path
->nstroke
; j
++)
2285 printf("%f\t%f\n", path
->stroke
[j
].x
, path
->stroke
[j
].y
);
2290 void nvgFill(NVGcontext
* ctx
)
2292 NVGstate
* state
= nvg__getState(ctx
);
2293 const NVGpath
* path
;
2294 NVGpaint fillPaint
= state
->fill
;
2297 nvg__flattenPaths(ctx
);
2298 if (ctx
->params
.edgeAntiAlias
&& state
->shapeAntiAlias
)
2299 nvg__expandFill(ctx
, ctx
->fringeWidth
, NVG_MITER
, 2.4f
);
2301 nvg__expandFill(ctx
, 0.0f
, NVG_MITER
, 2.4f
);
2303 // Apply global tint
2304 for (i
= 0; i
< 4; i
++) {
2305 fillPaint
.innerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2306 fillPaint
.outerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2309 ctx
->params
.renderFill(ctx
->params
.userPtr
, &fillPaint
, state
->compositeOperation
, &state
->scissor
, ctx
->fringeWidth
,
2310 ctx
->cache
->bounds
, ctx
->cache
->paths
, ctx
->cache
->npaths
);
2313 for (i
= 0; i
< ctx
->cache
->npaths
; i
++) {
2314 path
= &ctx
->cache
->paths
[i
];
2315 ctx
->fillTriCount
+= path
->nfill
-2;
2316 ctx
->fillTriCount
+= path
->nstroke
-2;
2317 ctx
->drawCallCount
+= 2;
2321 void nvgStroke(NVGcontext
* ctx
)
2323 NVGstate
* state
= nvg__getState(ctx
);
2324 float scale
= nvg__getAverageScale(state
->xform
);
2325 float strokeWidth
= nvg__clampf(state
->strokeWidth
* scale
, 0.0f
, 200.0f
);
2326 NVGpaint strokePaint
= state
->stroke
;
2327 const NVGpath
* path
;
2331 if (strokeWidth
< ctx
->fringeWidth
) {
2332 // If the stroke width is less than pixel size, use alpha to emulate coverage.
2333 // Since coverage is area, scale by alpha*alpha.
2334 float alpha
= nvg__clampf(strokeWidth
/ ctx
->fringeWidth
, 0.0f
, 1.0f
);
2335 strokePaint
.innerColor
.a
*= alpha
*alpha
;
2336 strokePaint
.outerColor
.a
*= alpha
*alpha
;
2337 strokeWidth
= ctx
->fringeWidth
;
2340 // Apply global tint
2341 for (i
= 0; i
< 4; i
++) {
2342 strokePaint
.innerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2343 strokePaint
.outerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2346 nvg__flattenPaths(ctx
);
2348 if (ctx
->params
.edgeAntiAlias
&& state
->shapeAntiAlias
)
2349 nvg__expandStroke(ctx
, strokeWidth
*0.5f
, ctx
->fringeWidth
, state
->lineCap
, state
->lineJoin
, state
->miterLimit
);
2351 nvg__expandStroke(ctx
, strokeWidth
*0.5f
, 0.0f
, state
->lineCap
, state
->lineJoin
, state
->miterLimit
);
2353 ctx
->params
.renderStroke(ctx
->params
.userPtr
, &strokePaint
, state
->compositeOperation
, &state
->scissor
, ctx
->fringeWidth
,
2354 strokeWidth
, ctx
->cache
->paths
, ctx
->cache
->npaths
);
2357 for (i
= 0; i
< ctx
->cache
->npaths
; i
++) {
2358 path
= &ctx
->cache
->paths
[i
];
2359 ctx
->strokeTriCount
+= path
->nstroke
-2;
2360 ctx
->drawCallCount
++;
2365 int nvgCreateFont(NVGcontext
* ctx
, const char* name
, const char* filename
)
2367 return fonsAddFont(ctx
->fontContext
->fs
, name
, filename
, 0);
2370 int nvgCreateFontAtIndex(NVGcontext
* ctx
, const char* name
, const char* filename
, const int fontIndex
)
2372 return fonsAddFont(ctx
->fontContext
->fs
, name
, filename
, fontIndex
);
2375 int nvgCreateFontMem(NVGcontext
* ctx
, const char* name
, unsigned char* data
, int ndata
, int freeData
)
2377 return fonsAddFontMem(ctx
->fontContext
->fs
, name
, data
, ndata
, freeData
, 0);
2380 int nvgCreateFontMemAtIndex(NVGcontext
* ctx
, const char* name
, unsigned char* data
, int ndata
, int freeData
, const int fontIndex
)
2382 return fonsAddFontMem(ctx
->fontContext
->fs
, name
, data
, ndata
, freeData
, fontIndex
);
2385 int nvgFindFont(NVGcontext
* ctx
, const char* name
)
2387 if (name
== NULL
) return -1;
2388 return fonsGetFontByName(ctx
->fontContext
->fs
, name
);
2392 int nvgAddFallbackFontId(NVGcontext
* ctx
, int baseFont
, int fallbackFont
)
2394 if(baseFont
== -1 || fallbackFont
== -1) return 0;
2395 return fonsAddFallbackFont(ctx
->fontContext
->fs
, baseFont
, fallbackFont
);
2398 int nvgAddFallbackFont(NVGcontext
* ctx
, const char* baseFont
, const char* fallbackFont
)
2400 return nvgAddFallbackFontId(ctx
, nvgFindFont(ctx
, baseFont
), nvgFindFont(ctx
, fallbackFont
));
2403 void nvgResetFallbackFontsId(NVGcontext
* ctx
, int baseFont
)
2405 fonsResetFallbackFont(ctx
->fontContext
->fs
, baseFont
);
2408 void nvgResetFallbackFonts(NVGcontext
* ctx
, const char* baseFont
)
2410 nvgResetFallbackFontsId(ctx
, nvgFindFont(ctx
, baseFont
));
2414 void nvgFontSize(NVGcontext
* ctx
, float size
)
2416 NVGstate
* state
= nvg__getState(ctx
);
2417 state
->fontSize
= size
;
2420 void nvgFontBlur(NVGcontext
* ctx
, float blur
)
2422 NVGstate
* state
= nvg__getState(ctx
);
2423 state
->fontBlur
= blur
;
2426 void nvgTextLetterSpacing(NVGcontext
* ctx
, float spacing
)
2428 NVGstate
* state
= nvg__getState(ctx
);
2429 state
->letterSpacing
= spacing
;
2432 void nvgTextLineHeight(NVGcontext
* ctx
, float lineHeight
)
2434 NVGstate
* state
= nvg__getState(ctx
);
2435 state
->lineHeight
= lineHeight
;
2438 void nvgTextAlign(NVGcontext
* ctx
, int align
)
2440 NVGstate
* state
= nvg__getState(ctx
);
2441 state
->textAlign
= align
;
2444 void nvgFontFaceId(NVGcontext
* ctx
, int font
)
2446 NVGstate
* state
= nvg__getState(ctx
);
2447 state
->fontId
= font
;
2450 void nvgFontFace(NVGcontext
* ctx
, const char* font
)
2452 NVGstate
* state
= nvg__getState(ctx
);
2453 state
->fontId
= fonsGetFontByName(ctx
->fontContext
->fs
, font
);
2456 static float nvg__quantize(float a
, float d
)
2458 return ((int)(a
/ d
+ 0.5f
)) * d
;
2461 static float nvg__getFontScale(NVGstate
* state
)
2463 return nvg__minf(nvg__quantize(nvg__getAverageScale(state
->xform
), 0.01f
), 4.0f
);
2466 static void nvg__flushTextTexture(NVGcontext
* ctx
)
2470 if (fonsValidateTexture(ctx
->fontContext
->fs
, dirty
)) {
2471 int fontImage
= ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
];
2473 if (fontImage
!= 0) {
2475 const unsigned char* data
= fonsGetTextureData(ctx
->fontContext
->fs
, &iw
, &ih
);
2478 int w
= dirty
[2] - dirty
[0];
2479 int h
= dirty
[3] - dirty
[1];
2480 ctx
->params
.renderUpdateTexture(ctx
->params
.userPtr
, fontImage
, x
,y
, w
,h
, data
);
2485 static int nvg__allocTextAtlas(NVGcontext
* ctx
)
2488 nvg__flushTextTexture(ctx
);
2489 if (ctx
->fontContext
->fontImageIdx
>= NVG_MAX_FONTIMAGES
-1)
2491 // if next fontImage already have a texture
2492 if (ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
+1] != 0)
2493 nvgImageSize(ctx
, ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
+1], &iw
, &ih
);
2494 else { // calculate the new font image size and create it.
2495 nvgImageSize(ctx
, ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
], &iw
, &ih
);
2500 if (iw
> NVG_MAX_FONTIMAGE_SIZE
|| ih
> NVG_MAX_FONTIMAGE_SIZE
)
2501 iw
= ih
= NVG_MAX_FONTIMAGE_SIZE
;
2502 ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
+1]
2503 = ctx
->params
.renderCreateTexture(ctx
->params
.userPtr
,
2504 NVG_TEXTURE_ALPHA
, iw
, ih
, NVG_FONT_TEXTURE_FLAGS
, NULL
);
2506 ++ctx
->fontContext
->fontImageIdx
;
2507 fonsResetAtlas(ctx
->fontContext
->fs
, iw
, ih
);
2511 static void nvg__renderText(NVGcontext
* ctx
, NVGvertex
* verts
, int nverts
)
2514 NVGstate
* state
= nvg__getState(ctx
);
2515 NVGpaint paint
= state
->fill
;
2517 // Render triangles.
2518 paint
.image
= ctx
->fontContext
->fontImages
[ctx
->fontContext
->fontImageIdx
];
2520 // Apply global tint
2521 for (i
= 0; i
< 4; i
++) {
2522 paint
.innerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2523 paint
.outerColor
.rgba
[i
] *= state
->tint
.rgba
[i
];
2526 ctx
->params
.renderTriangles(ctx
->params
.userPtr
, &paint
, state
->compositeOperation
, &state
->scissor
, verts
, nverts
, ctx
->fringeWidth
);
2528 ctx
->drawCallCount
++;
2529 ctx
->textTriCount
+= nverts
/3;
2532 static int nvg__isTransformFlipped(const float *xform
)
2534 float det
= xform
[0] * xform
[3] - xform
[2] * xform
[1];
2538 float nvgText(NVGcontext
* ctx
, float x
, float y
, const char* string
, const char* end
)
2540 NVGstate
* state
= nvg__getState(ctx
);
2541 FONStextIter iter
, prevIter
;
2544 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
2545 float invscale
= 1.0f
/ scale
;
2548 int isFlipped
= nvg__isTransformFlipped(state
->xform
);
2551 end
= string
+ strlen(string
);
2553 if (state
->fontId
== FONS_INVALID
) return x
;
2555 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
2556 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
2557 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
2558 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
2559 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
2561 cverts
= nvg__maxi(2, (int)(end
- string
)) * 6; // conservative estimate.
2562 verts
= nvg__allocTempVerts(ctx
, cverts
);
2563 if (verts
== NULL
) return x
;
2565 fonsTextIterInit(ctx
->fontContext
->fs
, &iter
, x
*scale
, y
*scale
, string
, end
, FONS_GLYPH_BITMAP_REQUIRED
);
2567 while (fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
)) {
2569 if (iter
.prevGlyphIndex
== -1) { // can not retrieve glyph?
2571 nvg__renderText(ctx
, verts
, nverts
);
2574 if (!nvg__allocTextAtlas(ctx
))
2575 break; // no memory :(
2577 fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
); // try again
2578 if (iter
.prevGlyphIndex
== -1) // still can not find glyph?
2585 tmp
= q
.y0
; q
.y0
= q
.y1
; q
.y1
= tmp
;
2586 tmp
= q
.t0
; q
.t0
= q
.t1
; q
.t1
= tmp
;
2588 // Transform corners.
2589 nvgTransformPoint(&c
[0],&c
[1], state
->xform
, q
.x0
*invscale
, q
.y0
*invscale
);
2590 nvgTransformPoint(&c
[2],&c
[3], state
->xform
, q
.x1
*invscale
, q
.y0
*invscale
);
2591 nvgTransformPoint(&c
[4],&c
[5], state
->xform
, q
.x1
*invscale
, q
.y1
*invscale
);
2592 nvgTransformPoint(&c
[6],&c
[7], state
->xform
, q
.x0
*invscale
, q
.y1
*invscale
);
2594 if (nverts
+6 <= cverts
) {
2595 nvg__vset(&verts
[nverts
], c
[0], c
[1], q
.s0
, q
.t0
); nverts
++;
2596 nvg__vset(&verts
[nverts
], c
[4], c
[5], q
.s1
, q
.t1
); nverts
++;
2597 nvg__vset(&verts
[nverts
], c
[2], c
[3], q
.s1
, q
.t0
); nverts
++;
2598 nvg__vset(&verts
[nverts
], c
[0], c
[1], q
.s0
, q
.t0
); nverts
++;
2599 nvg__vset(&verts
[nverts
], c
[6], c
[7], q
.s0
, q
.t1
); nverts
++;
2600 nvg__vset(&verts
[nverts
], c
[4], c
[5], q
.s1
, q
.t1
); nverts
++;
2604 // TODO: add back-end bit to do this just once per frame.
2605 nvg__flushTextTexture(ctx
);
2607 nvg__renderText(ctx
, verts
, nverts
);
2609 return iter
.nextx
/ scale
;
2612 void nvgTextBox(NVGcontext
* ctx
, float x
, float y
, float breakRowWidth
, const char* string
, const char* end
)
2614 NVGstate
* state
= nvg__getState(ctx
);
2617 int oldAlign
= state
->textAlign
;
2618 int haling
= state
->textAlign
& (NVG_ALIGN_LEFT
| NVG_ALIGN_CENTER
| NVG_ALIGN_RIGHT
);
2619 int valign
= state
->textAlign
& (NVG_ALIGN_TOP
| NVG_ALIGN_MIDDLE
| NVG_ALIGN_BOTTOM
| NVG_ALIGN_BASELINE
);
2622 if (state
->fontId
== FONS_INVALID
) return;
2624 nvgTextMetrics(ctx
, NULL
, NULL
, &lineh
);
2626 state
->textAlign
= NVG_ALIGN_LEFT
| valign
;
2628 while ((nrows
= nvgTextBreakLines(ctx
, string
, end
, breakRowWidth
, rows
, 2))) {
2629 for (i
= 0; i
< nrows
; i
++) {
2630 NVGtextRow
* row
= &rows
[i
];
2631 if (haling
& NVG_ALIGN_LEFT
)
2632 nvgText(ctx
, x
, y
, row
->start
, row
->end
);
2633 else if (haling
& NVG_ALIGN_CENTER
)
2634 nvgText(ctx
, x
+ breakRowWidth
*0.5f
- row
->width
*0.5f
, y
, row
->start
, row
->end
);
2635 else if (haling
& NVG_ALIGN_RIGHT
)
2636 nvgText(ctx
, x
+ breakRowWidth
- row
->width
, y
, row
->start
, row
->end
);
2637 y
+= lineh
* state
->lineHeight
;
2639 string
= rows
[nrows
-1].next
;
2642 state
->textAlign
= oldAlign
;
2645 int nvgTextGlyphPositions(NVGcontext
* ctx
, float x
, float y
, const char* string
, const char* end
, NVGglyphPosition
* positions
, int maxPositions
)
2647 NVGstate
* state
= nvg__getState(ctx
);
2648 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
2649 float invscale
= 1.0f
/ scale
;
2650 FONStextIter iter
, prevIter
;
2654 if (state
->fontId
== FONS_INVALID
) return 0;
2657 end
= string
+ strlen(string
);
2662 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
2663 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
2664 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
2665 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
2666 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
2668 fonsTextIterInit(ctx
->fontContext
->fs
, &iter
, x
*scale
, y
*scale
, string
, end
, FONS_GLYPH_BITMAP_OPTIONAL
);
2670 while (fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
)) {
2671 if (iter
.prevGlyphIndex
< 0 && nvg__allocTextAtlas(ctx
)) { // can not retrieve glyph?
2673 fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
); // try again
2676 positions
[npos
].str
= iter
.str
;
2677 positions
[npos
].x
= iter
.x
* invscale
;
2678 positions
[npos
].minx
= nvg__minf(iter
.x
, q
.x0
) * invscale
;
2679 positions
[npos
].maxx
= nvg__maxf(iter
.nextx
, q
.x1
) * invscale
;
2681 if (npos
>= maxPositions
)
2688 enum NVGcodepointType
{
2695 int nvgTextBreakLines(NVGcontext
* ctx
, const char* string
, const char* end
, float breakRowWidth
, NVGtextRow
* rows
, int maxRows
)
2697 NVGstate
* state
= nvg__getState(ctx
);
2698 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
2699 float invscale
= 1.0f
/ scale
;
2700 FONStextIter iter
, prevIter
;
2703 float rowStartX
= 0;
2707 const char* rowStart
= NULL
;
2708 const char* rowEnd
= NULL
;
2709 const char* wordStart
= NULL
;
2710 float wordStartX
= 0;
2712 const char* breakEnd
= NULL
;
2713 float breakWidth
= 0;
2714 float breakMaxX
= 0;
2715 int type
= NVG_SPACE
, ptype
= NVG_SPACE
;
2716 unsigned int pcodepoint
= 0;
2718 if (maxRows
== 0) return 0;
2719 if (state
->fontId
== FONS_INVALID
) return 0;
2722 end
= string
+ strlen(string
);
2724 if (string
== end
) return 0;
2726 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
2727 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
2728 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
2729 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
2730 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
2732 breakRowWidth
*= scale
;
2734 fonsTextIterInit(ctx
->fontContext
->fs
, &iter
, 0, 0, string
, end
, FONS_GLYPH_BITMAP_OPTIONAL
);
2736 while (fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
)) {
2737 if (iter
.prevGlyphIndex
< 0 && nvg__allocTextAtlas(ctx
)) { // can not retrieve glyph?
2739 fonsTextIterNext(ctx
->fontContext
->fs
, &iter
, &q
); // try again
2742 switch (iter
.codepoint
) {
2747 case 0x00a0: // NBSP
2751 type
= pcodepoint
== 13 ? NVG_SPACE
: NVG_NEWLINE
;
2754 type
= pcodepoint
== 10 ? NVG_SPACE
: NVG_NEWLINE
;
2760 if ((iter
.codepoint
>= 0x4E00 && iter
.codepoint
<= 0x9FFF) ||
2761 (iter
.codepoint
>= 0x3000 && iter
.codepoint
<= 0x30FF) ||
2762 (iter
.codepoint
>= 0xFF00 && iter
.codepoint
<= 0xFFEF) ||
2763 (iter
.codepoint
>= 0x1100 && iter
.codepoint
<= 0x11FF) ||
2764 (iter
.codepoint
>= 0x3130 && iter
.codepoint
<= 0x318F) ||
2765 (iter
.codepoint
>= 0xAC00 && iter
.codepoint
<= 0xD7AF))
2766 type
= NVG_CJK_CHAR
;
2772 if (type
== NVG_NEWLINE
) {
2773 // Always handle new lines.
2774 rows
[nrows
].start
= rowStart
!= NULL
? rowStart
: iter
.str
;
2775 rows
[nrows
].end
= rowEnd
!= NULL
? rowEnd
: iter
.str
;
2776 rows
[nrows
].width
= rowWidth
* invscale
;
2777 rows
[nrows
].minx
= rowMinX
* invscale
;
2778 rows
[nrows
].maxx
= rowMaxX
* invscale
;
2779 rows
[nrows
].next
= iter
.next
;
2781 if (nrows
>= maxRows
)
2783 // Set null break point
2784 breakEnd
= rowStart
;
2787 // Indicate to skip the white space at the beginning of the row.
2791 rowMinX
= rowMaxX
= 0;
2793 if (rowStart
== NULL
) {
2794 // Skip white space until the beginning of the line
2795 if (type
== NVG_CHAR
|| type
== NVG_CJK_CHAR
|| type
== NVG_SKIPPED_CHAR
) {
2796 // The current char is the row so far
2798 rowStart
= iter
.str
;
2800 rowWidth
= iter
.nextx
- rowStartX
;
2801 rowMinX
= q
.x0
- rowStartX
;
2802 rowMaxX
= q
.x1
- rowStartX
;
2803 wordStart
= iter
.str
;
2804 wordStartX
= iter
.x
;
2805 wordMinX
= q
.x0
- rowStartX
;
2806 // Set null break point
2807 breakEnd
= rowStart
;
2812 float nextWidth
= iter
.nextx
- rowStartX
;
2814 // track last non-white space character
2815 if (type
== NVG_CHAR
|| type
== NVG_CJK_CHAR
|| type
== NVG_SKIPPED_CHAR
) {
2817 rowWidth
= iter
.nextx
- rowStartX
;
2818 rowMaxX
= q
.x1
- rowStartX
;
2820 // track last end of a word
2821 if (((ptype
== NVG_CHAR
|| ptype
== NVG_CJK_CHAR
) && type
== NVG_SPACE
) || type
== NVG_CJK_CHAR
) {
2822 breakEnd
= iter
.str
;
2823 breakWidth
= rowWidth
;
2824 breakMaxX
= rowMaxX
;
2826 // track last beginning of a word
2827 if ((ptype
== NVG_SPACE
&& (type
== NVG_CHAR
|| type
== NVG_CJK_CHAR
)) || type
== NVG_CJK_CHAR
) {
2828 wordStart
= iter
.str
;
2829 wordStartX
= iter
.x
;
2833 // Break to new line when a character is beyond break width.
2834 if ((type
== NVG_CHAR
|| type
== NVG_CJK_CHAR
) && nextWidth
> breakRowWidth
) {
2835 // The run length is too long, need to break to new line.
2836 if (breakEnd
== rowStart
) {
2837 // The current word is longer than the row length, just break it from here.
2838 rows
[nrows
].start
= rowStart
;
2839 rows
[nrows
].end
= iter
.str
;
2840 rows
[nrows
].width
= rowWidth
* invscale
;
2841 rows
[nrows
].minx
= rowMinX
* invscale
;
2842 rows
[nrows
].maxx
= rowMaxX
* invscale
;
2843 rows
[nrows
].next
= iter
.str
;
2845 if (nrows
>= maxRows
)
2848 rowStart
= iter
.str
;
2850 rowWidth
= iter
.nextx
- rowStartX
;
2851 rowMinX
= q
.x0
- rowStartX
;
2852 rowMaxX
= q
.x1
- rowStartX
;
2853 wordStart
= iter
.str
;
2854 wordStartX
= iter
.x
;
2855 wordMinX
= q
.x0
- rowStartX
;
2857 // Break the line from the end of the last word, and start new line from the beginning of the new.
2858 rows
[nrows
].start
= rowStart
;
2859 rows
[nrows
].end
= breakEnd
;
2860 rows
[nrows
].width
= breakWidth
* invscale
;
2861 rows
[nrows
].minx
= rowMinX
* invscale
;
2862 rows
[nrows
].maxx
= breakMaxX
* invscale
;
2863 rows
[nrows
].next
= wordStart
;
2865 if (nrows
>= maxRows
)
2868 rowStartX
= wordStartX
;
2869 rowStart
= wordStart
;
2871 rowWidth
= iter
.nextx
- rowStartX
;
2872 rowMinX
= wordMinX
- rowStartX
;
2873 rowMaxX
= q
.x1
- rowStartX
;
2875 // Set null break point
2876 breakEnd
= rowStart
;
2883 pcodepoint
= iter
.codepoint
;
2887 // Break the line from the end of the last word, and start new line from the beginning of the new.
2888 if (rowStart
!= NULL
) {
2889 rows
[nrows
].start
= rowStart
;
2890 rows
[nrows
].end
= rowEnd
;
2891 rows
[nrows
].width
= rowWidth
* invscale
;
2892 rows
[nrows
].minx
= rowMinX
* invscale
;
2893 rows
[nrows
].maxx
= rowMaxX
* invscale
;
2894 rows
[nrows
].next
= end
;
2901 float nvgTextBounds(NVGcontext
* ctx
, float x
, float y
, const char* string
, const char* end
, float* bounds
)
2903 NVGstate
* state
= nvg__getState(ctx
);
2904 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
2905 float invscale
= 1.0f
/ scale
;
2908 if (state
->fontId
== FONS_INVALID
) return 0;
2910 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
2911 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
2912 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
2913 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
2914 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
2916 width
= fonsTextBounds(ctx
->fontContext
->fs
, x
*scale
, y
*scale
, string
, end
, bounds
);
2917 if (bounds
!= NULL
) {
2918 // Use line bounds for height.
2919 fonsLineBounds(ctx
->fontContext
->fs
, y
*scale
, &bounds
[1], &bounds
[3]);
2920 bounds
[0] *= invscale
;
2921 bounds
[1] *= invscale
;
2922 bounds
[2] *= invscale
;
2923 bounds
[3] *= invscale
;
2925 return width
* invscale
;
2928 void nvgTextBoxBounds(NVGcontext
* ctx
, float x
, float y
, float breakRowWidth
, const char* string
, const char* end
, float* bounds
)
2930 NVGstate
* state
= nvg__getState(ctx
);
2932 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
2933 float invscale
= 1.0f
/ scale
;
2935 int oldAlign
= state
->textAlign
;
2936 int haling
= state
->textAlign
& (NVG_ALIGN_LEFT
| NVG_ALIGN_CENTER
| NVG_ALIGN_RIGHT
);
2937 int valign
= state
->textAlign
& (NVG_ALIGN_TOP
| NVG_ALIGN_MIDDLE
| NVG_ALIGN_BOTTOM
| NVG_ALIGN_BASELINE
);
2938 float lineh
= 0, rminy
= 0, rmaxy
= 0;
2939 float minx
, miny
, maxx
, maxy
;
2941 if (state
->fontId
== FONS_INVALID
) {
2943 bounds
[0] = bounds
[1] = bounds
[2] = bounds
[3] = 0.0f
;
2947 nvgTextMetrics(ctx
, NULL
, NULL
, &lineh
);
2949 state
->textAlign
= NVG_ALIGN_LEFT
| valign
;
2954 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
2955 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
2956 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
2957 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
2958 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
2959 fonsLineBounds(ctx
->fontContext
->fs
, 0, &rminy
, &rmaxy
);
2963 while ((nrows
= nvgTextBreakLines(ctx
, string
, end
, breakRowWidth
, rows
, 2))) {
2964 for (i
= 0; i
< nrows
; i
++) {
2965 NVGtextRow
* row
= &rows
[i
];
2966 float rminx
, rmaxx
, dx
= 0;
2967 // Horizontal bounds
2968 if (haling
& NVG_ALIGN_LEFT
)
2970 else if (haling
& NVG_ALIGN_CENTER
)
2971 dx
= breakRowWidth
*0.5f
- row
->width
*0.5f
;
2972 else if (haling
& NVG_ALIGN_RIGHT
)
2973 dx
= breakRowWidth
- row
->width
;
2974 rminx
= x
+ row
->minx
+ dx
;
2975 rmaxx
= x
+ row
->maxx
+ dx
;
2976 minx
= nvg__minf(minx
, rminx
);
2977 maxx
= nvg__maxf(maxx
, rmaxx
);
2979 miny
= nvg__minf(miny
, y
+ rminy
);
2980 maxy
= nvg__maxf(maxy
, y
+ rmaxy
);
2982 y
+= lineh
* state
->lineHeight
;
2984 string
= rows
[nrows
-1].next
;
2987 state
->textAlign
= oldAlign
;
2989 if (bounds
!= NULL
) {
2997 void nvgTextMetrics(NVGcontext
* ctx
, float* ascender
, float* descender
, float* lineh
)
2999 NVGstate
* state
= nvg__getState(ctx
);
3000 float scale
= nvg__getFontScale(state
) * ctx
->devicePxRatio
;
3001 float invscale
= 1.0f
/ scale
;
3003 if (state
->fontId
== FONS_INVALID
) return;
3005 fonsSetSize(ctx
->fontContext
->fs
, state
->fontSize
*scale
);
3006 fonsSetSpacing(ctx
->fontContext
->fs
, state
->letterSpacing
*scale
);
3007 fonsSetBlur(ctx
->fontContext
->fs
, state
->fontBlur
*scale
);
3008 fonsSetAlign(ctx
->fontContext
->fs
, state
->textAlign
);
3009 fonsSetFont(ctx
->fontContext
->fs
, state
->fontId
);
3011 fonsVertMetrics(ctx
->fontContext
->fs
, ascender
, descender
, lineh
);
3012 if (ascender
!= NULL
)
3013 *ascender
*= invscale
;
3014 if (descender
!= NULL
)
3015 *descender
*= invscale
;
3019 // vim: ft=c nu noet ts=4