I am attempting to load an icon from a third party executable for use in SDL_SetWindowIcon.
Based on some debugging, I believe I am loading the icon correctly, but I don't seem to be populating the SDL_Surface correctly.
Here's what I'm trying currently:
//attempts to load an icon resource from the specified assembly
//uses rcName if provided, or rcId (as an int resource id) if rcName is null
//if successful, convert and set it as SDL's window icon
void LoadIconFrom(std::string assembly, int rcId, const char* rcName) {
//get a module handle for the target assembly
HMODULE hModule = LoadLibrary(assembly.c_str());
if (hModule == NULL) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "hModule is null!");
return;
}
//get a handle for the desired icon
HICON hIcon = NULL;
if (rcName == NULL) {
hIcon = LoadIcon(hModule, MAKEINTRESOURCE(rcId));
}
else {
hIcon = LoadIcon(hModule, rcName);
}
if (hIcon == NULL) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "hIcon is null!");
return;
}
//load some info regarding the selected icon, make sure it has bitmap data
ICONINFO ii;
if (!GetIconInfo(hIcon, &ii)) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "IconInfo is null!");
return;
}
if (!ii.hbmColor) {
ShowError("Icon Error", "Icon does not have bitmap data!");
return;
}
//attempt to determine the size of the icon
int iWidth, iHeight;
BITMAP bm;
if (!GetObject(ii.hbmColor, sizeof(bm), &bm)) {
ShowError("Icon Error", "Could not read bitmap data!");
return;
}
iWidth = bm.bmWidth;
iHeight = bm.bmHeight;
//ShowError("Icon Win!!!",(std::string("Loaded icon of size: ") + std::to_string(iWidth) + "x" + std::to_string(iHeight)).c_str());
icon = SDL_CreateRGBSurface(SDL_SWSURFACE, bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
Uint8 * bits = NULL;
Uint8 * temp = NULL;
bits = new Uint8[bm.bmWidthBytes*bm.bmHeight];
temp = new Uint8[bm.bmWidthBytes*bm.bmHeight];
memcpy(temp, bm.bmBits, bm.bmWidthBytes*bm.bmHeight);
Uint8 *ptemp;
Uint8 *pbits = bits;
for (int j = bm.bmHeight - 1; j >= 0; j--)
{
ptemp = temp + j * bm.bmWidthBytes;
for (int x = 0; x < bm.bmWidthBytes; x++)
{
*pbits = *ptemp;
pbits++;
ptemp++;
}
}
if (SDL_MUSTLOCK(icon)) SDL_LockSurface(icon);
memcpy(icon->pixels, bits, bm.bmWidthBytes*bm.bmHeight);
if (SDL_MUSTLOCK(icon)) SDL_UnlockSurface(icon);
delete[] bits;
delete[] temp;
SDL_SetWindowIcon(mainWindow, icon);
}
It crashes at SDL_SetWindowIcon. The last bit is supposed to flip the image over, which I believe to be required from examples I've found. Removing that part doesn't seem to have any effect.
If I don't modify "bits" at all, and leave it empty, the program doesn't crash but I get a blank icon.
What am I missing here?
Edit: I have also tried CreateRGBSurfaceFrom, which seems to have identical behaviour - either blank on a blank array or crashes if there's any data in it.
Edit 2: "icon" is an SDL_Surface*, declared elsewhere.
Edit 3: Using SDL 2.0.7.
Edit 4: FIXED CODE :
//attempts to load an icon resource from the specified assembly
//uses rcName if provided, or rcId (as an int resource id) if rcName is null
//if successful, convert and set it as SDL's window icon
void LoadIconFrom(std::string assembly, int rcId, const char* rcName) {
//todo: make error throwing here only happen in debug, while
//release should just continue on its merry way, iconless
//get a module handle for the target assembly
HMODULE hModule = LoadLibrary(assembly.c_str());
if (hModule == NULL) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "hModule is null!");
return;
}
//get a handle for the desired icon
HICON hIcon = NULL;
if (rcName == NULL) {
hIcon = LoadIcon(hModule, MAKEINTRESOURCE(rcId));
}
else {
hIcon = LoadIcon(hModule, rcName);
}
if (hIcon == NULL) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "hIcon is null!");
return;
}
//load some info regarding the selected icon, make sure it has bitmap data
ICONINFO ii;
if (!GetIconInfo(hIcon, &ii)) {
ShowError((std::string("Icon Error ") + std::to_string(GetLastError())).c_str(), "IconInfo is null!");
return;
}
if (!ii.hbmColor) {
ShowError("Icon Error", "Icon does not have bitmap data!");
return;
}
BITMAP bm;
if (!GetObject(ii.hbmColor, sizeof(bm), &bm)) {
ShowError("Icon Error", "Bitmap data does not exist!");
return;
}
HBITMAP hbitmap = (HBITMAP)CopyImage(ii.hbmColor, IMAGE_BITMAP, bm.bmWidth, bm.bmHeight, LR_CREATEDIBSECTION);
if (!GetObject(hbitmap, sizeof(BITMAP), &bm)) {
ShowError("Icon Error", "Could not read bitmap data!");
return;
}
// Verify that the data we have obtained is a 32bpp bitmap with color info
if (bm.bmBitsPixel != 32) {
ShowError("Icon Error", "Bitmap data not in a 32bpp format!");
return;
}
if (bm.bmBits == NULL) {
ShowError("Icon Error", "Extracted bitmap data is null!");
return;
}
// Create an SDL surface - note the mask varies by platform endian-ness
int rmask = 0x00FF0000;
int gmask = 0x0000FF00;
int bmask = 0x000000FF;
int amask = 0xFF000000;
icon = SDL_CreateRGBSurface(SDL_SWSURFACE, bm.bmWidth, bm.bmHeight, bm.bmBitsPixel, rmask, gmask, bmask, amask);
if (icon == NULL) {
ShowError("Icon Error", (std::string("SDL surface creation failed: ") + SDL_GetError()).c_str());
return;
}
// Re-orient the bytes to flip the image vertically
Uint8 * bits = NULL;
Uint8 * temp = NULL;
bits = new Uint8[bm.bmWidthBytes*bm.bmHeight];
temp = new Uint8[bm.bmWidthBytes*bm.bmHeight];
memcpy(temp, bm.bmBits, bm.bmWidthBytes*bm.bmHeight);
Uint8 *ptemp;
Uint8 *pbits = bits;
for (int j = bm.bmHeight - 1; j >= 0; j--)
{
ptemp = temp + j * bm.bmWidthBytes;
for (int x = 0; x < bm.bmWidthBytes; x++)
{
*pbits = *ptemp;
pbits++;
ptemp++;
}
}
// Copy the formatted bits to the surface
if (SDL_MUSTLOCK(icon)) SDL_LockSurface(icon);
memcpy(icon->pixels, bits, bm.bmWidthBytes*bm.bmHeight);
if (SDL_MUSTLOCK(icon)) SDL_UnlockSurface(icon);
// Set the window icon to the loaded surface
SDL_SetWindowIcon(mainWindow, icon);
// Cleanup
delete[] bits;
delete[] temp;
DeleteObject(hbitmap);
SDL_FreeSurface(icon);
}
Thank you to everyone who helped. I appreciate it. (If I'm missing anything in error testing or cleanup, please feel free to point it out and I'll update.)
bm.bmBits in your code, obtained from HICON, is most likely NULL. Use CopyImage with LR_CREATEDIBSECTION to access bmBits
HBITMAP hbitmap = (HBITMAP)CopyImage(ii.hbmColor, IMAGE_BITMAP,
bm.bmWidth, bm.bmHeight, LR_CREATEDIBSECTION);
BITMAP bm2;
GetObject(hbitmap, sizeof(BITMAP), &bm2);
...
DeleteObject(hbitmap);
Check bm2.bmBitsPixel to make sure it's 32-bit. Check bm2.bmBits to make sure it is not NULL.
void LoadIconFrom(std::string assembly, int rcId, const char* rcName)
{
...
ICONINFO ii;
GetIconInfo(hicon, &ii);
BITMAP bm;
GetObject(ii.hbmColor, sizeof(bm), &bm);
HBITMAP hbitmap = (HBITMAP)CopyImage(ii.hbmColor, IMAGE_BITMAP,
bm.bmWidth, bm.bmHeight, LR_CREATEDIBSECTION);
GetObject(hbitmap, sizeof(BITMAP), &bm);
if (bm.bmBitsPixel != 32) {error(); ...}
if (bm.bmBits == NULL) {error(); ...}
...
icon = SDL_CreateRGBSurface(SDL_SWSURFACE, bm.bmWidth, bm.bmHeight,
bm.bmBitsPixel, rmask, gmask, bmask, amask);
//copy bits upside down
if(SDL_MUSTLOCK(icon)) SDL_LockSurface(icon);
int wb = bm.bmWidthBytes;
BYTE* bits = icon->pixels;
BYTE* src = (BYTE*)bm.bmBits;
for(int j = 0; j < bm.bmHeight; j++)
memcpy(bits + j * wb, src + (bm.bmHeight - j - 1) * wb, wb);
if(SDL_MUSTLOCK(icon)) SDL_UnlockSurface(icon);
SDL_SetWindowIcon(mainWindow, icon);
// Cleanup
SDL_FreeSurface(icon);
DeleteObject(hbitmap);
}
User contributions licensed under CC BY-SA 3.0