I have two choices. I can read my image pinmap.jpg
from Resource.rc
or read the image from STBI_Image
library.
// Simple helper function to load an image into a OpenGL texture with common settings
inline bool ReadImageFromResource(GLuint* out_texture, int width, int height) {
// Locate the resource in the application's executable.
HRSRC imageResHandle = FindResource(
NULL, // This component.
MAKEINTRESOURCE(IDR_IMAGE1), // Resource name.
L"Image"); // Resource type.
HRESULT hr = (imageResHandle ? S_OK : E_FAIL);
// Load the resource to the HGLOBAL.
HGLOBAL imageResDataHandle = NULL;
if (SUCCEEDED(hr)) {
imageResDataHandle = LoadResource(NULL, imageResHandle);
hr = (imageResDataHandle ? S_OK : E_FAIL);
}
// Lock the resource to retrieve memory pointer.
LPVOID image_data = NULL;
if (SUCCEEDED(hr)) {
image_data = LockResource(imageResDataHandle);
UnlockResource(imageResDataHandle);
hr = (image_data ? S_OK : E_FAIL);
}
if (image_data == NULL)
return false;
// Create a OpenGL texture identifier
GLuint image_texture;
glGenTextures(1, &image_texture);
glBindTexture(GL_TEXTURE_2D, image_texture);
// Setup filtering parameters for display
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_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non power-of-two textures
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same
// Upload pixels into texture
#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data);
*out_texture = image_texture;
return true;
}
The results is:
When I read the image from STBI-library, then the image looks like this:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// Simple helper function to load an image into a OpenGL texture with common settings
inline bool ReadImageFromSTBI(const char* filename, GLuint* out_texture, int* out_width, int* out_height)
{
// Load from file
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)
return false;
// Create a OpenGL texture identifier
GLuint image_texture;
glGenTextures(1, &image_texture);
glBindTexture(GL_TEXTURE_2D, image_texture);
// Setup filtering parameters for display
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_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non power-of-two textures
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same
// Upload pixels into texture
#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
stbi_image_free(image_data);
*out_texture = image_texture;
*out_width = image_width;
*out_height = image_height;
return true;
}
You see the difference between the color and the image above is shifted due to the C
to the left. It should be at the right side of the image. Notice that the image above is displayed with GL_RBG
and this image is displayed with GL_RGBA
. Else, I cannot display the image.
Question:
It seems to be different data depending on how I load the data of the image. I have looked at the data of the image. The variable image_data
differes.
How do I properly display images with OpenGL if the image is loaded by Resource.rc file and not loaded by file such as STBI-library?
CodePudding user response:
LoadResource
does not decode, decompress, or interpret the resource data in any way. What you get in your image_data
is a pointer to a memory block that contains exactly the contents of your pinmap.jpg
file.
Interestingly, what you have is a BITMAP rather than an actual JPEG file. If it wasn't, you'd get a bunch of noise instead of anything resembling your image. From what my crystal ball tells me, you imported that once has been JPEG into the resource editor with the "Add Resource > Import..." function of Visual Studio. When done that way, it converts the file to a BITMAP, overwriting the original on disk. (Seriously, Microsoft, what were you smoking!?)
The results you observe thus can be explained as follows:
- The horizontal wrap-around offset is due to the BITMAP file and info headers at the beginning of the resource.
- The colors are off because of the BGR <-> RGB mismatch.
If you want to keep the resource as JPEG, you can add it by manually editing the rc file, or by reverting the file after Visual Studio is done with messing it up. However, if you choose this route, you must decompress the JPEG (because OpenGL expects plain RGB data rather than JPEG). One way would be to call stbi_load_from_memory
on that image_data
you got from LockResource
. Stbi will decompress the JPEG into RGB (in that order).
If you want to keep working with the BITMAP resource, then all you need is to skip the headers and flip the channels.
For skipping the headers you parse the BITMAPFILEHEADER
that's located at image_data
. The offset to the pixel data is in the bfOffBits
field in the header:
actual_image_data = (char*)image_data ((BITMAPFILEHEADER*)image_data)->bfOffBits;
For fixing the channel order you can pass GL_BGR
for the format
parameter (not the internalformat
!!) of glTexImage2D
:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, actual_image_data);