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