Browse Source

Merge pull request #229 from jpcima/update-nanovg

Update NanoVG
pull/264/head
JP Cimalando GitHub 4 years ago
parent
commit
6ec074d14d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 9945 additions and 3477 deletions
  1. +9
    -1
      dgl/src/NanoVG.cpp
  2. +176
    -62
      dgl/src/nanovg/fontstash.h
  3. +268
    -94
      dgl/src/nanovg/nanovg.c
  4. +95
    -18
      dgl/src/nanovg/nanovg.h
  5. +197
    -62
      dgl/src/nanovg/nanovg_gl.h
  6. +29
    -7
      dgl/src/nanovg/nanovg_gl_utils.h
  7. +4160
    -1152
      dgl/src/nanovg/stb_image.h
  8. +5011
    -2081
      dgl/src/nanovg/stb_truetype.h

+ 9
- 1
dgl/src/NanoVG.cpp View File

@@ -66,15 +66,23 @@ DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer)
#if defined(NANOVG_GL2) #if defined(NANOVG_GL2)
# define nvgCreateGL nvgCreateGL2 # define nvgCreateGL nvgCreateGL2
# define nvgDeleteGL nvgDeleteGL2 # define nvgDeleteGL nvgDeleteGL2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL2
# define nvglImageHandle nvglImageHandleGL2
#elif defined(NANOVG_GL3) #elif defined(NANOVG_GL3)
# define nvgCreateGL nvgCreateGL3 # define nvgCreateGL nvgCreateGL3
# define nvgDeleteGL nvgDeleteGL3 # define nvgDeleteGL nvgDeleteGL3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGL3
# define nvglImageHandle nvglImageHandleGL3
#elif defined(NANOVG_GLES2) #elif defined(NANOVG_GLES2)
# define nvgCreateGL nvgCreateGLES2 # define nvgCreateGL nvgCreateGLES2
# define nvgDeleteGL nvgDeleteGLES2 # define nvgDeleteGL nvgDeleteGLES2
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES2
# define nvglImageHandle nvglImageHandleGLES2
#elif defined(NANOVG_GLES3) #elif defined(NANOVG_GLES3)
# define nvgCreateGL nvgCreateGLES3 # define nvgCreateGL nvgCreateGLES3
# define nvgDeleteGL nvgDeleteGLES3 # define nvgDeleteGL nvgDeleteGLES3
# define nvglCreateImageFromHandle nvglCreateImageFromHandleGLES3
# define nvglImageHandle nvglImageHandleGLES3
#endif #endif


static NVGcontext* nvgCreateGL_helper(int flags) static NVGcontext* nvgCreateGL_helper(int flags)
@@ -922,7 +930,7 @@ void NanoVG::loadSharedResources()


using namespace dpf_resources; using namespace dpf_resources;


nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0);
} }
#endif #endif




+ 176
- 62
dgl/src/nanovg/fontstash.h View File

@@ -38,10 +38,15 @@ enum FONSalign {
FONS_ALIGN_BASELINE = 1<<6, // Default FONS_ALIGN_BASELINE = 1<<6, // Default
}; };


enum FONSglyphBitmap {
FONS_GLYPH_BITMAP_OPTIONAL = 1,
FONS_GLYPH_BITMAP_REQUIRED = 2,
};

enum FONSerrorCode { enum FONSerrorCode {
// Font atlas is full. // Font atlas is full.
FONS_ATLAS_FULL = 1, 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.
// 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, FONS_SCRATCH_FULL = 2,
// Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
FONS_STATES_OVERFLOW = 3, FONS_STATES_OVERFLOW = 3,
@@ -78,6 +83,7 @@ struct FONStextIter {
const char* next; const char* next;
const char* end; const char* end;
unsigned int utf8state; unsigned int utf8state;
int bitmapOption;
}; };
typedef struct FONStextIter FONStextIter; typedef struct FONStextIter FONStextIter;


@@ -90,14 +96,14 @@ void fonsDeleteInternal(FONScontext* s);
void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);
// Returns current atlas size. // Returns current atlas size.
void fonsGetAtlasSize(FONScontext* s, int* width, int* height); void fonsGetAtlasSize(FONScontext* s, int* width, int* height);
// Expands the atlas size.
// Expands the atlas size.
int fonsExpandAtlas(FONScontext* s, int width, int height); int fonsExpandAtlas(FONScontext* s, int width, int height);
// Resets the whole stash. // Resets the whole stash.
int fonsResetAtlas(FONScontext* stash, int width, int height); int fonsResetAtlas(FONScontext* stash, int width, int height);


// Add fonts // Add fonts
int fonsAddFont(FONScontext* s, const char* name, const char* path);
int fonsAddFontMem(FONScontext* s, const char* name, const unsigned char* data, int ndata, int freeData);
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); int fonsGetFontByName(FONScontext* s, const char* name);


// State handling // State handling
@@ -122,7 +128,7 @@ void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);
void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh);


// Text iterator // Text iterator
int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end);
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); int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);


// Pull texture changes // Pull texture changes
@@ -154,20 +160,28 @@ typedef struct FONSttFontImpl FONSttFontImpl;
static FT_Library ftLibrary; static FT_Library ftLibrary;


int fons__tt_init(FONScontext *context) int fons__tt_init(FONScontext *context)
{
{
FT_Error ftError; FT_Error ftError;
FONS_NOTUSED(context);
FONS_NOTUSED(context);
ftError = FT_Init_FreeType(&ftLibrary); ftError = FT_Init_FreeType(&ftLibrary);
return ftError == 0; return ftError == 0;
} }


int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
int fons__tt_done(FONScontext *context)
{
FT_Error ftError;
FONS_NOTUSED(context);
ftError = FT_Done_FreeType(ftLibrary);
return ftError == 0;
}

int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex)
{ {
FT_Error ftError; FT_Error ftError;
FONS_NOTUSED(context); FONS_NOTUSED(context);


//font->font.userdata = stash; //font->font.userdata = stash;
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);
ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font);
return ftError == 0; return ftError == 0;
} }


@@ -180,7 +194,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i


float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{ {
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return size / (font->font->ascender - font->font->descender); return size / (font->font->ascender - font->font->descender);
#else
return size / font->font->units_per_EM;
#endif
} }


int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
@@ -193,16 +212,28 @@ int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float
{ {
FT_Error ftError; FT_Error ftError;
FT_GlyphSlot ftGlyph; FT_GlyphSlot ftGlyph;
FT_Fixed advFixed;
FONS_NOTUSED(scale); FONS_NOTUSED(scale);


#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));
#else
ftError = FT_Set_Pixel_Sizes(font->font, 0, size);
#endif
if (ftError) return 0; if (ftError) return 0;
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
#else
ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
#endif
if (ftError) return 0; if (ftError) return 0;
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance);
ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
if (ftError) return 0; if (ftError) return 0;
ftGlyph = font->font->glyph; ftGlyph = font->font->glyph;
*lsb = ftGlyph->metrics.horiBearingX;
*advance = (int)advFixed;
*lsb = (int)ftGlyph->metrics.horiBearingX;
*x0 = ftGlyph->bitmap_left; *x0 = ftGlyph->bitmap_left;
*x1 = *x0 + ftGlyph->bitmap.width; *x1 = *x0 + ftGlyph->bitmap.width;
*y0 = -ftGlyph->bitmap_top; *y0 = -ftGlyph->bitmap_top;
@@ -215,7 +246,7 @@ void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int
{ {
FT_GlyphSlot ftGlyph = font->font->glyph; FT_GlyphSlot ftGlyph = font->font->glyph;
int ftGlyphOffset = 0; int ftGlyphOffset = 0;
int x, y;
unsigned int x, y;
FONS_NOTUSED(outWidth); FONS_NOTUSED(outWidth);
FONS_NOTUSED(outHeight); FONS_NOTUSED(outHeight);
FONS_NOTUSED(scaleX); FONS_NOTUSED(scaleX);
@@ -233,7 +264,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
{ {
FT_Vector ftKerning; FT_Vector ftKerning;
FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
return ftKerning.x;
return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer
} }


#else #else
@@ -256,13 +287,24 @@ int fons__tt_init(FONScontext *context)
return 1; return 1;
} }


int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, const unsigned char *data, int dataSize)
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 stbError;
int offset, stbError;
FONS_NOTUSED(dataSize); FONS_NOTUSED(dataSize);


font->font.userdata = context; font->font.userdata = context;
stbError = stbtt_InitFont(&font->font, data, 0);
offset = stbtt_GetFontOffsetForIndex(data, fontIndex);
if (offset == -1) {
stbError = 0;
} else {
stbError = stbtt_InitFont(&font->font, data, offset);
}
return stbError; return stbError;
} }


@@ -273,7 +315,12 @@ void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, i


float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)
{ {
#if 1
// Note(DPF) maintain pixel-based units for compat after nanovg update
return stbtt_ScaleForPixelHeight(&font->font, size); return stbtt_ScaleForPixelHeight(&font->font, size);
#else
return stbtt_ScaleForMappingEmToPixels(&font->font, size);
#endif
} }


int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)
@@ -304,7 +351,7 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
#endif #endif


#ifndef FONS_SCRATCH_BUF_SIZE #ifndef FONS_SCRATCH_BUF_SIZE
# define FONS_SCRATCH_BUF_SIZE 16000
# define FONS_SCRATCH_BUF_SIZE 96000
#endif #endif
#ifndef FONS_HASH_LUT_SIZE #ifndef FONS_HASH_LUT_SIZE
# define FONS_HASH_LUT_SIZE 256 # define FONS_HASH_LUT_SIZE 256
@@ -324,6 +371,9 @@ int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)
#ifndef FONS_MAX_STATES #ifndef FONS_MAX_STATES
# define FONS_MAX_STATES 20 # define FONS_MAX_STATES 20
#endif #endif
#ifndef FONS_MAX_FALLBACKS
# define FONS_MAX_FALLBACKS 20
#endif


static unsigned int fons__hashint(unsigned int a) static unsigned int fons__hashint(unsigned int a)
{ {
@@ -361,7 +411,7 @@ struct FONSfont
{ {
FONSttFontImpl font; FONSttFontImpl font;
char name[64]; char name[64];
const unsigned char* data;
unsigned char* data;
int dataSize; int dataSize;
unsigned char freeData; unsigned char freeData;
float ascender; float ascender;
@@ -371,6 +421,8 @@ struct FONSfont
int cglyphs; int cglyphs;
int nglyphs; int nglyphs;
int lut[FONS_HASH_LUT_SIZE]; int lut[FONS_HASH_LUT_SIZE];
int fallbacks[FONS_MAX_FALLBACKS];
int nfallbacks;
}; };
typedef struct FONSfont FONSfont; typedef struct FONSfont FONSfont;


@@ -421,6 +473,8 @@ struct FONScontext
void* errorUptr; void* errorUptr;
}; };


#ifdef STB_TRUETYPE_IMPLEMENTATION

static void* fons__tmpalloc(size_t size, void* up) static void* fons__tmpalloc(size_t size, void* up)
{ {
unsigned char* ptr; unsigned char* ptr;
@@ -446,6 +500,8 @@ static void fons__tmpfree(void* ptr, void* up)
// empty // empty
} }


#endif // STB_TRUETYPE_IMPLEMENTATION

// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.


@@ -751,6 +807,27 @@ static FONSstate* fons__getState(FONScontext* stash)
return &stash->states[stash->nstates-1]; 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) void fonsSetSize(FONScontext* stash, float size)
{ {
fons__getState(stash)->size = size; fons__getState(stash)->size = size;
@@ -818,7 +895,7 @@ static void fons__freeFont(FONSfont* font)
{ {
if (font == NULL) return; if (font == NULL) return;
if (font->glyphs) free(font->glyphs); if (font->glyphs) free(font->glyphs);
if (font->freeData && font->data) free((void*)font->data);
if (font->freeData && font->data) free(font->data);
free(font); free(font);
} }


@@ -849,12 +926,12 @@ error:
return FONS_INVALID; return FONS_INVALID;
} }


int fonsAddFont(FONScontext* stash, const char* name, const char* path)
int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex)
{ {
FILE* fp = 0; FILE* fp = 0;
int dataSize = 0; int dataSize = 0;
size_t readed;
unsigned char* data = NULL; unsigned char* data = NULL;
size_t ignore;


// Read in the font data. // Read in the font data.
fp = fopen(path, "rb"); fp = fopen(path, "rb");
@@ -864,21 +941,20 @@ int fonsAddFont(FONScontext* stash, const char* name, const char* path)
fseek(fp,0,SEEK_SET); fseek(fp,0,SEEK_SET);
data = (unsigned char*)malloc(dataSize); data = (unsigned char*)malloc(dataSize);
if (data == NULL) goto error; if (data == NULL) goto error;
ignore = fread(data, 1, dataSize, fp);
readed = fread(data, 1, dataSize, fp);
fclose(fp); fclose(fp);
fp = 0; fp = 0;
if (readed != (size_t)dataSize) goto error;


return fonsAddFontMem(stash, name, data, dataSize, 1);
return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex);


error: error:
if (data) free(data); if (data) free(data);
if (fp) fclose(fp); if (fp) fclose(fp);
return FONS_INVALID; return FONS_INVALID;

FONS_NOTUSED(ignore);
} }


int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* data, int dataSize, int freeData)
int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex)
{ {
int i, ascent, descent, fh, lineGap; int i, ascent, descent, fh, lineGap;
FONSfont* font; FONSfont* font;
@@ -903,15 +979,16 @@ int fonsAddFontMem(FONScontext* stash, const char* name, const unsigned char* da


// Init font // Init font
stash->nscratch = 0; stash->nscratch = 0;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error;
if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error;


// Store normalized line height. The real line height is got // Store normalized line height. The real line height is got
// by multiplying the lineh by font size. // by multiplying the lineh by font size.
fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
ascent += lineGap;
fh = ascent - descent; fh = ascent - descent;
font->ascender = (float)ascent / (float)fh; font->ascender = (float)ascent / (float)fh;
font->descender = (float)descent / (float)fh; font->descender = (float)descent / (float)fh;
font->lineh = (float)(fh + lineGap) / (float)fh;
font->lineh = font->ascender - font->descender;


return idx; return idx;


@@ -1010,7 +1087,7 @@ static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int
} }


static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint,
short isize, short iblur)
short isize, short iblur, int bitmapOption)
{ {
int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;
float scale; float scale;
@@ -1020,6 +1097,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
int pad, added; int pad, added;
unsigned char* bdst; unsigned char* bdst;
unsigned char* dst; unsigned char* dst;
FONSfont* renderFont = font;


if (isize < 2) return NULL; if (isize < 2) return NULL;
if (iblur > 20) iblur = 20; if (iblur > 20) iblur = 20;
@@ -1032,32 +1110,66 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1);
i = font->lut[h]; i = font->lut[h];
while (i != -1) { while (i != -1) {
if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)
return &font->glyphs[i];
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; i = font->glyphs[i].next;
} }


// Could not find glyph, create it.
scale = fons__tt_getPixelHeightScale(&font->font, size);
// Create a new glyph or rasterize bitmap data for a cached glyph.
g = fons__tt_getGlyphIndex(&font->font, codepoint); g = fons__tt_getGlyphIndex(&font->font, codepoint);
fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);
// 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; gw = x1-x0 + pad*2;
gh = y1-y0 + pad*2; gh = y1-y0 + pad*2;


// 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);
// 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); 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;
} }
if (added == 0) return NULL;


// Init glyph. // Init glyph.
glyph = fons__allocGlyph(font);
glyph->codepoint = codepoint;
glyph->size = isize;
glyph->blur = iblur;
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->index = g;
glyph->x0 = (short)gx; glyph->x0 = (short)gx;
glyph->y0 = (short)gy; glyph->y0 = (short)gy;
@@ -1066,15 +1178,14 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
glyph->xadv = (short)(scale * advance * 10.0f); glyph->xadv = (short)(scale * advance * 10.0f);
glyph->xoff = (short)(x0 - pad); glyph->xoff = (short)(x0 - pad);
glyph->yoff = (short)(y0 - pad); glyph->yoff = (short)(y0 - pad);
glyph->next = 0;


// Insert char to hash lookup.
glyph->next = font->lut[h];
font->lut[h] = font->nglyphs-1;
if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) {
return glyph;
}


// Rasterize // Rasterize
dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width];
fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g);
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. // Make sure there is one pixel empty border.
dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
@@ -1101,7 +1212,7 @@ static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned in
if (iblur > 0) { if (iblur > 0) {
stash->nscratch = 0; stash->nscratch = 0;
bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);
fons__blur(stash, bdst, gw, gh, stash->params.width, iblur);
} }


stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
@@ -1134,8 +1245,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
y1 = (float)(glyph->y1-1); y1 = (float)(glyph->y1-1);


