parent
47bc7e439a
commit
93e8e11cf7
@ -0,0 +1,138 @@
|
|||||||
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||||
|
// Copyright (C) 2016 Winch Gate Property Limited
|
||||||
|
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <nel/misc/types_nl.h>
|
||||||
|
#include "texture_browser.h"
|
||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
// NeL includes
|
||||||
|
// #include <nel/misc/debug.h>
|
||||||
|
#include <nel/misc/common.h>
|
||||||
|
#include <nel/misc/bitmap.h>
|
||||||
|
#include <nel/misc/file.h>
|
||||||
|
#include <nel/pipeline/project_config.h>
|
||||||
|
|
||||||
|
// Project includes
|
||||||
|
#include "../shared_widgets/event_loop.h"
|
||||||
|
|
||||||
|
// See also: studio/.../gui_editor/texture_chooser.cpp
|
||||||
|
|
||||||
|
// UTILITY ->
|
||||||
|
QPixmap qPixmapFromCBitmap(NLMISC::CBitmap &bitmap, bool alpha)
|
||||||
|
{
|
||||||
|
QImage img(bitmap.getWidth(), bitmap.getHeight(), alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
|
||||||
|
NLMISC::CObjectVector<uint8> &pixels = bitmap.getPixels();
|
||||||
|
|
||||||
|
uint height = bitmap.getHeight();
|
||||||
|
uint stride = bitmap.getWidth() * sizeof(NLMISC::CRGBA);
|
||||||
|
for (uint y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
// memcpy(img.scanLine(y), &pixels[y * stride], stride);
|
||||||
|
// Convert from ABGR to ARGB
|
||||||
|
uint8 *dst = img.scanLine(y);
|
||||||
|
uint8 *src = &pixels[y * stride];
|
||||||
|
for (uint x = 0; x < stride; x += 4)
|
||||||
|
{
|
||||||
|
dst[x] = src[x + 2];
|
||||||
|
dst[x + 1] = src[x + 1];
|
||||||
|
dst[x + 2] = src[x];
|
||||||
|
dst[x + 3] = src[x + 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPixmap::fromImage(img);
|
||||||
|
}
|
||||||
|
// <- UTILITY
|
||||||
|
|
||||||
|
CTextureBrowser::CTextureBrowser(QWidget *parent) : QListWidget(parent)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<CStdFunctionVoid>("CStdFunctionVoid");
|
||||||
|
m_Thread = new CEventLoop();
|
||||||
|
m_Thread->run();
|
||||||
|
|
||||||
|
setViewMode(QListWidget::IconMode);
|
||||||
|
setIconSize(QSize(200, 200));
|
||||||
|
setResizeMode(QListWidget::Adjust);
|
||||||
|
|
||||||
|
setDirectory("W:/database/stuff/fyros/agents/_textures/actors/");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTextureBrowser::~CTextureBrowser()
|
||||||
|
{
|
||||||
|
m_Thread->clear();
|
||||||
|
delete m_Thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureBrowser::setDirectory(const QString &dir)
|
||||||
|
{
|
||||||
|
// Remove any pending stuff
|
||||||
|
m_Thread->clear();
|
||||||
|
|
||||||
|
// Sync up, clear, and start processing
|
||||||
|
m_Thread->immediate([this, dir]() -> void {
|
||||||
|
invokeStdFunction([this, dir]() -> void {
|
||||||
|
// m_listeWidget->addItem(new QListWidgetItem(QIcon("../earth.jpg"), "Earth"));
|
||||||
|
clear();
|
||||||
|
std::vector<std::string> files;
|
||||||
|
NLMISC::CPath::getPathContent(dir.toUtf8().data(), false, false, true, files);
|
||||||
|
for (size_t i = 0; i < files.size(); ++i)
|
||||||
|
{
|
||||||
|
std::string &file = files[i];
|
||||||
|
m_Thread->immediate([this, file]() -> void {
|
||||||
|
std::string ext = NLMISC::toLower(NLMISC::CFile::getExtension(file));
|
||||||
|
if (ext == "dds" || ext == "tga" || ext == "png" || ext == "jpg" || ext == "jpeg")
|
||||||
|
{
|
||||||
|
NLMISC::CIFile f;
|
||||||
|
if (f.open(file))
|
||||||
|
{
|
||||||
|
NLMISC::CBitmap bitmap;
|
||||||
|
bitmap.load(f);
|
||||||
|
bitmap.resample(128, 128);
|
||||||
|
QPixmap pixmap = qPixmapFromCBitmap(bitmap, false);
|
||||||
|
QString fileName = QFileInfo(QString::fromUtf8(file.c_str())).fileName();
|
||||||
|
invokeStdFunction([this, pixmap, fileName]() -> void {
|
||||||
|
addItem(new QListWidgetItem(QIcon(pixmap), fileName));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureBrowser::invokeStdFunction(CStdFunctionVoid f)
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(this, "callStdFunction", Qt::QueuedConnection, Q_ARG(CStdFunctionVoid, f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureBrowser::callStdFunction(CStdFunctionVoid f)
|
||||||
|
{
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of file */
|
@ -0,0 +1,70 @@
|
|||||||
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||||
|
// Copyright (C) 2016 Winch Gate Property Limited
|
||||||
|
// Author: Jan Boon <jan.boon@kaetemi.be>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef NL_TEXTURE_BROWSER_H
|
||||||
|
#define NL_TEXTURE_BROWSER_H
|
||||||
|
#include <nel/misc/types_nl.h>
|
||||||
|
|
||||||
|
// STL includes
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QListWidget>
|
||||||
|
|
||||||
|
// NeL includes
|
||||||
|
// ...
|
||||||
|
|
||||||
|
class CEventLoop;
|
||||||
|
typedef std::function<void()> CStdFunctionVoid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CTextureBrowser
|
||||||
|
* \brief CTextureBrowser
|
||||||
|
* \date 2016-02-18 14:06GMT
|
||||||
|
* \author Jan Boon <jan.boon@kaetemi.be>
|
||||||
|
*/
|
||||||
|
class CTextureBrowser : public QListWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CTextureBrowser(QWidget *parent = NULL);
|
||||||
|
virtual ~CTextureBrowser();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setDirectory(const QString &dir);
|
||||||
|
|
||||||
|
// STD INVOKE ->
|
||||||
|
public:
|
||||||
|
void invokeStdFunction(CStdFunctionVoid f);
|
||||||
|
private slots:
|
||||||
|
void callStdFunction(CStdFunctionVoid f);
|
||||||
|
// <- STD INVOKE
|
||||||
|
|
||||||
|
private:
|
||||||
|
CEventLoop *m_Thread;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CTextureBrowser(const CTextureBrowser &);
|
||||||
|
CTextureBrowser &operator=(const CTextureBrowser &);
|
||||||
|
|
||||||
|
}; /* class CTextureBrowser */
|
||||||
|
|
||||||
|
#endif /* #ifndef NL_TEXTURE_BROWSER_H */
|
||||||
|
|
||||||
|
/* end of file */
|
@ -0,0 +1,281 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (C) 2016 by authors
|
||||||
|
Author: Jan Boon <jan.boon@kaetemi.be>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Source: https://github.com/kaetemi/threadutil/blob/master/threadutil/eventloop.h
|
||||||
|
|
||||||
|
#ifndef NL_EVENT_LOOP_H
|
||||||
|
#define NL_EVENT_LOOP_H
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class CEventLoop
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CEventLoop() : m_Running(false), m_Handle(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~CEventLoop()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
m_Running = true;
|
||||||
|
m_Thread = std::move(std::thread(&CEventLoop::loop, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void runSync()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
m_Running = true;
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() // thread-safe
|
||||||
|
{
|
||||||
|
m_Running = false;
|
||||||
|
poke();
|
||||||
|
if (m_Thread.joinable())
|
||||||
|
m_Thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() // thread-safe
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueLock);
|
||||||
|
std::unique_lock<std::mutex> tlock(m_QueueTimeoutLock);
|
||||||
|
m_Immediate = std::move(std::queue<std::function<void()>>());
|
||||||
|
m_Timeout = std::move(std::priority_queue<timeout_func>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(int handle) // thread-safe, relatively slow, not recommended nor reliable to do this for timeouts, only reliable for intervals
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
|
||||||
|
std::priority_queue<timeout_func> timeout;
|
||||||
|
while (m_Timeout.size())
|
||||||
|
{
|
||||||
|
if (m_Timeout.top().handle != handle)
|
||||||
|
timeout.push(m_Timeout.top());
|
||||||
|
m_Timeout.pop();
|
||||||
|
}
|
||||||
|
m_Timeout = std::move(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void join() // thread-safe
|
||||||
|
{
|
||||||
|
std::mutex syncLock;
|
||||||
|
std::condition_variable syncCond;
|
||||||
|
std::unique_lock<std::mutex> lock(syncLock);
|
||||||
|
immediate([this, &syncCond]() -> void {
|
||||||
|
syncCond.notify_one();
|
||||||
|
});
|
||||||
|
m_PokeCond.wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void immediate(std::function<void()> f) // thread-safe
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueLock);
|
||||||
|
m_Immediate.push(f);
|
||||||
|
poke();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class rep, class period>
|
||||||
|
int timeout(std::function<void()> f, const std::chrono::duration<rep, period>& delta) // thread-safe
|
||||||
|
{
|
||||||
|
timeout_func tf;
|
||||||
|
tf.f = f;
|
||||||
|
tf.time = std::chrono::steady_clock::now() + delta;
|
||||||
|
tf.interval = std::chrono::nanoseconds::zero();
|
||||||
|
tf.handle = ++m_Handle;
|
||||||
|
; {
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
|
||||||
|
m_Timeout.push(tf);
|
||||||
|
poke();
|
||||||
|
}
|
||||||
|
return tf.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class rep, class period>
|
||||||
|
int interval(std::function<void()> f, const std::chrono::duration<rep, period>& interval) // thread-safe
|
||||||
|
{
|
||||||
|
timeout_func tf;
|
||||||
|
tf.f = f;
|
||||||
|
tf.time = std::chrono::steady_clock::now() + interval;
|
||||||
|
tf.interval = interval;
|
||||||
|
tf.handle = ++m_Handle;
|
||||||
|
; {
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
|
||||||
|
m_Timeout.push(tf);
|
||||||
|
poke();
|
||||||
|
}
|
||||||
|
return tf.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int timed(std::function<void()> f, const std::chrono::steady_clock::time_point &point) // thread-safe
|
||||||
|
{
|
||||||
|
timeout_func tf;
|
||||||
|
tf.f = f;
|
||||||
|
tf.time = point;
|
||||||
|
tf.interval = std::chrono::steady_clock::duration::zero();
|
||||||
|
tf.handle = ++m_Handle;
|
||||||
|
; {
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
|
||||||
|
m_Timeout.push(tf);
|
||||||
|
poke();
|
||||||
|
}
|
||||||
|
return tf.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void thread(std::function<void()> f, std::function<void()> callback)
|
||||||
|
{
|
||||||
|
std::thread t([this, f, callback]() -> void {
|
||||||
|
f();
|
||||||
|
immediate(callback);
|
||||||
|
});
|
||||||
|
t.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
while (m_Running)
|
||||||
|
{
|
||||||
|
m_Poked = false;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
m_QueueLock.lock();
|
||||||
|
if (!m_Immediate.size())
|
||||||
|
{
|
||||||
|
m_QueueLock.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::function<void()> f = m_Immediate.front();
|
||||||
|
m_Immediate.pop();
|
||||||
|
m_QueueLock.unlock();
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool poked = false;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
m_QueueTimeoutLock.lock();
|
||||||
|
if (!m_Timeout.size())
|
||||||
|
{
|
||||||
|
m_QueueTimeoutLock.unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const timeout_func &tfr = m_Timeout.top();
|
||||||
|
if (tfr.time > std::chrono::steady_clock::now()) // wait
|
||||||
|
{
|
||||||
|
m_QueueTimeoutLock.unlock();
|
||||||
|
; {
|
||||||
|
std::unique_lock<std::mutex> lock(m_PokeLock);
|
||||||
|
if (!m_Poked)
|
||||||
|
m_PokeCond.wait_until(lock, tfr.time);
|
||||||
|
}
|
||||||
|
poked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
timeout_func tf = tfr;
|
||||||
|
m_Timeout.pop();
|
||||||
|
m_QueueTimeoutLock.unlock();
|
||||||
|
tf.f(); // call
|
||||||
|
if (tf.interval > std::chrono::nanoseconds::zero()) // repeat
|
||||||
|
{
|
||||||
|
tf.time += tf.interval;
|
||||||
|
; {
|
||||||
|
std::unique_lock<std::mutex> lock(m_QueueTimeoutLock);
|
||||||
|
m_Timeout.push(tf);
|
||||||
|
poke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!poked)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_PokeLock);
|
||||||
|
if (!m_Poked)
|
||||||
|
m_PokeCond.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void poke() // private
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_PokeLock);
|
||||||
|
m_PokeCond.notify_one();
|
||||||
|
m_Poked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct timeout_func
|
||||||
|
{
|
||||||
|
std::function<void()> f;
|
||||||
|
std::chrono::steady_clock::time_point time;
|
||||||
|
std::chrono::steady_clock::duration interval;
|
||||||
|
int handle;
|
||||||
|
|
||||||
|
bool operator <(const timeout_func &o) const
|
||||||
|
{
|
||||||
|
return time > o.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile bool m_Running;
|
||||||
|
volatile bool m_Poked;
|
||||||
|
std::thread m_Thread;
|
||||||
|
std::mutex m_PokeLock;
|
||||||
|
std::condition_variable m_PokeCond;
|
||||||
|
|
||||||
|
std::mutex m_QueueLock;
|
||||||
|
std::queue<std::function<void()>> m_Immediate;
|
||||||
|
std::mutex m_QueueTimeoutLock;
|
||||||
|
std::priority_queue<timeout_func> m_Timeout;
|
||||||
|
int m_Handle;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* NL_EVENT_LOOP_H */
|
||||||
|
|
||||||
|
/* end of file */
|
Loading…
Reference in New Issue