vurtun / nuklear

A single-header ANSI C gui library
13.69k stars 1.11k forks source link

Image created in memory triggers "if this triggers you forgot to add a font" #927

Open crazyBaboon opened 5 years ago

crazyBaboon commented 5 years ago

I am trying to modify a function called icon_load() that currently is responsible for loading a .png image into memory, into a function that simply loads an image that is created in heap memory.

Here is the original source code for it, defined in example/extended.c:


static struct nk_image
icon_load(const char *filename)
{
    int x,y,n;
    GLuint tex;
    unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
    if (!data) die("[SDL]: failed to load image: %s", filename);

    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(data);
    return nk_image_id((int)tex);
}

Here is my modified version. Instead of generating an image buffer from a .png file, I generated a 4 x 4 texel GL_RGBA texture on heap memory. I basically replaced stbi_load() (which returns a pointer to stbi_uc just a typedef for unsigned char) by malloc() (which returns a void pointer but it is cast to unsigned char). I have also replaced stbi_image_free() by free()):

static struct nk_image
icon_load(const char *filename)
{
    GLuint tex;

    unsigned char *data = (unsigned char*)malloc(64 * sizeof(char));
    static int messi = 1;    

    for (int i = 0; i < 64; i += 4)
    {   
        data[i + 0] = 4 * i;
        data[i + 1] = 0;
        data[i + 2] = 0;
        data[i + 3] = 0;
        printf("data[%d] = %d\n", i, data[i]);
    }   

    printf("Messi is %d\n", messi);
    messi++; 

    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    free(data);

    return nk_image_id((int)tex);
}

The output:


...
data[54] = 127
data[55] = 255
data[56] = 255
data[57] = 127
data[58] = 255
data[59] = 127
data[60] = 255
data[61] = 127
data[62] = 255
data[63] = 127
Messi is 32
Segmentation fault (core dumped)
...

Looking at the output I assume that malloc() was successful.

The fact that Messi is now 32, indicates that icon_load() was successfully called 31 times for each of the icons/images in lines 821 - 829 , before any rendering was actually done.

Using good old printf() debugging I realized the crash occurs inside function nk_begin_titled():

NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font");

How can this be fixed?

Is there another way in nuklear to display images that are created in the heap memory?

revolucas commented 5 years ago

You need to initialize the font atlas and either add your own fonts or use the default font (requires NK_INCLUDE_DEFAULT_FONT define) then bake. Then don't forget to nk_style_set_font for a font.

No matter your rendering backend or how you load textures, you'll want to pass the texture id to nuklear functions as a struct nk_image. Eventually, in the draw list loop it will spit this back out in a draw command as cmd->texture, where you will call glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);

If you need image loading library you can try DevIL.