Sign in to follow this  
MrPickle

SDL_UpperBlit: passed a NULL surface

Recommended Posts

Ok, so I have a function to apply surfaces to whatever but whenever it's called it's giving me the error "SDL_UpperBlit: passed a NULL surface". I don't see how it's being sent a NULL surface because I have checks after all the surfaces are updated to see if the surface equals NULL and it gets passed there perfectly fine; All surfaces are global.
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
     SDL_Rect offset;
     offset.x = x;
     offset.y = y;
     SDL_BlitSurface(source, clip, destination, &offset);
}

void Button::show()
{
    apply_surface(box.x, box.y, buttonSheet, screen, image);
}

void Tile::show()
{
    apply_surface(box.x, box.y, isometricSQ, screen, &tile_clips[type]);
}

Share this post


Link to post
Share on other sites
It could be a number of things. The code you posted is fine, so the error must be elsewhere.

When you update the surface, are you sure that you are updating the global surface and not a local copy by mistake?

Are you perhaps writing past the bounds of an array and into your surface?

You should put a check in the apply_surface function so you can see which surface is NULL and post the code where it is created.

Share this post


Link to post
Share on other sites
Use a debugger. A simple way would be to dereference each surface. This will cause an access violation, which when run in debugger will allow you to get a backtrace, which will make obvious which surface is NULL. Add a line like "offset.w = source->w * destination->h" to your apply_surface function (SDL_BlitSurface only cares about the source rectangle's x and y values IIRC).

You have posted the wrong code anyway. None of those functions directly modify the surface pointers, so they cannot be the cause*. The problem is more likely in your setup code. Perhaps you forgot to call it (happens the best of us [smile].



*If only this were true. In C++, one of those surfaces could accidentally be pointing at memory that isn't a surface. If you were to blit to that pointer, one could accidentally write over one of your global pointers with whatever bit pattern is null on your system. I would hope that the problem is much simpler.

Share this post


Link to post
Share on other sites
I ran in a debugger and it says the following: "An Access Violation (Segmentation Fault) raised in your program."

If it makes a difference, I'm using Dev-C++.

I put a check in apply_surface and it's saying that SDL_Surface* source is null.


//Declared as globals and set to NULL as globals.
//Updated in "init()"
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

//Updated in "main(int argc, char* args[])"
isometricSQ = load_image("Tile.png");
buttonSheet = load_image("buttons.png");


load_image:
SDL_Surface *load_image(std::string filename)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = IMG_Load(filename.c_str());

if(loadedImage != NULL)
{
optimizedImage = SDL_DisplayFormat(loadedImage);
SDL_FreeSurface(loadedImage);
}

if(optimizedImage != NULL)
{
Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 0xff, 0, 0xff);
SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, colorkey );
}

return optimizedImage;
}

Share this post


Link to post
Share on other sites
You really should get a better debugger - the one that comes with Dev-C++ is pretty bad. The best is Visual Studio's. There is a free version of it somewhere. Other options I can vouch for are the one that comes with Code::Blocks, and DDD.

Quote:

I ran in a debugger and it says the following: "An Access Violation (Segmentation Fault) raised in your program."


If this is your only interaction with the debugger, you need to learn to use it better. You should be able to step through your code and see exactly what value your surfaces have at each point of execution. This will tell you what's going wrong.

The code you posted is, again, fine looking on the surface.

Share this post


Link to post
Share on other sites
Ok, lets say IMG_Load returns NULL. What happens? Well, the first if block is skipped because the condition doesn't match. However, because the code in that function doesn't execute, optimisedImage remains null, so the next block is skipped. The function returns null. I cannot see any checks to guard against this from the little of the calling code you have posted.

Most of the time, we expect the images to load fine. So not finding the image is a pretty exceptional situation. We can use a C++ language feature, an exception, to handle this.

Simply put, if we find that the surface fails to load, we throw an exception. Should we choose to, we can catch the exception anywhere is the calling code.


#include <stdexcept>

class ImageLoadingError : public std::runtime_error
{
public:
ImageLoadingError(const std::string &error)
:
std::runtime_error(error)
{
}
}

