I am working on a "slide show" project. The goal is to display multiple images simultaneously in different parts of the screen and to provide some sophisticated transitions as images enter and leave. It appears that SFML may have most of the features I need. I have hit several rough patches of road along the way and have been able to work through them with help from the SFML community (Many thanks!).
My current confusion centers around loading images via a background thread. I have written an image loader that employs a background worker thread in order to avoid pauses in the animation of the slide show. I have included the loader and some simple test code below. I encounter several problems with this code:
1) I still experience pauses in the execution
2) Some of the images are only rendered as white rectangles.
3) I am apparently leaking large amounts of memory (even though I delete the images).
Any insight to my problems would be greatly appreciated.
Note: I am using SFML 2.0 build 1444 because I need to use RenderImage until clipping paths are available.
Another Note: Upon re-examination, my "real" slide show routine does not appear to leak as much memory. the only real differences appear to be that the sprite that uses the image goes out of context and is destructed, and the RenderImage pointer that uses the sprite which uses the image is deleted immediately after the image is deleted. (Clear as mud?)
This is my test routine. The image file spinner.png is just a small image that I rotate to visualize hiccups in execution. The other images are from digital still cameras.
//-----------------------------------------------------------------------------
void LoaderTest()
{
// Create a clock for measuring the time elapsed
sf::Clock clock;
// Create main window
sf::RenderWindow canvas(sf::VideoMode(800, 600), "loaderTest");
sf::Image spin_image;
spin_image.LoadFromFile("C:/images/spinner.png");
sf::Sprite spin_sprite(spin_image);
spin_sprite.SetOrigin(spin_image.GetWidth()/2.0F, spin_image.GetHeight()/2.0F);
spin_sprite.SetPosition(canvas.GetWidth()/2.0F, canvas.GetHeight()/2.0F);
vector<string> fnames;
fnames.push_back("C:/images/smallA.jpg");
fnames.push_back("C:/images/largeA.jpg");
fnames.push_back("C:/images/smallB.jpg");
fnames.push_back("C:/images/largeB.jpg");
SlideShowImageLoader loader;
int idx = 0;
sf::Image *pImage = 0;
sf::Sprite image_sprite;
std::string filename;
// Start game loop
float switchDelay = 2.0F;
float switchTime = clock.GetElapsedTime();
float currentTime = clock.GetElapsedTime();
while (canvas.IsOpened())
{
// Process events
sf::Event Event;
while (canvas.GetEvent(Event))
{
// Close window : exit
if (Event.Type == sf::Event::Closed)
canvas.Close();
// Escape key : exit
else if (Event.Type == sf::Event::KeyPressed)
{
if (Event.Key.Code == sf::Key::Escape)
canvas.Close();
}
}
// queue up images
while (loader.imagesTotal() < fnames.size())
loader.RequestImageFile(fnames[idx++ % (int)fnames.size()]);
// switch images periodically
currentTime = clock.GetElapsedTime();
if (currentTime >= switchTime)
{
if (pImage)
{
delete pImage;
pImage = 0;
}
if (loader.DequeImage(filename, pImage))
{
image_sprite.SetImage(*pImage, true);
image_sprite.SetOrigin(pImage->GetWidth()/2.0F, pImage->GetHeight()/2.0F);
image_sprite.SetPosition(canvas.GetWidth()/2.0F, canvas.GetHeight()/2.0F);
float scale = std::min((float)canvas.GetWidth()/pImage->GetWidth(), (float)canvas.GetHeight()/pImage->GetHeight());
image_sprite.SetScale(scale, scale);
switchTime = currentTime + switchDelay;
}
}
// Display
canvas.Clear();
canvas.Draw(image_sprite);
spin_sprite.SetRotation(currentTime * 180);
canvas.Draw(spin_sprite);
// Finally, display the rendered frame on screen
canvas.Display();
}
}
The SlideShowImageLoader.h looks like this:
#pragma once
#include <SFML/Graphics.hpp>
#include <deque>
typedef std::pair<std::string, sf::Image *> NamedImagePtr;
//-----------------------------------------------------------------------------
class SlideShowImageLoader
{
public:
SlideShowImageLoader();
virtual ~SlideShowImageLoader();
static void LoaderLoop(void *pData);
void RequestImageFile(std::string &filename);
bool DequeImage(std::string &filename, sf::Image *&pImage);
inline unsigned imagesAvailable() const { return (unsigned)responses.size(); }
inline unsigned imagesPending() const { return (unsigned)requests.size(); }
inline unsigned imagesTotal() const { return (unsigned)responses.size() + (unsigned)requests.size(); }
protected:
bool exitSignal;
sf::Thread loaderThread;
sf::Mutex responseMutex;
sf::Mutex requestMutex;
std::deque<NamedImagePtr> responses;
std::deque<std::string> requests;
};
SlideShowImageLoader.cpp
#include "SlideShowImageLoader.h"
//-----------------------------------------------------------------------------
SlideShowImageLoader::SlideShowImageLoader() : exitSignal(false), loaderThread(LoaderLoop, this)
{
loaderThread.Launch();
}
//-----------------------------------------------------------------------------
SlideShowImageLoader::~SlideShowImageLoader()
{
exitSignal = true;
loaderThread.Wait();
while (responses.size())
{
delete responses.front().second;
responses.pop_front();
}
}
//-----------------------------------------------------------------------------
void SlideShowImageLoader::LoaderLoop(void *pData)
{
sf::Context context; // this line is needed for images to be loaded properly
SlideShowImageLoader &loader = *(SlideShowImageLoader *)pData;
while(!loader.exitSignal)
{
if (loader.requests.size())
{
loader.requestMutex.Lock();
std::string filename = loader.requests.front();
loader.requests.pop_front();
loader.requestMutex.Unlock();
sf::Image *pImage = new sf::Image();
if (pImage->LoadFromFile(filename))
{
loader.responseMutex.Lock();
loader.responses.push_back(NamedImagePtr(filename, pImage));
loader.responseMutex.Unlock();
}
}
sf::Sleep(0.01f);
}
}
//-----------------------------------------------------------------------------
void SlideShowImageLoader::RequestImageFile(std::string &filename)
{
requestMutex.Lock();
requests.push_back(filename);
requestMutex.Unlock();
}
//-----------------------------------------------------------------------------
bool SlideShowImageLoader::DequeImage(std::string &filename, sf::Image *&pImage)
{
bool rval = false;
if (responses.size())
{
responseMutex.Lock();
filename = responses.front().first;
pImage = responses.front().second;
responses.pop_front();
responseMutex.Unlock();
rval = true;
}
return rval;
}
:lol: