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