Introduction

This is a quick tutorial on how to load images in a thread and show progress.
The full code is here: loadimagesinthread.cpp
It will load all images in the „Images/“ directory, displaying the progress and the currently loading image.
When it's done loading, it will show how many images were succesfully loaded.

Okay let's look at the code.

findFiles()

#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <boost/filesystem.hpp>
#include <cstring>
 
typedef std::vector<std::string> FileList;
typedef FileList::iterator FileListIterator;
 
unsigned int findFiles(FileList& Files, const std::string& Directory)
{
	boost::filesystem::path Dir(Directory);
 
	if (!boost::filesystem::exists(Dir))
		return 0;
 
	boost::filesystem::directory_iterator itEnd;
	for (boost::filesystem::directory_iterator it(Dir); it != itEnd; ++it)
	{
		if (boost::filesystem::is_directory(it->status()))
			findFiles(Files, it->path().string());
		else if (boost::filesystem::is_regular(it->status()))
			Files.push_back(it->path().string());
		else; //Not a directory or regular file
	}
	return Files.size();
}

This is a recursive function to build a list of all (regular) files in a directory.
It takes a reference to a FileList (see the typedef) and the directory to start searching in.
We use this function to build a list of files in „Images/“.
It uses boost::filesystem: http://www.boost.org/doc/libs

LoadingThreadInfo

typedef std::vector<sf::Image*> ImageList;
 
struct LoadingThreadInfo
{
	sf::RenderWindow& Window;
	//Mutex for OpenGL context
	sf::Mutex& GLMutex;
 
	std::string& Filename;
	//Mutex for Filename in main()
	sf::Mutex& FilenameMutex;
 
	//Progress (percentage)
	float& Progress;
	//Are we still loading?
	bool& Loading;
	ImageList& Images;
};

This is a simple struct that is passed to the thread function with necessary references.
We have a mutex for OpenGL and a mutex for the filename of the image currently being loaded.
ImageList is simply a vector of sf::Image pointers.

LoadImages()

void LoadImages(void* Data)
{
	LoadingThreadInfo* Info = (LoadingThreadInfo*)Data;
	sf::RenderWindow& Window = Info->Window;
	float& Progress = Info->Progress;
	bool& isLoading = Info->Loading;
	sf::Mutex& GLMutex = Info->GLMutex;
	sf::Mutex& FilenameMutex = Info->FilenameMutex;
	ImageList& Images = Info->Images;
	std::string& Filename = Info->Filename;
 
	FileList ImageFiles;
	findFiles(ImageFiles, "Images/");
	std::size_t numFiles = ImageFiles.size();
	printf("Found %d images\n", numFiles);
	for (std::size_t i = 0; i < numFiles; ++i)
	{
		sf::Lock lockGL(GLMutex);
		Window.SetActive();
 
		FilenameMutex.Lock();
		Filename = ImageFiles[i];
		FilenameMutex.Unlock();
 
		printf("Loading %s\n", ImageFiles[i].c_str());
		sf::Image* newImage = new sf::Image;
		if (newImage->LoadFromFile(ImageFiles[i]))
			Images.push_back(newImage);
		else
			delete newImage;
 
		Progress = static_cast<float>(i + 1) / static_cast<float>(numFiles) * 100.0f;
		Window.SetActive(false);
	}
	isLoading = false;
	FilenameMutex.Lock();
	Filename.clear();
	FilenameMutex.Unlock();
}

This is the actual thread function.
We build a list of files in the „Images/“ directory.
Then we attempt to load them.
If we fail to load an image, we continue on to the next.
Note the call to SetActive. See this thread: http://sfml-dev.org/forum/viewtopic.php?t=349
We must be careful to lock and unlock our mutexes when we need to access shared data.

main()

int main(int argc, char* argv[])
{
	sf::RenderWindow Window(sf::VideoMode(800, 600, 32), "", sf::Style::Close);
	sf::String ProgressString;
	sf::String FilenameString;
	char Buffer[32];
 
	bool isLoading = true;
	float loadingProgress = 0.0f;
	std::string Filename;
	sf::Mutex GLMutex;
	sf::Mutex FilenameMutex;
	ImageList Images;
	LoadingThreadInfo LoadingInfo = {Window, GLMutex, Filename, FilenameMutex, loadingProgress, isLoading, Images};
 
	sf::Thread LoadingThread(&LoadImages, &LoadingInfo);
	LoadingThread.Launch();
 
	sf::Event Event;
	bool Done = false;
	while (!Done)
	{
		if (isLoading)
		{
			GLMutex.Lock();
			Window.SetActive();
		}
		while (Window.GetEvent(Event))
		{
			switch (Event.Type)
			{
				case sf::Event::Closed:
					Done = true;
				break;
			}
		}
		if (isLoading)
		{
			if (Filename.empty())
			{
				FilenameString.SetText("Loading...");
			}
			else
			{
				FilenameMutex.Lock();
				FilenameString.SetText("Loading " + Filename);
				FilenameMutex.Unlock();
			}
			sprintf_s(Buffer, sizeof(Buffer), "%.2f%%", loadingProgress);
			ProgressString.SetText(Buffer);
		}
		else
		{
			sprintf_s(Buffer, sizeof(Buffer), "Succesfully loaded %d images", Images.size());
			ProgressString.SetText(Buffer);
		}
		ProgressString.SetPosition(400 - ProgressString.GetRect().GetWidth() / 2, 500);
		Window.Draw(ProgressString);
		if (!Filename.empty())
		{
			FilenameString.SetPosition(400 - FilenameString.GetRect().GetWidth() / 2, 550);
			Window.Draw(FilenameString);
		}
		Window.Display();
		if (isLoading)
		{
			Window.SetActive(false);
			GLMutex.Unlock();
		}
	}
	for (std::size_t i = 0; i < Images.size(); ++i)
		delete Images[i];
 
    return 0;
}

Lastly, here's the main function.
If the thread is still loading, we display the progress and the currently loading file.
Otherwise, we display the number of succesfully loaded images.

Download

http://www.sendspace.com/file/4am7gy
This download contains:

  • Windows Binary
  • Source Code
  • Code::Blocks Project (setup for MSVC9)
  • VS2008 Project
  • Dummy images for quick testing
 
en/tutorials/loadimagesinthread.txt · Last modified: 2008/07/15 09:46 by dewyatt
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki