Signed-off-by: falkTX <falktx@falktx.com>pull/272/head
@@ -1,4 +1,6 @@ | |||||
language: cpp | |||||
os: linux | |||||
dist: bionic | |||||
cache: | cache: | ||||
directories: | directories: | ||||
@@ -13,8 +13,11 @@ if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then | |||||
sudo dpkg --add-architecture i386 | sudo dpkg --add-architecture i386 | ||||
fi | fi | ||||
sudo add-apt-repository -y ppa:kxstudio-debian/kxstudio | |||||
sudo add-apt-repository -y ppa:kxstudio-debian/mingw | |||||
sudo add-apt-repository -y ppa:kxstudio-debian/toolchain | |||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test | |||||
sudo apt-get update -qq | |||||
sudo apt-get install kxstudio-repos | |||||
sudo apt-get update -qq | sudo apt-get update -qq | ||||
if [ "${TARGET}" = "win32" ] || [ "${TARGET}" = "win64" ]; then | |||||
sudo apt-get install -y -o APT::Immediate-Configure=false libc6 libc6:i386 libgcc-s1:i386 | |||||
fi | |||||
sudo apt-get install -y -f |
@@ -53,6 +53,7 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
# undef DGL_EXT | # undef DGL_EXT | ||||
#endif | #endif | ||||
@@ -66,20 +67,32 @@ 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) | ||||
{ | { | ||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
# if defined(__GNUC__) && (__GNUC__ >= 10) | |||||
# pragma GCC diagnostic push | |||||
# pragma GCC diagnostic ignored "-Wcast-function-type" | |||||
# endif | |||||
static bool needsInit = true; | static bool needsInit = true; | ||||
# define DGL_EXT(PROC, func) \ | # define DGL_EXT(PROC, func) \ | ||||
if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | if (needsInit) func = (PROC) wglGetProcAddress ( #func ); \ | ||||
@@ -111,8 +124,12 @@ DGL_EXT(PFNGLUNIFORM2FVPROC, glUniform2fv) | |||||
DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | DGL_EXT(PFNGLUNIFORM4FVPROC, glUniform4fv) | ||||
DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | DGL_EXT(PFNGLUSEPROGRAMPROC, glUseProgram) | ||||
DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | DGL_EXT(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer) | ||||
DGL_EXT(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate) | |||||
# undef DGL_EXT | # undef DGL_EXT | ||||
needsInit = false; | needsInit = false; | ||||
# if defined(__GNUC__) && (__GNUC__ >= 10) | |||||
# pragma GCC diagnostic pop | |||||
# endif | |||||
#endif | #endif | ||||
return nvgCreateGL(flags); | return nvgCreateGL(flags); | ||||
} | } | ||||
@@ -921,8 +938,7 @@ bool NanoVG::loadSharedResources() | |||||
using namespace dpf_resources; | using namespace dpf_resources; | ||||
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, | |||||
(const uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; | |||||
return nvgCreateFontMem(fContext, NANOVG_DEJAVU_SANS_TTF, (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0) >= 0; | |||||
} | } | ||||
#endif | #endif | ||||
@@ -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); | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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); | ||||
@@ -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 | ||||
} | } | ||||
@@ -316,7 +316,7 @@ protected: | |||||
LV2_Atom_MidiEvent atomMidiEvent; | LV2_Atom_MidiEvent atomMidiEvent; | ||||
atomMidiEvent.atom.size = 3; | atomMidiEvent.atom.size = 3; | ||||
atomMidiEvent.atom.type = fMidiEventURID; | |||||
atomMidiEvent.atom.type = fURIDs.midiEvent; | |||||
atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); | atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80); | ||||
atomMidiEvent.data[1] = note; | atomMidiEvent.data[1] = note; | ||||