diff options
Diffstat (limited to 'nanovg')
-rw-r--r-- | nanovg/LICENSE | 46 | ||||
-rw-r--r-- | nanovg/fontstash.h | 1790 | ||||
-rw-r--r-- | nanovg/nanovg.c | 2958 | ||||
-rw-r--r-- | nanovg/nanovg.h | 708 | ||||
-rw-r--r-- | nanovg/nanovg_gl.c | 1649 | ||||
-rw-r--r-- | nanovg/nanovg_gl.h | 92 |
6 files changed, 0 insertions, 7243 deletions
diff --git a/nanovg/LICENSE b/nanovg/LICENSE deleted file mode 100644 index 4667b48..0000000 --- a/nanovg/LICENSE +++ /dev/null @@ -1,46 +0,0 @@ -NanoVG - Antialiased vector graphics rendering library for OpenGL -Copyright (c) 2013 Mikko Mononen memon@inside.org - -NanoSVG - Simple single-header-file SVG parse -Copyright (c) 2013-14 Mikko Mononen memon@inside.org - - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not -claim that you wrote the original software. If you use this software -in a product, an acknowledgment in the product documentation would be -appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be -misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - ------------------------------------------------------------------------------- - -Blendish - Blender 2.5 UI based theming functions for NanoVG -Copyright (c) 2014 Leonard Ritter <leonard.ritter@duangle.com> - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/nanovg/fontstash.h b/nanovg/fontstash.h deleted file mode 100644 index 29d5caf..0000000 --- a/nanovg/fontstash.h +++ /dev/null @@ -1,1790 +0,0 @@ -// -// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef FONTSTASH_H_F380F1FA_CDA3_11EA_AF58_0FE5E5086B14 -#define FONTSTASH_H_F380F1FA_CDA3_11EA_AF58_0FE5E5086B14 - -#define FONS_INVALID -1 - -enum FONSflags { - FONS_ZERO_TOPLEFT = 1, - FONS_ZERO_BOTTOMLEFT = 2, -}; - -enum FONSalign { - // Horizontal align - FONS_ALIGN_LEFT = 1<<0, // Default - FONS_ALIGN_CENTER = 1<<1, - FONS_ALIGN_RIGHT = 1<<2, - // Vertical align - FONS_ALIGN_TOP = 1<<3, - FONS_ALIGN_MIDDLE = 1<<4, - FONS_ALIGN_BOTTOM = 1<<5, - FONS_ALIGN_BASELINE = 1<<6, // Default -}; - -enum FONSglyphBitmap { - FONS_GLYPH_BITMAP_OPTIONAL = 1, - FONS_GLYPH_BITMAP_REQUIRED = 2, -}; - -enum FONSerrorCode { - // Font atlas is full. - FONS_ATLAS_FULL = 1, - // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. - FONS_SCRATCH_FULL = 2, - // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. - FONS_STATES_OVERFLOW = 3, - // Trying to pop too many states fonsPopState(). - FONS_STATES_UNDERFLOW = 4, -}; - -struct FONSparams { - int width, height; - unsigned char flags; - void* userPtr; - int (*renderCreate)(void* uptr, int width, int height); - int (*renderResize)(void* uptr, int width, int height); - void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); - void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); - void (*renderDelete)(void* uptr); -}; -typedef struct FONSparams FONSparams; - -struct FONSquad -{ - float x0,y0,s0,t0; - float x1,y1,s1,t1; -}; -typedef struct FONSquad FONSquad; - -struct FONStextIter { - float x, y, nextx, nexty, scale, spacing; - unsigned int codepoint; - short isize, iblur; - struct FONSfont* font; - int prevGlyphIndex; - const char* str; - const char* next; - const char* end; - unsigned int utf8state; - int bitmapOption; -}; -typedef struct FONStextIter FONStextIter; - -typedef struct FONScontext FONScontext; - -// Constructor and destructor. -FONScontext* fonsCreateInternal(FONSparams* params); -void fonsDeleteInternal(FONScontext* s); - -void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); -// Returns current atlas size. -void fonsGetAtlasSize(FONScontext* s, int* width, int* height); -// Expands the atlas size. -int fonsExpandAtlas(FONScontext* s, int width, int height); -// Resets the whole stash. -int fonsResetAtlas(FONScontext* stash, int width, int height); - -// Add fonts -int fonsAddFont(FONScontext* s, const char* name, const char* path, int fontIndex); -int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData, int fontIndex); -int fonsGetFontByName(FONScontext* s, const char* name); - -// State handling -void fonsPushState(FONScontext* s); -void fonsPopState(FONScontext* s); -void fonsClearState(FONScontext* s); - -// State setting -void fonsSetSize(FONScontext* s, float size); -void fonsSetColor(FONScontext* s, unsigned int color); -void fonsSetSpacing(FONScontext* s, float spacing); -void fonsSetBlur(FONScontext* s, float blur); -void fonsSetAlign(FONScontext* s, int align); -void fonsSetFont(FONScontext* s, int font); - -// Draw text -float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); - -// Measure text -float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); -void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); -void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); - -// Text iterator -int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end, int bitmapOption); -int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); - -// Pull texture changes -const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); -int fonsValidateTexture(FONScontext* s, int* dirty); - -// Draws the stash texture for debugging -void fonsDrawDebug(FONScontext* s, float x, float y); - -#endif // FONTSTASH_H_F380F1FA_CDA3_11EA_AF58_0FE5E5086B14 - - -#ifdef FONTSTASH_IMPLEMENTATION - -#define FONS_NOTUSED(v) (void)sizeof(v) - -#ifdef FONS_USE_FREETYPE - -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_ADVANCES_H -#include <math.h> - -struct FONSttFontImpl { - FT_Face font; -}; -typedef struct FONSttFontImpl FONSttFontImpl; - -#else - -#define STB_TRUETYPE_IMPLEMENTATION -static void* fons__tmpalloc(size_t size, void* up); -static void fons__tmpfree(void* ptr, void* up); -#define STBTT_malloc(x,u) fons__tmpalloc(x,u) -#define STBTT_free(x,u) fons__tmpfree(x,u) -#include "../stb/stb_truetype.h" - -struct FONSttFontImpl { - stbtt_fontinfo font; -}; -typedef struct FONSttFontImpl FONSttFontImpl; - -#endif - -#ifndef FONS_SCRATCH_BUF_SIZE -# define FONS_SCRATCH_BUF_SIZE 96000 -#endif -#ifndef FONS_HASH_LUT_SIZE -# define FONS_HASH_LUT_SIZE 256 -#endif -#ifndef FONS_INIT_FONTS -# define FONS_INIT_FONTS 4 -#endif -#ifndef FONS_INIT_GLYPHS -# define FONS_INIT_GLYPHS 256 -#endif -#ifndef FONS_INIT_ATLAS_NODES -# define FONS_INIT_ATLAS_NODES 256 -#endif -#ifndef FONS_VERTEX_COUNT -# define FONS_VERTEX_COUNT 1024 -#endif -#ifndef FONS_MAX_STATES -# define FONS_MAX_STATES 20 -#endif -#ifndef FONS_MAX_FALLBACKS -# define FONS_MAX_FALLBACKS 20 -#endif - -static unsigned int fons__hashint(unsigned int a) -{ - a += ~(a<<15); - a ^= (a>>10); - a += (a<<3); - a ^= (a>>6); - a += ~(a<<11); - a ^= (a>>16); - return a; -} - -static int fons__mini(int a, int b) -{ - return a < b ? a : b; -} - -static int fons__maxi(int a, int b) -{ - return a > b ? a : b; -} - -struct FONSglyph -{ - unsigned int codepoint; - int index; - int next; - short size, blur; - short x0,y0,x1,y1; - short xadv,xoff,yoff; -}; -typedef struct FONSglyph FONSglyph; - -struct FONSfont -{ - FONSttFontImpl font; - char name[64]; - unsigned char* data; - int dataSize; - unsigned char freeData; - float ascender; - float descender; - float lineh; - FONSglyph* glyphs; - int cglyphs; - int nglyphs; - int lut[FONS_HASH_LUT_SIZE]; - int fallbacks[FONS_MAX_FALLBACKS]; - int nfallbacks; -}; -typedef struct FONSfont FONSfont; - -struct FONSstate -{ - int font; - int align; - float size; - unsigned int color; - float blur; - float spacing; -}; -typedef struct FONSstate FONSstate; - -struct FONSatlasNode { - short x, y, width; -}; -typedef struct FONSatlasNode FONSatlasNode; - -struct FONSatlas -{ - int width, height; - FONSatlasNode* nodes; - int nnodes; - int cnodes; -}; -typedef struct FONSatlas FONSatlas; - -struct FONScontext -{ - FONSparams params; - float itw,ith; - unsigned char* texData; - int dirtyRect[4]; - FONSfont** fonts; - FONSatlas* atlas; - int cfonts; - int nfonts; - float verts[FONS_VERTEX_COUNT*2]; - float tcoords[FONS_VERTEX_COUNT*2]; - unsigned int colors[FONS_VERTEX_COUNT]; - int nverts; - unsigned char* scratch; - int nscratch; - FONSstate states[FONS_MAX_STATES]; - int nstates; - void (*handleError)(void* uptr, int error, int val); - void* errorUptr; -#ifdef FONS_USE_FREETYPE - FT_Library ftLibrary; -#endif -}; - -#ifdef FONS_USE_FREETYPE - -int fons__tt_init(FONScontext *context) -{ - FT_Error ftError; - FONS_NOTUSED(context); - ftError = FT_Init_FreeType(&context->ftLibrary); - return ftError == 0; -} - -int fons__tt_done(FONScontext *context) -{ - FT_Error ftError; - FONS_NOTUSED(context); - ftError = FT_Done_FreeType(context->ftLibrary); - return ftError == 0; -} - -int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) -{ - FT_Error ftError; - FONS_NOTUSED(context); - - ftError = FT_New_Memory_Face(context->ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); - return ftError == 0; -} - -void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) -{ - *ascent = font->font->ascender; - *descent = font->font->descender; - *lineGap = font->font->height - (*ascent - *descent); -} - -float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) -{ - return size / font->font->units_per_EM; -} - -int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) -{ - return FT_Get_Char_Index(font->font, codepoint); -} - -int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, - int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) -{ - FT_Error ftError; - FT_GlyphSlot ftGlyph; - FT_Fixed advFixed; - FONS_NOTUSED(scale); - - ftError = FT_Set_Pixel_Sizes(font->font, 0, size); - if (ftError) return 0; - ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT); - if (ftError) return 0; - ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); - if (ftError) return 0; - ftGlyph = font->font->glyph; - *advance = (int)advFixed; - *lsb = (int)ftGlyph->metrics.horiBearingX; - *x0 = ftGlyph->bitmap_left; - *x1 = *x0 + ftGlyph->bitmap.width; - *y0 = -ftGlyph->bitmap_top; - *y1 = *y0 + ftGlyph->bitmap.rows; - return 1; -} - -void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, - float scaleX, float scaleY, int glyph) -{ - FT_GlyphSlot ftGlyph = font->font->glyph; - int ftGlyphOffset = 0; - unsigned int x, y; - FONS_NOTUSED(outWidth); - FONS_NOTUSED(outHeight); - FONS_NOTUSED(scaleX); - FONS_NOTUSED(scaleY); - FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap - - for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { - for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { - output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; - } - } -} - -int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) -{ - FT_Vector ftKerning; - FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); - return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer -} - -#else - -int fons__tt_init(FONScontext *context) -{ - FONS_NOTUSED(context); - return 1; -} - -int fons__tt_done(FONScontext *context) -{ - FONS_NOTUSED(context); - return 1; -} - -int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) -{ - int offset, stbError; - FONS_NOTUSED(dataSize); - - font->font.userdata = context; - offset = stbtt_GetFontOffsetForIndex(data, fontIndex); - if (offset == -1) { - stbError = 0; - } else { - stbError = stbtt_InitFont(&font->font, data, offset); - } - return stbError; -} - -void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) -{ - stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); -} - -float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) -{ - return stbtt_ScaleForMappingEmToPixels(&font->font, size); -} - -int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) -{ - return stbtt_FindGlyphIndex(&font->font, codepoint); -} - -int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, - int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) -{ - FONS_NOTUSED(size); - stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); - stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); - return 1; -} - -void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, - float scaleX, float scaleY, int glyph) -{ - stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); -} - -int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) -{ - return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); -} - -#endif - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -static void* fons__tmpalloc(size_t size, void* up) -{ - unsigned char* ptr; - FONScontext* stash = (FONScontext*)up; - - // 16-byte align the returned pointer - size = (size + 0xf) & ~0xf; - - if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { - if (stash->handleError) - stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); - return NULL; - } - ptr = stash->scratch + stash->nscratch; - stash->nscratch += (int)size; - return ptr; -} - -static void fons__tmpfree(void* ptr, void* up) -{ - (void)ptr; - (void)up; - // empty -} - -#endif // STB_TRUETYPE_IMPLEMENTATION - -// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> -// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. - -#define FONS_UTF8_ACCEPT 0 -#define FONS_UTF8_REJECT 12 - -static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) -{ - static const unsigned char utf8d[] = { - // The first part of the table maps bytes to character classes that - // to reduce the size of the transition table and create bitmasks. - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - - // The second part is a transition table that maps a combination - // of a state of the automaton and a character class to a state. - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, - }; - - unsigned int type = utf8d[byte]; - - *codep = (*state != FONS_UTF8_ACCEPT) ? - (byte & 0x3fu) | (*codep << 6) : - (0xff >> type) & (byte); - - *state = utf8d[256 + *state + type]; - return *state; -} - -// Atlas based on Skyline Bin Packer by Jukka Jylänki - -static void fons__deleteAtlas(FONSatlas* atlas) -{ - if (atlas == NULL) return; - if (atlas->nodes != NULL) free(atlas->nodes); - free(atlas); -} - -static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) -{ - FONSatlas* atlas = NULL; - - // Allocate memory for the font stash. - atlas = (FONSatlas*)malloc(sizeof(FONSatlas)); - if (atlas == NULL) goto error; - memset(atlas, 0, sizeof(FONSatlas)); - - atlas->width = w; - atlas->height = h; - - // Allocate space for skyline nodes - atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes); - if (atlas->nodes == NULL) goto error; - memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); - atlas->nnodes = 0; - atlas->cnodes = nnodes; - - // Init root node. - atlas->nodes[0].x = 0; - atlas->nodes[0].y = 0; - atlas->nodes[0].width = (short)w; - atlas->nnodes++; - - return atlas; - -error: - if (atlas) fons__deleteAtlas(atlas); - return NULL; -} - -static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) -{ - int i; - // Insert node - if (atlas->nnodes+1 > atlas->cnodes) { - atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; - atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); - if (atlas->nodes == NULL) - return 0; - } - for (i = atlas->nnodes; i > idx; i--) - atlas->nodes[i] = atlas->nodes[i-1]; - atlas->nodes[idx].x = (short)x; - atlas->nodes[idx].y = (short)y; - atlas->nodes[idx].width = (short)w; - atlas->nnodes++; - - return 1; -} - -static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) -{ - int i; - if (atlas->nnodes == 0) return; - for (i = idx; i < atlas->nnodes-1; i++) - atlas->nodes[i] = atlas->nodes[i+1]; - atlas->nnodes--; -} - -static void fons__atlasExpand(FONSatlas* atlas, int w, int h) -{ - // Insert node for empty space - if (w > atlas->width) - fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); - atlas->width = w; - atlas->height = h; -} - -static void fons__atlasReset(FONSatlas* atlas, int w, int h) -{ - atlas->width = w; - atlas->height = h; - atlas->nnodes = 0; - - // Init root node. - atlas->nodes[0].x = 0; - atlas->nodes[0].y = 0; - atlas->nodes[0].width = (short)w; - atlas->nnodes++; -} - -static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) -{ - int i; - - // Insert new node - if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) - return 0; - - // Delete skyline segments that fall under the shadow of the new segment. - for (i = idx+1; i < atlas->nnodes; i++) { - if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { - int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; - atlas->nodes[i].x += (short)shrink; - atlas->nodes[i].width -= (short)shrink; - if (atlas->nodes[i].width <= 0) { - fons__atlasRemoveNode(atlas, i); - i--; - } else { - break; - } - } else { - break; - } - } - - // Merge same height skyline segments that are next to each other. - for (i = 0; i < atlas->nnodes-1; i++) { - if (atlas->nodes[i].y == atlas->nodes[i+1].y) { - atlas->nodes[i].width += atlas->nodes[i+1].width; - fons__atlasRemoveNode(atlas, i+1); - i--; - } - } - - return 1; -} - -static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) -{ - // Checks if there is enough space at the location of skyline span 'i', - // and return the max height of all skyline spans under that at that location, - // (think tetris block being dropped at that position). Or -1 if no space found. - int x = atlas->nodes[i].x; - int y = atlas->nodes[i].y; - int spaceLeft; - if (x + w > atlas->width) - return -1; - spaceLeft = w; - while (spaceLeft > 0) { - if (i == atlas->nnodes) return -1; - y = fons__maxi(y, atlas->nodes[i].y); - if (y + h > atlas->height) return -1; - spaceLeft -= atlas->nodes[i].width; - ++i; - } - return y; -} - -static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) -{ - int besth = atlas->height, bestw = atlas->width, besti = -1; - int bestx = -1, besty = -1, i; - - // Bottom left fit heuristic. - for (i = 0; i < atlas->nnodes; i++) { - int y = fons__atlasRectFits(atlas, i, rw, rh); - if (y != -1) { - if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { - besti = i; - bestw = atlas->nodes[i].width; - besth = y + rh; - bestx = atlas->nodes[i].x; - besty = y; - } - } - } - - if (besti == -1) - return 0; - - // Perform the actual packing. - if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) - return 0; - - *rx = bestx; - *ry = besty; - - return 1; -} - -static void fons__addWhiteRect(FONScontext* stash, int w, int h) -{ - int x, y, gx, gy; - unsigned char* dst; - if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) - return; - - // Rasterize - dst = &stash->texData[gx + gy * stash->params.width]; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) - dst[x] = 0xff; - dst += stash->params.width; - } - - stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); - stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); - stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); - stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); -} - -FONScontext* fonsCreateInternal(FONSparams* params) -{ - FONScontext* stash = NULL; - - // Allocate memory for the font stash. - stash = (FONScontext*)malloc(sizeof(FONScontext)); - if (stash == NULL) goto error; - memset(stash, 0, sizeof(FONScontext)); - - stash->params = *params; - - // Allocate scratch buffer. - stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE); - if (stash->scratch == NULL) goto error; - - // Initialize implementation library - if (!fons__tt_init(stash)) goto error; - - if (stash->params.renderCreate != NULL) { - if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) - goto error; - } - - stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); - if (stash->atlas == NULL) goto error; - - // Allocate space for fonts. - stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS); - if (stash->fonts == NULL) goto error; - memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); - stash->cfonts = FONS_INIT_FONTS; - stash->nfonts = 0; - - // Create texture for the cache. - stash->itw = 1.0f/stash->params.width; - stash->ith = 1.0f/stash->params.height; - stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height); - if (stash->texData == NULL) goto error; - memset(stash->texData, 0, stash->params.width * stash->params.height); - - stash->dirtyRect[0] = stash->params.width; - stash->dirtyRect[1] = stash->params.height; - stash->dirtyRect[2] = 0; - stash->dirtyRect[3] = 0; - - // Add white rect at 0,0 for debug drawing. - fons__addWhiteRect(stash, 2,2); - - fonsPushState(stash); - fonsClearState(stash); - - return stash; - -error: - fonsDeleteInternal(stash); - return NULL; -} - -static FONSstate* fons__getState(FONScontext* stash) -{ - return &stash->states[stash->nstates-1]; -} - -int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) -{ - FONSfont* baseFont = stash->fonts[base]; - if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { - baseFont->fallbacks[baseFont->nfallbacks++] = fallback; - return 1; - } - return 0; -} - -void fonsResetFallbackFont(FONScontext* stash, int base) -{ - int i; - - FONSfont* baseFont = stash->fonts[base]; - baseFont->nfallbacks = 0; - baseFont->nglyphs = 0; - for (i = 0; i < FONS_HASH_LUT_SIZE; i++) - baseFont->lut[i] = -1; -} - -void fonsSetSize(FONScontext* stash, float size) -{ - fons__getState(stash)->size = size; -} - -void fonsSetColor(FONScontext* stash, unsigned int color) -{ - fons__getState(stash)->color = color; -} - -void fonsSetSpacing(FONScontext* stash, float spacing) -{ - fons__getState(stash)->spacing = spacing; -} - -void fonsSetBlur(FONScontext* stash, float blur) -{ - fons__getState(stash)->blur = blur; -} - -void fonsSetAlign(FONScontext* stash, int align) -{ - fons__getState(stash)->align = align; -} - -void fonsSetFont(FONScontext* stash, int font) -{ - fons__getState(stash)->font = font; -} - -void fonsPushState(FONScontext* stash) -{ - if (stash->nstates >= FONS_MAX_STATES) { - if (stash->handleError) - stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); - return; - } - if (stash->nstates > 0) - memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); - stash->nstates++; -} - -void fonsPopState(FONScontext* stash) -{ - if (stash->nstates <= 1) { - if (stash->handleError) - stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); - return; - } - stash->nstates--; -} - -void fonsClearState(FONScontext* stash) -{ - FONSstate* state = fons__getState(stash); - state->size = 12.0f; - state->color = 0xffffffff; - state->font = 0; - state->blur = 0; - state->spacing = 0; - state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; -} - -static void fons__freeFont(FONSfont* font) -{ - if (font == NULL) return; - if (font->glyphs) free(font->glyphs); - if (font->freeData && font->data) free(font->data); - free(font); -} - -static int fons__allocFont(FONScontext* stash) -{ - FONSfont* font = NULL; - if (stash->nfonts+1 > stash->cfonts) { - stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; - stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); - if (stash->fonts == NULL) - return -1; - } - font = (FONSfont*)malloc(sizeof(FONSfont)); - if (font == NULL) goto error; - memset(font, 0, sizeof(FONSfont)); - - font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS); - if (font->glyphs == NULL) goto error; - font->cglyphs = FONS_INIT_GLYPHS; - font->nglyphs = 0; - - stash->fonts[stash->nfonts++] = font; - return stash->nfonts-1; - -error: - fons__freeFont(font); - - return FONS_INVALID; -} - -int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) -{ - FILE* fp = 0; - int dataSize = 0; - size_t readed; - unsigned char* data = NULL; - - // Read in the font data. - fp = fopen(path, "rb"); - if (fp == NULL) goto error; - fseek(fp,0,SEEK_END); - dataSize = (int)ftell(fp); - fseek(fp,0,SEEK_SET); - data = (unsigned char*)malloc(dataSize); - if (data == NULL) goto error; - readed = fread(data, 1, dataSize, fp); - fclose(fp); - fp = 0; - if (readed != (size_t)dataSize) goto error; - - return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); - -error: - if (data) free(data); - if (fp) fclose(fp); - return FONS_INVALID; -} - -int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) -{ - int i, ascent, descent, fh, lineGap; - FONSfont* font; - - int idx = fons__allocFont(stash); - if (idx == FONS_INVALID) - return FONS_INVALID; - - font = stash->fonts[idx]; - - strncpy(font->name, name, sizeof(font->name)); - font->name[sizeof(font->name)-1] = '\0'; - - // Init hash lookup. - for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) - font->lut[i] = -1; - - // Read in the font data. - font->dataSize = dataSize; - font->data = data; - font->freeData = (unsigned char)freeData; - - // Init font - stash->nscratch = 0; - if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; - - // Store normalized line height. The real line height is got - // by multiplying the lineh by font size. - fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); - ascent += lineGap; - fh = ascent - descent; - font->ascender = (float)ascent / (float)fh; - font->descender = (float)descent / (float)fh; - font->lineh = font->ascender - font->descender; - - return idx; - -error: - fons__freeFont(font); - stash->nfonts--; - return FONS_INVALID; -} - -int fonsGetFontByName(FONScontext* s, const char* name) -{ - int i; - for (i = 0; i < s->nfonts; i++) { - if (strcmp(s->fonts[i]->name, name) == 0) - return i; - } - return FONS_INVALID; -} - - -static FONSglyph* fons__allocGlyph(FONSfont* font) -{ - if (font->nglyphs+1 > font->cglyphs) { - font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; - font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); - if (font->glyphs == NULL) return NULL; - } - font->nglyphs++; - return &font->glyphs[font->nglyphs-1]; -} - - -// Based on Exponential blur, Jani Huhtanen, 2006 - -#define APREC 16 -#define ZPREC 7 - -static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) -{ - int x, y; - for (y = 0; y < h; y++) { - int z = 0; // force zero border - for (x = 1; x < w; x++) { - z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; - dst[x] = (unsigned char)(z >> ZPREC); - } - dst[w-1] = 0; // force zero border - z = 0; - for (x = w-2; x >= 0; x--) { - z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; - dst[x] = (unsigned char)(z >> ZPREC); - } - dst[0] = 0; // force zero border - dst += dstStride; - } -} - -static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) -{ - int x, y; - for (x = 0; x < w; x++) { - int z = 0; // force zero border - for (y = dstStride; y < h*dstStride; y += dstStride) { - z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; - dst[y] = (unsigned char)(z >> ZPREC); - } - dst[(h-1)*dstStride] = 0; // force zero border - z = 0; - for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { - z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; - dst[y] = (unsigned char)(z >> ZPREC); - } - dst[0] = 0; // force zero border - dst++; - } -} - - -static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) -{ - int alpha; - float sigma; - (void)stash; - - if (blur < 1) - return; - // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) - sigma = (float)blur * 0.57735f; // 1 / sqrt(3) - alpha = (int)((1<<APREC) * (1.0f - expf(-2.3f / (sigma+1.0f)))); - fons__blurRows(dst, w, h, dstStride, alpha); - fons__blurCols(dst, w, h, dstStride, alpha); - fons__blurRows(dst, w, h, dstStride, alpha); - fons__blurCols(dst, w, h, dstStride, alpha); -// fons__blurrows(dst, w, h, dstStride, alpha); -// fons__blurcols(dst, w, h, dstStride, alpha); -} - -static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, - short isize, short iblur, int bitmapOption) -{ - int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; - float scale; - FONSglyph* glyph = NULL; - unsigned int h; - float size = isize/10.0f; - int pad, added; - unsigned char* bdst; - unsigned char* dst; - FONSfont* renderFont = font; - - if (isize < 2) return NULL; - if (iblur > 20) iblur = 20; - pad = iblur+2; - - // Reset allocator. - stash->nscratch = 0; - - // Find code point and size. - h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); - i = font->lut[h]; - while (i != -1) { - if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) { - glyph = &font->glyphs[i]; - if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL || (glyph->x0 >= 0 && glyph->y0 >= 0)) { - return glyph; - } - // At this point, glyph exists but the bitmap data is not yet created. - break; - } - i = font->glyphs[i].next; - } - - // Create a new glyph or rasterize bitmap data for a cached glyph. - g = fons__tt_getGlyphIndex(&font->font, codepoint); - // Try to find the glyph in fallback fonts. - if (g == 0) { - for (i = 0; i < font->nfallbacks; ++i) { - FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; - int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); - if (fallbackIndex != 0) { - g = fallbackIndex; - renderFont = fallbackFont; - break; - } - } - // It is possible that we did not find a fallback glyph. - // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. - } - scale = fons__tt_getPixelHeightScale(&renderFont->font, size); - fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); - gw = x1-x0 + pad*2; - gh = y1-y0 + pad*2; - - // Determines the spot to draw glyph in the atlas. - if (bitmapOption == FONS_GLYPH_BITMAP_REQUIRED) { - // Find free spot for the rect in the atlas - added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); - if (added == 0 && stash->handleError != NULL) { - // Atlas is full, let the user to resize the atlas (or not), and try again. - stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); - added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); - } - if (added == 0) return NULL; - } else { - // Negative coordinate indicates there is no bitmap data created. - gx = -1; - gy = -1; - } - - // Init glyph. - if (glyph == NULL) { - glyph = fons__allocGlyph(font); - glyph->codepoint = codepoint; - glyph->size = isize; - glyph->blur = iblur; - glyph->next = 0; - - // Insert char to hash lookup. - glyph->next = font->lut[h]; - font->lut[h] = font->nglyphs-1; - } - glyph->index = g; - glyph->x0 = (short)gx; - glyph->y0 = (short)gy; - glyph->x1 = (short)(glyph->x0+gw); - glyph->y1 = (short)(glyph->y0+gh); - glyph->xadv = (short)(scale * advance * 10.0f); - glyph->xoff = (short)(x0 - pad); - glyph->yoff = (short)(y0 - pad); - - if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) { - return glyph; - } - - // Rasterize - dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; - fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale, scale, g); - - // Make sure there is one pixel empty border. - dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; - for (y = 0; y < gh; y++) { - dst[y*stash->params.width] = 0; - dst[gw-1 + y*stash->params.width] = 0; - } - for (x = 0; x < gw; x++) { - dst[x] = 0; - dst[x + (gh-1)*stash->params.width] = 0; - } - - // Debug code to color the glyph background -/* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; - for (y = 0; y < gh; y++) { - for (x = 0; x < gw; x++) { - int a = (int)fdst[x+y*stash->params.width] + 20; - if (a > 255) a = 255; - fdst[x+y*stash->params.width] = a; - } - }*/ - - // Blur - if (iblur > 0) { - stash->nscratch = 0; - bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; - fons__blur(stash, bdst, gw, gh, stash->params.width, iblur); - } - - stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); - stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); - stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); - stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); - - return glyph; -} - -static void fons__getQuad(FONScontext* stash, FONSfont* font, - int prevGlyphIndex, FONSglyph* glyph, - float scale, float spacing, float* x, float* y, FONSquad* q) -{ - float rx,ry,xoff,yoff,x0,y0,x1,y1; - - if (prevGlyphIndex != -1) { - float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; - *x += (int)(adv + spacing + 0.5f); - } - - // Each glyph has 2px border to allow good interpolation, - // one pixel to prevent leaking, and one to allow good interpolation for rendering. - // Inset the texture region by one pixel for correct interpolation. - xoff = (short)(glyph->xoff+1); - yoff = (short)(glyph->yoff+1); - x0 = (float)(glyph->x0+1); - y0 = (float)(glyph->y0+1); - x1 = (float)(glyph->x1-1); - y1 = (float)(glyph->y1-1); - - if (stash->params.flags & FONS_ZERO_TOPLEFT) { - rx = floorf(*x + xoff); - ry = floorf(*y + yoff); - - q->x0 = rx; - q->y0 = ry; - q->x1 = rx + x1 - x0; - q->y1 = ry + y1 - y0; - - q->s0 = x0 * stash->itw; - q->t0 = y0 * stash->ith; - q->s1 = x1 * stash->itw; - q->t1 = y1 * stash->ith; - } else { - rx = floorf(*x + xoff); - ry = floorf(*y - yoff); - - q->x0 = rx; - q->y0 = ry; - q->x1 = rx + x1 - x0; - q->y1 = ry - y1 + y0; - - q->s0 = x0 * stash->itw; - q->t0 = y0 * stash->ith; - q->s1 = x1 * stash->itw; - q->t1 = y1 * stash->ith; - } - - *x += (int)(glyph->xadv / 10.0f + 0.5f); -} - -static void fons__flush(FONScontext* stash) -{ - // Flush texture - if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { - if (stash->params.renderUpdate != NULL) - stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); - // Reset dirty rect - stash->dirtyRect[0] = stash->params.width; - stash->dirtyRect[1] = stash->params.height; - stash->dirtyRect[2] = 0; - stash->dirtyRect[3] = 0; - } - - // Flush triangles - if (stash->nverts > 0) { - if (stash->params.renderDraw != NULL) - stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); - stash->nverts = 0; - } -} - -static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) -{ - stash->verts[stash->nverts*2+0] = x; - stash->verts[stash->nverts*2+1] = y; - stash->tcoords[stash->nverts*2+0] = s; - stash->tcoords[stash->nverts*2+1] = t; - stash->colors[stash->nverts] = c; - stash->nverts++; -} - -static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) -{ - if (stash->params.flags & FONS_ZERO_TOPLEFT) { - if (align & FONS_ALIGN_TOP) { - return font->ascender * (float)isize/10.0f; - } else if (align & FONS_ALIGN_MIDDLE) { - return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; - } else if (align & FONS_ALIGN_BASELINE) { - return 0.0f; - } else if (align & FONS_ALIGN_BOTTOM) { - return font->descender * (float)isize/10.0f; - } - } else { - if (align & FONS_ALIGN_TOP) { - return -font->ascender * (float)isize/10.0f; - } else if (align & FONS_ALIGN_MIDDLE) { - return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; - } else if (align & FONS_ALIGN_BASELINE) { - return 0.0f; - } else if (align & FONS_ALIGN_BOTTOM) { - return -font->descender * (float)isize/10.0f; - } - } - return 0.0; -} - -float fonsDrawText(FONScontext* stash, - float x, float y, - const char* str, const char* end) -{ - FONSstate* state = fons__getState(stash); - unsigned int codepoint; - unsigned int utf8state = 0; - FONSglyph* glyph = NULL; - FONSquad q; - int prevGlyphIndex = -1; - short isize = (short)(state->size*10.0f); - short iblur = (short)state->blur; - float scale; - FONSfont* font; - float width; - - if (stash == NULL) return x; - if (state->font < 0 || state->font >= stash->nfonts) return x; - font = stash->fonts[state->font]; - if (font->data == NULL) return x; - - scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); - - if (end == NULL) - end = str + strlen(str); - - // Align horizontally - if (state->align & FONS_ALIGN_LEFT) { - // empty - } else if (state->align & FONS_ALIGN_RIGHT) { - width = fonsTextBounds(stash, x,y, str, end, NULL); - x -= width; - } else if (state->align & FONS_ALIGN_CENTER) { - width = fonsTextBounds(stash, x,y, str, end, NULL); - x -= width * 0.5f; - } - // Align vertically. - y += fons__getVertAlign(stash, font, state->align, isize); - - for (; str != end; ++str) { - if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) - continue; - glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED); - if (glyph != NULL) { - fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); - - if (stash->nverts+6 > FONS_VERTEX_COUNT) - fons__flush(stash); - - fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); - fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); - fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); - - fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); - fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); - fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); - } - prevGlyphIndex = glyph != NULL ? glyph->index : -1; - } - fons__flush(stash); - - return x; -} - -int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, - float x, float y, const char* str, const char* end, int bitmapOption) -{ - FONSstate* state = fons__getState(stash); - float width; - - memset(iter, 0, sizeof(*iter)); - - if (stash == NULL) return 0; - if (state->font < 0 || state->font >= stash->nfonts) return 0; - iter->font = stash->fonts[state->font]; - if (iter->font->data == NULL) return 0; - - iter->isize = (short)(state->size*10.0f); - iter->iblur = (short)state->blur; - iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); - - // Align horizontally - if (state->align & FONS_ALIGN_LEFT) { - // empty - } else if (state->align & FONS_ALIGN_RIGHT) { - width = fonsTextBounds(stash, x,y, str, end, NULL); - x -= width; - } else if (state->align & FONS_ALIGN_CENTER) { - width = fonsTextBounds(stash, x,y, str, end, NULL); - x -= width * 0.5f; - } - // Align vertically. - y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); - - if (end == NULL) - end = str + strlen(str); - - iter->x = iter->nextx = x; - iter->y = iter->nexty = y; - iter->spacing = state->spacing; - iter->str = str; - iter->next = str; - iter->end = end; - iter->codepoint = 0; - iter->prevGlyphIndex = -1; - iter->bitmapOption = bitmapOption; - - return 1; -} - -int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) -{ - FONSglyph* glyph = NULL; - const char* str = iter->next; - iter->str = iter->next; - - if (str == iter->end) - return 0; - - for (; str != iter->end; str++) { - if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) - continue; - str++; - // Get glyph and quad - iter->x = iter->nextx; - iter->y = iter->nexty; - glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur, iter->bitmapOption); - // If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid. - if (glyph != NULL) - fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); - iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; - break; - } - iter->next = str; - - return 1; -} - -void fonsDrawDebug(FONScontext* stash, float x, float y) -{ - int i; - int w = stash->params.width; - int h = stash->params.height; - float u = w == 0 ? 0 : (1.0f / w); - float v = h == 0 ? 0 : (1.0f / h); - - if (stash->nverts+6+6 > FONS_VERTEX_COUNT) - fons__flush(stash); - - // Draw background - fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); - fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); - fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); - - fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); - fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); - fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); - - // Draw texture - fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); - fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); - fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); - - fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); - fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); - fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); - - // Drawbug draw atlas - for (i = 0; i < stash->atlas->nnodes; i++) { - FONSatlasNode* n = &stash->atlas->nodes[i]; - - if (stash->nverts+6 > FONS_VERTEX_COUNT) - fons__flush(stash); - - fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); - fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); - fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); - - fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); - fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); - fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); - } - - fons__flush(stash); -} - -float fonsTextBounds(FONScontext* stash, - float x, float y, - const char* str, const char* end, - float* bounds) -{ - FONSstate* state = fons__getState(stash); - unsigned int codepoint; - unsigned int utf8state = 0; - FONSquad q; - FONSglyph* glyph = NULL; - int prevGlyphIndex = -1; - short isize = (short)(state->size*10.0f); - short iblur = (short)state->blur; - float scale; - FONSfont* font; - float startx, advance; - float minx, miny, maxx, maxy; - - if (stash == NULL) return 0; - if (state->font < 0 || state->font >= stash->nfonts) return 0; - font = stash->fonts[state->font]; - if (font->data == NULL) return 0; - - scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); - - // Align vertically. - y += fons__getVertAlign(stash, font, state->align, isize); - - minx = maxx = x; - miny = maxy = y; - startx = x; - - if (end == NULL) - end = str + strlen(str); - - for (; str != end; ++str) { - if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) - continue; - glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL); - if (glyph != NULL) { - fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); - if (q.x0 < minx) minx = q.x0; - if (q.x1 > maxx) maxx = q.x1; - if (stash->params.flags & FONS_ZERO_TOPLEFT) { - if (q.y0 < miny) miny = q.y0; - if (q.y1 > maxy) maxy = q.y1; - } else { - if (q.y1 < miny) miny = q.y1; - if (q.y0 > maxy) maxy = q.y0; - } - } - prevGlyphIndex = glyph != NULL ? glyph->index : -1; - } - - advance = x - startx; - - // Align horizontally - if (state->align & FONS_ALIGN_LEFT) { - // empty - } else if (state->align & FONS_ALIGN_RIGHT) { - minx -= advance; - maxx -= advance; - } else if (state->align & FONS_ALIGN_CENTER) { - minx -= advance * 0.5f; - maxx -= advance * 0.5f; - } - - if (bounds) { - bounds[0] = minx; - bounds[1] = miny; - bounds[2] = maxx; - bounds[3] = maxy; - } - - return advance; -} - -void fonsVertMetrics(FONScontext* stash, - float* ascender, float* descender, float* lineh) -{ - FONSfont* font; - FONSstate* state = fons__getState(stash); - short isize; - - if (stash == NULL) return; - if (state->font < 0 || state->font >= stash->nfonts) return; - font = stash->fonts[state->font]; - isize = (short)(state->size*10.0f); - if (font->data == NULL) return; - - if (ascender) - *ascender = font->ascender*isize/10.0f; - if (descender) - *descender = font->descender*isize/10.0f; - if (lineh) - *lineh = font->lineh*isize/10.0f; -} - -void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) -{ - FONSfont* font; - FONSstate* state = fons__getState(stash); - short isize; - - if (stash == NULL) return; - if (state->font < 0 || state->font >= stash->nfonts) return; - font = stash->fonts[state->font]; - isize = (short)(state->size*10.0f); - if (font->data == NULL) return; - - y += fons__getVertAlign(stash, font, state->align, isize); - - if (stash->params.flags & FONS_ZERO_TOPLEFT) { - *miny = y - font->ascender * (float)isize/10.0f; - *maxy = *miny + font->lineh*isize/10.0f; - } else { - *maxy = y + font->descender * (float)isize/10.0f; - *miny = *maxy - font->lineh*isize/10.0f; - } -} - -const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) -{ - if (width != NULL) - *width = stash->params.width; - if (height != NULL) - *height = stash->params.height; - return stash->texData; -} - -int fonsValidateTexture(FONScontext* stash, int* dirty) -{ - if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { - dirty[0] = stash->dirtyRect[0]; - dirty[1] = stash->dirtyRect[1]; - dirty[2] = stash->dirtyRect[2]; - dirty[3] = stash->dirtyRect[3]; - // Reset dirty rect - stash->dirtyRect[0] = stash->params.width; - stash->dirtyRect[1] = stash->params.height; - stash->dirtyRect[2] = 0; - stash->dirtyRect[3] = 0; - return 1; - } - return 0; -} - -void fonsDeleteInternal(FONScontext* stash) -{ - int i; - if (stash == NULL) return; - - if (stash->params.renderDelete) - stash->params.renderDelete(stash->params.userPtr); - - for (i = 0; i < stash->nfonts; ++i) - fons__freeFont(stash->fonts[i]); - - if (stash->atlas) fons__deleteAtlas(stash->atlas); - if (stash->fonts) free(stash->fonts); - if (stash->texData) free(stash->texData); - if (stash->scratch) free(stash->scratch); - fons__tt_done(stash); - free(stash); -} - -void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) -{ - if (stash == NULL) return; - stash->handleError = callback; - stash->errorUptr = uptr; -} - -void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) -{ - if (stash == NULL) return; - *width = stash->params.width; - *height = stash->params.height; -} - -int fonsExpandAtlas(FONScontext* stash, int width, int height) -{ - int i, maxy = 0; - unsigned char* data = NULL; - if (stash == NULL) return 0; - - width = fons__maxi(width, stash->params.width); - height = fons__maxi(height, stash->params.height); - - if (width == stash->params.width && height == stash->params.height) - return 1; - - // Flush pending glyphs. - fons__flush(stash); - - // Create new texture - if (stash->params.renderResize != NULL) { - if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) - return 0; - } - // Copy old texture data over. - data = (unsigned char*)malloc(width * height); - if (data == NULL) - return 0; - for (i = 0; i < stash->params.height; i++) { - unsigned char* dst = &data[i*width]; - unsigned char* src = &stash->texData[i*stash->params.width]; - memcpy(dst, src, stash->params.width); - if (width > stash->params.width) - memset(dst+stash->params.width, 0, width - stash->params.width); - } - if (height > stash->params.height) - memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); - - free(stash->texData); - stash->texData = data; - - // Increase atlas size - fons__atlasExpand(stash->atlas, width, height); - - // Add existing data as dirty. - for (i = 0; i < stash->atlas->nnodes; i++) - maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); - stash->dirtyRect[0] = 0; - stash->dirtyRect[1] = 0; - stash->dirtyRect[2] = stash->params.width; - stash->dirtyRect[3] = maxy; - - stash->params.width = width; - stash->params.height = height; - stash->itw = 1.0f/stash->params.width; - stash->ith = 1.0f/stash->params.height; - - return 1; -} - -int fonsResetAtlas(FONScontext* stash, int width, int height) -{ - int i, j; - if (stash == NULL) return 0; - - // Flush pending glyphs. - fons__flush(stash); - - // Create new texture - if (stash->params.renderResize != NULL) { - if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) - return 0; - } - - // Reset atlas - fons__atlasReset(stash->atlas, width, height); - - // Clear texture data. - stash->texData = (unsigned char*)realloc(stash->texData, width * height); - if (stash->texData == NULL) return 0; - memset(stash->texData, 0, width * height); - - // Reset dirty rect - stash->dirtyRect[0] = width; - stash->dirtyRect[1] = height; - stash->dirtyRect[2] = 0; - stash->dirtyRect[3] = 0; - - // Reset cached glyphs - for (i = 0; i < stash->nfonts; i++) { - FONSfont* font = stash->fonts[i]; - font->nglyphs = 0; - for (j = 0; j < FONS_HASH_LUT_SIZE; j++) - font->lut[j] = -1; - } - - stash->params.width = width; - stash->params.height = height; - stash->itw = 1.0f/stash->params.width; - stash->ith = 1.0f/stash->params.height; - - // Add white rect at 0,0 for debug drawing. - fons__addWhiteRect(stash, 2,2); - - return 1; -} - -#endif // FONTSTASH_IMPLEMENTATION diff --git a/nanovg/nanovg.c b/nanovg/nanovg.c deleted file mode 100644 index 4e110b3..0000000 --- a/nanovg/nanovg.c +++ /dev/null @@ -1,2958 +0,0 @@ -// -// Copyright (c) 2013 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#include <stdlib.h> -#include <stdio.h> -#include <math.h> -#include <memory.h> - -#include "nanovg.h" - -#define FONTSTASH_IMPLEMENTATION -#include "fontstash.h" - -#ifndef NVG_NO_STB -#define STB_IMAGE_IMPLEMENTATION -#include "../stb/stb_image.h" -#endif - -#ifdef _MSC_VER -#pragma warning(disable: 4100) // unreferenced formal parameter -#pragma warning(disable: 4127) // conditional expression is constant -#pragma warning(disable: 4204) // nonstandard extension used : non-constant aggregate initializer -#pragma warning(disable: 4706) // assignment within conditional expression -#endif - -#define NVG_INIT_FONTIMAGE_SIZE 512 -#define NVG_MAX_FONTIMAGE_SIZE 2048 -#define NVG_MAX_FONTIMAGES 4 - -#define NVG_INIT_COMMANDS_SIZE 256 -#define NVG_INIT_POINTS_SIZE 128 -#define NVG_INIT_PATHS_SIZE 16 -#define NVG_INIT_VERTS_SIZE 256 - -#ifndef NVG_MAX_STATES -#define NVG_MAX_STATES 64 -#endif - -#define NVG_KAPPA90 0.5522847493f // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NVG_COUNTOF(arr) (sizeof(arr) / sizeof(0[arr])) - - -enum NVGcommands { - NVG_MOVETO = 0, - NVG_LINETO = 1, - NVG_BEZIERTO = 2, - NVG_CLOSE = 3, - NVG_WINDING = 4, -}; - -enum NVGpointFlags -{ - NVG_PT_CORNER = 0x01, - NVG_PT_LEFT = 0x02, - NVG_PT_BEVEL = 0x04, - NVG_PR_INNERBEVEL = 0x08, -}; - -struct NVGstate { - NVGcompositeOperationState compositeOperation; - int shapeAntiAlias; - NVGpaint fill; - NVGpaint stroke; - float strokeWidth; - float miterLimit; - int lineJoin; - int lineCap; - float alpha; - float xform[6]; - NVGscissor scissor; - float fontSize; - float letterSpacing; - float lineHeight; - float fontBlur; - int textAlign; - int fontId; -}; -typedef struct NVGstate NVGstate; - -struct NVGpoint { - float x,y; - float dx, dy; - float len; - float dmx, dmy; - unsigned char flags; -}; -typedef struct NVGpoint NVGpoint; - -struct NVGpathCache { - NVGpoint* points; - int npoints; - int cpoints; - NVGpath* paths; - int npaths; - int cpaths; - NVGvertex* verts; - int nverts; - int cverts; - float bounds[4]; -}; -typedef struct NVGpathCache NVGpathCache; - -struct NVGcontext { - NVGparams params; - float* commands; - int ccommands; - int ncommands; - float commandx, commandy; - NVGstate states[NVG_MAX_STATES]; - int nstates; - NVGpathCache* cache; - float tessTol; - float distTol; - float fringeWidth; - float devicePxRatio; - struct FONScontext* fs; - int fontImages[NVG_MAX_FONTIMAGES]; - int fontImageIdx; - int drawCallCount; - int fillTriCount; - int strokeTriCount; - int textTriCount; -}; - -static float nvg__sqrtf(float a) { return sqrtf(a); } -static float nvg__modf(float a, float b) { return fmodf(a, b); } -static float nvg__sinf(float a) { return sinf(a); } -static float nvg__cosf(float a) { return cosf(a); } -static float nvg__tanf(float a) { return tanf(a); } -static float nvg__atan2f(float a,float b) { return atan2f(a, b); } -static float nvg__acosf(float a) { return acosf(a); } - -static int nvg__mini(int a, int b) { return a < b ? a : b; } -static int nvg__maxi(int a, int b) { return a > b ? a : b; } -static int nvg__clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); } -static float nvg__minf(float a, float b) { return a < b ? a : b; } -static float nvg__maxf(float a, float b) { return a > b ? a : b; } -static float nvg__absf(float a) { return a >= 0.0f ? a : -a; } -static float nvg__signf(float a) { return a >= 0.0f ? 1.0f : -1.0f; } -static float nvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } -static float nvg__cross(float dx0, float dy0, float dx1, float dy1) { return dx1*dy0 - dx0*dy1; } - -static float nvg__normalize(float *x, float* y) -{ - float d = nvg__sqrtf((*x)*(*x) + (*y)*(*y)); - if (d > 1e-6f) { - float id = 1.0f / d; - *x *= id; - *y *= id; - } - return d; -} - - -static void nvg__deletePathCache(NVGpathCache* c) -{ - if (c == NULL) return; - if (c->points != NULL) free(c->points); - if (c->paths != NULL) free(c->paths); - if (c->verts != NULL) free(c->verts); - free(c); -} - -static NVGpathCache* nvg__allocPathCache(void) -{ - NVGpathCache* c = (NVGpathCache*)malloc(sizeof(NVGpathCache)); - if (c == NULL) goto error; - memset(c, 0, sizeof(NVGpathCache)); - - c->points = (NVGpoint*)malloc(sizeof(NVGpoint)*NVG_INIT_POINTS_SIZE); - if (!c->points) goto error; - c->npoints = 0; - c->cpoints = NVG_INIT_POINTS_SIZE; - - c->paths = (NVGpath*)malloc(sizeof(NVGpath)*NVG_INIT_PATHS_SIZE); - if (!c->paths) goto error; - c->npaths = 0; - c->cpaths = NVG_INIT_PATHS_SIZE; - - c->verts = (NVGvertex*)malloc(sizeof(NVGvertex)*NVG_INIT_VERTS_SIZE); - if (!c->verts) goto error; - c->nverts = 0; - c->cverts = NVG_INIT_VERTS_SIZE; - - return c; -error: - nvg__deletePathCache(c); - return NULL; -} - -static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio) -{ - ctx->tessTol = 0.25f / ratio; - ctx->distTol = 0.01f / ratio; - ctx->fringeWidth = 1.0f / ratio; - ctx->devicePxRatio = ratio; -} - -static NVGcompositeOperationState nvg__compositeOperationState(int op) -{ - int sfactor, dfactor; - - if (op == NVG_SOURCE_OVER) - { - sfactor = NVG_ONE; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_SOURCE_IN) - { - sfactor = NVG_DST_ALPHA; - dfactor = NVG_ZERO; - } - else if (op == NVG_SOURCE_OUT) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ZERO; - } - else if (op == NVG_ATOP) - { - sfactor = NVG_DST_ALPHA; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_OVER) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ONE; - } - else if (op == NVG_DESTINATION_IN) - { - sfactor = NVG_ZERO; - dfactor = NVG_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_OUT) - { - sfactor = NVG_ZERO; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else if (op == NVG_DESTINATION_ATOP) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_SRC_ALPHA; - } - else if (op == NVG_LIGHTER) - { - sfactor = NVG_ONE; - dfactor = NVG_ONE; - } - else if (op == NVG_COPY) - { - sfactor = NVG_ONE; - dfactor = NVG_ZERO; - } - else if (op == NVG_XOR) - { - sfactor = NVG_ONE_MINUS_DST_ALPHA; - dfactor = NVG_ONE_MINUS_SRC_ALPHA; - } - else - { - sfactor = NVG_ONE; - dfactor = NVG_ZERO; - } - - NVGcompositeOperationState state; - state.srcRGB = sfactor; - state.dstRGB = dfactor; - state.srcAlpha = sfactor; - state.dstAlpha = dfactor; - return state; -} - -static NVGstate* nvg__getState(NVGcontext* ctx) -{ - return &ctx->states[ctx->nstates-1]; -} - -NVGcontext* nvgCreateInternal(NVGparams* params) -{ - FONSparams fontParams; - NVGcontext* ctx = (NVGcontext*)malloc(sizeof(NVGcontext)); - int i; - if (ctx == NULL) goto error; - memset(ctx, 0, sizeof(NVGcontext)); - - ctx->params = *params; - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) - ctx->fontImages[i] = 0; - - ctx->commands = (float*)malloc(sizeof(float)*NVG_INIT_COMMANDS_SIZE); - if (!ctx->commands) goto error; - ctx->ncommands = 0; - ctx->ccommands = NVG_INIT_COMMANDS_SIZE; - - ctx->cache = nvg__allocPathCache(); - if (ctx->cache == NULL) goto error; - - nvgSave(ctx); - nvgReset(ctx); - - nvg__setDevicePixelRatio(ctx, 1.0f); - - if (ctx->params.renderCreate(ctx->params.userPtr) == 0) goto error; - - // Init font rendering - memset(&fontParams, 0, sizeof(fontParams)); - fontParams.width = NVG_INIT_FONTIMAGE_SIZE; - fontParams.height = NVG_INIT_FONTIMAGE_SIZE; - fontParams.flags = FONS_ZERO_TOPLEFT; - fontParams.renderCreate = NULL; - fontParams.renderUpdate = NULL; - fontParams.renderDraw = NULL; - fontParams.renderDelete = NULL; - fontParams.userPtr = NULL; - ctx->fs = fonsCreateInternal(&fontParams); - if (ctx->fs == NULL) goto error; - - // Create font texture - ctx->fontImages[0] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, fontParams.width, fontParams.height, 0, NULL); - if (ctx->fontImages[0] == 0) goto error; - ctx->fontImageIdx = 0; - - return ctx; - -error: - nvgDeleteInternal(ctx); - return 0; -} - -NVGparams* nvgInternalParams(NVGcontext* ctx) -{ - return &ctx->params; -} - -void nvgDeleteInternal(NVGcontext* ctx) -{ - int i; - if (ctx == NULL) return; - if (ctx->commands != NULL) free(ctx->commands); - if (ctx->cache != NULL) nvg__deletePathCache(ctx->cache); - - if (ctx->fs) - fonsDeleteInternal(ctx->fs); - - for (i = 0; i < NVG_MAX_FONTIMAGES; i++) { - if (ctx->fontImages[i] != 0) { - nvgDeleteImage(ctx, ctx->fontImages[i]); - ctx->fontImages[i] = 0; - } - } - - if (ctx->params.renderDelete != NULL) - ctx->params.renderDelete(ctx->params.userPtr); - - free(ctx); -} - -void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio) -{ -/* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", - ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, - ctx->fillTriCount+ctx->strokeTriCount+ctx->textTriCount);*/ - - ctx->nstates = 0; - nvgSave(ctx); - nvgReset(ctx); - - nvg__setDevicePixelRatio(ctx, devicePixelRatio); - - ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio); - - ctx->drawCallCount = 0; - ctx->fillTriCount = 0; - ctx->strokeTriCount = 0; - ctx->textTriCount = 0; -} - -void nvgCancelFrame(NVGcontext* ctx) -{ - ctx->params.renderCancel(ctx->params.userPtr); -} - -void nvgEndFrame(NVGcontext* ctx) -{ - ctx->params.renderFlush(ctx->params.userPtr); - if (ctx->fontImageIdx != 0) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; - ctx->fontImages[ctx->fontImageIdx] = 0; - int i, j, iw, ih; - // delete images that smaller than current one - if (fontImage == 0) - return; - nvgImageSize(ctx, fontImage, &iw, &ih); - for (i = j = 0; i < ctx->fontImageIdx; i++) { - if (ctx->fontImages[i] != 0) { - int nw, nh; - int image = ctx->fontImages[i]; - ctx->fontImages[i] = 0; - nvgImageSize(ctx, image, &nw, &nh); - if (nw < iw || nh < ih) - nvgDeleteImage(ctx, image); - else - ctx->fontImages[j++] = image; - } - } - // make current font image to first - ctx->fontImages[j] = ctx->fontImages[0]; - ctx->fontImages[0] = fontImage; - ctx->fontImageIdx = 0; - } -} - -NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b) -{ - return nvgRGBA(r,g,b,255); -} - -NVGcolor nvgRGBf(float r, float g, float b) -{ - return nvgRGBAf(r,g,b,1.0f); -} - -NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - NVGcolor color; - // Use longer initialization to suppress warning. - color.r = r / 255.0f; - color.g = g / 255.0f; - color.b = b / 255.0f; - color.a = a / 255.0f; - return color; -} - -NVGcolor nvgRGBAf(float r, float g, float b, float a) -{ - NVGcolor color; - // Use longer initialization to suppress warning. - color.r = r; - color.g = g; - color.b = b; - color.a = a; - return color; -} - -NVGcolor nvgTransRGBA(NVGcolor c, unsigned char a) -{ - c.a = a / 255.0f; - return c; -} - -NVGcolor nvgTransRGBAf(NVGcolor c, float a) -{ - c.a = a; - return c; -} - -NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u) -{ - int i; - float oneminu; - NVGcolor cint = {{{0}}}; - - u = nvg__clampf(u, 0.0f, 1.0f); - oneminu = 1.0f - u; - for( i = 0; i <4; i++ ) - { - cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; - } - - return cint; -} - -NVGcolor nvgHSL(float h, float s, float l) -{ - return nvgHSLA(h,s,l,255); -} - -static float nvg__hue(float h, float m1, float m2) -{ - if (h < 0) h += 1; - if (h > 1) h -= 1; - if (h < 1.0f/6.0f) - return m1 + (m2 - m1) * h * 6.0f; - else if (h < 3.0f/6.0f) - return m2; - else if (h < 4.0f/6.0f) - return m1 + (m2 - m1) * (2.0f/3.0f - h) * 6.0f; - return m1; -} - -NVGcolor nvgHSLA(float h, float s, float l, unsigned char a) -{ - float m1, m2; - NVGcolor col; - h = nvg__modf(h, 1.0f); - if (h < 0.0f) h += 1.0f; - s = nvg__clampf(s, 0.0f, 1.0f); - l = nvg__clampf(l, 0.0f, 1.0f); - m2 = l <= 0.5f ? (l * (1 + s)) : (l + s - l * s); - m1 = 2 * l - m2; - col.r = nvg__clampf(nvg__hue(h + 1.0f/3.0f, m1, m2), 0.0f, 1.0f); - col.g = nvg__clampf(nvg__hue(h, m1, m2), 0.0f, 1.0f); - col.b = nvg__clampf(nvg__hue(h - 1.0f/3.0f, m1, m2), 0.0f, 1.0f); - col.a = a/255.0f; - return col; -} - -void nvgTransformIdentity(float* t) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformTranslate(float* t, float tx, float ty) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = tx; t[5] = ty; -} - -void nvgTransformScale(float* t, float sx, float sy) -{ - t[0] = sx; t[1] = 0.0f; - t[2] = 0.0f; t[3] = sy; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformRotate(float* t, float a) -{ - float cs = nvg__cosf(a), sn = nvg__sinf(a); - t[0] = cs; t[1] = sn; - t[2] = -sn; t[3] = cs; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformSkewX(float* t, float a) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = nvg__tanf(a); t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformSkewY(float* t, float a) -{ - t[0] = 1.0f; t[1] = nvg__tanf(a); - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -void nvgTransformMultiply(float* t, const float* s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -void nvgTransformPremultiply(float* t, const float* s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float)*6); - nvgTransformMultiply(s2, t); - memcpy(t, s2, sizeof(float)*6); -} - -int nvgTransformInverse(float* inv, const float* t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nvgTransformIdentity(inv); - return 0; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); - return 1; -} - -void nvgTransformPoint(float* dx, float* dy, const float* t, float sx, float sy) -{ - *dx = sx*t[0] + sy*t[2] + t[4]; - *dy = sx*t[1] + sy*t[3] + t[5]; -} - -float nvgDegToRad(float deg) -{ - return deg / 180.0f * NVG_PI; -} - -float nvgRadToDeg(float rad) -{ - return rad / NVG_PI * 180.0f; -} - -static void nvg__setPaintColor(NVGpaint* p, NVGcolor color) -{ - memset(p, 0, sizeof(*p)); - nvgTransformIdentity(p->xform); - p->radius = 0.0f; - p->feather = 1.0f; - p->innerColor = color; - p->outerColor = color; -} - - -// State handling -void nvgSave(NVGcontext* ctx) -{ - if (ctx->nstates >= NVG_MAX_STATES) - return; - if (ctx->nstates > 0) - memcpy(&ctx->states[ctx->nstates], &ctx->states[ctx->nstates-1], sizeof(NVGstate)); - ctx->nstates++; -} - -void nvgRestore(NVGcontext* ctx) -{ - if (ctx->nstates <= 1) - return; - ctx->nstates--; -} - -void nvgReset(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - memset(state, 0, sizeof(*state)); - - nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); - nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255)); - state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER); - state->shapeAntiAlias = 1; - state->strokeWidth = 1.0f; - state->miterLimit = 10.0f; - state->lineCap = NVG_BUTT; - state->lineJoin = NVG_MITER; - state->alpha = 1.0f; - nvgTransformIdentity(state->xform); - - state->scissor.extent[0] = -1.0f; - state->scissor.extent[1] = -1.0f; - - state->fontSize = 16.0f; - state->letterSpacing = 0.0f; - state->lineHeight = 1.0f; - state->fontBlur = 0.0f; - state->textAlign = NVG_ALIGN_LEFT | NVG_ALIGN_BASELINE; - state->fontId = 0; -} - -// State setting -void nvgShapeAntiAlias(NVGcontext* ctx, int enabled) -{ - NVGstate* state = nvg__getState(ctx); - state->shapeAntiAlias = enabled; -} - -void nvgStrokeWidth(NVGcontext* ctx, float width) -{ - NVGstate* state = nvg__getState(ctx); - state->strokeWidth = width; -} - -void nvgMiterLimit(NVGcontext* ctx, float limit) -{ - NVGstate* state = nvg__getState(ctx); - state->miterLimit = limit; -} - -void nvgLineCap(NVGcontext* ctx, int cap) -{ - NVGstate* state = nvg__getState(ctx); - state->lineCap = cap; -} - -void nvgLineJoin(NVGcontext* ctx, int join) -{ - NVGstate* state = nvg__getState(ctx); - state->lineJoin = join; -} - -void nvgGlobalAlpha(NVGcontext* ctx, float alpha) -{ - NVGstate* state = nvg__getState(ctx); - state->alpha = alpha; -} - -void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f) -{ - NVGstate* state = nvg__getState(ctx); - float t[6] = { a, b, c, d, e, f }; - nvgTransformPremultiply(state->xform, t); -} - -void nvgResetTransform(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - nvgTransformIdentity(state->xform); -} - -void nvgTranslate(NVGcontext* ctx, float x, float y) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformTranslate(t, x,y); - nvgTransformPremultiply(state->xform, t); -} - -void nvgRotate(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformRotate(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgSkewX(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformSkewX(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgSkewY(NVGcontext* ctx, float angle) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformSkewY(t, angle); - nvgTransformPremultiply(state->xform, t); -} - -void nvgScale(NVGcontext* ctx, float x, float y) -{ - NVGstate* state = nvg__getState(ctx); - float t[6]; - nvgTransformScale(t, x,y); - nvgTransformPremultiply(state->xform, t); -} - -void nvgCurrentTransform(NVGcontext* ctx, float* xform) -{ - NVGstate* state = nvg__getState(ctx); - if (xform == NULL) return; - memcpy(xform, state->xform, sizeof(float)*6); -} - -void nvgStrokeColor(NVGcontext* ctx, NVGcolor color) -{ - NVGstate* state = nvg__getState(ctx); - nvg__setPaintColor(&state->stroke, color); -} - -void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint) -{ - NVGstate* state = nvg__getState(ctx); - state->stroke = paint; - nvgTransformMultiply(state->stroke.xform, state->xform); -} - -void nvgFillColor(NVGcontext* ctx, NVGcolor color) -{ - NVGstate* state = nvg__getState(ctx); - nvg__setPaintColor(&state->fill, color); -} - -void nvgFillPaint(NVGcontext* ctx, NVGpaint paint) -{ - NVGstate* state = nvg__getState(ctx); - state->fill = paint; - nvgTransformMultiply(state->fill.xform, state->xform); -} - -#ifndef NVG_NO_STB -int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags) -{ - int w, h, n, image; - unsigned char* img; - stbi_set_unpremultiply_on_load(1); - stbi_convert_iphone_png_to_rgb(1); - img = stbi_load(filename, &w, &h, &n, 4); - if (img == NULL) { -// printf("Failed to load %s - %s\n", filename, stbi_failure_reason()); - return 0; - } - image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img); - stbi_image_free(img); - return image; -} - -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata) -{ - int w, h, n, image; - unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); - if (img == NULL) { -// printf("Failed to load %s - %s\n", filename, stbi_failure_reason()); - return 0; - } - image = nvgCreateImageRGBA(ctx, w, h, imageFlags, img); - stbi_image_free(img); - return image; -} -#endif - -int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) -{ - return ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_RGBA, w, h, imageFlags, data); -} - -int nvgCreateImageAlpha(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data) -{ - return ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, w, h, imageFlags, data); -} - -void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data) -{ - int w, h; - ctx->params.renderGetTextureSize(ctx->params.userPtr, image, &w, &h); - ctx->params.renderUpdateTexture(ctx->params.userPtr, image, 0,0, w,h, data); -} - -void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h) -{ - ctx->params.renderGetTextureSize(ctx->params.userPtr, image, w, h); -} - -void nvgDeleteImage(NVGcontext* ctx, int image) -{ - ctx->params.renderDeleteTexture(ctx->params.userPtr, image); -} - -NVGpaint nvgLinearGradient(NVGcontext* ctx, - float sx, float sy, float ex, float ey, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - float dx, dy, d; - const float large = 1e5; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - // Calculate transform aligned to the line - dx = ex - sx; - dy = ey - sy; - d = sqrtf(dx*dx + dy*dy); - if (d > 0.0001f) { - dx /= d; - dy /= d; - } else { - dx = 0; - dy = 1; - } - - p.xform[0] = dy; p.xform[1] = -dx; - p.xform[2] = dx; p.xform[3] = dy; - p.xform[4] = sx - dx*large; p.xform[5] = sy - dy*large; - - p.extent[0] = large; - p.extent[1] = large + d*0.5f; - - p.radius = 0.0f; - - p.feather = nvg__maxf(1.0f, d); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - -NVGpaint nvgRadialGradient(NVGcontext* ctx, - float cx, float cy, float inr, float outr, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - float r = (inr+outr)*0.5f; - float f = (outr-inr); - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformIdentity(p.xform); - p.xform[4] = cx; - p.xform[5] = cy; - - p.extent[0] = r; - p.extent[1] = r; - - p.radius = r; - - p.feather = nvg__maxf(1.0f, f); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - -NVGpaint nvgBoxGradient(NVGcontext* ctx, - float x, float y, float w, float h, float r, float f, - NVGcolor icol, NVGcolor ocol) -{ - NVGpaint p; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformIdentity(p.xform); - p.xform[4] = x+w*0.5f; - p.xform[5] = y+h*0.5f; - - p.extent[0] = w*0.5f; - p.extent[1] = h*0.5f; - - p.radius = r; - - p.feather = nvg__maxf(1.0f, f); - - p.innerColor = icol; - p.outerColor = ocol; - - return p; -} - - -NVGpaint nvgImagePattern(NVGcontext* ctx, - float cx, float cy, float w, float h, float angle, - int image, float alpha) -{ - NVGpaint p; - NVG_NOTUSED(ctx); - memset(&p, 0, sizeof(p)); - - nvgTransformRotate(p.xform, angle); - p.xform[4] = cx; - p.xform[5] = cy; - - p.extent[0] = w; - p.extent[1] = h; - - p.image = image; - - p.innerColor = p.outerColor = nvgRGBAf(1,1,1,alpha); - - return p; -} - -// Scissoring -void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h) -{ - NVGstate* state = nvg__getState(ctx); - - w = nvg__maxf(0.0f, w); - h = nvg__maxf(0.0f, h); - - nvgTransformIdentity(state->scissor.xform); - state->scissor.xform[4] = x+w*0.5f; - state->scissor.xform[5] = y+h*0.5f; - nvgTransformMultiply(state->scissor.xform, state->xform); - - state->scissor.extent[0] = w*0.5f; - state->scissor.extent[1] = h*0.5f; -} - -static void nvg__isectRects(float* dst, - float ax, float ay, float aw, float ah, - float bx, float by, float bw, float bh) -{ - float minx = nvg__maxf(ax, bx); - float miny = nvg__maxf(ay, by); - float maxx = nvg__minf(ax+aw, bx+bw); - float maxy = nvg__minf(ay+ah, by+bh); - dst[0] = minx; - dst[1] = miny; - dst[2] = nvg__maxf(0.0f, maxx - minx); - dst[3] = nvg__maxf(0.0f, maxy - miny); -} - -void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h) -{ - NVGstate* state = nvg__getState(ctx); - float pxform[6], invxorm[6]; - float rect[4]; - float ex, ey, tex, tey; - - // If no previous scissor has been set, set the scissor as current scissor. - if (state->scissor.extent[0] < 0) { - nvgScissor(ctx, x, y, w, h); - return; - } - - // Transform the current scissor rect into current transform space. - // If there is difference in rotation, this will be approximation. - memcpy(pxform, state->scissor.xform, sizeof(float)*6); - ex = state->scissor.extent[0]; - ey = state->scissor.extent[1]; - nvgTransformInverse(invxorm, state->xform); - nvgTransformMultiply(pxform, invxorm); - tex = ex*nvg__absf(pxform[0]) + ey*nvg__absf(pxform[2]); - tey = ex*nvg__absf(pxform[1]) + ey*nvg__absf(pxform[3]); - - // Intersect rects. - nvg__isectRects(rect, pxform[4]-tex,pxform[5]-tey,tex*2,tey*2, x,y,w,h); - - nvgScissor(ctx, rect[0], rect[1], rect[2], rect[3]); -} - -void nvgResetScissor(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - memset(state->scissor.xform, 0, sizeof(state->scissor.xform)); - state->scissor.extent[0] = -1.0f; - state->scissor.extent[1] = -1.0f; -} - -// Global composite operation. -void nvgGlobalCompositeOperation(NVGcontext* ctx, int op) -{ - NVGstate* state = nvg__getState(ctx); - state->compositeOperation = nvg__compositeOperationState(op); -} - -void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor) -{ - nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor); -} - -void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) -{ - NVGcompositeOperationState op; - op.srcRGB = srcRGB; - op.dstRGB = dstRGB; - op.srcAlpha = srcAlpha; - op.dstAlpha = dstAlpha; - - NVGstate* state = nvg__getState(ctx); - state->compositeOperation = op; -} - -static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) -{ - float dx = x2 - x1; - float dy = y2 - y1; - return dx*dx + dy*dy < tol*tol; -} - -static float nvg__distPtSeg(float x, float y, float px, float py, float qx, float qy) -{ - float pqx, pqy, dx, dy, d, t; - pqx = qx-px; - pqy = qy-py; - dx = x-px; - dy = y-py; - d = pqx*pqx + pqy*pqy; - t = pqx*dx + pqy*dy; - if (d > 0) t /= d; - if (t < 0) t = 0; - else if (t > 1) t = 1; - dx = px + t*pqx - x; - dy = py + t*pqy - y; - return dx*dx + dy*dy; -} - -static void nvg__appendCommands(NVGcontext* ctx, float* vals, int nvals) -{ - NVGstate* state = nvg__getState(ctx); - int i; - - if (ctx->ncommands+nvals > ctx->ccommands) { - float* commands; - int ccommands = ctx->ncommands+nvals + ctx->ccommands/2; - commands = (float*)realloc(ctx->commands, sizeof(float)*ccommands); - if (commands == NULL) return; - ctx->commands = commands; - ctx->ccommands = ccommands; - } - - if ((int)vals[0] != NVG_CLOSE && (int)vals[0] != NVG_WINDING) { - ctx->commandx = vals[nvals-2]; - ctx->commandy = vals[nvals-1]; - } - - // transform commands - i = 0; - while (i < nvals) { - int cmd = (int)vals[i]; - switch (cmd) { - case NVG_MOVETO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - i += 3; - break; - case NVG_LINETO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - i += 3; - break; - case NVG_BEZIERTO: - nvgTransformPoint(&vals[i+1],&vals[i+2], state->xform, vals[i+1],vals[i+2]); - nvgTransformPoint(&vals[i+3],&vals[i+4], state->xform, vals[i+3],vals[i+4]); - nvgTransformPoint(&vals[i+5],&vals[i+6], state->xform, vals[i+5],vals[i+6]); - i += 7; - break; - case NVG_CLOSE: - i++; - break; - case NVG_WINDING: - i += 2; - break; - default: - i++; - } - } - - memcpy(&ctx->commands[ctx->ncommands], vals, nvals*sizeof(float)); - - ctx->ncommands += nvals; -} - - -static void nvg__clearPathCache(NVGcontext* ctx) -{ - ctx->cache->npoints = 0; - ctx->cache->npaths = 0; -} - -static NVGpath* nvg__lastPath(NVGcontext* ctx) -{ - if (ctx->cache->npaths > 0) - return &ctx->cache->paths[ctx->cache->npaths-1]; - return NULL; -} - -static void nvg__addPath(NVGcontext* ctx) -{ - NVGpath* path; - if (ctx->cache->npaths+1 > ctx->cache->cpaths) { - NVGpath* paths; - int cpaths = ctx->cache->npaths+1 + ctx->cache->cpaths/2; - paths = (NVGpath*)realloc(ctx->cache->paths, sizeof(NVGpath)*cpaths); - if (paths == NULL) return; - ctx->cache->paths = paths; - ctx->cache->cpaths = cpaths; - } - path = &ctx->cache->paths[ctx->cache->npaths]; - memset(path, 0, sizeof(*path)); - path->first = ctx->cache->npoints; - path->winding = NVG_CCW; - - ctx->cache->npaths++; -} - -static NVGpoint* nvg__lastPoint(NVGcontext* ctx) -{ - if (ctx->cache->npoints > 0) - return &ctx->cache->points[ctx->cache->npoints-1]; - return NULL; -} - -static void nvg__addPoint(NVGcontext* ctx, float x, float y, int flags) -{ - NVGpath* path = nvg__lastPath(ctx); - NVGpoint* pt; - if (path == NULL) return; - - if (path->count > 0 && ctx->cache->npoints > 0) { - pt = nvg__lastPoint(ctx); - if (nvg__ptEquals(pt->x,pt->y, x,y, ctx->distTol)) { - pt->flags |= flags; - return; - } - } - - if (ctx->cache->npoints+1 > ctx->cache->cpoints) { - NVGpoint* points; - int cpoints = ctx->cache->npoints+1 + ctx->cache->cpoints/2; - points = (NVGpoint*)realloc(ctx->cache->points, sizeof(NVGpoint)*cpoints); - if (points == NULL) return; - ctx->cache->points = points; - ctx->cache->cpoints = cpoints; - } - - pt = &ctx->cache->points[ctx->cache->npoints]; - memset(pt, 0, sizeof(*pt)); - pt->x = x; - pt->y = y; - pt->flags = (unsigned char)flags; - - ctx->cache->npoints++; - path->count++; -} - -static void nvg__closePath(NVGcontext* ctx) -{ - NVGpath* path = nvg__lastPath(ctx); - if (path == NULL) return; - path->closed = 1; -} - -static void nvg__pathWinding(NVGcontext* ctx, int winding) -{ - NVGpath* path = nvg__lastPath(ctx); - if (path == NULL) return; - path->winding = winding; -} - -static float nvg__getAverageScale(float *t) -{ - float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); - float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); - return (sx + sy) * 0.5f; -} - -static NVGvertex* nvg__allocTempVerts(NVGcontext* ctx, int nverts) -{ - if (nverts > ctx->cache->cverts) { - NVGvertex* verts; - int cverts = (nverts + 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly. - verts = (NVGvertex*)realloc(ctx->cache->verts, sizeof(NVGvertex)*cverts); - if (verts == NULL) return NULL; - ctx->cache->verts = verts; - ctx->cache->cverts = cverts; - } - - return ctx->cache->verts; -} - -static float nvg__triarea2(float ax, float ay, float bx, float by, float cx, float cy) -{ - float abx = bx - ax; - float aby = by - ay; - float acx = cx - ax; - float acy = cy - ay; - return acx*aby - abx*acy; -} - -static float nvg__polyArea(NVGpoint* pts, int npts) -{ - int i; - float area = 0; - for (i = 2; i < npts; i++) { - NVGpoint* a = &pts[0]; - NVGpoint* b = &pts[i-1]; - NVGpoint* c = &pts[i]; - area += nvg__triarea2(a->x,a->y, b->x,b->y, c->x,c->y); - } - return area * 0.5f; -} - -static void nvg__polyReverse(NVGpoint* pts, int npts) -{ - NVGpoint tmp; - int i = 0, j = npts-1; - while (i < j) { - tmp = pts[i]; - pts[i] = pts[j]; - pts[j] = tmp; - i++; - j--; - } -} - - -static void nvg__vset(NVGvertex* vtx, float x, float y, float u, float v) -{ - vtx->x = x; - vtx->y = y; - vtx->u = u; - vtx->v = v; -} - -static void nvg__tesselateBezier(NVGcontext* ctx, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, - int level, int type) -{ - float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; - float dx,dy,d2,d3; - - if (level > 10) return; - - x12 = (x1+x2)*0.5f; - y12 = (y1+y2)*0.5f; - x23 = (x2+x3)*0.5f; - y23 = (y2+y3)*0.5f; - x34 = (x3+x4)*0.5f; - y34 = (y3+y4)*0.5f; - x123 = (x12+x23)*0.5f; - y123 = (y12+y23)*0.5f; - - dx = x4 - x1; - dy = y4 - y1; - d2 = nvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); - d3 = nvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); - - if ((d2 + d3)*(d2 + d3) < ctx->tessTol * (dx*dx + dy*dy)) { - nvg__addPoint(ctx, x4, y4, type); - return; - } - -/* 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) { - nvg__addPoint(ctx, x4, y4, type); - return; - }*/ - - x234 = (x23+x34)*0.5f; - y234 = (y23+y34)*0.5f; - x1234 = (x123+x234)*0.5f; - y1234 = (y123+y234)*0.5f; - - nvg__tesselateBezier(ctx, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); - nvg__tesselateBezier(ctx, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); -} - -static void nvg__flattenPaths(NVGcontext* ctx) -{ - NVGpathCache* cache = ctx->cache; -// NVGstate* state = nvg__getState(ctx); - NVGpoint* last; - NVGpoint* p0; - NVGpoint* p1; - NVGpoint* pts; - NVGpath* path; - int i, j; - float* cp1; - float* cp2; - float* p; - float area; - - if (cache->npaths > 0) - return; - - // Flatten - i = 0; - while (i < ctx->ncommands) { - int cmd = (int)ctx->commands[i]; - switch (cmd) { - case NVG_MOVETO: - nvg__addPath(ctx); - p = &ctx->commands[i+1]; - nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER); - i += 3; - break; - case NVG_LINETO: - p = &ctx->commands[i+1]; - nvg__addPoint(ctx, p[0], p[1], NVG_PT_CORNER); - i += 3; - break; - case NVG_BEZIERTO: - last = nvg__lastPoint(ctx); - if (last != NULL) { - cp1 = &ctx->commands[i+1]; - cp2 = &ctx->commands[i+3]; - p = &ctx->commands[i+5]; - nvg__tesselateBezier(ctx, last->x,last->y, cp1[0],cp1[1], cp2[0],cp2[1], p[0],p[1], 0, NVG_PT_CORNER); - } - i += 7; - break; - case NVG_CLOSE: - nvg__closePath(ctx); - i++; - break; - case NVG_WINDING: - nvg__pathWinding(ctx, (int)ctx->commands[i+1]); - i += 2; - break; - default: - i++; - } - } - - cache->bounds[0] = cache->bounds[1] = 1e6f; - cache->bounds[2] = cache->bounds[3] = -1e6f; - - // Calculate the direction and length of line segments. - for (j = 0; j < cache->npaths; j++) { - path = &cache->paths[j]; - pts = &cache->points[path->first]; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &pts[path->count-1]; - p1 = &pts[0]; - if (nvg__ptEquals(p0->x,p0->y, p1->x,p1->y, ctx->distTol)) { - path->count--; - p0 = &pts[path->count-1]; - path->closed = 1; - } - - // Enforce winding. - if (path->count > 2) { - area = nvg__polyArea(pts, path->count); - if (path->winding == NVG_CCW && area < 0.0f) - nvg__polyReverse(pts, path->count); - if (path->winding == NVG_CW && area > 0.0f) - nvg__polyReverse(pts, path->count); - } - - for(i = 0; i < path->count; i++) { - // Calculate segment direction and length - p0->dx = p1->x - p0->x; - p0->dy = p1->y - p0->y; - p0->len = nvg__normalize(&p0->dx, &p0->dy); - // Update bounds - cache->bounds[0] = nvg__minf(cache->bounds[0], p0->x); - cache->bounds[1] = nvg__minf(cache->bounds[1], p0->y); - cache->bounds[2] = nvg__maxf(cache->bounds[2], p0->x); - cache->bounds[3] = nvg__maxf(cache->bounds[3], p0->y); - // Advance - p0 = p1++; - } - } -} - -static int nvg__curveDivs(float r, float arc, float tol) -{ - float da = acosf(r / (r + tol)) * 2.0f; - return nvg__maxi(2, (int)ceilf(arc / da)); -} - -static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w, - float* x0, float* y0, float* x1, float* y1) -{ - if (bevel) { - *x0 = p1->x + p0->dy * w; - *y0 = p1->y - p0->dx * w; - *x1 = p1->x + p1->dy * w; - *y1 = p1->y - p1->dx * w; - } else { - *x0 = p1->x + p1->dmx * w; - *y0 = p1->y + p1->dmy * w; - *x1 = p1->x + p1->dmx * w; - *y1 = p1->y + p1->dmy * w; - } -} - -static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, - float lw, float rw, float lu, float ru, int ncap, - float fringe) -{ - int i, n; - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - NVG_NOTUSED(fringe); - - if (p1->flags & NVG_PT_LEFT) { - float lx0,ly0,lx1,ly1,a0,a1; - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1); - a0 = atan2f(-dly0, -dlx0); - a1 = atan2f(-dly1, -dlx1); - if (a1 > a0) a1 -= NVG_PI*2; - - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; - - n = nvg__clampi((int)ceilf(((a0 - a1) / NVG_PI) * ncap), 2, ncap); - for (i = 0; i < n; i++) { - float u = i/(float)(n-1); - float a = a0 + u*(a1-a0); - float rx = p1->x + cosf(a) * rw; - float ry = p1->y + sinf(a) * rw; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, rx, ry, ru,1); dst++; - } - - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; - - } else { - float rx0,ry0,rx1,ry1,a0,a1; - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1); - a0 = atan2f(dly0, dlx0); - a1 = atan2f(dly1, dlx1); - if (a1 < a0) a1 += NVG_PI*2; - - nvg__vset(dst, p1->x + dlx0*rw, p1->y + dly0*rw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; - - n = nvg__clampi((int)ceilf(((a1 - a0) / NVG_PI) * ncap), 2, ncap); - for (i = 0; i < n; i++) { - float u = i/(float)(n-1); - float a = a0 + u*(a1-a0); - float lx = p1->x + cosf(a) * lw; - float ly = p1->y + sinf(a) * lw; - nvg__vset(dst, lx, ly, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - } - - nvg__vset(dst, p1->x + dlx1*rw, p1->y + dly1*rw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; - - } - return dst; -} - -static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, - float lw, float rw, float lu, float ru, float fringe) -{ - float rx0,ry0,rx1,ry1; - float lx0,ly0,lx1,ly1; - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - NVG_NOTUSED(fringe); - - if (p1->flags & NVG_PT_LEFT) { - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, lw, &lx0,&ly0, &lx1,&ly1); - - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; - - if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; - - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; - } else { - rx0 = p1->x - p1->dmx * rw; - ry0 = p1->y - p1->dmy * rw; - - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, p1->x - dlx0*rw, p1->y - dly0*rw, ru,1); dst++; - - nvg__vset(dst, rx0, ry0, ru,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; - - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; - } - - nvg__vset(dst, lx1, ly1, lu,1); dst++; - nvg__vset(dst, p1->x - dlx1*rw, p1->y - dly1*rw, ru,1); dst++; - - } else { - nvg__chooseBevel(p1->flags & NVG_PR_INNERBEVEL, p0, p1, -rw, &rx0,&ry0, &rx1,&ry1); - - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; - - if (p1->flags & NVG_PT_BEVEL) { - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, rx0, ry0, ru,1); dst++; - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; - } else { - lx0 = p1->x + p1->dmx * lw; - ly0 = p1->y + p1->dmy * lw; - - nvg__vset(dst, p1->x + dlx0*lw, p1->y + dly0*lw, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - - nvg__vset(dst, lx0, ly0, lu,1); dst++; - nvg__vset(dst, lx0, ly0, lu,1); dst++; - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, p1->x, p1->y, 0.5f,1); dst++; - } - - nvg__vset(dst, p1->x + dlx1*lw, p1->y + dly1*lw, lu,1); dst++; - nvg__vset(dst, rx1, ry1, ru,1); dst++; - } - - return dst; -} - -static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, float d, - float aa, float u0, float u1) -{ - float px = p->x - dx*d; - float py = p->y - dy*d; - float dlx = dy; - float dly = -dx; - nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, u0,0); dst++; - nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, u1,0); dst++; - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; - return dst; -} - -static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, float d, - float aa, float u0, float u1) -{ - float px = p->x + dx*d; - float py = p->y + dy*d; - float dlx = dy; - float dly = -dx; - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; - nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, u0,0); dst++; - nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, u1,0); dst++; - return dst; -} - - -static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, int ncap, - float aa, float u0, float u1) -{ - int i; - float px = p->x; - float py = p->y; - float dlx = dy; - float dly = -dx; - NVG_NOTUSED(aa); - for (i = 0; i < ncap; i++) { - float a = i/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, u0,1); dst++; - nvg__vset(dst, px, py, 0.5f,1); dst++; - } - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; - return dst; -} - -static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, - float dx, float dy, float w, int ncap, - float aa, float u0, float u1) -{ - int i; - float px = p->x; - float py = p->y; - float dlx = dy; - float dly = -dx; - NVG_NOTUSED(aa); - nvg__vset(dst, px + dlx*w, py + dly*w, u0,1); dst++; - nvg__vset(dst, px - dlx*w, py - dly*w, u1,1); dst++; - for (i = 0; i < ncap; i++) { - float a = i/(float)(ncap-1)*NVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - nvg__vset(dst, px, py, 0.5f,1); dst++; - nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++; - } - return dst; -} - - -static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - int i, j; - float iw = 0.0f; - - if (w > 0.0f) iw = 1.0f / w; - - // Calculate which joins needs extra vertices to append, and gather vertex count. - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0 = &pts[path->count-1]; - NVGpoint* p1 = &pts[0]; - int nleft = 0; - - path->nbevel = 0; - - for (j = 0; j < path->count; j++) { - float dlx0, dly0, dlx1, dly1, dmr2, cross, limit; - dlx0 = p0->dy; - dly0 = -p0->dx; - dlx1 = p1->dy; - dly1 = -p1->dx; - // Calculate extrusions - p1->dmx = (dlx0 + dlx1) * 0.5f; - p1->dmy = (dly0 + dly1) * 0.5f; - dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; - if (dmr2 > 0.000001f) { - float scale = 1.0f / dmr2; - if (scale > 600.0f) { - scale = 600.0f; - } - p1->dmx *= scale; - p1->dmy *= scale; - } - - // Clear flags, but keep the corner. - p1->flags = (p1->flags & NVG_PT_CORNER) ? NVG_PT_CORNER : 0; - - // Keep track of left turns. - cross = p1->dx * p0->dy - p0->dx * p1->dy; - if (cross > 0.0f) { - nleft++; - p1->flags |= NVG_PT_LEFT; - } - - // Calculate if we should use bevel or miter for inner join. - limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw); - if ((dmr2 * limit*limit) < 1.0f) - p1->flags |= NVG_PR_INNERBEVEL; - - // Check to see if the corner needs to be beveled. - if (p1->flags & NVG_PT_CORNER) { - if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) { - p1->flags |= NVG_PT_BEVEL; - } - } - - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) - path->nbevel++; - - p0 = p1++; - } - - path->convex = (nleft == path->count) ? 1 : 0; - } -} - - -static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - NVGvertex* verts; - NVGvertex* dst; - int cverts, i, j; - float aa = fringe;//ctx->fringeWidth; - float u0 = 0.0f, u1 = 1.0f; - int ncap = nvg__curveDivs(w, NVG_PI, ctx->tessTol); // Calculate divisions per half circle. - - w += aa * 0.5f; - - // Disable the gradient used for antialiasing when antialiasing is not used. - if (aa == 0.0f) { - u0 = 0.5f; - u1 = 0.5f; - } - - nvg__calculateJoins(ctx, w, lineJoin, miterLimit); - - // Calculate max vertex usage. - cverts = 0; - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - int loop = (path->closed == 0) ? 0 : 1; - if (lineJoin == NVG_ROUND) - cverts += (path->count + path->nbevel*(ncap+2) + 1) * 2; // plus one for loop - else - cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop - if (loop == 0) { - // space for caps - if (lineCap == NVG_ROUND) { - cverts += (ncap*2 + 2)*2; - } else { - cverts += (3+3)*2; - } - } - } - - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return 0; - - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0; - NVGpoint* p1; - int s, e, loop; - float dx, dy; - - path->fill = 0; - path->nfill = 0; - - // Calculate fringe or stroke - loop = (path->closed == 0) ? 0 : 1; - dst = verts; - path->stroke = dst; - - if (loop) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - s = 0; - e = path->count; - } else { - // Add cap - p0 = &pts[0]; - p1 = &pts[1]; - s = 1; - e = path->count-1; - } - - if (loop == 0) { - // Add cap - dx = p1->x - p0->x; - dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); - if (lineCap == NVG_BUTT) - dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1); - else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1); - else if (lineCap == NVG_ROUND) - dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1); - } - - for (j = s; j < e; ++j) { - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - if (lineJoin == NVG_ROUND) { - dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa); - } else { - dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa); - } - } else { - nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), u0,1); dst++; - nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), u1,1); dst++; - } - p0 = p1++; - } - - if (loop) { - // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, u0,1); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, u1,1); dst++; - } else { - // Add cap - dx = p1->x - p0->x; - dy = p1->y - p0->y; - nvg__normalize(&dx, &dy); - if (lineCap == NVG_BUTT) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1); - else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) - dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1); - else if (lineCap == NVG_ROUND) - dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1); - } - - path->nstroke = (int)(dst - verts); - - verts = dst; - } - - return 1; -} - -static int nvg__expandFill(NVGcontext* ctx, float w, int lineJoin, float miterLimit) -{ - NVGpathCache* cache = ctx->cache; - NVGvertex* verts; - NVGvertex* dst; - int cverts, convex, i, j; - float aa = ctx->fringeWidth; - int fringe = w > 0.0f; - - nvg__calculateJoins(ctx, w, lineJoin, miterLimit); - - // Calculate max vertex usage. - cverts = 0; - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - cverts += path->count + path->nbevel + 1; - if (fringe) - cverts += (path->count + path->nbevel*5 + 1) * 2; // plus one for loop - } - - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return 0; - - convex = cache->npaths == 1 && cache->paths[0].convex; - - for (i = 0; i < cache->npaths; i++) { - NVGpath* path = &cache->paths[i]; - NVGpoint* pts = &cache->points[path->first]; - NVGpoint* p0; - NVGpoint* p1; - float rw, lw, woff; - float ru, lu; - - // Calculate shape vertices. - woff = 0.5f*aa; - dst = verts; - path->fill = dst; - - if (fringe) { - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - for (j = 0; j < path->count; ++j) { - if (p1->flags & NVG_PT_BEVEL) { - float dlx0 = p0->dy; - float dly0 = -p0->dx; - float dlx1 = p1->dy; - float dly1 = -p1->dx; - if (p1->flags & NVG_PT_LEFT) { - float lx = p1->x + p1->dmx * woff; - float ly = p1->y + p1->dmy * woff; - nvg__vset(dst, lx, ly, 0.5f,1); dst++; - } else { - float lx0 = p1->x + dlx0 * woff; - float ly0 = p1->y + dly0 * woff; - float lx1 = p1->x + dlx1 * woff; - float ly1 = p1->y + dly1 * woff; - nvg__vset(dst, lx0, ly0, 0.5f,1); dst++; - nvg__vset(dst, lx1, ly1, 0.5f,1); dst++; - } - } else { - nvg__vset(dst, p1->x + (p1->dmx * woff), p1->y + (p1->dmy * woff), 0.5f,1); dst++; - } - p0 = p1++; - } - } else { - for (j = 0; j < path->count; ++j) { - nvg__vset(dst, pts[j].x, pts[j].y, 0.5f,1); - dst++; - } - } - - path->nfill = (int)(dst - verts); - verts = dst; - - // Calculate fringe - if (fringe) { - lw = w + woff; - rw = w - woff; - lu = 0; - ru = 1; - dst = verts; - path->stroke = dst; - - // Create only half a fringe for convex shapes so that - // the shape can be rendered without stenciling. - if (convex) { - lw = woff; // This should generate the same vertex as fill inset above. - lu = 0.5f; // Set outline fade at middle. - } - - // Looping - p0 = &pts[path->count-1]; - p1 = &pts[0]; - - for (j = 0; j < path->count; ++j) { - if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { - dst = nvg__bevelJoin(dst, p0, p1, lw, rw, lu, ru, ctx->fringeWidth); - } else { - nvg__vset(dst, p1->x + (p1->dmx * lw), p1->y + (p1->dmy * lw), lu,1); dst++; - nvg__vset(dst, p1->x - (p1->dmx * rw), p1->y - (p1->dmy * rw), ru,1); dst++; - } - p0 = p1++; - } - - // Loop it - nvg__vset(dst, verts[0].x, verts[0].y, lu,1); dst++; - nvg__vset(dst, verts[1].x, verts[1].y, ru,1); dst++; - - path->nstroke = (int)(dst - verts); - verts = dst; - } else { - path->stroke = NULL; - path->nstroke = 0; - } - } - - return 1; -} - - -// Draw -void nvgBeginPath(NVGcontext* ctx) -{ - ctx->ncommands = 0; - nvg__clearPathCache(ctx); -} - -void nvgMoveTo(NVGcontext* ctx, float x, float y) -{ - float vals[] = { NVG_MOVETO, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgLineTo(NVGcontext* ctx, float x, float y) -{ - float vals[] = { NVG_LINETO, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y) -{ - float vals[] = { NVG_BEZIERTO, c1x, c1y, c2x, c2y, x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y) -{ - float x0 = ctx->commandx; - float y0 = ctx->commandy; - float vals[] = { NVG_BEZIERTO, - x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), - x + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y), - x, y }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius) -{ - float x0 = ctx->commandx; - float y0 = ctx->commandy; - float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1; - int dir; - - if (ctx->ncommands == 0) { - return; - } - - // Handle degenerate cases. - if (nvg__ptEquals(x0,y0, x1,y1, ctx->distTol) || - nvg__ptEquals(x1,y1, x2,y2, ctx->distTol) || - nvg__distPtSeg(x1,y1, x0,y0, x2,y2) < ctx->distTol*ctx->distTol || - radius < ctx->distTol) { - nvgLineTo(ctx, x1,y1); - return; - } - - // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2). - dx0 = x0-x1; - dy0 = y0-y1; - dx1 = x2-x1; - dy1 = y2-y1; - nvg__normalize(&dx0,&dy0); - nvg__normalize(&dx1,&dy1); - a = nvg__acosf(dx0*dx1 + dy0*dy1); - d = radius / nvg__tanf(a/2.0f); - -// printf("a=%f° d=%f\n", a/NVG_PI*180.0f, d); - - if (d > 10000.0f) { - nvgLineTo(ctx, x1,y1); - return; - } - - if (nvg__cross(dx0,dy0, dx1,dy1) > 0.0f) { - cx = x1 + dx0*d + dy0*radius; - cy = y1 + dy0*d + -dx0*radius; - a0 = nvg__atan2f(dx0, -dy0); - a1 = nvg__atan2f(-dx1, dy1); - dir = NVG_CW; -// printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f); - } else { - cx = x1 + dx0*d + -dy0*radius; - cy = y1 + dy0*d + dx0*radius; - a0 = nvg__atan2f(-dx0, dy0); - a1 = nvg__atan2f(dx1, -dy1); - dir = NVG_CCW; -// printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f); - } - - nvgArc(ctx, cx, cy, radius, a0, a1, dir); -} - -void nvgClosePath(NVGcontext* ctx) -{ - float vals[] = { NVG_CLOSE }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgPathWinding(NVGcontext* ctx, int dir) -{ - float vals[] = { NVG_WINDING, (float)dir }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgBarc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir, int join) -{ - float a = 0, da = 0, hda = 0, kappa = 0; - float dx = 0, dy = 0, x = 0, y = 0, tanx = 0, tany = 0; - float px = 0, py = 0, ptanx = 0, ptany = 0; - float vals[3 + 5*7 + 100]; - int i, ndivs, nvals; - int move = join && ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO; - - // Clamp angles - da = a1 - a0; - if (dir == NVG_CW) { - if (nvg__absf(da) >= NVG_PI*2) { - da = NVG_PI*2; - } else { - while (da < 0.0f) da += NVG_PI*2; - } - } else { - if (nvg__absf(da) >= NVG_PI*2) { - da = -NVG_PI*2; - } else { - while (da > 0.0f) da -= NVG_PI*2; - } - } - - // Split arc into max 90 degree segments. - ndivs = nvg__maxi(1, nvg__mini((int)(nvg__absf(da) / (NVG_PI*0.5f) + 0.5f), 5)); - hda = (da / (float)ndivs) / 2.0f; - kappa = nvg__absf(4.0f / 3.0f * (1.0f - nvg__cosf(hda)) / nvg__sinf(hda)); - - if (dir == NVG_CCW) - kappa = -kappa; - - nvals = 0; - for (i = 0; i <= ndivs; i++) { - a = a0 + da * (i/(float)ndivs); - dx = nvg__cosf(a); - dy = nvg__sinf(a); - x = cx + dx*r; - y = cy + dy*r; - tanx = -dy*r*kappa; - tany = dx*r*kappa; - - if (i == 0) { - vals[nvals++] = (float)move; - vals[nvals++] = x; - vals[nvals++] = y; - } else { - vals[nvals++] = NVG_BEZIERTO; - vals[nvals++] = px+ptanx; - vals[nvals++] = py+ptany; - vals[nvals++] = x-tanx; - vals[nvals++] = y-tany; - vals[nvals++] = x; - vals[nvals++] = y; - } - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - nvg__appendCommands(ctx, vals, nvals); -} - -void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir) -{ - nvgBarc(ctx, cx, cy, r, a0, a1, dir, 1); -} - -void nvgRect(NVGcontext* ctx, float x, float y, float w, float h) -{ - float vals[] = { - NVG_MOVETO, x,y, - NVG_LINETO, x,y+h, - NVG_LINETO, x+w,y+h, - NVG_LINETO, x+w,y, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) -{ - nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r); -} - -void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) -{ - if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) { - nvgRect(ctx, x, y, w, h); - return; - } else { - float halfw = nvg__absf(w)*0.5f; - float halfh = nvg__absf(h)*0.5f; - float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h); - float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h); - float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h); - float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h); - float vals[] = { - NVG_MOVETO, x, y + ryTL, - NVG_LINETO, x, y + h - ryBL, - NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h, - NVG_LINETO, x + w - rxBR, y + h, - NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR, - NVG_LINETO, x + w, y + ryTR, - NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y, - NVG_LINETO, x + rxTL, y, - NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); - } -} - -void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry) -{ - float vals[] = { - NVG_MOVETO, cx-rx, cy, - NVG_BEZIERTO, cx-rx, cy+ry*NVG_KAPPA90, cx-rx*NVG_KAPPA90, cy+ry, cx, cy+ry, - NVG_BEZIERTO, cx+rx*NVG_KAPPA90, cy+ry, cx+rx, cy+ry*NVG_KAPPA90, cx+rx, cy, - NVG_BEZIERTO, cx+rx, cy-ry*NVG_KAPPA90, cx+rx*NVG_KAPPA90, cy-ry, cx, cy-ry, - NVG_BEZIERTO, cx-rx*NVG_KAPPA90, cy-ry, cx-rx, cy-ry*NVG_KAPPA90, cx-rx, cy, - NVG_CLOSE - }; - nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); -} - -void nvgCircle(NVGcontext* ctx, float cx, float cy, float r) -{ - nvgEllipse(ctx, cx,cy, r,r); -} - -void nvgDebugDumpPathCache(NVGcontext* ctx) -{ - const NVGpath* path; - int i, j; - - printf("Dumping %d cached paths\n", ctx->cache->npaths); - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - printf(" - Path %d\n", i); - if (path->nfill) { - printf(" - fill: %d\n", path->nfill); - for (j = 0; j < path->nfill; j++) - printf("%f\t%f\n", path->fill[j].x, path->fill[j].y); - } - if (path->nstroke) { - printf(" - stroke: %d\n", path->nstroke); - for (j = 0; j < path->nstroke; j++) - printf("%f\t%f\n", path->stroke[j].x, path->stroke[j].y); - } - } -} - -void nvgFill(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - const NVGpath* path; - NVGpaint fillPaint = state->fill; - int i; - - nvg__flattenPaths(ctx); - if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) - nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); - else - nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); - - // Apply global alpha - fillPaint.innerColor.a *= state->alpha; - fillPaint.outerColor.a *= state->alpha; - - ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, - ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); - - // Count triangles - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - ctx->fillTriCount += path->nfill-2; - ctx->fillTriCount += path->nstroke-2; - ctx->drawCallCount += 2; - } -} - -void nvgStroke(NVGcontext* ctx) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getAverageScale(state->xform); - float strokeWidth = nvg__clampf(state->strokeWidth * scale, 0.0f, 200.0f); - NVGpaint strokePaint = state->stroke; - const NVGpath* path; - int i; - - - if (strokeWidth < ctx->fringeWidth) { - // If the stroke width is less than pixel size, use alpha to emulate coverage. - // Since coverage is area, scale by alpha*alpha. - float alpha = nvg__clampf(strokeWidth / ctx->fringeWidth, 0.0f, 1.0f); - strokePaint.innerColor.a *= alpha*alpha; - strokePaint.outerColor.a *= alpha*alpha; - strokeWidth = ctx->fringeWidth; - } - - // Apply global alpha - strokePaint.innerColor.a *= state->alpha; - strokePaint.outerColor.a *= state->alpha; - - nvg__flattenPaths(ctx); - - if (ctx->params.edgeAntiAlias && state->shapeAntiAlias) - nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit); - else - nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit); - - ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth, - strokeWidth, ctx->cache->paths, ctx->cache->npaths); - - // Count triangles - for (i = 0; i < ctx->cache->npaths; i++) { - path = &ctx->cache->paths[i]; - ctx->strokeTriCount += path->nstroke-2; - ctx->drawCallCount++; - } -} - -// Add fonts -int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename) -{ - return fonsAddFont(ctx->fs, name, filename, 0); -} - -int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex) -{ - return fonsAddFont(ctx->fs, name, filename, fontIndex); -} - -int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData) -{ - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, 0); -} - -int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex) -{ - return fonsAddFontMem(ctx->fs, name, data, ndata, freeData, fontIndex); -} - -int nvgFindFont(NVGcontext* ctx, const char* name) -{ - if (name == NULL) return -1; - return fonsGetFontByName(ctx->fs, name); -} - - -int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont) -{ - if(baseFont == -1 || fallbackFont == -1) return 0; - return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont); -} - -int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont) -{ - return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont)); -} - -void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont) -{ - fonsResetFallbackFont(ctx->fs, baseFont); -} - -void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont) -{ - nvgResetFallbackFontsId(ctx, nvgFindFont(ctx, baseFont)); -} - -// State setting -void nvgFontSize(NVGcontext* ctx, float size) -{ - NVGstate* state = nvg__getState(ctx); - state->fontSize = size; -} - -void nvgFontBlur(NVGcontext* ctx, float blur) -{ - NVGstate* state = nvg__getState(ctx); - state->fontBlur = blur; -} - -void nvgTextLetterSpacing(NVGcontext* ctx, float spacing) -{ - NVGstate* state = nvg__getState(ctx); - state->letterSpacing = spacing; -} - -void nvgTextLineHeight(NVGcontext* ctx, float lineHeight) -{ - NVGstate* state = nvg__getState(ctx); - state->lineHeight = lineHeight; -} - -void nvgTextAlign(NVGcontext* ctx, int align) -{ - NVGstate* state = nvg__getState(ctx); - state->textAlign = align; -} - -void nvgFontFaceId(NVGcontext* ctx, int font) -{ - NVGstate* state = nvg__getState(ctx); - state->fontId = font; -} - -void nvgFontFace(NVGcontext* ctx, const char* font) -{ - NVGstate* state = nvg__getState(ctx); - state->fontId = fonsGetFontByName(ctx->fs, font); -} - -static float nvg__quantize(float a, float d) -{ - return ((int)(a / d + 0.5f)) * d; -} - -static float nvg__getFontScale(NVGstate* state) -{ - return nvg__minf(nvg__quantize(nvg__getAverageScale(state->xform), 0.01f), 4.0f); -} - -static void nvg__flushTextTexture(NVGcontext* ctx) -{ - int dirty[4]; - - if (fonsValidateTexture(ctx->fs, dirty)) { - int fontImage = ctx->fontImages[ctx->fontImageIdx]; - // Update texture - if (fontImage != 0) { - int iw, ih; - const unsigned char* data = fonsGetTextureData(ctx->fs, &iw, &ih); - int x = dirty[0]; - int y = dirty[1]; - int w = dirty[2] - dirty[0]; - int h = dirty[3] - dirty[1]; - ctx->params.renderUpdateTexture(ctx->params.userPtr, fontImage, x,y, w,h, data); - } - } -} - -static int nvg__allocTextAtlas(NVGcontext* ctx) -{ - int iw, ih; - nvg__flushTextTexture(ctx); - if (ctx->fontImageIdx >= NVG_MAX_FONTIMAGES-1) - return 0; - // if next fontImage already have a texture - if (ctx->fontImages[ctx->fontImageIdx+1] != 0) - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx+1], &iw, &ih); - else { // calculate the new font image size and create it. - nvgImageSize(ctx, ctx->fontImages[ctx->fontImageIdx], &iw, &ih); - if (iw > ih) - ih *= 2; - else - iw *= 2; - if (iw > NVG_MAX_FONTIMAGE_SIZE || ih > NVG_MAX_FONTIMAGE_SIZE) - iw = ih = NVG_MAX_FONTIMAGE_SIZE; - ctx->fontImages[ctx->fontImageIdx+1] = ctx->params.renderCreateTexture(ctx->params.userPtr, NVG_TEXTURE_ALPHA, iw, ih, 0, NULL); - } - ++ctx->fontImageIdx; - fonsResetAtlas(ctx->fs, iw, ih); - return 1; -} - -static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts) -{ - NVGstate* state = nvg__getState(ctx); - NVGpaint paint = state->fill; - - // Render triangles. - paint.image = ctx->fontImages[ctx->fontImageIdx]; - - // Apply global alpha - paint.innerColor.a *= state->alpha; - paint.outerColor.a *= state->alpha; - - ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth); - - ctx->drawCallCount++; - ctx->textTriCount += nverts/3; -} - -static int nvg__isTransformFlipped(const float *xform) -{ - float det = xform[0] * xform[3] - xform[2] * xform[1]; - return( det < 0); -} - -float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end) -{ - NVGstate* state = nvg__getState(ctx); - FONStextIter iter, prevIter; - FONSquad q; - NVGvertex* verts; - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - int cverts = 0; - int nverts = 0; - int isFlipped = nvg__isTransformFlipped(state->xform); - - if (end == NULL) - end = string + strlen(string); - - if (state->fontId == FONS_INVALID) return x; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - cverts = nvg__maxi(2, (int)(end - string)) * 6; // conservative estimate. - verts = nvg__allocTempVerts(ctx, cverts); - if (verts == NULL) return x; - - fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - float c[4*2]; - if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? - if (nverts != 0) { - nvg__renderText(ctx, verts, nverts); - nverts = 0; - } - if (!nvg__allocTextAtlas(ctx)) - break; // no memory :( - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - if (iter.prevGlyphIndex == -1) // still can not find glyph? - break; - } - prevIter = iter; - if(isFlipped) { - float tmp; - - tmp = q.y0; q.y0 = q.y1; q.y1 = tmp; - tmp = q.t0; q.t0 = q.t1; q.t1 = tmp; - } - // Transform corners. - nvgTransformPoint(&c[0],&c[1], state->xform, q.x0*invscale, q.y0*invscale); - nvgTransformPoint(&c[2],&c[3], state->xform, q.x1*invscale, q.y0*invscale); - nvgTransformPoint(&c[4],&c[5], state->xform, q.x1*invscale, q.y1*invscale); - nvgTransformPoint(&c[6],&c[7], state->xform, q.x0*invscale, q.y1*invscale); - // Create triangles - if (nverts+6 <= cverts) { - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++; - nvg__vset(&verts[nverts], c[2], c[3], q.s1, q.t0); nverts++; - nvg__vset(&verts[nverts], c[0], c[1], q.s0, q.t0); nverts++; - nvg__vset(&verts[nverts], c[6], c[7], q.s0, q.t1); nverts++; - nvg__vset(&verts[nverts], c[4], c[5], q.s1, q.t1); nverts++; - } - } - - // TODO: add back-end bit to do this just once per frame. - nvg__flushTextTexture(ctx); - - nvg__renderText(ctx, verts, nverts); - - return iter.nextx / scale; -} - -void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) -{ - NVGstate* state = nvg__getState(ctx); - NVGtextRow rows[2]; - int nrows = 0, i; - int oldAlign = state->textAlign; - int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT); - int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE); - float lineh = 0; - - if (state->fontId == FONS_INVALID) return; - - nvgTextMetrics(ctx, NULL, NULL, &lineh); - - state->textAlign = NVG_ALIGN_LEFT | valign; - - while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) { - for (i = 0; i < nrows; i++) { - NVGtextRow* row = &rows[i]; - if (haling & NVG_ALIGN_LEFT) - nvgText(ctx, x, y, row->start, row->end); - else if (haling & NVG_ALIGN_CENTER) - nvgText(ctx, x + breakRowWidth*0.5f - row->width*0.5f, y, row->start, row->end); - else if (haling & NVG_ALIGN_RIGHT) - nvgText(ctx, x + breakRowWidth - row->width, y, row->start, row->end); - y += lineh * state->lineHeight; - } - string = rows[nrows-1].next; - } - - state->textAlign = oldAlign; -} - -int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - FONStextIter iter, prevIter; - FONSquad q; - int npos = 0; - - if (state->fontId == FONS_INVALID) return 0; - - if (end == NULL) - end = string + strlen(string); - - if (string == end) - return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - } - prevIter = iter; - positions[npos].str = iter.str; - positions[npos].x = iter.x * invscale; - positions[npos].minx = nvg__minf(iter.x, q.x0) * invscale; - positions[npos].maxx = nvg__maxf(iter.nextx, q.x1) * invscale; - npos++; - if (npos >= maxPositions) - break; - } - - return npos; -} - -enum NVGcodepointType { - NVG_SPACE, - NVG_NEWLINE, - NVG_CHAR, - NVG_CJK_CHAR, -}; - -int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - FONStextIter iter, prevIter; - FONSquad q; - int nrows = 0; - float rowStartX = 0; - float rowWidth = 0; - float rowMinX = 0; - float rowMaxX = 0; - const char* rowStart = NULL; - const char* rowEnd = NULL; - const char* wordStart = NULL; - float wordStartX = 0; - float wordMinX = 0; - const char* breakEnd = NULL; - float breakWidth = 0; - float breakMaxX = 0; - int type = NVG_SPACE, ptype = NVG_SPACE; - unsigned int pcodepoint = 0; - - if (maxRows == 0) return 0; - if (state->fontId == FONS_INVALID) return 0; - - if (end == NULL) - end = string + strlen(string); - - if (string == end) return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - breakRowWidth *= scale; - - fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL); - prevIter = iter; - while (fonsTextIterNext(ctx->fs, &iter, &q)) { - if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? - iter = prevIter; - fonsTextIterNext(ctx->fs, &iter, &q); // try again - } - prevIter = iter; - switch (iter.codepoint) { - case 9: // \t - case 11: // \v - case 12: // \f - case 32: // space - case 0x00a0: // NBSP - type = NVG_SPACE; - break; - case 10: // \n - type = pcodepoint == 13 ? NVG_SPACE : NVG_NEWLINE; - break; - case 13: // \r - type = pcodepoint == 10 ? NVG_SPACE : NVG_NEWLINE; - break; - case 0x0085: // NEL - type = NVG_NEWLINE; - break; - default: - if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) || - (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) || - (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) || - (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) || - (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) || - (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF)) - type = NVG_CJK_CHAR; - else - type = NVG_CHAR; - break; - } - - if (type == NVG_NEWLINE) { - // Always handle new lines. - rows[nrows].start = rowStart != NULL ? rowStart : iter.str; - rows[nrows].end = rowEnd != NULL ? rowEnd : iter.str; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = iter.next; - nrows++; - if (nrows >= maxRows) - return nrows; - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - // Indicate to skip the white space at the beginning of the row. - rowStart = NULL; - rowEnd = NULL; - rowWidth = 0; - rowMinX = rowMaxX = 0; - } else { - if (rowStart == NULL) { - // Skip white space until the beginning of the line - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { - // The current char is the row so far - rowStartX = iter.x; - rowStart = iter.str; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = q.x0 - rowStartX; - rowMaxX = q.x1 - rowStartX; - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0 - rowStartX; - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - } - } else { - float nextWidth = iter.nextx - rowStartX; - - // track last non-white space character - if (type == NVG_CHAR || type == NVG_CJK_CHAR) { - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMaxX = q.x1 - rowStartX; - } - // track last end of a word - if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) { - breakEnd = iter.str; - breakWidth = rowWidth; - breakMaxX = rowMaxX; - } - // track last beginning of a word - if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0; - } - - // Break to new line when a character is beyond break width. - if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) { - // The run length is too long, need to break to new line. - if (breakEnd == rowStart) { - // The current word is longer than the row length, just break it from here. - rows[nrows].start = rowStart; - rows[nrows].end = iter.str; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = iter.str; - nrows++; - if (nrows >= maxRows) - return nrows; - rowStartX = iter.x; - rowStart = iter.str; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = q.x0 - rowStartX; - rowMaxX = q.x1 - rowStartX; - wordStart = iter.str; - wordStartX = iter.x; - wordMinX = q.x0 - rowStartX; - } else { - // Break the line from the end of the last word, and start new line from the beginning of the new. - rows[nrows].start = rowStart; - rows[nrows].end = breakEnd; - rows[nrows].width = breakWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = breakMaxX * invscale; - rows[nrows].next = wordStart; - nrows++; - if (nrows >= maxRows) - return nrows; - // Update row - rowStartX = wordStartX; - rowStart = wordStart; - rowEnd = iter.next; - rowWidth = iter.nextx - rowStartX; - rowMinX = wordMinX - rowStartX; - rowMaxX = q.x1 - rowStartX; - } - // Set null break point - breakEnd = rowStart; - breakWidth = 0.0; - breakMaxX = 0.0; - } - } - } - - pcodepoint = iter.codepoint; - ptype = type; - } - - // Break the line from the end of the last word, and start new line from the beginning of the new. - if (rowStart != NULL) { - rows[nrows].start = rowStart; - rows[nrows].end = rowEnd; - rows[nrows].width = rowWidth * invscale; - rows[nrows].minx = rowMinX * invscale; - rows[nrows].maxx = rowMaxX * invscale; - rows[nrows].next = end; - nrows++; - } - - return nrows; -} - -float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - float width; - - if (state->fontId == FONS_INVALID) return 0; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - width = fonsTextBounds(ctx->fs, x*scale, y*scale, string, end, bounds); - if (bounds != NULL) { - // Use line bounds for height. - fonsLineBounds(ctx->fs, y*scale, &bounds[1], &bounds[3]); - bounds[0] *= invscale; - bounds[1] *= invscale; - bounds[2] *= invscale; - bounds[3] *= invscale; - } - return width * invscale; -} - -void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds) -{ - NVGstate* state = nvg__getState(ctx); - NVGtextRow rows[2]; - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - int nrows = 0, i; - int oldAlign = state->textAlign; - int haling = state->textAlign & (NVG_ALIGN_LEFT | NVG_ALIGN_CENTER | NVG_ALIGN_RIGHT); - int valign = state->textAlign & (NVG_ALIGN_TOP | NVG_ALIGN_MIDDLE | NVG_ALIGN_BOTTOM | NVG_ALIGN_BASELINE); - float lineh = 0, rminy = 0, rmaxy = 0; - float minx, miny, maxx, maxy; - - if (state->fontId == FONS_INVALID) { - if (bounds != NULL) - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0f; - return; - } - - nvgTextMetrics(ctx, NULL, NULL, &lineh); - - state->textAlign = NVG_ALIGN_LEFT | valign; - - minx = maxx = x; - miny = maxy = y; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - fonsLineBounds(ctx->fs, 0, &rminy, &rmaxy); - rminy *= invscale; - rmaxy *= invscale; - - while ((nrows = nvgTextBreakLines(ctx, string, end, breakRowWidth, rows, 2))) { - for (i = 0; i < nrows; i++) { - NVGtextRow* row = &rows[i]; - float rminx, rmaxx, dx = 0; - // Horizontal bounds - if (haling & NVG_ALIGN_LEFT) - dx = 0; - else if (haling & NVG_ALIGN_CENTER) - dx = breakRowWidth*0.5f - row->width*0.5f; - else if (haling & NVG_ALIGN_RIGHT) - dx = breakRowWidth - row->width; - rminx = x + row->minx + dx; - rmaxx = x + row->maxx + dx; - minx = nvg__minf(minx, rminx); - maxx = nvg__maxf(maxx, rmaxx); - // Vertical bounds. - miny = nvg__minf(miny, y + rminy); - maxy = nvg__maxf(maxy, y + rmaxy); - - y += lineh * state->lineHeight; - } - string = rows[nrows-1].next; - } - - state->textAlign = oldAlign; - - if (bounds != NULL) { - bounds[0] = minx; - bounds[1] = miny; - bounds[2] = maxx; - bounds[3] = maxy; - } -} - -void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh) -{ - NVGstate* state = nvg__getState(ctx); - float scale = nvg__getFontScale(state) * ctx->devicePxRatio; - float invscale = 1.0f / scale; - - if (state->fontId == FONS_INVALID) return; - - fonsSetSize(ctx->fs, state->fontSize*scale); - fonsSetSpacing(ctx->fs, state->letterSpacing*scale); - fonsSetBlur(ctx->fs, state->fontBlur*scale); - fonsSetAlign(ctx->fs, state->textAlign); - fonsSetFont(ctx->fs, state->fontId); - - fonsVertMetrics(ctx->fs, ascender, descender, lineh); - if (ascender != NULL) - *ascender *= invscale; - if (descender != NULL) - *descender *= invscale; - if (lineh != NULL) - *lineh *= invscale; -} -// vim: ft=c nu noet ts=4 diff --git a/nanovg/nanovg.h b/nanovg/nanovg.h deleted file mode 100644 index f32a175..0000000 --- a/nanovg/nanovg.h +++ /dev/null @@ -1,708 +0,0 @@ -// -// Copyright (c) 2013 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef NANOVG_H_F380EB38_CDA3_11EA_AF53_BBD96082A0CB -#define NANOVG_H_F380EB38_CDA3_11EA_AF53_BBD96082A0CB - -#ifdef __cplusplus -extern "C" { -#endif - -#define NVG_PI 3.14159265358979323846264338327f - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union -#endif - -typedef struct NVGcontext NVGcontext; - -struct NVGcolor { - union { - float rgba[4]; - struct { - float r,g,b,a; - }; - }; -}; -typedef struct NVGcolor NVGcolor; - -struct NVGpaint { - float xform[6]; - float extent[2]; - float radius; - float feather; - NVGcolor innerColor; - NVGcolor outerColor; - int image; -}; -typedef struct NVGpaint NVGpaint; - -enum NVGwinding { - NVG_CCW = 1, // Winding for solid shapes - NVG_CW = 2, // Winding for holes -}; - -enum NVGsolidity { - NVG_SOLID = 1, // CCW - NVG_HOLE = 2, // CW -}; - -enum NVGlineCap { - NVG_BUTT, - NVG_ROUND, - NVG_SQUARE, - NVG_BEVEL, - NVG_MITER, -}; - -enum NVGalign { - // Horizontal align - NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. - NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. - NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. - // Vertical align - NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. - NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. - NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. - NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. -}; - -enum NVGblendFactor { - NVG_ZERO = 1<<0, - NVG_ONE = 1<<1, - NVG_SRC_COLOR = 1<<2, - NVG_ONE_MINUS_SRC_COLOR = 1<<3, - NVG_DST_COLOR = 1<<4, - NVG_ONE_MINUS_DST_COLOR = 1<<5, - NVG_SRC_ALPHA = 1<<6, - NVG_ONE_MINUS_SRC_ALPHA = 1<<7, - NVG_DST_ALPHA = 1<<8, - NVG_ONE_MINUS_DST_ALPHA = 1<<9, - NVG_SRC_ALPHA_SATURATE = 1<<10, -}; - -enum NVGcompositeOperation { - NVG_SOURCE_OVER, - NVG_SOURCE_IN, - NVG_SOURCE_OUT, - NVG_ATOP, - NVG_DESTINATION_OVER, - NVG_DESTINATION_IN, - NVG_DESTINATION_OUT, - NVG_DESTINATION_ATOP, - NVG_LIGHTER, - NVG_COPY, - NVG_XOR, -}; - -struct NVGcompositeOperationState { - int srcRGB; - int dstRGB; - int srcAlpha; - int dstAlpha; -}; -typedef struct NVGcompositeOperationState NVGcompositeOperationState; - -struct NVGglyphPosition { - const char* str; // Position of the glyph in the input string. - float x; // The x-coordinate of the logical glyph position. - float minx, maxx; // The bounds of the glyph shape. -}; -typedef struct NVGglyphPosition NVGglyphPosition; - -struct NVGtextRow { - const char* start; // Pointer to the input text where the row starts. - const char* end; // Pointer to the input text where the row ends (one past the last character). - const char* next; // Pointer to the beginning of the next row. - float width; // Logical width of the row. - float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. -}; -typedef struct NVGtextRow NVGtextRow; - -enum NVGimageFlags { - NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. - NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. - NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. - NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. - NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. - NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear -}; - -// Begin drawing a new frame -// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() -// nvgBeginFrame() defines the size of the window to render to in relation currently -// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to -// control the rendering on Hi-DPI devices. -// For example, GLFW returns two dimension for an opened window: window size and -// frame buffer size. In that case you would set windowWidth/Height to the window size -// devicePixelRatio to: frameBufferWidth / windowWidth. -void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio); - -// Cancels drawing the current frame. -void nvgCancelFrame(NVGcontext* ctx); - -// Ends drawing flushing remaining render state. -void nvgEndFrame(NVGcontext* ctx); - -// -// Composite operation -// -// The composite operations in NanoVG are modeled after HTML Canvas API, and -// the blend func is based on OpenGL (see corresponding manuals for more info). -// The colors in the blending state have premultiplied alpha. - -// Sets the composite operation. The op parameter should be one of NVGcompositeOperation. -void nvgGlobalCompositeOperation(NVGcontext* ctx, int op); - -// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor. -void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor); - -// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor. -void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha); - -// -// Color utils -// -// Colors in NanoVG are stored as unsigned ints in ABGR format. - -// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). -NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); - -// Returns a color value from red, green, blue values. Alpha will be set to 1.0f. -NVGcolor nvgRGBf(float r, float g, float b); - - -// Returns a color value from red, green, blue and alpha values. -NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); - -// Returns a color value from red, green, blue and alpha values. -NVGcolor nvgRGBAf(float r, float g, float b, float a); - - -// Linearly interpolates from color c0 to c1, and returns resulting color value. -NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); - -// Sets transparency of a color value. -NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); - -// Sets transparency of a color value. -NVGcolor nvgTransRGBAf(NVGcolor c0, float a); - -// Returns color value specified by hue, saturation and lightness. -// HSL values are all in range [0..1], alpha will be set to 255. -NVGcolor nvgHSL(float h, float s, float l); - -// Returns color value specified by hue, saturation and lightness and alpha. -// HSL values are all in range [0..1], alpha in range [0..255] -NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); - -// -// State Handling -// -// NanoVG contains state which represents how paths will be rendered. -// The state contains transform, fill and stroke styles, text and font styles, -// and scissor clipping. - -// Pushes and saves the current render state into a state stack. -// A matching nvgRestore() must be used to restore the state. -void nvgSave(NVGcontext* ctx); - -// Pops and restores current render state. -void nvgRestore(NVGcontext* ctx); - -// Resets current render state to default values. Does not affect the render state stack. -void nvgReset(NVGcontext* ctx); - -// -// Render styles -// -// Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. -// Solid color is simply defined as a color value, different kinds of paints can be created -// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). -// -// Current render style can be saved and restored using nvgSave() and nvgRestore(). - -// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default. -void nvgShapeAntiAlias(NVGcontext* ctx, int enabled); - -// Sets current stroke style to a solid color. -void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); - -// Sets current stroke style to a paint, which can be a one of the gradients or a pattern. -void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); - -// Sets current fill style to a solid color. -void nvgFillColor(NVGcontext* ctx, NVGcolor color); - -// Sets current fill style to a paint, which can be a one of the gradients or a pattern. -void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); - -// Sets the miter limit of the stroke style. -// Miter limit controls when a sharp corner is beveled. -void nvgMiterLimit(NVGcontext* ctx, float limit); - -// Sets the stroke width of the stroke style. -void nvgStrokeWidth(NVGcontext* ctx, float size); - -// Sets how the end of the line (cap) is drawn, -// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. -void nvgLineCap(NVGcontext* ctx, int cap); - -// Sets how sharp path corners are drawn. -// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. -void nvgLineJoin(NVGcontext* ctx, int join); - -// Sets the transparency applied to all rendered shapes. -// Already transparent paths will get proportionally more transparent as well. -void nvgGlobalAlpha(NVGcontext* ctx, float alpha); - -// -// Transforms -// -// The paths, gradients, patterns and scissor region are transformed by an transformation -// matrix at the time when they are passed to the API. -// The current transformation matrix is a affine matrix: -// [sx kx tx] -// [ky sy ty] -// [ 0 0 1] -// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. -// The last row is assumed to be 0,0,1 and is not stored. -// -// Apart from nvgResetTransform(), each transformation function first creates -// specific transformation matrix and pre-multiplies the current transformation by it. -// -// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). - -// Resets current transform to a identity matrix. -void nvgResetTransform(NVGcontext* ctx); - -// Premultiplies current coordinate system by specified matrix. -// The parameters are interpreted as matrix as follows: -// [a c e] -// [b d f] -// [0 0 1] -void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); - -// Translates current coordinate system. -void nvgTranslate(NVGcontext* ctx, float x, float y); - -// Rotates current coordinate system. Angle is specified in radians. -void nvgRotate(NVGcontext* ctx, float angle); - -// Skews the current coordinate system along X axis. Angle is specified in radians. -void nvgSkewX(NVGcontext* ctx, float angle); - -// Skews the current coordinate system along Y axis. Angle is specified in radians. -void nvgSkewY(NVGcontext* ctx, float angle); - -// Scales the current coordinate system. -void nvgScale(NVGcontext* ctx, float x, float y); - -// Stores the top part (a-f) of the current transformation matrix in to the specified buffer. -// [a c e] -// [b d f] -// [0 0 1] -// There should be space for 6 floats in the return buffer for the values a-f. -void nvgCurrentTransform(NVGcontext* ctx, float* xform); - - -// The following functions can be used to make calculations on 2x3 transformation matrices. -// A 2x3 matrix is represented as float[6]. - -// Sets the transform to identity matrix. -void nvgTransformIdentity(float* dst); - -// Sets the transform to translation matrix matrix. -void nvgTransformTranslate(float* dst, float tx, float ty); - -// Sets the transform to scale matrix. -void nvgTransformScale(float* dst, float sx, float sy); - -// Sets the transform to rotate matrix. Angle is specified in radians. -void nvgTransformRotate(float* dst, float a); - -// Sets the transform to skew-x matrix. Angle is specified in radians. -void nvgTransformSkewX(float* dst, float a); - -// Sets the transform to skew-y matrix. Angle is specified in radians. -void nvgTransformSkewY(float* dst, float a); - -// Sets the transform to the result of multiplication of two transforms, of A = A*B. -void nvgTransformMultiply(float* dst, const float* src); - -// Sets the transform to the result of multiplication of two transforms, of A = B*A. -void nvgTransformPremultiply(float* dst, const float* src); - -// Sets the destination to inverse of specified transform. -// Returns 1 if the inverse could be calculated, else 0. -int nvgTransformInverse(float* dst, const float* src); - -// Transform a point by given transform. -void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); - -// Converts degrees to radians and vice versa. -float nvgDegToRad(float deg); -float nvgRadToDeg(float rad); - -// -// Images -// -// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. -// In addition you can upload your own image. The image loading is provided by stb_image. -// The parameter imageFlags is combination of flags defined in NVGimageFlags. - -// Creates image by loading it from the disk from specified file name. -// Returns handle to the image. -int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); - -// Creates image by loading it from the specified chunk of memory. -// Returns handle to the image. -int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); - -// Creates image from specified image data. -// Returns handle to the image. -int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); - -int nvgCreateImageAlpha(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); - -// Updates image data specified by image handle. -void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); - -// Returns the dimensions of a created image. -void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); - -// Deletes created image. -void nvgDeleteImage(NVGcontext* ctx, int image); - -// -// Paints -// -// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. -// These can be used as paints for strokes and fills. - -// Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates -// of the linear gradient, icol specifies the start color and ocol the end color. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, - NVGcolor icol, NVGcolor ocol); - -// Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering -// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, -// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry -// the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, - float r, float f, NVGcolor icol, NVGcolor ocol); - -// Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify -// the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, - NVGcolor icol, NVGcolor ocol); - -// Creates and returns an image pattern. Parameters (ox,oy) specify the left-top location of the image pattern, -// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. -// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). -NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, - float angle, int image, float alpha); - -// -// Scissoring -// -// Scissoring allows you to clip the rendering into a rectangle. This is useful for various -// user interface cases like rendering a text edit or a timeline. - -// Sets the current scissor rectangle. -// The scissor rectangle is transformed by the current transform. -void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); - -// Intersects current scissor rectangle with the specified rectangle. -// The scissor rectangle is transformed by the current transform. -// Note: in case the rotation of previous scissor rect differs from -// the current one, the intersection will be done between the specified -// rectangle and the previous scissor rectangle transformed in the current -// transform space. The resulting shape is always rectangle. -void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); - -// Reset and disables scissoring. -void nvgResetScissor(NVGcontext* ctx); - -// -// Paths -// -// Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. -// Then you define one or more paths and sub-paths which describe the shape. The are functions -// to draw common shapes like rectangles and circles, and lower level step-by-step functions, -// which allow to define a path curve by curve. -// -// NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise -// winding and holes should have counter clockwise order. To specify winding of a path you can -// call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. -// -// Finally you can fill the path using current fill style by calling nvgFill(), and stroke it -// with current stroke style by calling nvgStroke(). -// -// The curve segments and sub-paths are transformed by the current transform. - -// Clears the current path and sub-paths. -void nvgBeginPath(NVGcontext* ctx); - -// Starts new sub-path with specified point as first point. -void nvgMoveTo(NVGcontext* ctx, float x, float y); - -// Adds line segment from the last point in the path to the specified point. -void nvgLineTo(NVGcontext* ctx, float x, float y); - -// Adds cubic bezier segment from last point in the path via two control points to the specified point. -void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); - -// Adds quadratic bezier segment from last point in the path via a control point to the specified point. -void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); - -// Adds an arc segment at the corner defined by the last path point, and two specified points. -void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); - -// Closes current sub-path with a line segment. -void nvgClosePath(NVGcontext* ctx); - -// Sets the current sub-path winding, see NVGwinding and NVGsolidity. -void nvgPathWinding(NVGcontext* ctx, int dir); - -// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, -// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). -// Angles are specified in radians. -void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); - -void nvgBarc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir, int join); - -// Creates new rectangle shaped sub-path. -void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); - -// Creates new rounded rectangle shaped sub-path. -void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); - -// Creates new rounded rectangle shaped sub-path with varying radii for each corner. -void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); - -// Creates new ellipse shaped sub-path. -void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); - -// Creates new circle shaped sub-path. -void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); - -// Fills the current path with current fill style. -void nvgFill(NVGcontext* ctx); - -// Fills the current path with current stroke style. -void nvgStroke(NVGcontext* ctx); - - -// -// Text -// -// NanoVG allows you to load .ttf files and use the font to render text. -// -// The appearance of the text can be defined by setting the current text style -// and by specifying the fill color. Common text and font settings such as -// font size, letter spacing and text align are supported. Font blur allows you -// to create simple text effects such as drop shadows. -// -// At render time the font face can be set based on the font handles or name. -// -// Font measure functions return values in local space, the calculations are -// carried in the same resolution as the final rendering. This is done because -// the text glyph positions are snapped to the nearest pixels sharp rendering. -// -// The local space means that values are not rotated or scale as per the current -// transformation. For example if you set font size to 12, which would mean that -// line height is 16, then regardless of the current scaling and rotation, the -// returned line height is always 16. Some measures may vary because of the scaling -// since aforementioned pixel snapping. -// -// While this may sound a little odd, the setup allows you to always render the -// same way regardless of scaling. I.e. following works regardless of scaling: -// -// const char* txt = "Text me up."; -// nvgTextBounds(vg, x,y, txt, NULL, bounds); -// nvgBeginPath(vg); -// nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); -// nvgFill(vg); -// -// Note: currently only solid color fill is supported for text. - -// Creates font by loading it from the disk from specified file name. -// Returns handle to the font. -int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); - -// fontIndex specifies which font face to load from a .ttf/.ttc file. -int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex); - -// Creates font by loading it from the specified memory chunk. -// Returns handle to the font. -int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); - -// fontIndex specifies which font face to load from a .ttf/.ttc file. -int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex); - -// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. -int nvgFindFont(NVGcontext* ctx, const char* name); - -// Adds a fallback font by handle. -int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont); - -// Adds a fallback font by name. -int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont); - -// Resets fallback fonts by handle. -void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont); - -// Resets fallback fonts by name. -void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont); - -// Sets the font size of current text style. -void nvgFontSize(NVGcontext* ctx, float size); - -// Sets the blur of current text style. -void nvgFontBlur(NVGcontext* ctx, float blur); - -// Sets the letter spacing of current text style. -void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); - -// Sets the proportional line height of current text style. The line height is specified as multiple of font size. -void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); - -// Sets the text align of current text style, see NVGalign for options. -void nvgTextAlign(NVGcontext* ctx, int align); - -// Sets the font face based on specified id of current text style. -void nvgFontFaceId(NVGcontext* ctx, int font); - -// Sets the font face based on specified name of current text style. -void nvgFontFace(NVGcontext* ctx, const char* font); - -// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. -float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); - -// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. -// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. -// Words longer than the max width are slit at nearest character (i.e. no hyphenation). -void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); - -// Measures the specified text string. Parameter bounds should be a pointer to float[4], -// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] -// Returns the horizontal advance of the measured text (i.e. where the next character should drawn). -// Measured values are returned in local coordinate space. -float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); - -// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], -// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] -// Measured values are returned in local coordinate space. -void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); - -// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. -// Measured values are returned in local coordinate space. -int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); - -// Returns the vertical metrics based on the current text style. -// Measured values are returned in local coordinate space. -void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); - -// Breaks the specified text into lines. If end is specified only the sub-string will be used. -// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. -// Words longer than the max width are slit at nearest character (i.e. no hyphenation). -int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); - -// -// Internal Render API -// -enum NVGtexture { - NVG_TEXTURE_ALPHA = 0x01, - NVG_TEXTURE_RGBA = 0x02, -}; - -struct NVGscissor { - float xform[6]; - float extent[2]; -}; -typedef struct NVGscissor NVGscissor; - -struct NVGvertex { - float x,y,u,v; -}; -typedef struct NVGvertex NVGvertex; - -struct NVGpath { - int first; - int count; - unsigned char closed; - int nbevel; - NVGvertex* fill; - int nfill; - NVGvertex* stroke; - int nstroke; - int winding; - int convex; -}; -typedef struct NVGpath NVGpath; - -struct NVGparams { - void* userPtr; - int edgeAntiAlias; - int (*renderCreate)(void* uptr); - int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); - int (*renderDeleteTexture)(void* uptr, int image); - int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); - int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); - void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio); - void (*renderCancel)(void* uptr); - void (*renderFlush)(void* uptr); - void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); - void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); - void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe); - void (*renderDelete)(void* uptr); -}; -typedef struct NVGparams NVGparams; - -// Constructor and destructor, called by the render back-end. -NVGcontext* nvgCreateInternal(NVGparams* params); -void nvgDeleteInternal(NVGcontext* ctx); - -NVGparams* nvgInternalParams(NVGcontext* ctx); - -// Debug function to dump cached path data. -void nvgDebugDumpPathCache(NVGcontext* ctx); - -#if defined(__ANDROID__) -#include <stdio.h> -#include <android/asset_manager.h> -void nvgSetAndroidAssetManager(AAssetManager * manager); -FILE * android_fopen(const char* fname, const char* mode); -#endif - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } - -#ifdef __cplusplus -} -#endif - -#endif // NANOVG_H_F380EB38_CDA3_11EA_AF53_BBD96082A0CB diff --git a/nanovg/nanovg_gl.c b/nanovg/nanovg_gl.c deleted file mode 100644 index 9970842..0000000 --- a/nanovg/nanovg_gl.c +++ /dev/null @@ -1,1649 +0,0 @@ -// -// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#if defined(NANOVG_GL2) + defined(NANOVG_GL3) + defined(NANOVG_GLES2) + defined(NANOVG_GLES3) != 1 -#error Define exactly one of NANOVG_GL2, NANOVG_GL3, NANOVG_GLES2, NANOVG_GLES3 -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <math.h> - -#ifdef NANOVG_USE_GLEW -# include <GL/glew.h> -static int glew_initialized = 0; -#endif - -#ifdef NANOVG_GLES2 -# include <GLES2/gl2.h> -# include <GLES2/gl2ext.h> -#endif - -#ifdef NANOVG_GLES3 -# include <GLES3/gl3.h> -#endif - -#if defined(NANOVG_GL2) || defined(NANOVG_GL3) -#include <GL/gl.h> -#endif - -#include "nanovg_gl.h" - -#if defined NANOVG_GL2 -//# define NANOVG_GL2 1 -#elif defined NANOVG_GL3 -//# define NANOVG_GL3 1 -# define NANOVG_GL_USE_UNIFORMBUFFER 1 -#elif defined NANOVG_GLES2 -//# define NANOVG_GLES2 1 -#elif defined NANOVG_GLES3 -//# define NANOVG_GLES3 1 -#endif - -#define NANOVG_GL_USE_STATE_FILTER (1) - -enum GLNVGuniformLoc { - GLNVG_LOC_VIEWSIZE, - GLNVG_LOC_TEX, - GLNVG_LOC_FRAG, - GLNVG_MAX_LOCS -}; - -enum GLNVGshaderType { - NSVG_SHADER_FILLGRAD, - NSVG_SHADER_FILLIMG, - NSVG_SHADER_SIMPLE, - NSVG_SHADER_IMG -}; - -#if NANOVG_GL_USE_UNIFORMBUFFER -enum GLNVGuniformBindings { - GLNVG_FRAG_BINDING = 0, -}; -#endif - -struct GLNVGshader { - GLuint prog; - GLuint frag; - GLuint vert; - GLint loc[GLNVG_MAX_LOCS]; -}; -typedef struct GLNVGshader GLNVGshader; - -struct GLNVGtexture { - int id; - GLuint tex; - int width, height; - int type; - int flags; -}; -typedef struct GLNVGtexture GLNVGtexture; - -struct GLNVGblend -{ - GLenum srcRGB; - GLenum dstRGB; - GLenum srcAlpha; - GLenum dstAlpha; -}; -typedef struct GLNVGblend GLNVGblend; - -enum GLNVGcallType { - GLNVG_NONE = 0, - GLNVG_FILL, - GLNVG_CONVEXFILL, - GLNVG_STROKE, - GLNVG_TRIANGLES, -}; - -struct GLNVGcall { - int type; - int image; - int pathOffset; - int pathCount; - int triangleOffset; - int triangleCount; - int uniformOffset; - GLNVGblend blendFunc; -}; -typedef struct GLNVGcall GLNVGcall; - -struct GLNVGpath { - int fillOffset; - int fillCount; - int strokeOffset; - int strokeCount; -}; -typedef struct GLNVGpath GLNVGpath; - -struct GLNVGfragUniforms { - #if NANOVG_GL_USE_UNIFORMBUFFER - float scissorMat[12]; // matrices are actually 3 vec4s - float paintMat[12]; - struct NVGcolor innerCol; - struct NVGcolor outerCol; - float scissorExt[2]; - float scissorScale[2]; - float extent[2]; - float radius; - float feather; - float strokeMult; - float strokeThr; - int texType; - int type; - #else - // note: after modifying layout or size of uniform array, - // don't forget to also update the fragment shader source! - #define NANOVG_GL_UNIFORMARRAY_SIZE 11 - union { - struct { - float scissorMat[12]; // matrices are actually 3 vec4s - float paintMat[12]; - struct NVGcolor innerCol; - struct NVGcolor outerCol; - float scissorExt[2]; - float scissorScale[2]; - float extent[2]; - float radius; - float feather; - float strokeMult; - float strokeThr; - float texType; - float type; - }; - float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4]; - }; - #endif -}; -typedef struct GLNVGfragUniforms GLNVGfragUniforms; - -struct GLNVGcontext { - GLNVGshader shader; - GLNVGtexture* textures; - float view[2]; - int ntextures; - int ctextures; - int textureId; - GLuint vertBuf; -#if defined NANOVG_GL3 - GLuint vertArr; -#endif -#if NANOVG_GL_USE_UNIFORMBUFFER - GLuint fragBuf; -#endif - int fragSize; - int flags; - - // Per frame buffers - GLNVGcall* calls; - int ccalls; - int ncalls; - GLNVGpath* paths; - int cpaths; - int npaths; - struct NVGvertex* verts; - int cverts; - int nverts; - unsigned char* uniforms; - int cuniforms; - int nuniforms; - - // cached state - #if NANOVG_GL_USE_STATE_FILTER - GLuint boundTexture; - GLuint stencilMask; - GLenum stencilFunc; - GLint stencilFuncRef; - GLuint stencilFuncMask; - GLNVGblend blendFunc; - #endif - - int dummyTex; -}; -typedef struct GLNVGcontext GLNVGcontext; - -static int glnvg__maxi(int a, int b) { return a > b ? a : b; } - -#ifdef NANOVG_GLES2 -static unsigned int glnvg__nearestPow2(unsigned int num) -{ - unsigned n = num > 0 ? num - 1 : 0; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; -} -#endif - -static void glnvg__bindTexture(GLNVGcontext* gl, GLuint tex) -{ -#if NANOVG_GL_USE_STATE_FILTER - if (gl->boundTexture != tex) { - gl->boundTexture = tex; - glBindTexture(GL_TEXTURE_2D, tex); - } -#else - glBindTexture(GL_TEXTURE_2D, tex); -#endif -} - -static void glnvg__stencilMask(GLNVGcontext* gl, GLuint mask) -{ -#if NANOVG_GL_USE_STATE_FILTER - if (gl->stencilMask != mask) { - gl->stencilMask = mask; - glStencilMask(mask); - } -#else - glStencilMask(mask); -#endif -} - -static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint mask) -{ -#if NANOVG_GL_USE_STATE_FILTER - if ((gl->stencilFunc != func) || - (gl->stencilFuncRef != ref) || - (gl->stencilFuncMask != mask)) { - - gl->stencilFunc = func; - gl->stencilFuncRef = ref; - gl->stencilFuncMask = mask; - glStencilFunc(func, ref, mask); - } -#else - glStencilFunc(func, ref, mask); -#endif -} -static void glnvg__blendFuncSeparate(GLNVGcontext* gl, const GLNVGblend* blend) -{ -#if NANOVG_GL_USE_STATE_FILTER - if ((gl->blendFunc.srcRGB != blend->srcRGB) || - (gl->blendFunc.dstRGB != blend->dstRGB) || - (gl->blendFunc.srcAlpha != blend->srcAlpha) || - (gl->blendFunc.dstAlpha != blend->dstAlpha)) { - - gl->blendFunc = *blend; - glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); - } -#else - glBlendFuncSeparate(blend->srcRGB, blend->dstRGB, blend->srcAlpha,blend->dstAlpha); -#endif -} - -static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) -{ - GLNVGtexture* tex = NULL; - int i; - - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == 0) { - tex = &gl->textures[i]; - break; - } - } - if (tex == NULL) { - if (gl->ntextures+1 > gl->ctextures) { - GLNVGtexture* textures; - int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate - textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); - if (textures == NULL) return NULL; - gl->textures = textures; - gl->ctextures = ctextures; - } - tex = &gl->textures[gl->ntextures++]; - } - - memset(tex, 0, sizeof(*tex)); - tex->id = ++gl->textureId; - - return tex; -} - -static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) -{ - int i; - for (i = 0; i < gl->ntextures; i++) - if (gl->textures[i].id == id) - return &gl->textures[i]; - return NULL; -} - -static int glnvg__deleteTexture(GLNVGcontext* gl, int id) -{ - int i; - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].id == id) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); - memset(&gl->textures[i], 0, sizeof(gl->textures[i])); - return 1; - } - } - return 0; -} - -static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) -{ - GLchar str[512+1]; - GLsizei len = 0; - glGetShaderInfoLog(shader, 512, &len, str); - if (len > 512) len = 512; - str[len] = '\0'; - printf("Shader %s/%s error:\n%s\n", name, type, str); -} - -static void glnvg__dumpProgramError(GLuint prog, const char* name) -{ - GLchar str[512+1]; - GLsizei len = 0; - glGetProgramInfoLog(prog, 512, &len, str); - if (len > 512) len = 512; - str[len] = '\0'; - printf("Program %s error:\n%s\n", name, str); -} - -static void glnvg__checkError(GLNVGcontext* gl, const char* str) -{ - GLenum err; - if ((gl->flags & NVG_DEBUG) == 0) return; - err = glGetError(); - if (err != GL_NO_ERROR) { - printf("Error %08x after %s\n", err, str); - return; - } -} - -static int glnvg__createShader(GLNVGshader* shader, const char* name, const char* header, const char* opts, const char* vshader, const char* fshader) -{ - GLint status; - GLuint prog, vert, frag; - const char* str[3]; - str[0] = header; - str[1] = opts != NULL ? opts : ""; - - memset(shader, 0, sizeof(*shader)); - - prog = glCreateProgram(); - vert = glCreateShader(GL_VERTEX_SHADER); - frag = glCreateShader(GL_FRAGMENT_SHADER); - str[2] = vshader; - glShaderSource(vert, 3, str, 0); - str[2] = fshader; - glShaderSource(frag, 3, str, 0); - - glCompileShader(vert); - glGetShaderiv(vert, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpShaderError(vert, name, "vert"); - return 0; - } - - glCompileShader(frag); - glGetShaderiv(frag, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpShaderError(frag, name, "frag"); - return 0; - } - - glAttachShader(prog, vert); - glAttachShader(prog, frag); - - glBindAttribLocation(prog, 0, "vertex"); - glBindAttribLocation(prog, 1, "tcoord"); - - glLinkProgram(prog); - glGetProgramiv(prog, GL_LINK_STATUS, &status); - if (status != GL_TRUE) { - glnvg__dumpProgramError(prog, name); - return 0; - } - - shader->prog = prog; - shader->vert = vert; - shader->frag = frag; - - return 1; -} - -static void glnvg__deleteShader(GLNVGshader* shader) -{ - if (shader->prog != 0) - glDeleteProgram(shader->prog); - if (shader->vert != 0) - glDeleteShader(shader->vert); - if (shader->frag != 0) - glDeleteShader(shader->frag); -} - -static void glnvg__getUniforms(GLNVGshader* shader) -{ - shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); - shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); - -#if NANOVG_GL_USE_UNIFORMBUFFER - shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag"); -#else - shader->loc[GLNVG_LOC_FRAG] = glGetUniformLocation(shader->prog, "frag"); -#endif -} - -static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); - -static int glnvg__renderCreate(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int align = 4; - - // TODO: mediump float may not be enough for GLES2 in iOS. - // see the following discussion: https://github.com/memononen/nanovg/issues/46 - static const char* shaderHeader = -#if defined NANOVG_GL2 - "#define NANOVG_GL2 1\n" -#elif defined NANOVG_GL3 - "#version 150 core\n" - "#define NANOVG_GL3 1\n" -#elif defined NANOVG_GLES2 - "#version 100\n" - "#define NANOVG_GL2 1\n" -#elif defined NANOVG_GLES3 - "#version 300 es\n" - "#define NANOVG_GL3 1\n" -#endif - -#if NANOVG_GL_USE_UNIFORMBUFFER - "#define USE_UNIFORMBUFFER 1\n" -#else - "#define UNIFORMARRAY_SIZE 11\n" -#endif - "\n"; - - static const char* fillVertShader = - "#ifdef NANOVG_GL3\n" - " uniform vec2 viewSize;\n" - " in vec2 vertex;\n" - " in vec2 tcoord;\n" - " out vec2 ftcoord;\n" - " out vec2 fpos;\n" - "#else\n" - " uniform vec2 viewSize;\n" - " attribute vec2 vertex;\n" - " attribute vec2 tcoord;\n" - " varying vec2 ftcoord;\n" - " varying vec2 fpos;\n" - "#endif\n" - "void main(void) {\n" - " ftcoord = tcoord;\n" - " fpos = vertex;\n" - " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" - "}\n"; - - static const char* fillFragShader = - "#ifdef GL_ES\n" - "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" - " precision highp float;\n" - "#else\n" - " precision mediump float;\n" - "#endif\n" - "#endif\n" - "#ifdef NANOVG_GL3\n" - "#ifdef USE_UNIFORMBUFFER\n" - " layout(std140) uniform frag {\n" - " mat3 scissorMat;\n" - " mat3 paintMat;\n" - " vec4 innerCol;\n" - " vec4 outerCol;\n" - " vec2 scissorExt;\n" - " vec2 scissorScale;\n" - " vec2 extent;\n" - " float radius;\n" - " float feather;\n" - " float strokeMult;\n" - " float strokeThr;\n" - " int texType;\n" - " int type;\n" - " };\n" - "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER - " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" - "#endif\n" - " uniform sampler2D tex;\n" - " in vec2 ftcoord;\n" - " in vec2 fpos;\n" - " out vec4 outColor;\n" - "#else\n" // !NANOVG_GL3 - " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" - " uniform sampler2D tex;\n" - " varying vec2 ftcoord;\n" - " varying vec2 fpos;\n" - "#endif\n" - "#ifndef USE_UNIFORMBUFFER\n" - " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" - " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" - " #define innerCol frag[6]\n" - " #define outerCol frag[7]\n" - " #define scissorExt frag[8].xy\n" - " #define scissorScale frag[8].zw\n" - " #define extent frag[9].xy\n" - " #define radius frag[9].z\n" - " #define feather frag[9].w\n" - " #define strokeMult frag[10].x\n" - " #define strokeThr frag[10].y\n" - " #define texType int(frag[10].z)\n" - " #define type int(frag[10].w)\n" - "#endif\n" - "\n" - "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" - " vec2 ext2 = ext - vec2(rad,rad);\n" - " vec2 d = abs(pt) - ext2;\n" - " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" - "}\n" - "\n" - "// Scissoring\n" - "float scissorMask(vec2 p) {\n" - " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" - " sc = vec2(0.5,0.5) - sc * scissorScale;\n" - " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" - "}\n" - "#ifdef EDGE_AA\n" - "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" - "float strokeMask() {\n" - " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" - "}\n" - "#endif\n" - "\n" - "void main(void) {\n" - " vec4 result;\n" - " float scissor = scissorMask(fpos);\n" - "#ifdef EDGE_AA\n" - " float strokeAlpha = strokeMask();\n" - " if (strokeAlpha < strokeThr) discard;\n" - "#else\n" - " float strokeAlpha = 1.0;\n" - "#endif\n" - " if (type == 0) { // Gradient\n" - " // Calculate gradient color using box gradient\n" - " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" - " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" - " vec4 color = mix(innerCol,outerCol,d);\n" - " // Combine alpha\n" - " color *= strokeAlpha * scissor;\n" - " result = color;\n" - " } else if (type == 1) { // Image\n" - " // Calculate color fron texture\n" - " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" - "#ifdef NANOVG_GL3\n" - " vec4 color = texture(tex, pt);\n" - "#else\n" - " vec4 color = texture2D(tex, pt);\n" - "#endif\n" - " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" - " if (texType == 2) color = vec4(color.x);" - " // Apply color tint and alpha.\n" - " color *= innerCol;\n" - " // Combine alpha\n" - " color *= strokeAlpha * scissor;\n" - " result = color;\n" - " } else if (type == 2) { // Stencil fill\n" - " result = vec4(1,1,1,1);\n" - " } else if (type == 3) { // Textured tris\n" - "#ifdef NANOVG_GL3\n" - " vec4 color = texture(tex, ftcoord);\n" - "#else\n" - " vec4 color = texture2D(tex, ftcoord);\n" - "#endif\n" - " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" - " if (texType == 2) color = vec4(color.x);" - " color *= scissor;\n" - " result = color * innerCol;\n" - " }\n" - "#ifdef NANOVG_GL3\n" - " outColor = result;\n" - "#else\n" - " gl_FragColor = result;\n" - "#endif\n" - "}\n"; - - glnvg__checkError(gl, "init"); - - if (gl->flags & NVG_ANTIALIAS) { - if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0) - return 0; - } else { - if (glnvg__createShader(&gl->shader, "shader", shaderHeader, NULL, fillVertShader, fillFragShader) == 0) - return 0; - } - - glnvg__checkError(gl, "uniform locations"); - glnvg__getUniforms(&gl->shader); - - // Create dynamic vertex array -#if defined NANOVG_GL3 - glGenVertexArrays(1, &gl->vertArr); -#endif - glGenBuffers(1, &gl->vertBuf); - -#if NANOVG_GL_USE_UNIFORMBUFFER - // Create UBOs - glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); - glGenBuffers(1, &gl->fragBuf); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); -#endif - gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; - - // Some platforms does not allow to have samples to unset textures. - // Create empty one which is bound when there's no texture specified. - gl->dummyTex = glnvg__renderCreateTexture(gl, NVG_TEXTURE_ALPHA, 1, 1, 0, NULL); - - glnvg__checkError(gl, "create done"); - - glFinish(); - - return 1; -} - -static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__allocTexture(gl); - - if (tex == NULL) return 0; - -#ifdef NANOVG_GLES2 - // Check for non-power of 2. - if (glnvg__nearestPow2(w) != (unsigned int)w || glnvg__nearestPow2(h) != (unsigned int)h) { - // No repeat - if ((imageFlags & NVG_IMAGE_REPEATX) != 0 || (imageFlags & NVG_IMAGE_REPEATY) != 0) { - printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); - imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); - } - // No mips. - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); - imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; - } - } -#endif - - glGenTextures(1, &tex->tex); - tex->width = w; - tex->height = h; - tex->type = type; - tex->flags = imageFlags; - glnvg__bindTexture(gl, tex->tex); - - glPixelStorei(GL_UNPACK_ALIGNMENT,1); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - -#if defined (NANOVG_GL2) - // GL 1.4 and later has support for generating mipmaps using a tex parameter. - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - } -#endif - - if (type == NVG_TEXTURE_RGBA) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - else -#if defined(NANOVG_GLES2) || defined (NANOVG_GL2) - glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); -#elif defined(NANOVG_GLES3) - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); -#else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); -#endif - - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - } else { - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - } - - if (imageFlags & NVG_IMAGE_NEAREST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - if (imageFlags & NVG_IMAGE_REPEATX) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - - if (imageFlags & NVG_IMAGE_REPEATY) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - - // The new way to build mipmaps on GLES and GL3 -#if !defined(NANOVG_GL2) - if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { - glGenerateMipmap(GL_TEXTURE_2D); - } -#endif - - glnvg__checkError(gl, "create tex"); - glnvg__bindTexture(gl, 0); - - return tex->id; -} - - -static int glnvg__renderDeleteTexture(void* uptr, int image) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - return glnvg__deleteTexture(gl, image); -} - -static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - - if (tex == NULL) return 0; - glnvg__bindTexture(gl, tex->tex); - - glPixelStorei(GL_UNPACK_ALIGNMENT,1); - -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); - glPixelStorei(GL_UNPACK_SKIP_ROWS, y); -#else - // No support for all of skip, need to update a whole row at a time. - if (tex->type == NVG_TEXTURE_RGBA) - data += y*tex->width*4; - else - data += y*tex->width; - x = 0; - w = tex->width; -#endif - - if (tex->type == NVG_TEXTURE_RGBA) - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); - else -#if defined(NANOVG_GLES2) || defined(NANOVG_GL2) - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); -#else - glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); -#endif - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); -#ifndef NANOVG_GLES2 - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); -#endif - - glnvg__bindTexture(gl, 0); - - return 1; -} - -static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - if (tex == NULL) return 0; - *w = tex->width; - *h = tex->height; - return 1; -} - -static void glnvg__xformToMat3x4(float* m3, float* t) -{ - m3[0] = t[0]; - m3[1] = t[1]; - m3[2] = 0.0f; - m3[3] = 0.0f; - m3[4] = t[2]; - m3[5] = t[3]; - m3[6] = 0.0f; - m3[7] = 0.0f; - m3[8] = t[4]; - m3[9] = t[5]; - m3[10] = 1.0f; - m3[11] = 0.0f; -} - -static NVGcolor glnvg__premulColor(NVGcolor c) -{ - c.r *= c.a; - c.g *= c.a; - c.b *= c.a; - return c; -} - -static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint, - NVGscissor* scissor, float width, float fringe, float strokeThr) -{ - GLNVGtexture* tex = NULL; - float invxform[6]; - - memset(frag, 0, sizeof(*frag)); - - frag->innerCol = glnvg__premulColor(paint->innerColor); - frag->outerCol = glnvg__premulColor(paint->outerColor); - - if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { - memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); - frag->scissorExt[0] = 1.0f; - frag->scissorExt[1] = 1.0f; - frag->scissorScale[0] = 1.0f; - frag->scissorScale[1] = 1.0f; - } else { - nvgTransformInverse(invxform, scissor->xform); - glnvg__xformToMat3x4(frag->scissorMat, invxform); - frag->scissorExt[0] = scissor->extent[0]; - frag->scissorExt[1] = scissor->extent[1]; - frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; - frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; - } - - memcpy(frag->extent, paint->extent, sizeof(frag->extent)); - frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; - frag->strokeThr = strokeThr; - - if (paint->image != 0) { - tex = glnvg__findTexture(gl, paint->image); - if (tex == NULL) return 0; - if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { - float m1[6], m2[6]; - nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f); - nvgTransformMultiply(m1, paint->xform); - nvgTransformScale(m2, 1.0f, -1.0f); - nvgTransformMultiply(m2, m1); - nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f); - nvgTransformMultiply(m1, m2); - nvgTransformInverse(invxform, m1); - } else { - nvgTransformInverse(invxform, paint->xform); - } - frag->type = NSVG_SHADER_FILLIMG; - - #if NANOVG_GL_USE_UNIFORMBUFFER - if (tex->type == NVG_TEXTURE_RGBA) - frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; - else - frag->texType = 2; - #else - if (tex->type == NVG_TEXTURE_RGBA) - frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; - else - frag->texType = 2.0f; - #endif -// printf("frag->texType = %d\n", frag->texType); - } else { - frag->type = NSVG_SHADER_FILLGRAD; - frag->radius = paint->radius; - frag->feather = paint->feather; - nvgTransformInverse(invxform, paint->xform); - } - - glnvg__xformToMat3x4(frag->paintMat, invxform); - - return 1; -} - -static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); - -static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) -{ - GLNVGtexture* tex = NULL; -#if NANOVG_GL_USE_UNIFORMBUFFER - glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); -#else - GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset); - glUniform4fv(gl->shader.loc[GLNVG_LOC_FRAG], NANOVG_GL_UNIFORMARRAY_SIZE, &(frag->uniformArray[0][0])); -#endif - - if (image != 0) { - tex = glnvg__findTexture(gl, image); - } - // If no image is set, use empty texture - if (tex == NULL) { - tex = glnvg__findTexture(gl, gl->dummyTex); - } - glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); - glnvg__checkError(gl, "tex paint tex"); -} - -static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio) -{ - NVG_NOTUSED(devicePixelRatio); - GLNVGcontext* gl = (GLNVGcontext*)uptr; - gl->view[0] = width; - gl->view[1] = height; -} - -static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int i, npaths = call->pathCount; - - // Draw shapes - glEnable(GL_STENCIL_TEST); - glnvg__stencilMask(gl, 0xff); - glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - - // set bindpoint for solid loc - glnvg__setUniforms(gl, call->uniformOffset, 0); - glnvg__checkError(gl, "fill simple"); - - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); - glDisable(GL_CULL_FACE); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); - glEnable(GL_CULL_FACE); - - // Draw anti-aliased pixels - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); - glnvg__checkError(gl, "fill fill"); - - if (gl->flags & NVG_ANTIALIAS) { - glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - // Draw fringes - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } - - // Draw fill - glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount); - - glDisable(GL_STENCIL_TEST); -} - -static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int i, npaths = call->pathCount; - - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "convex fill"); - - for (i = 0; i < npaths; i++) { - glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); - // Draw fringes - if (paths[i].strokeCount > 0) { - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } - } -} - -static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) -{ - GLNVGpath* paths = &gl->paths[call->pathOffset]; - int npaths = call->pathCount, i; - - if (gl->flags & NVG_STENCIL_STROKES) { - - glEnable(GL_STENCIL_TEST); - glnvg__stencilMask(gl, 0xff); - - // Fill the stroke base without overlap - glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); - glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); - glnvg__checkError(gl, "stroke fill 0"); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - - // Draw anti-aliased pixels. - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - - // Clear stencil buffer. - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); - glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); - glnvg__checkError(gl, "stroke fill 1"); - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glDisable(GL_STENCIL_TEST); - -// glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); - - } else { - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "stroke fill"); - // Draw Strokes - for (i = 0; i < npaths; i++) - glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); - } -} - -static void glnvg__triangles(GLNVGcontext* gl, GLNVGcall* call) -{ - glnvg__setUniforms(gl, call->uniformOffset, call->image); - glnvg__checkError(gl, "triangles fill"); - - glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); -} - -static void glnvg__renderCancel(void* uptr) { - GLNVGcontext* gl = (GLNVGcontext*)uptr; - gl->nverts = 0; - gl->npaths = 0; - gl->ncalls = 0; - gl->nuniforms = 0; -} - -static GLenum glnvg_convertBlendFuncFactor(int factor) -{ - if (factor == NVG_ZERO) - return GL_ZERO; - if (factor == NVG_ONE) - return GL_ONE; - if (factor == NVG_SRC_COLOR) - return GL_SRC_COLOR; - if (factor == NVG_ONE_MINUS_SRC_COLOR) - return GL_ONE_MINUS_SRC_COLOR; - if (factor == NVG_DST_COLOR) - return GL_DST_COLOR; - if (factor == NVG_ONE_MINUS_DST_COLOR) - return GL_ONE_MINUS_DST_COLOR; - if (factor == NVG_SRC_ALPHA) - return GL_SRC_ALPHA; - if (factor == NVG_ONE_MINUS_SRC_ALPHA) - return GL_ONE_MINUS_SRC_ALPHA; - if (factor == NVG_DST_ALPHA) - return GL_DST_ALPHA; - if (factor == NVG_ONE_MINUS_DST_ALPHA) - return GL_ONE_MINUS_DST_ALPHA; - if (factor == NVG_SRC_ALPHA_SATURATE) - return GL_SRC_ALPHA_SATURATE; - return GL_INVALID_ENUM; -} - -static GLNVGblend glnvg__blendCompositeOperation(NVGcompositeOperationState op) -{ - GLNVGblend blend; - blend.srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB); - blend.dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB); - blend.srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha); - blend.dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha); - if (blend.srcRGB == GL_INVALID_ENUM || blend.dstRGB == GL_INVALID_ENUM || blend.srcAlpha == GL_INVALID_ENUM || blend.dstAlpha == GL_INVALID_ENUM) - { - blend.srcRGB = GL_ONE; - blend.dstRGB = GL_ONE_MINUS_SRC_ALPHA; - blend.srcAlpha = GL_ONE; - blend.dstAlpha = GL_ONE_MINUS_SRC_ALPHA; - } - return blend; -} - -static void glnvg__renderFlush(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int i; - - if (gl->ncalls > 0) { - - // Setup require GL state. - glUseProgram(gl->shader.prog); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glFrontFace(GL_CCW); - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glStencilMask(0xffffffff); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_ALWAYS, 0, 0xffffffff); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, 0); - #if NANOVG_GL_USE_STATE_FILTER - gl->boundTexture = 0; - gl->stencilMask = 0xffffffff; - gl->stencilFunc = GL_ALWAYS; - gl->stencilFuncRef = 0; - gl->stencilFuncMask = 0xffffffff; - gl->blendFunc.srcRGB = GL_INVALID_ENUM; - gl->blendFunc.srcAlpha = GL_INVALID_ENUM; - gl->blendFunc.dstRGB = GL_INVALID_ENUM; - gl->blendFunc.dstAlpha = GL_INVALID_ENUM; - #endif - -#if NANOVG_GL_USE_UNIFORMBUFFER - // Upload ubo for frag shaders - glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); - glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW); -#endif - - // Upload vertex data -#if defined NANOVG_GL3 - glBindVertexArray(gl->vertArr); -#endif - glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); - glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(NVGvertex), gl->verts, GL_STREAM_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); - - // Set view and texture just once per frame. - glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); - glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view); - -#if NANOVG_GL_USE_UNIFORMBUFFER - glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); -#endif - - for (i = 0; i < gl->ncalls; i++) { - GLNVGcall* call = &gl->calls[i]; - glnvg__blendFuncSeparate(gl,&call->blendFunc); - if (call->type == GLNVG_FILL) - glnvg__fill(gl, call); - else if (call->type == GLNVG_CONVEXFILL) - glnvg__convexFill(gl, call); - else if (call->type == GLNVG_STROKE) - glnvg__stroke(gl, call); - else if (call->type == GLNVG_TRIANGLES) - glnvg__triangles(gl, call); - } - - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); -#if defined NANOVG_GL3 - glBindVertexArray(0); -#endif - glDisable(GL_CULL_FACE); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glUseProgram(0); - glnvg__bindTexture(gl, 0); - } - - // Reset calls - gl->nverts = 0; - gl->npaths = 0; - gl->ncalls = 0; - gl->nuniforms = 0; -} - -static int glnvg__maxVertCount(const NVGpath* paths, int npaths) -{ - int i, count = 0; - for (i = 0; i < npaths; i++) { - count += paths[i].nfill; - count += paths[i].nstroke; - } - return count; -} - -static GLNVGcall* glnvg__allocCall(GLNVGcontext* gl) -{ - GLNVGcall* ret = NULL; - if (gl->ncalls+1 > gl->ccalls) { - GLNVGcall* calls; - int ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls/2; // 1.5x Overallocate - calls = (GLNVGcall*)realloc(gl->calls, sizeof(GLNVGcall) * ccalls); - if (calls == NULL) return NULL; - gl->calls = calls; - gl->ccalls = ccalls; - } - ret = &gl->calls[gl->ncalls++]; - memset(ret, 0, sizeof(GLNVGcall)); - return ret; -} - -static int glnvg__allocPaths(GLNVGcontext* gl, int n) -{ - int ret = 0; - if (gl->npaths+n > gl->cpaths) { - GLNVGpath* paths; - int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths/2; // 1.5x Overallocate - paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths); - if (paths == NULL) return -1; - gl->paths = paths; - gl->cpaths = cpaths; - } - ret = gl->npaths; - gl->npaths += n; - return ret; -} - -static int glnvg__allocVerts(GLNVGcontext* gl, int n) -{ - int ret = 0; - if (gl->nverts+n > gl->cverts) { - NVGvertex* verts; - int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate - verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts); - if (verts == NULL) return -1; - gl->verts = verts; - gl->cverts = cverts; - } - ret = gl->nverts; - gl->nverts += n; - return ret; -} - -static int glnvg__allocFragUniforms(GLNVGcontext* gl, int n) -{ - int ret = 0, structSize = gl->fragSize; - if (gl->nuniforms+n > gl->cuniforms) { - unsigned char* uniforms; - int cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate - uniforms = (unsigned char*)realloc(gl->uniforms, structSize * cuniforms); - if (uniforms == NULL) return -1; - gl->uniforms = uniforms; - gl->cuniforms = cuniforms; - } - ret = gl->nuniforms * structSize; - gl->nuniforms += n; - return ret; -} - -static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i) -{ - return (GLNVGfragUniforms*)&gl->uniforms[i]; -} - -static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) -{ - vtx->x = x; - vtx->y = y; - vtx->u = u; - vtx->v = v; -} - -static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, - const float* bounds, const NVGpath* paths, int npaths) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - NVGvertex* quad; - GLNVGfragUniforms* frag; - int i, maxverts, offset; - - if (call == NULL) return; - - call->type = GLNVG_FILL; - call->triangleCount = 4; - call->pathOffset = glnvg__allocPaths(gl, npaths); - if (call->pathOffset == -1) goto error; - call->pathCount = npaths; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - if (npaths == 1 && paths[0].convex) - { - call->type = GLNVG_CONVEXFILL; - call->triangleCount = 0; // Bounding box fill quad not needed for convex fill - } - - // Allocate vertices for all the paths. - maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount; - offset = glnvg__allocVerts(gl, maxverts); - if (offset == -1) goto error; - - for (i = 0; i < npaths; i++) { - GLNVGpath* copy = &gl->paths[call->pathOffset + i]; - const NVGpath* path = &paths[i]; - memset(copy, 0, sizeof(GLNVGpath)); - if (path->nfill > 0) { - copy->fillOffset = offset; - copy->fillCount = path->nfill; - memcpy(&gl->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); - offset += path->nfill; - } - if (path->nstroke > 0) { - copy->strokeOffset = offset; - copy->strokeCount = path->nstroke; - memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); - offset += path->nstroke; - } - } - - // Setup uniforms for draw calls - if (call->type == GLNVG_FILL) { - // Quad - call->triangleOffset = offset; - quad = &gl->verts[call->triangleOffset]; - glnvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); - glnvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); - glnvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); - glnvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); - - call->uniformOffset = glnvg__allocFragUniforms(gl, 2); - if (call->uniformOffset == -1) goto error; - // Simple shader for stencil - frag = nvg__fragUniformPtr(gl, call->uniformOffset); - memset(frag, 0, sizeof(*frag)); - frag->strokeThr = -1.0f; - frag->type = NSVG_SHADER_SIMPLE; - // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f); - } else { - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - // Fill shader - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); - } - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, - float strokeWidth, const NVGpath* paths, int npaths) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - int i, maxverts, offset; - - if (call == NULL) return; - - call->type = GLNVG_STROKE; - call->pathOffset = glnvg__allocPaths(gl, npaths); - if (call->pathOffset == -1) goto error; - call->pathCount = npaths; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - // Allocate vertices for all the paths. - maxverts = glnvg__maxVertCount(paths, npaths); - offset = glnvg__allocVerts(gl, maxverts); - if (offset == -1) goto error; - - for (i = 0; i < npaths; i++) { - GLNVGpath* copy = &gl->paths[call->pathOffset + i]; - const NVGpath* path = &paths[i]; - memset(copy, 0, sizeof(GLNVGpath)); - if (path->nstroke) { - copy->strokeOffset = offset; - copy->strokeCount = path->nstroke; - memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); - offset += path->nstroke; - } - } - - if (gl->flags & NVG_STENCIL_STROKES) { - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 2); - if (call->uniformOffset == -1) goto error; - - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); - - } else { - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); - } - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, - const NVGvertex* verts, int nverts, float fringe) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - GLNVGcall* call = glnvg__allocCall(gl); - GLNVGfragUniforms* frag; - - if (call == NULL) return; - - call->type = GLNVG_TRIANGLES; - call->image = paint->image; - call->blendFunc = glnvg__blendCompositeOperation(compositeOperation); - - // Allocate vertices for all the paths. - call->triangleOffset = glnvg__allocVerts(gl, nverts); - if (call->triangleOffset == -1) goto error; - call->triangleCount = nverts; - - memcpy(&gl->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); - - // Fill shader - call->uniformOffset = glnvg__allocFragUniforms(gl, 1); - if (call->uniformOffset == -1) goto error; - frag = nvg__fragUniformPtr(gl, call->uniformOffset); - glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f); - frag->type = NSVG_SHADER_IMG; - - return; - -error: - // We get here if call alloc was ok, but something else is not. - // Roll back the last call to prevent drawing it. - if (gl->ncalls > 0) gl->ncalls--; -} - -static void glnvg__renderDelete(void* uptr) -{ - GLNVGcontext* gl = (GLNVGcontext*)uptr; - int i; - if (gl == NULL) return; - - glnvg__deleteShader(&gl->shader); - -#if NANOVG_GL3 -#if NANOVG_GL_USE_UNIFORMBUFFER - if (gl->fragBuf != 0) - glDeleteBuffers(1, &gl->fragBuf); -#endif - if (gl->vertArr != 0) - glDeleteVertexArrays(1, &gl->vertArr); -#endif - if (gl->vertBuf != 0) - glDeleteBuffers(1, &gl->vertBuf); - - for (i = 0; i < gl->ntextures; i++) { - if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) - glDeleteTextures(1, &gl->textures[i].tex); - } - free(gl->textures); - - free(gl->paths); - free(gl->verts); - free(gl->uniforms); - free(gl->calls); - - free(gl); -} - - -#if defined NANOVG_GL2 -NVGcontext* nvgCreateGL2(int flags) -#elif defined NANOVG_GL3 -NVGcontext* nvgCreateGL3(int flags) -#elif defined NANOVG_GLES2 -NVGcontext* nvgCreateGLES2(int flags) -#elif defined NANOVG_GLES3 -NVGcontext* nvgCreateGLES3(int flags) -#endif -{ - #ifdef NANOVG_USE_GLEW - if (!glew_initialized) { - if (glewInit() == GLEW_OK) { - glew_initialized = 1; - } else { - printf("Failed to initialize GLEW"); - // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. - glGetError(); - } - } - #endif - - NVGparams params; - NVGcontext* ctx = NULL; - GLNVGcontext* gl = (GLNVGcontext*)malloc(sizeof(GLNVGcontext)); - if (gl == NULL) goto error; - memset(gl, 0, sizeof(GLNVGcontext)); - - memset(¶ms, 0, sizeof(params)); - params.renderCreate = glnvg__renderCreate; - params.renderCreateTexture = glnvg__renderCreateTexture; - params.renderDeleteTexture = glnvg__renderDeleteTexture; - params.renderUpdateTexture = glnvg__renderUpdateTexture; - params.renderGetTextureSize = glnvg__renderGetTextureSize; - params.renderViewport = glnvg__renderViewport; - params.renderCancel = glnvg__renderCancel; - params.renderFlush = glnvg__renderFlush; - params.renderFill = glnvg__renderFill; - params.renderStroke = glnvg__renderStroke; - params.renderTriangles = glnvg__renderTriangles; - params.renderDelete = glnvg__renderDelete; - params.userPtr = gl; - params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; - - gl->flags = flags; - - ctx = nvgCreateInternal(¶ms); - if (ctx == NULL) goto error; - - return ctx; - -error: - // 'gl' is freed by nvgDeleteInternal. - if (ctx != NULL) nvgDeleteInternal(ctx); - return NULL; -} - -#if defined NANOVG_GL2 -void nvgDeleteGL2(NVGcontext* ctx) -#elif defined NANOVG_GL3 -void nvgDeleteGL3(NVGcontext* ctx) -#elif defined NANOVG_GLES2 -void nvgDeleteGLES2(NVGcontext* ctx) -#elif defined NANOVG_GLES3 -void nvgDeleteGLES3(NVGcontext* ctx) -#endif -{ - nvgDeleteInternal(ctx); -} - -#if defined NANOVG_GL2 -int nvglCreateImageFromHandleGL2(NVGcontext* ctx, unsigned int textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GL3 -int nvglCreateImageFromHandleGL3(NVGcontext* ctx, unsigned int textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GLES2 -int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, unsigned int textureId, int w, int h, int imageFlags) -#elif defined NANOVG_GLES3 -int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, unsigned int textureId, int w, int h, int imageFlags) -#endif -{ - GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; - GLNVGtexture* tex = glnvg__allocTexture(gl); - - if (tex == NULL) return 0; - - tex->type = NVG_TEXTURE_RGBA; - tex->tex = textureId; - tex->flags = imageFlags; - tex->width = w; - tex->height = h; - - return tex->id; -} - -#if defined NANOVG_GL2 -unsigned int nvglImageHandleGL2(NVGcontext* ctx, int image) -#elif defined NANOVG_GL3 -unsigned int nvglImageHandleGL3(NVGcontext* ctx, int image) -#elif defined NANOVG_GLES2 -unsigned int nvglImageHandleGLES2(NVGcontext* ctx, int image) -#elif defined NANOVG_GLES3 -unsigned int nvglImageHandleGLES3(NVGcontext* ctx, int image) -#endif -{ - GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; - GLNVGtexture* tex = glnvg__findTexture(gl, image); - return tex->tex; -} - -#if defined NANOVG_GL2 -const NanoVG_GL_Functions_VTable NanoVG_GL2_Functions_VTable = { - .name = "GL2", - .createContext = &nvgCreateGL2, - .deleteContext = &nvgDeleteGL2, - .createImageFromHandle = &nvglCreateImageFromHandleGL2, - .getImageHandle = &nvglImageHandleGL2, -}; -#elif defined NANOVG_GL3 -const NanoVG_GL_Functions_VTable NanoVG_GL3_Functions_VTable = { - .name = "GL3", - .createContext = &nvgCreateGL3, - .deleteContext = &nvgDeleteGL3, - .createImageFromHandle = &nvglCreateImageFromHandleGL3, - .getImageHandle = &nvglImageHandleGL3, -}; -#elif defined NANOVG_GLES2 -const NanoVG_GL_Functions_VTable NanoVG_GLES2_Functions_VTable = { - .name = "GLES2", - .createContext = &nvgCreateGLES2, - .deleteContext = &nvgDeleteGLES2, - .createImageFromHandle = &nvglCreateImageFromHandleGLES2, - .getImageHandle = &nvglImageHandleGLES2, -}; -#elif defined NANOVG_GLES3 -const NanoVG_GL_Functions_VTable NanoVG_GLES3_Functions_VTable = { - .name = "GLES2", - .createContext = &nvgCreateGLES3, - .deleteContext = &nvgDeleteGLES3, - .createImageFromHandle = &nvglCreateImageFromHandleGLES3, - .getImageHandle = &nvglImageHandleGLES3, -}; -#endif diff --git a/nanovg/nanovg_gl.h b/nanovg/nanovg_gl.h deleted file mode 100644 index 9f90d8f..0000000 --- a/nanovg/nanovg_gl.h +++ /dev/null @@ -1,92 +0,0 @@ -// -// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef NANOVG_GL_H_F380EF20_CDA3_11EA_AF55_D3B6DB88CFCC -#define NANOVG_GL_H_F380EF20_CDA3_11EA_AF55_D3B6DB88CFCC - -#include "nanovg.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Create flags - -enum NVGcreateFlags { - // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). - NVG_ANTIALIAS = 1<<0, - // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little - // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. - NVG_STENCIL_STROKES = 1<<1, - // Flag indicating that additional debug checks are done. - NVG_DEBUG = 1<<2, -}; - -// Define VTable with pointers to the functions for a each OpenGL (ES) version. - -typedef struct { - const char *name; - NVGcontext* (*createContext)(int flags); - void (*deleteContext) (NVGcontext* ctx); - int (*createImageFromHandle) (NVGcontext* ctx, unsigned int textureId, int w, int h, int flags); - unsigned int (*getImageHandle) (NVGcontext* ctx, int image); -} NanoVG_GL_Functions_VTable; - -// Create NanoVG contexts for different OpenGL (ES) versions. - -NVGcontext* nvgCreateGL2(int flags); -void nvgDeleteGL2(NVGcontext* ctx); - -int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGL2(NVGcontext* ctx, int image); - -NVGcontext* nvgCreateGL3(int flags); -void nvgDeleteGL3(NVGcontext* ctx); - -int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGL3(NVGcontext* ctx, int image); - -NVGcontext* nvgCreateGLES2(int flags); -void nvgDeleteGLES2(NVGcontext* ctx); - -int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image); - -NVGcontext* nvgCreateGLES3(int flags); -void nvgDeleteGLES3(NVGcontext* ctx); - -int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); -GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image); - -// These are additional flags on top of NVGimageFlags. -enum NVGimageFlagsGL { - NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. -}; - -// Create VTables for different OpenGL (ES) versions. - -extern const NanoVG_GL_Functions_VTable NanoVG_GL2_Functions_VTable; -extern const NanoVG_GL_Functions_VTable NanoVG_GL3_Functions_VTable; -extern const NanoVG_GL_Functions_VTable NanoVG_GLES2_Functions_VTable; -extern const NanoVG_GL_Functions_VTable NanoVG_GLES3_Functions_VTable; - -#ifdef __cplusplus -} -#endif - -#endif // NANOVG_GL_H_F380EF20_CDA3_11EA_AF55_D3B6DB88CFCC |