mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-29 19:55:37 +00:00
Simple resizing for GuiImages using SDL_gfx.
Added multithreaded image loading for the GuiImage class.
This commit is contained in:
parent
6cfe83e8fe
commit
9c86241cf8
2
Makefile
2
Makefile
|
@ -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)
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue