mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 23:40:02 +01:00
@@ -6,10 +6,12 @@
|
||||
#include "Mixer.h"
|
||||
#include "MixingGroup.h"
|
||||
#include "tinyxml2Toolkit.h"
|
||||
#include "SessionSource.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "Settings.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "Interpolator.h"
|
||||
|
||||
#include "ActionManager.h"
|
||||
|
||||
@@ -17,20 +19,59 @@
|
||||
#define ACTION_DEBUG
|
||||
#endif
|
||||
|
||||
#define HISTORY_NODE(i) std::to_string(i).insert(0,1,'H')
|
||||
#define SNAPSHOT_NODE(i) std::to_string(i).insert(0,1,'S')
|
||||
|
||||
using namespace tinyxml2;
|
||||
|
||||
Action::Action(): step_(0), max_step_(0)
|
||||
void captureMixerSession(tinyxml2::XMLDocument *doc, std::string node, std::string label)
|
||||
{
|
||||
|
||||
// create node
|
||||
XMLElement *sessionNode = doc->NewElement( node.c_str() );
|
||||
doc->InsertEndChild(sessionNode);
|
||||
// label describes the action
|
||||
sessionNode->SetAttribute("label", label.c_str() );
|
||||
// view indicates the view when this action occured
|
||||
sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode());
|
||||
|
||||
// get session to operate on
|
||||
Session *se = Mixer::manager().session();
|
||||
se->lock();
|
||||
|
||||
// get the thumbnail (requires one opengl update to render)
|
||||
FrameBufferImage *thumbnail = se->thumbnail();
|
||||
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, doc);
|
||||
if (imageelement)
|
||||
sessionNode->InsertEndChild(imageelement);
|
||||
delete thumbnail;
|
||||
|
||||
// save all sources using source visitor
|
||||
SessionVisitor sv(doc, sessionNode);
|
||||
for (auto iter = se->begin(); iter != se->end(); ++iter, sv.setRoot(sessionNode) )
|
||||
(*iter)->accept(sv);
|
||||
|
||||
se->unlock();
|
||||
}
|
||||
|
||||
void Action::clear()
|
||||
|
||||
Action::Action(): history_step_(0), history_max_step_(0), locked_(false),
|
||||
snapshot_id_(0), snapshot_node_(nullptr), interpolator_(nullptr), interpolator_node_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Action::init()
|
||||
{
|
||||
// clean the history
|
||||
xmlDoc_.Clear();
|
||||
step_ = 0;
|
||||
max_step_ = 0;
|
||||
history_doc_.Clear();
|
||||
history_step_ = 0;
|
||||
history_max_step_ = 0;
|
||||
|
||||
// reset snapshot
|
||||
snapshot_id_ = 0;
|
||||
snapshot_node_ = nullptr;
|
||||
|
||||
// start fresh
|
||||
store("Session start");
|
||||
}
|
||||
|
||||
@@ -41,69 +82,53 @@ void Action::store(const std::string &label)
|
||||
return;
|
||||
|
||||
// incremental naming of history nodes
|
||||
step_++;
|
||||
std::string nodename = "H" + std::to_string(step_);
|
||||
history_step_++;
|
||||
|
||||
// erase future
|
||||
for (uint e = step_; e <= max_step_; e++) {
|
||||
std::string name = "H" + std::to_string(e);
|
||||
XMLElement *node = xmlDoc_.FirstChildElement( name.c_str() );
|
||||
for (uint e = history_step_; e <= history_max_step_; e++) {
|
||||
XMLElement *node = history_doc_.FirstChildElement( HISTORY_NODE(e).c_str() );
|
||||
if ( node )
|
||||
xmlDoc_.DeleteChild(node);
|
||||
history_doc_.DeleteChild(node);
|
||||
}
|
||||
max_step_ = step_;
|
||||
history_max_step_ = history_step_;
|
||||
|
||||
// create history node
|
||||
XMLElement *sessionNode = xmlDoc_.NewElement( nodename.c_str() );
|
||||
xmlDoc_.InsertEndChild(sessionNode);
|
||||
// label describes the action
|
||||
sessionNode->SetAttribute("label", label.c_str());
|
||||
// view indicates the view when this action occured
|
||||
sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode());
|
||||
// threaded capturing state of current session
|
||||
std::thread(captureMixerSession, &history_doc_, HISTORY_NODE(history_step_), label).detach();
|
||||
|
||||
// get session to operate on
|
||||
Session *se = Mixer::manager().session();
|
||||
|
||||
// save all sources using source visitor
|
||||
SessionVisitor sv(&xmlDoc_, sessionNode);
|
||||
for (auto iter = se->begin(); iter != se->end(); iter++, sv.setRoot(sessionNode) )
|
||||
(*iter)->accept(sv);
|
||||
|
||||
// debug
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str());
|
||||
// XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml");
|
||||
Log::Info("Action stored %d '%s'", history_step_, label.c_str());
|
||||
// XMLSaveDoc(&history_doc_, "/home/bhbn/history.xml");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Action::undo()
|
||||
{
|
||||
// not possible to go to 1 -1 = 0
|
||||
if (step_ <= 1)
|
||||
if (history_step_ <= 1)
|
||||
return;
|
||||
|
||||
// restore always changes step_ to step_ - 1
|
||||
restore( step_ - 1);
|
||||
restore( history_step_ - 1);
|
||||
}
|
||||
|
||||
void Action::redo()
|
||||
{
|
||||
// not possible to go to max_step_ + 1
|
||||
if (step_ >= max_step_)
|
||||
if (history_step_ >= history_max_step_)
|
||||
return;
|
||||
|
||||
// restore always changes step_ to step_ + 1
|
||||
restore( step_ + 1);
|
||||
restore( history_step_ + 1);
|
||||
}
|
||||
|
||||
|
||||
void Action::stepTo(uint target)
|
||||
{
|
||||
// get reasonable target
|
||||
uint t = CLAMP(target, 1, max_step_);
|
||||
uint t = CLAMP(target, 1, history_max_step_);
|
||||
|
||||
// ignore t == step_
|
||||
if (t != step_)
|
||||
if (t != history_step_)
|
||||
restore(t);
|
||||
}
|
||||
|
||||
@@ -111,137 +136,263 @@ std::string Action::label(uint s) const
|
||||
{
|
||||
std::string l = "";
|
||||
|
||||
if (s > 0 && s <= max_step_) {
|
||||
std::string nodename = "H" + std::to_string(s);
|
||||
const XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
|
||||
l = sessionNode->Attribute("label");
|
||||
if (s > 0 && s <= history_max_step_) {
|
||||
const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s).c_str());
|
||||
if (sessionNode)
|
||||
l = sessionNode->Attribute("label");
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
FrameBufferImage *Action::thumbnail(uint s) const
|
||||
{
|
||||
FrameBufferImage *img = nullptr;
|
||||
|
||||
if (s > 0 && s <= history_max_step_) {
|
||||
const XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(s).c_str());
|
||||
if (sessionNode)
|
||||
img = SessionLoader::XMLToImage(sessionNode);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void Action::restore(uint target)
|
||||
{
|
||||
// lock
|
||||
locked_ = true;
|
||||
|
||||
// get history node of target step
|
||||
step_ = CLAMP(target, 1, max_step_);
|
||||
std::string nodename = "H" + std::to_string(step_);
|
||||
XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
|
||||
history_step_ = CLAMP(target, 1, history_max_step_);
|
||||
XMLElement *sessionNode = history_doc_.FirstChildElement( HISTORY_NODE(history_step_).c_str() );
|
||||
|
||||
// ask view to refresh, and switch to action view if user prefers
|
||||
int view = Settings::application.current_view ;
|
||||
if (Settings::application.action_history_follow_view)
|
||||
sessionNode->QueryIntAttribute("view", &view);
|
||||
Mixer::manager().setView( (View::Mode) view);
|
||||
if (sessionNode) {
|
||||
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Restore %s '%s' ", nodename.c_str(), sessionNode->Attribute("label"));
|
||||
#endif
|
||||
// ask view to refresh, and switch to action view if user prefers
|
||||
int view = Settings::application.current_view ;
|
||||
if (Settings::application.action_history_follow_view)
|
||||
sessionNode->QueryIntAttribute("view", &view);
|
||||
Mixer::manager().setView( (View::Mode) view);
|
||||
|
||||
//
|
||||
// compare source lists
|
||||
//
|
||||
|
||||
// we operate on the current session
|
||||
Session *se = Mixer::manager().session();
|
||||
if (se == nullptr)
|
||||
return;
|
||||
|
||||
// sessionsources contains list of ids of all sources currently in the session (before loading)
|
||||
SourceIdList session_sources = se->getIdList();
|
||||
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
|
||||
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
|
||||
|
||||
// load history status:
|
||||
// - if a source exists, its attributes are updated, and that's all
|
||||
// - if a source does not exists (in current session), it is created inside the session
|
||||
SessionLoader loader( se );
|
||||
loader.load( sessionNode );
|
||||
|
||||
// loaded_sources contains map of xml ids of all sources treated by loader
|
||||
std::map< uint64_t, Source* > loaded_sources = loader.getSources();
|
||||
|
||||
// remove intersect of both lists (sources were updated by SessionLoader)
|
||||
for( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); ){
|
||||
auto ssit = std::find(session_sources.begin(), session_sources.end(), (*lsit).first);
|
||||
if ( ssit != session_sources.end() ) {
|
||||
lsit = loaded_sources.erase(lsit);
|
||||
session_sources.erase(ssit);
|
||||
}
|
||||
else
|
||||
lsit++;
|
||||
}
|
||||
|
||||
// remaining ids in list sessionsources : to remove
|
||||
while ( !session_sources.empty() ){
|
||||
Source *s = Mixer::manager().findSource( session_sources.front() );
|
||||
if (s!=nullptr) {
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Delete id %s\n", std::to_string(session_sources.front() ).c_str());
|
||||
#endif
|
||||
// remove the source from the mixer
|
||||
Mixer::manager().detach( s );
|
||||
// delete source from session
|
||||
se->deleteSource( s );
|
||||
}
|
||||
session_sources.pop_front();
|
||||
}
|
||||
|
||||
// remaining sources in list loaded_sources : to add
|
||||
for ( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); lsit++)
|
||||
{
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Recreate id %s to %s\n", std::to_string((*lsit).first).c_str(), std::to_string((*lsit).second->id()).c_str());
|
||||
#endif
|
||||
// attach created source
|
||||
Mixer::manager().attach( (*lsit).second );
|
||||
|
||||
// change the history to match the new id
|
||||
replaceSourceId( (*lsit).first, (*lsit).second->id());
|
||||
}
|
||||
|
||||
//
|
||||
// compare mixing groups
|
||||
//
|
||||
|
||||
// Get the list of mixing groups in the xml loader
|
||||
std::list< SourceList > loadergroups = loader.getMixingGroups();
|
||||
|
||||
// clear all session groups
|
||||
auto group_iter = se->beginMixingGroup();
|
||||
while ( group_iter != se->endMixingGroup() )
|
||||
group_iter = se->deleteMixingGroup(group_iter);
|
||||
|
||||
// apply all changes creating or modifying groups in the session
|
||||
// (after this, new groups are created and existing groups are adjusted)
|
||||
for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++) {
|
||||
se->link( *group_loader_it, Mixer::manager().view(View::MIXING)->scene.fg() );
|
||||
// actually restore
|
||||
Mixer::manager().restore(sessionNode);
|
||||
}
|
||||
|
||||
// free
|
||||
locked_ = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Action::replaceSourceId(uint64_t previousid, uint64_t newid)
|
||||
|
||||
void Action::snapshot(const std::string &label)
|
||||
{
|
||||
// loop over every session history step
|
||||
XMLElement* historyNode = xmlDoc_.FirstChildElement("H1");
|
||||
for( ; historyNode ; historyNode = historyNode->NextSiblingElement())
|
||||
// ignore if locked
|
||||
if (locked_)
|
||||
return;
|
||||
|
||||
std::string snap_label = BaseToolkit::uniqueName(label, labels());
|
||||
|
||||
// create snapshot id
|
||||
u_int64_t id = BaseToolkit::uniqueId();
|
||||
|
||||
// get session to operate on
|
||||
Session *se = Mixer::manager().session();
|
||||
se->snapshots()->keys_.push_back(id);
|
||||
|
||||
// threaded capture state of current session
|
||||
std::thread(captureMixerSession, se->snapshots()->xmlDoc_, SNAPSHOT_NODE(id), snap_label).detach();
|
||||
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Snapshot stored %d '%s'", id, snap_label.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Action::open(uint64_t snapshotid)
|
||||
{
|
||||
if ( snapshot_id_ != snapshotid )
|
||||
{
|
||||
// loop over every source in session history
|
||||
XMLElement* sourceNode = historyNode->FirstChildElement("Source");
|
||||
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
|
||||
{
|
||||
// check if this source node has this id
|
||||
uint64_t id_source_ = 0;
|
||||
sourceNode->QueryUnsigned64Attribute("id", &id_source_);
|
||||
if ( id_source_ == previousid )
|
||||
// change to new id
|
||||
sourceNode->SetAttribute("id", newid);
|
||||
// get snapshot node of target in current session
|
||||
Session *se = Mixer::manager().session();
|
||||
snapshot_node_ = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
|
||||
|
||||
if (snapshot_node_)
|
||||
snapshot_id_ = snapshotid;
|
||||
else
|
||||
snapshot_id_ = 0;
|
||||
|
||||
interpolator_node_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Action::replace(uint64_t snapshotid)
|
||||
{
|
||||
// ignore if locked or if no label is given
|
||||
if (locked_)
|
||||
return;
|
||||
|
||||
if (snapshotid > 0)
|
||||
open(snapshotid);
|
||||
|
||||
if (snapshot_node_) {
|
||||
// remember label
|
||||
std::string label = snapshot_node_->Attribute("label");
|
||||
|
||||
// remove previous node
|
||||
Session *se = Mixer::manager().session();
|
||||
se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ );
|
||||
|
||||
// threaded capture state of current session
|
||||
std::thread(captureMixerSession, se->snapshots()->xmlDoc_, SNAPSHOT_NODE(snapshot_id_), label).detach();
|
||||
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Snapshot replaced %d '%s'", snapshot_id_, label.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
std::list<uint64_t> Action::snapshots() const
|
||||
{
|
||||
return Mixer::manager().session()->snapshots()->keys_;
|
||||
}
|
||||
|
||||
std::list<std::string> Action::labels() const
|
||||
{
|
||||
std::list<std::string> names;
|
||||
|
||||
tinyxml2::XMLDocument *doc = Mixer::manager().session()->snapshots()->xmlDoc_;
|
||||
for ( XMLElement *snap = doc->FirstChildElement(); snap ; snap = snap->NextSiblingElement() )
|
||||
names.push_back( snap->Attribute("label"));
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
std::string Action::label(uint64_t snapshotid) const
|
||||
{
|
||||
std::string label = "";
|
||||
|
||||
// get snapshot node of target in current session
|
||||
Session *se = Mixer::manager().session();
|
||||
const XMLElement *snap = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
|
||||
|
||||
if (snap)
|
||||
label = snap->Attribute("label");
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
void Action::setLabel (uint64_t snapshotid, const std::string &label)
|
||||
{
|
||||
open(snapshotid);
|
||||
|
||||
if (snapshot_node_)
|
||||
snapshot_node_->SetAttribute("label", label.c_str());
|
||||
}
|
||||
|
||||
FrameBufferImage *Action::thumbnail(uint64_t snapshotid) const
|
||||
{
|
||||
FrameBufferImage *img = nullptr;
|
||||
|
||||
// get snapshot node of target in current session
|
||||
Session *se = Mixer::manager().session();
|
||||
const XMLElement *snap = se->snapshots()->xmlDoc_->FirstChildElement( SNAPSHOT_NODE(snapshotid).c_str() );
|
||||
|
||||
if (snap){
|
||||
img = SessionLoader::XMLToImage(snap);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void Action::remove(uint64_t snapshotid)
|
||||
{
|
||||
if (snapshotid > 0)
|
||||
open(snapshotid);
|
||||
|
||||
if (snapshot_node_) {
|
||||
// remove
|
||||
Session *se = Mixer::manager().session();
|
||||
se->snapshots()->xmlDoc_->DeleteChild( snapshot_node_ );
|
||||
se->snapshots()->keys_.remove( snapshot_id_ );
|
||||
}
|
||||
|
||||
snapshot_node_ = nullptr;
|
||||
snapshot_id_ = 0;
|
||||
}
|
||||
|
||||
void Action::restore(uint64_t snapshotid)
|
||||
{
|
||||
// lock
|
||||
locked_ = true;
|
||||
|
||||
if (snapshotid > 0)
|
||||
open(snapshotid);
|
||||
|
||||
if (snapshot_node_)
|
||||
// actually restore
|
||||
Mixer::manager().restore(snapshot_node_);
|
||||
|
||||
// free
|
||||
locked_ = false;
|
||||
|
||||
store("Snapshot " + label(snapshot_id_));
|
||||
}
|
||||
|
||||
float Action::interpolation()
|
||||
{
|
||||
float ret = 0.f;
|
||||
if ( interpolator_node_ == snapshot_node_ && interpolator_)
|
||||
ret = interpolator_->current();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Action::interpolate(float val, uint64_t snapshotid)
|
||||
{
|
||||
if (snapshotid > 0)
|
||||
open(snapshotid);
|
||||
|
||||
if (snapshot_node_) {
|
||||
|
||||
if ( interpolator_node_ != snapshot_node_ ) {
|
||||
|
||||
// change interpolator
|
||||
if (interpolator_)
|
||||
delete interpolator_;
|
||||
|
||||
// create new interpolator
|
||||
interpolator_ = new Interpolator;
|
||||
|
||||
// current session
|
||||
Session *se = Mixer::manager().session();
|
||||
|
||||
XMLElement* N = snapshot_node_->FirstChildElement("Source");
|
||||
for( ; N ; N = N->NextSiblingElement()) {
|
||||
|
||||
// check if a source with the given id exists in the session
|
||||
uint64_t id_xml_ = 0;
|
||||
N->QueryUnsigned64Attribute("id", &id_xml_);
|
||||
SourceList::iterator sit = se->find(id_xml_);
|
||||
|
||||
// a source with this id exists
|
||||
if ( sit != se->end() ) {
|
||||
// read target in the snapshot xml
|
||||
SourceCore target;
|
||||
SessionLoader::XMLToSourcecore(N, target);
|
||||
|
||||
// add an interpolator for this source
|
||||
interpolator_->add(*sit, target);
|
||||
}
|
||||
}
|
||||
|
||||
// operate interpolation on opened snapshot
|
||||
interpolator_node_ = snapshot_node_;
|
||||
}
|
||||
|
||||
if (interpolator_) {
|
||||
// Log::Info("Action::interpolate %f", val);
|
||||
interpolator_->apply( val );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,76 @@
|
||||
#ifndef ACTIONMANAGER_H
|
||||
#define ACTIONMANAGER_H
|
||||
|
||||
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
|
||||
class Interpolator;
|
||||
class FrameBufferImage;
|
||||
|
||||
class Action
|
||||
{
|
||||
// Private Constructor
|
||||
Action();
|
||||
Action(Action const& copy); // Not Implemented
|
||||
Action& operator=(Action const& copy); // Not Implemented
|
||||
Action(Action const& copy) = delete;
|
||||
Action& operator=(Action const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
static Action& manager()
|
||||
static Action& manager ()
|
||||
{
|
||||
// The only instance
|
||||
static Action _instance;
|
||||
return _instance;
|
||||
}
|
||||
void init ();
|
||||
|
||||
void store(const std::string &label);
|
||||
// Undo History
|
||||
void store (const std::string &label);
|
||||
void undo ();
|
||||
void redo ();
|
||||
void stepTo (uint target);
|
||||
|
||||
void clear();
|
||||
void undo();
|
||||
void redo();
|
||||
void stepTo(uint target);
|
||||
inline uint current () const { return history_step_; }
|
||||
inline uint max () const { return history_max_step_; }
|
||||
std::string label (uint s) const;
|
||||
FrameBufferImage *thumbnail (uint s) const;
|
||||
|
||||
inline uint current() const { return step_; }
|
||||
inline uint max() const { return max_step_; }
|
||||
// Snapshots
|
||||
void snapshot (const std::string &label = "");
|
||||
|
||||
std::string label(uint s) const;
|
||||
std::list<uint64_t> snapshots () const;
|
||||
uint64_t currentSnapshot () const { return snapshot_id_; }
|
||||
|
||||
void open (uint64_t snapshotid);
|
||||
void replace (uint64_t snapshotid = 0);
|
||||
void restore (uint64_t snapshotid = 0);
|
||||
void remove (uint64_t snapshotid = 0);
|
||||
|
||||
std::string label (uint64_t snapshotid) const;
|
||||
std::list<std::string> labels () const;
|
||||
void setLabel (uint64_t snapshotid, const std::string &label);
|
||||
FrameBufferImage *thumbnail (uint64_t snapshotid) const;
|
||||
|
||||
float interpolation ();
|
||||
void interpolate (float val, uint64_t snapshotid = 0);
|
||||
|
||||
private:
|
||||
|
||||
void restore(uint target);
|
||||
void replaceSourceId(uint64_t previousid, uint64_t newid);
|
||||
// void replaceSourceId(tinyxml2::XMLElement* parentnode, uint64_t previousid, uint64_t newid);
|
||||
|
||||
tinyxml2::XMLDocument xmlDoc_;
|
||||
uint step_;
|
||||
uint max_step_;
|
||||
tinyxml2::XMLDocument history_doc_;
|
||||
uint history_step_;
|
||||
uint history_max_step_;
|
||||
std::atomic<bool> locked_;
|
||||
void restore(uint target);
|
||||
|
||||
uint64_t snapshot_id_;
|
||||
tinyxml2::XMLElement *snapshot_node_;
|
||||
|
||||
Interpolator *interpolator_;
|
||||
tinyxml2::XMLElement *interpolator_node_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // ACTIONMANAGER_H
|
||||
|
||||
244
BaseToolkit.cpp
Normal file
244
BaseToolkit.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "BaseToolkit.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
#include <locale>
|
||||
#include <unicode/ustream.h>
|
||||
#include <unicode/translit.h>
|
||||
|
||||
uint64_t BaseToolkit::uniqueId()
|
||||
{
|
||||
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||
// 64-bit int 18446744073709551615UL
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000000000000UL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string BaseToolkit::uniqueName(const std::string &basename, std::list<std::string> existingnames)
|
||||
{
|
||||
std::string tentativename = basename;
|
||||
int count = 1;
|
||||
int max = 100;
|
||||
|
||||
// while tentativename can be found in the list of existingnames
|
||||
while ( std::find( existingnames.begin(), existingnames.end(), tentativename ) != existingnames.end() )
|
||||
{
|
||||
for( auto it = existingnames.cbegin(); it != existingnames.cend(); ++it) {
|
||||
if ( it->find(tentativename) != std::string::npos)
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count > 1)
|
||||
tentativename = basename + "_" + std::to_string( count );
|
||||
else
|
||||
tentativename += "_";
|
||||
|
||||
if ( --max < 0 ) // for safety only, should never be needed
|
||||
break;
|
||||
}
|
||||
|
||||
return tentativename;
|
||||
}
|
||||
|
||||
// Using ICU transliteration :
|
||||
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
|
||||
|
||||
std::string BaseToolkit::transliterate(const std::string &input)
|
||||
{
|
||||
auto ucs = icu::UnicodeString::fromUTF8(input);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
|
||||
firstTrans->transliterate(ucs);
|
||||
delete firstTrans;
|
||||
|
||||
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
|
||||
secondTrans->transliterate(ucs);
|
||||
delete secondTrans;
|
||||
|
||||
std::ostringstream output;
|
||||
output << ucs;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::byte_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
std::ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1024.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1024.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string BaseToolkit::bits_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
std::ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1000.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1000.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::trunc_string(const std::string& path, int N)
|
||||
{
|
||||
std::string trunc = path;
|
||||
int l = path.size();
|
||||
if ( l > N ) {
|
||||
trunc = std::string("...") + path.substr( l - N + 3 );
|
||||
}
|
||||
return trunc;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
}
|
||||
|
||||
return std::string(s0.cbegin(), _end);
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_suffix(const std::list<std::string> & allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
return suffix;
|
||||
}
|
||||
|
||||
|
||||
std::string BaseToolkit::common_pattern(const std::list<std::string> &allStrings)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
|
||||
return std::string(s0.cbegin(), _end) + "*" + suffix;
|
||||
}
|
||||
|
||||
std::string BaseToolkit::common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max)
|
||||
{
|
||||
if (allStrings.empty())
|
||||
return std::string();
|
||||
|
||||
// find common prefix and suffix
|
||||
const std::string &s0 = allStrings.front();
|
||||
auto _end = s0.cend();
|
||||
auto r_end = s0.crend();
|
||||
for (auto it=std::next(allStrings.cbegin()); it != allStrings.cend(); ++it)
|
||||
{
|
||||
auto _loc = std::mismatch(s0.cbegin(), s0.cend(), it->cbegin(), it->cend());
|
||||
if (std::distance(_loc.first, _end) > 0)
|
||||
_end = _loc.first;
|
||||
|
||||
auto r_loc = std::mismatch(s0.crbegin(), s0.crend(), it->crbegin(), it->crend());
|
||||
if (std::distance(r_loc.first, r_end) > 0)
|
||||
r_end = r_loc.first;
|
||||
}
|
||||
|
||||
// range of middle string, after prefix and before suffix
|
||||
size_t pos_prefix = std::distance(s0.cbegin(), _end);
|
||||
size_t pos_suffix = s0.size() - pos_prefix - std::distance(s0.crbegin(), r_end);
|
||||
|
||||
int n = -1;
|
||||
*max = 0;
|
||||
*min = INT_MAX;
|
||||
// loop over all strings to verify there are numbers between prefix and suffix
|
||||
for (auto it = allStrings.cbegin(); it != allStrings.cend(); ++it)
|
||||
{
|
||||
// get middle string, after prefix and before suffix
|
||||
std::string s = it->substr(pos_prefix, pos_suffix);
|
||||
// is this central string ONLY made of digits?
|
||||
if (s.end() == std::find_if(s.begin(), s.end(), [](unsigned char c)->bool { return !isdigit(c); })) {
|
||||
// yes, validate
|
||||
*max = std::max(*max, std::atoi(s.c_str()) );
|
||||
*min = std::min(*min, std::atoi(s.c_str()) );
|
||||
if (n < 0)
|
||||
n = s.size();
|
||||
else if ( n != s.size() ) {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( n < 1 )
|
||||
return std::string();
|
||||
|
||||
std::string suffix = std::string(s0.crbegin(), r_end);
|
||||
std::reverse(suffix.begin(), suffix.end());
|
||||
std::string pattern = std::string(s0.cbegin(), _end);
|
||||
pattern += "%0" + std::to_string(n) + "d";
|
||||
pattern += suffix;
|
||||
return pattern;
|
||||
}
|
||||
41
BaseToolkit.h
Normal file
41
BaseToolkit.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef BASETOOLKIT_H
|
||||
#define BASETOOLKIT_H
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
namespace BaseToolkit
|
||||
{
|
||||
|
||||
// get integer with unique id
|
||||
uint64_t uniqueId();
|
||||
|
||||
// proposes a name that is not already in the list
|
||||
std::string uniqueName(const std::string &basename, std::list<std::string> existingnames);
|
||||
|
||||
// get a transliteration to Latin of any string
|
||||
std::string transliterate(const std::string &input);
|
||||
|
||||
// get a string to display memory size with unit KB, MB, GB, TB
|
||||
std::string byte_to_string(long b);
|
||||
|
||||
// get a string to display bit size with unit Kbit, MBit, Gbit, Tbit
|
||||
std::string bits_to_string(long b);
|
||||
|
||||
// Truncate a string to display the right most N characters (e.g. ./home/me/toto.mpg -> ...ome/me/toto.mpg)
|
||||
std::string trunc_string(const std::string& path, int N);
|
||||
|
||||
// find common parts in a list of strings
|
||||
std::string common_prefix(const std::list<std::string> &allStrings);
|
||||
std::string common_suffix(const std::list<std::string> &allStrings);
|
||||
|
||||
// form a pattern "prefix*suffix" (e.g. file list)
|
||||
std::string common_pattern(const std::list<std::string> &allStrings);
|
||||
|
||||
// form a pattern "prefix%03dsuffix" (e.g. numbered file list)
|
||||
std::string common_numbered_pattern(const std::list<std::string> &allStrings, int *min, int *max);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // BASETOOLKIT_H
|
||||
@@ -72,7 +72,7 @@ GlmToolkit::AxisAlignedBoundingBox BoundingBoxVisitor::AABB(SourceList l, View *
|
||||
{
|
||||
// calculate bbox on selection
|
||||
BoundingBoxVisitor selection_visitor_bbox;
|
||||
for (auto it = l.begin(); it != l.end(); it++) {
|
||||
for (auto it = l.begin(); it != l.end(); ++it) {
|
||||
// calculate bounding box of area covered by selection
|
||||
selection_visitor_bbox.setModelview( view->scene.ws()->transform_ );
|
||||
(*it)->group( view->mode() )->accept(selection_visitor_bbox);
|
||||
@@ -86,7 +86,7 @@ GlmToolkit::OrientedBoundingBox BoundingBoxVisitor::OBB(SourceList l, View *view
|
||||
GlmToolkit::OrientedBoundingBox obb_;
|
||||
|
||||
// try the orientation of each source in the list
|
||||
for (auto source_it = l.begin(); source_it != l.end(); source_it++) {
|
||||
for (auto source_it = l.begin(); source_it != l.end(); ++source_it) {
|
||||
|
||||
float angle = (*source_it)->group( view->mode() )->rotation_.z;
|
||||
glm::mat4 transform = view->scene.ws()->transform_;
|
||||
@@ -94,7 +94,7 @@ GlmToolkit::OrientedBoundingBox BoundingBoxVisitor::OBB(SourceList l, View *view
|
||||
|
||||
// calculate bbox of the list in this orientation
|
||||
BoundingBoxVisitor selection_visitor_bbox;
|
||||
for (auto it = l.begin(); it != l.end(); it++) {
|
||||
for (auto it = l.begin(); it != l.end(); ++it) {
|
||||
// calculate bounding box of area covered by sources' nodes
|
||||
selection_visitor_bbox.setModelview( transform );
|
||||
(*it)->group( view->mode() )->accept(selection_visitor_bbox);
|
||||
|
||||
@@ -40,6 +40,7 @@ if(UNIX)
|
||||
set(CMAKE_OSX_ARCHITECTURES x86_64)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13")
|
||||
# set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X version to target for deployment")
|
||||
set (ICU_ROOT /usr/local/Cellar/icu4c/67.1)
|
||||
|
||||
else()
|
||||
add_definitions(-DLINUX)
|
||||
@@ -267,6 +268,7 @@ set(VMIX_BINARY "vimix")
|
||||
set(VMIX_SRCS
|
||||
main.cpp
|
||||
Log.cpp
|
||||
BaseToolkit.cpp
|
||||
Shader.cpp
|
||||
ImageShader.cpp
|
||||
ImageProcessingShader.cpp
|
||||
@@ -289,7 +291,7 @@ set(VMIX_SRCS
|
||||
Selection.cpp
|
||||
SessionSource.cpp
|
||||
SessionVisitor.cpp
|
||||
GarbageVisitor.cpp
|
||||
Interpolator.cpp
|
||||
SessionCreator.cpp
|
||||
Mixer.cpp
|
||||
FrameGrabber.cpp
|
||||
@@ -299,7 +301,6 @@ set(VMIX_SRCS
|
||||
Settings.cpp
|
||||
Screenshot.cpp
|
||||
Resource.cpp
|
||||
FileDialog.cpp
|
||||
Timeline.cpp
|
||||
Stream.cpp
|
||||
MediaPlayer.cpp
|
||||
@@ -308,6 +309,7 @@ set(VMIX_SRCS
|
||||
PatternSource.cpp
|
||||
DeviceSource.cpp
|
||||
NetworkSource.cpp
|
||||
MultiFileSource.cpp
|
||||
FrameBuffer.cpp
|
||||
RenderingManager.cpp
|
||||
UserInterfaceManager.cpp
|
||||
@@ -325,6 +327,7 @@ set(VMIX_SRCS
|
||||
NetworkToolkit.cpp
|
||||
Connection.cpp
|
||||
ActionManager.cpp
|
||||
Overlay.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -427,6 +430,7 @@ set(VMIX_RSC_FILES
|
||||
./rsc/mesh/icon_eye_slash.ply
|
||||
./rsc/mesh/icon_vector_square_slash.ply
|
||||
./rsc/mesh/icon_cube.ply
|
||||
./rsc/mesh/icon_sequence.ply
|
||||
./rsc/mesh/h_line.ply
|
||||
./rsc/mesh/h_mark.ply
|
||||
)
|
||||
|
||||
@@ -62,8 +62,8 @@ class Connection
|
||||
|
||||
// Private Constructor
|
||||
Connection();
|
||||
Connection(Connection const& copy); // Not Implemented
|
||||
Connection& operator=(Connection const& copy); // Not Implemented
|
||||
Connection(Connection const& copy) = delete;
|
||||
Connection& operator=(Connection const& copy) = delete;
|
||||
|
||||
public:
|
||||
static Connection& manager()
|
||||
|
||||
85
CopyVisitor.cpp
Normal file
85
CopyVisitor.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "GlmToolkit.h"
|
||||
#include "Scene.h"
|
||||
#include "Source.h"
|
||||
|
||||
#include "CopyVisitor.h"
|
||||
|
||||
Node *CopyVisitor::deepCopy(Node *node)
|
||||
{
|
||||
CopyVisitor cv;
|
||||
node->accept(cv);
|
||||
|
||||
return cv.current_;
|
||||
}
|
||||
|
||||
CopyVisitor::CopyVisitor() : current_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CopyVisitor::visit(Node &n)
|
||||
{
|
||||
}
|
||||
|
||||
void CopyVisitor::visit(Group &n)
|
||||
{
|
||||
Group *here = new Group;
|
||||
|
||||
// node
|
||||
current_ = here;
|
||||
current_->copyTransform(&n);
|
||||
current_->visible_ = n.visible_;
|
||||
|
||||
// loop
|
||||
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
|
||||
(*node)->accept(*this);
|
||||
|
||||
here->attach( current_ );
|
||||
|
||||
current_ = here;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CopyVisitor::visit(Switch &n)
|
||||
{
|
||||
Switch *here = new Switch;
|
||||
|
||||
// node
|
||||
current_ = here;
|
||||
current_->copyTransform(&n);
|
||||
current_->visible_ = n.visible_;
|
||||
|
||||
// switch properties
|
||||
here->setActive( n.active() );
|
||||
|
||||
// loop
|
||||
for (uint i = 0; i < n.numChildren(); ++i) {
|
||||
n.child(i)->accept(*this);
|
||||
|
||||
here->attach( current_ );
|
||||
|
||||
current_ = here;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CopyVisitor::visit(Scene &n)
|
||||
{
|
||||
Scene *here = new Scene;
|
||||
|
||||
current_ = here->root();
|
||||
n.root()->accept(*this);
|
||||
}
|
||||
|
||||
|
||||
void CopyVisitor::visit(Primitive &n)
|
||||
{
|
||||
Primitive *here = new Primitive;
|
||||
|
||||
// node
|
||||
current_ = here;
|
||||
current_->copyTransform(&n);
|
||||
current_->visible_ = n.visible_;
|
||||
|
||||
}
|
||||
24
CopyVisitor.h
Normal file
24
CopyVisitor.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef COPYVISITOR_H
|
||||
#define COPYVISITOR_H
|
||||
|
||||
#include "Visitor.h"
|
||||
|
||||
class SourceCore;
|
||||
|
||||
class CopyVisitor : public Visitor
|
||||
{
|
||||
Node *current_;
|
||||
CopyVisitor();
|
||||
|
||||
public:
|
||||
|
||||
static Node *deepCopy(Node *node);
|
||||
|
||||
void visit(Scene& n) override;
|
||||
void visit(Node& n) override;
|
||||
void visit(Primitive& n) override;
|
||||
void visit(Group& n) override;
|
||||
void visit(Switch& n) override;
|
||||
};
|
||||
|
||||
#endif // COPYVISITOR_H
|
||||
@@ -404,6 +404,8 @@ Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t)
|
||||
shadows[SQUARE_POINT] = nullptr;
|
||||
icons[IMAGE] = new Mesh("mesh/icon_image.ply");
|
||||
shadows[IMAGE] = shadow;
|
||||
icons[SEQUENCE] = new Mesh("mesh/icon_sequence.ply");
|
||||
shadows[SEQUENCE]= shadow;
|
||||
icons[VIDEO] = new Mesh("mesh/icon_video.ply");
|
||||
shadows[VIDEO] = shadow;
|
||||
icons[SESSION] = new Mesh("mesh/icon_vimix.ply");
|
||||
|
||||
@@ -60,9 +60,9 @@ protected:
|
||||
class Symbol : public Node
|
||||
{
|
||||
public:
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
|
||||
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, VECTORSLASH, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
|
||||
Symbol(Type t = CIRCLE_POINT, glm::vec3 pos = glm::vec3(0.f));
|
||||
Symbol(Type t, glm::vec3 pos = glm::vec3(0.f));
|
||||
~Symbol();
|
||||
|
||||
void draw (glm::mat4 modelview, glm::mat4 projection) override;
|
||||
|
||||
@@ -325,7 +325,7 @@ int Device::index(const std::string &device) const
|
||||
return i;
|
||||
}
|
||||
|
||||
DeviceSource::DeviceSource() : StreamSource()
|
||||
DeviceSource::DeviceSource(uint64_t id) : StreamSource(id)
|
||||
{
|
||||
// create stream
|
||||
stream_ = new Stream;
|
||||
@@ -342,7 +342,7 @@ DeviceSource::~DeviceSource()
|
||||
}
|
||||
|
||||
void DeviceSource::setDevice(const std::string &devicename)
|
||||
{
|
||||
{
|
||||
device_ = devicename;
|
||||
Log::Notify("Creating Source with device '%s'", device_.c_str());
|
||||
|
||||
@@ -384,8 +384,12 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
|
||||
// open gstreamer
|
||||
stream_->open( pipeline.str(), best.width, best.height);
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
else
|
||||
Log::Warning("No such device '%s'", device_.c_str());
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class DeviceSource : public StreamSource
|
||||
{
|
||||
public:
|
||||
DeviceSource();
|
||||
DeviceSource(uint64_t id = 0);
|
||||
~DeviceSource();
|
||||
|
||||
// Source interface
|
||||
@@ -87,8 +87,8 @@ class Device
|
||||
friend class DeviceSource;
|
||||
|
||||
Device();
|
||||
Device(Device const& copy); // Not Implemented
|
||||
Device& operator=(Device const& copy); // Not Implemented
|
||||
Device(Device const& copy) = delete;
|
||||
Device& operator=(Device const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
|
||||
}
|
||||
|
||||
|
||||
std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
std::string DialogToolkit::openMediaFileDialog(const std::string &path)
|
||||
{
|
||||
std::string filename = "";
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
@@ -190,7 +190,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
"*.gif", "*.tif", "*.svg" };
|
||||
#if USE_TINYFILEDIALOG
|
||||
char const * open_file_name;
|
||||
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
|
||||
open_file_name = tinyfd_openFileDialog( "Open Media File", startpath.c_str(), 18, open_pattern, "All supported formats", 0);
|
||||
|
||||
if (open_file_name)
|
||||
filename = std::string(open_file_name);
|
||||
@@ -201,7 +201,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
return filename;
|
||||
}
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Import Media File", NULL,
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Media File", NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT, NULL );
|
||||
@@ -238,7 +238,7 @@ std::string DialogToolkit::ImportFileDialog(const std::string &path)
|
||||
return filename;
|
||||
}
|
||||
|
||||
std::string DialogToolkit::FolderDialog(const std::string &path)
|
||||
std::string DialogToolkit::openFolderDialog(const std::string &path)
|
||||
{
|
||||
std::string foldername = "";
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
@@ -290,6 +290,91 @@ std::string DialogToolkit::FolderDialog(const std::string &path)
|
||||
}
|
||||
|
||||
|
||||
std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &path)
|
||||
{
|
||||
std::list<std::string> files;
|
||||
|
||||
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
|
||||
char const * open_pattern[3] = { "*.tif", "*.jpg", "*.png" };
|
||||
|
||||
#if USE_TINYFILEDIALOG
|
||||
char const * open_file_names;
|
||||
open_file_names = tinyfd_openFileDialog( "Select images", startpath.c_str(), 3, open_pattern, "Images", 1);
|
||||
|
||||
if (open_file_names) {
|
||||
|
||||
const std::string& str (open_file_names);
|
||||
const std::string& delimiters = "|";
|
||||
// Skip delimiters at beginning.
|
||||
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
|
||||
|
||||
// Find first non-delimiter.
|
||||
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
|
||||
|
||||
while (std::string::npos != pos || std::string::npos != lastPos) {
|
||||
// Found a token, add it to the vector.
|
||||
files.push_back(str.substr(lastPos, pos - lastPos));
|
||||
|
||||
// Skip delimiters.
|
||||
lastPos = str.find_first_not_of(delimiters, pos);
|
||||
|
||||
// Find next non-delimiter.
|
||||
pos = str.find_first_of(delimiters, lastPos);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
if (!gtk_init()) {
|
||||
ErrorDialog("Could not initialize GTK+ for dialog");
|
||||
return files;
|
||||
}
|
||||
|
||||
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select images", NULL,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
||||
"_Open", GTK_RESPONSE_ACCEPT, NULL );
|
||||
|
||||
// set file filters
|
||||
add_filter_file_dialog(dialog, 3, open_pattern, "All supported formats");
|
||||
add_filter_any_file_dialog(dialog);
|
||||
|
||||
// multiple files
|
||||
gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), true );
|
||||
|
||||
// Set the default path
|
||||
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
|
||||
|
||||
// ensure front and centered
|
||||
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
|
||||
if (window_x > 0 && window_y > 0)
|
||||
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
|
||||
|
||||
// display and get filename
|
||||
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
|
||||
|
||||
GSList *open_file_names = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) );
|
||||
|
||||
while (open_file_names) {
|
||||
files.push_back( (char *) open_file_names->data );
|
||||
open_file_names = open_file_names->next;
|
||||
// g_free( open_file_names->data );
|
||||
}
|
||||
|
||||
g_slist_free( open_file_names );
|
||||
}
|
||||
|
||||
// remember position
|
||||
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
|
||||
|
||||
// done
|
||||
gtk_widget_destroy(dialog);
|
||||
wait_for_event();
|
||||
#endif
|
||||
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void DialogToolkit::ErrorDialog(const char* message)
|
||||
{
|
||||
#if USE_TINYFILEDIALOG
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define DIALOGTOOLKIT_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
|
||||
namespace DialogToolkit
|
||||
@@ -11,9 +12,11 @@ std::string saveSessionFileDialog(const std::string &path);
|
||||
|
||||
std::string openSessionFileDialog(const std::string &path);
|
||||
|
||||
std::string ImportFileDialog(const std::string &path);
|
||||
std::string openMediaFileDialog(const std::string &path);
|
||||
|
||||
std::string FolderDialog(const std::string &path);
|
||||
std::string openFolderDialog(const std::string &path);
|
||||
|
||||
std::list<std::string> selectImagesFileDialog(const std::string &path);
|
||||
|
||||
void ErrorDialog(const char* message);
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ inline PathStruct ParsePathFileName(const std::string& vPathFileName)
|
||||
return res;
|
||||
}
|
||||
|
||||
inline void AppendToBuffer(char* vBuffer, size_t vBufferLen, std::string vStr)
|
||||
inline void AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string &vStr)
|
||||
{
|
||||
std::string st = vStr;
|
||||
size_t len = vBufferLen - 1u;
|
||||
@@ -253,8 +253,6 @@ static bool stringComparator(const FileInfoStruct& a, const FileInfoStruct& b)
|
||||
void FileDialog::ScanDir(const std::string& vPath)
|
||||
{
|
||||
struct dirent **files = NULL;
|
||||
int i = 0;
|
||||
int n = 0;
|
||||
std::string path = vPath;
|
||||
|
||||
#if defined(LINUX) or defined(APPLE)
|
||||
@@ -280,12 +278,12 @@ void FileDialog::ScanDir(const std::string& vPath)
|
||||
path += PATH_SEP;
|
||||
}
|
||||
#endif
|
||||
n = scandir(path.c_str(), &files, NULL, alphaSort);
|
||||
int n = scandir(path.c_str(), &files, NULL, alphaSort);
|
||||
if (n > 0)
|
||||
{
|
||||
m_FileList.clear();
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
struct dirent *ent = files[i];
|
||||
|
||||
@@ -328,7 +326,7 @@ void FileDialog::ScanDir(const std::string& vPath)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
free(files[i]);
|
||||
}
|
||||
@@ -434,7 +432,7 @@ void FileDialog::ComposeNewPath(std::vector<std::string>::iterator vIter)
|
||||
break;
|
||||
}
|
||||
|
||||
vIter--;
|
||||
--vIter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,9 +701,9 @@ bool FileDialog::Render(const std::string& vKey, ImVec2 geometry)
|
||||
SetPath("."); // Go Home
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
bool drivesClick = false;
|
||||
|
||||
#ifdef WIN32
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Drives"))
|
||||
@@ -865,10 +863,12 @@ bool FileDialog::Render(const std::string& vKey, ImVec2 geometry)
|
||||
SetPath(m_CurrentPath);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (drivesClick == true)
|
||||
{
|
||||
GetDrives();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopFont();
|
||||
@@ -1013,12 +1013,12 @@ std::string FileDialog::GetUserString()
|
||||
return dlg_userString;
|
||||
}
|
||||
|
||||
void FileDialog::SetFilterColor(std::string vFilter, ImVec4 vColor)
|
||||
void FileDialog::SetFilterColor(const std::string &vFilter, ImVec4 vColor)
|
||||
{
|
||||
m_FilterColor[vFilter] = vColor;
|
||||
}
|
||||
|
||||
bool FileDialog::GetFilterColor(std::string vFilter, ImVec4 *vColor)
|
||||
bool FileDialog::GetFilterColor(const std::string &vFilter, ImVec4 *vColor)
|
||||
{
|
||||
if (vColor)
|
||||
{
|
||||
@@ -1054,7 +1054,7 @@ inline void InfosPane(std::string vFilter, bool *vCantContinue)
|
||||
*vCantContinue = canValidateDialog;
|
||||
}
|
||||
|
||||
inline void TextInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
|
||||
inline void TextInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
|
||||
{
|
||||
ImGui::TextColored(ImVec4(0, 1, 1, 1), "Text");
|
||||
|
||||
@@ -1091,7 +1091,7 @@ inline void TextInfosPane(std::string vFilter, bool *vCantContinue) // if vCantC
|
||||
*vCantContinue = text.size() > 0;
|
||||
}
|
||||
|
||||
inline void ImageInfosPane(std::string vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
|
||||
inline void ImageInfosPane(const std::string &vFilter, bool *vCantContinue) // if vCantContinue is false, the user cant validate the dialog
|
||||
{
|
||||
// opengl texture
|
||||
static GLuint tex = 0;
|
||||
|
||||
@@ -71,8 +71,8 @@ public:
|
||||
protected:
|
||||
|
||||
FileDialog(); // Prevent construction
|
||||
FileDialog(const FileDialog&) {} // Prevent construction by copying
|
||||
FileDialog& operator =(const FileDialog&) { return *this; } // Prevent assignment
|
||||
FileDialog(const FileDialog&) = delete; // Prevent construction by copying
|
||||
FileDialog& operator =(const FileDialog&) = delete; // Prevent assignment
|
||||
~FileDialog(); // Prevent unwanted destruction
|
||||
|
||||
public:
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
std::string GetCurrentFilter();
|
||||
std::string GetUserString();
|
||||
|
||||
void SetFilterColor(std::string vFilter, ImVec4 vColor);
|
||||
bool GetFilterColor(std::string vFilter, ImVec4 *vColor);
|
||||
void SetFilterColor(const std::string &vFilter, ImVec4 vColor);
|
||||
bool GetFilterColor(const std::string &vFilter, ImVec4 *vColor);
|
||||
void ClearFilterColor();
|
||||
|
||||
private:
|
||||
|
||||
@@ -227,13 +227,17 @@ void FrameBuffer::readPixels(uint8_t *target_data)
|
||||
|
||||
bool FrameBuffer::blit(FrameBuffer *destination)
|
||||
{
|
||||
if (!framebufferid_ || !destination)
|
||||
if (!framebufferid_ || !destination || (use_alpha_ != destination->use_alpha_) )
|
||||
return false;
|
||||
|
||||
if (!destination->framebufferid_)
|
||||
destination->init();
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
|
||||
if (use_multi_sampling_)
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
|
||||
else
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebufferid_);
|
||||
// blit to the frame buffer object
|
||||
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
|
||||
@@ -318,7 +322,7 @@ FrameBufferImage::~FrameBufferImage() {
|
||||
delete rgb;
|
||||
}
|
||||
|
||||
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg()
|
||||
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg() const
|
||||
{
|
||||
jpegBuffer jpgimg;
|
||||
|
||||
@@ -377,7 +381,9 @@ bool FrameBuffer::fill(FrameBufferImage *image)
|
||||
// fill texture with image
|
||||
glBindTexture(GL_TEXTURE_2D, textureid_);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
|
||||
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
class FrameBufferImage
|
||||
{
|
||||
public:
|
||||
uint8_t *rgb = nullptr;
|
||||
uint8_t *rgb;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
@@ -20,10 +20,13 @@ public:
|
||||
unsigned char *buffer = nullptr;
|
||||
uint len = 0;
|
||||
};
|
||||
jpegBuffer getJpeg();
|
||||
jpegBuffer getJpeg() const;
|
||||
|
||||
FrameBufferImage(int w, int h);
|
||||
FrameBufferImage(jpegBuffer jpgimg);
|
||||
// non assignable class
|
||||
FrameBufferImage(FrameBufferImage const&) = delete;
|
||||
FrameBufferImage& operator=(FrameBufferImage const&) = delete;
|
||||
~FrameBufferImage();
|
||||
};
|
||||
|
||||
@@ -45,6 +48,7 @@ public:
|
||||
|
||||
FrameBuffer(glm::vec3 resolution, bool useAlpha = false, bool multiSampling = false);
|
||||
FrameBuffer(uint width, uint height, bool useAlpha = false, bool multiSampling = false);
|
||||
FrameBuffer(FrameBuffer const&) = delete;
|
||||
~FrameBuffer();
|
||||
|
||||
// Bind & push attribs to prepare draw
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "FrameBuffer.h"
|
||||
|
||||
#include "FrameGrabber.h"
|
||||
@@ -30,8 +31,8 @@ FrameGrabbing::~FrameGrabbing()
|
||||
// cleanup
|
||||
if (caps_)
|
||||
gst_caps_unref (caps_);
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
// if (pbo_[0] > 0) // automatically deleted at shutdown
|
||||
// glDeleteBuffers(2, pbo_);
|
||||
}
|
||||
|
||||
void FrameGrabbing::add(FrameGrabber *rec)
|
||||
@@ -145,6 +146,7 @@ void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer, float dt)
|
||||
#else
|
||||
glBindTexture(GL_TEXTURE_2D, frame_buffer->texture());
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
#endif
|
||||
|
||||
// update case ; alternating indices
|
||||
@@ -210,7 +212,7 @@ FrameGrabber::FrameGrabber(): finished_(false), active_(false), accept_buffer_(f
|
||||
pipeline_(nullptr), src_(nullptr), caps_(nullptr), timestamp_(0)
|
||||
{
|
||||
// unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
// configure fix parameter
|
||||
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // 30 FPS
|
||||
timeframe_ = 2 * frame_duration_;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <gst/gst.h>
|
||||
#include <gst/app/gstappsrc.h>
|
||||
|
||||
#include "GlmToolkit.h"
|
||||
|
||||
// use glReadPixel or glGetTextImage
|
||||
// read pixels & pbo should be the fastest
|
||||
@@ -84,8 +83,8 @@ class FrameGrabbing
|
||||
|
||||
// Private Constructor
|
||||
FrameGrabbing();
|
||||
FrameGrabbing(FrameGrabbing const& copy); // Not Implemented
|
||||
FrameGrabbing& operator=(FrameGrabbing const& copy); // Not Implemented
|
||||
FrameGrabbing(FrameGrabbing const& copy) = delete;
|
||||
FrameGrabbing& operator=(FrameGrabbing const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -82,73 +82,3 @@ void GarbageVisitor::visit(Primitive &n)
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Surface &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(ImageSurface &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(FrameBufferSurface &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(MediaSurface &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(MediaPlayer &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Shader &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(ImageShader &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(ImageProcessingShader &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(LineStrip &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(LineSquare &)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Mesh &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit(Frame &n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void GarbageVisitor::visit (Source& s)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GarbageVisitor::visit (MediaSource& s)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -24,23 +24,6 @@ public:
|
||||
void visit(Switch& n) override;
|
||||
void visit(Primitive& n) override;
|
||||
|
||||
void visit(Surface& n) override;
|
||||
void visit(ImageSurface& n) override;
|
||||
void visit(MediaSurface& n) override;
|
||||
void visit(FrameBufferSurface& n) override;
|
||||
void visit(LineStrip& n) override;
|
||||
void visit(LineSquare&) override;
|
||||
void visit(Mesh& n) override;
|
||||
void visit(Frame& n) override;
|
||||
|
||||
void visit(MediaPlayer& n) override;
|
||||
void visit(Shader& n) override;
|
||||
void visit(ImageShader& n) override;
|
||||
void visit(ImageProcessingShader& n) override;
|
||||
|
||||
void visit (Source& s) override;
|
||||
void visit (MediaSource& s) override;
|
||||
|
||||
};
|
||||
|
||||
#endif // GARBAGEVISITOR_H
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "Mixer.h"
|
||||
#include "defines.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "PickingVisitor.h"
|
||||
#include "DrawVisitor.h"
|
||||
@@ -196,7 +197,7 @@ void GeometryView::draw()
|
||||
std::vector<Node *> surfaces;
|
||||
std::vector<Node *> overlays;
|
||||
for (auto source_iter = Mixer::manager().session()->begin();
|
||||
source_iter != Mixer::manager().session()->end(); source_iter++) {
|
||||
source_iter != Mixer::manager().session()->end(); ++source_iter) {
|
||||
// if it is in the current workspace
|
||||
if ((*source_iter)->workspace() == Settings::application.current_workspace) {
|
||||
// will draw its surface
|
||||
@@ -257,7 +258,7 @@ void GeometryView::draw()
|
||||
static std::vector< std::pair<int, int> > icons_ws = { {10,16}, {11,16}, {12,16} };
|
||||
static std::vector< std::string > labels_ws = { "Background", "Workspace", "Foreground" };
|
||||
if ( ImGuiToolkit::ComboIcon (icons_ws, labels_ws, &Settings::application.current_workspace) ){
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(8); // 14 colors
|
||||
@@ -319,7 +320,7 @@ void GeometryView::draw()
|
||||
|
||||
// batch manipulation of sources in Geometry view
|
||||
if (ImGui::Selectable( ICON_FA_EXPAND " Fit all" )){
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
|
||||
(*sit)->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ (*sit)->frame()->aspectRatio(), 1.f, 1.f);
|
||||
(*sit)->group(mode_)->rotation_.z = 0;
|
||||
(*sit)->group(mode_)->translation_ = glm::vec3(0.f);
|
||||
@@ -329,7 +330,7 @@ void GeometryView::draw()
|
||||
}
|
||||
if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset all" )){
|
||||
// apply to every sources in selection
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
|
||||
(*sit)->group(mode_)->scale_ = glm::vec3(1.f);
|
||||
(*sit)->group(mode_)->rotation_.z = 0;
|
||||
(*sit)->group(mode_)->crop_ = glm::vec3(1.f);
|
||||
@@ -351,7 +352,7 @@ void GeometryView::draw()
|
||||
}
|
||||
if (ImGui::Selectable( ICON_FA_COMPASS " Align" )){
|
||||
// apply to every sources in selection
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
|
||||
(*sit)->group(mode_)->rotation_.z = overlay_selection_->rotation_.z;
|
||||
(*sit)->touch();
|
||||
}
|
||||
@@ -413,7 +414,7 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
|
||||
|
||||
// find if the current source was picked
|
||||
auto itp = pv.rbegin();
|
||||
for (; itp != pv.rend(); itp++){
|
||||
for (; itp != pv.rend(); ++itp){
|
||||
// test if source contains this node
|
||||
Source::hasNode is_in_source((*itp).first );
|
||||
if ( is_in_source( current ) ){
|
||||
@@ -431,41 +432,45 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
|
||||
openContextMenu(MENU_SOURCE);
|
||||
}
|
||||
// pick on the lock icon; unlock source
|
||||
else if ( pick.first == current->lock_ ) {
|
||||
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->lock_ ) {
|
||||
lock(current, false);
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
// pick on the open lock icon; lock source and cancel pick
|
||||
else if ( pick.first == current->unlock_ ) {
|
||||
else if ( UserInterface::manager().ctrlModifier() && pick.first == current->unlock_ ) {
|
||||
lock(current, true);
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
// pick a locked source without CTRL key; cancel pick
|
||||
else if ( current->locked() && !UserInterface::manager().ctrlModifier() ) {
|
||||
// pick a locked source ; cancel pick
|
||||
else if ( current->locked() ) {
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
}
|
||||
// the clicked source changed (not the current source)
|
||||
if (current == nullptr) {
|
||||
|
||||
// default to failed pick
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
if (UserInterface::manager().ctrlModifier()) {
|
||||
|
||||
// loop over all nodes picked to detect clic on locks
|
||||
for (auto itp = pv.rbegin(); itp != pv.rend(); itp++){
|
||||
// get if a source was picked
|
||||
Source *s = Mixer::manager().findSource((*itp).first);
|
||||
// lock icon of a source (not current) is picked : unlock
|
||||
if ( s!=nullptr && (*itp).first == s->lock_) {
|
||||
lock(s, false);
|
||||
pick = { s->locker_, (*itp).second };
|
||||
break;
|
||||
// default to failed pick
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
|
||||
// loop over all nodes picked to detect clic on locks
|
||||
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
|
||||
// get if a source was picked
|
||||
Source *s = Mixer::manager().findSource((*itp).first);
|
||||
// lock icon of a source (not current) is picked : unlock
|
||||
if ( s!=nullptr && (*itp).first == s->lock_) {
|
||||
lock(s, false);
|
||||
pick = { s->locker_, (*itp).second };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// no lock icon picked, find what else was picked
|
||||
if ( pick.first == nullptr) {
|
||||
|
||||
// loop over all nodes picked
|
||||
for (auto itp = pv.rbegin(); itp != pv.rend(); itp++){
|
||||
for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){
|
||||
// get if a source was picked
|
||||
Source *s = Mixer::manager().findSource((*itp).first);
|
||||
// accept picked sources in current workspaces
|
||||
@@ -511,13 +516,13 @@ std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
|
||||
|
||||
bool GeometryView::canSelect(Source *s) {
|
||||
|
||||
return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace);
|
||||
return ( View::canSelect(s) && s->ready() && s->active() && s->workspace() == Settings::application.current_workspace);
|
||||
}
|
||||
|
||||
|
||||
void GeometryView::applySelectionTransform(glm::mat4 M)
|
||||
{
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); sit++){
|
||||
for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){
|
||||
// recompute all from matrix transform
|
||||
glm::mat4 transform = M * (*sit)->stored_status_->transform_;
|
||||
glm::vec3 tra, rot, sca;
|
||||
@@ -1009,7 +1014,7 @@ void GeometryView::terminate()
|
||||
// restore of all handles overlays
|
||||
glm::vec2 c(0.f, 0.f);
|
||||
for (auto sit = Mixer::manager().session()->begin();
|
||||
sit != Mixer::manager().session()->end(); sit++){
|
||||
sit != Mixer::manager().session()->end(); ++sit){
|
||||
|
||||
(*sit)->handles_[mode_][Handles::RESIZE]->overlayActiveCorner(c);
|
||||
(*sit)->handles_[mode_][Handles::RESIZE_H]->overlayActiveCorner(c);
|
||||
@@ -1037,7 +1042,7 @@ void GeometryView::arrow (glm::vec2 movement)
|
||||
|
||||
bool first = true;
|
||||
glm::vec3 delta_translation(0.f);
|
||||
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
|
||||
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
|
||||
|
||||
// individual move with SHIFT
|
||||
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )
|
||||
|
||||
@@ -7,6 +7,9 @@ class GeometryView : public View
|
||||
{
|
||||
public:
|
||||
GeometryView();
|
||||
// non assignable class
|
||||
GeometryView(GeometryView const&) = delete;
|
||||
GeometryView& operator=(GeometryView const&) = delete;
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Freely inspired from https://github.com/alter-rokuz/glm-aabb.git
|
||||
|
||||
#include "GlmToolkit.h"
|
||||
|
||||
@@ -10,14 +9,6 @@
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
uint64_t GlmToolkit::uniqueId()
|
||||
{
|
||||
auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();
|
||||
// 64-bit int 18446744073709551615UL
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000000000000UL;
|
||||
}
|
||||
|
||||
glm::mat4 GlmToolkit::transform(glm::vec3 translation, glm::vec3 rotation, glm::vec3 scale)
|
||||
{
|
||||
glm::mat4 View = glm::translate(glm::identity<glm::mat4>(), translation);
|
||||
@@ -59,6 +50,8 @@ void GlmToolkit::inverse_transform(glm::mat4 M, glm::vec3 &translation, glm::vec
|
||||
// return angle;
|
||||
//}
|
||||
|
||||
// Freely inspired from https://github.com/alter-rokuz/glm-aabb.git
|
||||
|
||||
GlmToolkit::AxisAlignedBoundingBox::AxisAlignedBoundingBox() :
|
||||
mMin(glm::vec3(1.f)), mMax(glm::vec3(-1.f))
|
||||
{
|
||||
@@ -79,7 +72,7 @@ void GlmToolkit::AxisAlignedBoundingBox::extend(const glm::vec3& point)
|
||||
|
||||
void GlmToolkit::AxisAlignedBoundingBox::extend(std::vector<glm::vec3> points)
|
||||
{
|
||||
for (auto p = points.begin(); p != points.end(); p++)
|
||||
for (auto p = points.begin(); p != points.end(); ++p)
|
||||
extend(*p);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
#ifndef GLMTOOLKIT_H
|
||||
#define GLMTOOLKIT_H
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace GlmToolkit
|
||||
{
|
||||
|
||||
// get integer with unique id
|
||||
uint64_t uniqueId();
|
||||
|
||||
// get Matrix for these transformation components
|
||||
glm::mat4 transform(glm::vec3 translation, glm::vec3 rotation, glm::vec3 scale);
|
||||
void inverse_transform(glm::mat4 M, glm::vec3 &translation, glm::vec3 &rotation, glm::vec3 &scale);
|
||||
|
||||
182
ImGuiToolkit.cpp
182
ImGuiToolkit.cpp
@@ -1,6 +1,8 @@
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
@@ -20,7 +22,6 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "Resource.h"
|
||||
#include "FileDialog.h"
|
||||
#include "ImGuiToolkit.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
@@ -30,14 +31,12 @@ unsigned int textureicons = 0;
|
||||
std::map <ImGuiToolkit::font_style, ImFont*>fontmap;
|
||||
|
||||
|
||||
void ImGuiToolkit::ButtonOpenUrl( const char* url, const ImVec2& size_arg )
|
||||
void ImGuiToolkit::ButtonOpenUrl( const char* label, const char* url, const ImVec2& size_arg )
|
||||
{
|
||||
char label[512];
|
||||
char _label[512];
|
||||
sprintf( _label, "%s %s", ICON_FA_EXTERNAL_LINK_ALT, label );
|
||||
|
||||
std::string str = SystemToolkit::transliterate( url );
|
||||
sprintf( label, "%s %s", ICON_FA_EXTERNAL_LINK_ALT, str.c_str() );
|
||||
|
||||
if ( ImGui::Button(label, size_arg) )
|
||||
if ( ImGui::Button(_label, size_arg) )
|
||||
SystemToolkit::open(url);
|
||||
}
|
||||
|
||||
@@ -58,8 +57,10 @@ bool ImGuiToolkit::ButtonToggle( const char* label, bool* toggle )
|
||||
}
|
||||
|
||||
|
||||
void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* help)
|
||||
bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* help)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// utility style
|
||||
ImVec4* colors = ImGui::GetStyle().Colors;
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
@@ -76,8 +77,10 @@ void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* hel
|
||||
|
||||
// toggle action : operate on the whole area
|
||||
ImGui::InvisibleButton(label, ImVec2(frame_width, frame_height));
|
||||
if (ImGui::IsItemClicked())
|
||||
if (ImGui::IsItemClicked()) {
|
||||
*toggle = !*toggle;
|
||||
ret = true;
|
||||
}
|
||||
float t = *toggle ? 1.0f : 0.0f;
|
||||
|
||||
// animation
|
||||
@@ -113,6 +116,7 @@ void ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* hel
|
||||
draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), col_bg, height * 0.5f);
|
||||
draw_list->AddCircleFilled(ImVec2(p.x + radius + t * (width - radius * 2.0f), p.y + radius), radius - 1.5f, IM_COL32(255, 255, 255, 250));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +205,36 @@ bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool ImGuiToolkit::IconButton(const char* icon, const char *tooltip)
|
||||
{
|
||||
bool ret = false;
|
||||
ImGui::PushID( icon );
|
||||
|
||||
float frame_height = ImGui::GetFrameHeight();
|
||||
float frame_width = frame_height;
|
||||
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// toggle action : operate on the whole area
|
||||
ImGui::InvisibleButton("##iconbutton", ImVec2(frame_width, frame_height));
|
||||
if (ImGui::IsItemClicked())
|
||||
ret = true;
|
||||
|
||||
ImGui::SetCursorScreenPos(draw_pos);
|
||||
ImGui::Text(icon);
|
||||
|
||||
if (tooltip != nullptr && ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s", tooltip);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool ImGuiToolkit::IconToggle(int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[])
|
||||
{
|
||||
bool ret = false;
|
||||
@@ -311,33 +345,34 @@ void ImGuiToolkit::ShowIconsWindow(bool* p_open)
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
ImGui::Begin("Icons", p_open);
|
||||
if ( ImGui::Begin("Icons", p_open) ) {
|
||||
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
float my_tex_w = 640.0;
|
||||
float my_tex_h = 640.0;
|
||||
float zoom = 4.0f;
|
||||
float region_sz = 32.0f; // 64 x 64 icons
|
||||
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
|
||||
if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
|
||||
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
|
||||
if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
|
||||
ImGui::BeginTooltip();
|
||||
int i = (int) ( (region_x + region_sz * 0.5f) / region_sz);
|
||||
int j = (int) ( (region_y + region_sz * 0.5f)/ region_sz);
|
||||
ImGuiToolkit::Icon(i, j);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" Icon (%d, %d)", i, j);
|
||||
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
|
||||
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
|
||||
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
ImGui::EndTooltip();
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(640, 640));
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
float my_tex_w = 640.0;
|
||||
float my_tex_h = 640.0;
|
||||
float zoom = 4.0f;
|
||||
float region_sz = 32.0f; // 64 x 64 icons
|
||||
float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
|
||||
if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz;
|
||||
float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
|
||||
if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz;
|
||||
ImGui::BeginTooltip();
|
||||
int i = (int) ( (region_x + region_sz * 0.5f) / region_sz);
|
||||
int j = (int) ( (region_y + region_sz * 0.5f)/ region_sz);
|
||||
ImGuiToolkit::Icon(i, j);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(" Icon (%d, %d)", i, j);
|
||||
ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
|
||||
ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
|
||||
ImGui::Image((void*)(intptr_t)textureicons, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), ImVec4(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ImGuiToolkit::ToolTip(const char* desc, const char* shortcut)
|
||||
@@ -1113,4 +1148,83 @@ void ImGuiToolkit::SetAccentColor(accent_color color)
|
||||
|
||||
}
|
||||
|
||||
void word_wrap(std::string *str, unsigned per_line)
|
||||
{
|
||||
unsigned line_begin = 0;
|
||||
while (line_begin < str->size())
|
||||
{
|
||||
const unsigned ideal_end = line_begin + per_line ;
|
||||
unsigned line_end = ideal_end < str->size() ? ideal_end : str->size()-1;
|
||||
|
||||
if (line_end == str->size() - 1)
|
||||
++line_end;
|
||||
else if (std::isspace(str->at(line_end)))
|
||||
{
|
||||
str->replace(line_end, 1, 1, '\n' );
|
||||
++line_end;
|
||||
}
|
||||
else // backtrack
|
||||
{
|
||||
unsigned end = line_end;
|
||||
while ( end > line_begin && !std::isspace(str->at(end)))
|
||||
--end;
|
||||
|
||||
if (end != line_begin)
|
||||
{
|
||||
line_end = end;
|
||||
str->replace(line_end++, 1, 1, '\n' );
|
||||
}
|
||||
else {
|
||||
str->insert(line_end++, 1, '\n' );
|
||||
}
|
||||
}
|
||||
|
||||
line_begin = line_end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct InputTextCallback_UserData
|
||||
{
|
||||
std::string* Str;
|
||||
int WordWrap;
|
||||
};
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
InputTextCallback_UserData* user_data = static_cast<InputTextCallback_UserData*>(data->UserData);
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||
{
|
||||
// if (user_data->WordWrap > 1)
|
||||
// word_wrap(user_data->Str, user_data->WordWrap );
|
||||
|
||||
// Resize string callback
|
||||
std::string* str = user_data->Str;
|
||||
IM_ASSERT(data->Buf == str->c_str());
|
||||
str->resize(data->BufTextLen);
|
||||
data->Buf = (char*)str->c_str();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ImGuiToolkit::InputText(const char* label, std::string* str)
|
||||
{
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CharsNoBlank;
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
|
||||
return ImGui::InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
bool ImGuiToolkit::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, int linesize)
|
||||
{
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
|
||||
InputTextCallback_UserData cb_user_data;
|
||||
cb_user_data.Str = str;
|
||||
cb_user_data.WordWrap = linesize;
|
||||
|
||||
return ImGui::InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace ImGuiToolkit
|
||||
// Icons from resource icon.dds
|
||||
void Icon (int i, int j, bool enabled = true);
|
||||
bool IconButton (int i, int j, const char *tooltips = nullptr);
|
||||
bool IconButton (const char* icon = ICON_FA_EXCLAMATION_CIRCLE, const char *tooltips = nullptr);
|
||||
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
|
||||
void ShowIconsWindow(bool* p_open);
|
||||
|
||||
@@ -24,8 +25,8 @@ namespace ImGuiToolkit
|
||||
|
||||
// utility buttons
|
||||
bool ButtonToggle (const char* label, bool* toggle);
|
||||
void ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
|
||||
void ButtonOpenUrl (const char* url, const ImVec2& size_arg = ImVec2(0,0));
|
||||
bool ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
|
||||
void ButtonOpenUrl (const char* label, const char* url, const ImVec2& size_arg = ImVec2(0,0));
|
||||
|
||||
void ToolTip (const char* desc, const char* shortcut = nullptr);
|
||||
void HelpMarker (const char* desc, const char* icon = ICON_FA_QUESTION_CIRCLE, const char* shortcut = nullptr);
|
||||
@@ -62,6 +63,8 @@ namespace ImGuiToolkit
|
||||
void SetAccentColor (accent_color color);
|
||||
struct ImVec4 HighlightColor (bool active = true);
|
||||
|
||||
bool InputText(const char* label, std::string* str);
|
||||
bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), int linesize = 0);
|
||||
}
|
||||
|
||||
#endif // __IMGUI_TOOLKIT_H_
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
#include "Settings.h"
|
||||
@@ -32,6 +34,7 @@
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
#include "SystemToolkit.h"
|
||||
|
||||
@@ -395,7 +398,6 @@ void ImGuiVisitor::visit(ImageProcessingShader &n)
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (Source& s)
|
||||
{
|
||||
ImGui::PushID(std::to_string(s.id()).c_str());
|
||||
@@ -539,10 +541,6 @@ void ImGuiVisitor::visit (Source& s)
|
||||
s.processingShader()->accept(*this);
|
||||
}
|
||||
|
||||
// geometry direct control for DEBUG
|
||||
// s.groupNode(View::GEOMETRY)->accept(*this);
|
||||
// s.groupNode((View::Mode) Settings::application.current_view)->accept(*this);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
@@ -557,7 +555,12 @@ void ImGuiVisitor::visit (MediaSource& s)
|
||||
|
||||
if ( ImGui::Button(IMGUI_TITLE_MEDIAPLAYER, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
|
||||
UserInterface::manager().showMediaPlayer( s.mediaplayer());
|
||||
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
|
||||
std::string path = SystemToolkit::path_filename(s.path());
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
}
|
||||
@@ -588,7 +591,10 @@ void ImGuiVisitor::visit (SessionFileSource& s)
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("File");
|
||||
|
||||
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
std::string path = SystemToolkit::path_filename(s.path());
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
@@ -703,6 +709,60 @@ void ImGuiVisitor::visit (NetworkSource& s)
|
||||
s.setConnection(s.connection());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
|
||||
ImGui::SameLine(0, 10);
|
||||
ImGui::Text("Images sequence");
|
||||
static int64_t id = s.id();
|
||||
|
||||
// information text
|
||||
std::ostringstream msg;
|
||||
msg << "Sequence of " << s.sequence().max - s.sequence().min + 1 << " ";
|
||||
msg << s.sequence().codec << " images";
|
||||
ImGui::Text("%s", msg.str().c_str());
|
||||
|
||||
// change range
|
||||
static int _begin = -1;
|
||||
if (_begin < 0 || id != s.id())
|
||||
_begin = s.begin();
|
||||
static int _end = -1;
|
||||
if (_end < 0 || id != s.id())
|
||||
_end = s.end();
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::DragIntRange2("Range", &_begin, &_end, 1, s.sequence().min, s.sequence().max);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
s.setRange( _begin, _end );
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Range " << _begin << "-" << _end;
|
||||
Action::manager().store(oss.str());
|
||||
_begin = _end = -1;
|
||||
}
|
||||
|
||||
// change framerate
|
||||
static int _fps = -1;
|
||||
if (_fps < 0 || id != s.id())
|
||||
_fps = s.framerate();
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
ImGui::SliderInt("Framerate", &_fps, 1, 30, "%d fps");
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()){
|
||||
s.setFramerate(_fps);
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << ": Framerate " << _fps << " fps";
|
||||
Action::manager().store(oss.str());
|
||||
_fps = -1;
|
||||
}
|
||||
|
||||
// offer to open file browser at location
|
||||
std::string path = SystemToolkit::path_filename(s.sequence().location);
|
||||
std::string label = BaseToolkit::trunc_string(path, 25);
|
||||
label = BaseToolkit::transliterate(label);
|
||||
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Folder");
|
||||
|
||||
id = s.id();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
};
|
||||
|
||||
#endif // IMGUIVISITOR_H
|
||||
|
||||
@@ -58,7 +58,7 @@ void ImageProcessingShader::reset()
|
||||
|
||||
}
|
||||
|
||||
void ImageProcessingShader::copy(const ImageProcessingShader &S)
|
||||
void ImageProcessingShader::copy(ImageProcessingShader const& S)
|
||||
{
|
||||
brightness = S.brightness;
|
||||
contrast = S.contrast;
|
||||
@@ -75,11 +75,6 @@ void ImageProcessingShader::copy(const ImageProcessingShader &S)
|
||||
chromadelta = S.chromadelta;
|
||||
}
|
||||
|
||||
void ImageProcessingShader::operator = (const ImageProcessingShader &S )
|
||||
{
|
||||
copy(S);
|
||||
}
|
||||
|
||||
|
||||
void ImageProcessingShader::accept(Visitor& v)
|
||||
{
|
||||
|
||||
@@ -16,8 +16,7 @@ public:
|
||||
void reset() override;
|
||||
void accept(Visitor& v) override;
|
||||
|
||||
void operator = (const ImageProcessingShader &S);
|
||||
void copy(const ImageProcessingShader &S);
|
||||
void copy(ImageProcessingShader const& S);
|
||||
|
||||
// color effects
|
||||
float brightness; // [-1 1]
|
||||
|
||||
@@ -58,10 +58,8 @@ void ImageShader::reset()
|
||||
stipple = 0.f;
|
||||
}
|
||||
|
||||
void ImageShader::operator = (const ImageShader &S)
|
||||
void ImageShader::copy(ImageShader const& S)
|
||||
{
|
||||
Shader::operator =(S);
|
||||
|
||||
mask_texture = S.mask_texture;
|
||||
stipple = S.stipple;
|
||||
}
|
||||
@@ -133,10 +131,8 @@ void MaskShader::reset()
|
||||
effect = 0;
|
||||
}
|
||||
|
||||
void MaskShader::operator = (const MaskShader &S)
|
||||
void MaskShader::copy(MaskShader const& S)
|
||||
{
|
||||
Shader::operator =(S);
|
||||
|
||||
mode = S.mode;
|
||||
shape = S.shape;
|
||||
blur = S.blur;
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
void use() override;
|
||||
void reset() override;
|
||||
void accept(Visitor& v) override;
|
||||
void operator = (const ImageShader &S);
|
||||
void copy(ImageShader const& S);
|
||||
|
||||
uint mask_texture;
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
void use() override;
|
||||
void reset() override;
|
||||
void accept(Visitor& v) override;
|
||||
void operator = (const MaskShader &S);
|
||||
void copy(MaskShader const& S);
|
||||
|
||||
enum Modes {
|
||||
NONE = 0,
|
||||
|
||||
166
Interpolator.cpp
Normal file
166
Interpolator.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "Source.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "UpdateCallback.h"
|
||||
|
||||
#include "Interpolator.h"
|
||||
|
||||
|
||||
SourceInterpolator::SourceInterpolator(Source *subject, const SourceCore &target) :
|
||||
subject_(subject), from_(static_cast<SourceCore>(*subject)), to_(target), current_cursor_(0.f)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SourceInterpolator::interpolateGroup(View::Mode m)
|
||||
{
|
||||
current_state_.group(m)->translation_ =
|
||||
(1.f - current_cursor_) * from_.group(m)->translation_
|
||||
+ current_cursor_ * to_.group(m)->translation_;
|
||||
current_state_.group(m)->scale_ =
|
||||
(1.f - current_cursor_) * from_.group(m)->scale_
|
||||
+ current_cursor_ * to_.group(m)->scale_;
|
||||
current_state_.group(m)->rotation_ =
|
||||
(1.f - current_cursor_) * from_.group(m)->rotation_
|
||||
+ current_cursor_ * to_.group(m)->rotation_;
|
||||
current_state_.group(m)->crop_ =
|
||||
(1.f - current_cursor_) * from_.group(m)->crop_
|
||||
+ current_cursor_ * to_.group(m)->crop_;
|
||||
|
||||
CopyCallback *anim = new CopyCallback( current_state_.group(m) );
|
||||
subject_->group(m)->update_callbacks_.clear();
|
||||
subject_->group(m)->update_callbacks_.push_back(anim);
|
||||
}
|
||||
|
||||
void SourceInterpolator::interpolateImageProcessing()
|
||||
{
|
||||
current_state_.processingShader()->brightness =
|
||||
(1.f - current_cursor_) * from_.processingShader()->brightness
|
||||
+ current_cursor_ * to_.processingShader()->brightness;
|
||||
|
||||
current_state_.processingShader()->contrast =
|
||||
(1.f - current_cursor_) * from_.processingShader()->contrast
|
||||
+ current_cursor_ * to_.processingShader()->contrast;
|
||||
|
||||
current_state_.processingShader()->saturation =
|
||||
(1.f - current_cursor_) * from_.processingShader()->saturation
|
||||
+ current_cursor_ * to_.processingShader()->saturation;
|
||||
|
||||
current_state_.processingShader()->hueshift =
|
||||
(1.f - current_cursor_) * from_.processingShader()->hueshift
|
||||
+ current_cursor_ * to_.processingShader()->hueshift;
|
||||
|
||||
current_state_.processingShader()->threshold =
|
||||
(1.f - current_cursor_) * from_.processingShader()->threshold
|
||||
+ current_cursor_ * to_.processingShader()->threshold;
|
||||
|
||||
current_state_.processingShader()->lumakey =
|
||||
(1.f - current_cursor_) * from_.processingShader()->lumakey
|
||||
+ current_cursor_ * to_.processingShader()->lumakey;
|
||||
|
||||
current_state_.processingShader()->nbColors =
|
||||
(1.f - current_cursor_) * from_.processingShader()->nbColors
|
||||
+ current_cursor_ * to_.processingShader()->nbColors;
|
||||
|
||||
current_state_.processingShader()->gamma =
|
||||
(1.f - current_cursor_) * from_.processingShader()->gamma
|
||||
+ current_cursor_ * to_.processingShader()->gamma;
|
||||
|
||||
current_state_.processingShader()->levels =
|
||||
(1.f - current_cursor_) * from_.processingShader()->levels
|
||||
+ current_cursor_ * to_.processingShader()->levels;
|
||||
|
||||
current_state_.processingShader()->chromakey =
|
||||
(1.f - current_cursor_) * from_.processingShader()->chromakey
|
||||
+ current_cursor_ * to_.processingShader()->chromakey;
|
||||
|
||||
current_state_.processingShader()->chromadelta =
|
||||
(1.f - current_cursor_) * from_.processingShader()->chromadelta
|
||||
+ current_cursor_ * to_.processingShader()->chromadelta;
|
||||
|
||||
subject_->processingShader()->copy( *current_state_.processingShader() );
|
||||
|
||||
// not interpolated : invert , filterid
|
||||
}
|
||||
|
||||
float SourceInterpolator::current() const
|
||||
{
|
||||
return current_cursor_;
|
||||
}
|
||||
|
||||
void SourceInterpolator::apply(float percent)
|
||||
{
|
||||
percent = CLAMP( percent, 0.f, 1.f);
|
||||
|
||||
if ( subject_ && ABS_DIFF(current_cursor_, percent) > EPSILON)
|
||||
{
|
||||
current_cursor_ = percent;
|
||||
|
||||
if (current_cursor_ < EPSILON) {
|
||||
current_cursor_ = 0.f;
|
||||
current_state_ = from_;
|
||||
subject_->copy(current_state_);
|
||||
}
|
||||
else if (current_cursor_ > 1.f - EPSILON) {
|
||||
current_cursor_ = 1.f;
|
||||
current_state_ = to_;
|
||||
subject_->copy(current_state_);
|
||||
}
|
||||
else {
|
||||
interpolateGroup(View::MIXING);
|
||||
interpolateGroup(View::GEOMETRY);
|
||||
interpolateGroup(View::LAYER);
|
||||
interpolateGroup(View::TEXTURE);
|
||||
interpolateImageProcessing();
|
||||
// Log::Info("SourceInterpolator::update %f", cursor);
|
||||
}
|
||||
|
||||
subject_->touch();
|
||||
}
|
||||
}
|
||||
|
||||
Interpolator::Interpolator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Interpolator::~Interpolator()
|
||||
{
|
||||
for (auto i = interpolators_.begin(); i != interpolators_.end(); ) {
|
||||
delete *i;
|
||||
i = interpolators_.erase(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Interpolator::add (Source *s, const SourceCore &target)
|
||||
{
|
||||
SourceInterpolator *i = new SourceInterpolator(s, target);
|
||||
interpolators_.push_back(i);
|
||||
}
|
||||
|
||||
|
||||
float Interpolator::current() const
|
||||
{
|
||||
float ret = 0.f;
|
||||
if (interpolators_.size() > 0)
|
||||
ret = interpolators_.front()->current();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Interpolator::apply(float percent)
|
||||
{
|
||||
for (auto i = interpolators_.begin(); i != interpolators_.end(); ++i)
|
||||
(*i)->apply( percent );
|
||||
|
||||
}
|
||||
|
||||
|
||||
43
Interpolator.h
Normal file
43
Interpolator.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef INTERPOLATOR_H
|
||||
#define INTERPOLATOR_H
|
||||
|
||||
#include "Source.h"
|
||||
#include "SourceList.h"
|
||||
|
||||
class SourceInterpolator
|
||||
{
|
||||
public:
|
||||
SourceInterpolator(Source *subject, const SourceCore &target);
|
||||
|
||||
void apply (float percent);
|
||||
float current() const;
|
||||
|
||||
protected:
|
||||
Source *subject_;
|
||||
|
||||
SourceCore from_;
|
||||
SourceCore to_;
|
||||
SourceCore current_state_;
|
||||
float current_cursor_;
|
||||
|
||||
void interpolateGroup (View::Mode m);
|
||||
void interpolateImageProcessing ();
|
||||
};
|
||||
|
||||
class Interpolator
|
||||
{
|
||||
public:
|
||||
Interpolator();
|
||||
~Interpolator();
|
||||
|
||||
void add (Source *s, const SourceCore &target );
|
||||
|
||||
void apply (float percent);
|
||||
float current() const;
|
||||
|
||||
protected:
|
||||
std::list<SourceInterpolator *> interpolators_;
|
||||
|
||||
};
|
||||
|
||||
#endif // INTERPOLATOR_H
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "Mixer.h"
|
||||
#include "defines.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "Decorations.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
@@ -123,7 +124,7 @@ void LayerView::draw()
|
||||
(*it)->setDepth(depth);
|
||||
}
|
||||
Action::manager().store(std::string("Selection: Layer Distribute"));
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
if (ImGui::Selectable( ICON_FA_RULER_HORIZONTAL " Compress" )){
|
||||
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
|
||||
@@ -134,7 +135,7 @@ void LayerView::draw()
|
||||
(*it)->setDepth(depth);
|
||||
}
|
||||
Action::manager().store(std::string("Selection: Layer Compress"));
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
if (ImGui::Selectable( ICON_FA_EXCHANGE_ALT " Reverse order" )){
|
||||
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
|
||||
@@ -144,7 +145,7 @@ void LayerView::draw()
|
||||
(*it)->setDepth((*rit)->depth());
|
||||
}
|
||||
Action::manager().store(std::string("Selection: Layer Reverse order"));
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(2);
|
||||
@@ -226,12 +227,13 @@ std::pair<Node *, glm::vec2> LayerView::pick(glm::vec2 P)
|
||||
Source *s = Mixer::manager().findSource(pick.first);
|
||||
if (s != nullptr) {
|
||||
// pick on the lock icon; unlock source
|
||||
if ( pick.first == s->lock_) {
|
||||
if ( UserInterface::manager().ctrlModifier() && pick.first == s->lock_) {
|
||||
lock(s, false);
|
||||
pick = { s->locker_, pick.second };
|
||||
// pick = { s->locker_, pick.second };
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
// pick on the open lock icon; lock source and cancel pick
|
||||
else if ( pick.first == s->unlock_ ) {
|
||||
else if ( UserInterface::manager().ctrlModifier() && pick.first == s->unlock_ ) {
|
||||
lock(s, true);
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ class LayerView : public View
|
||||
{
|
||||
public:
|
||||
LayerView();
|
||||
// non assignable class
|
||||
LayerView(LayerView const&) = delete;
|
||||
LayerView& operator=(LayerView const&) = delete;
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
|
||||
114
Log.cpp
114
Log.cpp
@@ -57,7 +57,7 @@ struct AppLog
|
||||
ImGui::SetNextWindowPos(ImVec2(430, 660), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(1150, 220), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 180), ImVec2(FLT_MAX, FLT_MAX));
|
||||
if (!ImGui::Begin(title, p_open))
|
||||
if ( !ImGui::Begin(title, p_open))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
@@ -75,69 +75,71 @@ struct AppLog
|
||||
Filter.Draw("Filter", -60.0f);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar);
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
if (copy)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
mtx.lock();
|
||||
|
||||
const char* buf = Buf.begin();
|
||||
const char* buf_end = Buf.end();
|
||||
if (Filter.IsActive())
|
||||
if ( ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_AlwaysHorizontalScrollbar) )
|
||||
{
|
||||
// In this example we don't use the clipper when Filter is enabled.
|
||||
// This is because we don't have a random access on the result on our filter.
|
||||
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
|
||||
// especially if the filtering function is not trivial (e.g. reg-exp).
|
||||
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
||||
if (clear)
|
||||
Clear();
|
||||
if (copy)
|
||||
ImGui::LogToClipboard();
|
||||
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
mtx.lock();
|
||||
|
||||
const char* buf = Buf.begin();
|
||||
const char* buf_end = Buf.end();
|
||||
if (Filter.IsActive())
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no];
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
if (Filter.PassFilter(line_start, line_end))
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The simplest and easy way to display the entire buffer:
|
||||
// ImGui::TextUnformatted(buf_begin, buf_end);
|
||||
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
|
||||
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
|
||||
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
|
||||
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
|
||||
// both of which we can handle since we an array pointing to the beginning of each line of text.
|
||||
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
|
||||
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(LineOffsets.Size);
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||
// In this example we don't use the clipper when Filter is enabled.
|
||||
// This is because we don't have a random access on the result on our filter.
|
||||
// A real application processing logs with ten of thousands of entries may want to store the result of search/filter.
|
||||
// especially if the filtering function is not trivial (e.g. reg-exp).
|
||||
for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
|
||||
const char* line_start = buf + LineOffsets[line_no];
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
if (Filter.PassFilter(line_start, line_end))
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
else
|
||||
{
|
||||
// The simplest and easy way to display the entire buffer:
|
||||
// ImGui::TextUnformatted(buf_begin, buf_end);
|
||||
// And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward to skip non-visible lines.
|
||||
// Here we instead demonstrate using the clipper to only process lines that are within the visible area.
|
||||
// If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them on your side is recommended.
|
||||
// Using ImGuiListClipper requires A) random access into your data, and B) items all being the same height,
|
||||
// both of which we can handle since we an array pointing to the beginning of each line of text.
|
||||
// When using the filter (in the block of code above) we don't have random access into the data to display anymore, which is why we don't use the clipper.
|
||||
// Storing or skimming through the search result would make it possible (and would be recommended if you want to search through tens of thousands of entries)
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(LineOffsets.Size);
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
|
||||
{
|
||||
const char* line_start = buf + LineOffsets[line_no] + (numbering?0:6);
|
||||
const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
|
||||
ImGui::TextUnformatted(line_start, line_end);
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopFont();
|
||||
|
||||
// Auto scroll
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
mtx.unlock();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopFont();
|
||||
|
||||
// Auto scroll
|
||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
}
|
||||
};
|
||||
|
||||
139
MediaPlayer.cpp
139
MediaPlayer.cpp
@@ -12,7 +12,7 @@ using namespace std;
|
||||
#include "Resource.h"
|
||||
#include "Visitor.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "GlmToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "RenderingManager.h"
|
||||
|
||||
@@ -27,20 +27,20 @@ std::list<MediaPlayer*> MediaPlayer::registered_;
|
||||
MediaPlayer::MediaPlayer()
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
uri_ = "undefined";
|
||||
pipeline_ = nullptr;
|
||||
opened_ = false;
|
||||
enabled_ = true;
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
|
||||
ready_ = false;
|
||||
failed_ = false;
|
||||
seeking_ = false;
|
||||
enabled_ = true;
|
||||
force_software_decoding_ = false;
|
||||
hardware_decoder_ = "";
|
||||
rate_ = 1.0;
|
||||
position_ = GST_CLOCK_TIME_NONE;
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
loop_ = LoopMode::LOOP_REWIND;
|
||||
|
||||
// start index in frame_ stack
|
||||
@@ -65,6 +65,9 @@ MediaPlayer::~MediaPlayer()
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
}
|
||||
|
||||
void MediaPlayer::accept(Visitor& v) {
|
||||
@@ -81,7 +84,7 @@ guint MediaPlayer::texture() const
|
||||
|
||||
#define LIMIT_DISCOVERER
|
||||
|
||||
static MediaInfo UriDiscoverer_(std::string uri)
|
||||
MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri)
|
||||
{
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
Log::Info("Checking file '%s'", uri.c_str());
|
||||
@@ -217,23 +220,27 @@ static MediaInfo UriDiscoverer_(std::string uri)
|
||||
return video_stream_info;
|
||||
}
|
||||
|
||||
void MediaPlayer::open(string path)
|
||||
void MediaPlayer::open (const std::string & filename, const string &uri)
|
||||
{
|
||||
// set path
|
||||
filename_ = SystemToolkit::transliterate( path );
|
||||
filename_ = BaseToolkit::transliterate( filename );
|
||||
|
||||
// set uri to open
|
||||
uri_ = GstToolkit::filename_to_uri(path);
|
||||
if (uri.empty())
|
||||
uri_ = GstToolkit::filename_to_uri( filename );
|
||||
else
|
||||
uri_ = uri;
|
||||
|
||||
// reset
|
||||
ready_ = false;
|
||||
// close before re-openning
|
||||
if (isOpen())
|
||||
close();
|
||||
|
||||
// start URI discovering thread:
|
||||
discoverer_ = std::async( UriDiscoverer_, uri_);
|
||||
discoverer_ = std::async( MediaPlayer::UriDiscoverer, uri_);
|
||||
// wait for discoverer to finish in the future (test in update)
|
||||
|
||||
// // debug without thread
|
||||
// media_ = UriDiscoverer_(uri_);
|
||||
// media_ = MediaPlayer::UriDiscoverer(uri_);
|
||||
// if (media_.valid) {
|
||||
// timeline_.setEnd( media_.end );
|
||||
// timeline_.setStep( media_.dt );
|
||||
@@ -329,47 +336,48 @@ void MediaPlayer::execute_open()
|
||||
|
||||
// setup appsink
|
||||
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
|
||||
if (sink) {
|
||||
|
||||
// instruct the sink to send samples synched in time
|
||||
gst_base_sink_set_sync (GST_BASE_SINK(sink), true);
|
||||
|
||||
// instruct sink to use the required caps
|
||||
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
|
||||
|
||||
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
|
||||
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5);
|
||||
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
|
||||
|
||||
#ifdef USE_GST_APPSINK_CALLBACKS
|
||||
// set the callbacks
|
||||
GstAppSinkCallbacks callbacks;
|
||||
callbacks.new_preroll = callback_new_preroll;
|
||||
if (media_.isimage) {
|
||||
callbacks.eos = NULL;
|
||||
callbacks.new_sample = NULL;
|
||||
}
|
||||
else {
|
||||
callbacks.eos = callback_end_of_stream;
|
||||
callbacks.new_sample = callback_new_sample;
|
||||
}
|
||||
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
|
||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
|
||||
#else
|
||||
// connect signals callbacks
|
||||
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
|
||||
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
|
||||
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
|
||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
|
||||
#endif
|
||||
// done with ref to sink
|
||||
gst_object_unref (sink);
|
||||
}
|
||||
else {
|
||||
if (!sink) {
|
||||
Log::Warning("MediaPlayer %s Could not configure sink", std::to_string(id_).c_str());
|
||||
failed_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// instruct the sink to send samples synched in time
|
||||
gst_base_sink_set_sync (GST_BASE_SINK(sink), true);
|
||||
|
||||
// instruct sink to use the required caps
|
||||
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
|
||||
|
||||
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
|
||||
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5);
|
||||
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
|
||||
|
||||
#ifdef USE_GST_APPSINK_CALLBACKS
|
||||
// set the callbacks
|
||||
GstAppSinkCallbacks callbacks;
|
||||
callbacks.new_preroll = callback_new_preroll;
|
||||
if (media_.isimage) {
|
||||
callbacks.eos = NULL;
|
||||
callbacks.new_sample = NULL;
|
||||
}
|
||||
else {
|
||||
callbacks.eos = callback_end_of_stream;
|
||||
callbacks.new_sample = callback_new_sample;
|
||||
}
|
||||
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
|
||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
|
||||
#else
|
||||
// connect signals callbacks
|
||||
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
|
||||
if (!media_.isimage) {
|
||||
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
|
||||
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
|
||||
}
|
||||
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
|
||||
#endif
|
||||
|
||||
// done with ref to sink
|
||||
gst_object_unref (sink);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
#ifdef USE_GST_OPENGL_SYNC_HANDLER
|
||||
@@ -399,7 +407,7 @@ void MediaPlayer::execute_open()
|
||||
Log::Info("MediaPlayer %s Timeline [%ld %ld] %ld frames, %d gaps", std::to_string(id_).c_str(),
|
||||
timeline_.begin(), timeline_.end(), timeline_.numFrames(), timeline_.numGaps());
|
||||
|
||||
ready_ = true;
|
||||
opened_ = true;
|
||||
|
||||
// register media player
|
||||
MediaPlayer::registered_.push_back(this);
|
||||
@@ -407,7 +415,7 @@ void MediaPlayer::execute_open()
|
||||
|
||||
bool MediaPlayer::isOpen() const
|
||||
{
|
||||
return ready_;
|
||||
return opened_;
|
||||
}
|
||||
|
||||
bool MediaPlayer::failed() const
|
||||
@@ -417,16 +425,15 @@ bool MediaPlayer::failed() const
|
||||
|
||||
void MediaPlayer::Frame::unmap()
|
||||
{
|
||||
if ( full ) {
|
||||
if ( full )
|
||||
gst_video_frame_unmap(&vframe);
|
||||
full = false;
|
||||
}
|
||||
full = false;
|
||||
}
|
||||
|
||||
void MediaPlayer::close()
|
||||
{
|
||||
// not openned?
|
||||
if (!ready_) {
|
||||
if (!opened_) {
|
||||
// wait for loading to finish
|
||||
if (discoverer_.valid())
|
||||
discoverer_.wait();
|
||||
@@ -435,7 +442,7 @@ void MediaPlayer::close()
|
||||
}
|
||||
|
||||
// un-ready the media player
|
||||
ready_ = false;
|
||||
opened_ = false;
|
||||
|
||||
// clean up GST
|
||||
if (pipeline_ != nullptr) {
|
||||
@@ -463,12 +470,6 @@ void MediaPlayer::close()
|
||||
write_index_ = 0;
|
||||
last_index_ = 0;
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_size_ = 0;
|
||||
pbo_index_ = 0;
|
||||
pbo_next_index_ = 0;
|
||||
|
||||
#ifdef MEDIA_PLAYER_DEBUG
|
||||
Log::Info("MediaPlayer %s closed", std::to_string(id_).c_str());
|
||||
@@ -507,7 +508,7 @@ GstClockTime MediaPlayer::position()
|
||||
|
||||
void MediaPlayer::enable(bool on)
|
||||
{
|
||||
if ( !ready_ )
|
||||
if ( !opened_ || pipeline_ == nullptr)
|
||||
return;
|
||||
|
||||
if ( enabled_ != on ) {
|
||||
@@ -772,6 +773,7 @@ void MediaPlayer::init_texture(guint index)
|
||||
// for possible hadrware decoding plugins used. Empty string means none.
|
||||
hardware_decoder_ = GstToolkit::used_gpu_decoding_plugins(pipeline_);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -819,6 +821,7 @@ void MediaPlayer::fill_texture(guint index)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,7 +832,7 @@ void MediaPlayer::update()
|
||||
return;
|
||||
|
||||
// not ready yet
|
||||
if (!ready_) {
|
||||
if (!opened_) {
|
||||
if (discoverer_.valid()) {
|
||||
// try to get info from discoverer
|
||||
if (discoverer_.wait_for( std::chrono::milliseconds(4) ) == std::future_status::ready )
|
||||
@@ -1153,7 +1156,7 @@ bool MediaPlayer::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
void MediaPlayer::callback_end_of_stream (GstAppSink *, gpointer p)
|
||||
{
|
||||
MediaPlayer *m = static_cast<MediaPlayer *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
m->fill_frame(NULL, MediaPlayer::EOS);
|
||||
}
|
||||
}
|
||||
@@ -1170,7 +1173,7 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p)
|
||||
|
||||
// send frames to media player only if ready
|
||||
MediaPlayer *m = static_cast<MediaPlayer *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
|
||||
// get buffer from sample
|
||||
GstBuffer *buf = gst_sample_get_buffer (sample);
|
||||
@@ -1205,7 +1208,7 @@ GstFlowReturn MediaPlayer::callback_new_sample (GstAppSink *sink, gpointer p)
|
||||
|
||||
// send frames to media player only if ready
|
||||
MediaPlayer *m = static_cast<MediaPlayer *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
|
||||
// get buffer from sample (valid until sample is released)
|
||||
GstBuffer *buf = gst_sample_get_buffer (sample) ;
|
||||
|
||||
@@ -91,8 +91,8 @@ public:
|
||||
/**
|
||||
* Open a media using gstreamer URI
|
||||
* */
|
||||
void open( std::string path);
|
||||
void reopen();
|
||||
void open ( const std::string &filename, const std::string &uri = "");
|
||||
void reopen ();
|
||||
/**
|
||||
* Get name of the media
|
||||
* */
|
||||
@@ -265,6 +265,8 @@ public:
|
||||
static std::list<MediaPlayer*>::const_iterator begin() { return registered_.cbegin(); }
|
||||
static std::list<MediaPlayer*>::const_iterator end() { return registered_.cend(); }
|
||||
|
||||
static MediaInfo UriDiscoverer(const std::string &uri);
|
||||
|
||||
private:
|
||||
|
||||
// video player description
|
||||
@@ -285,7 +287,7 @@ private:
|
||||
GstState desired_state_;
|
||||
GstElement *pipeline_;
|
||||
GstVideoInfo v_frame_video_info_;
|
||||
std::atomic<bool> ready_;
|
||||
std::atomic<bool> opened_;
|
||||
std::atomic<bool> failed_;
|
||||
bool seeking_;
|
||||
bool enabled_;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "Visitor.h"
|
||||
#include "Log.h"
|
||||
|
||||
MediaSource::MediaSource() : Source(), path_("")
|
||||
MediaSource::MediaSource(uint64_t id) : Source(id), path_("")
|
||||
{
|
||||
// create media player
|
||||
mediaplayer_ = new MediaPlayer;
|
||||
@@ -25,11 +25,15 @@ MediaSource::~MediaSource()
|
||||
|
||||
void MediaSource::setPath(const std::string &p)
|
||||
{
|
||||
Log::Notify("Creating Source with media '%s'", p.c_str());
|
||||
|
||||
path_ = p;
|
||||
Log::Notify("Creating Source with media '%s'", path_.c_str());
|
||||
|
||||
// open gstreamer
|
||||
mediaplayer_->open(path_);
|
||||
mediaplayer_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
std::string MediaSource::path() const
|
||||
@@ -91,10 +95,9 @@ void MediaSource::init()
|
||||
active_ = true;
|
||||
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
initialized_ = true;
|
||||
Log::Info("Source '%s' linked to Media %s.", name().c_str(), std::to_string(mediaplayer_->id()).c_str());
|
||||
}
|
||||
}
|
||||
@@ -123,21 +126,16 @@ void MediaSource::update(float dt)
|
||||
|
||||
void MediaSource::render()
|
||||
{
|
||||
if (!initialized_)
|
||||
if ( renderbuffer_ == nullptr )
|
||||
init();
|
||||
else {
|
||||
// blendingshader_->color.r = mediaplayer_->currentTimelineFading();
|
||||
// blendingshader_->color.g = mediaplayer_->currentTimelineFading();
|
||||
// blendingshader_->color.b = mediaplayer_->currentTimelineFading();
|
||||
|
||||
// render the media player into frame buffer
|
||||
renderbuffer_->begin();
|
||||
// texturesurface_->shader()->color.a = mediaplayer_->currentTimelineFading();
|
||||
texturesurface_->shader()->color.r = mediaplayer_->currentTimelineFading();
|
||||
texturesurface_->shader()->color.g = mediaplayer_->currentTimelineFading();
|
||||
texturesurface_->shader()->color.b = mediaplayer_->currentTimelineFading();
|
||||
// apply fading
|
||||
texturesurface_->shader()->color = glm::vec4( glm::vec3(mediaplayer_->currentTimelineFading()), 1.f);
|
||||
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
|
||||
renderbuffer_->end();
|
||||
ready_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ class MediaPlayer;
|
||||
class MediaSource : public Source
|
||||
{
|
||||
public:
|
||||
MediaSource();
|
||||
MediaSource(uint64_t id = 0);
|
||||
~MediaSource();
|
||||
|
||||
// implementation of source API
|
||||
|
||||
266
Mixer.cpp
266
Mixer.cpp
@@ -18,6 +18,7 @@
|
||||
#include "Log.h"
|
||||
#include "View.h"
|
||||
#include "ImageShader.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "SessionCreator.h"
|
||||
#include "SessionVisitor.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include "MediaSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "StreamSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "ActionManager.h"
|
||||
@@ -138,7 +140,7 @@ void Mixer::update()
|
||||
if ( back_session_ ) {
|
||||
// swap front and back sessions
|
||||
swap();
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
// set session filename
|
||||
Rendering::manager().mainWindow().setTitle(session_->filename());
|
||||
Settings::application.recentSessions.push(session_->filename());
|
||||
@@ -181,7 +183,7 @@ void Mixer::update()
|
||||
failure = nullptr; // prevent delete (already done in recreateSource)
|
||||
}
|
||||
// delete the source
|
||||
deleteSource(failure, false);
|
||||
deleteSource(failure);
|
||||
}
|
||||
|
||||
// update views
|
||||
@@ -193,7 +195,7 @@ void Mixer::update()
|
||||
|
||||
// deep update was performed
|
||||
if (View::need_deep_update_ > 0)
|
||||
View::need_deep_update_--;
|
||||
--View::need_deep_update_;
|
||||
}
|
||||
|
||||
void Mixer::draw()
|
||||
@@ -244,6 +246,37 @@ Source * Mixer::createSourceFile(const std::string &path)
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceMultifile(const std::list<std::string> &list_files, uint fps)
|
||||
{
|
||||
// ready to create a source
|
||||
Source *s = nullptr;
|
||||
|
||||
if ( list_files.size() >0 ) {
|
||||
|
||||
// validate the creation of a sequence from the list
|
||||
MultiFileSequence sequence(list_files);
|
||||
|
||||
if ( sequence.valid() ) {
|
||||
|
||||
// try to create a sequence
|
||||
MultiFileSource *mfs = new MultiFileSource;
|
||||
mfs->setSequence(sequence, fps);
|
||||
s = mfs;
|
||||
|
||||
// remember in recent media
|
||||
Settings::application.recentImport.path = SystemToolkit::path_filename(list_files.front());
|
||||
|
||||
// propose a new name
|
||||
s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) );
|
||||
}
|
||||
else {
|
||||
Log::Notify("Could not find a sequence of consecutively numbered files.");
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceRender()
|
||||
{
|
||||
// ready to create a source
|
||||
@@ -251,7 +284,10 @@ Source * Mixer::createSourceRender()
|
||||
s->setSession(session_);
|
||||
|
||||
// propose a new name based on session name
|
||||
s->setName(SystemToolkit::base_filename(session_->filename()));
|
||||
if ( !session_->filename().empty() )
|
||||
s->setName(SystemToolkit::base_filename(session_->filename()));
|
||||
else
|
||||
s->setName("Output");
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -263,8 +299,7 @@ Source * Mixer::createSourceStream(const std::string &gstreamerpipeline)
|
||||
s->setDescription(gstreamerpipeline);
|
||||
|
||||
// propose a new name based on pattern name
|
||||
std::string name = gstreamerpipeline.substr(0, gstreamerpipeline.find(" "));
|
||||
s->setName(name);
|
||||
s->setName( gstreamerpipeline.substr(0, gstreamerpipeline.find(" ")) );
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -289,8 +324,7 @@ Source * Mixer::createSourceDevice(const std::string &namedevice)
|
||||
Source *s = Device::manager().createSource(namedevice);
|
||||
|
||||
// propose a new name based on pattern name
|
||||
std::string name = namedevice.substr(0, namedevice.find(" "));
|
||||
s->setName(name);
|
||||
s->setName( namedevice.substr(0, namedevice.find(" ")) );
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -333,23 +367,17 @@ Source * Mixer::createSourceClone(const std::string &namesource)
|
||||
origin = current_source_;
|
||||
|
||||
// have an origin, can clone it
|
||||
if (origin != session_->end()) {
|
||||
|
||||
if (origin != session_->end())
|
||||
// create a source
|
||||
s = (*origin)->clone();
|
||||
|
||||
// propose new name (this automatically increments name)
|
||||
renameSource(s, (*origin)->name());
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void Mixer::addSource(Source *s)
|
||||
{
|
||||
if (s != nullptr) {
|
||||
if (s != nullptr)
|
||||
candidate_sources_.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::insertSource(Source *s, View::Mode m)
|
||||
@@ -357,7 +385,7 @@ void Mixer::insertSource(Source *s, View::Mode m)
|
||||
if ( s != nullptr )
|
||||
{
|
||||
// avoid duplicate name
|
||||
renameSource(s, s->name());
|
||||
renameSource(s);
|
||||
|
||||
// Add source to Session (ignored if source already in)
|
||||
SourceList::iterator sit = session_->addSource(s);
|
||||
@@ -455,7 +483,7 @@ bool Mixer::recreateSource(Source *s)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mixer::deleteSource(Source *s, bool withundo)
|
||||
void Mixer::deleteSource(Source *s)
|
||||
{
|
||||
if ( s != nullptr )
|
||||
{
|
||||
@@ -468,10 +496,6 @@ void Mixer::deleteSource(Source *s, bool withundo)
|
||||
// delete source
|
||||
session_->deleteSource(s);
|
||||
|
||||
// store new state in history manager
|
||||
if (withundo)
|
||||
Action::manager().store(name + std::string(" source deleted"));
|
||||
|
||||
// log
|
||||
Log::Notify("Source %s deleted.", name.c_str());
|
||||
}
|
||||
@@ -570,22 +594,36 @@ void Mixer::deselect(Source *s)
|
||||
|
||||
void Mixer::deleteSelection()
|
||||
{
|
||||
// get clones first : this way we store the history of deletion in the right order
|
||||
SourceList selection_clones_;
|
||||
for ( auto sit = selection().begin(); sit != selection().end(); sit++ ) {
|
||||
CloneSource *clone = dynamic_cast<CloneSource *>(*sit);
|
||||
if (clone)
|
||||
selection_clones_.push_back(clone);
|
||||
}
|
||||
// delete all clones
|
||||
while ( !selection_clones_.empty() ) {
|
||||
deleteSource( selection_clones_.front());
|
||||
selection_clones_.pop_front();
|
||||
}
|
||||
// empty the selection
|
||||
while ( !selection().empty() )
|
||||
deleteSource( selection().front() ); // this also remove element from selection()
|
||||
// number of sources in selection
|
||||
int N = selection().size();
|
||||
// ignore if selection empty
|
||||
if (N > 0) {
|
||||
|
||||
// adapt Action::manager undo info depending on case
|
||||
std::ostringstream info;
|
||||
if (N > 1)
|
||||
info << N << " sources deleted";
|
||||
else
|
||||
info << selection().front()->name() << ": deleted";
|
||||
|
||||
// get clones first : this way we store the history of deletion in the right order
|
||||
SourceList selection_clones_;
|
||||
for ( auto sit = selection().begin(); sit != selection().end(); sit++ ) {
|
||||
CloneSource *clone = dynamic_cast<CloneSource *>(*sit);
|
||||
if (clone)
|
||||
selection_clones_.push_back(clone);
|
||||
}
|
||||
// delete all clones
|
||||
while ( !selection_clones_.empty() ) {
|
||||
deleteSource( selection_clones_.front());// this also removes element from selection()
|
||||
selection_clones_.pop_front();
|
||||
}
|
||||
// empty the selection
|
||||
while ( !selection().empty() )
|
||||
deleteSource( selection().front() ); // this also removes element from selection()
|
||||
|
||||
Action::manager().store(info.str());
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::groupSelection()
|
||||
@@ -637,7 +675,7 @@ void Mixer::groupSelection()
|
||||
|
||||
// store in action manager
|
||||
std::ostringstream info;
|
||||
info << sessiongroup->name() << " source inserted, " << sessiongroup->session()->numSource() << " sources flatten.";
|
||||
info << sessiongroup->name() << " inserted: " << sessiongroup->session()->numSource() << " sources flatten.";
|
||||
Action::manager().store(info.str());
|
||||
|
||||
// give the hand to the user
|
||||
@@ -650,19 +688,13 @@ void Mixer::renameSource(Source *s, const std::string &newname)
|
||||
if ( s != nullptr )
|
||||
{
|
||||
// tentative new name
|
||||
std::string tentativename = newname;
|
||||
std::string tentativename = s->name();
|
||||
|
||||
// refuse to rename to an empty name
|
||||
if ( newname.empty() )
|
||||
tentativename = "source";
|
||||
// try the given new name if valid
|
||||
if ( !newname.empty() )
|
||||
tentativename = newname;
|
||||
|
||||
// search for a source of the name 'tentativename'
|
||||
std::string basename = tentativename;
|
||||
int count = 1;
|
||||
for( auto it = session_->begin(); it != session_->end(); it++){
|
||||
if ( s->id() != (*it)->id() && (*it)->name() == tentativename )
|
||||
tentativename = basename + std::to_string( ++count );
|
||||
}
|
||||
tentativename = BaseToolkit::uniqueName(tentativename, session_->getNameList(s->id()));
|
||||
|
||||
// ok to rename
|
||||
s->setName(tentativename);
|
||||
@@ -679,7 +711,7 @@ void Mixer::setCurrentSource(SourceList::iterator it)
|
||||
unsetCurrentSource();
|
||||
|
||||
// change current if 'it' is valid
|
||||
if ( it != session_->end() ) {
|
||||
if ( it != session_->end() /*&& (*it)->mode() > Source::UNINITIALIZED */) {
|
||||
current_source_ = it;
|
||||
current_source_index_ = session_->index(current_source_);
|
||||
|
||||
@@ -886,7 +918,7 @@ void Mixer::setView(View::Mode m)
|
||||
}
|
||||
|
||||
// need to deeply update view to apply eventual changes
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
|
||||
View *Mixer::view(View::Mode m)
|
||||
@@ -942,25 +974,28 @@ void Mixer::load(const std::string& filename)
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mixer::open(const std::string& filename)
|
||||
void Mixer::open(const std::string& filename, bool smooth)
|
||||
{
|
||||
if (Settings::application.smooth_transition)
|
||||
if (smooth)
|
||||
{
|
||||
Log::Info("\nStarting transition to session %s", filename.c_str());
|
||||
|
||||
// create special SessionSource to be used for the smooth transition
|
||||
SessionFileSource *ts = new SessionFileSource;
|
||||
|
||||
// open filename if specified
|
||||
if (!filename.empty())
|
||||
{
|
||||
Log::Info("\nStarting transition to session %s", filename.c_str());
|
||||
ts->load(filename);
|
||||
// propose a new name based on uri
|
||||
renameSource(ts, SystemToolkit::base_filename(filename));
|
||||
// propose a new name based on uri
|
||||
ts->setName(SystemToolkit::base_filename(filename));
|
||||
}
|
||||
|
||||
// attach the SessionSource to the transition view
|
||||
transition_.attach(ts);
|
||||
|
||||
// insert source and switch to transition view
|
||||
insertSource(ts, View::TRANSITION);
|
||||
|
||||
// attach the SessionSource to the transition view
|
||||
transition_.attach(ts);
|
||||
}
|
||||
else
|
||||
load(filename);
|
||||
@@ -993,15 +1028,12 @@ void Mixer::merge(Session *session)
|
||||
return;
|
||||
}
|
||||
|
||||
// new state in history manager
|
||||
std::ostringstream info;
|
||||
info << session->numSource() << " sources imported from " << session->filename();
|
||||
Action::manager().store(info.str());
|
||||
|
||||
// import every sources
|
||||
std::ostringstream info;
|
||||
info << session->numSource() << " sources imported from:" << session->filename();
|
||||
for ( Source *s = session->popSource(); s != nullptr; s = session->popSource()) {
|
||||
// avoid name duplicates
|
||||
renameSource(s, s->name());
|
||||
renameSource(s);
|
||||
|
||||
// Add source to Session
|
||||
session_->addSource(s);
|
||||
@@ -1018,10 +1050,14 @@ void Mixer::merge(Session *session)
|
||||
}
|
||||
|
||||
// needs to update !
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
// avoid display issues
|
||||
current_view_->update(0.f);
|
||||
|
||||
// new state in history manager
|
||||
Action::manager().store(info.str());
|
||||
|
||||
}
|
||||
|
||||
void Mixer::merge(SessionSource *source)
|
||||
@@ -1036,7 +1072,7 @@ void Mixer::merge(SessionSource *source)
|
||||
|
||||
// prepare Action manager info
|
||||
std::ostringstream info;
|
||||
info << source->name().c_str() << " source deleted, " << session->numSource() << " sources imported";
|
||||
info << source->name().c_str() << " expanded:" << session->numSource() << " sources imported";
|
||||
|
||||
// import sources of the session (if not empty)
|
||||
if ( !session->empty() ) {
|
||||
@@ -1067,7 +1103,7 @@ void Mixer::merge(SessionSource *source)
|
||||
for ( Source *s = session->popSource(); s != nullptr; s = session->popSource()) {
|
||||
|
||||
// avoid name duplicates
|
||||
renameSource(s, s->name());
|
||||
renameSource(s);
|
||||
|
||||
// scale alpha
|
||||
s->setAlpha( s->alpha() * source->alpha() );
|
||||
@@ -1101,7 +1137,7 @@ void Mixer::merge(SessionSource *source)
|
||||
}
|
||||
|
||||
// needs to update !
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
}
|
||||
|
||||
@@ -1109,12 +1145,11 @@ void Mixer::merge(SessionSource *source)
|
||||
detach(source);
|
||||
session_->deleteSource(source);
|
||||
|
||||
// new state in history manager
|
||||
Action::manager().store(info.str());
|
||||
|
||||
// avoid display issues
|
||||
current_view_->update(0.f);
|
||||
|
||||
// new state in history manager
|
||||
Action::manager().store(info.str());
|
||||
}
|
||||
|
||||
void Mixer::swap()
|
||||
@@ -1164,15 +1199,15 @@ void Mixer::swap()
|
||||
back_session_ = nullptr;
|
||||
|
||||
// reset History manager
|
||||
Action::manager().clear();
|
||||
Action::manager().init();
|
||||
|
||||
// notification
|
||||
Log::Notify("Session %s loaded. %d source(s) created.", session_->filename().c_str(), session_->numSource());
|
||||
}
|
||||
|
||||
void Mixer::close()
|
||||
void Mixer::close(bool smooth)
|
||||
{
|
||||
if (Settings::application.smooth_transition)
|
||||
if (smooth)
|
||||
{
|
||||
// create empty SessionSource to be used for the smooth transition
|
||||
SessionFileSource *ts = new SessionFileSource;
|
||||
@@ -1200,7 +1235,7 @@ void Mixer::clear()
|
||||
sessionSwapRequested_ = true;
|
||||
|
||||
// need to deeply update view to apply eventual changes
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
Log::Info("New session ready.");
|
||||
}
|
||||
@@ -1241,3 +1276,82 @@ void Mixer::paste(const std::string& clipboard)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mixer::restore(tinyxml2::XMLElement *sessionNode)
|
||||
{
|
||||
//
|
||||
// source lists
|
||||
//
|
||||
|
||||
// sessionsources contains list of ids of all sources currently in the session (before loading)
|
||||
SourceIdList session_sources = session_->getIdList();
|
||||
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
|
||||
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
|
||||
|
||||
// load history status:
|
||||
// - if a source exists, its attributes are updated, and that's all
|
||||
// - if a source does not exists (in current session), it is created inside the session
|
||||
SessionLoader loader( session_ );
|
||||
loader.load( sessionNode );
|
||||
|
||||
// loaded_sources contains map of xml ids of all sources treated by loader
|
||||
std::map< uint64_t, Source* > loaded_sources = loader.getSources();
|
||||
|
||||
// remove intersect of both lists (sources were updated by SessionLoader)
|
||||
for( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); ){
|
||||
auto ssit = std::find(session_sources.begin(), session_sources.end(), (*lsit).first);
|
||||
if ( ssit != session_sources.end() ) {
|
||||
lsit = loaded_sources.erase(lsit);
|
||||
session_sources.erase(ssit);
|
||||
}
|
||||
else
|
||||
lsit++;
|
||||
}
|
||||
|
||||
// remaining ids in list sessionsources : to remove
|
||||
while ( !session_sources.empty() ){
|
||||
Source *s = Mixer::manager().findSource( session_sources.front() );
|
||||
if (s!=nullptr) {
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Delete id %s\n", std::to_string(session_sources.front() ).c_str());
|
||||
#endif
|
||||
// remove the source from the mixer
|
||||
detach( s );
|
||||
// delete source from session
|
||||
session_->deleteSource( s );
|
||||
}
|
||||
session_sources.pop_front();
|
||||
}
|
||||
|
||||
// remaining sources in list loaded_sources : to add
|
||||
for ( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); lsit++)
|
||||
{
|
||||
#ifdef ACTION_DEBUG
|
||||
Log::Info("Recreate id %s to %s\n", std::to_string((*lsit).first).c_str(), std::to_string((*lsit).second->id()).c_str());
|
||||
#endif
|
||||
// attach created source
|
||||
attach( (*lsit).second );
|
||||
}
|
||||
|
||||
//
|
||||
// mixing groups
|
||||
//
|
||||
|
||||
// Get the list of mixing groups in the xml loader
|
||||
std::list< SourceList > loadergroups = loader.getMixingGroups();
|
||||
|
||||
// clear all session groups
|
||||
auto group_iter = session_->beginMixingGroup();
|
||||
while ( group_iter != session_->endMixingGroup() )
|
||||
group_iter = session_->deleteMixingGroup(group_iter);
|
||||
|
||||
// apply all changes creating or modifying groups in the session
|
||||
// (after this, new groups are created and existing groups are adjusted)
|
||||
for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++)
|
||||
session_->link( *group_loader_it, view(View::MIXING)->scene.fg() );
|
||||
|
||||
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
|
||||
|
||||
18
Mixer.h
18
Mixer.h
@@ -9,14 +9,18 @@
|
||||
#include "Session.h"
|
||||
#include "Selection.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLElement;
|
||||
}
|
||||
|
||||
class SessionSource;
|
||||
|
||||
class Mixer
|
||||
{
|
||||
// Private Constructor
|
||||
Mixer();
|
||||
Mixer(Mixer const& copy); // Not Implemented
|
||||
Mixer& operator=(Mixer const& copy); // Not Implemented
|
||||
Mixer(Mixer const& copy) = delete;
|
||||
Mixer& operator=(Mixer const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
@@ -44,6 +48,7 @@ public:
|
||||
|
||||
// creation of sources
|
||||
Source * createSourceFile (const std::string &path);
|
||||
Source * createSourceMultifile(const std::list<std::string> &list_files, uint fps);
|
||||
Source * createSourceClone (const std::string &namesource = "");
|
||||
Source * createSourceRender ();
|
||||
Source * createSourceStream (const std::string &gstreamerpipeline);
|
||||
@@ -54,8 +59,8 @@ public:
|
||||
|
||||
// operations on sources
|
||||
void addSource (Source *s);
|
||||
void deleteSource (Source *s, bool withundo=true);
|
||||
void renameSource (Source *s, const std::string &newname);
|
||||
void deleteSource (Source *s);
|
||||
void renameSource (Source *s, const std::string &newname = "");
|
||||
void attach (Source *s);
|
||||
void detach (Source *s);
|
||||
void deselect (Source *s);
|
||||
@@ -103,11 +108,12 @@ public:
|
||||
void set (Session *session);
|
||||
|
||||
// operations depending on transition mode
|
||||
void close ();
|
||||
void open (const std::string& filename);
|
||||
void close (bool smooth = false);
|
||||
void open (const std::string& filename, bool smooth = false);
|
||||
|
||||
// create sources if clipboard contains well-formed xml text
|
||||
void paste (const std::string& clipboard);
|
||||
void restore(tinyxml2::XMLElement *sessionNode);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Source.h"
|
||||
#include "Decorations.h"
|
||||
#include "Visitor.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "MixingGroup.h"
|
||||
@@ -16,10 +17,10 @@ MixingGroup::MixingGroup (SourceList sources) : parent_(nullptr), root_(nullptr)
|
||||
center_pos_(glm::vec2(0.f, 0.f)), active_(true), update_action_(ACTION_NONE), updated_source_(nullptr)
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
// fill the vector of sources with the given list
|
||||
for (auto it = sources.begin(); it != sources.end(); it++){
|
||||
for (auto it = sources.begin(); it != sources.end(); ++it){
|
||||
// add only if not linked already
|
||||
if ((*it)->mixinggroup_ == nullptr) {
|
||||
(*it)->mixinggroup_ = this;
|
||||
@@ -43,7 +44,7 @@ MixingGroup::MixingGroup (SourceList sources) : parent_(nullptr), root_(nullptr)
|
||||
|
||||
MixingGroup::~MixingGroup ()
|
||||
{
|
||||
for (auto it = sources_.begin(); it != sources_.end(); it++)
|
||||
for (auto it = sources_.begin(); it != sources_.end(); ++it)
|
||||
(*it)->clearMixingGroup();
|
||||
|
||||
if (parent_)
|
||||
@@ -71,7 +72,7 @@ void MixingGroup::recenter()
|
||||
{
|
||||
// compute barycenter (0)
|
||||
center_pos_ = glm::vec2(0.f, 0.f);
|
||||
for (auto it = sources_.begin(); it != sources_.end(); it++){
|
||||
for (auto it = sources_.begin(); it != sources_.end(); ++it){
|
||||
// compute barycenter (1)
|
||||
center_pos_ += glm::vec2((*it)->group(View::MIXING)->translation_);
|
||||
}
|
||||
@@ -126,7 +127,7 @@ void MixingGroup::update (float)
|
||||
// compute barycenter (0)
|
||||
center_pos_ = glm::vec2(0.f, 0.f);
|
||||
auto it = sources_.begin();
|
||||
for (; it != sources_.end(); it++){
|
||||
for (; it != sources_.end(); ++it){
|
||||
// update point
|
||||
p[ index_points_[*it] ] = glm::vec2((*it)->group(View::MIXING)->translation_);
|
||||
|
||||
@@ -160,7 +161,7 @@ void MixingGroup::update (float)
|
||||
// compute barycenter (0)
|
||||
center_pos_ = glm::vec2(0.f, 0.f);
|
||||
auto it = sources_.begin();
|
||||
for (; it != sources_.end(); it++){
|
||||
for (; it != sources_.end(); ++it){
|
||||
|
||||
// modify all but the already updated source
|
||||
if ( *it != updated_source_ && !(*it)->locked() ) {
|
||||
@@ -196,7 +197,7 @@ void MixingGroup::update (float)
|
||||
|
||||
int numactions = 0;
|
||||
auto it = sources_.begin();
|
||||
for (; it != sources_.end(); it++){
|
||||
for (; it != sources_.end(); ++it){
|
||||
|
||||
// modify all but the already updated source
|
||||
if ( *it != updated_source_ && !(*it)->locked() ) {
|
||||
@@ -256,7 +257,7 @@ void MixingGroup::detach (Source *s)
|
||||
|
||||
void MixingGroup::detach (SourceList l)
|
||||
{
|
||||
for (auto sit = l.begin(); sit != l.end(); sit++) {
|
||||
for (auto sit = l.begin(); sit != l.end(); ++sit) {
|
||||
// find the source
|
||||
SourceList::iterator its = std::find(sources_.begin(), sources_.end(), *sit);
|
||||
// ok, its in the list !
|
||||
@@ -291,7 +292,7 @@ void MixingGroup::attach (Source *s)
|
||||
|
||||
void MixingGroup::attach (SourceList l)
|
||||
{
|
||||
for (auto sit = l.begin(); sit != l.end(); sit++) {
|
||||
for (auto sit = l.begin(); sit != l.end(); ++sit) {
|
||||
if ( (*sit)->mixinggroup_ == nullptr) {
|
||||
// tell the source
|
||||
(*sit)->mixinggroup_ = this;
|
||||
@@ -359,7 +360,7 @@ void MixingGroup::createLineStrip()
|
||||
|
||||
// path linking all sources
|
||||
std::vector<glm::vec2> path;
|
||||
for (auto it = sources_.begin(); it != sources_.end(); it++){
|
||||
for (auto it = sources_.begin(); it != sources_.end(); ++it){
|
||||
index_points_[*it] = path.size();
|
||||
path.push_back(glm::vec2((*it)->group(View::MIXING)->translation_));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ class MixingGroup
|
||||
|
||||
public:
|
||||
MixingGroup (SourceList sources);
|
||||
// non assignable class
|
||||
MixingGroup(MixingGroup const&) = delete;
|
||||
MixingGroup& operator=(MixingGroup const&) = delete;
|
||||
~MixingGroup ();
|
||||
|
||||
// Get unique id
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "Mixer.h"
|
||||
#include "defines.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "Decorations.h"
|
||||
#include "UserInterfaceManager.h"
|
||||
@@ -351,12 +352,13 @@ std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
|
||||
Source *s = Mixer::manager().findSource(pick.first);
|
||||
if (s != nullptr) {
|
||||
// pick on the lock icon; unlock source
|
||||
if ( pick.first == s->lock_) {
|
||||
if ( UserInterface::manager().ctrlModifier() && pick.first == s->lock_) {
|
||||
lock(s, false);
|
||||
pick = { s->locker_, pick.second };
|
||||
// pick = { s->locker_, pick.second };
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
// pick on the open lock icon; lock source and cancel pick
|
||||
else if ( pick.first == s->unlock_ ) {
|
||||
else if ( UserInterface::manager().ctrlModifier() && pick.first == s->unlock_ ) {
|
||||
lock(s, true);
|
||||
pick = { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
@@ -494,7 +496,7 @@ void MixingView::terminate()
|
||||
|
||||
// terminate all mixing group actions
|
||||
for (auto g = Mixer::manager().session()->beginMixingGroup();
|
||||
g != Mixer::manager().session()->endMixingGroup(); g++)
|
||||
g != Mixer::manager().session()->endMixingGroup(); ++g)
|
||||
(*g)->setAction( MixingGroup::ACTION_FINISH );
|
||||
|
||||
}
|
||||
@@ -527,7 +529,7 @@ void MixingView::arrow (glm::vec2 movement)
|
||||
|
||||
bool first = true;
|
||||
glm::vec3 delta_translation(0.f);
|
||||
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
|
||||
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) {
|
||||
|
||||
// individual move with SHIFT
|
||||
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )
|
||||
@@ -678,7 +680,7 @@ uint textureMixingQuadratic()
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return texid;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ class MixingView : public View
|
||||
{
|
||||
public:
|
||||
MixingView();
|
||||
// non assignable class
|
||||
MixingView(MixingView const&) = delete;
|
||||
MixingView& operator=(MixingView const&) = delete;
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
|
||||
202
MultiFileSource.cpp
Normal file
202
MultiFileSource.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "defines.h"
|
||||
#include "ImageShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
#include "GstToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include "MultiFileSource.h"
|
||||
|
||||
// example test gstreamer pipelines
|
||||
//
|
||||
// multifile : sequence of numbered images
|
||||
// gst-launch-1.0 multifilesrc location="/home/bhbn/Images/sequence/frames%03d.png" caps="image/png,framerate=\(fraction\)12/1" loop=1 ! decodebin ! videoconvert ! autovideosink
|
||||
//
|
||||
// imagesequencesrc : sequence of numbered images (cannot loop)
|
||||
// gst-launch-1.0 imagesequencesrc location=frames%03d.png start-index=1 framerate=24/1 ! decodebin ! videoconvert ! autovideosink
|
||||
//
|
||||
|
||||
MultiFileSequence::MultiFileSequence() : width(0), height(0), min(0), max(0)
|
||||
{
|
||||
}
|
||||
|
||||
MultiFileSequence::MultiFileSequence(const std::list<std::string> &list_files)
|
||||
{
|
||||
location = BaseToolkit::common_numbered_pattern(list_files, &min, &max);
|
||||
|
||||
// sanity check: the location pattern looks like a filename and seems consecutive numbered
|
||||
if ( SystemToolkit::extension_filename(location).empty() ||
|
||||
SystemToolkit::path_filename(location) != SystemToolkit::path_filename(list_files.front()) ||
|
||||
list_files.size() != max - min + 1 ) {
|
||||
location.clear();
|
||||
}
|
||||
|
||||
if ( !location.empty() ) {
|
||||
MediaInfo media = MediaPlayer::UriDiscoverer( GstToolkit::filename_to_uri( list_files.front() ) );
|
||||
if (media.valid && media.isimage) {
|
||||
codec.resize(media.codec_name.size());
|
||||
std::transform(media.codec_name.begin(), media.codec_name.end(), codec.begin(), ::tolower);
|
||||
width = media.width;
|
||||
height = media.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiFileSequence::valid() const
|
||||
{
|
||||
return !( location.empty() || codec.empty() || width < 1 || height < 1 || max == min);
|
||||
}
|
||||
|
||||
inline MultiFileSequence& MultiFileSequence::operator = (const MultiFileSequence& b)
|
||||
{
|
||||
if (this != &b) {
|
||||
this->width = b.width;
|
||||
this->height = b.height;
|
||||
this->min = b.min;
|
||||
this->max = b.max;
|
||||
this->location = b.location;
|
||||
this->codec = b.codec;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
bool MultiFileSequence::operator != (const MultiFileSequence& b)
|
||||
{
|
||||
return ( location != b.location || codec != b.codec || width != b.width ||
|
||||
height != b.height || min != b.min || max != b.max );
|
||||
}
|
||||
|
||||
MultiFile::MultiFile() : Stream(), src_(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MultiFile::open (const MultiFileSequence &sequence, uint framerate )
|
||||
{
|
||||
if (sequence.location.empty())
|
||||
return;
|
||||
|
||||
std::ostringstream gstreamer_pipeline;
|
||||
gstreamer_pipeline << "multifilesrc name=src location=\"";
|
||||
gstreamer_pipeline << sequence.location;
|
||||
gstreamer_pipeline << "\" caps=\"image/";
|
||||
gstreamer_pipeline << sequence.codec;
|
||||
gstreamer_pipeline << ",framerate=(fraction)";
|
||||
gstreamer_pipeline << framerate;
|
||||
gstreamer_pipeline << "/1\" loop=1";
|
||||
gstreamer_pipeline << " start-index=";
|
||||
gstreamer_pipeline << sequence.min;
|
||||
gstreamer_pipeline << " stop-index=";
|
||||
gstreamer_pipeline << sequence.max;
|
||||
gstreamer_pipeline << " ! decodebin ! videoconvert";
|
||||
|
||||
// (private) open stream
|
||||
Stream::open(gstreamer_pipeline.str(), sequence.width, sequence.height);
|
||||
|
||||
// keep multifile source for dynamic properties change
|
||||
src_ = gst_bin_get_by_name (GST_BIN (pipeline_), "src");
|
||||
}
|
||||
|
||||
void MultiFile::close ()
|
||||
{
|
||||
if (src_ != nullptr) {
|
||||
gst_object_unref (src_);
|
||||
src_ = nullptr;
|
||||
}
|
||||
Stream::close();
|
||||
}
|
||||
|
||||
void MultiFile::setProperties (int begin, int end, int loop)
|
||||
{
|
||||
if (src_) {
|
||||
g_object_set (src_, "start-index", MAX(begin, 0), NULL);
|
||||
g_object_set (src_, "stop-index", MAX(end, 0), NULL);
|
||||
g_object_set (src_, "loop", MIN(loop, 1), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MultiFileSource::MultiFileSource (uint64_t id) : StreamSource(id), framerate_(0), begin_(-1), end_(INT_MAX), loop_(1)
|
||||
{
|
||||
// create stream
|
||||
stream_ = static_cast<Stream *>( new MultiFile );
|
||||
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::SEQUENCE, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
|
||||
void MultiFileSource::setFiles (const std::list<std::string> &list_files, uint framerate)
|
||||
{
|
||||
setSequence(MultiFileSequence(list_files), framerate);
|
||||
}
|
||||
|
||||
void MultiFileSource::setSequence (const MultiFileSequence &sequence, uint framerate)
|
||||
{
|
||||
framerate_ = CLAMP( framerate, 1, 30);
|
||||
sequence_ = sequence;
|
||||
|
||||
if (sequence_.valid())
|
||||
{
|
||||
// open gstreamer
|
||||
multifile()->open( sequence_, framerate_ );
|
||||
stream_->play(true);
|
||||
|
||||
// validate range and apply loop_
|
||||
setRange(begin_, end_);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setFramerate (uint framerate)
|
||||
{
|
||||
if (multifile()) {
|
||||
setSequence(sequence_, framerate);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setLoop (bool on)
|
||||
{
|
||||
if (multifile()) {
|
||||
loop_ = on ? 1 : 0;
|
||||
multifile()->setProperties (begin_, end_, loop_);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiFileSource::setRange (int begin, int end)
|
||||
{
|
||||
begin_ = glm::clamp( begin, sequence_.min, sequence_.max );
|
||||
end_ = glm::clamp( end , sequence_.min, sequence_.max );
|
||||
begin_ = glm::min( begin_, end_ );
|
||||
end_ = glm::max( begin_, end_ );
|
||||
|
||||
if (multifile())
|
||||
multifile()->setProperties (begin_, end_, loop_);
|
||||
}
|
||||
|
||||
void MultiFileSource::accept (Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
MultiFile *MultiFileSource::multifile () const
|
||||
{
|
||||
return dynamic_cast<MultiFile *>(stream_);
|
||||
}
|
||||
|
||||
|
||||
76
MultiFileSource.h
Normal file
76
MultiFileSource.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef MULTIFILESOURCE_H
|
||||
#define MULTIFILESOURCE_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include "StreamSource.h"
|
||||
|
||||
struct MultiFileSequence {
|
||||
std::string location;
|
||||
std::string codec;
|
||||
uint width;
|
||||
uint height;
|
||||
int min;
|
||||
int max;
|
||||
|
||||
MultiFileSequence ();
|
||||
MultiFileSequence (const std::list<std::string> &list_files);
|
||||
bool valid () const;
|
||||
MultiFileSequence& operator = (const MultiFileSequence& b);
|
||||
bool operator != (const MultiFileSequence& b);
|
||||
};
|
||||
|
||||
class MultiFile : public Stream
|
||||
{
|
||||
public:
|
||||
MultiFile ();
|
||||
void open (const MultiFileSequence &sequence, uint framerate = 30);
|
||||
void close () override;
|
||||
|
||||
// dynamic change of gstreamer multifile source properties
|
||||
void setProperties(int begin, int end, int loop);
|
||||
|
||||
protected:
|
||||
GstElement *src_ ;
|
||||
};
|
||||
|
||||
class MultiFileSource : public StreamSource
|
||||
{
|
||||
public:
|
||||
MultiFileSource (uint64_t id = 0);
|
||||
|
||||
// Source interface
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
// StreamSource interface
|
||||
Stream *stream () const override { return stream_; }
|
||||
glm::ivec2 icon () const override { return glm::ivec2(3, 9); }
|
||||
|
||||
// specific interface
|
||||
void setFiles (const std::list<std::string> &list_files, uint framerate);
|
||||
|
||||
void setSequence (const MultiFileSequence &sequence, uint framerate);
|
||||
inline MultiFileSequence sequence () const { return sequence_; }
|
||||
|
||||
void setFramerate (uint fps);
|
||||
inline uint framerate () const { return framerate_; }
|
||||
|
||||
void setLoop (bool on);
|
||||
inline bool loop () const { return loop_ > 0 ? true : false; }
|
||||
|
||||
void setRange (int begin, int end);
|
||||
inline int begin() const { return begin_; }
|
||||
inline int end () const { return end_; }
|
||||
|
||||
MultiFile *multifile () const;
|
||||
|
||||
private:
|
||||
MultiFileSequence sequence_;
|
||||
uint framerate_;
|
||||
int begin_, end_, loop_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // MULTIFILESOURCE_H
|
||||
@@ -204,7 +204,7 @@ void NetworkStream::update()
|
||||
{
|
||||
Stream::update();
|
||||
|
||||
if ( !ready_ && !failed_ && received_config_)
|
||||
if ( !opened_ && !failed_ && received_config_)
|
||||
{
|
||||
// only once
|
||||
received_config_ = false;
|
||||
@@ -268,7 +268,7 @@ void NetworkStream::update()
|
||||
}
|
||||
|
||||
|
||||
NetworkSource::NetworkSource() : StreamSource()
|
||||
NetworkSource::NetworkSource(uint64_t id) : StreamSource(id)
|
||||
{
|
||||
// create stream
|
||||
stream_ = static_cast<Stream *>( new NetworkStream );
|
||||
@@ -297,6 +297,9 @@ void NetworkSource::setConnection(const std::string &nameconnection)
|
||||
// open network stream
|
||||
networkStream()->connect( connection_name_ );
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ protected:
|
||||
const IpEndpointName& remoteEndpoint );
|
||||
public:
|
||||
inline void setParent(NetworkStream *s) { parent_ = s; }
|
||||
StreamerResponseListener() : parent_(nullptr) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -59,7 +60,7 @@ class NetworkSource : public StreamSource
|
||||
std::string connection_name_;
|
||||
|
||||
public:
|
||||
NetworkSource();
|
||||
NetworkSource(uint64_t id = 0);
|
||||
~NetworkSource();
|
||||
|
||||
// Source interface
|
||||
|
||||
23
Overlay.cpp
Normal file
23
Overlay.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "Overlay.h"
|
||||
|
||||
Overlay::Overlay()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SnapshotOverlay::SnapshotOverlay() : Overlay()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SnapshotOverlay::draw()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SnapshotOverlay::update (float dt)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
38
Overlay.h
Normal file
38
Overlay.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef OVERLAY_H
|
||||
#define OVERLAY_H
|
||||
|
||||
#include "View.h"
|
||||
|
||||
class Overlay
|
||||
{
|
||||
public:
|
||||
Overlay();
|
||||
|
||||
virtual void update (float dt) = 0;
|
||||
virtual void draw () = 0;
|
||||
|
||||
virtual std::pair<Node *, glm::vec2> pick(glm::vec2) {
|
||||
return { nullptr, glm::vec2(0.f) };
|
||||
}
|
||||
|
||||
virtual View::Cursor grab (Source*, glm::vec2, glm::vec2, std::pair<Node *, glm::vec2>) {
|
||||
return View::Cursor ();
|
||||
}
|
||||
|
||||
virtual View::Cursor over (glm::vec2) {
|
||||
return View::Cursor ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SnapshotOverlay: public Overlay
|
||||
{
|
||||
public:
|
||||
SnapshotOverlay();
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
};
|
||||
|
||||
|
||||
#endif // OVERLAY_H
|
||||
@@ -131,7 +131,7 @@ void Pattern::open( uint pattern, glm::ivec2 res )
|
||||
Stream::open(gstreamer_pattern, res.x, res.y);
|
||||
}
|
||||
|
||||
PatternSource::PatternSource() : StreamSource()
|
||||
PatternSource::PatternSource(uint64_t id) : StreamSource(id)
|
||||
{
|
||||
// create stream
|
||||
stream_ = static_cast<Stream *>( new Pattern );
|
||||
@@ -145,8 +145,12 @@ void PatternSource::setPattern(uint type, glm::ivec2 resolution)
|
||||
{
|
||||
Log::Notify("Creating Source with pattern '%s'", Pattern::pattern_types[type].c_str());
|
||||
|
||||
// open gstreamer
|
||||
pattern()->open( (uint) type, resolution );
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
void PatternSource::accept(Visitor& v)
|
||||
|
||||
@@ -23,7 +23,7 @@ private:
|
||||
class PatternSource : public StreamSource
|
||||
{
|
||||
public:
|
||||
PatternSource();
|
||||
PatternSource(uint64_t id = 0);
|
||||
|
||||
// Source interface
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
@@ -24,11 +24,6 @@ RenderView::~RenderView()
|
||||
delete fading_overlay_;
|
||||
}
|
||||
|
||||
bool RenderView::canSelect(Source *s) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderView::setFading(float f)
|
||||
{
|
||||
if (fading_overlay_ == nullptr)
|
||||
@@ -82,3 +77,65 @@ void RenderView::draw()
|
||||
frame_buffer_->end();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderView::drawThumbnail()
|
||||
{
|
||||
if (frame_buffer_) {
|
||||
// if a thumbnailer is pending
|
||||
if (thumbnailer_.size() > 0) {
|
||||
|
||||
try {
|
||||
// new thumbnailing framebuffer
|
||||
FrameBuffer *frame_thumbnail = new FrameBuffer( glm::vec3(SESSION_THUMBNAIL_HEIGHT * frame_buffer_->aspectRatio(), SESSION_THUMBNAIL_HEIGHT, 1.f) );
|
||||
|
||||
// render
|
||||
if (Settings::application.render.blit) {
|
||||
if ( !frame_buffer_->blit(frame_thumbnail) )
|
||||
throw std::runtime_error("no blit");
|
||||
}
|
||||
else {
|
||||
FrameBufferSurface *thumb = new FrameBufferSurface(frame_buffer_);
|
||||
frame_thumbnail->begin();
|
||||
thumb->draw(glm::identity<glm::mat4>(), frame_thumbnail->projection());
|
||||
frame_thumbnail->end();
|
||||
delete thumb;
|
||||
}
|
||||
|
||||
// return valid thumbnail promise
|
||||
thumbnailer_.back().set_value( frame_thumbnail->image() );
|
||||
|
||||
// done with thumbnailing framebuffer
|
||||
delete frame_thumbnail;
|
||||
}
|
||||
catch(...) {
|
||||
// return failed thumbnail promise
|
||||
thumbnailer_.back().set_exception(std::current_exception());
|
||||
}
|
||||
|
||||
// done with this promise
|
||||
thumbnailer_.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameBufferImage *RenderView::thumbnail ()
|
||||
{
|
||||
// by default null image
|
||||
FrameBufferImage *img = nullptr;
|
||||
|
||||
// create and store a promise for a FrameBufferImage
|
||||
thumbnailer_.emplace_back( std::promise<FrameBufferImage *>() );
|
||||
|
||||
// future will return the primised FrameBufferImage
|
||||
std::future<FrameBufferImage *> t = thumbnailer_.back().get_future();
|
||||
|
||||
try {
|
||||
// wait for valid return value from promise
|
||||
img = t.get();
|
||||
}
|
||||
// catch any failed promise
|
||||
catch (std::runtime_error&){
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
15
RenderView.h
15
RenderView.h
@@ -1,19 +1,27 @@
|
||||
#ifndef RENDERVIEW_H
|
||||
#define RENDERVIEW_H
|
||||
|
||||
#include <vector>
|
||||
#include <future>
|
||||
|
||||
#include "View.h"
|
||||
|
||||
class RenderView : public View
|
||||
{
|
||||
// rendering FBO
|
||||
FrameBuffer *frame_buffer_;
|
||||
Surface *fading_overlay_;
|
||||
|
||||
// promises of returning thumbnails after an update
|
||||
std::vector< std::promise<FrameBufferImage *> > thumbnailer_;
|
||||
|
||||
public:
|
||||
RenderView ();
|
||||
~RenderView ();
|
||||
|
||||
// render frame (in opengl context)
|
||||
void draw () override;
|
||||
bool canSelect(Source *) override;
|
||||
bool canSelect(Source *) override { return false; }
|
||||
|
||||
void setResolution (glm::vec3 resolution = glm::vec3(0.f), bool useAlpha = false);
|
||||
glm::vec3 resolution() const { return frame_buffer_->resolution(); }
|
||||
@@ -21,7 +29,12 @@ public:
|
||||
void setFading(float f = 0.f);
|
||||
float fading() const;
|
||||
|
||||
// current frame
|
||||
inline FrameBuffer *frame () const { return frame_buffer_; }
|
||||
|
||||
// get a thumbnail outside of opengl context; wait for a promise to be fullfiled after draw
|
||||
void drawThumbnail();
|
||||
FrameBufferImage *thumbnail ();
|
||||
};
|
||||
|
||||
#endif // RENDERVIEW_H
|
||||
|
||||
@@ -188,6 +188,7 @@ bool Rendering::init()
|
||||
// additional window callbacks for main window
|
||||
glfwSetWindowRefreshCallback( main_.window(), WindowRefreshCallback );
|
||||
glfwSetDropCallback( main_.window(), Rendering::FileDropped);
|
||||
glfwSetWindowSizeLimits( main_.window(), 800, 500, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
||||
|
||||
//
|
||||
// Gstreamer setup
|
||||
@@ -342,7 +343,7 @@ void Rendering::terminate()
|
||||
// close window
|
||||
glfwDestroyWindow(output_.window());
|
||||
glfwDestroyWindow(main_.window());
|
||||
glfwTerminate();
|
||||
// glfwTerminate();
|
||||
}
|
||||
|
||||
|
||||
@@ -423,14 +424,15 @@ glm::vec2 Rendering::project(glm::vec3 scene_coordinate, glm::mat4 modelview, bo
|
||||
|
||||
void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
|
||||
{
|
||||
for (int i = 0; i < path_count; ++i) {
|
||||
int i = 0;
|
||||
for (; i < path_count; ++i) {
|
||||
std::string filename(paths[i]);
|
||||
if (filename.empty())
|
||||
break;
|
||||
// try to create a source
|
||||
Mixer::manager().addSource ( Mixer::manager().createSourceFile( filename ) );
|
||||
}
|
||||
if (path_count>0) {
|
||||
if (i>0) {
|
||||
UserInterface::manager().showPannel();
|
||||
Rendering::manager().mainWindow().show();
|
||||
}
|
||||
@@ -726,6 +728,8 @@ bool RenderingWindow::init(int index, GLFWwindow *share)
|
||||
// DPI scaling (retina)
|
||||
dpi_scale_ = float(window_attributes_.viewport.y) / float(winset.h);
|
||||
|
||||
// We decide for byte aligned textures all over
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
|
||||
// This hint can improve the speed of texturing when perspective-correct texture coordinate interpolation isn't needed
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
||||
// fast mipmaps (we are not really using mipmaps anyway)
|
||||
|
||||
@@ -92,8 +92,8 @@ class Rendering
|
||||
|
||||
// Private Constructor
|
||||
Rendering();
|
||||
Rendering(Rendering const& copy); // Not Implemented
|
||||
Rendering& operator=(Rendering const& copy); // Not Implemented
|
||||
Rendering(Rendering const& copy) = delete;
|
||||
Rendering& operator=(Rendering const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ uint Resource::getTextureBlack()
|
||||
// texture with one black pixel
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
return tex_index_black;
|
||||
@@ -59,6 +60,7 @@ uint Resource::getTextureWhite()
|
||||
// texture with one black pixel
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
return tex_index_white;
|
||||
@@ -80,6 +82,7 @@ uint Resource::getTextureTransparent()
|
||||
// texture with one black pixel
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
return tex_index_transparent;
|
||||
@@ -219,6 +222,7 @@ uint Resource::getTextureDDS(const std::string& path, float *aspect_ratio)
|
||||
if(height < 1) height = 1;
|
||||
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// remember to avoid openning the same resource twice
|
||||
textureIndex[path] = textureID;
|
||||
@@ -263,19 +267,20 @@ uint Resource::getTextureImage(const std::string& path, float *aspect_ratio)
|
||||
}
|
||||
if (h == 0){
|
||||
Log::Error("Invalid image in ressource %s", std::string(path).c_str());
|
||||
stbi_image_free(img);
|
||||
return 0;
|
||||
}
|
||||
ar = static_cast<float>(w) / static_cast<float>(h);
|
||||
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture( GL_TEXTURE_2D, textureID);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glBindTexture( GL_TEXTURE_2D, textureID);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w, h);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// free memory
|
||||
stbi_image_free(img);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Visitor.h"
|
||||
#include "GarbageVisitor.h"
|
||||
#include "Log.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "GlmToolkit.h"
|
||||
#include "SessionVisitor.h"
|
||||
|
||||
@@ -25,7 +26,7 @@ static int num_nodes_ = 0;
|
||||
Node::Node() : initialized_(false), visible_(true), refcount_(0)
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
transform_ = glm::identity<glm::mat4>();
|
||||
scale_ = glm::vec3(1.f);
|
||||
@@ -56,7 +57,7 @@ void Node::clearCallbacks()
|
||||
}
|
||||
}
|
||||
|
||||
void Node::copyTransform(Node *other)
|
||||
void Node::copyTransform(const Node *other)
|
||||
{
|
||||
if (!other)
|
||||
return;
|
||||
|
||||
6
Scene.h
6
Scene.h
@@ -13,6 +13,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "UpdateCallback.h"
|
||||
#include "GlmToolkit.h"
|
||||
|
||||
// Forward declare classes referenced
|
||||
class Shader;
|
||||
@@ -64,7 +65,7 @@ public:
|
||||
// accept all kind of visitors
|
||||
virtual void accept (Visitor& v);
|
||||
|
||||
void copyTransform (Node *other);
|
||||
void copyTransform (const Node *other);
|
||||
|
||||
// public members, to manipulate with care
|
||||
bool visible_;
|
||||
@@ -239,6 +240,9 @@ class Scene {
|
||||
|
||||
public:
|
||||
Scene();
|
||||
// non assignable class
|
||||
Scene(Scene const&) = delete;
|
||||
Scene& operator=(Scene const&) = delete;
|
||||
~Scene();
|
||||
|
||||
void accept (Visitor& v);
|
||||
|
||||
@@ -61,7 +61,7 @@ void Selection::set(SourceList l)
|
||||
{
|
||||
clear();
|
||||
|
||||
for(auto it = l.begin(); it != l.end(); it++)
|
||||
for(auto it = l.begin(); it != l.end(); ++it)
|
||||
(*it)->setMode(Source::SELECTED);
|
||||
|
||||
l.sort();
|
||||
@@ -71,7 +71,7 @@ void Selection::set(SourceList l)
|
||||
|
||||
void Selection::add(SourceList l)
|
||||
{
|
||||
for(auto it = l.begin(); it != l.end(); it++)
|
||||
for(auto it = l.begin(); it != l.end(); ++it)
|
||||
(*it)->setMode(Source::SELECTED);
|
||||
|
||||
// generate new set as union of current selection and give list
|
||||
@@ -86,7 +86,7 @@ void Selection::add(SourceList l)
|
||||
|
||||
void Selection::remove(SourceList l)
|
||||
{
|
||||
for(auto it = l.begin(); it != l.end(); it++)
|
||||
for(auto it = l.begin(); it != l.end(); ++it)
|
||||
(*it)->setMode(Source::VISIBLE);
|
||||
|
||||
// generate new set as difference of current selection and give list
|
||||
@@ -98,7 +98,7 @@ void Selection::remove(SourceList l)
|
||||
|
||||
void Selection::clear()
|
||||
{
|
||||
for(auto it = selection_.begin(); it != selection_.end(); it++)
|
||||
for(auto it = selection_.begin(); it != selection_.end(); ++it)
|
||||
(*it)->setMode(Source::VISIBLE);
|
||||
|
||||
selection_.clear();
|
||||
|
||||
71
Session.cpp
71
Session.cpp
@@ -1,6 +1,8 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "defines.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "Session.h"
|
||||
@@ -11,7 +13,12 @@
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f), filename_("")
|
||||
SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_string(BaseToolkit::uniqueId())),
|
||||
text(t), large(l), stick(s), pos(glm::vec2(520.f, 30.f)), size(glm::vec2(220.f, 220.f))
|
||||
{
|
||||
}
|
||||
|
||||
Session::Session() : active_(true), filename_(""), failedSource_(nullptr), fading_target_(0.f)
|
||||
{
|
||||
config_[View::RENDERING] = new Group;
|
||||
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
|
||||
@@ -31,6 +38,8 @@ Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f),
|
||||
config_[View::TEXTURE] = new Group;
|
||||
config_[View::TEXTURE]->scale_ = Settings::application.views[View::TEXTURE].default_scale;
|
||||
config_[View::TEXTURE]->translation_ = Settings::application.views[View::TEXTURE].default_translation;
|
||||
|
||||
snapshots_.xmlDoc_ = new tinyxml2::XMLDocument;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,13 +63,16 @@ Session::~Session()
|
||||
delete config_[View::LAYER];
|
||||
delete config_[View::MIXING];
|
||||
delete config_[View::TEXTURE];
|
||||
|
||||
snapshots_.keys_.clear();
|
||||
delete snapshots_.xmlDoc_;
|
||||
}
|
||||
|
||||
void Session::setActive (bool on)
|
||||
{
|
||||
if (active_ != on) {
|
||||
active_ = on;
|
||||
for(auto it = sources_.begin(); it != sources_.end(); it++) {
|
||||
for(auto it = sources_.begin(); it != sources_.end(); ++it) {
|
||||
(*it)->setActive(active_);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +87,7 @@ void Session::update(float dt)
|
||||
|
||||
// pre-render of all sources
|
||||
failedSource_ = nullptr;
|
||||
bool ready = true;
|
||||
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){
|
||||
|
||||
// ensure the RenderSource is rendering this session
|
||||
@@ -86,6 +99,8 @@ void Session::update(float dt)
|
||||
failedSource_ = (*it);
|
||||
}
|
||||
else {
|
||||
if ( !(*it)->ready() )
|
||||
ready = false;
|
||||
// render the source
|
||||
(*it)->render();
|
||||
// update the source
|
||||
@@ -118,9 +133,11 @@ void Session::update(float dt)
|
||||
// draw render view in Frame Buffer
|
||||
render_.draw();
|
||||
|
||||
// draw the thumbnail only after all sources are ready
|
||||
if (ready)
|
||||
render_.drawThumbnail();
|
||||
}
|
||||
|
||||
|
||||
SourceList::iterator Session::addSource(Source *s)
|
||||
{
|
||||
// lock before change
|
||||
@@ -172,7 +189,6 @@ SourceList::iterator Session::deleteSource(Source *s)
|
||||
return its;
|
||||
}
|
||||
|
||||
|
||||
void Session::removeSource(Source *s)
|
||||
{
|
||||
// lock before change
|
||||
@@ -195,7 +211,6 @@ void Session::removeSource(Source *s)
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
|
||||
Source *Session::popSource()
|
||||
{
|
||||
Source *s = nullptr;
|
||||
@@ -279,6 +294,19 @@ SourceIdList Session::getIdList() const
|
||||
return ids(sources_);
|
||||
}
|
||||
|
||||
std::list<std::string> Session::getNameList(uint64_t exceptid) const
|
||||
{
|
||||
std::list<std::string> namelist;
|
||||
|
||||
for( SourceList::const_iterator it = sources_.cbegin(); it != sources_.cend(); ++it) {
|
||||
if ( (*it)->id() != exceptid )
|
||||
namelist.push_back( (*it)->name() );
|
||||
}
|
||||
|
||||
return namelist;
|
||||
}
|
||||
|
||||
|
||||
bool Session::empty() const
|
||||
{
|
||||
return sources_.empty();
|
||||
@@ -302,7 +330,7 @@ int Session::index(SourceList::iterator it) const
|
||||
{
|
||||
int index = -1;
|
||||
int count = 0;
|
||||
for(auto i = sources_.begin(); i != sources_.end(); i++, count++) {
|
||||
for(auto i = sources_.begin(); i != sources_.end(); ++i, ++count) {
|
||||
if ( i == it ) {
|
||||
index = count;
|
||||
break;
|
||||
@@ -335,7 +363,7 @@ bool Session::canlink (SourceList sources)
|
||||
// verify that all sources given are valid in the sesion
|
||||
validate(sources);
|
||||
|
||||
for (auto it = sources.begin(); it != sources.end(); it++) {
|
||||
for (auto it = sources.begin(); it != sources.end(); ++it) {
|
||||
// this source is linked
|
||||
if ( (*it)->mixingGroup() != nullptr ) {
|
||||
// askt its group to detach it
|
||||
@@ -370,7 +398,7 @@ void Session::unlink (SourceList sources)
|
||||
validate(sources);
|
||||
|
||||
// brute force : detach all given sources
|
||||
for (auto it = sources.begin(); it != sources.end(); it++) {
|
||||
for (auto it = sources.begin(); it != sources.end(); ++it) {
|
||||
// this source is linked
|
||||
if ( (*it)->mixingGroup() != nullptr ) {
|
||||
// askt its group to detach it
|
||||
@@ -379,17 +407,39 @@ void Session::unlink (SourceList sources)
|
||||
}
|
||||
}
|
||||
|
||||
void Session::addNote(SessionNote note)
|
||||
{
|
||||
notes_.push_back( note );
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::beginNotes ()
|
||||
{
|
||||
return notes_.begin();
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::endNotes ()
|
||||
{
|
||||
return notes_.end();
|
||||
}
|
||||
|
||||
std::list<SessionNote>::iterator Session::deleteNote (std::list<SessionNote>::iterator n)
|
||||
{
|
||||
if (n != notes_.end())
|
||||
return notes_.erase(n);
|
||||
|
||||
return notes_.end();
|
||||
}
|
||||
|
||||
std::list<SourceList> Session::getMixingGroups () const
|
||||
{
|
||||
std::list<SourceList> lmg;
|
||||
|
||||
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++)
|
||||
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); ++group_it)
|
||||
lmg.push_back( (*group_it)->getCopy() );
|
||||
|
||||
return lmg;
|
||||
}
|
||||
|
||||
|
||||
std::list<MixingGroup *>::iterator Session::deleteMixingGroup (std::list<MixingGroup *>::iterator g)
|
||||
{
|
||||
if (g != mixing_groups_.end()) {
|
||||
@@ -419,7 +469,6 @@ void Session::unlock()
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
|
||||
void Session::validate (SourceList &sources)
|
||||
{
|
||||
// verify that all sources given are valid in the sesion
|
||||
|
||||
44
Session.h
44
Session.h
@@ -3,12 +3,33 @@
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "SourceList.h"
|
||||
#include "RenderView.h"
|
||||
#include "Source.h"
|
||||
|
||||
namespace tinyxml2 {
|
||||
class XMLDocument;
|
||||
}
|
||||
class FrameGrabber;
|
||||
class MixingGroup;
|
||||
|
||||
struct SessionNote
|
||||
{
|
||||
std::string label;
|
||||
std::string text;
|
||||
bool large;
|
||||
int stick;
|
||||
glm::vec2 pos;
|
||||
glm::vec2 size;
|
||||
|
||||
SessionNote(const std::string &t = "", bool l = false, int s = 0);
|
||||
};
|
||||
|
||||
struct SessionSnapshots {
|
||||
|
||||
tinyxml2::XMLDocument *xmlDoc_;
|
||||
std::list<uint64_t> keys_;
|
||||
};
|
||||
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
@@ -45,6 +66,8 @@ public:
|
||||
SourceList::iterator find (uint64_t id);
|
||||
SourceIdList getIdList() const;
|
||||
|
||||
std::list<std::string> getNameList(uint64_t exceptid=0) const;
|
||||
|
||||
SourceList::iterator at (int index);
|
||||
int index (SourceList::iterator it) const;
|
||||
void move (int current_index, int target_index);
|
||||
@@ -62,6 +85,9 @@ public:
|
||||
// get frame result of render
|
||||
inline FrameBuffer *frame () const { return render_.frame(); }
|
||||
|
||||
// get thumbnail image
|
||||
inline FrameBufferImage *thumbnail () { return render_.thumbnail(); }
|
||||
|
||||
// configure rendering resolution
|
||||
void setResolution (glm::vec3 resolution, bool useAlpha = false);
|
||||
|
||||
@@ -76,6 +102,12 @@ public:
|
||||
void setFilename (const std::string &filename) { filename_ = filename; }
|
||||
std::string filename () const { return filename_; }
|
||||
|
||||
// get the list of notes
|
||||
void addNote(SessionNote note = SessionNote());
|
||||
std::list<SessionNote>::iterator beginNotes ();
|
||||
std::list<SessionNote>::iterator endNotes ();
|
||||
std::list<SessionNote>::iterator deleteNote (std::list<SessionNote>::iterator n);
|
||||
|
||||
// get the list of sources in mixing groups
|
||||
std::list<SourceList> getMixingGroups () const;
|
||||
// returns true if something can be done to create a mixing group
|
||||
@@ -91,23 +123,27 @@ public:
|
||||
std::list<MixingGroup *>::iterator endMixingGroup ();
|
||||
std::list<MixingGroup *>::iterator deleteMixingGroup (std::list<MixingGroup *>::iterator g);
|
||||
|
||||
// snapshots
|
||||
SessionSnapshots * const snapshots () { return &snapshots_; }
|
||||
|
||||
// lock and unlock access (e.g. while saving)
|
||||
void lock ();
|
||||
void unlock ();
|
||||
|
||||
protected:
|
||||
bool active_;
|
||||
RenderView render_;
|
||||
std::string filename_;
|
||||
Source *failedSource_;
|
||||
SourceList sources_;
|
||||
void validate(SourceList &sources);
|
||||
|
||||
std::list<SessionNote> notes_;
|
||||
std::list<MixingGroup *> mixing_groups_;
|
||||
std::map<View::Mode, Group*> config_;
|
||||
bool active_;
|
||||
std::list<FrameGrabber *> grabbers_;
|
||||
SessionSnapshots snapshots_;
|
||||
float fading_target_;
|
||||
std::mutex access_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "SessionCreator.h"
|
||||
#include <sstream>
|
||||
|
||||
#include "Log.h"
|
||||
#include "defines.h"
|
||||
@@ -12,43 +12,49 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "Session.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "SystemToolkit.h"
|
||||
|
||||
#include <tinyxml2.h>
|
||||
#include "tinyxml2Toolkit.h"
|
||||
using namespace tinyxml2;
|
||||
|
||||
#include "SessionCreator.h"
|
||||
|
||||
std::string SessionCreator::info(const std::string& filename)
|
||||
SessionInformation SessionCreator::info(const std::string& filename)
|
||||
{
|
||||
std::string ret = "";
|
||||
SessionInformation ret;
|
||||
|
||||
// if the file exists
|
||||
if (SystemToolkit::file_exists(filename)) {
|
||||
// impose C locale
|
||||
setlocale(LC_ALL, "C");
|
||||
// try to load the file
|
||||
XMLDocument doc;
|
||||
XMLError eResult = doc.LoadFile(filename.c_str());
|
||||
// silently ignore on error
|
||||
if ( !XMLResultError(eResult, false)) {
|
||||
|
||||
XMLElement *header = doc.FirstChildElement(APP_NAME);
|
||||
if (header != nullptr && header->Attribute("date") != 0) {
|
||||
const XMLElement *header = doc.FirstChildElement(APP_NAME);
|
||||
if (header != nullptr) {
|
||||
int s = header->IntAttribute("size");
|
||||
ret = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
|
||||
ret.description = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
|
||||
const char *att_string = header->Attribute("resolution");
|
||||
if (att_string)
|
||||
ret += std::string( att_string ) + "\n";
|
||||
ret.description += std::string( att_string ) + "\n";
|
||||
att_string = header->Attribute("date");
|
||||
if (att_string) {
|
||||
std::string date( att_string );
|
||||
ret += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
|
||||
ret += date.substr(8,2) + ":" + date.substr(10,2);
|
||||
ret.description += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
|
||||
ret.description += date.substr(8,2) + ":" + date.substr(10,2);
|
||||
}
|
||||
|
||||
}
|
||||
const XMLElement *session = doc.FirstChildElement("Session");
|
||||
if (session != nullptr ) {
|
||||
ret.thumbnail = XMLToImage(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,9 +103,15 @@ void SessionCreator::load(const std::string& filename)
|
||||
|
||||
// create groups
|
||||
std::list< SourceList > groups = getMixingGroups();
|
||||
for (auto group_it = groups.begin(); group_it != groups.end(); group_it++)
|
||||
for (auto group_it = groups.begin(); group_it != groups.end(); ++group_it)
|
||||
session_->link( *group_it );
|
||||
|
||||
// load snapshots
|
||||
loadSnapshots( xmlDoc_.FirstChildElement("Snapshots") );
|
||||
|
||||
// load notes
|
||||
loadNotes( xmlDoc_.FirstChildElement("Notes") );
|
||||
|
||||
// all good
|
||||
session_->setFilename(filename);
|
||||
}
|
||||
@@ -107,7 +119,7 @@ void SessionCreator::load(const std::string& filename)
|
||||
|
||||
void SessionCreator::loadConfig(XMLElement *viewsNode)
|
||||
{
|
||||
if (viewsNode != nullptr) {
|
||||
if (viewsNode != nullptr && session_ != nullptr) {
|
||||
// ok, ready to read views
|
||||
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Mixing"), *session_->config(View::MIXING));
|
||||
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Geometry"), *session_->config(View::GEOMETRY));
|
||||
@@ -117,6 +129,48 @@ void SessionCreator::loadConfig(XMLElement *viewsNode)
|
||||
}
|
||||
}
|
||||
|
||||
void SessionCreator::loadSnapshots(XMLElement *snapshotsNode)
|
||||
{
|
||||
if (snapshotsNode != nullptr && session_ != nullptr) {
|
||||
|
||||
const XMLElement* N = snapshotsNode->FirstChildElement();
|
||||
for( ; N ; N = N->NextSiblingElement()) {
|
||||
|
||||
char c;
|
||||
u_int64_t id = 0;
|
||||
std::istringstream nodename( N->Name() );
|
||||
nodename >> c >> id;
|
||||
|
||||
session_->snapshots()->keys_.push_back(id);
|
||||
session_->snapshots()->xmlDoc_->InsertEndChild( N->DeepClone(session_->snapshots()->xmlDoc_) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SessionCreator::loadNotes(XMLElement *notesNode)
|
||||
{
|
||||
if (notesNode != nullptr && session_ != nullptr) {
|
||||
|
||||
XMLElement* note = notesNode->FirstChildElement("Note");
|
||||
for( ; note ; note = note->NextSiblingElement())
|
||||
{
|
||||
SessionNote N;
|
||||
|
||||
note->QueryBoolAttribute("large", &N.large);
|
||||
note->QueryIntAttribute("stick", &N.stick);
|
||||
XMLElement *posNode = note->FirstChildElement("pos");
|
||||
if (posNode) tinyxml2::XMLElementToGLM( posNode->FirstChildElement("vec2"), N.pos);
|
||||
XMLElement *sizeNode = note->FirstChildElement("size");
|
||||
if (sizeNode) tinyxml2::XMLElementToGLM( sizeNode->FirstChildElement("vec2"), N.size);
|
||||
XMLElement* contentNode = note->FirstChildElement("text");
|
||||
if (contentNode) N.text = std::string ( contentNode->GetText() );
|
||||
|
||||
session_->addNote(N);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SessionLoader::SessionLoader(): Visitor(),
|
||||
session_(nullptr), xmlCurrent_(nullptr), recursion_(0)
|
||||
{
|
||||
@@ -146,10 +200,10 @@ std::list< SourceList > SessionLoader::getMixingGroups() const
|
||||
std::list< SourceList > groups_new_sources_id;
|
||||
|
||||
// perform conversion from xml id to new id
|
||||
for (auto git = groups_sources_id_.begin(); git != groups_sources_id_.end(); git++)
|
||||
for (auto git = groups_sources_id_.begin(); git != groups_sources_id_.end(); ++git)
|
||||
{
|
||||
SourceList new_sources;
|
||||
for (auto sit = (*git).begin(); sit != (*git).end(); sit++ ) {
|
||||
for (auto sit = (*git).begin(); sit != (*git).end(); ++sit ) {
|
||||
if (sources_id_.count(*sit) > 0)
|
||||
new_sources.push_back( sources_id_.at(*sit) );
|
||||
}
|
||||
@@ -194,25 +248,28 @@ void SessionLoader::load(XMLElement *sessionNode)
|
||||
if (!pType)
|
||||
continue;
|
||||
if ( std::string(pType) == "MediaSource") {
|
||||
load_source = new MediaSource;
|
||||
load_source = new MediaSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "SessionSource") {
|
||||
load_source = new SessionFileSource;
|
||||
load_source = new SessionFileSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "GroupSource") {
|
||||
load_source = new SessionGroupSource;
|
||||
load_source = new SessionGroupSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "RenderSource") {
|
||||
load_source = new RenderSource;
|
||||
load_source = new RenderSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "PatternSource") {
|
||||
load_source = new PatternSource;
|
||||
load_source = new PatternSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "DeviceSource") {
|
||||
load_source = new DeviceSource;
|
||||
load_source = new DeviceSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource;
|
||||
load_source = new NetworkSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "MultiFileSource") {
|
||||
load_source = new MultiFileSource(id_xml_);
|
||||
}
|
||||
|
||||
// skip failed (including clones)
|
||||
@@ -255,12 +312,17 @@ void SessionLoader::load(XMLElement *sessionNode)
|
||||
// clone from given origin
|
||||
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
|
||||
if (originNode) {
|
||||
std::string sourcename = std::string ( originNode->GetText() );
|
||||
SourceList::iterator origin = session_->find(sourcename);
|
||||
uint64_t id_origin_ = 0;
|
||||
originNode->QueryUnsigned64Attribute("id", &id_origin_);
|
||||
SourceList::iterator origin;
|
||||
if (id_origin_ > 0)
|
||||
origin = session_->find(id_origin_);
|
||||
else
|
||||
origin = session_->find( std::string ( originNode->GetText() ) );
|
||||
// found the orign source
|
||||
if (origin != session_->end()) {
|
||||
// create a new source of type Clone
|
||||
Source *clone_source = (*origin)->clone();
|
||||
Source *clone_source = (*origin)->clone(id_xml_);
|
||||
|
||||
// add source to session
|
||||
session_->addSource(clone_source);
|
||||
@@ -291,11 +353,12 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
||||
Source *load_source = nullptr;
|
||||
bool is_clone = false;
|
||||
|
||||
SourceList::iterator sit = session_->end();
|
||||
uint64_t id__ = 0;
|
||||
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
|
||||
|
||||
// check if a source with the given id exists in the session
|
||||
SourceList::iterator sit = session_->end();
|
||||
if (mode == CLONE) {
|
||||
uint64_t id__ = 0;
|
||||
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
|
||||
sit = session_->find(id__);
|
||||
}
|
||||
|
||||
@@ -305,35 +368,43 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
||||
const char *pType = xmlCurrent_->Attribute("type");
|
||||
if (pType) {
|
||||
if ( std::string(pType) == "MediaSource") {
|
||||
load_source = new MediaSource;
|
||||
load_source = new MediaSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "SessionSource") {
|
||||
load_source = new SessionFileSource;
|
||||
load_source = new SessionFileSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "GroupSource") {
|
||||
load_source = new SessionGroupSource;
|
||||
load_source = new SessionGroupSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "RenderSource") {
|
||||
load_source = new RenderSource;
|
||||
load_source = new RenderSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "PatternSource") {
|
||||
load_source = new PatternSource;
|
||||
load_source = new PatternSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "DeviceSource") {
|
||||
load_source = new DeviceSource;
|
||||
load_source = new DeviceSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource;
|
||||
load_source = new NetworkSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "MultiFileSource") {
|
||||
load_source = new MultiFileSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "CloneSource") {
|
||||
// clone from given origin
|
||||
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
|
||||
if (originNode) {
|
||||
std::string sourcename = std::string ( originNode->GetText() );
|
||||
SourceList::iterator origin = session_->find(sourcename);
|
||||
uint64_t id_origin_ = 0;
|
||||
originNode->QueryUnsigned64Attribute("id", &id_origin_);
|
||||
SourceList::iterator origin;
|
||||
if (id_origin_ > 0)
|
||||
origin = session_->find(id_origin_);
|
||||
else
|
||||
origin = session_->find( std::string ( originNode->GetText() ) );
|
||||
// found the orign source
|
||||
if (origin != session_->end())
|
||||
load_source = (*origin)->clone();
|
||||
load_source = (*origin)->clone(id__);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,7 +427,7 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
||||
}
|
||||
|
||||
|
||||
bool SessionLoader::isClipboard(std::string clipboard)
|
||||
bool SessionLoader::isClipboard(const std::string &clipboard)
|
||||
{
|
||||
if (clipboard.size() > 6 && clipboard.substr(0, 6) == "<" APP_NAME )
|
||||
return true;
|
||||
@@ -364,7 +435,7 @@ bool SessionLoader::isClipboard(std::string clipboard)
|
||||
return false;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* SessionLoader::firstSourceElement(std::string clipboard, XMLDocument &xmlDoc)
|
||||
tinyxml2::XMLElement* SessionLoader::firstSourceElement(const std::string &clipboard, XMLDocument &xmlDoc)
|
||||
{
|
||||
tinyxml2::XMLElement* sourceNode = nullptr;
|
||||
|
||||
@@ -385,7 +456,7 @@ tinyxml2::XMLElement* SessionLoader::firstSourceElement(std::string clipboard, X
|
||||
return sourceNode;
|
||||
}
|
||||
|
||||
void SessionLoader::applyImageProcessing(const Source &s, std::string clipboard)
|
||||
void SessionLoader::applyImageProcessing(const Source &s, const std::string &clipboard)
|
||||
{
|
||||
if ( !isClipboard(clipboard) )
|
||||
return;
|
||||
@@ -449,28 +520,85 @@ void SessionLoader::applyImageProcessing(const Source &s, std::string clipboard)
|
||||
//// s.processingShader()->accept(loader);
|
||||
//}
|
||||
|
||||
void SessionLoader::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
|
||||
void SessionLoader::XMLToNode(const tinyxml2::XMLElement *xml, Node &n)
|
||||
{
|
||||
if (xml != nullptr){
|
||||
XMLElement *node = xml->FirstChildElement("Node");
|
||||
const XMLElement *node = xml->FirstChildElement("Node");
|
||||
if ( !node || std::string(node->Name()).find("Node") == std::string::npos )
|
||||
return;
|
||||
|
||||
XMLElement *scaleNode = node->FirstChildElement("scale");
|
||||
const XMLElement *scaleNode = node->FirstChildElement("scale");
|
||||
if (scaleNode)
|
||||
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"), n.scale_);
|
||||
XMLElement *translationNode = node->FirstChildElement("translation");
|
||||
const XMLElement *translationNode = node->FirstChildElement("translation");
|
||||
if (translationNode)
|
||||
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"), n.translation_);
|
||||
XMLElement *rotationNode = node->FirstChildElement("rotation");
|
||||
const XMLElement *rotationNode = node->FirstChildElement("rotation");
|
||||
if (rotationNode)
|
||||
tinyxml2::XMLElementToGLM( rotationNode->FirstChildElement("vec3"), n.rotation_);
|
||||
XMLElement *cropNode = node->FirstChildElement("crop");
|
||||
const XMLElement *cropNode = node->FirstChildElement("crop");
|
||||
if (cropNode)
|
||||
tinyxml2::XMLElementToGLM( cropNode->FirstChildElement("vec3"), n.crop_);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionLoader::XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s)
|
||||
{
|
||||
SessionLoader::XMLToNode(xml->FirstChildElement("Mixing"), *s.group(View::MIXING) );
|
||||
SessionLoader::XMLToNode(xml->FirstChildElement("Geometry"),*s.group(View::GEOMETRY) );
|
||||
SessionLoader::XMLToNode(xml->FirstChildElement("Layer"), *s.group(View::LAYER) );
|
||||
SessionLoader::XMLToNode(xml->FirstChildElement("Texture"), *s.group(View::TEXTURE) );
|
||||
|
||||
SessionLoader v(nullptr);
|
||||
v.xmlCurrent_ = xml->FirstChildElement("ImageProcessing");
|
||||
if (v.xmlCurrent_)
|
||||
s.processingShader()->accept(v);
|
||||
|
||||
}
|
||||
|
||||
FrameBufferImage *SessionLoader::XMLToImage(const XMLElement *xml)
|
||||
{
|
||||
FrameBufferImage *i = nullptr;
|
||||
|
||||
if (xml != nullptr){
|
||||
// if there is an Image mask stored
|
||||
const XMLElement* imageNode = xml->FirstChildElement("Image");
|
||||
if (imageNode) {
|
||||
// get theoretical image size
|
||||
int w = 0, h = 0;
|
||||
imageNode->QueryIntAttribute("width", &w);
|
||||
imageNode->QueryIntAttribute("height", &h);
|
||||
// if there is an internal array of data
|
||||
const XMLElement* array = imageNode->FirstChildElement("array");
|
||||
if (array) {
|
||||
// create a temporary jpeg with size of the array
|
||||
FrameBufferImage::jpegBuffer jpgimg;
|
||||
array->QueryUnsignedAttribute("len", &jpgimg.len);
|
||||
// ok, we got a size of data to load
|
||||
if (jpgimg.len>0) {
|
||||
// allocate jpeg buffer
|
||||
jpgimg.buffer = (unsigned char*) malloc(jpgimg.len);
|
||||
// actual decoding of array
|
||||
if (XMLElementDecodeArray(array, jpgimg.buffer, jpgimg.len) ) {
|
||||
// create and set the image from jpeg
|
||||
i = new FrameBufferImage(jpgimg);
|
||||
// failed if wrong size
|
||||
if ( (w>0 && h>0) && (i->width != w || i->height != h) ) {
|
||||
delete i;
|
||||
i = nullptr;
|
||||
}
|
||||
}
|
||||
// free temporary buffer
|
||||
if (jpgimg.buffer)
|
||||
free(jpgimg.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void SessionLoader::visit(Node &n)
|
||||
{
|
||||
XMLToNode(xmlCurrent_, n);
|
||||
@@ -634,35 +762,15 @@ void SessionLoader::visit (Source& s)
|
||||
}
|
||||
|
||||
xmlCurrent_ = sourceNode->FirstChildElement("Blending");
|
||||
if (xmlCurrent_) s.blendingShader()->accept(*this);
|
||||
if (xmlCurrent_)
|
||||
s.blendingShader()->accept(*this);
|
||||
|
||||
xmlCurrent_ = sourceNode->FirstChildElement("Mask");
|
||||
if (xmlCurrent_) {
|
||||
// read the mask shader attributes
|
||||
s.maskShader()->accept(*this);
|
||||
// if there is an Image mask stored
|
||||
XMLElement* imageNode = xmlCurrent_->FirstChildElement("Image");
|
||||
if (imageNode) {
|
||||
// if there is an internal array of data
|
||||
XMLElement* array = imageNode->FirstChildElement("array");
|
||||
if (array) {
|
||||
// create a temporary jpeg with size of the array
|
||||
FrameBufferImage::jpegBuffer jpgimg;
|
||||
array->QueryUnsignedAttribute("len", &jpgimg.len);
|
||||
// ok, we got a size of data to load
|
||||
if (jpgimg.len>0) {
|
||||
// allocate jpeg buffer
|
||||
jpgimg.buffer = (unsigned char*) malloc(jpgimg.len);
|
||||
// actual decoding of array
|
||||
if (XMLElementDecodeArray(array, jpgimg.buffer, jpgimg.len) )
|
||||
// create and set the image from jpeg
|
||||
s.setMask(new FrameBufferImage(jpgimg));
|
||||
// free temporary buffer
|
||||
if (jpgimg.buffer)
|
||||
free(jpgimg.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
// set the mask from jpeg
|
||||
s.setMask( SessionLoader::XMLToImage(xmlCurrent_) );
|
||||
}
|
||||
|
||||
xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing");
|
||||
@@ -778,27 +886,48 @@ void SessionLoader::visit (NetworkSource& s)
|
||||
s.setConnection(connect);
|
||||
}
|
||||
|
||||
// dirty hack wich can be useful ?
|
||||
|
||||
//class DummySource : public Source
|
||||
//{
|
||||
// friend class SessionLoader;
|
||||
//public:
|
||||
// uint texture() const override { return 0; }
|
||||
// bool failed() const override { return true; }
|
||||
// void accept (Visitor& v) override { Source::accept(v); }
|
||||
//protected:
|
||||
// DummySource() : Source() {}
|
||||
// void init() override {}
|
||||
//};
|
||||
void SessionLoader::visit (MultiFileSource& s)
|
||||
{
|
||||
XMLElement* seq = xmlCurrent_->FirstChildElement("Sequence");
|
||||
|
||||
//Source *SessionLoader::createDummy(tinyxml2::XMLElement *sourceNode)
|
||||
//{
|
||||
// SessionLoader loader;
|
||||
// loader.xmlCurrent_ = sourceNode;
|
||||
// DummySource *dum = new DummySource;
|
||||
// dum->accept(loader);
|
||||
// return dum;
|
||||
//}
|
||||
if (seq) {
|
||||
|
||||
MultiFileSequence sequence;
|
||||
sequence.location = std::string ( seq->GetText() );
|
||||
seq->QueryIntAttribute("min", &sequence.min);
|
||||
seq->QueryIntAttribute("max", &sequence.max);
|
||||
seq->QueryUnsignedAttribute("width", &sequence.width);
|
||||
seq->QueryUnsignedAttribute("height", &sequence.height);
|
||||
const char *codec = seq->Attribute("codec");
|
||||
if (codec)
|
||||
sequence.codec = std::string(codec);
|
||||
|
||||
uint fps = 0;
|
||||
seq->QueryUnsignedAttribute("fps", &fps);
|
||||
|
||||
// different sequence
|
||||
if ( sequence != s.sequence() ) {
|
||||
s.setSequence( sequence, fps);
|
||||
}
|
||||
// same sequence, different framerate
|
||||
else if ( fps != s.framerate() ) {
|
||||
s.setFramerate( fps );
|
||||
}
|
||||
|
||||
int begin = -1;
|
||||
seq->QueryIntAttribute("begin", &begin);
|
||||
int end = INT_MAX;
|
||||
seq->QueryIntAttribute("end", &end);
|
||||
if ( begin != s.begin() || end != s.end() )
|
||||
s.setRange(begin, end);
|
||||
|
||||
bool loop = true;
|
||||
seq->QueryBoolAttribute("loop", &loop);
|
||||
if ( loop != s.loop() )
|
||||
s.setLoop(loop);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "SourceList.h"
|
||||
|
||||
class Session;
|
||||
class FrameBufferImage;
|
||||
|
||||
|
||||
class SessionLoader : public Visitor {
|
||||
@@ -29,10 +30,10 @@ public:
|
||||
} Mode;
|
||||
Source *createSource(tinyxml2::XMLElement *sourceNode, Mode mode = CLONE);
|
||||
|
||||
static bool isClipboard(std::string clipboard);
|
||||
static tinyxml2::XMLElement* firstSourceElement(std::string clipboard, tinyxml2::XMLDocument &xmlDoc);
|
||||
static void applyImageProcessing(const Source &s, std::string clipboard);
|
||||
//TODO static void applyMask(const Source &s, std::string clipboard);
|
||||
static bool isClipboard(const std::string &clipboard);
|
||||
static tinyxml2::XMLElement* firstSourceElement(const std::string &clipboard, tinyxml2::XMLDocument &xmlDoc);
|
||||
static void applyImageProcessing(const Source &s, const std::string &clipboard);
|
||||
//TODO static void applyMask(const Source &s, const std::string &clipboard);
|
||||
|
||||
// Elements of Scene
|
||||
void visit (Node& n) override;
|
||||
@@ -57,6 +58,11 @@ public:
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
|
||||
static void XMLToNode(const tinyxml2::XMLElement *xml, Node &n);
|
||||
static void XMLToSourcecore(tinyxml2::XMLElement *xml, SourceCore &s);
|
||||
static FrameBufferImage *XMLToImage(const tinyxml2::XMLElement *xml);
|
||||
|
||||
protected:
|
||||
// result created session
|
||||
@@ -70,7 +76,15 @@ protected:
|
||||
// list of groups (lists of xml source id)
|
||||
std::list< SourceIdList > groups_sources_id_;
|
||||
|
||||
static void XMLToNode(tinyxml2::XMLElement *xml, Node &n);
|
||||
};
|
||||
|
||||
struct SessionInformation {
|
||||
std::string description;
|
||||
FrameBufferImage *thumbnail;
|
||||
SessionInformation() {
|
||||
description = "";
|
||||
thumbnail = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class SessionCreator : public SessionLoader {
|
||||
@@ -78,13 +92,15 @@ class SessionCreator : public SessionLoader {
|
||||
tinyxml2::XMLDocument xmlDoc_;
|
||||
|
||||
void loadConfig(tinyxml2::XMLElement *viewsNode);
|
||||
void loadNotes(tinyxml2::XMLElement *notesNode);
|
||||
void loadSnapshots(tinyxml2::XMLElement *snapshotNode);
|
||||
|
||||
public:
|
||||
SessionCreator(int recursion = 0);
|
||||
|
||||
void load(const std::string& filename);
|
||||
|
||||
static std::string info(const std::string& filename);
|
||||
static SessionInformation info(const std::string& filename);
|
||||
};
|
||||
|
||||
#endif // SESSIONCREATOR_H
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
#include "SessionSource.h"
|
||||
|
||||
@@ -17,7 +18,7 @@
|
||||
#include "Mixer.h"
|
||||
|
||||
|
||||
SessionSource::SessionSource() : Source(), failed_(false)
|
||||
SessionSource::SessionSource(uint64_t id) : Source(id), failed_(false)
|
||||
{
|
||||
session_ = new Session;
|
||||
}
|
||||
@@ -37,8 +38,8 @@ Session *SessionSource::detach()
|
||||
// work on a new session
|
||||
session_ = new Session;
|
||||
|
||||
// make disabled
|
||||
initialized_ = false;
|
||||
// un-ready
|
||||
ready_ = false;
|
||||
|
||||
// ask to delete me
|
||||
failed_ = true;
|
||||
@@ -90,7 +91,7 @@ void SessionSource::update(float dt)
|
||||
}
|
||||
|
||||
|
||||
SessionFileSource::SessionFileSource() : SessionSource(), path_("")
|
||||
SessionFileSource::SessionFileSource(uint64_t id) : SessionSource(id), path_(""), initialized_(false), wait_for_sources_(false)
|
||||
{
|
||||
// specific node for transition view
|
||||
groups_[View::TRANSITION]->visible_ = false;
|
||||
@@ -124,7 +125,6 @@ SessionFileSource::SessionFileSource() : SessionSource(), path_("")
|
||||
symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
|
||||
wait_for_sources_ = false;
|
||||
}
|
||||
|
||||
void SessionFileSource::load(const std::string &p, uint recursion)
|
||||
@@ -148,6 +148,9 @@ void SessionFileSource::load(const std::string &p, uint recursion)
|
||||
sessionLoader_ = std::async(std::launch::async, Session::load, path_, recursion);
|
||||
Log::Notify("Opening %s", p.c_str());
|
||||
}
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
void SessionFileSource::init()
|
||||
@@ -163,26 +166,18 @@ void SessionFileSource::init()
|
||||
}
|
||||
else {
|
||||
|
||||
session_->update(dt_);
|
||||
|
||||
if (wait_for_sources_) {
|
||||
|
||||
// force update of of all sources
|
||||
active_ = true;
|
||||
touch();
|
||||
|
||||
// check that every source is ready..
|
||||
bool ready = true;
|
||||
for (SourceList::iterator iter = session_->begin(); iter != session_->end(); ++iter)
|
||||
{
|
||||
// interrupt if any source is NOT ready
|
||||
if ( !(*iter)->ready() ){
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update to draw framebuffer
|
||||
session_->update(dt_);
|
||||
|
||||
// if all sources are ready, done with initialization!
|
||||
if (ready) {
|
||||
auto unintitializedsource = std::find_if_not(session_->begin(), session_->end(), Source::isInitialized);
|
||||
if (unintitializedsource == session_->end()) {
|
||||
// done init
|
||||
wait_for_sources_ = false;
|
||||
initialized_ = true;
|
||||
@@ -223,7 +218,20 @@ void SessionFileSource::init()
|
||||
overlays_[View::TRANSITION]->detach(loader);
|
||||
delete loader;
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
}
|
||||
}
|
||||
|
||||
void SessionFileSource::render()
|
||||
{
|
||||
if ( !initialized_ )
|
||||
init();
|
||||
else {
|
||||
// render the media player into frame buffer
|
||||
renderbuffer_->begin();
|
||||
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
|
||||
renderbuffer_->end();
|
||||
ready_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +243,7 @@ void SessionFileSource::accept(Visitor& v)
|
||||
}
|
||||
|
||||
|
||||
SessionGroupSource::SessionGroupSource() : SessionSource(), resolution_(glm::vec3(0.f))
|
||||
SessionGroupSource::SessionGroupSource(uint64_t id) : SessionSource(id), resolution_(glm::vec3(0.f))
|
||||
{
|
||||
// // redo frame for layers view
|
||||
// frames_[View::LAYER]->clear();
|
||||
@@ -287,10 +295,9 @@ void SessionGroupSource::init()
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
initialized_ = true;
|
||||
Log::Info("Source Group (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) );
|
||||
}
|
||||
}
|
||||
@@ -316,19 +323,16 @@ void SessionGroupSource::accept(Visitor& v)
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
RenderSource::RenderSource() : Source(), session_(nullptr)
|
||||
RenderSource::RenderSource(uint64_t id) : Source(id), session_(nullptr)
|
||||
{
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::RENDER, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
|
||||
bool RenderSource::failed() const
|
||||
{
|
||||
if (initialized_ && session_!=nullptr)
|
||||
if ( mode_ > Source::UNINITIALIZED && session_!=nullptr )
|
||||
return renderbuffer_->resolution() != session_->frame()->resolution();
|
||||
|
||||
return false;
|
||||
@@ -358,10 +362,9 @@ void RenderSource::init()
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
initialized_ = true;
|
||||
Log::Info("Source Render linked to session (%d x %d).", int(fb->resolution().x), int(fb->resolution().y) );
|
||||
}
|
||||
}
|
||||
@@ -369,7 +372,7 @@ void RenderSource::init()
|
||||
|
||||
glm::vec3 RenderSource::resolution() const
|
||||
{
|
||||
if (initialized_)
|
||||
if (mode_ > Source::UNINITIALIZED)
|
||||
return renderbuffer_->resolution();
|
||||
else if (session_ && session_->frame())
|
||||
return session_->frame()->resolution();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
class SessionSource : public Source
|
||||
{
|
||||
public:
|
||||
SessionSource();
|
||||
SessionSource(uint64_t id = 0);
|
||||
virtual ~SessionSource();
|
||||
|
||||
// implementation of source API
|
||||
@@ -29,10 +29,11 @@ protected:
|
||||
class SessionFileSource : public SessionSource
|
||||
{
|
||||
public:
|
||||
SessionFileSource();
|
||||
SessionFileSource(uint64_t id = 0);
|
||||
|
||||
// implementation of source API
|
||||
void accept (Visitor& v) override;
|
||||
void render() override;
|
||||
|
||||
// SessionFile Source specific interface
|
||||
void load(const std::string &p = "", uint recursion = 0);
|
||||
@@ -45,14 +46,15 @@ protected:
|
||||
void init() override;
|
||||
|
||||
std::string path_;
|
||||
std::atomic<bool> wait_for_sources_;
|
||||
bool initialized_;
|
||||
bool wait_for_sources_;
|
||||
std::future<Session *> sessionLoader_;
|
||||
};
|
||||
|
||||
class SessionGroupSource : public SessionSource
|
||||
{
|
||||
public:
|
||||
SessionGroupSource();
|
||||
SessionGroupSource(uint64_t id = 0);
|
||||
|
||||
// implementation of source API
|
||||
void accept (Visitor& v) override;
|
||||
@@ -75,7 +77,7 @@ protected:
|
||||
class RenderSource : public Source
|
||||
{
|
||||
public:
|
||||
RenderSource();
|
||||
RenderSource(uint64_t id = 0);
|
||||
|
||||
// implementation of source API
|
||||
bool failed () const override;
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "MixingGroup.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "ActionManager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
@@ -44,10 +46,17 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session)
|
||||
XMLElement *sessionNode = xmlDoc.NewElement("Session");
|
||||
xmlDoc.InsertEndChild(sessionNode);
|
||||
SessionVisitor sv(&xmlDoc, sessionNode);
|
||||
for (auto iter = session->begin(); iter != session->end(); iter++, sv.setRoot(sessionNode) )
|
||||
for (auto iter = session->begin(); iter != session->end(); ++iter, sv.setRoot(sessionNode) )
|
||||
// source visitor
|
||||
(*iter)->accept(sv);
|
||||
|
||||
// get the thumbnail
|
||||
FrameBufferImage *thumbnail = session->thumbnail();
|
||||
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
|
||||
if (imageelement)
|
||||
sessionNode->InsertEndChild(imageelement);
|
||||
delete thumbnail;
|
||||
|
||||
// 2. config of views
|
||||
XMLElement *views = xmlDoc.NewElement("Views");
|
||||
xmlDoc.InsertEndChild(views);
|
||||
@@ -73,6 +82,35 @@ bool SessionVisitor::saveSession(const std::string& filename, Session *session)
|
||||
views->InsertEndChild(render);
|
||||
}
|
||||
|
||||
// 3. snapshots
|
||||
XMLElement *snapshots = xmlDoc.NewElement("Snapshots");
|
||||
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
|
||||
for( ; N ; N=N->NextSiblingElement())
|
||||
snapshots->InsertEndChild( N->DeepClone( &xmlDoc ));
|
||||
xmlDoc.InsertEndChild(snapshots);
|
||||
|
||||
// 4. optional notes
|
||||
XMLElement *notes = xmlDoc.NewElement("Notes");
|
||||
xmlDoc.InsertEndChild(notes);
|
||||
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
|
||||
XMLElement *note = xmlDoc.NewElement( "Note" );
|
||||
note->SetAttribute("large", (*nit).large );
|
||||
note->SetAttribute("stick", (*nit).stick );
|
||||
XMLElement *pos = xmlDoc.NewElement("pos");
|
||||
pos->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).pos) );
|
||||
note->InsertEndChild(pos);
|
||||
XMLElement *size = xmlDoc.NewElement("size");
|
||||
size->InsertEndChild( XMLElementFromGLM(&xmlDoc, (*nit).size) );
|
||||
note->InsertEndChild(size);
|
||||
XMLElement *content = xmlDoc.NewElement("text");
|
||||
XMLText *text = xmlDoc.NewText( (*nit).text.c_str() );
|
||||
content->InsertEndChild( text );
|
||||
note->InsertEndChild(content);
|
||||
|
||||
notes->InsertEndChild(note);
|
||||
}
|
||||
|
||||
|
||||
// save file to disk
|
||||
return ( XMLSaveDoc(&xmlDoc, filename) );
|
||||
}
|
||||
@@ -90,7 +128,7 @@ SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
|
||||
xmlDoc_ = doc;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *SessionVisitor::NodeToXML(Node &n, tinyxml2::XMLDocument *doc)
|
||||
XMLElement *SessionVisitor::NodeToXML(const Node &n, XMLDocument *doc)
|
||||
{
|
||||
XMLElement *newelement = doc->NewElement("Node");
|
||||
newelement->SetAttribute("visible", n.visible_);
|
||||
@@ -115,6 +153,31 @@ tinyxml2::XMLElement *SessionVisitor::NodeToXML(Node &n, tinyxml2::XMLDocument *
|
||||
return newelement;
|
||||
}
|
||||
|
||||
|
||||
XMLElement *SessionVisitor::ImageToXML(const FrameBufferImage *img, XMLDocument *doc)
|
||||
{
|
||||
XMLElement *imageelement = nullptr;
|
||||
if (img != nullptr) {
|
||||
// get the jpeg encoded buffer
|
||||
FrameBufferImage::jpegBuffer jpgimg = img->getJpeg();
|
||||
if (jpgimg.buffer != nullptr) {
|
||||
// fill the xml array with jpeg buffer
|
||||
XMLElement *array = XMLElementEncodeArray(doc, jpgimg.buffer, jpgimg.len);
|
||||
// free the buffer
|
||||
free(jpgimg.buffer);
|
||||
// if we could create the array
|
||||
if (array) {
|
||||
// create an Image node to store the mask image
|
||||
imageelement = doc->NewElement("Image");
|
||||
imageelement->SetAttribute("width", img->width);
|
||||
imageelement->SetAttribute("height", img->height);
|
||||
imageelement->InsertEndChild(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageelement;
|
||||
}
|
||||
|
||||
void SessionVisitor::visit(Node &n)
|
||||
{
|
||||
XMLElement *newelement = NodeToXML(n, xmlDoc_);
|
||||
@@ -151,7 +214,7 @@ void SessionVisitor::visit(Switch &n)
|
||||
if (recursive_) {
|
||||
// loop over members of the group
|
||||
XMLElement *group = xmlCurrent_;
|
||||
for(uint i = 0; i < n.numChildren(); i++) {
|
||||
for(uint i = 0; i < n.numChildren(); ++i) {
|
||||
n.child(i)->accept(*this);
|
||||
// revert to group as current
|
||||
xmlCurrent_ = group;
|
||||
@@ -225,7 +288,7 @@ void SessionVisitor::visit(MediaPlayer &n)
|
||||
// gaps in timeline
|
||||
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
|
||||
TimeIntervalSet gaps = n.timeline()->gaps();
|
||||
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
|
||||
for( auto it = gaps.begin(); it!= gaps.end(); ++it) {
|
||||
XMLElement *g = xmlDoc_->NewElement("Interval");
|
||||
g->SetAttribute("begin", (*it).begin);
|
||||
g->SetAttribute("end", (*it).end);
|
||||
@@ -419,24 +482,9 @@ void SessionVisitor::visit (Source& s)
|
||||
// if we are saving a pain mask
|
||||
if (s.maskShader()->mode == MaskShader::PAINT) {
|
||||
// get the mask previously stored
|
||||
FrameBufferImage *img = s.getMask();
|
||||
if (img != nullptr) {
|
||||
// get the jpeg encoded buffer
|
||||
FrameBufferImage::jpegBuffer jpgimg = img->getJpeg();
|
||||
if (jpgimg.buffer != nullptr) {
|
||||
// fill the xml array with jpeg buffer
|
||||
XMLElement *array = XMLElementEncodeArray(xmlDoc_, jpgimg.buffer, jpgimg.len);
|
||||
// free the buffer
|
||||
free(jpgimg.buffer);
|
||||
// if we could create the array
|
||||
if (array) {
|
||||
// create an Image node to store the mask image
|
||||
XMLElement *imageelement = xmlDoc_->NewElement("Image");
|
||||
imageelement->InsertEndChild(array);
|
||||
xmlCurrent_->InsertEndChild(imageelement);
|
||||
}
|
||||
}
|
||||
}
|
||||
XMLElement *imageelement = SessionVisitor::ImageToXML(s.getMask(), xmlDoc_);
|
||||
if (imageelement)
|
||||
xmlCurrent_->InsertEndChild(imageelement);
|
||||
}
|
||||
|
||||
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
|
||||
@@ -487,7 +535,7 @@ void SessionVisitor::visit (SessionGroupSource& s)
|
||||
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
|
||||
xmlCurrent_->InsertEndChild(sessionNode);
|
||||
|
||||
for (auto iter = se->begin(); iter != se->end(); iter++){
|
||||
for (auto iter = se->begin(); iter != se->end(); ++iter){
|
||||
setRoot(sessionNode);
|
||||
(*iter)->accept(*this);
|
||||
}
|
||||
@@ -504,6 +552,7 @@ void SessionVisitor::visit (CloneSource& s)
|
||||
xmlCurrent_->SetAttribute("type", "CloneSource");
|
||||
|
||||
XMLElement *origin = xmlDoc_->NewElement("origin");
|
||||
origin->SetAttribute("id", s.origin()->id());
|
||||
xmlCurrent_->InsertEndChild(origin);
|
||||
XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() );
|
||||
origin->InsertEndChild( text );
|
||||
@@ -512,11 +561,14 @@ void SessionVisitor::visit (CloneSource& s)
|
||||
void SessionVisitor::visit (PatternSource& s)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("type", "PatternSource");
|
||||
xmlCurrent_->SetAttribute("pattern", s.pattern()->type() );
|
||||
|
||||
XMLElement *resolution = xmlDoc_->NewElement("resolution");
|
||||
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.pattern()->resolution() ) );
|
||||
xmlCurrent_->InsertEndChild(resolution);
|
||||
if (s.pattern()) {
|
||||
xmlCurrent_->SetAttribute("pattern", s.pattern()->type() );
|
||||
|
||||
XMLElement *resolution = xmlDoc_->NewElement("resolution");
|
||||
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.pattern()->resolution() ) );
|
||||
xmlCurrent_->InsertEndChild(resolution);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (DeviceSource& s)
|
||||
@@ -535,14 +587,36 @@ void SessionVisitor::visit (MixingGroup& g)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("size", g.size());
|
||||
|
||||
for (auto it = g.begin(); it != g.end(); it++) {
|
||||
for (auto it = g.begin(); it != g.end(); ++it) {
|
||||
XMLElement *sour = xmlDoc_->NewElement("source");
|
||||
sour->SetAttribute("id", (*it)->id());
|
||||
xmlCurrent_->InsertEndChild(sour);
|
||||
}
|
||||
}
|
||||
|
||||
std::string SessionVisitor::getClipboard(SourceList list)
|
||||
void SessionVisitor::visit (MultiFileSource& s)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("type", "MultiFileSource");
|
||||
|
||||
XMLElement *sequence = xmlDoc_->NewElement("Sequence");
|
||||
// play properties
|
||||
sequence->SetAttribute("fps", s.framerate());
|
||||
sequence->SetAttribute("begin", s.begin());
|
||||
sequence->SetAttribute("end", s.end());
|
||||
sequence->SetAttribute("loop", s.loop());
|
||||
// file sequence description
|
||||
sequence->SetAttribute("min", s.sequence().min);
|
||||
sequence->SetAttribute("max", s.sequence().max);
|
||||
sequence->SetAttribute("width", s.sequence().width);
|
||||
sequence->SetAttribute("height", s.sequence().height);
|
||||
sequence->SetAttribute("codec", s.sequence().codec.c_str());
|
||||
XMLText *location = xmlDoc_->NewText( s.sequence().location.c_str() );
|
||||
sequence->InsertEndChild( location );
|
||||
|
||||
xmlCurrent_->InsertEndChild(sequence);
|
||||
}
|
||||
|
||||
std::string SessionVisitor::getClipboard(const SourceList &list)
|
||||
{
|
||||
std::string x = "";
|
||||
|
||||
@@ -557,7 +631,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
|
||||
// fill doc by visiting sources
|
||||
SourceList selection_clones_;
|
||||
SessionVisitor sv(&xmlDoc, selectionNode);
|
||||
for (auto iter = list.begin(); iter != list.end(); iter++, sv.setRoot(selectionNode) ){
|
||||
for (auto iter = list.begin(); iter != list.end(); ++iter, sv.setRoot(selectionNode) ){
|
||||
// start with clones
|
||||
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
|
||||
if (clone)
|
||||
@@ -566,7 +640,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
|
||||
selection_clones_.push_back(*iter);
|
||||
}
|
||||
// add others in front
|
||||
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); iter++, sv.setRoot(selectionNode) ){
|
||||
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); ++iter, sv.setRoot(selectionNode) ){
|
||||
(*iter)->accept(sv);
|
||||
}
|
||||
|
||||
@@ -579,7 +653,7 @@ std::string SessionVisitor::getClipboard(SourceList list)
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string SessionVisitor::getClipboard(Source *s)
|
||||
std::string SessionVisitor::getClipboard(Source * const s)
|
||||
{
|
||||
std::string x = "";
|
||||
|
||||
@@ -603,7 +677,7 @@ std::string SessionVisitor::getClipboard(Source *s)
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string SessionVisitor::getClipboard(ImageProcessingShader *s)
|
||||
std::string SessionVisitor::getClipboard(ImageProcessingShader * const s)
|
||||
{
|
||||
std::string x = "";
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "SourceList.h"
|
||||
|
||||
class Session;
|
||||
class FrameBufferImage;
|
||||
|
||||
class SessionVisitor : public Visitor {
|
||||
|
||||
@@ -22,31 +23,31 @@ public:
|
||||
|
||||
static bool saveSession(const std::string& filename, Session *session);
|
||||
|
||||
static std::string getClipboard(SourceList list);
|
||||
static std::string getClipboard(Source *s);
|
||||
static std::string getClipboard(ImageProcessingShader *s);
|
||||
static std::string getClipboard(const SourceList &list);
|
||||
static std::string getClipboard(Source * const s);
|
||||
static std::string getClipboard(ImageProcessingShader * const s);
|
||||
|
||||
// Elements of Scene
|
||||
void visit(Scene& n) override;
|
||||
void visit(Node& n) override;
|
||||
void visit(Group& n) override;
|
||||
void visit(Switch& n) override;
|
||||
void visit(Primitive& n) override;
|
||||
void visit(Surface&) override;
|
||||
void visit(ImageSurface& n) override;
|
||||
void visit(MediaSurface& n) override;
|
||||
void visit(FrameBufferSurface&) override;
|
||||
void visit(LineStrip& n) override;
|
||||
void visit(LineSquare&) override;
|
||||
void visit(Mesh& n) override;
|
||||
void visit(Frame& n) override;
|
||||
void visit (Scene& n) override;
|
||||
void visit (Node& n) override;
|
||||
void visit (Group& n) override;
|
||||
void visit (Switch& n) override;
|
||||
void visit (Primitive& n) override;
|
||||
void visit (Surface&) override;
|
||||
void visit (ImageSurface& n) override;
|
||||
void visit (MediaSurface& n) override;
|
||||
void visit (FrameBufferSurface&) override;
|
||||
void visit (LineStrip& n) override;
|
||||
void visit (LineSquare&) override;
|
||||
void visit (Mesh& n) override;
|
||||
void visit (Frame& n) override;
|
||||
|
||||
// Elements with attributes
|
||||
void visit(MediaPlayer& n) override;
|
||||
void visit(Shader& n) override;
|
||||
void visit(ImageShader& n) override;
|
||||
void visit(MaskShader& n) override;
|
||||
void visit(ImageProcessingShader& n) override;
|
||||
void visit (MediaPlayer& n) override;
|
||||
void visit (Shader& n) override;
|
||||
void visit (ImageShader& n) override;
|
||||
void visit (MaskShader& n) override;
|
||||
void visit (ImageProcessingShader& n) override;
|
||||
|
||||
// Sources
|
||||
void visit (Source& s) override;
|
||||
@@ -59,9 +60,10 @@ public:
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MixingGroup& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
|
||||
protected:
|
||||
static tinyxml2::XMLElement *NodeToXML(Node &n, tinyxml2::XMLDocument *doc);
|
||||
static tinyxml2::XMLElement *NodeToXML(const Node &n, tinyxml2::XMLDocument *doc);
|
||||
static tinyxml2::XMLElement *ImageToXML(const FrameBufferImage *img, tinyxml2::XMLDocument *doc);
|
||||
};
|
||||
|
||||
#endif // XMLVISITOR_H
|
||||
|
||||
45
Settings.cpp
45
Settings.cpp
@@ -63,11 +63,12 @@ void Settings::Save()
|
||||
XMLElement *applicationNode = xmlDoc.NewElement( "Application" );
|
||||
applicationNode->SetAttribute("scale", application.scale);
|
||||
applicationNode->SetAttribute("accent_color", application.accent_color);
|
||||
applicationNode->SetAttribute("pannel_stick", application.pannel_stick);
|
||||
applicationNode->SetAttribute("smooth_transition", application.smooth_transition);
|
||||
applicationNode->SetAttribute("smooth_snapshot", application.smooth_snapshot);
|
||||
applicationNode->SetAttribute("smooth_cursor", application.smooth_cursor);
|
||||
applicationNode->SetAttribute("action_history_follow_view", application.action_history_follow_view);
|
||||
applicationNode->SetAttribute("accept_connections", application.accept_connections);
|
||||
applicationNode->SetAttribute("pannel_history_mode", application.pannel_history_mode);
|
||||
pRoot->InsertEndChild(applicationNode);
|
||||
|
||||
// Widgets
|
||||
@@ -175,7 +176,7 @@ void Settings::Save()
|
||||
recentsession->SetAttribute("autosave", application.recentSessions.save_on_exit);
|
||||
recentsession->SetAttribute("valid", application.recentSessions.front_is_valid);
|
||||
for(auto it = application.recentSessions.filenames.begin();
|
||||
it != application.recentSessions.filenames.end(); it++) {
|
||||
it != application.recentSessions.filenames.end(); ++it) {
|
||||
XMLElement *fileNode = xmlDoc.NewElement("path");
|
||||
XMLText *text = xmlDoc.NewText( (*it).c_str() );
|
||||
fileNode->InsertEndChild( text );
|
||||
@@ -185,7 +186,7 @@ void Settings::Save()
|
||||
|
||||
XMLElement *recentfolder = xmlDoc.NewElement( "Folder" );
|
||||
for(auto it = application.recentFolders.filenames.begin();
|
||||
it != application.recentFolders.filenames.end(); it++) {
|
||||
it != application.recentFolders.filenames.end(); ++it) {
|
||||
XMLElement *fileNode = xmlDoc.NewElement("path");
|
||||
XMLText *text = xmlDoc.NewText( (*it).c_str() );
|
||||
fileNode->InsertEndChild( text );
|
||||
@@ -196,7 +197,7 @@ void Settings::Save()
|
||||
XMLElement *recentmedia = xmlDoc.NewElement( "Import" );
|
||||
recentmedia->SetAttribute("path", application.recentImport.path.c_str());
|
||||
for(auto it = application.recentImport.filenames.begin();
|
||||
it != application.recentImport.filenames.end(); it++) {
|
||||
it != application.recentImport.filenames.end(); ++it) {
|
||||
XMLElement *fileNode = xmlDoc.NewElement("path");
|
||||
XMLText *text = xmlDoc.NewText( (*it).c_str() );
|
||||
fileNode->InsertEndChild( text );
|
||||
@@ -254,11 +255,12 @@ void Settings::Load()
|
||||
if (applicationNode != nullptr) {
|
||||
applicationNode->QueryFloatAttribute("scale", &application.scale);
|
||||
applicationNode->QueryIntAttribute("accent_color", &application.accent_color);
|
||||
applicationNode->QueryBoolAttribute("pannel_stick", &application.pannel_stick);
|
||||
applicationNode->QueryBoolAttribute("smooth_transition", &application.smooth_transition);
|
||||
applicationNode->QueryBoolAttribute("smooth_snapshot", &application.smooth_snapshot);
|
||||
applicationNode->QueryBoolAttribute("smooth_cursor", &application.smooth_cursor);
|
||||
applicationNode->QueryBoolAttribute("action_history_follow_view", &application.action_history_follow_view);
|
||||
applicationNode->QueryBoolAttribute("accept_connections", &application.accept_connections);
|
||||
applicationNode->QueryIntAttribute("pannel_history_mode", &application.pannel_history_mode);
|
||||
}
|
||||
|
||||
// Widgets
|
||||
@@ -453,6 +455,39 @@ void Settings::Load()
|
||||
|
||||
}
|
||||
|
||||
void Settings::History::push(const string &filename)
|
||||
{
|
||||
if (filename.empty()) {
|
||||
front_is_valid = false;
|
||||
return;
|
||||
}
|
||||
filenames.remove(filename);
|
||||
filenames.push_front(filename);
|
||||
if (filenames.size() > MAX_RECENT_HISTORY)
|
||||
filenames.pop_back();
|
||||
front_is_valid = true;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void Settings::History::remove(const std::string &filename)
|
||||
{
|
||||
if (filename.empty())
|
||||
return;
|
||||
if (filenames.front() == filename)
|
||||
front_is_valid = false;
|
||||
filenames.remove(filename);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void Settings::History::validate()
|
||||
{
|
||||
for (auto fit = filenames.begin(); fit != filenames.end();) {
|
||||
if ( SystemToolkit::file_exists( *fit ))
|
||||
++fit;
|
||||
else
|
||||
fit = filenames.erase(fit);
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::Lock()
|
||||
{
|
||||
|
||||
33
Settings.h
33
Settings.h
@@ -93,26 +93,9 @@ struct History
|
||||
save_on_exit = false;
|
||||
changed = false;
|
||||
}
|
||||
void push(const std::string &filename) {
|
||||
if (filename.empty()) {
|
||||
front_is_valid = false;
|
||||
return;
|
||||
}
|
||||
filenames.remove(filename);
|
||||
filenames.push_front(filename);
|
||||
if (filenames.size() > MAX_RECENT_HISTORY)
|
||||
filenames.pop_back();
|
||||
front_is_valid = true;
|
||||
changed = true;
|
||||
}
|
||||
void remove(const std::string &filename) {
|
||||
if (filename.empty())
|
||||
return;
|
||||
if (filenames.front() == filename)
|
||||
front_is_valid = false;
|
||||
filenames.remove(filename);
|
||||
changed = true;
|
||||
}
|
||||
void push(const std::string &filename);
|
||||
void remove(const std::string &filename);
|
||||
void validate();
|
||||
};
|
||||
|
||||
struct TransitionConfig
|
||||
@@ -178,14 +161,15 @@ struct Application
|
||||
// Global settings Application interface
|
||||
float scale;
|
||||
int accent_color;
|
||||
bool pannel_stick;
|
||||
bool smooth_snapshot;
|
||||
bool smooth_transition;
|
||||
bool smooth_cursor;
|
||||
bool action_history_follow_view;
|
||||
|
||||
int pannel_history_mode;
|
||||
|
||||
// connection settings
|
||||
bool accept_connections;
|
||||
// std::map<int, std::string> instance_names;
|
||||
|
||||
// Settings of widgets
|
||||
WidgetsConfig widget;
|
||||
@@ -221,11 +205,12 @@ struct Application
|
||||
Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) {
|
||||
scale = 1.f;
|
||||
accent_color = 0;
|
||||
pannel_stick = false;
|
||||
smooth_transition = true;
|
||||
smooth_transition = false;
|
||||
smooth_snapshot = false;
|
||||
smooth_cursor = false;
|
||||
action_history_follow_view = false;
|
||||
accept_connections = false;
|
||||
pannel_history_mode = 0;
|
||||
current_view = 1;
|
||||
current_workspace= 1;
|
||||
brush = glm::vec3(0.5f, 0.1f, 0.f);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "GlmToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
@@ -203,14 +203,14 @@ bool Shader::force_blending_opacity = false;
|
||||
Shader::Shader() : blending(BLEND_OPACITY)
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
program_ = &simpleShadingProgram;
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void Shader::operator = (const Shader &S )
|
||||
void Shader::copy(Shader const& S)
|
||||
{
|
||||
color = S.color;
|
||||
blending = S.blending;
|
||||
|
||||
3
Shader.h
3
Shader.h
@@ -50,8 +50,7 @@ public:
|
||||
virtual void use();
|
||||
virtual void reset();
|
||||
virtual void accept(Visitor& v);
|
||||
|
||||
void operator = (const Shader &D );
|
||||
void copy(Shader const& S);
|
||||
|
||||
glm::mat4 projection;
|
||||
glm::mat4 modelview;
|
||||
|
||||
175
Source.cpp
175
Source.cpp
@@ -11,16 +11,97 @@
|
||||
#include "SearchVisitor.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "BaseToolkit.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "Log.h"
|
||||
#include "MixingGroup.h"
|
||||
|
||||
#include "Source.h"
|
||||
|
||||
Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_(false), need_update_(true), workspace_(STAGE)
|
||||
SourceCore::SourceCore()
|
||||
{
|
||||
// default nodes
|
||||
groups_[View::RENDERING] = new Group;
|
||||
groups_[View::RENDERING]->visible_ = false;
|
||||
groups_[View::MIXING] = new Group;
|
||||
groups_[View::MIXING]->visible_ = false;
|
||||
groups_[View::GEOMETRY] = new Group;
|
||||
groups_[View::GEOMETRY]->visible_ = false;
|
||||
groups_[View::LAYER] = new Group;
|
||||
groups_[View::LAYER]->visible_ = false;
|
||||
groups_[View::TEXTURE] = new Group;
|
||||
groups_[View::TEXTURE]->visible_ = false;
|
||||
groups_[View::TRANSITION] = new Group;
|
||||
// temp node
|
||||
stored_status_ = new Group;
|
||||
|
||||
// filtered image shader (with texturing and processing) for rendering
|
||||
processingshader_ = new ImageProcessingShader;
|
||||
// default rendering with image processing disabled
|
||||
renderingshader_ = static_cast<Shader *>(new ImageShader);
|
||||
}
|
||||
|
||||
SourceCore::SourceCore(SourceCore const& other) : SourceCore()
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
|
||||
SourceCore::~SourceCore()
|
||||
{
|
||||
// all groups and their children are deleted in the scene
|
||||
// this deletes renderingshader_ (and all source-attached nodes
|
||||
// e.g. rendersurface_, overlays, blendingshader_, etc.)
|
||||
delete groups_[View::RENDERING];
|
||||
delete groups_[View::MIXING];
|
||||
delete groups_[View::GEOMETRY];
|
||||
delete groups_[View::LAYER];
|
||||
delete groups_[View::TEXTURE];
|
||||
delete groups_[View::TRANSITION];
|
||||
delete stored_status_;
|
||||
|
||||
// don't forget that the processing shader
|
||||
// could be created but not used and not deleted above
|
||||
if ( renderingshader_ != processingshader_ )
|
||||
delete processingshader_;
|
||||
|
||||
groups_.clear();
|
||||
}
|
||||
|
||||
void SourceCore::copy(SourceCore const& other)
|
||||
{
|
||||
// copy groups properties
|
||||
// groups_[View::RENDERING]->copyTransform( other.group(View::RENDERING) );
|
||||
groups_[View::MIXING]->copyTransform( other.group(View::MIXING) );
|
||||
groups_[View::GEOMETRY]->copyTransform( other.group(View::GEOMETRY) );
|
||||
groups_[View::LAYER]->copyTransform( other.group(View::LAYER) );
|
||||
groups_[View::TEXTURE]->copyTransform( other.group(View::TEXTURE) );
|
||||
groups_[View::TRANSITION]->copyTransform( other.group(View::TRANSITION) );
|
||||
stored_status_->copyTransform( other.stored_status_ );
|
||||
|
||||
// copy shader properties
|
||||
processingshader_->copy(*other.processingshader_);
|
||||
renderingshader_->copy(*other.renderingshader_);
|
||||
}
|
||||
|
||||
void SourceCore::store (View::Mode m)
|
||||
{
|
||||
stored_status_->copyTransform(groups_[m]);
|
||||
}
|
||||
|
||||
SourceCore& SourceCore::operator= (SourceCore const& other)
|
||||
{
|
||||
if (this != &other) // no self assignment
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Source::Source(uint64_t id) : SourceCore(), id_(id), ready_(false), symbol_(nullptr),
|
||||
active_(true), locked_(false), need_update_(true), dt_(0), workspace_(STAGE)
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
if (id_ == 0)
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
sprintf(initials_, "__");
|
||||
name_ = "Source";
|
||||
@@ -28,13 +109,7 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
|
||||
// create groups and overlays for each view
|
||||
|
||||
// default rendering node
|
||||
groups_[View::RENDERING] = new Group;
|
||||
groups_[View::RENDERING]->visible_ = false;
|
||||
|
||||
// default mixing nodes
|
||||
groups_[View::MIXING] = new Group;
|
||||
groups_[View::MIXING]->visible_ = false;
|
||||
groups_[View::MIXING]->scale_ = glm::vec3(0.15f, 0.15f, 1.f);
|
||||
groups_[View::MIXING]->translation_ = glm::vec3(DEFAULT_MIXING_TRANSLATION, 0.f);
|
||||
|
||||
@@ -69,9 +144,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
groups_[View::MIXING]->attach(overlay_mixinggroup_);
|
||||
|
||||
// default geometry nodes
|
||||
groups_[View::GEOMETRY] = new Group;
|
||||
groups_[View::GEOMETRY]->visible_ = false;
|
||||
|
||||
frames_[View::GEOMETRY] = new Switch;
|
||||
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
|
||||
frame->translation_.z = 0.1;
|
||||
@@ -122,8 +194,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
groups_[View::GEOMETRY]->attach(overlays_[View::GEOMETRY]);
|
||||
|
||||
// default layer nodes
|
||||
groups_[View::LAYER] = new Group;
|
||||
groups_[View::LAYER]->visible_ = false;
|
||||
groups_[View::LAYER]->translation_.z = -1.f;
|
||||
|
||||
frames_[View::LAYER] = new Switch;
|
||||
@@ -143,9 +213,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
groups_[View::LAYER]->attach(overlays_[View::LAYER]);
|
||||
|
||||
// default appearance node
|
||||
groups_[View::TEXTURE] = new Group;
|
||||
groups_[View::TEXTURE]->visible_ = false;
|
||||
|
||||
frames_[View::TEXTURE] = new Switch;
|
||||
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
|
||||
frame->translation_.z = 0.1;
|
||||
@@ -186,9 +253,6 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::MENU]);
|
||||
groups_[View::TEXTURE]->attach(overlays_[View::TEXTURE]);
|
||||
|
||||
// empty transition node
|
||||
groups_[View::TRANSITION] = new Group;
|
||||
|
||||
// locker switch button : locked / unlocked icons
|
||||
locker_ = new Switch;
|
||||
lock_ = new Handles(Handles::LOCKED);
|
||||
@@ -196,20 +260,12 @@ Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_
|
||||
unlock_ = new Handles(Handles::UNLOCKED);
|
||||
locker_->attach(unlock_);
|
||||
|
||||
// create objects
|
||||
stored_status_ = new Group;
|
||||
|
||||
// simple image shader (with texturing) for blending
|
||||
blendingshader_ = new ImageShader;
|
||||
// mask produced by dedicated shader
|
||||
maskshader_ = new MaskShader;
|
||||
masksurface_ = new Surface(maskshader_);
|
||||
|
||||
// filtered image shader (with texturing and processing) for rendering
|
||||
processingshader_ = new ImageProcessingShader;
|
||||
// default rendering with image processing enabled
|
||||
renderingshader_ = static_cast<Shader *>(processingshader_);
|
||||
|
||||
// for drawing in mixing view
|
||||
mixingshader_ = new ImageShader;
|
||||
mixingshader_->stipple = 1.0;
|
||||
@@ -238,12 +294,11 @@ Source::~Source()
|
||||
links_.front()->disconnect();
|
||||
|
||||
// inform clones that they lost their origin
|
||||
for (auto it = clones_.begin(); it != clones_.end(); it++)
|
||||
for (auto it = clones_.begin(); it != clones_.end(); ++it)
|
||||
(*it)->detach();
|
||||
clones_.clear();
|
||||
|
||||
// delete objects
|
||||
delete stored_status_;
|
||||
if (renderbuffer_)
|
||||
delete renderbuffer_;
|
||||
if (maskbuffer_)
|
||||
@@ -253,30 +308,17 @@ Source::~Source()
|
||||
if (masksurface_)
|
||||
delete masksurface_; // deletes maskshader_
|
||||
|
||||
// all groups and their children are deleted in the scene
|
||||
// this includes rendersurface_, overlays, blendingshader_ and rendershader_
|
||||
delete groups_[View::RENDERING];
|
||||
delete groups_[View::MIXING];
|
||||
delete groups_[View::GEOMETRY];
|
||||
delete groups_[View::LAYER];
|
||||
delete groups_[View::TEXTURE];
|
||||
delete groups_[View::TRANSITION];
|
||||
|
||||
groups_.clear();
|
||||
frames_.clear();
|
||||
overlays_.clear();
|
||||
|
||||
// don't forget that the processing shader
|
||||
// could be created but not used
|
||||
if ( renderingshader_ != processingshader_ )
|
||||
delete processingshader_;
|
||||
|
||||
delete texturesurface_;
|
||||
|
||||
overlays_.clear();
|
||||
frames_.clear();
|
||||
handles_.clear();
|
||||
}
|
||||
|
||||
void Source::setName (const std::string &name)
|
||||
{
|
||||
name_ = SystemToolkit::transliterate(name);
|
||||
if (!name.empty())
|
||||
name_ = BaseToolkit::transliterate(name);
|
||||
|
||||
initials_[0] = std::toupper( name_.front(), std::locale("C") );
|
||||
initials_[1] = std::toupper( name_.back(), std::locale("C") );
|
||||
@@ -296,18 +338,18 @@ void Source::setMode(Source::Mode m)
|
||||
{
|
||||
// make visible on first time
|
||||
if ( mode_ == Source::UNINITIALIZED ) {
|
||||
for (auto g = groups_.begin(); g != groups_.end(); g++)
|
||||
for (auto g = groups_.begin(); g != groups_.end(); ++g)
|
||||
(*g).second->visible_ = true;
|
||||
}
|
||||
|
||||
// choose frame 0 if visible, 1 if selected
|
||||
uint index_frame = m == Source::VISIBLE ? 0 : 1;
|
||||
for (auto f = frames_.begin(); f != frames_.end(); f++)
|
||||
for (auto f = frames_.begin(); f != frames_.end(); ++f)
|
||||
(*f).second->setActive(index_frame);
|
||||
|
||||
// show overlay if current
|
||||
bool current = m >= Source::CURRENT;
|
||||
for (auto o = overlays_.begin(); o != overlays_.end(); o++)
|
||||
for (auto o = overlays_.begin(); o != overlays_.end(); ++o)
|
||||
(*o).second->visible_ = (current && !locked_);
|
||||
|
||||
// the lock icon
|
||||
@@ -372,13 +414,14 @@ bool Source::imageProcessingEnabled()
|
||||
|
||||
void Source::render()
|
||||
{
|
||||
if (!initialized_)
|
||||
if ( renderbuffer_ == nullptr )
|
||||
init();
|
||||
else {
|
||||
// render the view into frame buffer
|
||||
renderbuffer_->begin();
|
||||
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
|
||||
renderbuffer_->end();
|
||||
ready_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,7 +502,7 @@ void Source::setActive (bool on)
|
||||
active_ = on;
|
||||
|
||||
// do not disactivate if a clone depends on it
|
||||
for(auto clone = clones_.begin(); clone != clones_.end(); clone++) {
|
||||
for(auto clone = clones_.begin(); clone != clones_.end(); ++clone) {
|
||||
if ( (*clone)->active() )
|
||||
active_ = true;
|
||||
}
|
||||
@@ -665,7 +708,7 @@ void Source::update(float dt)
|
||||
|
||||
FrameBuffer *Source::frame() const
|
||||
{
|
||||
if (initialized_ && renderbuffer_)
|
||||
if ( mode_ > Source::UNINITIALIZED && renderbuffer_)
|
||||
{
|
||||
return renderbuffer_;
|
||||
}
|
||||
@@ -738,13 +781,13 @@ bool Source::hasNode::operator()(const Source* elem) const
|
||||
// general case: traverse tree of all Groups recursively using a SearchVisitor
|
||||
SearchVisitor sv(_n);
|
||||
// search in groups for all views
|
||||
for (auto g = elem->groups_.begin(); g != elem->groups_.end(); g++) {
|
||||
for (auto g = elem->groups_.begin(); g != elem->groups_.end(); ++g) {
|
||||
(*g).second->accept(sv);
|
||||
if (sv.found())
|
||||
return true;
|
||||
}
|
||||
// search in overlays for all views
|
||||
for (auto g = elem->overlays_.begin(); g != elem->overlays_.end(); g++) {
|
||||
for (auto g = elem->overlays_.begin(); g != elem->overlays_.end(); ++g) {
|
||||
(*g).second->accept(sv);
|
||||
if (sv.found())
|
||||
return true;
|
||||
@@ -762,9 +805,9 @@ void Source::clearMixingGroup()
|
||||
}
|
||||
|
||||
|
||||
CloneSource *Source::clone()
|
||||
CloneSource *Source::clone(uint64_t id)
|
||||
{
|
||||
CloneSource *s = new CloneSource(this);
|
||||
CloneSource *s = new CloneSource(this, id);
|
||||
|
||||
clones_.push_back(s);
|
||||
|
||||
@@ -772,8 +815,9 @@ CloneSource *Source::clone()
|
||||
}
|
||||
|
||||
|
||||
CloneSource::CloneSource(Source *origin) : Source(), origin_(origin)
|
||||
CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin)
|
||||
{
|
||||
name_ = origin->name();
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
@@ -785,18 +829,18 @@ CloneSource::~CloneSource()
|
||||
origin_->clones_.remove(this);
|
||||
}
|
||||
|
||||
CloneSource *CloneSource::clone()
|
||||
CloneSource *CloneSource::clone(uint64_t id)
|
||||
{
|
||||
// do not clone a clone : clone the original instead
|
||||
if (origin_)
|
||||
return origin_->clone();
|
||||
return origin_->clone(id);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CloneSource::init()
|
||||
{
|
||||
if (origin_ && origin_->ready()) {
|
||||
if (origin_ && origin_->mode_ > Source::UNINITIALIZED) {
|
||||
|
||||
// get the texture index from framebuffer of view, apply it to the surface
|
||||
texturesurface_->setTextureIndex( origin_->texture() );
|
||||
@@ -808,10 +852,9 @@ void CloneSource::init()
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
initialized_ = true;
|
||||
Log::Info("Source %s cloning source %s.", name().c_str(), origin_->name().c_str() );
|
||||
}
|
||||
}
|
||||
@@ -824,14 +867,14 @@ void CloneSource::setActive (bool on)
|
||||
groups_[View::GEOMETRY]->visible_ = active_;
|
||||
groups_[View::LAYER]->visible_ = active_;
|
||||
|
||||
if (initialized_ && origin_ != nullptr)
|
||||
if ( mode_ > Source::UNINITIALIZED && origin_ != nullptr)
|
||||
origin_->touch();
|
||||
}
|
||||
|
||||
|
||||
uint CloneSource::texture() const
|
||||
{
|
||||
if (initialized_ && origin_ != nullptr)
|
||||
if (origin_ != nullptr)
|
||||
return origin_->texture();
|
||||
else
|
||||
return Resource::getTextureBlack();
|
||||
|
||||
71
Source.h
71
Source.h
@@ -23,7 +23,39 @@ class MixingGroup;
|
||||
|
||||
typedef std::list<CloneSource *> CloneList;
|
||||
|
||||
class Source
|
||||
class SourceCore
|
||||
{
|
||||
public:
|
||||
SourceCore();
|
||||
SourceCore(SourceCore const&);
|
||||
SourceCore& operator= (SourceCore const& other);
|
||||
virtual ~SourceCore();
|
||||
|
||||
// get handle on the nodes used to manipulate the source in a view
|
||||
inline Group *group (View::Mode m) const { return groups_.at(m); }
|
||||
inline Node *groupNode (View::Mode m) const { return static_cast<Node*>(groups_.at(m)); }
|
||||
void store (View::Mode m);
|
||||
|
||||
// a Source has a shader used to render in fbo
|
||||
inline Shader *renderingShader () const { return renderingshader_; }
|
||||
// a Source always has an image processing shader
|
||||
inline ImageProcessingShader *processingShader () const { return processingshader_; }
|
||||
|
||||
void copy(SourceCore const& other);
|
||||
|
||||
protected:
|
||||
// nodes
|
||||
std::map<View::Mode, Group*> groups_;
|
||||
// temporary copy for interaction
|
||||
Group *stored_status_;
|
||||
// image processing shaders
|
||||
ImageProcessingShader *processingshader_;
|
||||
// pointer to the currently attached shader
|
||||
// (will be processingshader_ if image processing is enabled)
|
||||
Shader *renderingshader_;
|
||||
};
|
||||
|
||||
class Source : public SourceCore
|
||||
{
|
||||
friend class SourceLink;
|
||||
friend class CloneSource;
|
||||
@@ -38,7 +70,10 @@ class Source
|
||||
public:
|
||||
// create a source and add it to the list
|
||||
// only subclasses of sources can actually be instanciated
|
||||
Source ();
|
||||
Source (uint64_t id = 0);
|
||||
// non assignable class
|
||||
Source(Source const&) = delete;
|
||||
Source& operator=(Source const&) = delete;
|
||||
virtual ~Source ();
|
||||
|
||||
// Get unique id
|
||||
@@ -50,7 +85,7 @@ public:
|
||||
inline const char *initials () const { return initials_; }
|
||||
|
||||
// cloning mechanism
|
||||
virtual CloneSource *clone ();
|
||||
virtual CloneSource *clone (uint64_t id = 0);
|
||||
|
||||
// Display mode
|
||||
typedef enum {
|
||||
@@ -62,16 +97,9 @@ public:
|
||||
Mode mode () const;
|
||||
void setMode (Mode m);
|
||||
|
||||
// get handle on the nodes used to manipulate the source in a view
|
||||
inline Group *group (View::Mode m) const { return groups_.at(m); }
|
||||
inline Node *groupNode (View::Mode m) const { return static_cast<Node*>(groups_.at(m)); }
|
||||
|
||||
// tests if a given node is part of the source
|
||||
bool contains (Node *node) const;
|
||||
|
||||
// the rendering shader always have an image processing shader
|
||||
inline ImageProcessingShader *processingShader () const { return processingshader_; }
|
||||
|
||||
// the image processing shader can be enabled or disabled
|
||||
// (NB: when disabled, a simple ImageShader is applied)
|
||||
void setImageProcessingEnabled (bool on);
|
||||
@@ -80,9 +108,6 @@ public:
|
||||
// a Source has a shader to control mixing effects
|
||||
inline ImageShader *blendingShader () const { return blendingshader_; }
|
||||
|
||||
// a Source has a shader used to render in fbo
|
||||
inline Shader *renderingShader () const { return renderingshader_; }
|
||||
|
||||
// every Source has a frame buffer from the renderbuffer
|
||||
virtual FrameBuffer *frame () const;
|
||||
|
||||
@@ -93,7 +118,7 @@ public:
|
||||
inline void touch () { need_update_ = true; }
|
||||
|
||||
// informs if its ready (i.e. initialized)
|
||||
inline bool ready () const { return initialized_; }
|
||||
inline bool ready () const { return ready_; }
|
||||
|
||||
// a Source shall be updated before displayed (Mixing, Geometry and Layer)
|
||||
virtual void update (float dt);
|
||||
@@ -206,12 +231,9 @@ protected:
|
||||
char initials_[3];
|
||||
uint64_t id_;
|
||||
|
||||
// every Source shall be initialized on first draw
|
||||
bool initialized_;
|
||||
// every Source shall be initialized to be ready after first draw
|
||||
virtual void init() = 0;
|
||||
|
||||
// nodes
|
||||
std::map<View::Mode, Group*> groups_;
|
||||
bool ready_;
|
||||
|
||||
// render() fills in the renderbuffer at every frame
|
||||
// NB: rendershader_ is applied at render()
|
||||
@@ -223,12 +245,6 @@ protected:
|
||||
FrameBufferSurface *rendersurface_;
|
||||
FrameBufferSurface *mixingsurface_;
|
||||
|
||||
// image processing shaders
|
||||
ImageProcessingShader *processingshader_;
|
||||
// pointer to the currently attached shader
|
||||
// (will be processingshader_ if image processing is enabled)
|
||||
Shader *renderingshader_;
|
||||
|
||||
// blendingshader provides mixing controls
|
||||
ImageShader *blendingshader_;
|
||||
ImageShader *mixingshader_;
|
||||
@@ -259,7 +275,6 @@ protected:
|
||||
bool locked_;
|
||||
bool need_update_;
|
||||
float dt_;
|
||||
Group *stored_status_;
|
||||
Workspace workspace_;
|
||||
|
||||
// clones
|
||||
@@ -288,7 +303,7 @@ public:
|
||||
bool failed() const override { return origin_ == nullptr; }
|
||||
void accept (Visitor& v) override;
|
||||
|
||||
CloneSource *clone() override;
|
||||
CloneSource *clone(uint64_t id = 0) override;
|
||||
inline void detach() { origin_ = nullptr; }
|
||||
inline Source *origin() const { return origin_; }
|
||||
|
||||
@@ -296,7 +311,7 @@ public:
|
||||
|
||||
protected:
|
||||
// only Source class can create new CloneSource via clone();
|
||||
CloneSource(Source *origin);
|
||||
CloneSource(Source *origin, uint64_t id = 0);
|
||||
|
||||
void init() override;
|
||||
Source *origin_;
|
||||
|
||||
@@ -49,7 +49,7 @@ SourceIdList ids (const SourceList &list)
|
||||
{
|
||||
SourceIdList idlist;
|
||||
|
||||
for( auto sit = list.begin(); sit != list.end(); sit++)
|
||||
for( auto sit = list.begin(); sit != list.end(); ++sit)
|
||||
idlist.push_back( (*sit)->id() );
|
||||
|
||||
// make sure no duplicate
|
||||
@@ -67,7 +67,7 @@ SourceListCompare compare (const SourceList &first, const SourceList &second)
|
||||
|
||||
// a new test list: start with the second list and remove all commons with first list
|
||||
SourceList test = second;
|
||||
for (auto it = first.begin(); it != first.end(); it++){
|
||||
for (auto it = first.begin(); it != first.end(); ++it){
|
||||
test.remove(*it);
|
||||
}
|
||||
|
||||
@@ -100,12 +100,12 @@ SourceList intersect (const SourceList &first, const SourceList &second)
|
||||
// take second list and remove all elements also in first list
|
||||
// -> builds the list of what remains in second list
|
||||
SourceList l1 = second;
|
||||
for (auto it = first.begin(); it != first.end(); it++)
|
||||
for (auto it = first.begin(); it != first.end(); ++it)
|
||||
l1.remove(*it);
|
||||
// take second list and remove all elements in the remainer list
|
||||
// -> builds the list of what is in second list and was part of the first list
|
||||
SourceList l2 = second;
|
||||
for (auto it = l1.begin(); it != l1.end(); it++)
|
||||
for (auto it = l1.begin(); it != l1.end(); ++it)
|
||||
l2.remove(*it);
|
||||
return l2;
|
||||
}
|
||||
@@ -114,13 +114,15 @@ SourceList intersect (const SourceList &first, const SourceList &second)
|
||||
SourceList join (const SourceList &first, const SourceList &second)
|
||||
{
|
||||
SourceList l = second;
|
||||
for (auto it = first.begin(); it != first.end(); it++)
|
||||
for (auto it = first.begin(); it != first.end(); ++it)
|
||||
l.push_back(*it);
|
||||
l.unique();
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SourceLink::connect(uint64_t id, Session *se)
|
||||
{
|
||||
if (connected())
|
||||
@@ -136,8 +138,9 @@ void SourceLink::connect(Source *s)
|
||||
disconnect();
|
||||
|
||||
target_ = s;
|
||||
id_ = s->id();
|
||||
target_->links_.push_back(this);
|
||||
|
||||
id_ = s->id();
|
||||
// TODO veryfy circular dependency recursively ?
|
||||
}
|
||||
|
||||
@@ -145,9 +148,9 @@ void SourceLink::disconnect()
|
||||
{
|
||||
if (target_)
|
||||
target_->links_.remove(this);
|
||||
target_ = nullptr;
|
||||
|
||||
id_ = 0;
|
||||
target_ = nullptr;
|
||||
host_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -181,3 +184,21 @@ Source *SourceLink::source()
|
||||
return target_;
|
||||
}
|
||||
|
||||
|
||||
SourceList validate (const SourceLinkList &list)
|
||||
{
|
||||
SourceList sourcelist;
|
||||
|
||||
for( auto sit = list.begin(); sit != list.end(); ++sit) {
|
||||
|
||||
Source *s = (*sit)->source();
|
||||
if (s)
|
||||
sourcelist.push_back( s );
|
||||
|
||||
}
|
||||
|
||||
// make sure no duplicate
|
||||
sourcelist.unique();
|
||||
|
||||
return sourcelist;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Source;
|
||||
class SourceCore;
|
||||
class Session;
|
||||
|
||||
typedef std::list<Source *> SourceList;
|
||||
typedef std::list<SourceCore *> SourceCoreList;
|
||||
|
||||
SourceList depth_sorted (const SourceList &list);
|
||||
SourceList mixing_sorted (const SourceList &list, glm::vec2 center = glm::vec2(0.f, 0.f));
|
||||
@@ -42,9 +44,11 @@ public:
|
||||
|
||||
protected:
|
||||
Session *host_;
|
||||
Source *target_;
|
||||
Source *target_;
|
||||
uint64_t id_;
|
||||
};
|
||||
|
||||
typedef std::list<SourceLink*> SourceLinkList;
|
||||
SourceList validate (const SourceLinkList &list);
|
||||
|
||||
#endif // SOURCELIST_H
|
||||
|
||||
160
Stream.cpp
160
Stream.cpp
@@ -12,7 +12,7 @@ using namespace std;
|
||||
#include "Resource.h"
|
||||
#include "Visitor.h"
|
||||
#include "SystemToolkit.h"
|
||||
#include "GlmToolkit.h"
|
||||
#include "BaseToolkit.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
@@ -24,19 +24,19 @@ using namespace std;
|
||||
Stream::Stream()
|
||||
{
|
||||
// create unique id
|
||||
id_ = GlmToolkit::uniqueId();
|
||||
id_ = BaseToolkit::uniqueId();
|
||||
|
||||
description_ = "undefined";
|
||||
pipeline_ = nullptr;
|
||||
opened_ = false;
|
||||
enabled_ = true;
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
|
||||
width_ = -1;
|
||||
height_ = -1;
|
||||
single_frame_ = false;
|
||||
live_ = false;
|
||||
ready_ = false;
|
||||
failed_ = false;
|
||||
enabled_ = true;
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
|
||||
// start index in frame_ stack
|
||||
write_index_ = 0;
|
||||
@@ -50,11 +50,20 @@ Stream::Stream()
|
||||
|
||||
// OpenGL texture
|
||||
textureindex_ = 0;
|
||||
textureinitialized_ = false;
|
||||
}
|
||||
|
||||
Stream::~Stream()
|
||||
{
|
||||
close();
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
}
|
||||
|
||||
void Stream::accept(Visitor& v) {
|
||||
@@ -70,8 +79,11 @@ guint Stream::texture() const
|
||||
}
|
||||
|
||||
|
||||
void Stream::open(const std::string &gstreamer_description, int w, int h)
|
||||
void Stream::open(const std::string &gstreamer_description, guint w, guint h)
|
||||
{
|
||||
if (w != width_ || h != height_ )
|
||||
textureinitialized_ = false;
|
||||
|
||||
// set gstreamer pipeline source
|
||||
description_ = gstreamer_description;
|
||||
width_ = w;
|
||||
@@ -81,6 +93,7 @@ void Stream::open(const std::string &gstreamer_description, int w, int h)
|
||||
if (isOpen())
|
||||
close();
|
||||
|
||||
// open the stream
|
||||
execute_open();
|
||||
}
|
||||
|
||||
@@ -93,7 +106,7 @@ std::string Stream::description() const
|
||||
void Stream::execute_open()
|
||||
{
|
||||
// reset
|
||||
ready_ = false;
|
||||
opened_ = false;
|
||||
|
||||
// Add custom app sink to the gstreamer pipeline
|
||||
string description = description_;
|
||||
@@ -109,6 +122,7 @@ void Stream::execute_open()
|
||||
return;
|
||||
}
|
||||
g_object_set(G_OBJECT(pipeline_), "name", std::to_string(id_).c_str(), NULL);
|
||||
gst_pipeline_set_auto_flush_bus( GST_PIPELINE(pipeline_), true);
|
||||
|
||||
// GstCaps *caps = gst_static_caps_get (&frame_render_caps);
|
||||
string capstring = "video/x-raw,format=RGBA,width="+ std::to_string(width_) +
|
||||
@@ -138,14 +152,13 @@ void Stream::execute_open()
|
||||
#ifdef USE_GST_APPSINK_CALLBACKS
|
||||
// set the callbacks
|
||||
GstAppSinkCallbacks callbacks;
|
||||
callbacks.new_preroll = callback_new_preroll;
|
||||
if (single_frame_) {
|
||||
callbacks.new_preroll = callback_new_preroll;
|
||||
callbacks.eos = NULL;
|
||||
callbacks.new_sample = NULL;
|
||||
Log::Info("Stream %s contains a single frame", std::to_string(id_).c_str());
|
||||
}
|
||||
else {
|
||||
callbacks.new_preroll = callback_new_preroll;
|
||||
callbacks.eos = callback_end_of_stream;
|
||||
callbacks.new_sample = callback_new_sample;
|
||||
}
|
||||
@@ -176,18 +189,18 @@ void Stream::execute_open()
|
||||
// instruct the sink to send samples synched in time if not live source
|
||||
gst_base_sink_set_sync (GST_BASE_SINK(sink), !live_);
|
||||
|
||||
// all good
|
||||
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
|
||||
ready_ = true;
|
||||
|
||||
// done with refs
|
||||
gst_object_unref (sink);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
// all good
|
||||
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
|
||||
opened_ = true;
|
||||
}
|
||||
|
||||
bool Stream::isOpen() const
|
||||
{
|
||||
return ready_;
|
||||
return opened_;
|
||||
}
|
||||
|
||||
bool Stream::failed() const
|
||||
@@ -197,22 +210,23 @@ bool Stream::failed() const
|
||||
|
||||
void Stream::Frame::unmap()
|
||||
{
|
||||
if ( full ) {
|
||||
if ( full )
|
||||
gst_video_frame_unmap(&vframe);
|
||||
full = false;
|
||||
}
|
||||
full = false;
|
||||
}
|
||||
|
||||
void Stream::close()
|
||||
{
|
||||
// not openned?
|
||||
if (!ready_) {
|
||||
if (!opened_) {
|
||||
// nothing else to change
|
||||
return;
|
||||
}
|
||||
|
||||
// un-ready
|
||||
ready_ = false;
|
||||
opened_ = false;
|
||||
single_frame_ = false;
|
||||
live_ = false;
|
||||
|
||||
// clean up GST
|
||||
if (pipeline_ != nullptr) {
|
||||
@@ -229,10 +243,9 @@ void Stream::close()
|
||||
gst_object_unref (pipeline_);
|
||||
pipeline_ = nullptr;
|
||||
}
|
||||
desired_state_ = GST_STATE_PAUSED;
|
||||
|
||||
// cleanup eventual remaining frame memory
|
||||
for(guint i = 0; i < N_FRAME; i++){
|
||||
for(guint i = 0; i < N_FRAME; ++i){
|
||||
frame_[i].access.lock();
|
||||
frame_[i].unmap();
|
||||
frame_[i].access.unlock();
|
||||
@@ -240,15 +253,6 @@ void Stream::close()
|
||||
write_index_ = 0;
|
||||
last_index_ = 0;
|
||||
|
||||
// cleanup opengl texture
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
textureindex_ = 0;
|
||||
|
||||
// cleanup picture buffer
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_size_ = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -269,7 +273,7 @@ float Stream::aspectRatio() const
|
||||
|
||||
void Stream::enable(bool on)
|
||||
{
|
||||
if ( !ready_ || pipeline_ == nullptr)
|
||||
if ( !opened_ || pipeline_ == nullptr)
|
||||
return;
|
||||
|
||||
if ( enabled_ != on ) {
|
||||
@@ -312,7 +316,7 @@ bool Stream::live() const
|
||||
void Stream::play(bool on)
|
||||
{
|
||||
// ignore if disabled, and cannot play an image
|
||||
if (!enabled_)
|
||||
if (!enabled_ || single_frame_)
|
||||
return;
|
||||
|
||||
// request state
|
||||
@@ -353,6 +357,10 @@ void Stream::play(bool on)
|
||||
|
||||
bool Stream::isPlaying(bool testpipeline) const
|
||||
{
|
||||
// image cannot play
|
||||
if (single_frame_)
|
||||
return false;
|
||||
|
||||
// if not ready yet, answer with requested state
|
||||
if ( !testpipeline || pipeline_ == nullptr || !enabled_)
|
||||
return desired_state_ == GST_STATE_PLAYING;
|
||||
@@ -368,6 +376,8 @@ bool Stream::isPlaying(bool testpipeline) const
|
||||
void Stream::init_texture(guint index)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (textureindex_)
|
||||
glDeleteTextures(1, &textureindex_);
|
||||
glGenTextures(1, &textureindex_);
|
||||
glBindTexture(GL_TEXTURE_2D, textureindex_);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width_, height_);
|
||||
@@ -378,53 +388,58 @@ void Stream::init_texture(guint index)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// set pbo image size
|
||||
pbo_size_ = height_ * width_ * 4;
|
||||
if (!single_frame_) {
|
||||
|
||||
// create pixel buffer objects,
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
glGenBuffers(2, pbo_);
|
||||
// set pbo image size
|
||||
pbo_size_ = height_ * width_ * 4;
|
||||
|
||||
for(int i = 0; i < 2; i++ ) {
|
||||
// create 2 PBOs
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
|
||||
// glBufferDataARB with NULL pointer reserves only memory space.
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// fill in with reset picture
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
else {
|
||||
// did not work, disable PBO
|
||||
// create pixel buffer objects,
|
||||
if (pbo_[0])
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = pbo_[1] = 0;
|
||||
pbo_size_ = 0;
|
||||
break;
|
||||
glGenBuffers(2, pbo_);
|
||||
|
||||
for(int i = 0; i < 2; i++ ) {
|
||||
// create 2 PBOs
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
|
||||
// glBufferDataARB with NULL pointer reserves only memory space.
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
|
||||
// fill in with reset picture
|
||||
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
if (ptr) {
|
||||
// update data directly on the mapped buffer
|
||||
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
|
||||
// release pointer to mapping buffer
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
}
|
||||
else {
|
||||
// did not work, disable PBO
|
||||
glDeleteBuffers(2, pbo_);
|
||||
pbo_[0] = pbo_[1] = 0;
|
||||
pbo_size_ = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// should be good to go, wrap it up
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
pbo_index_ = 0;
|
||||
pbo_next_index_ = 1;
|
||||
// should be good to go, wrap it up
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
pbo_index_ = 0;
|
||||
pbo_next_index_ = 1;
|
||||
|
||||
#ifdef STREAM_DEBUG
|
||||
Log::Info("Stream %s Use Pixel Buffer Object texturing.", std::to_string(id_).c_str());
|
||||
#endif
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
textureinitialized_ = true;
|
||||
}
|
||||
|
||||
|
||||
void Stream::fill_texture(guint index)
|
||||
{
|
||||
// is this the first frame ?
|
||||
if (textureindex_ < 1)
|
||||
if ( !textureinitialized_ || textureindex_ < 1)
|
||||
{
|
||||
// initialize texture
|
||||
init_texture(index);
|
||||
@@ -466,6 +481,7 @@ void Stream::fill_texture(guint index)
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,12 +492,14 @@ void Stream::update()
|
||||
return;
|
||||
|
||||
// not ready yet
|
||||
if (!ready_)
|
||||
if (!opened_){
|
||||
// wait next frame to display
|
||||
return;
|
||||
}
|
||||
|
||||
// // prevent unnecessary updates: disabled or already filled image
|
||||
// if (!enabled_)
|
||||
// return;
|
||||
// prevent unnecessary updates: already filled image
|
||||
if (single_frame_ && textureindex_>0)
|
||||
return;
|
||||
|
||||
// local variables before trying to update
|
||||
guint read_index = 0;
|
||||
@@ -627,7 +645,7 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
|
||||
void Stream::callback_end_of_stream (GstAppSink *, gpointer p)
|
||||
{
|
||||
Stream *m = static_cast<Stream *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
m->fill_frame(NULL, Stream::EOS);
|
||||
}
|
||||
}
|
||||
@@ -643,7 +661,7 @@ GstFlowReturn Stream::callback_new_preroll (GstAppSink *sink, gpointer p)
|
||||
if (sample != NULL) {
|
||||
// send frames to media player only if ready
|
||||
Stream *m = static_cast<Stream *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
|
||||
// get buffer from sample
|
||||
GstBuffer *buf = gst_sample_get_buffer (sample);
|
||||
@@ -677,7 +695,7 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
|
||||
|
||||
// send frames to media player only if ready
|
||||
Stream *m = static_cast<Stream *>(p);
|
||||
if (m && m->ready_) {
|
||||
if (m && m->opened_) {
|
||||
|
||||
// get buffer from sample (valid until sample is released)
|
||||
GstBuffer *buf = gst_sample_get_buffer (sample) ;
|
||||
@@ -710,7 +728,7 @@ void Stream::TimeCounter::tic ()
|
||||
GstClockTime dt = t - last_time;
|
||||
|
||||
// one more frame since last time
|
||||
nbFrames++;
|
||||
++nbFrames;
|
||||
|
||||
// calculate instantaneous framerate
|
||||
// Exponential moving averate with previous framerate to filter jitter (50/50)
|
||||
|
||||
7
Stream.h
7
Stream.h
@@ -33,7 +33,7 @@ public:
|
||||
/**
|
||||
* Open a media using gstreamer pipeline keyword
|
||||
* */
|
||||
void open(const std::string &gstreamer_description, int w = 1024, int h = 576);
|
||||
void open(const std::string &gstreamer_description, guint w = 1024, guint h = 576);
|
||||
/**
|
||||
* Get description string
|
||||
* */
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
/**
|
||||
* Close the Media
|
||||
* */
|
||||
void close();
|
||||
virtual void close();
|
||||
/**
|
||||
* Update texture with latest frame
|
||||
* Must be called in rendering update loop
|
||||
@@ -129,7 +129,7 @@ protected:
|
||||
GstState desired_state_;
|
||||
GstElement *pipeline_;
|
||||
GstVideoInfo v_frame_video_info_;
|
||||
std::atomic<bool> ready_;
|
||||
std::atomic<bool> opened_;
|
||||
std::atomic<bool> failed_;
|
||||
bool enabled_;
|
||||
|
||||
@@ -185,6 +185,7 @@ protected:
|
||||
virtual void execute_open();
|
||||
|
||||
// gst frame filling
|
||||
bool textureinitialized_;
|
||||
void init_texture(guint index);
|
||||
void fill_texture(guint index);
|
||||
bool fill_frame(GstBuffer *buf, FrameStatus status);
|
||||
|
||||
@@ -26,8 +26,12 @@ void GenericStreamSource::setDescription(const std::string &desc)
|
||||
{
|
||||
Log::Notify("Creating Source with Stream description '%s'", desc.c_str());
|
||||
|
||||
// open gstreamer
|
||||
stream_->open(desc);
|
||||
stream_->play(true);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
|
||||
void GenericStreamSource::accept(Visitor& v)
|
||||
@@ -37,7 +41,7 @@ void GenericStreamSource::accept(Visitor& v)
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
StreamSource::StreamSource() : Source(), stream_(nullptr)
|
||||
StreamSource::StreamSource(uint64_t id) : Source(id), stream_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -81,14 +85,13 @@ void StreamSource::init()
|
||||
// set the renderbuffer of the source and attach rendering nodes
|
||||
attach(renderbuffer);
|
||||
|
||||
// deep update to reorder
|
||||
View::need_deep_update_++;
|
||||
|
||||
// force update of activation mode
|
||||
active_ = true;
|
||||
|
||||
// deep update to reorder
|
||||
++View::need_deep_update_;
|
||||
|
||||
// done init
|
||||
initialized_ = true;
|
||||
Log::Info("Source '%s' linked to Stream %s", name().c_str(), std::to_string(stream_->id()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
class StreamSource: public Source
|
||||
{
|
||||
public:
|
||||
StreamSource();
|
||||
StreamSource(uint64_t id = 0);
|
||||
virtual ~StreamSource();
|
||||
|
||||
// implementation of source API
|
||||
|
||||
@@ -29,8 +29,8 @@ class Streaming
|
||||
|
||||
// Private Constructor
|
||||
Streaming();
|
||||
Streaming(Streaming const& copy); // Not Implemented
|
||||
Streaming& operator=(Streaming const& copy); // Not Implemented
|
||||
Streaming(Streaming const& copy) = delete;
|
||||
Streaming& operator=(Streaming const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
|
||||
#include <locale>
|
||||
#include <unicode/ustream.h>
|
||||
#include <unicode/translit.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -88,45 +84,10 @@ long SystemToolkit::memory_max_usage() {
|
||||
|
||||
struct rusage r_usage;
|
||||
getrusage(RUSAGE_SELF,&r_usage);
|
||||
return r_usage.ru_maxrss;
|
||||
return 1024 * r_usage.ru_maxrss;
|
||||
// return r_usage.ru_isrss;
|
||||
}
|
||||
|
||||
string SystemToolkit::byte_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1024.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1024.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
string SystemToolkit::bits_to_string(long b)
|
||||
{
|
||||
double numbytes = static_cast<double>(b);
|
||||
ostringstream oss;
|
||||
|
||||
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
|
||||
std::list<std::string>::iterator i = list.begin();
|
||||
|
||||
while(numbytes >= 1000.0 && i != list.end())
|
||||
{
|
||||
++i;
|
||||
numbytes /= 1000.0;
|
||||
}
|
||||
oss << std::fixed << std::setprecision(2) << numbytes << *i;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
string SystemToolkit::date_time_string()
|
||||
{
|
||||
@@ -171,19 +132,12 @@ string SystemToolkit::path_filename(const string& path)
|
||||
return path.substr(0, path.find_last_of(PATH_SEP) + 1);
|
||||
}
|
||||
|
||||
string SystemToolkit::trunc_filename(const string& path, int lenght)
|
||||
{
|
||||
string trunc = path;
|
||||
int l = path.size();
|
||||
if ( l > lenght ) {
|
||||
trunc = string("...") + path.substr( l - lenght + 3 );
|
||||
}
|
||||
return trunc;
|
||||
}
|
||||
|
||||
string SystemToolkit::extension_filename(const string& filename)
|
||||
{
|
||||
string ext = filename.substr(filename.find_last_of(".") + 1);
|
||||
string ext;
|
||||
auto loc = filename.find_last_of(".");
|
||||
if (loc != string::npos)
|
||||
ext = filename.substr( loc + 1 );
|
||||
return ext;
|
||||
}
|
||||
|
||||
@@ -323,7 +277,7 @@ std::string SystemToolkit::path_directory(const std::string& path)
|
||||
return directorypath;
|
||||
}
|
||||
|
||||
list<string> SystemToolkit::list_directory(const string& path, const string& filter)
|
||||
list<string> SystemToolkit::list_directory(const string& path, const list<string>& extensions)
|
||||
{
|
||||
list<string> ls;
|
||||
|
||||
@@ -335,13 +289,16 @@ list<string> SystemToolkit::list_directory(const string& path, const string& fil
|
||||
if ( ent->d_type == DT_REG)
|
||||
{
|
||||
string filename = string(ent->d_name);
|
||||
if ( extension_filename(filename) == filter)
|
||||
string ext = extension_filename(filename);
|
||||
if ( extensions.empty() || find(extensions.cbegin(), extensions.cend(), ext) != extensions.cend())
|
||||
ls.push_back( full_filename(path, filename) );
|
||||
}
|
||||
}
|
||||
closedir (dir);
|
||||
}
|
||||
|
||||
ls.sort();
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
@@ -376,27 +333,3 @@ void SystemToolkit::execute(const string& command)
|
||||
// std::thread (SystemToolkit::execute,
|
||||
// "gst-launch-1.0 udpsrc port=5000 ! application/x-rtp,encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink").detach();;
|
||||
|
||||
|
||||
// Using ICU transliteration :
|
||||
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
|
||||
|
||||
std::string SystemToolkit::transliterate(std::string input)
|
||||
{
|
||||
auto ucs = icu::UnicodeString::fromUTF8(input);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
|
||||
firstTrans->transliterate(ucs);
|
||||
delete firstTrans;
|
||||
|
||||
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
|
||||
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
|
||||
secondTrans->transliterate(ucs);
|
||||
delete secondTrans;
|
||||
|
||||
std::ostringstream output;
|
||||
output << ucs;
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
@@ -42,9 +42,6 @@ namespace SystemToolkit
|
||||
// extract the path of a filename from a full URI (e.g. file:://home/me/toto.mpg -> file:://home/me/)
|
||||
std::string path_filename(const std::string& path);
|
||||
|
||||
// Truncate a full filename to display the right part (e.g. file:://home/me/toto.mpg -> ...ome/me/toto.mpg)
|
||||
std::string trunc_filename(const std::string& path, int lenght);
|
||||
|
||||
// extract the extension of a filename
|
||||
std::string extension_filename(const std::string& filename);
|
||||
|
||||
@@ -52,7 +49,7 @@ namespace SystemToolkit
|
||||
std::string path_directory(const std::string& path);
|
||||
|
||||
// list all files of a directory mathing the given filter extension (if any)
|
||||
std::list<std::string> list_directory(const std::string& path, const std::string& filter = "");
|
||||
std::list<std::string> list_directory(const std::string& path, const std::list<std::string> &extensions);
|
||||
|
||||
// true of file exists
|
||||
bool file_exists(const std::string& path);
|
||||
@@ -69,18 +66,11 @@ namespace SystemToolkit
|
||||
// try to execute a command
|
||||
void execute(const std::string& command);
|
||||
|
||||
// return memory resident set size used (in bytes)
|
||||
// return memory used (in bytes)
|
||||
long memory_usage();
|
||||
// return maximum memory resident set size used (in bytes)
|
||||
long memory_max_usage();
|
||||
|
||||
// get a string to display memory size with unit KB, MB, GB, TB
|
||||
std::string byte_to_string(long b);
|
||||
|
||||
// get a string to display bit size with unit Kbit, MBit, Gbit, Tbit
|
||||
std::string bits_to_string(long b);
|
||||
|
||||
// get a transliteration to Latin of any string
|
||||
std::string transliterate(std::string input);
|
||||
}
|
||||
|
||||
#endif // SYSTEMTOOLKIT_H
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "defines.h"
|
||||
#include "Mixer.h"
|
||||
#include "Source.h"
|
||||
#include "Settings.h"
|
||||
#include "Resource.h"
|
||||
#include "PickingVisitor.h"
|
||||
|
||||
@@ -8,6 +8,9 @@ class TextureView : public View
|
||||
{
|
||||
public:
|
||||
TextureView();
|
||||
// non assignable class
|
||||
TextureView(TextureView const&) = delete;
|
||||
TextureView& operator=(TextureView const&) = delete;
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
|
||||
@@ -7,6 +7,9 @@ class TransitionView : public View
|
||||
{
|
||||
public:
|
||||
TransitionView();
|
||||
// non assignable class
|
||||
TransitionView(TransitionView const&) = delete;
|
||||
TransitionView& operator=(TransitionView const&) = delete;
|
||||
|
||||
void draw () override;
|
||||
void update (float dt) override;
|
||||
|
||||
@@ -11,6 +11,18 @@ UpdateCallback::UpdateCallback() : enabled_(true), finished_(false)
|
||||
|
||||
}
|
||||
|
||||
CopyCallback::CopyCallback(Node *target) : UpdateCallback(), target_(target)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CopyCallback::update(Node *n, float dt)
|
||||
{
|
||||
n->copyTransform(target_);
|
||||
finished_ = true;
|
||||
}
|
||||
|
||||
|
||||
MoveToCallback::MoveToCallback(glm::vec3 target, float duration) : UpdateCallback(),
|
||||
duration_(duration), progress_(0.f), initialized_(false), target_(target)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef UPDATECALLBACK_H
|
||||
#define UPDATECALLBACK_H
|
||||
|
||||
#include "GlmToolkit.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Node;
|
||||
|
||||
@@ -22,6 +22,15 @@ protected:
|
||||
bool finished_;
|
||||
};
|
||||
|
||||
class CopyCallback : public UpdateCallback
|
||||
{
|
||||
Node *target_;
|
||||
|
||||
public:
|
||||
CopyCallback(Node *target);
|
||||
void update(Node *n, float dt);
|
||||
};
|
||||
|
||||
class MoveToCallback : public UpdateCallback
|
||||
{
|
||||
float duration_;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,13 @@
|
||||
struct ImVec2;
|
||||
class Source;
|
||||
class MediaPlayer;
|
||||
class FrameBufferImage;
|
||||
|
||||
class SourcePreview {
|
||||
|
||||
Source *source_;
|
||||
std::string label_;
|
||||
bool reset_;
|
||||
|
||||
public:
|
||||
SourcePreview();
|
||||
@@ -30,26 +32,43 @@ public:
|
||||
inline bool filled() const { return source_ != nullptr; }
|
||||
};
|
||||
|
||||
class Thumbnail
|
||||
{
|
||||
float aspect_ratio_;
|
||||
uint texture_;
|
||||
|
||||
public:
|
||||
Thumbnail();
|
||||
~Thumbnail();
|
||||
|
||||
void reset();
|
||||
void set (const FrameBufferImage *image);
|
||||
void Render(float width);
|
||||
};
|
||||
|
||||
class Navigator
|
||||
{
|
||||
// geometry left bar & pannel
|
||||
float width_;
|
||||
float height_;
|
||||
float pannel_width_;
|
||||
float sourcelist_height_;
|
||||
float padding_width_;
|
||||
|
||||
// behavior pannel
|
||||
bool show_config_;
|
||||
bool pannel_visible_;
|
||||
bool view_pannel_visible;
|
||||
bool selected_button[NAV_COUNT];
|
||||
int pattern_type;
|
||||
std::list<std::string> _selectedFiles;
|
||||
void clearButtonSelection();
|
||||
void applyButtonSelection(int index);
|
||||
|
||||
// side pannels
|
||||
void RenderSourcePannel(Source *s);
|
||||
void RenderMainPannel();
|
||||
void RenderMainPannelVimix();
|
||||
void RenderMainPannelSettings();
|
||||
void RenderTransitionPannel();
|
||||
void RenderNewPannel();
|
||||
void RenderViewPannel(ImVec2 draw_pos, ImVec2 draw_size);
|
||||
@@ -99,6 +118,7 @@ public:
|
||||
void Render();
|
||||
};
|
||||
|
||||
|
||||
class UserInterface
|
||||
{
|
||||
friend class Navigator;
|
||||
@@ -123,8 +143,8 @@ class UserInterface
|
||||
|
||||
// Private Constructor
|
||||
UserInterface();
|
||||
UserInterface(UserInterface const& copy); // Not Implemented
|
||||
UserInterface& operator=(UserInterface const& copy); // Not Implemented
|
||||
UserInterface(UserInterface const& copy) = delete;
|
||||
UserInterface& operator=(UserInterface const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
@@ -149,9 +169,7 @@ public:
|
||||
inline bool altModifier() const { return alt_modifier_active; }
|
||||
inline bool shiftModifier() const { return shift_modifier_active; }
|
||||
|
||||
void StartScreenshot();
|
||||
void showPannel(int id = 0);
|
||||
|
||||
void showSourceEditor(Source *s);
|
||||
void showMediaPlayer(MediaPlayer *mp);
|
||||
|
||||
@@ -159,6 +177,8 @@ public:
|
||||
std::string currentTextEdit;
|
||||
void fillShaderEditor(const std::string &text);
|
||||
|
||||
void StartScreenshot();
|
||||
|
||||
protected:
|
||||
|
||||
void showMenuFile();
|
||||
@@ -171,10 +191,12 @@ protected:
|
||||
void RenderHistory();
|
||||
void RenderShaderEditor();
|
||||
int RenderViewNavigator(int* shift);
|
||||
void RenderAbout(bool* p_open);
|
||||
void RenderNotes();
|
||||
|
||||
void handleKeyboard();
|
||||
void handleMouse();
|
||||
void handleScreenshot();
|
||||
void RenderAbout(bool* p_open);
|
||||
};
|
||||
|
||||
#endif /* #define __UI_MANAGER_H_ */
|
||||
|
||||
9
View.cpp
9
View.cpp
@@ -114,11 +114,8 @@ void View::initiate()
|
||||
{
|
||||
current_action_ = "";
|
||||
for (auto sit = Mixer::manager().session()->begin();
|
||||
sit != Mixer::manager().session()->end(); sit++){
|
||||
|
||||
(*sit)->stored_status_->copyTransform((*sit)->group(mode_));
|
||||
}
|
||||
|
||||
sit != Mixer::manager().session()->end(); ++sit)
|
||||
(*sit)->store(mode_);
|
||||
}
|
||||
|
||||
void View::terminate()
|
||||
@@ -190,7 +187,7 @@ void View::selectAll()
|
||||
{
|
||||
Mixer::selection().clear();
|
||||
for(auto sit = Mixer::manager().session()->begin();
|
||||
sit != Mixer::manager().session()->end(); sit++) {
|
||||
sit != Mixer::manager().session()->end(); ++sit) {
|
||||
if (canSelect(*sit))
|
||||
Mixer::selection().add(*sit);
|
||||
}
|
||||
|
||||
2
View.h
2
View.h
@@ -80,7 +80,7 @@ public:
|
||||
return Cursor ();
|
||||
}
|
||||
|
||||
//TODO: test mouse over provided a point in screen coordinates
|
||||
// test mouse over provided a point in screen coordinates
|
||||
virtual Cursor over (glm::vec2) {
|
||||
return Cursor ();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class RenderSource;
|
||||
class CloneSource;
|
||||
class NetworkSource;
|
||||
class MixingGroup;
|
||||
class MultiFileSource;
|
||||
|
||||
// Declares the interface for the visitors
|
||||
class Visitor {
|
||||
@@ -81,6 +82,7 @@ public:
|
||||
virtual void visit (SessionGroupSource&) {}
|
||||
virtual void visit (RenderSource&) {}
|
||||
virtual void visit (CloneSource&) {}
|
||||
virtual void visit (MultiFileSource&) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
10
defines.h
10
defines.h
@@ -59,20 +59,24 @@
|
||||
#define TRANSITION_MIN_DURATION 0.2f
|
||||
#define TRANSITION_MAX_DURATION 10.f
|
||||
#define ARROWS_MOVEMENT_FACTOR 4.f
|
||||
#define SESSION_THUMBNAIL_HEIGHT 120.f
|
||||
|
||||
#define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix"
|
||||
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player"
|
||||
#define IMGUI_TITLE_HISTORY ICON_FA_HISTORY " History"
|
||||
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Tools"
|
||||
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code"
|
||||
#define IMGUI_TITLE_LOGS ICON_FA_LIST " Logs"
|
||||
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Toolbox"
|
||||
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code Editor"
|
||||
#define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput"
|
||||
#define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?"
|
||||
#define IMGUI_LABEL_RECENT_FILES " Select recent"
|
||||
#define IMGUI_LABEL_RECENT_FILES " Recent files"
|
||||
#define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing()
|
||||
#define IMGUI_TOP_ALIGN 10
|
||||
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
|
||||
#define IMGUI_COLOR_RECORD 1.0, 0.05, 0.05
|
||||
#define IMGUI_COLOR_STREAM 0.05, 0.8, 1.0
|
||||
#define IMGUI_NOTIFICATION_DURATION 2.5f
|
||||
#define IMGUI_TOOLTIP_TIMEOUT 80
|
||||
#ifdef APPLE
|
||||
#define CTRL_MOD "Cmd+"
|
||||
#else
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user