SDL_Surface *load_image(const std::string &filename)
{
SDL_Surface* surface = IMG_Load(filename.c_str());

if(!surface)
{
throw ImageLoadingError(IMG_GetError());
}


// if we fail to optimise a surface, it really isn't a critical error
// after all, we can still use the original surface
if( SDL_Surface* optimized = SDL_DisplayFormat(surface) )
{
SDL_FreeSurface(surface);
surface = optimized;
}

Uint32 colorkey = SDL_MapRGB(surface->format, 0xff, 0, 0xff);
SDL_SetColorKey(surface, SDL_SRCCOLORKEY, colorkey );

return surface;
}



The above guarantees that we will either get a good surface, or an exception will be thrown.

The next step is to deal with the error in a sensible way. This is trickier. The simplest approach is to print an error message, or bring up an error message box explaining the error.

There are more complex solutions, such as loading a default image (the image could be procedurally generated), or notifying the user and asking them if they wish to use a different image and or switch the image directory.

In this example, we can simply wrap main in a try...catch block and print errors:

int main(int argc, char **argv)
{
try
{
init();
load();
run();
}
catch(const std::exception &e)
{
std::cerr << e.what() << '\n';
return 1;
}
return 0;
}



To illustrate the filtering that we can do using exceptions:

void load()
{
try
{
someSurface = load_image("some_surface.png");
}
catch( const ImageLoadingError &e )
{
bool b = notifyUser("Image loading failed! " + e.what() + "\nDo you wish to continue with default image?");
if(b)
{
someSurface = generateImage();
}
else
{
throw; // let the caller deal with the problem
}
}
}



For the moment the most productive thing you could do would be to learn how to use a debugger. The debugger that Dev-C++ provides is almost useless, and is the main reason why it isn't recommended. A great debugger for C++ is included with the free versions of Microsoft's Visual C++ Studio. I recommend you switch.

Share this post


Link to post
Share on other sites
I don't know how to use debuggers, there's a bit that controls it, but I'm not sure what they do :S

I'll look into it.

Sorry to be a bother but I'm trying to debug in Visual C++ and it's saying: "LINK : fatal error LNK1561: entry point must be defined"

I have set up the libaries in project options.

Fixed it, had to set the entery point in Project Properties > Linker > Advanced

Here's what it says;

Message box:
Quote:
Unhandled exception at 0x689bfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.


In the little bit at bottom:
Quote:
'Isometric.exe': Loaded 'C:\Users\James\Documents\Visual Studio 2008\Projects\Isometric\Debug\Isometric.exe', Symbols loaded.
'Isometric.exe': Loaded 'C:\Windows\System32\ntdll.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\kernel32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\SDL.dll', Binary was not built with debug information.
'Isometric.exe': Loaded 'C:\Windows\System32\advapi32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\rpcrt4.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\gdi32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\user32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\msvcrt.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\winmm.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\ole32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\oleaut32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\oleacc.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\SDL_image.dll', Binary was not built with debug information.
'Isometric.exe': Loaded 'C:\Windows\System32\SDL_ttf.dll', Binary was not built with debug information.
'Isometric.exe': Loaded 'C:\Windows\System32\libfreetype-6.dll', Binary was not built with debug information.
'Isometric.exe': Loaded 'C:\Windows\System32\zlib1.dll', Binary was not built with debug information.
'Isometric.exe': Loaded 'C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.21022.8_none_96748342450f6aa2\msvcp90d.dll'
'Isometric.exe': Loaded 'C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.21022.8_none_96748342450f6aa2\msvcr90d.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\imm32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\msctf.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\lpk.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\usp10.dll'
'Isometric.exe': Loaded 'C:\Program Files\Google\Google Desktop Search\GoogleDesktopNetwork3.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\ws2_32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\nsi.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\ntmarta.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\Wldap32.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\psapi.dll'
'Isometric.exe': Loaded 'C:\Windows\System32\samlib.dll'
'Isometric.exe': Unloaded 'C:\Program Files\Google\Google Desktop Search\GoogleDesktopNetwork3.dll'
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
First-chance exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.
Unhandled exception at 0x688dfcac in Isometric.exe: 0xC0000005: Access violation reading location 0x00000000.


[Edited by - MrPickle on February 22, 2008 2:56:12 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this