ThumbnailBroker for background retrieval and rendering
This commit is contained in:
parent
303b546ceb
commit
1d258eab6b
@ -21,10 +21,12 @@
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Misc.h"
|
||||
|
||||
#include "simulation/SaveRenderer.h"
|
||||
#include "interface/Point.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "ClientListener.h"
|
||||
#include "Update.h"
|
||||
#include "ThumbnailBroker.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -232,6 +234,9 @@ std::vector<unsigned char> Client::ReadFile(std::string filename)
|
||||
|
||||
void Client::Tick()
|
||||
{
|
||||
//Check thumbnail queue
|
||||
ThumbnailBroker::Ref().FlushThumbQueue();
|
||||
|
||||
//Check status on version check request
|
||||
if(versionCheckRequest && http_async_req_status(versionCheckRequest))
|
||||
{
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
UpdateInfo(int time, std::string file, BuildType type) : Major(0), Minor(0), Build(0), Time(time), File(file), Type(type) {}
|
||||
};
|
||||
|
||||
class ThumbnailListener;
|
||||
class ClientListener;
|
||||
class Client: public Singleton<Client> {
|
||||
private:
|
||||
@ -74,6 +75,7 @@ private:
|
||||
//Config file handle
|
||||
json::Object configDocument;
|
||||
public:
|
||||
|
||||
vector<ClientListener*> listeners;
|
||||
|
||||
UpdateInfo GetUpdateInfo();
|
||||
|
372
src/client/ThumbnailBroker.cpp
Normal file
372
src/client/ThumbnailBroker.cpp
Normal file
@ -0,0 +1,372 @@
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#include "ThumbnailBroker.h"
|
||||
#include "ThumbnailListener.h"
|
||||
#include "Client.h"
|
||||
#include "GameSave.h"
|
||||
#include "search/Thumbnail.h"
|
||||
#include "simulation/SaveRenderer.h"
|
||||
|
||||
//Asynchronous Thumbnail render & request processing
|
||||
|
||||
class ThumbnailBroker::ThumbnailSpec
|
||||
{
|
||||
public:
|
||||
int Width, Height;
|
||||
ThumbnailListener * CompletedListener;
|
||||
ThumbnailSpec(int width, int height, ThumbnailListener * completedListener) :
|
||||
Width(width), Height(height), CompletedListener(completedListener) {}
|
||||
};
|
||||
|
||||
class ThumbnailBroker::ThumbnailID
|
||||
{
|
||||
public:
|
||||
int SaveID, SaveDate;
|
||||
bool operator ==(const ThumbnailID & second)
|
||||
{
|
||||
return SaveID == second.SaveID && SaveDate == second.SaveDate;
|
||||
}
|
||||
ThumbnailID(int saveID, int saveDate) : SaveID(saveID), SaveDate(saveDate) {}
|
||||
ThumbnailID() : SaveID(0), SaveDate(0) {}
|
||||
};
|
||||
|
||||
class ThumbnailBroker::ThumbnailRequest
|
||||
{
|
||||
public:
|
||||
bool Complete;
|
||||
void * HTTPContext;
|
||||
int RequestTime;
|
||||
|
||||
ThumbnailID ID;
|
||||
std::vector<ThumbnailSpec> SubRequests;
|
||||
|
||||
ThumbnailRequest(int saveID, int saveDate, int width, int height, ThumbnailListener * completedListener) :
|
||||
ID(saveID, saveDate), Complete(false), HTTPContext(NULL), RequestTime(0)
|
||||
{
|
||||
SubRequests.push_back(ThumbnailSpec(width, height, completedListener));
|
||||
}
|
||||
ThumbnailRequest() : Complete(false), HTTPContext(NULL), RequestTime(0) {}
|
||||
};
|
||||
|
||||
class ThumbnailBroker::ThumbRenderRequest
|
||||
{
|
||||
public:
|
||||
int Width, Height;
|
||||
GameSave * Save;
|
||||
ThumbnailListener * CompletedListener;
|
||||
ThumbRenderRequest(GameSave * save, int width, int height, ThumbnailListener * completedListener) :
|
||||
Save(save), Width(width), Height(height), CompletedListener(completedListener) {}
|
||||
ThumbRenderRequest() : Save(0), Width(0), Height(0), CompletedListener(NULL) {}
|
||||
};
|
||||
|
||||
ThumbnailBroker::ThumbnailBroker()
|
||||
{
|
||||
thumbnailQueueRunning = false;
|
||||
thumbnailQueueMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_init (&thumbnailQueueMutex, NULL);
|
||||
|
||||
listenersMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_init (&listenersMutex, NULL);
|
||||
}
|
||||
|
||||
ThumbnailBroker::~ThumbnailBroker()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ThumbnailBroker::RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener)
|
||||
{
|
||||
AttachThumbnailListener(tListener);
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
bool running = thumbnailQueueRunning;
|
||||
thumbnailQueueRunning = true;
|
||||
renderRequests.push_back(ThumbRenderRequest(new GameSave(*gameSave), width, height, tListener));
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
if(!running)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
|
||||
#endif
|
||||
pthread_create(&thumbnailQueueThread, 0, &ThumbnailBroker::thumbnailQueueProcessHelper, this);
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailBroker::RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener)
|
||||
{
|
||||
AttachThumbnailListener(tListener);
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
bool running = thumbnailQueueRunning;
|
||||
thumbnailQueueRunning = true;
|
||||
thumbnailRequests.push_back(ThumbnailRequest(saveID, saveDate, width, height, tListener));
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
if(!running)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " Starting background thread for new " << __FUNCTION__ << " request" << std::endl;
|
||||
#endif
|
||||
pthread_create(&thumbnailQueueThread, 0, &ThumbnailBroker::thumbnailQueueProcessHelper, this);
|
||||
}
|
||||
}
|
||||
|
||||
void * ThumbnailBroker::thumbnailQueueProcessHelper(void * ref)
|
||||
{
|
||||
((ThumbnailBroker*)ref)->thumbnailQueueProcessTH();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ThumbnailBroker::FlushThumbQueue()
|
||||
{
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
while(thumbnailComplete.size())
|
||||
{
|
||||
if(CheckThumbnailListener(thumbnailComplete.front().first))
|
||||
thumbnailComplete.front().first->OnThumbnailReady(thumbnailComplete.front().second);
|
||||
else
|
||||
delete thumbnailComplete.front().second;
|
||||
thumbnailComplete.pop_front();
|
||||
}
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
|
||||
void ThumbnailBroker::thumbnailQueueProcessTH()
|
||||
{
|
||||
time_t lastAction = time(NULL);
|
||||
while(true)
|
||||
{
|
||||
//Shutdown after 2 seconds of idle
|
||||
if(time(NULL) - lastAction > 2)
|
||||
{
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
thumbnailQueueRunning = false;
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
break;
|
||||
}
|
||||
|
||||
//Renderer
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
if(renderRequests.size())
|
||||
{
|
||||
lastAction = time(NULL);
|
||||
ThumbRenderRequest req;
|
||||
req = renderRequests.front();
|
||||
renderRequests.pop_front();
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " Processing render request" << std::endl;
|
||||
#endif
|
||||
|
||||
Thumbnail * thumbnail = SaveRenderer::Ref().Render(req.Save);
|
||||
delete req.Save;
|
||||
|
||||
if(thumbnail)
|
||||
{
|
||||
thumbnail->Resize(req.Width, req.Height);
|
||||
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
thumbnailComplete.push_back(std::pair<ThumbnailListener*, Thumbnail*>(req.CompletedListener, thumbnail));
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
|
||||
//Renderer
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
if(thumbnailRequests.size())
|
||||
{
|
||||
lastAction = time(NULL);
|
||||
Thumbnail * thumbnail = NULL;
|
||||
|
||||
ThumbnailRequest req;
|
||||
req = thumbnailRequests.front();
|
||||
|
||||
//Check the cache
|
||||
for(std::deque<std::pair<ThumbnailID, Thumbnail*> >::iterator iter = thumbnailCache.begin(), end = thumbnailCache.end(); iter != end; ++iter)
|
||||
{
|
||||
if((*iter).first == req.ID)
|
||||
{
|
||||
thumbnail = (*iter).second;
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " " << req.ID.SaveID << ":" << req.ID.SaveDate << " found in cache" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if(thumbnail)
|
||||
{
|
||||
//Got thumbnail from cache
|
||||
thumbnailRequests.pop_front();
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
|
||||
{
|
||||
Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
|
||||
tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
|
||||
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
thumbnailComplete.push_back(std::pair<ThumbnailListener*, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
}
|
||||
else if(!thumbnail)
|
||||
{
|
||||
//Check for ongoing requests
|
||||
bool requested = false;
|
||||
for(std::list<ThumbnailRequest>::iterator iter = currentRequests.begin(), end = currentRequests.end(); iter != end; ++iter)
|
||||
{
|
||||
if((*iter).ID == req.ID)
|
||||
{
|
||||
requested = true;
|
||||
|
||||
//Add the current listener to the item already being requested
|
||||
(*iter).SubRequests.push_back(req.SubRequests.front());
|
||||
}
|
||||
}
|
||||
|
||||
if(requested)
|
||||
{
|
||||
//Already requested
|
||||
thumbnailRequests.pop_front();
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
else if(currentRequests.size() < IMGCONNS) //If it's not already being requested and we still have more space for a new connection, request it
|
||||
{
|
||||
thumbnailRequests.pop_front();
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
//If it's not already being requested, request it
|
||||
if(!requested && CheckThumbnailListener(req.SubRequests.front().CompletedListener))
|
||||
{
|
||||
std::stringstream urlStream;
|
||||
urlStream << "http://" << STATICSERVER << "/" << req.ID.SaveID;
|
||||
if(req.ID.SaveDate)
|
||||
{
|
||||
urlStream << "_" << req.ID.SaveDate;
|
||||
}
|
||||
urlStream << "_small.pti";
|
||||
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " Creating new request for " << req.ID.SaveID << ":" << req.ID.SaveDate << std::endl;
|
||||
#endif
|
||||
|
||||
req.HTTPContext = http_async_req_start(NULL, (char *)urlStream.str().c_str(), NULL, 0, 1);
|
||||
req.RequestTime = time(NULL);
|
||||
currentRequests.push_back(req);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Already full of requests
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
|
||||
std::list<ThumbnailRequest>::iterator iter = currentRequests.begin();
|
||||
std::list<ThumbnailRequest>::iterator end = currentRequests.end();
|
||||
while (iter != currentRequests.end())
|
||||
{
|
||||
lastAction = time(NULL);
|
||||
|
||||
ThumbnailRequest req = *iter;
|
||||
Thumbnail * thumbnail = NULL;
|
||||
|
||||
if(http_async_req_status(req.HTTPContext))
|
||||
{
|
||||
|
||||
pixel * thumbData;
|
||||
char * data;
|
||||
int status, data_size, imgw, imgh;
|
||||
data = http_async_req_stop(req.HTTPContext, &status, &data_size);
|
||||
free(req.HTTPContext);
|
||||
|
||||
if (status == 200 && data)
|
||||
{
|
||||
thumbData = Graphics::ptif_unpack(data, data_size, &imgw, &imgh);
|
||||
free(data);
|
||||
|
||||
if(thumbData)
|
||||
{
|
||||
thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, thumbData, ui::Point(imgw, imgh));
|
||||
free(thumbData);
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnail = new Thumbnail(req.ID.SaveID, req.ID.SaveID, thumbData, ui::Point(128, 128));
|
||||
free(thumbData);
|
||||
}
|
||||
|
||||
if(thumbnailCache.size() >= THUMB_CACHE_SIZE)
|
||||
{
|
||||
delete thumbnailCache.front().second;
|
||||
thumbnailCache.pop_front();
|
||||
}
|
||||
thumbnailCache.push_back(std::pair<ThumbnailID, Thumbnail*>(req.ID, thumbnail));
|
||||
|
||||
for(std::vector<ThumbnailSpec>::iterator specIter = req.SubRequests.begin(), specEnd = req.SubRequests.end(); specIter != specEnd; ++specIter)
|
||||
{
|
||||
Thumbnail * tempThumbnail = new Thumbnail(*thumbnail);
|
||||
tempThumbnail->Resize((*specIter).Width, (*specIter).Height);
|
||||
|
||||
pthread_mutex_lock(&thumbnailQueueMutex);
|
||||
thumbnailComplete.push_back(std::pair<ThumbnailListener*, Thumbnail*>((*specIter).CompletedListener, tempThumbnail));
|
||||
pthread_mutex_unlock(&thumbnailQueueMutex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cout << typeid(*this).name() << " Request for " << req.ID.SaveID << ":" << req.ID.SaveDate << " failed with status " << status << std::endl;
|
||||
#endif
|
||||
if(data)
|
||||
free(data);
|
||||
}
|
||||
iter = currentRequests.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailBroker::RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener)
|
||||
{
|
||||
RetrieveThumbnail(saveID, 0, width, height, tListener);
|
||||
}
|
||||
|
||||
bool ThumbnailBroker::CheckThumbnailListener(ThumbnailListener * tListener)
|
||||
{
|
||||
pthread_mutex_lock(&listenersMutex);
|
||||
int count = std::count(validListeners.begin(), validListeners.end(), tListener);
|
||||
pthread_mutex_unlock(&listenersMutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void ThumbnailBroker::AttachThumbnailListener(ThumbnailListener * tListener)
|
||||
{
|
||||
pthread_mutex_lock(&listenersMutex);
|
||||
validListeners.push_back(tListener);
|
||||
pthread_mutex_unlock(&listenersMutex);
|
||||
}
|
||||
|
||||
void ThumbnailBroker::DetachThumbnailListener(ThumbnailListener * tListener)
|
||||
{
|
||||
pthread_mutex_lock(&listenersMutex);
|
||||
std::remove(validListeners.begin(), validListeners.end(), tListener);
|
||||
pthread_mutex_unlock(&listenersMutex);
|
||||
}
|
58
src/client/ThumbnailBroker.h
Normal file
58
src/client/ThumbnailBroker.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <queue>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <deque>
|
||||
#include <pthread.h>
|
||||
#undef GetUserName //God dammit microsoft!
|
||||
|
||||
#include "Singleton.h"
|
||||
|
||||
class GameSave;
|
||||
class Thumbnail;
|
||||
class ThumbnailListener;
|
||||
class ThumbnailBroker: public Singleton<ThumbnailBroker>
|
||||
{
|
||||
private:
|
||||
class ThumbnailID;
|
||||
class ThumbnailRequest;
|
||||
class ThumbnailSpec;
|
||||
class ThumbRenderRequest;
|
||||
|
||||
//Thumbnail retreival
|
||||
/*int thumbnailCacheNextID;
|
||||
Thumbnail * thumbnailCache[THUMB_CACHE_SIZE];
|
||||
void * activeThumbRequests[IMGCONNS];
|
||||
int activeThumbRequestTimes[IMGCONNS];
|
||||
int activeThumbRequestCompleteTimes[IMGCONNS];
|
||||
std::string activeThumbRequestIDs[IMGCONNS];*/
|
||||
|
||||
pthread_mutex_t thumbnailQueueMutex;
|
||||
pthread_mutex_t listenersMutex;
|
||||
pthread_t thumbnailQueueThread;
|
||||
bool thumbnailQueueRunning;
|
||||
std::deque<ThumbnailRequest> thumbnailRequests;
|
||||
std::deque<ThumbRenderRequest> renderRequests;
|
||||
|
||||
std::deque<std::pair<ThumbnailListener*, Thumbnail*> > thumbnailComplete;
|
||||
std::list<ThumbnailRequest> currentRequests;
|
||||
std::deque<std::pair<ThumbnailID, Thumbnail*> > thumbnailCache;
|
||||
|
||||
std::vector<ThumbnailListener*> validListeners;
|
||||
|
||||
static void * thumbnailQueueProcessHelper(void * ref);
|
||||
void thumbnailQueueProcessTH();
|
||||
|
||||
public:
|
||||
ThumbnailBroker();
|
||||
virtual ~ThumbnailBroker();
|
||||
|
||||
void FlushThumbQueue();
|
||||
void RenderThumbnail(GameSave * gameSave, int width, int height, ThumbnailListener * tListener);
|
||||
void RetrieveThumbnail(int saveID, int saveDate, int width, int height, ThumbnailListener * tListener);
|
||||
void RetrieveThumbnail(int saveID, int width, int height, ThumbnailListener * tListener);
|
||||
|
||||
bool CheckThumbnailListener(ThumbnailListener * tListener);
|
||||
void AttachThumbnailListener(ThumbnailListener * tListener);
|
||||
void DetachThumbnailListener(ThumbnailListener * tListener);
|
||||
};
|
11
src/client/ThumbnailListener.h
Normal file
11
src/client/ThumbnailListener.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
class Thumbnail;
|
||||
class ThumbnailListener
|
||||
{
|
||||
public:
|
||||
ThumbnailListener() {}
|
||||
virtual ~ThumbnailListener() {}
|
||||
|
||||
virtual void OnThumbnailReady(Thumbnail * thumb) {}
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
#include "client/SaveInfo.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Engine.h"
|
||||
#include "client/Client.h"
|
||||
#include "client/ThumbnailBroker.h"
|
||||
#include "simulation/SaveRenderer.h"
|
||||
|
||||
namespace ui {
|
||||
@ -62,7 +62,8 @@ SaveButton::SaveButton(Point position, Point size, SaveFile * file):
|
||||
voteColour(255, 0, 0),
|
||||
selectable(false),
|
||||
selected(false),
|
||||
wantsDraw(false)
|
||||
wantsDraw(false),
|
||||
waitingForThumb(false)
|
||||
{
|
||||
if(file)
|
||||
{
|
||||
@ -78,6 +79,8 @@ SaveButton::SaveButton(Point position, Point size, SaveFile * file):
|
||||
|
||||
SaveButton::~SaveButton()
|
||||
{
|
||||
ThumbnailBroker::Ref().DetachThumbnailListener(this);
|
||||
|
||||
if(thumbnail)
|
||||
delete thumbnail;
|
||||
if(actionCallback)
|
||||
@ -88,11 +91,43 @@ SaveButton::~SaveButton()
|
||||
delete file;
|
||||
}
|
||||
|
||||
void SaveButton::OnThumbnailReady(Thumbnail * thumb)
|
||||
{
|
||||
if(thumb)
|
||||
{
|
||||
if(thumbnail)
|
||||
delete thumbnail;
|
||||
thumbnail = thumb;
|
||||
waitingForThumb = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveButton::Tick(float dt)
|
||||
{
|
||||
Thumbnail * tempThumb;
|
||||
if(!thumbnail && !waitingForThumb)
|
||||
{
|
||||
if(save)
|
||||
{
|
||||
if(save->GetGameSave())
|
||||
{
|
||||
waitingForThumb = true;
|
||||
ThumbnailBroker::Ref().RenderThumbnail(save->GetGameSave(), Size.X-3, Size.Y-25, this);
|
||||
}
|
||||
else if(save->GetID())
|
||||
{
|
||||
waitingForThumb = true;
|
||||
ThumbnailBroker::Ref().RetrieveThumbnail(save->GetID() , Size.X-3, Size.Y-25, this);
|
||||
}
|
||||
}
|
||||
else if(file && file->GetGameSave())
|
||||
{
|
||||
waitingForThumb = true;
|
||||
ThumbnailBroker::Ref().RenderThumbnail(file->GetGameSave(), Size.X-3, Size.Y-25, this);
|
||||
}
|
||||
}
|
||||
/*Thumbnail * tempThumb;
|
||||
float scaleFactorY = 1.0f, scaleFactorX = 1.0f;
|
||||
if(!thumbnail/* && wantsDraw*/)
|
||||
if(!thumbnail)
|
||||
{
|
||||
if(save)
|
||||
{
|
||||
@ -148,7 +183,7 @@ void SaveButton::Tick(float dt)
|
||||
free(thumbData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void SaveButton::Draw(const Point& screenPos)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Component.h"
|
||||
#include "client/SaveFile.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "client/ThumbnailListener.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "search/Thumbnail.h"
|
||||
#include "interface/Colour.h"
|
||||
@ -21,13 +22,14 @@ public:
|
||||
virtual ~SaveButtonAction() {}
|
||||
};
|
||||
|
||||
class SaveButton : public Component
|
||||
class SaveButton : public Component, public ThumbnailListener
|
||||
{
|
||||
SaveFile * file;
|
||||
SaveInfo * save;
|
||||
Thumbnail * thumbnail;
|
||||
std::string name;
|
||||
bool wantsDraw;
|
||||
bool waitingForThumb;
|
||||
public:
|
||||
SaveButton(Point position, Point size, SaveInfo * save);
|
||||
SaveButton(Point position, Point size, SaveFile * file);
|
||||
@ -42,6 +44,8 @@ public:
|
||||
virtual void Draw(const Point& screenPos);
|
||||
virtual void Tick(float dt);
|
||||
|
||||
virtual void OnThumbnailReady(Thumbnail * thumb);
|
||||
|
||||
void SetSelected(bool selected_) { selected = selected_; }
|
||||
bool GetSelected() { return selected; }
|
||||
void SetSelectable(bool selectable_) { selectable = selectable_; }
|
||||
|
@ -42,6 +42,33 @@ Thumbnail::Thumbnail(int _id, int _datestamp, pixel * _data, ui::Point _size):
|
||||
}
|
||||
}
|
||||
|
||||
void Thumbnail::Resize(int width, int height)
|
||||
{
|
||||
Resize(ui::Point(width, height));
|
||||
}
|
||||
|
||||
void Thumbnail::Resize(ui::Point newSize)
|
||||
{
|
||||
float scaleFactorX = 1.0f, scaleFactorY = 1.0f;
|
||||
if(Size.Y > newSize.Y)
|
||||
{
|
||||
scaleFactorY = float(newSize.Y)/((float)Size.Y);
|
||||
}
|
||||
if(Size.X > newSize.X)
|
||||
{
|
||||
scaleFactorX = float(newSize.X)/((float)Size.X);
|
||||
}
|
||||
if(scaleFactorY < 1.0f || scaleFactorX < 1.0f)
|
||||
{
|
||||
float scaleFactor = scaleFactorY < scaleFactorX ? scaleFactorY : scaleFactorX;
|
||||
pixel * thumbData = Data;
|
||||
Data = Graphics::resample_img(thumbData, Size.X, Size.Y, Size.X * scaleFactor, Size.Y * scaleFactor);
|
||||
Size.X *= scaleFactor;
|
||||
Size.Y *= scaleFactor;
|
||||
free(thumbData);
|
||||
}
|
||||
}
|
||||
|
||||
Thumbnail::~Thumbnail()
|
||||
{
|
||||
if(Data)
|
||||
|
@ -14,6 +14,9 @@ public:
|
||||
|
||||
~Thumbnail();
|
||||
|
||||
void Resize(int Width, int Height);
|
||||
void Resize(ui::Point newSize);
|
||||
|
||||
int ID, Datestamp;
|
||||
ui::Point Size;
|
||||
pixel * Data;
|
||||
|
Loading…
Reference in New Issue
Block a user