Simple resizing for GuiImages using SDL_gfx.

Added multithreaded image loading for the GuiImage class.
This commit is contained in:
Aloshi 2012-08-09 16:19:07 -05:00
parent 6cfe83e8fe
commit 9c86241cf8
8 changed files with 187 additions and 27 deletions

View file

@ -1,6 +1,6 @@
CC=g++
CFLAGS=-c -Wall
LDFLAGS=-lSDL -lSDL_ttf -lSDL_image -lboost_system -lboost_filesystem
LDFLAGS=-lSDL -lSDL_ttf -lSDL_image -lSDL_gfx -lboost_system -lboost_filesystem
SRCSOURCES=main.cpp Renderer.cpp Renderer_draw.cpp GuiComponent.cpp InputManager.cpp SystemData.cpp GameData.cpp FolderData.cpp XMLReader.cpp components/GuiGameList.cpp components/GuiInputConfig.cpp components/GuiImage.cpp components/GuiMenu.cpp pugiXML/pugixml.cpp
SOURCES=$(addprefix src/,$(SRCSOURCES))
OBJECTS=$(SOURCES:.cpp=.o)

View file

@ -9,12 +9,9 @@ I'm not associated with RetroArch in any way!
Building
========
EmulationStation has a few dependencies. For building, you'll need SDL 1.2, the SDL TTF library, the SDL image library, and Boost.Filesystem, which can easily be obtained with apt-get:
EmulationStation has quite a few dependencies. For building, you'll need SDL 1.2, the SDL TTF library, the SDL image library, the SDL_gfx library, and Boost.Filesystem, which can easily be obtained with apt-get:
```
sudo apt-get install libsdl1.2-dev
sudo apt-get install libsdl-ttf2.0-dev
sudo apt-get install libboost-filesystem-dev
sudo apt-get install libsdl-image1.2-dev
sudo apt-get install libsdl1.2-dev libsdl-ttf2.0-dev libboost-filesystem-dev libsdl-image1.2-dev libsdl-gfx1.2-dev
```
You can build EmulationStation by simply running `make`.
@ -24,7 +21,7 @@ Configuring
When first run, an example systems configuration file will be created at $HOME/.emulationstation/es_systems.cfg. This example has some comments explaining how to write the configuration file, and an example RetroArch launch command. Keep in mind you can define more than one system! Just use all the variables again.
If an SDL Joystick is detected at startup, and $HOME/.es_input.cfg is nonexistant, an Input Configuration screen will appear instead of the game list. This should be pretty self-explanatory. If you want to reconfigure, just delete $HOME/.emulationstation/es_input.cfg.
If an SDL Joystick is detected at startup, and $HOME/.emulationstation/es_input.cfg is nonexistant, an Input Configuration screen will appear instead of the game list. This should be pretty self-explanatory. If you want to reconfigure, just delete $HOME/.emulationstation/es_input.cfg.
Mappings will always be applied to the SDL joystick at index 0. An Xbox 360 controller with the xboxdrv driver was tested. POV hats are automatically mapped to directions (so if you're not using an analog stick, you'll need to skip mapping Up/Down/Left/Right by pressing a keyboard key).

View file

@ -1,3 +1,8 @@
August 8
-Added automatic resizing of images using SDL_gfx
-Multithreaded image loading for the GuiImage class!
-Removed warning if an unknown variable is found in a systems config file (useful for additional utilities)
August 7
-gamelist.xml files are now read from each system's individual search directory.
-The switch --gamelist-only was added. Use it to skip automatic searching and only use files defined in gamelist.xml.

View file

@ -74,7 +74,7 @@ void InputManager::processEvent(SDL_Event* event)
}
//catch emergency quit event
if(event->key.keysym.sym == SDLK_F4)
if(event->key.keysym.sym == SDLK_F4 && keyDown)
{
//I have no idea if SDL will delete this event, but we're quitting, so I don't think it really matters
SDL_Event* quit = new SDL_Event();

View file

@ -168,8 +168,8 @@ void SystemData::loadConfig()
sysExtension = varValue;
else if(varName == "COMMAND")
sysCommand = varValue;
else
std::cerr << "Error reading config file - unknown variable name \"" << varName << "\"!\n";
//else
// std::cerr << "Error reading config file - unknown variable name \"" << varName << "\"!\n";
//we have all our variables - create the system object
if(!sysName.empty() && !sysPath.empty() &&!sysExtension.empty() && !sysCommand.empty())

View file

@ -18,7 +18,7 @@ GuiGameList::GuiGameList(bool useDetail)
{
mList = new GuiList<FileData*>(Renderer::getScreenWidth() * 0.4, Renderer::getFontHeight(Renderer::LARGE) + 2);
mScreenshot = new GuiImage(Renderer::getScreenWidth() * 0.2 - (SCREENSHOTWIDTH / 2), Renderer::getFontHeight(Renderer::LARGE) + 2);
mScreenshot = new GuiImage(Renderer::getScreenWidth() * 0.2, Renderer::getFontHeight(Renderer::LARGE) + 2, "", Renderer::getScreenWidth() * 0.3);
addChild(mScreenshot);
}else{
mList = new GuiList<FileData*>(0, Renderer::getFontHeight(Renderer::LARGE) + 2);
@ -87,6 +87,7 @@ void GuiGameList::onRender()
{
GameData* game = (GameData*)mList->getSelectedObject();
//todo: cache this
std::string desc = game->getDescription();
if(!desc.empty())
Renderer::drawWrappedText(desc, 2, Renderer::getFontHeight(Renderer::LARGE) + SCREENSHOTHEIGHT + 12, Renderer::getScreenWidth() * 0.4, 0xFF0000);

View file

@ -1,12 +1,36 @@
#include "GuiImage.h"
#include <SDL/SDL_image.h>
#include <SDL/SDL_rotozoom.h>
#include <iostream>
#include <boost/filesystem.hpp>
GuiImage::GuiImage(int offsetX, int offsetY, std::string path)
int GuiImage::getWidth() { if(mSurface) return mSurface->w; else return 0; }
int GuiImage::getHeight() { if(mSurface) return mSurface->h; else return 0; }
int startImageLoadThread(void*);
GuiImage::GuiImage(int offsetX, int offsetY, std::string path, unsigned int maxWidth, unsigned int maxHeight)
{
std::cout << "Creating GuiImage\n";
mSurface = NULL;
SDL_Rect newRect = {offsetX, offsetY, 0, 0};
mRect = newRect;
mOffsetX = offsetX;
mOffsetY = offsetY;
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
mPathMutex = SDL_CreateMutex();
mSurfaceMutex = SDL_CreateMutex();
mDeleting = false;
mLoadThread = SDL_CreateThread(&startImageLoadThread, this);
if(!mLoadThread)
{
std::cerr << "Error - could not create image load thread!\n";
std::cerr << " " << SDL_GetError() << "\n";
}
if(!path.empty())
setImage(path);
@ -14,31 +38,142 @@ GuiImage::GuiImage(int offsetX, int offsetY, std::string path)
GuiImage::~GuiImage()
{
mDeleting = true;
if(mLoadThread)
SDL_WaitThread(mLoadThread, NULL);
if(mSurface)
SDL_FreeSurface(mSurface);
}
void GuiImage::setImage(std::string path)
std::string GuiImage::getPathThreadSafe()
{
//if we already have an image, clear it
if(mSurface)
std::string ret;
SDL_mutexP(mPathMutex);
ret = mPath;
SDL_mutexV(mPathMutex);
return ret;
}
void GuiImage::setPathThreadSafe(std::string path)
{
SDL_mutexP(mPathMutex);
mPath = path;
if(mPath.empty())
mLoadedPath = "";
SDL_mutexV(mPathMutex);
}
int GuiImage::runImageLoadThread()
{
while(!mDeleting)
{
SDL_FreeSurface(mSurface);
mSurface = NULL;
std::string path = getPathThreadSafe();
if(path != mLoadedPath && path != "" && boost::filesystem::exists(path))
{
//start loading the image
SDL_Surface* newSurf = IMG_Load(path.c_str());
//if we started loading something else or failed to load, don't bother resizing
if(path != getPathThreadSafe() || newSurf == NULL)
{
if(newSurf)
SDL_FreeSurface(newSurf);
continue;
}
//std::cout << "Loading complete, checking for resize\n";
//resize it
if(mMaxWidth && newSurf->w > mMaxWidth)
{
double scale = (double)mMaxWidth / (double)newSurf->w;
SDL_Surface* resSurf = zoomSurface(newSurf, scale, scale, SMOOTHING_OFF);
SDL_FreeSurface(newSurf);
newSurf = resSurf;
}
if(mMaxHeight && newSurf->h > mMaxHeight)
{
double scale = (double)mMaxHeight / (double)newSurf->h;
SDL_Surface* resSurf = zoomSurface(newSurf, scale, scale, SMOOTHING_OFF);
SDL_FreeSurface(newSurf);
newSurf = resSurf;
}
//again, make sure we're still good to go
if(path != getPathThreadSafe() || newSurf == NULL)
{
if(newSurf)
SDL_FreeSurface(newSurf);
continue;
}
//finally set the image and delete the old one
SDL_mutexP(mSurfaceMutex);
if(mSurface)
SDL_FreeSurface(mSurface);
mSurface = newSurf;
//Also update the rect
mRect.x = mOffsetX - (mSurface->w / 2);
mRect.y = mOffsetY;
mRect.w = 0;
mRect.h = 0;
mLoadedPath = path;
SDL_mutexV(mSurfaceMutex);
}
}
if(!path.empty())
std::cout << "Finishing image loader thread.\n";
return 0;
}
int startImageLoadThread(void* img)
{
return ((GuiImage*)img)->runImageLoadThread();
}
void GuiImage::setImage(std::string path)
{
setPathThreadSafe(path);
if(path.empty())
{
mSurface = IMG_Load(path.c_str());
if(mSurface == NULL)
std::cerr << "Error loading image \"" << path.c_str() << "\"\n";
if(mSurface)
{
SDL_mutexP(mSurfaceMutex);
SDL_FreeSurface(mSurface);
mSurface = NULL;
SDL_mutexV(mSurfaceMutex);
}
}
}
void GuiImage::onRender()
{
if(mSurface)
SDL_BlitSurface(mSurface, NULL, Renderer::screen, &mRect);
{
SDL_mutexP(mSurfaceMutex);
SDL_BlitSurface(mSurface, NULL, Renderer::screen, &mRect);
SDL_mutexV(mSurfaceMutex);
}else if(!getPathThreadSafe().empty())
{
Renderer::drawCenteredText("Loading...", -(Renderer::getScreenWidth() - mOffsetX)/*-mOffsetX * 3*/, mOffsetY, 0x000000);
}
}

View file

@ -3,20 +3,42 @@
#include "../GuiComponent.h"
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <string>
class GuiImage : public GuiComponent
{
public:
GuiImage(int offsetX = 0, int offsetY = 0, std::string path = "");
GuiImage(int offsetX = 0, int offsetY = 0, std::string path = "", unsigned int maxWidth = 0, unsigned int maxHeight = 0);
~GuiImage();
void setImage(std::string path);
int getWidth();
int getHeight();
void onRender();
//this should really never be called by anything except setImage
//but it was either make this function public or make mSurface public
//so just don't use this, okay?
int runImageLoadThread();
private:
int mMaxWidth, mMaxHeight;
std::string mPath, mLoadedPath;
SDL_Surface* mSurface;
int mOffsetX, mOffsetY;
SDL_Rect mRect;
SDL_Thread* mLoadThread;
void setPathThreadSafe(std::string path);
std::string getPathThreadSafe();
SDL_mutex* mPathMutex;
SDL_mutex* mSurfaceMutex;
bool mDeleting;
};
#endif