mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Mixing view handles to grab and scale limbo area. Saving of user defined limit in Session (and snapshot). Testing for source activation outside of update during session update loop.
792 lines
26 KiB
C++
792 lines
26 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2020-2021 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include <iostream>
|
|
#include <locale>
|
|
|
|
#include <tinyxml2.h>
|
|
using namespace tinyxml2;
|
|
|
|
#include "Log.h"
|
|
#include "defines.h"
|
|
#include "Scene.h"
|
|
#include "Decorations.h"
|
|
#include "Source.h"
|
|
#include "MediaSource.h"
|
|
#include "Session.h"
|
|
#include "SessionSource.h"
|
|
#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 "SessionVisitor.h"
|
|
|
|
|
|
bool SessionVisitor::saveSession(const std::string& filename, Session *session)
|
|
{
|
|
// impose C locale
|
|
setlocale(LC_ALL, "C");
|
|
|
|
// creation of XML doc
|
|
XMLDocument xmlDoc;
|
|
|
|
XMLElement *rootnode = xmlDoc.NewElement(APP_NAME);
|
|
rootnode->SetAttribute("major", XML_VERSION_MAJOR);
|
|
rootnode->SetAttribute("minor", XML_VERSION_MINOR);
|
|
rootnode->SetAttribute("size", session->numSource());
|
|
rootnode->SetAttribute("date", SystemToolkit::date_time_string().c_str());
|
|
rootnode->SetAttribute("resolution", session->frame()->info().c_str());
|
|
xmlDoc.InsertEndChild(rootnode);
|
|
|
|
// 1. list of sources
|
|
XMLElement *sessionNode = xmlDoc.NewElement("Session");
|
|
xmlDoc.InsertEndChild(sessionNode);
|
|
SessionVisitor sv(&xmlDoc, sessionNode);
|
|
sv.sessionFilePath_ = SystemToolkit::path_filename(filename);
|
|
for (auto iter = session->begin(); iter != session->end(); ++iter, sv.setRoot(sessionNode) )
|
|
// source visitor
|
|
(*iter)->accept(sv);
|
|
|
|
// save session attributes
|
|
sessionNode->SetAttribute("activationThreshold", session->activationThreshold());
|
|
|
|
// save the thumbnail
|
|
FrameBufferImage *thumbnail = session->thumbnail();
|
|
if (thumbnail != nullptr && thumbnail->width > 0 && thumbnail->height > 0) {
|
|
XMLElement *thumbnailelement = xmlDoc.NewElement("Thumbnail");
|
|
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
|
|
if (imageelement) {
|
|
sessionNode->InsertEndChild(thumbnailelement);
|
|
thumbnailelement->InsertEndChild(imageelement);
|
|
}
|
|
}
|
|
// if no thumbnail is set by user, capture thumbnail now
|
|
else {
|
|
thumbnail = session->renderThumbnail();
|
|
if (thumbnail) {
|
|
XMLElement *imageelement = SessionVisitor::ImageToXML(thumbnail, &xmlDoc);
|
|
if (imageelement)
|
|
sessionNode->InsertEndChild(imageelement);
|
|
delete thumbnail;
|
|
}
|
|
}
|
|
|
|
// 2. config of views
|
|
saveConfig( &xmlDoc, session );
|
|
|
|
// 3. snapshots
|
|
saveSnapshots( &xmlDoc, session );
|
|
|
|
// 4. optional notes
|
|
saveNotes( &xmlDoc, session );
|
|
|
|
// 5. optional playlists
|
|
savePlayGroups( &xmlDoc, session );
|
|
|
|
// save file to disk
|
|
return ( XMLSaveDoc(&xmlDoc, filename) );
|
|
}
|
|
|
|
void SessionVisitor::saveConfig(tinyxml2::XMLDocument *doc, Session *session)
|
|
{
|
|
if (doc != nullptr && session != nullptr)
|
|
{
|
|
XMLElement *views = doc->NewElement("Views");
|
|
|
|
XMLElement *mixing = doc->NewElement( "Mixing" );
|
|
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), doc));
|
|
views->InsertEndChild(mixing);
|
|
|
|
XMLElement *geometry = doc->NewElement( "Geometry" );
|
|
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), doc));
|
|
views->InsertEndChild(geometry);
|
|
|
|
XMLElement *layer = doc->NewElement( "Layer" );
|
|
layer->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::LAYER), doc));
|
|
views->InsertEndChild(layer);
|
|
|
|
XMLElement *appearance = doc->NewElement( "Texture" );
|
|
appearance->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::TEXTURE), doc));
|
|
views->InsertEndChild(appearance);
|
|
|
|
XMLElement *render = doc->NewElement( "Rendering" );
|
|
render->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::RENDERING), doc));
|
|
views->InsertEndChild(render);
|
|
|
|
doc->InsertEndChild(views);
|
|
}
|
|
}
|
|
|
|
|
|
void SessionVisitor::saveSnapshots(tinyxml2::XMLDocument *doc, Session *session)
|
|
{
|
|
if (doc != nullptr && session != nullptr)
|
|
{
|
|
XMLElement *snapshots = doc->NewElement("Snapshots");
|
|
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
|
|
for( ; N ; N=N->NextSiblingElement())
|
|
snapshots->InsertEndChild( N->DeepClone( doc ));
|
|
doc->InsertEndChild(snapshots);
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::saveNotes(tinyxml2::XMLDocument *doc, Session *session)
|
|
{
|
|
if (doc != nullptr && session != nullptr)
|
|
{
|
|
XMLElement *notes = doc->NewElement("Notes");
|
|
for (auto nit = session->beginNotes(); nit != session->endNotes(); ++nit) {
|
|
XMLElement *note = doc->NewElement( "Note" );
|
|
note->SetAttribute("large", (*nit).large );
|
|
note->SetAttribute("stick", (*nit).stick );
|
|
XMLElement *pos = doc->NewElement("pos");
|
|
pos->InsertEndChild( XMLElementFromGLM(doc, (*nit).pos) );
|
|
note->InsertEndChild(pos);
|
|
XMLElement *size = doc->NewElement("size");
|
|
size->InsertEndChild( XMLElementFromGLM(doc, (*nit).size) );
|
|
note->InsertEndChild(size);
|
|
XMLElement *content = doc->NewElement("text");
|
|
XMLText *text = doc->NewText( (*nit).text.c_str() );
|
|
content->InsertEndChild( text );
|
|
note->InsertEndChild(content);
|
|
|
|
notes->InsertEndChild(note);
|
|
}
|
|
doc->InsertEndChild(notes);
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::savePlayGroups(tinyxml2::XMLDocument *doc, Session *session)
|
|
{
|
|
if (doc != nullptr && session != nullptr)
|
|
{
|
|
XMLElement *playlistNode = doc->NewElement("PlayGroups");
|
|
std::vector<SourceIdList> pl = session->getPlayGroups();
|
|
for (auto plit = pl.begin(); plit != pl.end(); ++plit) {
|
|
XMLElement *list = doc->NewElement("PlayGroup");
|
|
playlistNode->InsertEndChild(list);
|
|
for (auto id = plit->begin(); id != plit->end(); ++id) {
|
|
XMLElement *sour = doc->NewElement("source");
|
|
sour->SetAttribute("id", *id);
|
|
list->InsertEndChild(sour);
|
|
}
|
|
}
|
|
doc->InsertEndChild(playlistNode);
|
|
}
|
|
}
|
|
|
|
SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
|
|
tinyxml2::XMLElement *root,
|
|
bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root)
|
|
{
|
|
// impose C locale
|
|
setlocale(LC_ALL, "C");
|
|
|
|
if (doc == nullptr)
|
|
xmlDoc_ = new XMLDocument;
|
|
else
|
|
xmlDoc_ = doc;
|
|
}
|
|
|
|
XMLElement *SessionVisitor::NodeToXML(const Node &n, XMLDocument *doc)
|
|
{
|
|
XMLElement *newelement = doc->NewElement("Node");
|
|
newelement->SetAttribute("visible", n.visible_);
|
|
newelement->SetAttribute("id", n.id());
|
|
|
|
XMLElement *scale = doc->NewElement("scale");
|
|
scale->InsertEndChild( XMLElementFromGLM(doc, n.scale_) );
|
|
newelement->InsertEndChild(scale);
|
|
|
|
XMLElement *translation = doc->NewElement("translation");
|
|
translation->InsertEndChild( XMLElementFromGLM(doc, n.translation_) );
|
|
newelement->InsertEndChild(translation);
|
|
|
|
XMLElement *rotation = doc->NewElement("rotation");
|
|
rotation->InsertEndChild( XMLElementFromGLM(doc, n.rotation_) );
|
|
newelement->InsertEndChild(rotation);
|
|
|
|
XMLElement *crop = doc->NewElement("crop");
|
|
crop->InsertEndChild( XMLElementFromGLM(doc, n.crop_) );
|
|
newelement->InsertEndChild(crop);
|
|
|
|
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_);
|
|
|
|
// insert into hierarchy
|
|
xmlCurrent_->InsertEndChild(newelement);
|
|
|
|
// parent for next visits
|
|
xmlCurrent_ = newelement;
|
|
}
|
|
|
|
void SessionVisitor::visit(Group &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "Group");
|
|
|
|
if (recursive_) {
|
|
// loop over members of a group
|
|
XMLElement *group = xmlCurrent_;
|
|
for (NodeSet::iterator node = n.begin(); node != n.end(); ++node) {
|
|
(*node)->accept(*this);
|
|
// revert to group as current
|
|
xmlCurrent_ = group;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::visit(Switch &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "Switch");
|
|
xmlCurrent_->SetAttribute("active", n.active());
|
|
|
|
if (recursive_) {
|
|
// loop over members of the group
|
|
XMLElement *group = xmlCurrent_;
|
|
for(uint i = 0; i < n.numChildren(); ++i) {
|
|
n.child(i)->accept(*this);
|
|
// revert to group as current
|
|
xmlCurrent_ = group;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::visit(Primitive &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "Primitive");
|
|
|
|
if (recursive_) {
|
|
// go over members of a primitive
|
|
XMLElement *Primitive = xmlCurrent_;
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement("Shader");
|
|
n.shader()->accept(*this);
|
|
Primitive->InsertEndChild(xmlCurrent_);
|
|
|
|
// revert to primitive as current
|
|
xmlCurrent_ = Primitive;
|
|
}
|
|
}
|
|
|
|
|
|
void SessionVisitor::visit(Surface &)
|
|
{
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit(ImageSurface &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "ImageSurface");
|
|
|
|
XMLText *filename = xmlDoc_->NewText( n.resource().c_str() );
|
|
XMLElement *image = xmlDoc_->NewElement("resource");
|
|
image->InsertEndChild(filename);
|
|
xmlCurrent_->InsertEndChild(image);
|
|
}
|
|
|
|
void SessionVisitor::visit(FrameBufferSurface &)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "FrameBufferSurface");
|
|
}
|
|
|
|
void SessionVisitor::visit(MediaPlayer &n)
|
|
{
|
|
XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer");
|
|
newelement->SetAttribute("id", n.id());
|
|
|
|
if (!n.isImage()) {
|
|
newelement->SetAttribute("play", n.isPlaying());
|
|
newelement->SetAttribute("loop", (int) n.loop());
|
|
newelement->SetAttribute("speed", n.playSpeed());
|
|
newelement->SetAttribute("software_decoding", n.softwareDecodingForced());
|
|
newelement->SetAttribute("rewind_on_disabled", n.rewindOnDisabled());
|
|
newelement->SetAttribute("sync_to_metronome", (int) n.syncToMetronome());
|
|
|
|
// timeline
|
|
XMLElement *timelineelement = xmlDoc_->NewElement("Timeline");
|
|
|
|
// gaps in timeline
|
|
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
|
|
TimeIntervalSet gaps = n.timeline()->gaps();
|
|
for( auto it = gaps.begin(); it!= gaps.end(); ++it) {
|
|
XMLElement *g = xmlDoc_->NewElement("Interval");
|
|
g->SetAttribute("begin", (*it).begin);
|
|
g->SetAttribute("end", (*it).end);
|
|
gapselement->InsertEndChild(g);
|
|
}
|
|
timelineelement->InsertEndChild(gapselement);
|
|
|
|
// fading in timeline
|
|
XMLElement *fadingelement = xmlDoc_->NewElement("Fading");
|
|
XMLElement *array = XMLElementEncodeArray(xmlDoc_, n.timeline()->fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float));
|
|
fadingelement->InsertEndChild(array);
|
|
timelineelement->InsertEndChild(fadingelement);
|
|
newelement->InsertEndChild(timelineelement);
|
|
}
|
|
|
|
xmlCurrent_->InsertEndChild(newelement);
|
|
}
|
|
|
|
void SessionVisitor::visit(Shader &n)
|
|
{
|
|
// Shader of a simple type
|
|
xmlCurrent_->SetAttribute("type", "Shader");
|
|
xmlCurrent_->SetAttribute("id", n.id());
|
|
|
|
XMLElement *color = xmlDoc_->NewElement("color");
|
|
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
|
|
xmlCurrent_->InsertEndChild(color);
|
|
|
|
XMLElement *blend = xmlDoc_->NewElement("blending");
|
|
blend->SetAttribute("mode", int(n.blending) );
|
|
xmlCurrent_->InsertEndChild(blend);
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit(ImageShader &n)
|
|
{
|
|
// Shader of a textured type
|
|
xmlCurrent_->SetAttribute("type", "ImageShader");
|
|
xmlCurrent_->SetAttribute("id", n.id());
|
|
|
|
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
|
|
uniforms->SetAttribute("stipple", n.stipple);
|
|
xmlCurrent_->InsertEndChild(uniforms);
|
|
}
|
|
|
|
void SessionVisitor::visit(MaskShader &n)
|
|
{
|
|
// Shader of a mask type
|
|
xmlCurrent_->SetAttribute("type", "MaskShader");
|
|
xmlCurrent_->SetAttribute("id", n.id());
|
|
xmlCurrent_->SetAttribute("mode", n.mode);
|
|
xmlCurrent_->SetAttribute("shape", n.shape);
|
|
|
|
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
|
|
uniforms->SetAttribute("blur", n.blur);
|
|
uniforms->SetAttribute("option", n.option);
|
|
XMLElement *size = xmlDoc_->NewElement("size");
|
|
size->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.size) );
|
|
uniforms->InsertEndChild(size);
|
|
xmlCurrent_->InsertEndChild(uniforms);
|
|
}
|
|
|
|
void SessionVisitor::visit(ImageProcessingShader &n)
|
|
{
|
|
// Shader of a textured type
|
|
xmlCurrent_->SetAttribute("type", "ImageProcessingShader");
|
|
xmlCurrent_->SetAttribute("id", n.id());
|
|
|
|
XMLElement *filter = xmlDoc_->NewElement("uniforms");
|
|
filter->SetAttribute("brightness", n.brightness);
|
|
filter->SetAttribute("contrast", n.contrast);
|
|
filter->SetAttribute("saturation", n.saturation);
|
|
filter->SetAttribute("hueshift", n.hueshift);
|
|
filter->SetAttribute("threshold", n.threshold);
|
|
filter->SetAttribute("lumakey", n.lumakey);
|
|
filter->SetAttribute("nbColors", n.nbColors);
|
|
filter->SetAttribute("invert", n.invert);
|
|
filter->SetAttribute("chromadelta", n.chromadelta);
|
|
filter->SetAttribute("filter", n.filterid);
|
|
xmlCurrent_->InsertEndChild(filter);
|
|
|
|
XMLElement *gamma = xmlDoc_->NewElement("gamma");
|
|
gamma->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.gamma) );
|
|
xmlCurrent_->InsertEndChild(gamma);
|
|
|
|
XMLElement *levels = xmlDoc_->NewElement("levels");
|
|
levels->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.levels) );
|
|
xmlCurrent_->InsertEndChild(levels);
|
|
|
|
XMLElement *chromakey = xmlDoc_->NewElement("chromakey");
|
|
chromakey->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.chromakey) );
|
|
xmlCurrent_->InsertEndChild(chromakey);
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit(LineStrip &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "LineStrip");
|
|
|
|
XMLElement *points_node = xmlDoc_->NewElement("points");
|
|
std::vector<glm::vec2> path = n.path();
|
|
for(size_t i = 0; i < path.size(); ++i)
|
|
{
|
|
XMLElement *p = XMLElementFromGLM(xmlDoc_, path[i]);
|
|
p->SetAttribute("index", (int) i);
|
|
points_node->InsertEndChild(p);
|
|
}
|
|
xmlCurrent_->InsertEndChild(points_node);
|
|
}
|
|
|
|
void SessionVisitor::visit(LineSquare &)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "LineSquare");
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit(Mesh &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "Mesh");
|
|
|
|
XMLText *filename = xmlDoc_->NewText( n.meshPath().c_str() );
|
|
XMLElement *obj = xmlDoc_->NewElement("resource");
|
|
obj->InsertEndChild(filename);
|
|
xmlCurrent_->InsertEndChild(obj);
|
|
|
|
filename = xmlDoc_->NewText( n.texturePath().c_str() );
|
|
XMLElement *tex = xmlDoc_->NewElement("texture");
|
|
tex->InsertEndChild(filename);
|
|
xmlCurrent_->InsertEndChild(tex);
|
|
}
|
|
|
|
void SessionVisitor::visit(Frame &n)
|
|
{
|
|
// Node of a different type
|
|
xmlCurrent_->SetAttribute("type", "Frame");
|
|
|
|
XMLElement *color = xmlDoc_->NewElement("color");
|
|
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
|
|
xmlCurrent_->InsertEndChild(color);
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit(Scene &n)
|
|
{
|
|
XMLElement *xmlRoot = xmlDoc_->NewElement("Scene");
|
|
xmlDoc_->InsertEndChild(xmlRoot);
|
|
|
|
// start recursive traverse from root node
|
|
recursive_ = true;
|
|
xmlCurrent_ = xmlRoot;
|
|
n.root()->accept(*this);
|
|
}
|
|
|
|
void SessionVisitor::visit (Source& s)
|
|
{
|
|
XMLElement *sourceNode = xmlDoc_->NewElement( "Source" );
|
|
sourceNode->SetAttribute("id", s.id());
|
|
sourceNode->SetAttribute("name", s.name().c_str() );
|
|
sourceNode->SetAttribute("locked", s.locked() );
|
|
|
|
// insert into hierarchy
|
|
xmlCurrent_->InsertFirstChild(sourceNode);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Mixing" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.groupNode(View::MIXING)->accept(*this);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Geometry" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.groupNode(View::GEOMETRY)->accept(*this);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Layer" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.groupNode(View::LAYER)->accept(*this);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Texture" );
|
|
xmlCurrent_->SetAttribute("mirrored", s.textureMirrored() );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.groupNode(View::TEXTURE)->accept(*this);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Blending" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.blendingShader()->accept(*this);
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Mask" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.maskShader()->accept(*this);
|
|
// if we are saving a pain mask
|
|
if (s.maskShader()->mode == MaskShader::PAINT) {
|
|
// get the mask previously stored
|
|
XMLElement *imageelement = SessionVisitor::ImageToXML(s.getMask(), xmlDoc_);
|
|
if (imageelement)
|
|
xmlCurrent_->InsertEndChild(imageelement);
|
|
}
|
|
|
|
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
|
|
xmlCurrent_->SetAttribute("enabled", s.imageProcessingEnabled());
|
|
xmlCurrent_->SetAttribute("follow", s.processingshader_link_.id());
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.processingShader()->accept(*this);
|
|
|
|
if (s.mixingGroup()) {
|
|
xmlCurrent_ = xmlDoc_->NewElement( "MixingGroup" );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
s.mixingGroup()->accept(*this);
|
|
}
|
|
|
|
xmlCurrent_ = sourceNode; // parent for next visits (other subtypes of Source)
|
|
}
|
|
|
|
void SessionVisitor::visit (MediaSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "MediaSource");
|
|
|
|
XMLElement *uri = xmlDoc_->NewElement("uri");
|
|
xmlCurrent_->InsertEndChild(uri);
|
|
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
|
|
uri->InsertEndChild( text );
|
|
|
|
if (!sessionFilePath_.empty())
|
|
uri->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.path(), sessionFilePath_).c_str());
|
|
|
|
s.mediaplayer()->accept(*this);
|
|
}
|
|
|
|
void SessionVisitor::visit (SessionFileSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "SessionSource");
|
|
if (s.session() != nullptr)
|
|
xmlCurrent_->SetAttribute("fading", s.session()->fading());
|
|
|
|
XMLElement *path = xmlDoc_->NewElement("path");
|
|
xmlCurrent_->InsertEndChild(path);
|
|
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
|
|
path->InsertEndChild( text );
|
|
|
|
if (!sessionFilePath_.empty())
|
|
path->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.path(), sessionFilePath_).c_str());
|
|
}
|
|
|
|
void SessionVisitor::visit (SessionGroupSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "GroupSource");
|
|
|
|
Session *se = s.session();
|
|
|
|
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
|
|
xmlCurrent_->InsertEndChild(sessionNode);
|
|
|
|
for (auto iter = se->begin(); iter != se->end(); ++iter){
|
|
setRoot(sessionNode);
|
|
(*iter)->accept(*this);
|
|
}
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit (RenderSource&)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "RenderSource");
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
void SessionVisitor::visit (PatternSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "PatternSource");
|
|
|
|
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)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "DeviceSource");
|
|
xmlCurrent_->SetAttribute("device", s.device().c_str() );
|
|
}
|
|
|
|
void SessionVisitor::visit (NetworkSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "NetworkSource");
|
|
xmlCurrent_->SetAttribute("connection", s.connection().c_str() );
|
|
}
|
|
|
|
void SessionVisitor::visit (MixingGroup& g)
|
|
{
|
|
xmlCurrent_->SetAttribute("size", g.size());
|
|
|
|
for (auto it = g.begin(); it != g.end(); ++it) {
|
|
XMLElement *sour = xmlDoc_->NewElement("source");
|
|
sour->SetAttribute("id", (*it)->id());
|
|
xmlCurrent_->InsertEndChild(sour);
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
if (!sessionFilePath_.empty())
|
|
sequence->SetAttribute("relative", SystemToolkit::path_relative_to_path(s.sequence().location, sessionFilePath_).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 = "";
|
|
|
|
if (!list.empty()) {
|
|
|
|
// create xml doc and root node
|
|
tinyxml2::XMLDocument xmlDoc;
|
|
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
|
|
selectionNode->SetAttribute("size", (int) list.size());
|
|
xmlDoc.InsertEndChild(selectionNode);
|
|
|
|
// fill doc by visiting sources
|
|
SourceList selection_clones_;
|
|
SessionVisitor sv(&xmlDoc, selectionNode);
|
|
for (auto iter = list.begin(); iter != list.end(); ++iter, sv.setRoot(selectionNode) ){
|
|
// start with clones
|
|
CloneSource *clone = dynamic_cast<CloneSource *>(*iter);
|
|
if (clone)
|
|
(*iter)->accept(sv);
|
|
else
|
|
selection_clones_.push_back(*iter);
|
|
}
|
|
// add others in front
|
|
for (auto iter = selection_clones_.begin(); iter != selection_clones_.end(); ++iter, sv.setRoot(selectionNode) ){
|
|
(*iter)->accept(sv);
|
|
}
|
|
|
|
// get compact string
|
|
tinyxml2::XMLPrinter xmlPrint(0, true);
|
|
xmlDoc.Print( &xmlPrint );
|
|
x = xmlPrint.CStr();
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
std::string SessionVisitor::getClipboard(Source * const s)
|
|
{
|
|
std::string x = "";
|
|
|
|
if (s != nullptr) {
|
|
// create xml doc and root node
|
|
tinyxml2::XMLDocument xmlDoc;
|
|
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
|
|
selectionNode->SetAttribute("size", 1);
|
|
xmlDoc.InsertEndChild(selectionNode);
|
|
|
|
// visit source
|
|
SessionVisitor sv(&xmlDoc, selectionNode);
|
|
s->accept(sv);
|
|
|
|
// get compact string
|
|
tinyxml2::XMLPrinter xmlPrint(0, true);
|
|
xmlDoc.Print( &xmlPrint );
|
|
x = xmlPrint.CStr();
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
std::string SessionVisitor::getClipboard(ImageProcessingShader * const s)
|
|
{
|
|
std::string x = "";
|
|
|
|
if (s != nullptr) {
|
|
// create xml doc and root node
|
|
tinyxml2::XMLDocument xmlDoc;
|
|
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
|
|
xmlDoc.InsertEndChild(selectionNode);
|
|
|
|
tinyxml2::XMLElement *imgprocNode = xmlDoc.NewElement( "ImageProcessing" );
|
|
selectionNode->InsertEndChild(imgprocNode);
|
|
|
|
// visit source
|
|
SessionVisitor sv(&xmlDoc, imgprocNode);
|
|
s->accept(sv);
|
|
|
|
// get compact string
|
|
tinyxml2::XMLPrinter xmlPrint(0, true);
|
|
xmlDoc.Print( &xmlPrint );
|
|
x = xmlPrint.CStr();
|
|
}
|
|
|
|
return x;
|
|
}
|