Skip to content

Commit

Permalink
Added atlas expand and reset functionality
Browse files Browse the repository at this point in the history
- added function to reset atlas/stash
- added function to expand atlas size
- fixed texture creation bug (used width for height)
  • Loading branch information
memononen committed Mar 22, 2014
1 parent 1f3463c commit 69593e8
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 13 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct FONSparams {
...
void* userPtr;
int (*renderCreate)(void* uptr, int width, int height);
int (*renderResize)(void* uptr, int width, int height);
void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);
void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts);
void (*renderDelete)(void* uptr);
Expand All @@ -69,6 +70,8 @@ struct FONSparams {

- **renderCreate** is called to create renderer for specific API, this is where you should create a texture of given size.
- return 1 of success, or 0 on failure.
- **renderResize** is called to resize the texture. Called when user explicitly expands or resets the atlas texture.
- return 1 of success, or 0 on failure.
- **renderUpdate** is called to update texture data
- _rect_ describes the region of the texture that has changed
- _data_ pointer to full texture data
Expand Down
45 changes: 36 additions & 9 deletions example/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define GLFONTSTASH_IMPLEMENTATION
#include "glfontstash.h"

int debug = 0;
struct FONScontext* fs = NULL;
int size = 90;

void dash(float dx, float dy)
Expand All @@ -48,14 +48,37 @@ void line(float sx, float sy, float ex, float ey)
glEnd();
}

static void expandAtlas(struct FONScontext* stash)
{
int w = 0, h = 0;
fonsGetAtlasSize(stash, &w, &h);
if (w < h)
w *= 2;
else
h *= 2;
fonsExpandAtlas(stash, w, h);
printf("expanded atlas to %d x %d\n", w, h);
}

static void resetAtlas(struct FONScontext* stash)
{
fonsReset(stash, 256,256);
printf("reset atlas to 256 x 256\n");
}

static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
{
(void)scancode;
(void)mods;
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
debug = !debug;

if (key == GLFW_KEY_E && action == GLFW_PRESS)
expandAtlas(fs);

if (key == GLFW_KEY_R && action == GLFW_PRESS) {
resetAtlas(fs);
}

if (key == 265 && action == GLFW_PRESS) {
size += 10;
Expand All @@ -69,10 +92,11 @@ static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
void stashError(void* uptr, int error, int val)
{
(void)uptr;
// struct FONScontext* fs = (struct FONScontext*)uptr;
struct FONScontext* stash = (struct FONScontext*)uptr;
switch (error) {
case FONS_ATLAS_FULL:
printf("atlas full\n");
expandAtlas(stash);
break;
case FONS_SCRATCH_FULL:
printf("scratch full, tried to allocate %d has %d\n", val, FONS_SCRATCH_BUF_SIZE);
Expand All @@ -95,8 +119,6 @@ int main()
GLFWwindow* window;
const GLFWvidmode* mode;

struct FONScontext* fs = NULL;

if (!glfwInit())
return -1;

Expand Down Expand Up @@ -143,7 +165,9 @@ int main()
{
float sx, sy, dx, dy, lh = 0;
int width, height;
int atlasw, atlash;
unsigned int white,black,brown,blue;
char msg[64];
glfwGetFramebufferSize(window, &width, &height);
// Update and render
glViewport(0, 0, width, height);
Expand Down Expand Up @@ -202,10 +226,13 @@ int main()
fonsSetSize(fs, 14);
fonsSetFont(fs, fontNormal);
fonsSetColor(fs, white);
dx = fonsDrawText(fs, 20, height-20,"Press UP / DOWN keys to change font size and to trigger atlas full callback.",NULL);
fonsDrawText(fs, 20, height-20,"Press UP / DOWN keys to change font size and to trigger atlas full callback, R to reset atlas, E to expand atlas.",NULL);

fonsGetAtlasSize(fs, &atlasw, &atlash);
snprintf(msg, sizeof(msg), "Atlas: %d × %d", atlasw, atlash);
fonsDrawText(fs, 20, height-50, msg, NULL);

// if (debug)
fonsDrawDebug(fs, 800.0, 50.0);
fonsDrawDebug(fs, width - atlasw - 20, 20.0);

glEnable(GL_DEPTH_TEST);

Expand Down
171 changes: 170 additions & 1 deletion src/fontstash.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct FONSparams {
unsigned char flags;
void* userPtr;
int (*renderCreate)(void* uptr, int width, int height);
int (*renderResize)(void* uptr, int width, int height);
void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);
void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts);
void (*renderDelete)(void* uptr);
Expand All @@ -80,6 +81,12 @@ struct FONScontext* fonsCreateInternal(struct FONSparams* params);
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 fonsReset(struct FONScontext* stash, int width, int height);

// Add fonts
int fonsAddFont(struct FONScontext* s, const char* name, const char* path);
Expand Down Expand Up @@ -525,6 +532,28 @@ static void fons__atlasRemoveNode(struct FONSatlas* atlas, int idx)
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)
{
int i;
Expand Down Expand Up @@ -1297,11 +1326,25 @@ int fonsTextIterNext(struct FONScontext* stash, struct FONStextIter* iter, struc

void fonsDrawDebug(struct FONScontext* stash, float x, float y)
{
int i;
int w = stash->params.width;
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);

// Draw background
fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);
fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff);

fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);
fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff);
fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);

