summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSimon Robertshaw <simon@hardwired.org.uk>2012-07-31 18:49:08 (GMT)
committer Simon Robertshaw <simon@hardwired.org.uk>2012-07-31 18:49:08 (GMT)
commit1d258eab6b0ec3740d634f014af5dbff882e0069 (patch)
tree956d446f144415d4f188dcca17c7dffba08851c2 /src
parent303b546ceb134df48763730cbfd8ce7b6df008a2 (diff)
downloadpowder-1d258eab6b0ec3740d634f014af5dbff882e0069.zip
powder-1d258eab6b0ec3740d634f014af5dbff882e0069.tar.gz
ThumbnailBroker for background retrieval and rendering
Diffstat (limited to 'src')
-rw-r--r--src/client/Client.cpp5
-rw-r--r--src/client/Client.h2
-rw-r--r--src/client/ThumbnailBroker.cpp372
-rw-r--r--src/client/ThumbnailBroker.h58
-rw-r--r--src/client/ThumbnailListener.h11
-rw-r--r--src/interface/SaveButton.cpp45
-rw-r--r--src/interface/SaveButton.h6
-rw-r--r--src/search/Thumbnail.cpp27
-rw-r--r--src/search/Thumbnail.h3
9 files changed, 523 insertions, 6 deletions
diff --git a/src/client/Client.cpp b/src/client/Client.cpp
index f5ea91d..7f26a4c 100644
--- a/src/client/Client.cpp
+++ b/src/client/Client.cpp
@@ -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))
{
diff --git a/src/client/Client.h b/src/client/Client.h
index d7e1afb..ccf62f3 100644
--- a/src/client/Client.h
+++ b/src/client/Client.h
@@ -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();
diff --git a/src/client/ThumbnailBroker.cpp b/src/client/ThumbnailBroker.cpp
new file mode 100644
index 0000000..86a38d8
--- /dev/null
+++ b/src/client/ThumbnailBroker.cpp
@@ -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);
+} \ No newline at end of file
diff --git a/src/client/ThumbnailBroker.h b/src/client/ThumbnailBroker.h
new file mode 100644
index 0000000..8fec6c1
--- /dev/null
+++ b/src/client/ThumbnailBroker.h
@@ -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);
+}; \ No newline at end of file
diff --git a/src/client/ThumbnailListener.h b/src/client/ThumbnailListener.h
new file mode 100644
index 0000000..c46b4a4
--- /dev/null
+++ b/src/client/ThumbnailListener.h
@@ -0,0 +1,11 @@
+#pragma once
+
+class Thumbnail;
+class ThumbnailListener
+{
+public:
+ ThumbnailListener() {}
+ virtual ~ThumbnailListener() {}
+
+ virtual void OnThumbnailReady(Thumbnail * thumb) {}
+};
diff --git a/src/interface/SaveButton.cpp b/src/interface/SaveButton.cpp
index b312d3a..18e382d 100644
--- a/src/interface/SaveButton.cpp
+++ b/src/interface/SaveButton.cpp
@@ -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)
diff --git a/src/interface/SaveButton.h b/src/interface/SaveButton.h
index faa85d7..a5902cd 100644
--- a/src/interface/SaveButton.h
+++ b/src/interface/SaveButton.h
@@ -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_; }
diff --git a/src/search/Thumbnail.cpp b/src/search/Thumbnail.cpp
index 6a97a08..60c6b2b 100644
--- a/src/search/Thumbnail.cpp
+++ b/src/search/Thumbnail.cpp
@@ -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)
diff --git a/src/search/Thumbnail.h b/src/search/Thumbnail.h
index d951a96..74ebbad 100644
--- a/src/search/Thumbnail.h
+++ b/src/search/Thumbnail.h
@@ -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;