| @@ -38,11 +38,23 @@ enum FONSalign { | |||||
| FONS_ALIGN_BASELINE = 1<<6, // Default | FONS_ALIGN_BASELINE = 1<<6, // Default | ||||
| }; | }; | ||||
| enum FONSerrorCode { | |||||
| // Font atlas is full. | |||||
| FONS_ATLAS_FULL = 1, | |||||
| // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. | |||||
| FONS_SCRATCH_FULL = 2, | |||||
| // Calls to fonsPushState has craeted too large stack, if you need deep state stack bump up FONS_MAX_STATES. | |||||
| FONS_STATES_OVERFLOW = 3, | |||||
| // Trying to pop too many states fonsPopState(). | |||||
| FONS_STATES_UNDERFLOW = 4, | |||||
| }; | |||||
| struct FONSparams { | struct FONSparams { | ||||
| int width, height; | int width, height; | ||||
| unsigned char flags; | unsigned char flags; | ||||
| void* userPtr; | void* userPtr; | ||||
| int (*renderCreate)(void* uptr, int width, int height); | int (*renderCreate)(void* uptr, int width, int height); | ||||
| int (*renderResize)(void* uptr, int width, int height); | |||||
| void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); | void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); | ||||
| void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); | void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); | ||||
| void (*renderDelete)(void* uptr); | void (*renderDelete)(void* uptr); | ||||
| @@ -68,6 +80,14 @@ struct FONStextIter { | |||||
| struct FONScontext* fonsCreateInternal(struct FONSparams* params); | struct FONScontext* fonsCreateInternal(struct FONSparams* params); | ||||
| void fonsDeleteInternal(struct FONScontext* s); | void fonsDeleteInternal(struct FONScontext* s); | ||||
| void fonsSetErrorCallback(struct FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); | |||||
| // Returns current atlas size. | |||||
| void fonsGetAtlasSize(struct FONScontext* s, int* width, int* height); | |||||
| // Expands the atlas size. | |||||
| int fonsExpandAtlas(struct FONScontext* s, int width, int height); | |||||
| // Reseta the whole stash. | |||||
| int fonsResetAtlas(struct FONScontext* stash, int width, int height); | |||||
| // Add fonts | // Add fonts | ||||
| int fonsAddFont(struct FONScontext* s, const char* name, const char* path); | int fonsAddFont(struct FONScontext* s, const char* name, const char* path); | ||||
| int fonsAddFontMem(struct FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); | int fonsAddFontMem(struct FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); | ||||
| @@ -377,6 +397,8 @@ struct FONScontext | |||||
| int nscratch; | int nscratch; | ||||
| struct FONSstate states[FONS_MAX_STATES]; | struct FONSstate states[FONS_MAX_STATES]; | ||||
| int nstates; | int nstates; | ||||
| void (*handleError)(void* uptr, int error, int val); | |||||
| void* errorUptr; | |||||
| }; | }; | ||||
| static void* fons__tmpalloc(size_t size, void* up) | static void* fons__tmpalloc(size_t size, void* up) | ||||
| @@ -384,8 +406,11 @@ static void* fons__tmpalloc(size_t size, void* up) | |||||
| unsigned char* ptr; | unsigned char* ptr; | ||||
| struct FONScontext* stash = (struct FONScontext*)up; | struct FONScontext* stash = (struct FONScontext*)up; | ||||
| if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) | |||||
| if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { | |||||
| if (stash->handleError) | |||||
| stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); | |||||
| return NULL; | return NULL; | ||||
| } | |||||
| ptr = stash->scratch + stash->nscratch; | ptr = stash->scratch + stash->nscratch; | ||||
| stash->nscratch += (int)size; | stash->nscratch += (int)size; | ||||
| return ptr; | return ptr; | ||||
| @@ -507,6 +532,28 @@ static void fons__atlasRemoveNode(struct FONSatlas* atlas, int idx) | |||||
| atlas->nnodes--; | atlas->nnodes--; | ||||
| } | } | ||||
| static void fons__atlasExpand(struct FONSatlas* atlas, int w, int h) | |||||
| { | |||||
| // Insert node for empty space | |||||
| if (w > atlas->width) | |||||
| fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); | |||||
| atlas->width = w; | |||||
| atlas->height = h; | |||||
| } | |||||
| static void fons__atlasReset(struct FONSatlas* atlas, int w, int h) | |||||
| { | |||||
| atlas->width = w; | |||||
| atlas->height = h; | |||||
| atlas->nnodes = 0; | |||||
| // Init root node. | |||||
| atlas->nodes[0].x = 0; | |||||
| atlas->nodes[0].y = 0; | |||||
| atlas->nodes[0].width = (short)w; | |||||
| atlas->nnodes++; | |||||
| } | |||||
| static int fons__atlasAddSkylineLevel(struct FONSatlas* atlas, int idx, int x, int y, int w, int h) | static int fons__atlasAddSkylineLevel(struct FONSatlas* atlas, int idx, int x, int y, int w, int h) | ||||
| { | { | ||||
| int i; | int i; | ||||
| @@ -597,6 +644,26 @@ static int fons__atlasAddRect(struct FONSatlas* atlas, int rw, int rh, int* rx, | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| static void fons__addWhiteRect(struct FONScontext* stash, int w, int h) | |||||
| { | |||||
| int x, y, gx, gy; | |||||
| unsigned char* dst; | |||||
| if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) | |||||
| return; | |||||
| // Rasterize | |||||
| dst = &stash->texData[gx + gy * stash->params.width]; | |||||
| for (y = 0; y < h; y++) { | |||||
| for (x = 0; x < w; x++) | |||||
| dst[x] = 0xff; | |||||
| dst += stash->params.width; | |||||
| } | |||||
| stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); | |||||
| stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); | |||||
| stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); | |||||
| stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); | |||||
| } | |||||
| struct FONScontext* fonsCreateInternal(struct FONSparams* params) | struct FONScontext* fonsCreateInternal(struct FONSparams* params) | ||||
| { | { | ||||
| @@ -639,6 +706,9 @@ struct FONScontext* fonsCreateInternal(struct FONSparams* params) | |||||
| stash->dirtyRect[2] = 0; | stash->dirtyRect[2] = 0; | ||||
| stash->dirtyRect[3] = 0; | stash->dirtyRect[3] = 0; | ||||
| // Add white rect at 0,0 for debug drawing. | |||||
| fons__addWhiteRect(stash, 2,2); | |||||
| fonsPushState(stash); | fonsPushState(stash); | ||||
| fonsClearState(stash); | fonsClearState(stash); | ||||
| @@ -686,8 +756,11 @@ void fonsSetFont(struct FONScontext* stash, int font) | |||||
| void fonsPushState(struct FONScontext* stash) | void fonsPushState(struct FONScontext* stash) | ||||
| { | { | ||||
| if (stash->nstates >= FONS_MAX_STATES) | |||||
| if (stash->nstates >= FONS_MAX_STATES) { | |||||
| if (stash->handleError) | |||||
| stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); | |||||
| return; | return; | ||||
| } | |||||
| if (stash->nstates > 0) | if (stash->nstates > 0) | ||||
| memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(struct FONSstate)); | memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(struct FONSstate)); | ||||
| stash->nstates++; | stash->nstates++; | ||||
| @@ -695,8 +768,11 @@ void fonsPushState(struct FONScontext* stash) | |||||
| void fonsPopState(struct FONScontext* stash) | void fonsPopState(struct FONScontext* stash) | ||||
| { | { | ||||
| if (stash->nstates <= 1) | |||||
| if (stash->nstates <= 1) { | |||||
| if (stash->handleError) | |||||
| stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); | |||||
| return; | return; | ||||
| } | |||||
| stash->nstates--; | stash->nstates--; | ||||
| } | } | ||||
| @@ -911,7 +987,7 @@ static struct FONSglyph* fons__getGlyph(struct FONScontext* stash, struct FONSfo | |||||
| struct FONSglyph* glyph = NULL; | struct FONSglyph* glyph = NULL; | ||||
| unsigned int h; | unsigned int h; | ||||
| float size = isize/10.0f; | float size = isize/10.0f; | ||||
| int pad; | |||||
| int pad, added; | |||||
| unsigned char* bdst; | unsigned char* bdst; | ||||
| unsigned char* dst; | unsigned char* dst; | ||||
| @@ -939,8 +1015,13 @@ static struct FONSglyph* fons__getGlyph(struct FONScontext* stash, struct FONSfo | |||||
| gh = y1-y0 + pad*2; | gh = y1-y0 + pad*2; | ||||
| // Find free spot for the rect in the atlas | // Find free spot for the rect in the atlas | ||||
| if (fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy) == 0) | |||||
| return NULL; | |||||
| 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; | |||||
| // Init glyph. | // Init glyph. | ||||
| glyph = fons__allocGlyph(font); | glyph = fons__allocGlyph(font); | ||||
| @@ -1133,6 +1214,9 @@ float fonsDrawText(struct FONScontext* stash, | |||||
| scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); | ||||
| if (end == NULL) | |||||
| end = str + strlen(str); | |||||
| // Align horizontally | // Align horizontally | ||||
| if (state->align & FONS_ALIGN_LEFT) { | if (state->align & FONS_ALIGN_LEFT) { | ||||
| // empty | // empty | ||||
| @@ -1146,10 +1230,7 @@ float fonsDrawText(struct FONScontext* stash, | |||||
| // Align vertically. | // Align vertically. | ||||
| y += fons__getVertAlign(stash, font, state->align, isize); | y += fons__getVertAlign(stash, font, state->align, isize); | ||||
| if (end == NULL) | |||||
| end = str + strlen(str); | |||||
| for (; *str; ++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); | ||||
| @@ -1243,11 +1324,25 @@ int fonsTextIterNext(struct FONScontext* stash, struct FONStextIter* iter, struc | |||||
| void fonsDrawDebug(struct FONScontext* stash, float x, float y) | void fonsDrawDebug(struct FONScontext* stash, float x, float y) | ||||
| { | { | ||||
| int i; | |||||
| int w = stash->params.width; | int w = stash->params.width; | ||||
| int h = stash->params.height; | int h = stash->params.height; | ||||
| if (stash->nverts+6 > FONS_VERTEX_COUNT) | |||||
| float u = w == 0 ? 0 : (1.0f / w); | |||||
| float v = h == 0 ? 0 : (1.0f / h); | |||||
| if (stash->nverts+6+6 > FONS_VERTEX_COUNT) | |||||
| fons__flush(stash); | fons__flush(stash); | ||||
| // Draw background | |||||
| fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); | |||||
| fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); | |||||
| fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); | |||||
| fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); | |||||
| fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); | |||||
| fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); | |||||
| // Draw texture | |||||
| fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); | ||||
| fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); | ||||
| fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); | fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); | ||||
| @@ -1255,6 +1350,24 @@ void fonsDrawDebug(struct FONScontext* stash, float x, float y) | |||||
| fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); | ||||
| fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); | fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); | ||||
| fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); | ||||
| // Drawbug draw atlas | |||||
| for (i = 0; i < stash->atlas->nnodes; i++) { | |||||
| struct FONSatlasNode* n = &stash->atlas->nodes[i]; | |||||
| if (stash->nverts+6 > FONS_VERTEX_COUNT) | |||||
| fons__flush(stash); | |||||
| fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); | |||||
| fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); | |||||
| fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); | |||||
| fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); | |||||
| fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); | |||||
| fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); | |||||
| } | |||||
| fons__flush(stash); | |||||
| } | } | ||||
| float fonsTextBounds(struct FONScontext* stash, | float fonsTextBounds(struct FONScontext* stash, | ||||
| @@ -1289,7 +1402,7 @@ float fonsTextBounds(struct FONScontext* stash, | |||||
| if (end == NULL) | if (end == NULL) | ||||
| end = str + strlen(str); | end = str + strlen(str); | ||||
| for (; *str; ++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); | ||||
| @@ -1388,4 +1501,122 @@ void fonsDeleteInternal(struct FONScontext* stash) | |||||
| free(stash); | free(stash); | ||||
| } | } | ||||
| void fonsSetErrorCallback(struct FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) | |||||
| { | |||||
| if (stash == NULL) return; | |||||
| stash->handleError = callback; | |||||
| stash->errorUptr = uptr; | |||||
| } | |||||
| void fonsGetAtlasSize(struct FONScontext* stash, int* width, int* height) | |||||
| { | |||||
| if (stash == NULL) return; | |||||
| *width = stash->params.width; | |||||
| *height = stash->params.height; | |||||
| } | |||||
| int fonsExpandAtlas(struct FONScontext* stash, int width, int height) | |||||
| { | |||||
| int i, maxy = 0; | |||||
| unsigned char* data = NULL; | |||||
| if (stash == NULL) return 0; | |||||
| width = fons__maxi(width, stash->params.width); | |||||
| height = fons__maxi(height, stash->params.height); | |||||
| if (width == stash->params.width && height == stash->params.height) | |||||
| return 1; | |||||
| // Flush pending glyphs. | |||||
| fons__flush(stash); | |||||
| // Create new texture | |||||
| if (stash->params.renderResize != NULL) { | |||||
| if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) | |||||
| return 0; | |||||
| } | |||||
| // Copy old texture data over. | |||||
| data = (unsigned char*)malloc(width * height); | |||||
| if (data == NULL) | |||||
| return 0; | |||||
| for (i = 0; i < stash->params.height; i++) { | |||||
| unsigned char* dst = &data[i*width]; | |||||
| unsigned char* src = &stash->texData[i*stash->params.width]; | |||||
| memcpy(dst, src, stash->params.width); | |||||
| if (width > stash->params.width) | |||||
| memset(dst+stash->params.width, 0, width - stash->params.width); | |||||
| } | |||||
| if (height > stash->params.height) | |||||
| memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); | |||||
| free(stash->texData); | |||||
| stash->texData = data; | |||||
| // Increase atlas size | |||||
| fons__atlasExpand(stash->atlas, width, height); | |||||
| // Add axisting data as dirty. | |||||
| for (i = 0; i < stash->atlas->nnodes; i++) | |||||
| maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); | |||||
| stash->dirtyRect[0] = 0; | |||||
| stash->dirtyRect[1] = 0; | |||||
| stash->dirtyRect[2] = stash->params.width; | |||||
| stash->dirtyRect[3] = maxy; | |||||
| stash->params.width = width; | |||||
| stash->params.height = height; | |||||
| stash->itw = 1.0f/stash->params.width; | |||||
| stash->ith = 1.0f/stash->params.height; | |||||
| return 1; | |||||
| } | |||||
| int fonsResetAtlas(struct FONScontext* stash, int width, int height) | |||||
| { | |||||
| int i, j; | |||||
| if (stash == NULL) return 0; | |||||
| // Flush pending glyphs. | |||||
| fons__flush(stash); | |||||
| // Create new texture | |||||
| if (stash->params.renderResize != NULL) { | |||||
| if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) | |||||
| return 0; | |||||
| } | |||||
| // Reset atlas | |||||
| fons__atlasReset(stash->atlas, width, height); | |||||
| // Clear texture data. | |||||
| stash->texData = (unsigned char*)realloc(stash->texData, width * height); | |||||
| if (stash->texData == NULL) return 0; | |||||
| memset(stash->texData, 0, width * height); | |||||
| // Reset dirty rect | |||||
| stash->dirtyRect[0] = width; | |||||
| stash->dirtyRect[1] = height; | |||||
| stash->dirtyRect[2] = 0; | |||||
| stash->dirtyRect[3] = 0; | |||||
| // Reset cached glyphs | |||||
| for (i = 0; i < stash->nfonts; i++) { | |||||
| struct FONSfont* font = stash->fonts[i]; | |||||
| font->nglyphs = 0; | |||||
| for (j = 0; j < FONS_HASH_LUT_SIZE; j++) | |||||
| font->lut[j] = -1; | |||||
| } | |||||
| stash->params.width = width; | |||||
| stash->params.height = height; | |||||
| stash->itw = 1.0f/stash->params.width; | |||||
| stash->ith = 1.0f/stash->params.height; | |||||
| // Add white rect at 0,0 for debug drawing. | |||||
| fons__addWhiteRect(stash, 2,2); | |||||
| return 1; | |||||
| } | |||||
| #endif | #endif | ||||