I have been fiddling around with creating my own Sprite Cache.
Currently, I am using the C header file, dirent.h
, to open up a directory and load all .bmp files. I do not have to check if the file ends with .bmp, as SDL_LoadBMP
will return a null pointer if it can not read the supplied file as a bitmap image.
One thing I do not like is having to look up sprites by their name. I feel like I could have it faster by not using strings, but then I do not know how I would be able to tell which image is which (AFAIK, dirent.h
reads in the files in the directory in whatever order they appear in said directory. So treating the first file as ID=0 would not be wise, as ID=0 could be a different file if the user has a different file order in that directory.)
I have the Sprite Cache separated into two different objects - a directory reader, and the SpriteStore.
The directory reader is responsible for receiving a directory and loading all the .bmp files into a map, while the SpriteStore is just responsible for handing out the requested sprites, or a nullptr
if the sprite can not be found.
Directory Reader object
//DirectoryReader object is responsible for reading an entire directory
//And loading the sprites into a map<string, SDL_Surface*> object store.
class DirectoryReader
{
public:
DirectoryReader(std::string dir, std::map<std::string, SDL_Surface*>& directory_map)
{
ReadDirectory(dir, directory_map);
}
private:
void ReadDirectory(std::string dir, std::map<std::string, SDL_Surface*>& directory_map)
{
//Directory entry point and directory pointer
struct dirent* directory_entry;
DIR* directory_pointer = opendir(dir.c_str());
//Vector of std::pair<int, string> representing each file in the directory
//And the order number it appeared as in the directory
std::vector<std::pair<std::string, SDL_Surface*> > pair_vector;
//Loop thru the directory
while((directory_entry = readdir(directory_pointer)) != nullptr)
{
//Check to make sure the directory_entry is at a regular file
if(directory_entry->d_type == DT_REG)
{
//Attempt to create an SDL_Surface object
//SDL_LoadBMP returns null if it can not load a bitmap image
SDL_Surface* surface = SDL_LoadBMP(directory_entry->d_name);
if(surface != nullptr)
pair_vector.emplace_back(std::make_pair(std::string(directory_entry->d_name), surface));
}
}
//Insert the pairs into the map
for(auto p : pair_vector)
{
directory_map.insert(p);
}
}
DirectoryReader() = delete;
DirectoryReader(DirectoryReader& copy) = delete;
DirectoryReader(DirectoryReader&& move) = delete;
DirectoryReader& operator=(const DirectoryReader& copy) = delete;
};
Sprite Cache object
//SpriteStore is responsible for holding all loaded sprites in memory
//To be passed to components who need a sprite
class SpriteStore
{
public:
SpriteStore(std::string directory) : directoryReader_uptr(nullptr)
{
directoryReader_uptr.reset(new DirectoryReader(directory, store));
}
~SpriteStore()
{
//Free every surface
for(auto sprite : store)
{
SDL_FreeSurface(sprite.second);
}
//Clean the map
map.clear();
}
//Attemp to return the sprite. If not found, return a nullptr.
SDL_Surface* GetSprite(std::string ID)
{
try
{
return store.at(ID).get();
}
//The ID was not found
catch(std::out_of_range ex)
{
return nullptr;
}
}
private:
std::unique_ptr<DirectoryReader> directoryReader_uptr;
std::map<std::string, SDL_Surface*> store;
SpriteStore() = delete;
SpriteStore(SpriteStore& copy) = delete;
SpriteStore(SpriteStore&& move) = delete;
SpriteStore& operator=(const SpriteStore& copy) = delete;
};