if (stash->params.flags & FONS_ZERO_TOPLEFT) { if (stash->params.flags & FONS_ZERO_TOPLEFT) {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y + yoff);
rx = floorf(*x + xoff);
ry = floorf(*y + yoff);


q->x0 = rx; q->x0 = rx;
q->y0 = ry; q->y0 = ry;
@@ -1147,8 +1258,8 @@ static void fons__getQuad(FONScontext* stash, FONSfont* font,
q->s1 = x1 * stash->itw; q->s1 = x1 * stash->itw;
q->t1 = y1 * stash->ith; q->t1 = y1 * stash->ith;
} else { } else {
rx = (float)(int)(*x + xoff);
ry = (float)(int)(*y - yoff);
rx = floorf(*x + xoff);
ry = floorf(*y - yoff);


q->x0 = rx; q->x0 = rx;
q->y0 = ry; q->y0 = ry;
@@ -1226,7 +1337,7 @@ float fonsDrawText(FONScontext* stash,
const char* str, const char* end) const char* str, const char* end)
{ {
FONSstate* state = fons__getState(stash); FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0; unsigned int utf8state = 0;
FONSglyph* glyph = NULL; FONSglyph* glyph = NULL;
FONSquad q; FONSquad q;
@@ -1263,7 +1374,7 @@ float fonsDrawText(FONScontext* stash,
for (; str != end; ++str) { for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue; continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_REQUIRED);
if (glyph != NULL) { if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);


@@ -1286,7 +1397,7 @@ float fonsDrawText(FONScontext* stash,
} }


int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
float x, float y, const char* str, const char* end)
float x, float y, const char* str, const char* end, int bitmapOption)
{ {
FONSstate* state = fons__getState(stash); FONSstate* state = fons__getState(stash);
float width; float width;
@@ -1326,6 +1437,7 @@ int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,
iter->end = end; iter->end = end;
iter->codepoint = 0; iter->codepoint = 0;
iter->prevGlyphIndex = -1; iter->prevGlyphIndex = -1;
iter->bitmapOption = bitmapOption;


return 1; return 1;
} }
@@ -1346,7 +1458,8 @@ int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)
// Get glyph and quad // Get glyph and quad
iter->x = iter->nextx; iter->x = iter->nextx;
iter->y = iter->nexty; iter->y = iter->nexty;
glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur);
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) if (glyph != NULL)
fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);
iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;
@@ -1406,12 +1519,12 @@ void fonsDrawDebug(FONScontext* stash, float x, float y)
} }


float fonsTextBounds(FONScontext* stash, float fonsTextBounds(FONScontext* stash,
float x, float y,
float x, float y,
const char* str, const char* end, const char* str, const char* end,
float* bounds) float* bounds)
{ {
FONSstate* state = fons__getState(stash); FONSstate* state = fons__getState(stash);
unsigned int codepoint = 0;
unsigned int codepoint;
unsigned int utf8state = 0; unsigned int utf8state = 0;
FONSquad q; FONSquad q;
FONSglyph* glyph = NULL; FONSglyph* glyph = NULL;
@@ -1443,7 +1556,7 @@ float fonsTextBounds(FONScontext* stash,
for (; str != end; ++str) { for (; str != end; ++str) {
if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))
continue; continue;
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
glyph = fons__getGlyph(stash, font, codepoint, isize, iblur, FONS_GLYPH_BITMAP_OPTIONAL);
if (glyph != NULL) { if (glyph != NULL) {
fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
if (q.x0 < minx) minx = q.x0; if (q.x0 < minx) minx = q.x0;
@@ -1568,6 +1681,7 @@ void fonsDeleteInternal(FONScontext* stash)
if (stash->texData) free(stash->texData); if (stash->texData) free(stash->texData);
if (stash->scratch) free(stash->scratch); if (stash->scratch) free(stash->scratch);
free(stash); free(stash);
fons__tt_done(stash);
} }


void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr)
@@ -1594,7 +1708,7 @@ int fonsExpandAtlas(FONScontext* stash, int width, int height)
height = fons__maxi(height, stash->params.height); height = fons__maxi(height, stash->params.height);


if (width == stash->params.width && height == stash->params.height) if (width == stash->params.width && height == stash->params.height)
return 1;
return 1;


// Flush pending glyphs. // Flush pending glyphs.
fons__flush(stash); fons__flush(stash);


+ 268
- 94
dgl/src/nanovg/nanovg.c View File

@@ -16,8 +16,11 @@
// 3. This notice may not be removed or altered from any source distribution. // 3. This notice may not be removed or altered from any source distribution.
// //


#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <memory.h>

#include "nanovg.h" #include "nanovg.h"
#define FONTSTASH_IMPLEMENTATION #define FONTSTASH_IMPLEMENTATION
#include "fontstash.h" #include "fontstash.h"
@@ -63,6 +66,8 @@ enum NVGpointFlags
}; };


