mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
1204 lines
39 KiB
C++
1204 lines
39 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2023 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 <tinyxml2.h>
|
|
using namespace tinyxml2;
|
|
|
|
#include "defines.h"
|
|
#include "Scene.h"
|
|
#include "Decorations.h"
|
|
#include "Source.h"
|
|
#include "SourceCallback.h"
|
|
#include "CloneSource.h"
|
|
#include "FrameBufferFilter.h"
|
|
#include "DelayFilter.h"
|
|
#include "ImageFilter.h"
|
|
#include "RenderSource.h"
|
|
#include "MediaSource.h"
|
|
#include "Session.h"
|
|
#include "SessionSource.h"
|
|
#include "PatternSource.h"
|
|
#include "DeviceSource.h"
|
|
#include "ScreenCaptureSource.h"
|
|
#include "NetworkSource.h"
|
|
#include "SrtReceiverSource.h"
|
|
#include "MultiFileSource.h"
|
|
#include "TextSource.h"
|
|
#include "ImageShader.h"
|
|
#include "ImageProcessingShader.h"
|
|
#include "MediaPlayer.h"
|
|
#include "MixingGroup.h"
|
|
#include "SystemToolkit.h"
|
|
#include "ShaderSource.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->size());
|
|
rootnode->SetAttribute("total", session->numSources());
|
|
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("id", session->id());
|
|
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 );
|
|
|
|
// 5. optional playlists
|
|
saveInputCallbacks( &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)
|
|
{
|
|
// create element
|
|
XMLElement *snapshots = doc->NewElement("Snapshots");
|
|
|
|
// access to session snapshot
|
|
session->snapshots()->access_.lock();
|
|
const XMLElement* N = session->snapshots()->xmlDoc_->FirstChildElement();
|
|
for( ; N ; N=N->NextSiblingElement())
|
|
snapshots->InsertEndChild( N->DeepClone( doc ));
|
|
session->snapshots()->access_.unlock();
|
|
|
|
// insert element
|
|
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->getAllBatch();
|
|
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);
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::saveInputCallbacks(tinyxml2::XMLDocument *doc, Session *session)
|
|
{
|
|
// invalid inputs
|
|
if (doc != nullptr && session != nullptr)
|
|
{
|
|
// create node
|
|
XMLElement *inputsNode = doc->NewElement("InputCallbacks");
|
|
doc->InsertEndChild(inputsNode);
|
|
|
|
// list of inputs assigned in the session
|
|
std::list<uint> inputs = session->assignedInputs();
|
|
if (!inputs.empty()) {
|
|
// loop over list of inputs
|
|
for (auto i = inputs.begin(); i != inputs.end(); ++i) {
|
|
// get all callbacks for this input
|
|
auto result = session->getSourceCallbacks(*i);
|
|
for (auto kit = result.cbegin(); kit != result.cend(); ++kit) {
|
|
// create node for this callback
|
|
XMLElement *cbNode = doc->NewElement("Callback");
|
|
cbNode->SetAttribute("input", *i);
|
|
// 1. Case of variant as Source pointer
|
|
if (Source * const* v = std::get_if<Source *>(&kit->first)) {
|
|
cbNode->SetAttribute("id", (*v)->id());
|
|
}
|
|
// 2. Case of variant as index of batch
|
|
else if ( const size_t* v = std::get_if<size_t>(&kit->first)) {
|
|
cbNode->SetAttribute("batch", (uint64_t) *v);
|
|
}
|
|
inputsNode->InsertEndChild(cbNode);
|
|
// launch visitor to complete attributes
|
|
SessionVisitor sv(doc, cbNode);
|
|
kit->second->accept(sv);
|
|
}
|
|
}
|
|
}
|
|
|
|
// save array of synchronyzation mode for all inputs (CSV)
|
|
std::ostringstream oss;
|
|
std::vector<Metronome::Synchronicity> synch = session->getInputSynchrony();
|
|
for (auto token = synch.begin(); token != synch.end(); ++token)
|
|
oss << (int) *token << ';';
|
|
XMLElement *syncNode = doc->NewElement("Synchrony");
|
|
XMLText *text = doc->NewText( oss.str().c_str() );
|
|
syncNode->InsertEndChild(text);
|
|
inputsNode->InsertEndChild(syncNode);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
XMLElement *data = doc->NewElement("data");
|
|
data->InsertEndChild( XMLElementFromGLM(doc, n.data_) );
|
|
newelement->InsertEndChild(data);
|
|
|
|
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(Stream &n)
|
|
{
|
|
XMLElement *newelement = xmlDoc_->NewElement("Stream");
|
|
newelement->SetAttribute("id", n.id());
|
|
|
|
if (!n.singleFrame()) {
|
|
newelement->SetAttribute("rewind_on_disabled", n.rewindOnDisabled());
|
|
}
|
|
|
|
xmlCurrent_->InsertEndChild(newelement);
|
|
}
|
|
|
|
void SessionVisitor::visit(MediaPlayer &n)
|
|
{
|
|
XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer");
|
|
newelement->SetAttribute("id", n.id());
|
|
|
|
if (!n.singleFrame()) {
|
|
newelement->SetAttribute("loop", (int) n.loop());
|
|
// special case; if media player stopped by loop (not by user)
|
|
// then override the play status of the Source to play the video
|
|
if (n.loopStatus() != MediaPlayer::LOOP_STATUS_DEFAULT)
|
|
xmlCurrent_->SetAttribute("play", true );
|
|
|
|
newelement->SetAttribute("speed", n.playSpeed());
|
|
newelement->SetAttribute("video_effect", n.videoEffect().c_str());
|
|
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");
|
|
timelineelement->SetAttribute("begin", (uint64_t) n.timeline()->begin());
|
|
timelineelement->SetAttribute("end", (uint64_t) n.timeline()->end());
|
|
timelineelement->SetAttribute("step", (uint64_t) n.timeline()->step());
|
|
|
|
// 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", (uint64_t) (*it).begin);
|
|
g->SetAttribute("end", (uint64_t) (*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);
|
|
|
|
// flags in timeline
|
|
XMLElement *flagselement = xmlDoc_->NewElement("Flags");
|
|
TimeIntervalSet flags = n.timeline()->flags();
|
|
for( auto it = flags.begin(); it!= flags.end(); ++it) {
|
|
XMLElement *f = xmlDoc_->NewElement("Interval");
|
|
f->SetAttribute("begin", (uint64_t) (*it).begin);
|
|
f->SetAttribute("end", (uint64_t) (*it).end);
|
|
flagselement->InsertEndChild(f);
|
|
}
|
|
timelineelement->InsertEndChild(flagselement);
|
|
|
|
newelement->InsertEndChild(timelineelement);
|
|
fadingelement->SetAttribute("mode", (uint) n.timelineFadingMode());
|
|
}
|
|
|
|
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("nbColors", n.nbColors);
|
|
filter->SetAttribute("invert", n.invert);
|
|
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);
|
|
}
|
|
|
|
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() );
|
|
sourceNode->SetAttribute("play", s.playing() );
|
|
|
|
// insert into hierarchy
|
|
xmlCurrent_->InsertEndChild(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 paint mask
|
|
if (s.maskShader()->mode == MaskShader::PAINT) {
|
|
// get the mask previously stored
|
|
XMLElement *imageelement = SessionVisitor::ImageToXML(s.getMask(), xmlDoc_);
|
|
if (imageelement)
|
|
xmlCurrent_->InsertEndChild(imageelement);
|
|
}
|
|
// if we are saving a source mask
|
|
else if (s.maskShader()->mode == MaskShader::SOURCE) {
|
|
// get the id of the source used as mask
|
|
xmlCurrent_->SetAttribute("source", s.maskSource()->id());
|
|
}
|
|
|
|
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_ = xmlDoc_->NewElement( "Audio" );
|
|
xmlCurrent_->SetAttribute("enabled", (bool) (s.audioFlags() & Source::Audio_enabled) );
|
|
xmlCurrent_->SetAttribute("volume", s.audioVolumeFactor(Source::VOLUME_BASE) );
|
|
xmlCurrent_->SetAttribute("volume_mix", (int) s.audioVolumeMix() );
|
|
sourceNode->InsertEndChild(xmlCurrent_);
|
|
|
|
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());
|
|
|
|
if (!s.failed())
|
|
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();
|
|
if (se) {
|
|
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
|
|
sessionNode->SetAttribute("id", se->id());
|
|
xmlCurrent_->InsertEndChild(sessionNode);
|
|
|
|
for (auto iter = se->begin(); iter != se->end(); ++iter){
|
|
setRoot(sessionNode);
|
|
(*iter)->accept(*this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SessionVisitor::visit (RenderSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "RenderSource");
|
|
xmlCurrent_->SetAttribute("provenance", (int) s.renderingProvenance());
|
|
}
|
|
|
|
void SessionVisitor::visit (FrameBufferFilter& f)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", (int) f.type());
|
|
}
|
|
|
|
void SessionVisitor::visit (DelayFilter& f)
|
|
{
|
|
xmlCurrent_->SetAttribute("delay", f.delay());
|
|
}
|
|
|
|
void SessionVisitor::visit (ResampleFilter& f)
|
|
{
|
|
xmlCurrent_->SetAttribute("factor", (int) f.factor());
|
|
}
|
|
|
|
XMLElement *list_parameters_(tinyxml2::XMLDocument *doc, std::map< std::string, float > filter_params)
|
|
{
|
|
XMLElement *parameters = doc->NewElement( "parameters" );
|
|
for (auto p = filter_params.begin(); p != filter_params.end(); ++p)
|
|
{
|
|
XMLElement *param = doc->NewElement( "uniform" );
|
|
param->SetAttribute("name", p->first.c_str() );
|
|
param->SetAttribute("value", p->second );
|
|
parameters->InsertEndChild(param);
|
|
}
|
|
return parameters;
|
|
}
|
|
|
|
void SessionVisitor::visit (BlurFilter& f)
|
|
{
|
|
// blur specific
|
|
xmlCurrent_->SetAttribute("method", (int) f.method());
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
}
|
|
|
|
void SessionVisitor::visit (SharpenFilter& f)
|
|
{
|
|
// sharpen specific
|
|
xmlCurrent_->SetAttribute("method", (int) f.method());
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
}
|
|
|
|
void SessionVisitor::visit (SmoothFilter& f)
|
|
{
|
|
// smooth specific
|
|
xmlCurrent_->SetAttribute("method", (int) f.method());
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
}
|
|
|
|
void SessionVisitor::visit (EdgeFilter& f)
|
|
{
|
|
// edge specific
|
|
xmlCurrent_->SetAttribute("method", (int) f.method());
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
}
|
|
|
|
void SessionVisitor::visit (AlphaFilter& f)
|
|
{
|
|
// alpha specific
|
|
xmlCurrent_->SetAttribute("operation", (int) f.operation());
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
}
|
|
|
|
void SessionVisitor::visit (ImageFilter& f)
|
|
{
|
|
xmlCurrent_->SetAttribute("name", f.program().name().c_str() );
|
|
xmlCurrent_->SetAttribute("filename", f.program().filename().c_str() );
|
|
|
|
// image filter code
|
|
std::pair< std::string, std::string > filter_codes = f.program().code();
|
|
XMLElement *firstpass = xmlDoc_->NewElement( "firstpass" );
|
|
xmlCurrent_->InsertEndChild(firstpass);
|
|
{
|
|
XMLText *code = xmlDoc_->NewText( filter_codes.first.c_str() );
|
|
firstpass->InsertEndChild(code);
|
|
}
|
|
|
|
XMLElement *secondpass = xmlDoc_->NewElement( "secondpass" );
|
|
xmlCurrent_->InsertEndChild(secondpass);
|
|
{
|
|
XMLText *code = xmlDoc_->NewText( filter_codes.second.c_str() );
|
|
secondpass->InsertEndChild(code);
|
|
}
|
|
|
|
// image filter parameters
|
|
xmlCurrent_->InsertEndChild( list_parameters_(xmlDoc_, f.program().parameters()) );
|
|
|
|
// global iMouse
|
|
XMLElement *imouse = xmlDoc_->NewElement("iMouse");
|
|
imouse->InsertEndChild( XMLElementFromGLM(xmlDoc_, FilteringProgram::iMouse) );
|
|
xmlCurrent_->InsertEndChild(imouse);
|
|
}
|
|
|
|
void SessionVisitor::visit (CloneSource& s)
|
|
{
|
|
XMLElement *cloneNode = xmlCurrent_;
|
|
cloneNode->SetAttribute("type", "CloneSource");
|
|
|
|
// origin of clone
|
|
XMLElement *origin = xmlDoc_->NewElement( "origin" );
|
|
XMLText *text = xmlDoc_->NewText( s.origin_name().c_str() );
|
|
if (s.origin())
|
|
origin->SetAttribute("id", s.origin()->id());
|
|
origin->InsertEndChild( text );
|
|
cloneNode->InsertEndChild(origin);
|
|
|
|
// Filter
|
|
xmlCurrent_ = xmlDoc_->NewElement( "Filter" );
|
|
s.filter()->accept(*this);
|
|
cloneNode->InsertEndChild(xmlCurrent_);
|
|
|
|
xmlCurrent_ = cloneNode; // parent for next visits (other subtypes of Source)
|
|
}
|
|
|
|
void SessionVisitor::visit (StreamSource& s)
|
|
{
|
|
if (s.stream() != nullptr)
|
|
s.stream()->accept(*this);
|
|
}
|
|
|
|
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 (TextSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "TextSource");
|
|
|
|
if (s.contents()) {
|
|
XMLElement *contents = xmlDoc_->NewElement("contents");
|
|
// simple text node to store filename of subtitle
|
|
if (s.contents()->isSubtitle()) {
|
|
XMLText *text = xmlDoc_->NewText(s.contents()->text().c_str());
|
|
contents->InsertEndChild(text);
|
|
}
|
|
// encoded text node to store string with Pango style
|
|
else {
|
|
GString *data = g_string_new(s.contents()->text().c_str());
|
|
XMLElement *text = XMLElementEncodeArray(xmlDoc_, data->str, data->len);
|
|
contents->InsertEndChild(text);
|
|
g_string_free(data, FALSE);
|
|
}
|
|
|
|
contents->SetAttribute("font-desc", s.contents()->fontDescriptor().c_str() );
|
|
contents->SetAttribute("color", s.contents()->color() );
|
|
contents->SetAttribute("halignment", s.contents()->horizontalAlignment() );
|
|
contents->SetAttribute("valignment", s.contents()->verticalAlignment() );
|
|
contents->SetAttribute("outline", s.contents()->outline() );
|
|
contents->SetAttribute("outline-color", s.contents()->outlineColor() );
|
|
contents->SetAttribute("x", s.contents()->horizontalPadding() );
|
|
contents->SetAttribute("y", s.contents()->verticalPadding() );
|
|
|
|
xmlCurrent_->InsertEndChild(contents);
|
|
}
|
|
|
|
XMLElement *resolution = xmlDoc_->NewElement("resolution");
|
|
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, glm::ivec2(s.stream()->width(), s.stream()->height())) );
|
|
xmlCurrent_->InsertEndChild(resolution);
|
|
}
|
|
|
|
void SessionVisitor::visit (ShaderSource& s)
|
|
{
|
|
XMLElement *_node = xmlCurrent_;
|
|
_node->SetAttribute("type", "ShaderSource");
|
|
|
|
XMLElement *resolution = xmlDoc_->NewElement("resolution");
|
|
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.filter()->resolution()) );
|
|
_node->InsertEndChild(resolution);
|
|
|
|
// Filter
|
|
xmlCurrent_ = xmlDoc_->NewElement("Filter");
|
|
s.filter()->accept(*this);
|
|
_node->InsertEndChild(xmlCurrent_);
|
|
|
|
xmlCurrent_ = _node; // parent for next visits
|
|
}
|
|
|
|
void SessionVisitor::visit (DeviceSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "DeviceSource");
|
|
xmlCurrent_->SetAttribute("device", s.device().c_str() );
|
|
}
|
|
|
|
void SessionVisitor::visit (ScreenCaptureSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "ScreenCaptureSource");
|
|
xmlCurrent_->SetAttribute("window", s.window().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);
|
|
}
|
|
|
|
|
|
void SessionVisitor::visit (GenericStreamSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "GenericStreamSource");
|
|
|
|
XMLElement *desc = xmlDoc_->NewElement("Description");
|
|
|
|
XMLText *text = xmlDoc_->NewText( s.description().c_str() );
|
|
desc->InsertEndChild( text );
|
|
|
|
xmlCurrent_->InsertEndChild(desc);
|
|
}
|
|
|
|
|
|
void SessionVisitor::visit (SrtReceiverSource& s)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", "SrtReceiverSource");
|
|
|
|
XMLElement *ip = xmlDoc_->NewElement("ip");
|
|
XMLText *iptext = xmlDoc_->NewText( s.ip().c_str() );
|
|
ip->InsertEndChild( iptext );
|
|
xmlCurrent_->InsertEndChild(ip);
|
|
|
|
XMLElement *port = xmlDoc_->NewElement("port");
|
|
XMLText *porttext = xmlDoc_->NewText( s.port().c_str() );
|
|
port->InsertEndChild( porttext );
|
|
xmlCurrent_->InsertEndChild(port);
|
|
}
|
|
|
|
void SessionVisitor::visit (SourceCallback &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("type", (uint) c.type());
|
|
}
|
|
|
|
void SessionVisitor::visit (ValueSourceCallback &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("value", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
}
|
|
|
|
void SessionVisitor::visit (Play &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("play", c.value());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
}
|
|
|
|
void SessionVisitor::visit (PlayFastForward &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("step", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
}
|
|
|
|
void SessionVisitor::visit (Seek &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("value", (uint64_t) c.value());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
}
|
|
|
|
void SessionVisitor::visit (SetAlpha &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("alpha", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
}
|
|
|
|
void SessionVisitor::visit (SetDepth &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("depth", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
}
|
|
|
|
void SessionVisitor::visit (SetGeometry &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
|
|
// get geometry of target
|
|
Group g;
|
|
c.getTarget(&g);
|
|
|
|
XMLElement *geom = xmlDoc_->NewElement( "Geometry" );
|
|
xmlCurrent_->InsertEndChild(geom);
|
|
xmlCurrent_ = geom;
|
|
g.accept(*this);
|
|
|
|
}
|
|
|
|
void SessionVisitor::visit (SetGamma &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
xmlCurrent_->SetAttribute("bidirectional", c.bidirectional());
|
|
|
|
XMLElement *gamma = xmlDoc_->NewElement("gamma");
|
|
gamma->InsertEndChild( XMLElementFromGLM(xmlDoc_, c.value()) );
|
|
xmlCurrent_->InsertEndChild(gamma);
|
|
}
|
|
|
|
void SessionVisitor::visit (Loom &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("delta", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
}
|
|
|
|
void SessionVisitor::visit (Grab &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("delta.x", c.value().x);
|
|
xmlCurrent_->SetAttribute("delta.y", c.value().y);
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
}
|
|
|
|
void SessionVisitor::visit (Resize &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("delta.x", c.value().x);
|
|
xmlCurrent_->SetAttribute("delta.y", c.value().y);
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
}
|
|
|
|
void SessionVisitor::visit (Turn &c)
|
|
{
|
|
xmlCurrent_->SetAttribute("delta", c.value());
|
|
xmlCurrent_->SetAttribute("duration", c.duration());
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|