1

I have a function which loads an image from file and successfully creates an opengl texture from it.

/**
 * @brief Loads a texture from file and generates an OpenGL texture from it.
 * 
 * @param filename Path to the image file.
 * @param out_texture Texture id the results are bound to.
 * @param out_width Value pointer the resulting image width is written to.
 * @param out_height Value pointer the resulting image height is written to.
 * @param flip_image Stb indicator for flipping the image.
 * @return true Image has been successfully loaded.
 * @return false Failed loading the image.
 */
bool LoadTextureFromFile(const char *filename, GLuint *out_texture, int *out_width, int *out_height, bool flip_image = false)
{
    // Load from file
    int image_width = 0;
    int image_height = 0;
    stbi_set_flip_vertically_on_load(flip_image);
    unsigned char *image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
    
    if (image_data == NULL)
    {
        std::cout << "ERROR::Tools::GLHelper::LoadTextureFromFile  -   Failed to load image from file '" << filename << "'." << std::endl;

        stbi_image_free(image_data);
        return false;
    }

    // Create a OpenGL texture identifier
    GLuint image_texture;
    glGenTextures(1, &image_texture);
    glBindTexture(GL_TEXTURE_2D, image_texture);

    // Set texture wrapping parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
    glGenerateMipmap(GL_TEXTURE_2D);

    *out_texture = image_texture;
    *out_width = image_width;
    *out_height = image_height;

    stbi_image_free(image_data);
    return true;
}

What I am trying to do is to load an image via stbi_load like above and save it as BLOB to sqlite. Afterwards I want to be able to load the very same blob and create an opengl texture from it in a separate function.

In the first step I created a function which only loads the image:

unsigned char *ImageDataFromFile(const char *filename)
{
    int image_width = 0;
    int image_height = 0;
    unsigned char *image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);

    if (image_data == NULL)
    {
        std::cout << "ERROR::Tools::GLHelper::LoadTextureFromFile  -   Failed to load image from file '" << filename << "'." << std::endl;

        stbi_image_free(image_data);
    }

    return image_data;
}

In the next step I want to store this data into my sqlite database:

void DBConnector::AddImage(std::string name, unsigned char *data)
{
    sqlite3_stmt *stmt;
    int err = sqlite3_prepare_v2(db, "INSERT INTO images (name, img_data) VALUES (?, ?)", -1, &stmt, NULL);

    if (err != SQLITE_OK)
    {
        std::cout << "ERROR::DATA::DBConnector  -   Failed to prepare sqlite3 statement: \n"
                      << sqlite3_errmsg(db) << std::endl;
    }

    sqlite3_bind_text(stmt, 1, name.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_blob(stmt, 2, data, -1, SQLITE_TRANSIENT);
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);

    return;
}

Finally I connect the pieces:

unsigned char *image_data = Tools::FileHelper::ImageDataFromFile(selected_filepath.c_str());
db->AddImage("Foo", image_data);

What happens is that seemingly arbitrary data ends up in the database, which is definetly not image data. Sometimes the entries are just empty.

I suspect that I am handling the return type of stbi_load incorrectly, forcing random memory data into the database. Extract from stbi documentation:

The return value from an image loader is an 'unsigned char *' which points to the pixel data..

As I understand it I am simply passing the array pointer to sqlite3_bind_blob which accepts const void * just like glTexImage2D does. So why is it working for the one but not for the other? Or could the error source be somewhere else?

Edit

I also tried something else. Normally I pass -1 for size when calling i.e. sqlite3_bind_text, because the call will then automatically search for a null terminator. So I thought that I might have to pass the correct size in bytes when calling sqlite3_bind_blob because there might be no terminator there. So for an image with the size of 225 x 225 with 3 channels, I passed 225 * 225 * 3 as size parameter. Unfortunately this did not work either.

チーズパン
  • 2,752
  • 8
  • 42
  • 63
  • 1
    You'll certainly have to pass a size to `sqlite3_bind_blob` -- the documentation is pretty clear about that. But are you sure 255x255x3 is the right size? Isn't there some header information as well? – Kevin Boone Sep 09 '20 at 14:14
  • 1
    Doesn't your call to `stbi_load` specify four channels (is that what the last argument means?) – Kevin Boone Sep 09 '20 at 14:20
  • @KevinBoone yes I do specify four channels, the image I am mostly working with has only three channels. I also tried reading the fourth parameter from the `stbi_load` call which clearly states that there are only 3 channels. However I also tried passing `225 * 225 * 4`. I don't know about the header information, will try to get more information. Although the docs do not mention any `The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components` – チーズパン Sep 09 '20 at 14:25
  • 1
    Perhaps use a debugger to examine `data` before the call to `sqlite3_bind_blob`, or just dump it in hex to a stdout? If it looks like garbage at that point, you're probably storing garbage. Incidentally, I'm not sure SQLITE_TRANSIENT is necessary here, and will probably fail if you don't specify a length. – Kevin Boone Sep 09 '20 at 14:34
  • @KevinBoone I am using the newest sqlite viewer version for the database and I have to admit the binary data in the viewer does look like grabage. I just cannot understand why. Like I mentioned above I am using the very same stbi_load for creating opengl textures and it works just fine. – チーズパン Sep 09 '20 at 14:38
  • 1
    Sure, but does it look like garbage in memory, before you store it? Can you compare what you see in the sqlite3 viewer with what you see in memory? I'm afraid I have no idea what's causing your problem -- all I know is how I'd go about troubleshooting it :/ – Kevin Boone Sep 09 '20 at 14:52

0 Answers0