struct NVGstate { struct NVGstate {
NVGcompositeOperationState compositeOperation;
int shapeAntiAlias;
NVGpaint fill; NVGpaint fill;
NVGpaint stroke; NVGpaint stroke;
float strokeWidth; float strokeWidth;
@@ -200,6 +205,84 @@ static void nvg__setDevicePixelRatio(NVGcontext* ctx, float ratio)
ctx->devicePxRatio = 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) NVGcontext* nvgCreateInternal(NVGparams* params)
{ {
FONSparams fontParams; FONSparams fontParams;
@@ -280,7 +363,7 @@ void nvgDeleteInternal(NVGcontext* ctx)
free(ctx); free(ctx);
} }


void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio)
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio)
{ {
/* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n", /* printf("Tris: draws:%d fill:%d stroke:%d text:%d TOT:%d\n",
ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount, ctx->drawCallCount, ctx->fillTriCount, ctx->strokeTriCount, ctx->textTriCount,
@@ -291,8 +374,8 @@ void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float dev
nvgReset(ctx); nvgReset(ctx);


nvg__setDevicePixelRatio(ctx, devicePixelRatio); nvg__setDevicePixelRatio(ctx, devicePixelRatio);
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight);
ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio);


ctx->drawCallCount = 0; ctx->drawCallCount = 0;
ctx->fillTriCount = 0; ctx->fillTriCount = 0;
@@ -383,7 +466,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
{ {
int i; int i;
float oneminu; float oneminu;
NVGcolor cint;
NVGcolor cint = {{{0}}};


u = nvg__clampf(u, 0.0f, 1.0f); u = nvg__clampf(u, 0.0f, 1.0f);
oneminu = 1.0f - u; oneminu = 1.0f - u;
@@ -391,7 +474,7 @@ NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u)
{ {
cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u; cint.rgba[i] = c0.rgba[i] * oneminu + c1.rgba[i] * u;
} }
return cint; return cint;
} }


@@ -430,12 +513,6 @@ NVGcolor nvgHSLA(float h, float s, float l, unsigned char a)
return col; return col;
} }



static NVGstate* nvg__getState(NVGcontext* ctx)
{
return &ctx->states[ctx->nstates-1];
}

void nvgTransformIdentity(float* t) void nvgTransformIdentity(float* t)
{ {
t[0] = 1.0f; t[1] = 0.0f; t[0] = 1.0f; t[1] = 0.0f;
@@ -568,6 +645,8 @@ void nvgReset(NVGcontext* ctx)


nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255));
nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,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->strokeWidth = 1.0f;
state->miterLimit = 10.0f; state->miterLimit = 10.0f;
state->lineCap = NVG_BUTT; state->lineCap = NVG_BUTT;
@@ -587,6 +666,12 @@ void nvgReset(NVGcontext* ctx)
} }


// State setting // State setting
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled)
{
NVGstate* state = nvg__getState(ctx);
state->shapeAntiAlias = enabled;
}

void nvgStrokeWidth(NVGcontext* ctx, float width) void nvgStrokeWidth(NVGcontext* ctx, float width)
{ {
NVGstate* state = nvg__getState(ctx); NVGstate* state = nvg__getState(ctx);
@@ -719,7 +804,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
return image; return image;
} }


int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata)
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata)
{ {
int w, h, n, image; int w, h, n, image;
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4); unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
@@ -913,7 +998,7 @@ void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h)
} }


// Transform the current scissor rect into current transform space. // Transform the current scissor rect into current transform space.
// If there is difference in rotation, this will be approximation.
// If there is difference in rotation, this will be approximation.
memcpy(pxform, state->scissor.xform, sizeof(float)*6); memcpy(pxform, state->scissor.xform, sizeof(float)*6);
ex = state->scissor.extent[0]; ex = state->scissor.extent[0];
ey = state->scissor.extent[1]; ey = state->scissor.extent[1];
@@ -936,6 +1021,30 @@ void nvgResetScissor(NVGcontext* ctx)
state->scissor.extent[1] = -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) static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol)
{ {
float dx = x2 - x1; float dx = x2 - x1;
@@ -1173,7 +1282,7 @@ static void nvg__tesselateBezier(NVGcontext* ctx,
{ {
float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
float dx,dy,d2,d3; float dx,dy,d2,d3;
if (level > 10) return; if (level > 10) return;


x12 = (x1+x2)*0.5f; x12 = (x1+x2)*0.5f;
@@ -1205,8 +1314,8 @@ static void nvg__tesselateBezier(NVGcontext* ctx,
x1234 = (x123+x234)*0.5f; x1234 = (x123+x234)*0.5f;
y1234 = (y123+y234)*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);
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) static void nvg__flattenPaths(NVGcontext* ctx)
@@ -1331,7 +1440,8 @@ static void nvg__chooseBevel(int bevel, NVGpoint* p0, NVGpoint* p1, float w,
} }


static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1, static NVGvertex* nvg__roundJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
float lw, float rw, float lu, float ru, int ncap, float fringe)
float lw, float rw, float lu, float ru, int ncap,
float fringe)
{ {
int i, n; int i, n;
float dlx0 = p0->dy; float dlx0 = p0->dy;
@@ -1464,36 +1574,39 @@ static NVGvertex* nvg__bevelJoin(NVGvertex* dst, NVGpoint* p0, NVGpoint* p1,
} }


static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__buttCapStart(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, float d, float aa)
float dx, float dy, float w, float d,
float aa, float u0, float u1)
{ {
float px = p->x - dx*d; float px = p->x - dx*d;
float py = p->y - dy*d; float py = p->y - dy*d;
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
nvg__vset(dst, px + dlx*w - dx*aa, py + dly*w - dy*aa, 0,0); dst++;
nvg__vset(dst, px - dlx*w - dx*aa, py - dly*w - dy*aa, 1,0); dst++;
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,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++;
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; return dst;
} }


static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__buttCapEnd(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, float d, float aa)
float dx, float dy, float w, float d,
float aa, float u0, float u1)
{ {
float px = p->x + dx*d; float px = p->x + dx*d;
float py = p->y + dy*d; float py = p->y + dy*d;
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,1); dst++;
nvg__vset(dst, px + dlx*w + dx*aa, py + dly*w + dy*aa, 0,0); dst++;
nvg__vset(dst, px - dlx*w + dx*aa, py - dly*w + dy*aa, 1,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++;
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; return dst;
} }




static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, int ncap, float aa)
float dx, float dy, float w, int ncap,
float aa, float u0, float u1)
{ {
int i; int i;
float px = p->x; float px = p->x;
@@ -1504,16 +1617,17 @@ static NVGvertex* nvg__roundCapStart(NVGvertex* dst, NVGpoint* p,
for (i = 0; i < ncap; i++) { for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NVG_PI; float a = i/(float)(ncap-1)*NVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
nvg__vset(dst, px - dlx*ax - dx*ay, py - dly*ax - dy*ay, 0,1); dst++;
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, py, 0.5f,1); dst++;
} }
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,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; return dst;
} }


static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p, static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
float dx, float dy, float w, int ncap, float aa)
float dx, float dy, float w, int ncap,
float aa, float u0, float u1)
{ {
int i; int i;
float px = p->x; float px = p->x;
@@ -1521,13 +1635,13 @@ static NVGvertex* nvg__roundCapEnd(NVGvertex* dst, NVGpoint* p,
float dlx = dy; float dlx = dy;
float dly = -dx; float dly = -dx;
NVG_NOTUSED(aa); NVG_NOTUSED(aa);
nvg__vset(dst, px + dlx*w, py + dly*w, 0,1); dst++;
nvg__vset(dst, px - dlx*w, py - dly*w, 1,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++;
for (i = 0; i < ncap; i++) { for (i = 0; i < ncap; i++) {
float a = i/(float)(ncap-1)*NVG_PI; float a = i/(float)(ncap-1)*NVG_PI;
float ax = cosf(a) * w, ay = sinf(a) * w; float ax = cosf(a) * w, ay = sinf(a) * w;
nvg__vset(dst, px, py, 0.5f,1); dst++; nvg__vset(dst, px, py, 0.5f,1); dst++;
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, 0,1); dst++;
nvg__vset(dst, px - dlx*ax + dx*ay, py - dly*ax + dy*ay, u0,1); dst++;
} }
return dst; return dst;
} }
@@ -1603,15 +1717,24 @@ static void nvg__calculateJoins(NVGcontext* ctx, float w, int lineJoin, float mi
} }




static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin, float miterLimit)
{
static int nvg__expandStroke(NVGcontext* ctx, float w, float fringe, int lineCap, int lineJoin, float miterLimit)
{
NVGpathCache* cache = ctx->cache; NVGpathCache* cache = ctx->cache;
NVGvertex* verts; NVGvertex* verts;
NVGvertex* dst; NVGvertex* dst;
int cverts, i, j; int cverts, i, j;
float aa = ctx->fringeWidth;
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. 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); nvg__calculateJoins(ctx, w, lineJoin, miterLimit);


// Calculate max vertex usage. // Calculate max vertex usage.
@@ -1672,42 +1795,42 @@ static int nvg__expandStroke(NVGcontext* ctx, float w, int lineCap, int lineJoin
dy = p1->y - p0->y; dy = p1->y - p0->y;
nvg__normalize(&dx, &dy); nvg__normalize(&dx, &dy);
if (lineCap == NVG_BUTT) if (lineCap == NVG_BUTT)
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa);
dst = nvg__buttCapStart(dst, p0, dx, dy, w, -aa*0.5f, aa, u0, u1);
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa);
dst = nvg__buttCapStart(dst, p0, dx, dy, w, w-aa, aa, u0, u1);
else if (lineCap == NVG_ROUND) else if (lineCap == NVG_ROUND)
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa);
dst = nvg__roundCapStart(dst, p0, dx, dy, w, ncap, aa, u0, u1);
} }


for (j = s; j < e; ++j) { for (j = s; j < e; ++j) {
if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) { if ((p1->flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
if (lineJoin == NVG_ROUND) { if (lineJoin == NVG_ROUND) {
dst = nvg__roundJoin(dst, p0, p1, w, w, 0, 1, ncap, aa);
dst = nvg__roundJoin(dst, p0, p1, w, w, u0, u1, ncap, aa);
} else { } else {
dst = nvg__bevelJoin(dst, p0, p1, w, w, 0, 1, aa);
dst = nvg__bevelJoin(dst, p0, p1, w, w, u0, u1, aa);
} }
} else { } else {
nvg__vset(dst, p1->x + (p1->dmx * w), p1->y + (p1->dmy * w), 0,1); dst++;
nvg__vset(dst, p1->x - (p1->dmx * w), p1->y - (p1->dmy * w), 1,1); dst++;
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++; p0 = p1++;
} }


if (loop) { if (loop) {
// Loop it // Loop it
nvg__vset(dst, verts[0].x, verts[0].y, 0,1); dst++;
nvg__vset(dst, verts[1].x, verts[1].y, 1,1); dst++;
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 { } else {
// Add cap // Add cap
dx = p1->x - p0->x; dx = p1->x - p0->x;
dy = p1->y - p0->y; dy = p1->y - p0->y;
nvg__normalize(&dx, &dy); nvg__normalize(&dx, &dy);
if (lineCap == NVG_BUTT) if (lineCap == NVG_BUTT)
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa);
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, -aa*0.5f, aa, u0, u1);
else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE) else if (lineCap == NVG_BUTT || lineCap == NVG_SQUARE)
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa);
dst = nvg__buttCapEnd(dst, p1, dx, dy, w, w-aa, aa, u0, u1);
else if (lineCap == NVG_ROUND) else if (lineCap == NVG_ROUND)
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa);
dst = nvg__roundCapEnd(dst, p1, dx, dy, w, ncap, aa, u0, u1);
} }


path->nstroke = (int)(dst - verts); path->nstroke = (int)(dst - verts);
@@ -1868,7 +1991,7 @@ void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y)
{ {
float x0 = ctx->commandx; float x0 = ctx->commandx;
float y0 = ctx->commandy; float y0 = ctx->commandy;
float vals[] = { NVG_BEZIERTO,
float vals[] = { NVG_BEZIERTO,
x0 + 2.0f/3.0f*(cx - x0), y0 + 2.0f/3.0f*(cy - y0), 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 + 2.0f/3.0f*(cx - x), y + 2.0f/3.0f*(cy - y),
x, y }; x, y };
@@ -1950,7 +2073,7 @@ void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, in
float px = 0, py = 0, ptanx = 0, ptany = 0; float px = 0, py = 0, ptanx = 0, ptany = 0;
float vals[3 + 5*7 + 100]; float vals[3 + 5*7 + 100];
int i, ndivs, nvals; int i, ndivs, nvals;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;
int move = ctx->ncommands > 0 ? NVG_LINETO : NVG_MOVETO;


// Clamp angles // Clamp angles
da = a1 - a0; da = a1 - a0;
@@ -2022,22 +2145,31 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h)


void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r)
{ {
if (r < 0.1f) {
nvgRect(ctx, x,y,w,h);
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; return;
}
else {
float rx = nvg__minf(r, nvg__absf(w)*0.5f) * nvg__signf(w), ry = nvg__minf(r, nvg__absf(h)*0.5f) * nvg__signf(h);
} 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[] = { float vals[] = {
NVG_MOVETO, x, y+ry,
NVG_LINETO, x, y+h-ry,
NVG_BEZIERTO, x, y+h-ry*(1-NVG_KAPPA90), x+rx*(1-NVG_KAPPA90), y+h, x+rx, y+h,
NVG_LINETO, x+w-rx, y+h,
NVG_BEZIERTO, x+w-rx*(1-NVG_KAPPA90), y+h, x+w, y+h-ry*(1-NVG_KAPPA90), x+w, y+h-ry,
NVG_LINETO, x+w, y+ry,
NVG_BEZIERTO, x+w, y+ry*(1-NVG_KAPPA90), x+w-rx*(1-NVG_KAPPA90), y, x+w-rx, y,
NVG_LINETO, x+rx, y,
NVG_BEZIERTO, x+rx*(1-NVG_KAPPA90), y, x, y+ry*(1-NVG_KAPPA90), x, y+ry,
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_CLOSE
}; };
nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals)); nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));
@@ -2092,7 +2224,7 @@ void nvgFill(NVGcontext* ctx)
int i; int i;


nvg__flattenPaths(ctx); nvg__flattenPaths(ctx);
if (ctx->params.edgeAntiAlias)
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f); nvg__expandFill(ctx, ctx->fringeWidth, NVG_MITER, 2.4f);
else else
nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f); nvg__expandFill(ctx, 0.0f, NVG_MITER, 2.4f);
@@ -2101,7 +2233,7 @@ void nvgFill(NVGcontext* ctx)
fillPaint.innerColor.a *= state->alpha; fillPaint.innerColor.a *= state->alpha;
fillPaint.outerColor.a *= state->alpha; fillPaint.outerColor.a *= state->alpha;


ctx->params.renderFill(ctx->params.userPtr, &fillPaint, &state->scissor, ctx->fringeWidth,
ctx->params.renderFill(ctx->params.userPtr, &fillPaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths); ctx->cache->bounds, ctx->cache->paths, ctx->cache->npaths);


// Count triangles // Count triangles
@@ -2122,6 +2254,7 @@ void nvgStroke(NVGcontext* ctx)
const NVGpath* path; const NVGpath* path;
int i; int i;



if (strokeWidth < ctx->fringeWidth) { if (strokeWidth < ctx->fringeWidth) {
// If the stroke width is less than pixel size, use alpha to emulate coverage. // If the stroke width is less than pixel size, use alpha to emulate coverage.
// Since coverage is area, scale by alpha*alpha. // Since coverage is area, scale by alpha*alpha.
@@ -2137,12 +2270,12 @@ void nvgStroke(NVGcontext* ctx)


nvg__flattenPaths(ctx); nvg__flattenPaths(ctx);


if (ctx->params.edgeAntiAlias)
nvg__expandStroke(ctx, strokeWidth*0.5f + ctx->fringeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit);
if (ctx->params.edgeAntiAlias && state->shapeAntiAlias)
nvg__expandStroke(ctx, strokeWidth*0.5f, ctx->fringeWidth, state->lineCap, state->lineJoin, state->miterLimit);
else else
nvg__expandStroke(ctx, strokeWidth*0.5f, state->lineCap, state->lineJoin, state->miterLimit);
nvg__expandStroke(ctx, strokeWidth*0.5f, 0.0f, state->lineCap, state->lineJoin, state->miterLimit);


ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, &state->scissor, ctx->fringeWidth,
ctx->params.renderStroke(ctx->params.userPtr, &strokePaint, state->compositeOperation, &state->scissor, ctx->fringeWidth,
strokeWidth, ctx->cache->paths, ctx->cache->npaths); strokeWidth, ctx->cache->paths, ctx->cache->npaths);


// Count triangles // Count triangles
@@ -2154,14 +2287,24 @@ void nvgStroke(NVGcontext* ctx)
} }


// Add fonts // Add fonts
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* path)
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename)
{ {
return fonsAddFont(ctx->fs, name, path);
return fonsAddFont(ctx->fs, name, filename, 0);
} }


int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData)
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex)
{ {
return fonsAddFontMem(ctx->fs, name, data, ndata, freeData);
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) int nvgFindFont(NVGcontext* ctx, const char* name)
@@ -2170,6 +2313,28 @@ int nvgFindFont(NVGcontext* ctx, const char* name)
return fonsGetFontByName(ctx->fs, name); 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 // State setting
void nvgFontSize(NVGcontext* ctx, float size) void nvgFontSize(NVGcontext* ctx, float size)
{ {
@@ -2278,7 +2443,7 @@ static void nvg__renderText(NVGcontext* ctx, NVGvertex* verts, int nverts)
paint.innerColor.a *= state->alpha; paint.innerColor.a *= state->alpha;
paint.outerColor.a *= state->alpha; paint.outerColor.a *= state->alpha;


ctx->params.renderTriangles(ctx->params.userPtr, &paint, &state->scissor, verts, nverts);
ctx->params.renderTriangles(ctx->params.userPtr, &paint, state->compositeOperation, &state->scissor, verts, nverts, ctx->fringeWidth);


ctx->drawCallCount++; ctx->drawCallCount++;
ctx->textTriCount += nverts/3; ctx->textTriCount += nverts/3;
@@ -2310,17 +2475,17 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char*
verts = nvg__allocTempVerts(ctx, cverts); verts = nvg__allocTempVerts(ctx, cverts);
if (verts == NULL) return x; if (verts == NULL) return x;


fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end);
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_REQUIRED);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
float c[4*2]; float c[4*2];
if (iter.prevGlyphIndex == -1) { // can not retrieve glyph? if (iter.prevGlyphIndex == -1) { // can not retrieve glyph?
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
if (nverts != 0) { if (nverts != 0) {
nvg__renderText(ctx, verts, nverts); nvg__renderText(ctx, verts, nverts);
nverts = 0; nverts = 0;
} }
if (!nvg__allocTextAtlas(ctx))
break; // no memory :(
iter = prevIter; iter = prevIter;
fonsTextIterNext(ctx->fs, &iter, &q); // try again fonsTextIterNext(ctx->fs, &iter, &q); // try again
if (iter.prevGlyphIndex == -1) // still can not find glyph? if (iter.prevGlyphIndex == -1) // still can not find glyph?
@@ -2343,12 +2508,12 @@ float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char*
} }
} }


// TODO: add back-end bit to do this just once per frame.
// TODO: add back-end bit to do this just once per frame.
nvg__flushTextTexture(ctx); nvg__flushTextTexture(ctx);


nvg__renderText(ctx, verts, nverts); nvg__renderText(ctx, verts, nverts);


return iter.x;
return iter.nextx / scale;
} }


void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end) void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end)
@@ -2407,7 +2572,7 @@ int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string,
fonsSetAlign(ctx->fs, state->textAlign); fonsSetAlign(ctx->fs, state->textAlign);
fonsSetFont(ctx->fs, state->fontId); fonsSetFont(ctx->fs, state->fontId);


fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end);
fonsTextIterInit(ctx->fs, &iter, x*scale, y*scale, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
@@ -2431,6 +2596,7 @@ enum NVGcodepointType {
NVG_SPACE, NVG_SPACE,
NVG_NEWLINE, NVG_NEWLINE,
NVG_CHAR, NVG_CHAR,
NVG_CJK_CHAR,
}; };


int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows) int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows)
@@ -2472,7 +2638,7 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa


breakRowWidth *= scale; breakRowWidth *= scale;


fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end);
fonsTextIterInit(ctx->fs, &iter, 0, 0, string, end, FONS_GLYPH_BITMAP_OPTIONAL);
prevIter = iter; prevIter = iter;
while (fonsTextIterNext(ctx->fs, &iter, &q)) { while (fonsTextIterNext(ctx->fs, &iter, &q)) {
if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph? if (iter.prevGlyphIndex < 0 && nvg__allocTextAtlas(ctx)) { // can not retrieve glyph?
@@ -2498,7 +2664,15 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
type = NVG_NEWLINE; type = NVG_NEWLINE;
break; break;
default: default:
type = NVG_CHAR;
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; break;
} }


@@ -2525,12 +2699,12 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
} else { } else {
if (rowStart == NULL) { if (rowStart == NULL) {
// Skip white space until the beginning of the line // Skip white space until the beginning of the line
if (type == NVG_CHAR) {
if (type == NVG_CHAR || type == NVG_CJK_CHAR) {
// The current char is the row so far // The current char is the row so far
rowStartX = iter.x; rowStartX = iter.x;
rowStart = iter.str; rowStart = iter.str;
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; // q.x1 - rowStartX;
rowWidth = iter.nextx - rowStartX;
rowMinX = q.x0 - rowStartX; rowMinX = q.x0 - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
wordStart = iter.str; wordStart = iter.str;
@@ -2545,26 +2719,26 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
float nextWidth = iter.nextx - rowStartX; float nextWidth = iter.nextx - rowStartX;


// track last non-white space character // track last non-white space character
if (type == NVG_CHAR) {
if (type == NVG_CHAR || type == NVG_CJK_CHAR) {
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; rowWidth = iter.nextx - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
} }
// track last end of a word // track last end of a word
if (ptype == NVG_CHAR && type == NVG_SPACE) {
if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) {
breakEnd = iter.str; breakEnd = iter.str;
breakWidth = rowWidth; breakWidth = rowWidth;
breakMaxX = rowMaxX; breakMaxX = rowMaxX;
} }
// track last beginning of a word // track last beginning of a word
if (ptype == NVG_SPACE && type == NVG_CHAR) {
if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) {
wordStart = iter.str; wordStart = iter.str;
wordStartX = iter.x; wordStartX = iter.x;
wordMinX = q.x0 - rowStartX;
wordMinX = q.x0;
} }


// Break to new line when a character is beyond break width. // Break to new line when a character is beyond break width.
if (type == NVG_CHAR && nextWidth > breakRowWidth) {
if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) {
// The run length is too long, need to break to new line. // The run length is too long, need to break to new line.
if (breakEnd == rowStart) { if (breakEnd == rowStart) {
// The current word is longer than the row length, just break it from here. // The current word is longer than the row length, just break it from here.
@@ -2597,13 +2771,13 @@ int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, floa
nrows++; nrows++;
if (nrows >= maxRows) if (nrows >= maxRows)
return nrows; return nrows;
// Update row
rowStartX = wordStartX; rowStartX = wordStartX;
rowStart = wordStart; rowStart = wordStart;
rowEnd = iter.next; rowEnd = iter.next;
rowWidth = iter.nextx - rowStartX; rowWidth = iter.nextx - rowStartX;
rowMinX = wordMinX;
rowMinX = wordMinX - rowStartX;
rowMaxX = q.x1 - rowStartX; rowMaxX = q.x1 - rowStartX;
// No change to the word start
} }
// Set null break point // Set null break point
breakEnd = rowStart; breakEnd = rowStart;


+ 95
- 18
dgl/src/nanovg/nanovg.h View File

@@ -79,10 +79,46 @@ enum NVGalign {
// Vertical align // Vertical align
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 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.
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 { struct NVGglyphPosition {
const char* str; // Position of the glyph in the input string. const char* str; // Position of the glyph in the input string.
float x; // The x-coordinate of the logical glyph position. float x; // The x-coordinate of the logical glyph position.
@@ -100,11 +136,12 @@ struct NVGtextRow {
typedef struct NVGtextRow NVGtextRow; typedef struct NVGtextRow NVGtextRow;


enum NVGimageFlags { enum NVGimageFlags {
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
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_REPEATX = 1<<1, // Repeat image in X direction.
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y 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_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
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 // Begin drawing a new frame
@@ -115,7 +152,7 @@ enum NVGimageFlags {
// For example, GLFW returns two dimension for an opened window: window size and // 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 // frame buffer size. In that case you would set windowWidth/Height to the window size
// devicePixelRatio to: frameBufferWidth / windowWidth. // devicePixelRatio to: frameBufferWidth / windowWidth.
void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio);
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio);


// Cancels drawing the current frame. // Cancels drawing the current frame.
void nvgCancelFrame(NVGcontext* ctx); void nvgCancelFrame(NVGcontext* ctx);
@@ -123,6 +160,22 @@ void nvgCancelFrame(NVGcontext* ctx);
// Ends drawing flushing remaining render state. // Ends drawing flushing remaining render state.
void nvgEndFrame(NVGcontext* ctx); 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 // Color utils
// //
@@ -183,7 +236,10 @@ void nvgReset(NVGcontext* ctx);
// Solid color is simply defined as a color value, different kinds of paints can be created // Solid color is simply defined as a color value, different kinds of paints can be created
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().
// //
// Current render style can be saved and restored using nvgSave() and nvgRestore().
// 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. // Sets current stroke style to a solid color.
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
@@ -231,7 +287,7 @@ void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
// Apart from nvgResetTransform(), each transformation function first creates // Apart from nvgResetTransform(), each transformation function first creates
// specific transformation matrix and pre-multiplies the current transformation by it. // specific transformation matrix and pre-multiplies the current transformation by it.
// //
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().


// Resets current transform to a identity matrix. // Resets current transform to a identity matrix.
void nvgResetTransform(NVGcontext* ctx); void nvgResetTransform(NVGcontext* ctx);
@@ -317,7 +373,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);


// Creates image by loading it from the specified chunk of memory. // Creates image by loading it from the specified chunk of memory.
// Returns handle to the image. // Returns handle to the image.
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata);
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);


// Creates image from specified image data. // Creates image from specified image data.
// Returns handle to the image. // Returns handle to the image.
@@ -368,7 +424,7 @@ NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey
// Scissoring // Scissoring
// //
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various // 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.
// user interface cases like rendering a text edit or a timeline.


// Sets the current scissor rectangle. // Sets the current scissor rectangle.
// The scissor rectangle is transformed by the current transform. // The scissor rectangle is transformed by the current transform.
@@ -423,7 +479,7 @@ void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float rad
// Closes current sub-path with a line segment. // Closes current sub-path with a line segment.
void nvgClosePath(NVGcontext* ctx); void nvgClosePath(NVGcontext* ctx);


// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
void nvgPathWinding(NVGcontext* ctx, int dir); 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, // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
@@ -437,10 +493,13 @@ void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);
// Creates new rounded rectangle shaped sub-path. // Creates new rounded rectangle shaped sub-path.
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); 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. // Creates new ellipse shaped sub-path.
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);


// Creates new circle shaped sub-path.
// Creates new circle shaped sub-path.
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);


// Fills the current path with current fill style. // Fills the current path with current fill style.
@@ -487,13 +546,31 @@ void nvgStroke(NVGcontext* ctx);
// Returns handle to the font. // Returns handle to the font.
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);


// Creates image by loading it from the specified memory chunk.
// 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. // Returns handle to the font.
int nvgCreateFontMem(NVGcontext* ctx, const char* name, const unsigned char* data, int ndata, int freeData);
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. // 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); 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. // Sets the font size of current text style.
void nvgFontSize(NVGcontext* ctx, float size); void nvgFontSize(NVGcontext* ctx, float size);


@@ -503,7 +580,7 @@ void nvgFontBlur(NVGcontext* ctx, float blur);
// Sets the letter spacing of current text style. // Sets the letter spacing of current text style.
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); 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.
// 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); void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);


// Sets the text align of current text style, see NVGalign for options. // Sets the text align of current text style, see NVGalign for options.
@@ -588,12 +665,12 @@ struct NVGparams {
int (*renderDeleteTexture)(void* uptr, int image); 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 (*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); int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);
void (*renderViewport)(void* uptr, int width, int height);
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
void (*renderCancel)(void* uptr); void (*renderCancel)(void* uptr);
void (*renderFlush)(void* uptr); void (*renderFlush)(void* uptr);
void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts);
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); void (*renderDelete)(void* uptr);
}; };
typedef struct NVGparams NVGparams; typedef struct NVGparams NVGparams;


+ 197
- 62
dgl/src/nanovg/nanovg_gl.h View File

@@ -59,6 +59,9 @@ enum NVGcreateFlags {
NVGcontext* nvgCreateGL2(int flags); NVGcontext* nvgCreateGL2(int flags);
void nvgDeleteGL2(NVGcontext* ctx); void nvgDeleteGL2(NVGcontext* ctx);


int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image);

#endif #endif


#if defined NANOVG_GL3 #if defined NANOVG_GL3
@@ -66,6 +69,9 @@ void nvgDeleteGL2(NVGcontext* ctx);
NVGcontext* nvgCreateGL3(int flags); NVGcontext* nvgCreateGL3(int flags);
void nvgDeleteGL3(NVGcontext* ctx); void nvgDeleteGL3(NVGcontext* ctx);


int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image);

#endif #endif


#if defined NANOVG_GLES2 #if defined NANOVG_GLES2
@@ -73,6 +79,9 @@ void nvgDeleteGL3(NVGcontext* ctx);
NVGcontext* nvgCreateGLES2(int flags); NVGcontext* nvgCreateGLES2(int flags);
void nvgDeleteGLES2(NVGcontext* ctx); void nvgDeleteGLES2(NVGcontext* ctx);


int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image);

#endif #endif


#if defined NANOVG_GLES3 #if defined NANOVG_GLES3
@@ -80,6 +89,9 @@ void nvgDeleteGLES2(NVGcontext* ctx);
NVGcontext* nvgCreateGLES3(int flags); NVGcontext* nvgCreateGLES3(int flags);
void nvgDeleteGLES3(NVGcontext* ctx); void nvgDeleteGLES3(NVGcontext* ctx);


int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image);

#endif #endif


// These are additional flags on top of NVGimageFlags. // These are additional flags on top of NVGimageFlags.
@@ -87,10 +99,6 @@ enum NVGimageFlagsGL {
NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.
}; };


int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);
GLuint nvglImageHandle(NVGcontext* ctx, int image);


#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -142,6 +150,15 @@ struct GLNVGtexture {
}; };
typedef struct GLNVGtexture GLNVGtexture; typedef struct GLNVGtexture GLNVGtexture;


struct GLNVGblend
{
GLenum srcRGB;
GLenum dstRGB;
GLenum srcAlpha;
GLenum dstAlpha;
};
typedef struct GLNVGblend GLNVGblend;

enum GLNVGcallType { enum GLNVGcallType {
GLNVG_NONE = 0, GLNVG_NONE = 0,
GLNVG_FILL, GLNVG_FILL,
@@ -158,6 +175,7 @@ struct GLNVGcall {
int triangleOffset; int triangleOffset;
int triangleCount; int triangleCount;
int uniformOffset; int uniformOffset;
GLNVGblend blendFunc;
}; };
typedef struct GLNVGcall GLNVGcall; typedef struct GLNVGcall GLNVGcall;


@@ -248,7 +266,10 @@ struct GLNVGcontext {
GLenum stencilFunc; GLenum stencilFunc;
GLint stencilFuncRef; GLint stencilFuncRef;
GLuint stencilFuncMask; GLuint stencilFuncMask;
GLNVGblend blendFunc;
#endif #endif

int dummyTex;
}; };
typedef struct GLNVGcontext GLNVGcontext; typedef struct GLNVGcontext GLNVGcontext;


@@ -298,7 +319,7 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
if ((gl->stencilFunc != func) || if ((gl->stencilFunc != func) ||
(gl->stencilFuncRef != ref) || (gl->stencilFuncRef != ref) ||
(gl->stencilFuncMask != mask)) { (gl->stencilFuncMask != mask)) {
gl->stencilFunc = func; gl->stencilFunc = func;
gl->stencilFuncRef = ref; gl->stencilFuncRef = ref;
gl->stencilFuncMask = mask; gl->stencilFuncMask = mask;
@@ -308,6 +329,21 @@ static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint
glStencilFunc(func, ref, mask); glStencilFunc(func, ref, mask);
#endif #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) static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
{ {
@@ -331,10 +367,10 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
} }
tex = &gl->textures[gl->ntextures++]; tex = &gl->textures[gl->ntextures++];
} }
memset(tex, 0, sizeof(*tex)); memset(tex, 0, sizeof(*tex));
tex->id = ++gl->textureId; tex->id = ++gl->textureId;
return tex; return tex;
} }


@@ -363,8 +399,8 @@ static int glnvg__deleteTexture(GLNVGcontext* gl, int id)


static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type)
{ {
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetShaderInfoLog(shader, 512, &len, str); glGetShaderInfoLog(shader, 512, &len, str);
if (len > 512) len = 512; if (len > 512) len = 512;
str[len] = '\0'; str[len] = '\0';
@@ -373,8 +409,8 @@ static void glnvg__dumpShaderError(GLuint shader, const char* name, const char*


static void glnvg__dumpProgramError(GLuint prog, const char* name) static void glnvg__dumpProgramError(GLuint prog, const char* name)
{ {
char str[512+1];
int len = 0;
GLchar str[512+1];
GLsizei len = 0;
glGetProgramInfoLog(prog, 512, &len, str); glGetProgramInfoLog(prog, 512, &len, str);
if (len > 512) len = 512; if (len > 512) len = 512;
str[len] = '\0'; str[len] = '\0';
@@ -466,6 +502,8 @@ static void glnvg__getUniforms(GLNVGshader* shader)
#endif #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) static int glnvg__renderCreate(void* uptr)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -514,7 +552,7 @@ static int glnvg__renderCreate(void* uptr)
" gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n"
"}\n"; "}\n";


static const char* fillFragShader =
static const char* fillFragShader =
"#ifdef GL_ES\n" "#ifdef GL_ES\n"
"#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"
" precision highp float;\n" " precision highp float;\n"
@@ -592,6 +630,7 @@ static int glnvg__renderCreate(void* uptr)
" float scissor = scissorMask(fpos);\n" " float scissor = scissorMask(fpos);\n"
"#ifdef EDGE_AA\n" "#ifdef EDGE_AA\n"
" float strokeAlpha = strokeMask();\n" " float strokeAlpha = strokeMask();\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#else\n" "#else\n"
" float strokeAlpha = 1.0;\n" " float strokeAlpha = 1.0;\n"
"#endif\n" "#endif\n"
@@ -631,9 +670,6 @@ static int glnvg__renderCreate(void* uptr)
" color *= scissor;\n" " color *= scissor;\n"
" result = color * innerCol;\n" " result = color * innerCol;\n"
" }\n" " }\n"
"#ifdef EDGE_AA\n"
" if (strokeAlpha < strokeThr) discard;\n"
"#endif\n"
"#ifdef NANOVG_GL3\n" "#ifdef NANOVG_GL3\n"
" outColor = result;\n" " outColor = result;\n"
"#else\n" "#else\n"
@@ -663,11 +699,15 @@ static int glnvg__renderCreate(void* uptr)
#if NANOVG_GL_USE_UNIFORMBUFFER #if NANOVG_GL_USE_UNIFORMBUFFER
// Create UBOs // Create UBOs
glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING);
glGenBuffers(1, &gl->fragBuf);
glGenBuffers(1, &gl->fragBuf);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align);
#endif #endif
gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; 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"); glnvg__checkError(gl, "create done");


glFinish(); glFinish();
@@ -690,7 +730,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); 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); imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
} }
// No mips.
// No mips.
if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h);
imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS;
@@ -722,7 +762,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
if (type == NVG_TEXTURE_RGBA) if (type == NVG_TEXTURE_RGBA)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else else
#if defined(NANOVG_GLES2)
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#elif defined(NANOVG_GLES3) #elif defined(NANOVG_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
@@ -731,11 +771,24 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
#endif #endif


if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
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 { } else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


if (imageFlags & NVG_IMAGE_REPEATX) if (imageFlags & NVG_IMAGE_REPEATX)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -801,7 +854,7 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w
if (tex->type == NVG_TEXTURE_RGBA) if (tex->type == NVG_TEXTURE_RGBA)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
else else
#ifdef NANOVG_GLES2
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#else #else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);
@@ -887,19 +940,30 @@ static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpai
tex = glnvg__findTexture(gl, paint->image); tex = glnvg__findTexture(gl, paint->image);
if (tex == NULL) return 0; if (tex == NULL) return 0;
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {
float flipped[6];
nvgTransformScale(flipped, 1.0f, -1.0f);
nvgTransformMultiply(flipped, paint->xform);
nvgTransformInverse(invxform, flipped);
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 { } else {
nvgTransformInverse(invxform, paint->xform); nvgTransformInverse(invxform, paint->xform);
} }
frag->type = NSVG_SHADER_FILLIMG; frag->type = NSVG_SHADER_FILLIMG;


#if NANOVG_GL_USE_UNIFORMBUFFER
if (tex->type == NVG_TEXTURE_RGBA) if (tex->type == NVG_TEXTURE_RGBA)
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
else else
frag->texType = 2; 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); // printf("frag->texType = %d\n", frag->texType);
} else { } else {
frag->type = NSVG_SHADER_FILLGRAD; frag->type = NSVG_SHADER_FILLGRAD;
@@ -917,6 +981,7 @@ static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i);


static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
{ {
GLNVGtexture* tex = NULL;
#if NANOVG_GL_USE_UNIFORMBUFFER #if NANOVG_GL_USE_UNIFORMBUFFER
glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms));
#else #else
@@ -925,19 +990,22 @@ static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)
#endif #endif


if (image != 0) { if (image != 0) {
GLNVGtexture* tex = glnvg__findTexture(gl, image);
glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);
glnvg__checkError(gl, "tex paint tex");
} else {
glnvg__bindTexture(gl, 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, int width, int height)
static void glnvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio)
{ {
NVG_NOTUSED(devicePixelRatio);
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
gl->view[0] = (float)width;
gl->view[1] = (float)height;
gl->view[0] = width;
gl->view[1] = height;
} }


static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
@@ -979,7 +1047,7 @@ static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)
// Draw fill // Draw fill
glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);
glDrawArrays(GL_TRIANGLE_STRIP, call->triangleOffset, call->triangleCount);


glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
} }
@@ -992,12 +1060,12 @@ static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call)
glnvg__setUniforms(gl, call->uniformOffset, call->image); glnvg__setUniforms(gl, call->uniformOffset, call->image);
glnvg__checkError(gl, "convex fill"); glnvg__checkError(gl, "convex fill");


for (i = 0; i < npaths; i++)
for (i = 0; i < npaths; i++) {
glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);
if (gl->flags & NVG_ANTIALIAS) {
// Draw fringes // Draw fringes
for (i = 0; i < npaths; i++)
if (paths[i].strokeCount > 0) {
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);
}
} }
} }


@@ -1026,7 +1094,7 @@ static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call)
for (i = 0; i < npaths; i++) for (i = 0; i < npaths; i++)
glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);


// Clear stencil buffer.
// Clear stencil buffer.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
@@ -1064,6 +1132,50 @@ static void glnvg__renderCancel(void* uptr) {
gl->nuniforms = 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) static void glnvg__renderFlush(void* uptr)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1074,7 +1186,6 @@ static void glnvg__renderFlush(void* uptr)
// Setup require GL state. // Setup require GL state.
glUseProgram(gl->shader.prog); glUseProgram(gl->shader.prog);


glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
@@ -1093,6 +1204,10 @@ static void glnvg__renderFlush(void* uptr)
gl->stencilFunc = GL_ALWAYS; gl->stencilFunc = GL_ALWAYS;
gl->stencilFuncRef = 0; gl->stencilFuncRef = 0;
gl->stencilFuncMask = 0xffffffff; 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 #endif


#if NANOVG_GL_USE_UNIFORMBUFFER #if NANOVG_GL_USE_UNIFORMBUFFER
@@ -1122,6 +1237,7 @@ static void glnvg__renderFlush(void* uptr)


for (i = 0; i < gl->ncalls; i++) { for (i = 0; i < gl->ncalls; i++) {
GLNVGcall* call = &gl->calls[i]; GLNVGcall* call = &gl->calls[i];
glnvg__blendFuncSeparate(gl,&call->blendFunc);
if (call->type == GLNVG_FILL) if (call->type == GLNVG_FILL)
glnvg__fill(gl, call); glnvg__fill(gl, call);
else if (call->type == GLNVG_CONVEXFILL) else if (call->type == GLNVG_CONVEXFILL)
@@ -1136,9 +1252,9 @@ static void glnvg__renderFlush(void* uptr)
glDisableVertexAttribArray(1); glDisableVertexAttribArray(1);
#if defined NANOVG_GL3 #if defined NANOVG_GL3
glBindVertexArray(0); glBindVertexArray(0);
#endif
#endif
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(0); glUseProgram(0);
glnvg__bindTexture(gl, 0); glnvg__bindTexture(gl, 0);
} }
@@ -1237,7 +1353,7 @@ static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
vtx->v = v; vtx->v = v;
} }


static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
const float* bounds, const NVGpath* paths, int npaths) const float* bounds, const NVGpath* paths, int npaths)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1249,16 +1365,21 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
if (call == NULL) return; if (call == NULL) return;


call->type = GLNVG_FILL; call->type = GLNVG_FILL;
call->triangleCount = 4;
call->pathOffset = glnvg__allocPaths(gl, npaths); call->pathOffset = glnvg__allocPaths(gl, npaths);
if (call->pathOffset == -1) goto error; if (call->pathOffset == -1) goto error;
call->pathCount = npaths; call->pathCount = npaths;
call->image = paint->image; call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);


if (npaths == 1 && paths[0].convex) if (npaths == 1 && paths[0].convex)
{
call->type = GLNVG_CONVEXFILL; call->type = GLNVG_CONVEXFILL;
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill
}


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths) + 6;
maxverts = glnvg__maxVertCount(paths, npaths) + call->triangleCount;
offset = glnvg__allocVerts(gl, maxverts); offset = glnvg__allocVerts(gl, maxverts);
if (offset == -1) goto error; if (offset == -1) goto error;


@@ -1280,20 +1401,16 @@ static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor,
} }
} }


// Quad
call->triangleOffset = offset;
call->triangleCount = 6;
quad = &gl->verts[call->triangleOffset];
glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);

glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);
glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);
glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);

// Setup uniforms for draw calls // Setup uniforms for draw calls
if (call->type == GLNVG_FILL) { 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); call->uniformOffset = glnvg__allocFragUniforms(gl, 2);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
// Simple shader for stencil // Simple shader for stencil
@@ -1318,7 +1435,7 @@ error:
if (gl->ncalls > 0) gl->ncalls--; if (gl->ncalls > 0) gl->ncalls--;
} }


static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,
static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
float strokeWidth, const NVGpath* paths, int npaths) float strokeWidth, const NVGpath* paths, int npaths)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
@@ -1332,6 +1449,7 @@ static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor
if (call->pathOffset == -1) goto error; if (call->pathOffset == -1) goto error;
call->pathCount = npaths; call->pathCount = npaths;
call->image = paint->image; call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
maxverts = glnvg__maxVertCount(paths, npaths); maxverts = glnvg__maxVertCount(paths, npaths);
@@ -1373,8 +1491,8 @@ error:
if (gl->ncalls > 0) gl->ncalls--; if (gl->ncalls > 0) gl->ncalls--;
} }


static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor,
const NVGvertex* verts, int nverts)
static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor,
const NVGvertex* verts, int nverts, float fringe)
{ {
GLNVGcontext* gl = (GLNVGcontext*)uptr; GLNVGcontext* gl = (GLNVGcontext*)uptr;
GLNVGcall* call = glnvg__allocCall(gl); GLNVGcall* call = glnvg__allocCall(gl);
@@ -1384,6 +1502,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis


call->type = GLNVG_TRIANGLES; call->type = GLNVG_TRIANGLES;
call->image = paint->image; call->image = paint->image;
call->blendFunc = glnvg__blendCompositeOperation(compositeOperation);


// Allocate vertices for all the paths. // Allocate vertices for all the paths.
call->triangleOffset = glnvg__allocVerts(gl, nverts); call->triangleOffset = glnvg__allocVerts(gl, nverts);
@@ -1396,7 +1515,7 @@ static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scis
call->uniformOffset = glnvg__allocFragUniforms(gl, 1); call->uniformOffset = glnvg__allocFragUniforms(gl, 1);
if (call->uniformOffset == -1) goto error; if (call->uniformOffset == -1) goto error;
frag = nvg__fragUniformPtr(gl, call->uniformOffset); frag = nvg__fragUniformPtr(gl, call->uniformOffset);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);
glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, fringe, -1.0f);
frag->type = NSVG_SHADER_IMG; frag->type = NSVG_SHADER_IMG;


return; return;
@@ -1499,7 +1618,15 @@ void nvgDeleteGLES3(NVGcontext* ctx)
nvgDeleteInternal(ctx); nvgDeleteInternal(ctx);
} }


int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#if defined NANOVG_GL2
int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GL3
int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES2
int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#elif defined NANOVG_GLES3
int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)
#endif
{ {
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__allocTexture(gl); GLNVGtexture* tex = glnvg__allocTexture(gl);
@@ -1515,7 +1642,15 @@ int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, i
return tex->id; return tex->id;
} }


GLuint nvglImageHandle(NVGcontext* ctx, int image)
#if defined NANOVG_GL2
GLuint nvglImageHandleGL2(NVGcontext* ctx, int image)
#elif defined NANOVG_GL3
GLuint nvglImageHandleGL3(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES2
GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image)
#elif defined NANOVG_GLES3
GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image)
#endif
{ {
GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;
GLNVGtexture* tex = glnvg__findTexture(gl, image); GLNVGtexture* tex = glnvg__findTexture(gl, image);


+ 29
- 7
dgl/src/nanovg/nanovg_gl_utils.h View File

@@ -30,7 +30,7 @@ typedef struct NVGLUframebuffer NVGLUframebuffer;
// Helper function to create GL frame buffer to render to. // Helper function to create GL frame buffer to render to.
void nvgluBindFramebuffer(NVGLUframebuffer* fb); void nvgluBindFramebuffer(NVGLUframebuffer* fb);
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb);
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);


#endif // NANOVG_GL_UTILS_H #endif // NANOVG_GL_UTILS_H


@@ -64,7 +64,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
memset(fb, 0, sizeof(NVGLUframebuffer)); memset(fb, 0, sizeof(NVGLUframebuffer));


fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);
fb->texture = nvglImageHandle(ctx, fb->image);

#if defined NANOVG_GL2
fb->texture = nvglImageHandleGL2(ctx, fb->image);
#elif defined NANOVG_GL3
fb->texture = nvglImageHandleGL3(ctx, fb->image);
#elif defined NANOVG_GLES2
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
#elif defined NANOVG_GLES3
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
#endif

fb->ctx = ctx;


// frame buffer object // frame buffer object
glGenFramebuffers(1, &fb->fbo); glGenFramebuffers(1, &fb->fbo);
@@ -79,7 +90,18 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);


if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
#ifdef GL_DEPTH24_STENCIL8
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
// Some graphics cards require a depth buffer along with a stencil.
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
#endif // GL_DEPTH24_STENCIL8
goto error;
}


glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
@@ -87,7 +109,7 @@ NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imag
error: error:
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
nvgluDeleteFramebuffer(ctx, fb);
nvgluDeleteFramebuffer(fb);
return NULL; return NULL;
#else #else
NVG_NOTUSED(ctx); NVG_NOTUSED(ctx);
@@ -108,7 +130,7 @@ void nvgluBindFramebuffer(NVGLUframebuffer* fb)
#endif #endif
} }


void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
{ {
#ifdef NANOVG_FBO_VALID #ifdef NANOVG_FBO_VALID
if (fb == NULL) return; if (fb == NULL) return;
@@ -117,14 +139,14 @@ void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb)
if (fb->rbo != 0) if (fb->rbo != 0)
glDeleteRenderbuffers(1, &fb->rbo); glDeleteRenderbuffers(1, &fb->rbo);
if (fb->image >= 0) if (fb->image >= 0)
nvgDeleteImage(ctx, fb->image);
nvgDeleteImage(fb->ctx, fb->image);
fb->ctx = NULL;
fb->fbo = 0; fb->fbo = 0;
fb->rbo = 0; fb->rbo = 0;
fb->texture = 0; fb->texture = 0;
fb->image = -1; fb->image = -1;
free(fb); free(fb);
#else #else
NVG_NOTUSED(ctx);
NVG_NOTUSED(fb); NVG_NOTUSED(fb);
#endif #endif
} }


+ 4160
- 1152
dgl/src/nanovg/stb_image.h
File diff suppressed because it is too large
View File


+ 5011
- 2081
dgl/src/nanovg/stb_truetype.h
File diff suppressed because it is too large
View File


Loading…
Cancel
Save