// Draw texture
fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);
fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);
fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff);
Expand All @@ -1310,6 +1353,22 @@ void fonsDrawDebug(struct FONScontext* stash, float x, float y)
fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff);
fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);

// Drawbug draw atlas
for (i = 0; i < stash->atlas->nnodes; i++) {
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);
}

Expand Down Expand Up @@ -1451,5 +1510,115 @@ void fonsSetErrorCallback(struct FONScontext* stash, void (*callback)(void* uptr
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 fonsReset(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
17 changes: 14 additions & 3 deletions src/glfontstash.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,27 @@ struct GLFONScontext {
static int glfons__renderCreate(void* userPtr, int width, int height)
{
struct GLFONScontext* gl = (struct GLFONScontext*)userPtr;
(void)height;
// Create may be called multiple times, delete existing texture.
if (gl->tex != 0) {
glDeleteTextures(1, &gl->tex);
gl->tex = 0;
}
glGenTextures(1, &gl->tex);
if (!gl->tex) return 0;
gl->width = width;
gl->height = width;
gl->height = height;
glBindTexture(GL_TEXTURE_2D, gl->tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gl->width, gl->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
return 1;
}

static int glfons__renderResize(void* userPtr, int width, int height)
{
// Reuse create to resize too.
return glfons__renderCreate(userPtr, width, height);
}

static void glfons__renderUpdate(void* userPtr, int* rect, const unsigned char* data)
{
struct GLFONScontext* gl = (struct GLFONScontext*)userPtr;
Expand Down Expand Up @@ -86,7 +96,7 @@ static void glfons__renderDraw(void* userPtr, const float* verts, const float* t
static void glfons__renderDelete(void* userPtr)
{
struct GLFONScontext* gl = (struct GLFONScontext*)userPtr;
if (gl->tex)
if (gl->tex != 0)
glDeleteTextures(1, &gl->tex);
gl->tex = 0;
free(gl);
Expand All @@ -107,6 +117,7 @@ struct FONScontext* glfonsCreate(int width, int height, int flags)
params.height = height;
params.flags = (unsigned char)flags;
params.renderCreate = glfons__renderCreate;
params.renderResize = glfons__renderResize;
params.renderUpdate = glfons__renderUpdate;
params.renderDraw = glfons__renderDraw;
params.renderDelete = glfons__renderDelete;
Expand Down

0 comments on commit 69593e8

Please sign in to comment.