summaryrefslogtreecommitdiff
path: root/rotord/src/rotor.h
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/src/rotor.h')
-rwxr-xr-xrotord/src/rotor.h1314
1 files changed, 554 insertions, 760 deletions
diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h
index 2af582a..937a884 100755
--- a/rotord/src/rotor.h
+++ b/rotord/src/rotor.h
@@ -1,26 +1,7 @@
#ifndef ROTOR_H
#define ROTOR_H
-/*
-nodes can have many inputs but only 1 output
-
-image nodes that use an image as input can pass on the incoming image only if its unchanged.
-
-TODO - parameter class that automatically links variable to correctly named inputs
-TODO - use try.. catch and dynamic_cast to verify node connections rather than checking 'type' tag
-
-TODO - put the boilerplate code for checking inputs into the base class, finally call checked_output
-
-http://stackoverflow.com/questions/5261658/how-to-seek-in-ffmpeg-c-c
-
-300713
-trying to use opencv video loader for seeking
-not so good - conflicts with included libav - seems to be incorrectly loaded
-
-would maybe need to do something like
-http://stackoverflow.com/questions/12427928/configure-and-build-opencv-to-custom-ffmpeg-install
-
-*/
+//definitions of base classes and types for rendering graph
#include <unordered_map>
#include <deque>
@@ -29,59 +10,13 @@ http://stackoverflow.com/questions/12427928/configure-and-build-opencv-to-custom
#include <sys/time.h>
#include <iostream>
-#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPResponse.h"
-#include "Poco/UUID.h"
-#include "Poco/UUIDGenerator.h"
-#include "Poco/Notification.h"
-#include "Poco/NotificationCenter.h"
-#include "Poco/Observer.h"
-#include "Poco/ThreadPool.h"
-#include "Poco/Thread.h"
-#include "Poco/Task.h"
-#include "Poco/Runnable.h"
-#include "Poco/Mutex.h"
-#include "Poco/Random.h"
-#include "Poco/AutoPtr.h"
-#include "Poco/File.h"
-#include "Poco/Base64Encoder.h"
-#include "Poco/Path.h"
-#include "Poco/StringTokenizer.h"
#include "Poco/Logger.h"
-
-
-using Poco::UUID;
-using Poco::UUIDGenerator;
-using Poco::Net::HTTPResponse;
-using Poco::Logger;
-
-/*
-extern "C" {
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libavutil/opt.h>
- #include <libavutil/channel_layout.h>
- #include <libavutil/common.h>
- #include <libavutil/imgutils.h>
- #include <libavutil/mathematics.h>
- #include <libavutil/samplefmt.h>
-
- #include <libavutil/dict.h>
- //#include <libavutil/dict.c> stops the compiler error but causes a linker error. does libavcodec need to be statically linked?
- #include <libavutil/imgutils.h>
- #include <libavutil/samplefmt.h>
- //#include <libavutil/timestamp.h>
-}
-*/
-
-
-#define AUDIO_INBUF_SIZE 20480
-#define AUDIO_REFILL_THRESH 4096
-
-#include <highgui.h> //for opencv video IO
+#include "Poco/Path.h"
+#include "Poco/Base64Encoder.h"
#include "xmlIO.h"
-#include "utils.h" //fequal
+#include "utils.h"
#include "cvimage.h"
#include "libavwrapper.h"
@@ -97,46 +32,17 @@ namespace Rotor {
#define ANALYSE_AUDIO 1
#define PREVIEW 2
#define RENDER 3
-
- //forward declaration
+ //forward declarations
class Node;
class Signal_node;
class Image_node;
- class Parameter_input;
-
- //http://blog.tomaka17.com/2012/03/libavcodeclibavformat-tutorial/
- /* struct Packet {
- explicit Packet(AVFormatContext* ctxt = nullptr) {
- av_init_packet(&packet);
- packet.data = nullptr;
- packet.size=0;
- if (ctxt) reset(ctxt);
- }
-
- Packet(Packet&& other) : packet(std::move(other.packet)) {
- other.packet.data = nullptr;
- }
+ class Parameter;
- ~Packet() {
- if (packet.data)
- av_free_packet(&packet);
- }
-
- void reset(AVFormatContext* ctxt) {
- if (packet.data)
- av_free_packet(&packet);
- if (av_read_frame(ctxt, &packet) < 0)
- packet.data = nullptr;
- }
-
- AVPacket packet;
- };
- */
class Time_spec{
public:
Time_spec(){};
Time_spec(float _time,float _framerate,float _duration){ time=_time; framerate=_framerate; duration=_duration;};
- float time;
+ float time; //num/denom ?
float framerate;
float duration;
Time_spec lastframe() const{
@@ -148,16 +54,14 @@ namespace Rotor {
};
class Frame_spec: public Time_spec{
public:
- Frame_spec(float _time,float _framerate,float _duration,int _w,int _h){ time=_time; framerate=_framerate; duration=_duration; w=_w; h=_h;};
- Frame_spec(int _frame,float _framerate,float _duration,int _w,int _h){ time=((float)_frame)/_framerate; framerate=_framerate; duration=_duration; w=_w; h=_h;};
- //Frame_spec(time,_framerate,_duration,_w,_h);};
-
- //float time; //this hould probably be implemented with a num/denom scheme eventually for accuracy
- //float framerate;
+ Frame_spec(float _time,float _framerate,float _duration,int _w,int _h)
+ { time=_time; framerate=_framerate; duration=_duration; w=_w; h=_h;};
+ Frame_spec(int _frame,float _framerate,float _duration,int _w,int _h)
+ { time=((float)_frame)/_framerate; framerate=_framerate; duration=_duration; w=_w; h=_h;};
int h,w;
- //Frame_spec lastframe(){
- // return Frame_spec(time-(1.0f/framerate),framerate,w,h);
- //}
+ Frame_spec lastframe(){
+ return Frame_spec(time-(1.0f/framerate),framerate,duration,w,h);
+ }
};
class Colour{
public:
@@ -185,17 +89,6 @@ namespace Rotor {
}
uint8_t r,g,b;
};
-
- class Render_status{
- public:
- int id;
- float progress;
- };
- class Render_requirements{
- public:
- int num_performances;
- int num_clips;
- };
class Command_response{
public:
Command_response() { status=Poco::Net::HTTPResponse::HTTP_OK; }
@@ -204,75 +97,131 @@ namespace Rotor {
};
class Input{
public:
- Input(const string &_desc): connection(nullptr),description(_desc){};
+ Input(const string &_desc,const string &_title): connection(nullptr),description(_desc),title(_title){};
Node* connection;
string description;
+ string title;
};
class Image_input: public Input{
public:
- bool connect(Image_node *source);
- Image_input(const string &_desc): Input(_desc){};
+ virtual ~Image_input(){};
+ bool connect(Node *source);
+ Image_input(const string &_desc,const string &_title,Node* _connect): Input(_desc,_title){
+ connect(_connect);
+ };
+ Image* get(const Frame_spec& time);
};
class Signal_input: public Input{
public:
- bool connect(Signal_node *source);
- Signal_input(const string &_desc): Input(_desc){};
+ virtual ~Signal_input(){};
+ bool connect(Node *source);
+ Signal_input(const string &_desc,const string &_title,Node* _connect): Input(_desc,_title){
+ connect(_connect);
+ };
+ float get(const Time_spec& time);
};
- class Parameter_input: public Signal_input{
+ class Parameter: public Signal_input{
public:
- Parameter_input(const string &_param,const string &_desc): Signal_input(_desc),receiver(nullptr),parameter(_param){};
- float *receiver;
- void update(const Time_spec& time);
- string parameter;
+ virtual ~Parameter(){};
+ Parameter(const string &_type,const string &_desc,const string &_title,float _value,float _min,float _max,Node* _connect): Signal_input(_desc,_title,_connect),value(_value),min(_min),max(_max),type(_type){};
+ float value,min,max;
+ float get(const Time_spec& time);
+ string type;
+ };
+ class Attribute{ //description of a static attribute which can be an enumerated string array
+ public:
+ virtual ~Attribute(){};
+ Attribute(const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}): description(_desc),title(_title),value(_value),intVal(0){
+ vals=_vals;
+ };
+ void init(const string &_key){ //inits int value from set::string vals index
+ value=_key;
+ std::vector<std::string>::iterator it=it = find(vals.begin(),vals.end(),value);
+ if (it!=vals.end()){
+ intVal = std::distance(vals.begin(),it)+1; //using 1-index for enums
+ }
+ else intVal=0;
+ }
+ string value,description,title;
+ std::vector<std::string> vals;
+ int intVal;
};
class Node{
public:
- virtual Node* clone(map<string,string> &_settings)=0;
- virtual ~Node(){};
- UUID uid; //every usable node has a UUID
- int id;
- vector<Signal_input*> inputs; //simple node can have signal inputs, output depends on node type
- vector<Parameter_input*> parameter_inputs; //linked parameters can convert from settings to inputs
- void create_signal_input(const string &description) {inputs.push_back(new Signal_input(description));};
- void create_parameter_input(const string &parameter,const string &description) {parameter_inputs.push_back(new Parameter_input(parameter,description));};
+ virtual Node* clone(map<string,string> &_settings)=0; //pure virtual
+ virtual ~Node(){
+ duplicate_inputs=false;
+ };
+ vector<Signal_input*> inputs; //simple node can have signal inputs, output depends on node type
+ unordered_map<string,Parameter*> parameters; //linked parameters can convert from settings to inputs
+ unordered_map<string,Attribute*> attributes;
+ void create_signal_input(const string &_desc,const string &_title,Node* _connect=nullptr ) {
+ inputs.push_back(new Signal_input(_desc,_title,_connect));
+ };
+ void create_parameter(const string &_name,const string &_type,const string &_desc,const string &_title,float _value=1.0f,float _min=0.0f,float _max=0.0f,Node* _connect=nullptr) {
+ parameters[_name]=new Parameter(_type,_desc,_title,_value,_min,_max,_connect);
+ };
+ void create_attribute(const string &_attr,const string &_desc,const string &_title,const string &_value,std::vector<std::string> _vals={}) {
+ attributes[_attr]=new Attribute(_desc,_title,_value,_vals);
+ };
string description;
string type;
- string output_type;
string ID;
+ string title;
+ bool duplicate_inputs;
string find_setting(map<string,string> &settings,string key,string def=""){ if (settings.find(key)!=settings.end()) return settings[key]; else return def;};
float find_setting(map<string,string> &settings,string key,float def){ if (settings.find(key)!=settings.end()) return ofToFloat(settings[key]); else return def;};
int find_setting(map<string,string> &settings,string key,int def){ if (settings.find(key)!=settings.end()) return ofToInt(settings[key]); else return def;};
void base_settings(map<string,string> &settings) {
description=find_setting(settings,"description");
type=find_setting(settings,"type");
- output_type=find_setting(settings,"output");
ID=find_setting(settings,"ID");
+ title=find_setting(settings,"title");
+ for (auto a: attributes){
+ if (find_setting(settings,a.first,"")!="") {
+ attributes[a.first]->init(find_setting(settings,a.first,""));
+ }
+ }
+ }
+ void update(const Time_spec &time){
+ for (auto p: parameters){
+ p.second->get(time);
+ }
}
virtual void set_parameter(const std::string &key,const std::string &value){};
- virtual void link_params(){}; //TODO make param classes that link automatically
- void update_params(const Time_spec& time);
};
class Signal_node: public Node{
- public:
- virtual ~Signal_node(){};
- const float get_output(const Time_spec &time) { update_params(time); return output(time); };
- virtual const float output(const Time_spec &time) { return 0.0f; };
+ public:
+ virtual ~Signal_node(){};
+ const float get_output(const Time_spec &time) {
+ update(time);
+ return output(time);
+ };
+ virtual const float output(const Time_spec &time) { return 0.0f; };
};
class Image_node: public Node{
public:
virtual ~Image_node(){};
vector<Image_input*> image_inputs; //image node also has image inputs and outputs
- void create_image_input(const string &description) {image_inputs.push_back(new Image_input(description));};
- Image *get_output(const Frame_spec &frame) { update_params((Time_spec)frame); return output(frame); };
+ void create_image_input(const string &_title,const string &_desc,Node* _connect=nullptr) {
+ image_inputs.push_back(new Image_input(_desc,_title,_connect));
+ };
+ Image *get_output(const Frame_spec &frame) {
+ image.setup(frame.w,frame.h);
+ update((Time_spec)frame);
+ return output(frame);
+ }
virtual const Image *output(const Frame_spec &frame)=0;
Image *get_preview(const Frame_spec &frame);
- Image *image; //this can be privately allocated or just passed on as the node see fit
- private:
- float image_time;
+ Image image;
+ private:
+ float image_time; //? could be used to detect image reuse?
+
};
- class Base_audio_processor: public Signal_node {
+ class Audio_processor: public Signal_node {
public:
- virtual ~Base_audio_processor(){};
+ virtual Audio_processor(){};
+ virtual ~Audio_processor(){};
virtual int process_frame(uint8_t *data,int samples)=0;
virtual bool init(int _channels,int _bits,int _samples,int _rate)=0;
virtual void cleanup()=0;
@@ -282,8 +231,11 @@ namespace Rotor {
//actual nodes-------------------------------------------------
class Time: public Signal_node {
public:
- Time(){};
- Time(map<string,string> &settings) {
+ Time(){
+ title="Time";
+ description="Outputs the time in seconds as a signal";
+ };
+ Time(map<string,string> &settings): Time() {
base_settings(settings);
};
Time* clone(map<string,string> &_settings) { return new Time(_settings);};
@@ -293,8 +245,11 @@ namespace Rotor {
};
class Track_time: public Signal_node {
public:
- Track_time(){};
- Track_time(map<string,string> &settings) {
+ Track_time(){
+ title="Track time";
+ description="Outputs the fraction of the track as a signal";
+ };
+ Track_time(map<string,string> &settings): Track_time() {
base_settings(settings);
};
Track_time* clone(map<string,string> &_settings) { return new Track_time(_settings);};
@@ -304,287 +259,230 @@ namespace Rotor {
};
class Signal_output: public Signal_node {
public:
- Signal_output(){};
- Signal_output(map<string,string> &settings) {
+ Signal_output(){
+ create_signal_input("signal","Signal Input");
+ title="Signal output";
+ description="Outputs a signal to xml for testing";
+ };
+ Signal_output(map<string,string> &settings): Signal_output() {
base_settings(settings);
};
Signal_output* clone(map<string,string> &_settings) { return new Signal_output(_settings);};
bool render(const float duration, const float framerate,string &xml_out);
const float output(const Time_spec &time) {
- if (inputs[0]->connection) {
- return ((Signal_node*)(inputs[0]->connection))->get_output(time);
- }
- else return 0.0f;
+ return inputs[0]->get(time);
}
};
class Testcard: public Image_node {
public:
- Testcard(){image=nullptr;};
- Testcard(map<string,string> &settings) {
+ Testcard(){
+ //internal testing node only
+ };
+ Testcard(map<string,string> &settings): Testcard() {
base_settings(settings);
- image=new Image();
};
- ~Testcard(){ if (image) delete image;};
+ ~Testcard(){};
Testcard* clone(map<string,string> &_settings) { return new Testcard(_settings);};
Image *output(const Frame_spec &frame){
- if (image->setup(frame.w,frame.h)) {
-
- }
- //always create testcard
- //float ws=(255.0f/frame.w);
float hs=(255.0f/frame.h);
for (int i=0;i<frame.h;i++){
for (int j=0;j<frame.w;j++){
- image->RGBdata[(i*frame.w+j)*3]=(uint8_t)((int)((i+(frame.time*25.0f)*hs))%255);
- image->RGBdata[((i*frame.w+j)*3)+1]=(uint8_t)((int)((j+(frame.time*100.0f)*hs))%255);
- image->RGBdata[((i*frame.w+j)*3)+2]=(uint8_t)(0);
+ image.RGBdata[(i*frame.w+j)*3]=(uint8_t)((int)((i+(frame.time*25.0f)*hs))%255);
+ image.RGBdata[((i*frame.w+j)*3)+1]=(uint8_t)((int)((j+(frame.time*100.0f)*hs))%255);
+ image.RGBdata[((i*frame.w+j)*3)+2]=(uint8_t)(0);
//image->Adata[i*frame.w+j]=(uint8_t)255;
//image->Zdata[i*frame.w+j]=(uint16_t)512; //1.0 in fixed point 8.8 bits
}
}
- return image;
+ return &image;
}
private:
- Image *image; //is an image generator
+
};
class Invert: public Image_node {
public:
Invert(){
- image=nullptr;
- create_image_input("image to invert");
+ create_image_input("Image to invert","Image input");
+ create_parameter("invert","number","Invert when greater than 0.0","Negative",1.0f,0.0f,1.0f);
+ title="Negative";
+ description="Inverts the input picture";
};
- Invert(map<string,string> &settings) {
+ Invert(map<string,string> &settings) :Invert() {
base_settings(settings);
- image=new Image();
};
- ~Invert(){ if (image) delete image;};
+ ~Invert(){};
Invert* clone(map<string,string> &_settings) { return new Invert(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- if (inputs[0]->connection) {
- if (fgreater_or_equal(1.0f,(((Signal_node*)inputs[0]->connection)->get_output((Time_spec)frame)))) {
- Image *in=(((Image_node*)image_inputs[0]->connection)->get_output(frame));
- if (in){
- image->setup(frame.w,frame.h);
- for (int i=0;i<in->w*in->h*3;i++) {
- image->RGBdata[i]=255-in->RGBdata[i];
- }
- return image;
- }
- }
- }
- return (((Image_node*)image_inputs[0]->connection)->get_output(frame));
- }
-
- }
- if (image_inputs[0]->connection) {
- return image;
- }
+ Image *in=image_inputs[0]->get(frame);
+ if (in) {
+ if (parameters["invert"]->value>0.0f){
+ for (int i=0;i<in->w*in->h*3;i++) {
+ image.RGBdata[i]=255-in->RGBdata[i];
+ }
+ return &image;
+ }
+ return in;
+ }
return nullptr;
}
private:
- Image *image; //is an image generator
- //bool invert;
};
class Video_cycler: public Image_node {
- //cycles through video inputs in order
public:
- Video_cycler(){create_image_input("duplicatable");};
- Video_cycler(map<string,string> &settings) {
+ Video_cycler(){
+ create_image_input("Image input","Image input");
+ create_signal_input("Selector","Selector input");
+ title="Video cycler";
+ description="Cycles through video inputs according to selector signal";
+ duplicate_inputs=true;
+ }
+ Video_cycler(map<string,string> &settings):Video_cycler() {
base_settings(settings);
};
~Video_cycler(){};
bool load(const string &filename);
Image *output(const Frame_spec &frame){
- int which_input=0;
- if (inputs[0]->connection) {
- which_input=((int)((Signal_node*)inputs[0]->connection)->get_output((Time_spec)frame))%image_inputs.size();
- }
- if (image_inputs.size()) {
- if (image_inputs[which_input]->connection){
- return (((Image_node*)image_inputs[which_input]->connection)->get_output(frame));
- }
- }
- return nullptr;
+ return image_inputs[((int)inputs[0]->get((Time_spec)frame))%image_inputs.size()]->get(frame);
}
Video_cycler* clone(map<string,string> &_settings) { return new Video_cycler(_settings);};
- private:
};
class Signal_colour: public Image_node {
- //cycles through video inputs in order
public:
Signal_colour(){
- create_signal_input("colour selector");
+ create_signal_input("Selector","Selector input");
+ create_attribute("colours","palette list of web colours","Colours","000000");
+ title="Signal colour";
+ description="Cycles through a palette of background colours according to selector signal";
};
- Signal_colour(map<string,string> &settings) {
+ Signal_colour(map<string,string> &settings):Signal_colour() {
base_settings(settings);
- string colours=find_setting(settings,"palette","");
- for (int i=0;i<colours.size()/6;i++){
- palette.push_back(Colour(colours.substr(i*6,6)));
- }
- for (auto i: palette) {
- cerr << "Signal_colour found palette colour: "<<(int)i.r<<" "<<(int)i.g<<" "<<(int)i.b<<endl;
+ for (int i=0;i<attributes["colours"]->value.size()/6;i++){
+ palette.push_back(Colour(attributes["colours"]->value.substr(i*6,6)));
}
prevcol=-1;
};
~Signal_colour(){};
Image *output(const Frame_spec &frame){
if (palette.size()) {
- if (inputs.size()) {
- if (inputs[0]->connection){
- int col= ((int)(((Signal_node*)inputs[0]->connection)->get_output(frame)))%palette.size();
- if (col!=prevcol||image.w!=frame.w||image.h!=frame.h){
- image.setup(frame.w,frame.h);
- for (int i=0;i<image.w*image.h;i++){
- image.RGBdata[i*3]=palette[col].r;
- image.RGBdata[i*3+1]=palette[col].g;
- image.RGBdata[i*3+2]=palette[col].b;
- }
- prevcol=col;
- }
- return &image;
+ int col=((int)inputs[0]->get((Time_spec)frame))%palette.size();
+ if (col!=prevcol){ //how about when starting a new render?
+ for (int i=0;i<image.w*image.h;i++){
+ image.RGBdata[i*3]=palette[col].r;
+ image.RGBdata[i*3+1]=palette[col].g;
+ image.RGBdata[i*3+2]=palette[col].b;
}
+ prevcol=col;
}
+ return &image;
}
return nullptr;
}
Signal_colour* clone(map<string,string> &_settings) { return new Signal_colour(_settings);};
private:
vector<Rotor::Colour> palette;
- Image image;
int prevcol;
};
class Signal_greyscale: public Image_node {
//Draws signal bars in greyscale
public:
- Signal_greyscale(){};
- Signal_greyscale(map<string,string> &settings) {
+ Signal_greyscale(){
+ create_signal_input("Signal","Signal input");
+ title="Signal greyscale";
+ description="Renders signal level (0..1) as greyscale background";
+ };
+ Signal_greyscale(map<string,string> &settings):Signal_greyscale() {
base_settings(settings);
prevcol=-1;
};
~Signal_greyscale(){};
Image *output(const Frame_spec &frame){
- if (inputs.size()) {
- if (inputs[0]->connection){
- float sig= ((((Signal_node*)inputs[0]->connection)->get_output(frame)));
- uint8_t col=255-((uint8_t)(sig*255.0f));
- if (col!=prevcol||image.w!=frame.w||image.h!=frame.h){
- image.setup(frame.w,frame.h);
- for (int i=0;i<image.w*image.h*3;i++){
- image.RGBdata[i]=col;
- }
- prevcol=col;
- }
- return &image;
+ uint8_t col=255-((uint8_t)(inputs[0]->get((Time_spec)frame)*255.0f));
+ if (col!=prevcol){ //how about when starting a new render?
+ for (int i=0;i<image.w*image.h*3;i++){
+ image.RGBdata[i]=col;
}
+ prevcol=col;
}
- return nullptr;
+ return &image;
+
}
Signal_greyscale* clone(map<string,string> &_settings) { return new Signal_greyscale(_settings);};
private:
- Image image;
uint8_t prevcol;
};
-#define ARITHMETIC_plus 1
-#define ARITHMETIC_minus 2
-#define ARITHMETIC_multiply 3
-#define ARITHMETIC_divide 4
-#define ARITHMETIC_modulo 5
+ #define ARITHMETIC_plus 1
+ #define ARITHMETIC_minus 2
+ #define ARITHMETIC_multiply 3
+ #define ARITHMETIC_divide 4
+ #define ARITHMETIC_modulo 5
class Image_arithmetic: public Image_node {
- //Draws signal bars in greyscale
public:
- Image_arithmetic(){image=nullptr;};
- Image_arithmetic(map<string,string> &settings) {
+ Image_arithmetic(){
+ create_image_input("image input","Image input");
+ create_parameter("value","number","Value or signal for operation","Value",1.0f);
+ create_attribute("operator","operator for image","Operator","+",{"+","-","*","/"});
+ title="Image arithmetic";
+ description="Performs arithmetic on an image with a signal or value";
+ };
+ Image_arithmetic(map<string,string> &settings):Image_arithmetic() {
base_settings(settings);
- value=find_setting(settings,"value",0.0f);
- string _op=find_setting(settings,"operator","+");
- if (_op=="+"||_op=="plus"||_op=="add") op=ARITHMETIC_plus;
- if (_op=="-"||_op=="minus"||_op=="subtract") op=ARITHMETIC_minus;
- if (_op=="*"||_op=="x"||_op=="multiply") op=ARITHMETIC_multiply;
- if (_op=="/"||_op=="divide") op=ARITHMETIC_divide;
- //if (_op=="%"||_op=="mod"||_op=="modulo"||_op=="modulus") op=ARITHMETIC_modulo; ??what would this even mean?
- image=nullptr;
}
- void link_params() {
- for (auto p:parameter_inputs){
- if (p->parameter=="value") {
- p->receiver=&value;
- }
- }
-
- };
- ~Image_arithmetic(){if (image) delete image;};
+ ~Image_arithmetic(){};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- if (image) delete image; //from the previous frame- this may not be ideal
- //because operator* made a new image it should be deleted
- Image *in=(((Image_node*)image_inputs[0]->connection)->get_output(frame));
- switch (op) {
- case ARITHMETIC_plus:
- image=(*in)+value;
- break;
- case ARITHMETIC_minus:
- image=(*in)-value;
- break;
- case ARITHMETIC_multiply:
- image=(*in)*value;
- break;
- case ARITHMETIC_divide:
- image=(*in)/value;
- break;
- }
- return image;
- }
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ switch (attributes["operator"]->intVal) {
+ case ARITHMETIC_plus:
+ image=(*in); //could be poss without copy?
+ image+=parameters["value"]->value;
+ break;
+ case ARITHMETIC_minus:
+ image=(*in);
+ image-=parameters["value"]->value;
+ break;
+ case ARITHMETIC_multiply:
+ image=(*in);
+ image*=parameters["value"]->value;
+ break;
+ case ARITHMETIC_divide:
+ image=(*in);
+ image/=parameters["value"]->value;
+ break;
+ }
}
- return nullptr;
+ return &image;
}
Image_arithmetic* clone(map<string,string> &_settings) { return new Image_arithmetic(_settings);};
private:
- Image *image;
- float value;
- int op;
};
-
class Luma_levels: public Image_node {
- //applies LUT To RGB channels equally
public:
- Luma_levels(){LUT=nullptr;image=nullptr;};
- Luma_levels(map<string,string> &settings) {
- base_settings(settings);
- levels_settings(settings);
- image=new Image();
- }
- void link_params() {
- for (auto p:parameter_inputs){
- if (p->parameter=="black_in") p->receiver=&black_in;
- if (p->parameter=="white_in") p->receiver=&white_in;
- if (p->parameter=="gamma") p->receiver=&gamma;
- if (p->parameter=="black_out") p->receiver=&black_out;
- if (p->parameter=="white_out") p->receiver=&white_out;
- }
- };
- ~Luma_levels(){if (LUT) {delete[] LUT;} if (image) delete image; };
- void levels_settings(map<string,string> &settings){
- black_in=find_setting(settings,"black_in",0.0f);
- white_in=find_setting(settings,"white_in",1.0f);
- gamma=find_setting(settings,"gamma",1.0f);
- black_out=find_setting(settings,"black_out",0.0f);
- white_out=find_setting(settings,"white_out",1.0f);
+ Luma_levels(){
+ create_image_input("image input","Image input");
+ create_parameter("black_in","number","input black point (0..1)","Input black point",0.0f,0.0f,1.0f);
+ create_parameter("white_in","number","input white point (0..1)","Input white point",1.0f,0.0f,1.0f);
+ create_parameter("gamma","number","gamma level","Gamma",1.0f,0.0f,10.0f);
+ create_parameter("black_out","number","output black point (0..1)","Output black point",0.0f,0.0f,1.0f);
+ create_parameter("white_out","number","output white point (0..1)","Output white point",1.0f,0.0f,1.0f);
+ title="Luma levels";
+ description="Remap luma values of image";
LUT=nullptr;
- generate_LUT();
+ };
+ Luma_levels(map<string,string> &settings):Luma_levels() {
+ base_settings(settings);
}
- void generate_LUT(){ //check this
+ ~Luma_levels(){if (LUT) { delete[] LUT;} };
+ void generate_LUT(){
+ //can check here if anything has changed
if (LUT) delete[] LUT;
LUT=new unsigned char[256];
float fltmax=(255.0f/256.0f);
for (int i=0;i<256;i++){
- LUT[i]=(unsigned char)(((pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-black_in)/(white_in-black_in)))),(1.0/gamma))*(white_out-black_out))+black_out)*256.0f);
+ LUT[i]=(unsigned char)(((pow(min(fltmax,max(0.0f,(((((float)i)/256.0f)-parameters["black_in"]->value)/(parameters["white_in"]->value-parameters["black_in"]->value)))),(1.0/parameters["gamma"]->value))*(parameters["white_out"]->value-parameters["black_out"]->value))+parameters["black_out"]->value)*255.0f);
}
}
void apply_LUT(const Image& in){
- apply_LUT(in,*image);
+ apply_LUT(in,image);
}
void apply_LUT(const Image& in,Image &out){ //facility to apply to other images for inherited classes
out.setup(in.w,in.h);
@@ -593,21 +491,16 @@ namespace Rotor {
}
}
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- if (LUT) {
- apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_output(frame)));
- return image;
- }
- }
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ generate_LUT();
+ apply_LUT(*in);
}
- return nullptr;
+ return &image;
}
Luma_levels* clone(map<string,string> &_settings) { return new Luma_levels(_settings);};
protected:
unsigned char *LUT;
- Image *image;
- float black_in,white_in,gamma,black_out,white_out;
};
class Echo_trails: public Luma_levels {
//draw trail frames additively that fade off over time
@@ -635,38 +528,25 @@ namespace Rotor {
//or is it actually best to use alpha keying after all!
public:
- Echo_trails(){image=nullptr;};
- Echo_trails(map<string,string> &settings) {
+ Echo_trails(){
+ //calls base class constructor first
+ create_parameter("number","number","number of echoes","Number echoes",25.0f);
+ create_parameter("fadeto","number","amount that echoes fade out (0..1)","Fadout amount",1.0f,0.0f,1.0f);
+ create_attribute("mode","blend mode for echoes","Blend mode","screen",{"screen","wrap"});
+ title="Echo trails";
+ description="Draw trail frames additively that fade off over time";
+ };
+ Echo_trails(map<string,string> &settings):Echo_trails() {
base_settings(settings);
- //duration=find_setting(settings,"duration",1.0f);
- number=find_setting(settings,"number",1);
- fadeto=find_setting(settings,"fadeto",1.0f);
- levels_settings(settings);
- image=nullptr;
lastframe=-1;
- mode=find_setting(settings,"mode",0.0f);
}
- void link_params() {
- for (auto p:parameter_inputs){
- if (p->parameter=="black_in") p->receiver=&black_in;
- if (p->parameter=="white_in") p->receiver=&white_in;
- if (p->parameter=="gamma") p->receiver=&gamma;
- if (p->parameter=="black_out") p->receiver=&black_out;
- if (p->parameter=="white_out") p->receiver=&white_out;
-
- //TODO: control an integer
- if (p->parameter=="mode") p->receiver=&mode;
- }
- };
- //~Echo_trails(){if (image) {delete image;} };
~Echo_trails(){
- if (image) delete image;
- for (auto i:images) {if (image) delete i.second;}
+ for (auto i:images) delete i.second;
};
Image *output(const Frame_spec &frame){
//check if cache is valid
if (images.size()){
- if (frame.w!=image->w||frame.h!=image->h){ //or framerate changed?
+ if (frame.w!=image.w||frame.h!=image.h){ //or framerate changed?
//clear cache and start over
images.clear();
lastframe=-1;
@@ -680,7 +560,7 @@ namespace Rotor {
auto i = std::begin(images);
while (i != std::end(images)) {
// check if the image is in the range we need
- if (thisframe-(*i).first>number||thisframe-(*i).first<0) {
+ if (thisframe-(*i).first>(int)parameters["number"]->value||thisframe-(*i).first<0) {
delete (*i).second;
i = images.erase(i);
}
@@ -688,144 +568,113 @@ namespace Rotor {
++i;
}
//if frame has already been calculated just return it
- if (thisframe==lastframe) {
- return image;
- }
- else {
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- if (LUT) {
- //need a better strategy here, should be able to get each image once
- //copy incoming image **writable
- if (image) image->free();
- image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone();
- images[thisframe]=new Image(frame.w,frame.h);
- apply_LUT(*(image),*(images[thisframe]));
- for (int i=1;i<number;i++){
- //check echo frame isn't at negative time
- int absframe=thisframe-i;
- if (absframe>-1){
- //check if image is in the cache
- if (images.find(absframe)==images.end()){
- images[absframe]=new Image(frame.w,frame.h);
- Frame_spec wanted=Frame_spec(absframe,frame.framerate,frame.duration,frame.w,frame.h);
- apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_output(wanted)),*(images[absframe]));
- }
- //cerr<<"Rotor: about to apply image ("<<images[absframe].w<<"x"<<images[absframe].h<<")"<<endl;
- if (fless(1.0f,fadeto)){
- float amount=((((float)number-i)/number)*(1.0f-fadeto))+(1.0f-fadeto);
- Image *temp=*images[absframe]*amount;
- if (mode<0.5) {
- (*image)+=*temp;
- }
- else {
- image->add_wrap(*temp);
- }
- delete temp;
- }
- else {
- if (mode<0.5) (*image)+=*(images[absframe]);
- else (*image)=image->add_wrap(*(images[absframe]));
- }
+ if (thisframe!=lastframe) {
+ Image *in=image_inputs[0]->get(frame);
+ if (in) {
+ generate_LUT();
+ //need a better strategy here, should be able to get each image once
+ //copy incoming image **writable
+ image=*(in);
+ images[thisframe]=new Image(frame.w,frame.h);
+ apply_LUT(image,*(images[thisframe]));
+ for (int i=1;i<(int)parameters["number"]->value;i++){
+ //check echo frame isn't at negative time
+ int absframe=thisframe-i;
+ if (absframe>-1){
+ //check if image is in the cache
+ if (images.find(absframe)==images.end()){
+ images[absframe]=new Image(frame.w,frame.h);
+ Frame_spec wanted=Frame_spec(absframe,frame.framerate,frame.duration,frame.w,frame.h);
+ apply_LUT(*(((Image_node*)image_inputs[0]->connection)->get_output(wanted)),*(images[absframe]));
+ }
+ if (fless(1.0f,parameters["fadeto"]->value)){
+ float amount=(((parameters["number"]->value-i)/parameters["number"]->value)*(1.0f-parameters["fadeto"]->value))+(1.0f-parameters["fadeto"]->value);
+ Image *temp=*images[absframe]*amount;
+ if (attributes["mode"]->value=="screen") {
+ image+=*temp;
+ }
+ else {
+ image.add_wrap(*temp);
}
+ delete temp;
+ }
+ else {
+ if (attributes["mode"]->value=="screen") image+=*(images[absframe]);
+ else image=image.add_wrap(*(images[absframe]));
}
- //for (int i=0;i<frame.w*frame.h*3;i++){
- // image->RGBdata[i]=LUT[in->RGBdata[i]];
- //}
- lastframe=thisframe;
- return image;
}
}
+ //for (int i=0;i<frame.w*frame.h*3;i++){
+ // image->RGBdata[i]=LUT[in->RGBdata[i]];
+ //}
+ lastframe=thisframe;
}
}
- return nullptr;
+ return &image;
}
Echo_trails* clone(map<string,string> &_settings) { return new Echo_trails(_settings);};
protected:
- float duration,fadeto;
- int number;
int interval,total,lastframe; //number of frames between displayed echoes
unordered_map<int,Image*> images;
- float mode; //TODO make int, enum string parameter types
};
- #define BLEND_screen 1
- #define BLEND_multiply 2
- #define BLEND_blend 3
+ #define BLEND_blend 1
+ #define BLEND_screen 2
+ #define BLEND_multiply 3
#define BLEND_alpha 4
- #define BLEND_screen_wrap 5
- #define BLEND_multiply_wrap 6
- #define BLEND_xor 7
+ #define BLEND_wrap 5
+ #define BLEND_xor 6
class Blend: public Image_node {
public:
- Blend(){image=nullptr;};
- Blend(map<string,string> &settings) {
- base_settings(settings);
- image=nullptr;
- amount=find_setting(settings,"amount",1.0f);
- string _mode=find_setting(settings,"mode","screen");
- if (_mode=="screen") mode=BLEND_screen;
- if (_mode=="multiply") mode=BLEND_multiply;
- if (_mode=="blend") mode=BLEND_blend;
- if (_mode=="alpha") mode=BLEND_alpha;
- if (_mode=="screen_wrap") mode=BLEND_screen_wrap;
- if (_mode=="multiply_wrap") mode=BLEND_multiply_wrap;
- if (_mode=="xor") mode=BLEND_xor;
+ Blend(){
+ create_image_input("image input 1","Image input 1");
+ create_image_input("image input 2","Image input 2");
+ create_parameter("amount","number","amount to blend input 2","Blend amount",0.5f,0.0f,1.0f);
+ create_attribute("mode","Blend mode","Blend mode","blend",{"blend","screen","multiply","alpha","wrap","xor"});
+ title ="Blend";
+ description="Blend images in various modes";
};
- void link_params() {
- for (auto p:parameter_inputs){
- if (p->parameter=="amount") p->receiver=&amount;
- }
+ Blend(map<string,string> &settings):Blend() {
+ base_settings(settings);
};
- ~Blend(){ if (image) delete image;};
+ ~Blend(){};
Blend* clone(map<string,string> &_settings) { return new Blend(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- if (image_inputs.size()>1) {
- if (image_inputs[1]->connection) {
- //copy incoming image **writable
- if (image) delete image;
- image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone();
- switch(mode){
- case BLEND_screen:
- (*image)+=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_multiply:
- (*image)*=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_xor:
- (*image)^=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_alpha:
- (*image)=(*image).alpha_blend(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_screen_wrap:
- (*image)=(*image).add_wrap(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_multiply_wrap:
- (*image)=(*image).divide_wrap(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)));
- break;
- case BLEND_blend: //has to be last because of initialser of *in? go figure
- (*image)*=(1.0f-amount);
- Image *in=(*(((Image_node*)image_inputs[1]->connection)->get_output(frame)))*amount;
- (*image)+=(*in);
- delete in;
- break;
-
- }
- return image;
- }
- }
- //if there aren't 2 image inputs connected just return the first
- return (((Image_node*)image_inputs[0]->connection)->get_output(frame));
- }
- }
+ Image *in1=image_inputs[0]->get(frame);
+ if (in1){
+ Image *in2=image_inputs[1]->get(frame);
+ if (in2) {
+ image=*(in1);
+ switch(attributes["mode"]->intVal){
+ case BLEND_screen:
+ image+=(*in2);
+ break;
+ case BLEND_multiply:
+ image*=(*in2);
+ break;
+ case BLEND_xor:
+ image^=(*in2);
+ break;
+ case BLEND_alpha:
+ image=image.alpha_blend(*in2);
+ break;
+ case BLEND_wrap:
+ image=image.add_wrap(*in2);
+ break;
+ case BLEND_blend: //has to be last because of initialser of *in? go figure
+ image*=(1.0f-parameters["amount"]->value);
+ Image *in=(*in2)*parameters["amount"]->value;
+ image+=(*in);
+ delete in;
+ break;
+ }
+ return &image;
+ }
+ //if there aren't 2 image inputs connected just return the first
+ return in1;
+ }
return nullptr;
}
private:
- Image *image; //is an image generator
- int mode;
- float amount; //for blend
};
#define MIRROR_horiz 1
#define MIRROR_vert 2
@@ -833,99 +682,95 @@ namespace Rotor {
#define MIRROR_vertR 4
class Mirror: public Image_node {
public:
- Mirror(){image=nullptr;};
- Mirror(map<string,string> &settings) {
+ Mirror(){
+ create_image_input("image input","Image input");
+ create_attribute("mode","Mirror mode","Mirror mode","horiz",{"horiz","vert","horizR","vertR"});
+ title="Mirror";
+ description="Mirror video across a central axis";
+ };
+ Mirror(map<string,string> &settings):Mirror() {
base_settings(settings);
- image=nullptr;
- string _mode=find_setting(settings,"mode","horiz");
- if (_mode=="horiz") mode=MIRROR_horiz;
- if (_mode=="vert") mode=MIRROR_vert;
- if (_mode=="horizR") mode=MIRROR_horizR;
- if (_mode=="vertR") mode=MIRROR_vertR;
};
- ~Mirror(){ if (image) delete image;};
+ ~Mirror(){ };
Mirror* clone(map<string,string> &_settings) { return new Mirror(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- //copy incoming image **writable
- if (image) delete image;
- image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone();
- switch (mode) {
- case MIRROR_horiz:
- for (int i=0;i<image->w/2;i++){
- for (int j=0;j<image->h;j++){
- for (int k=0;k<3;k++){
- image->RGBdata[(((j*image->w)+((image->w/2)+i))*3)+k]=image->RGBdata[(((j*image->w)+((image->w/2)-i))*3)+k];
- }
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ //copy incoming image **writable
+ image=(*in);
+ //could be more efficient here by only copying once
+ switch (attributes["mode"]->intVal) {
+ case MIRROR_horiz:
+ for (int i=0;i<image.w/2;i++){
+ for (int j=0;j<image.h;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k];
}
}
- break;
- case MIRROR_vert:
- for (int i=0;i<image->w;i++){
- for (int j=0;j<image->h/2;j++){
- for (int k=0;k<3;k++){
- image->RGBdata[((((image->h/2+j)*image->w)+i)*3)+k]=image->RGBdata[((((image->h/2-j)*image->w)+i)*3)+k];
- }
+ }
+ break;
+ case MIRROR_vert:
+ for (int i=0;i<image.w;i++){
+ for (int j=0;j<image.h/2;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k];
}
}
- break;
- case MIRROR_horizR:
- for (int i=0;i<image->w/2;i++){
- for (int j=0;j<image->h;j++){
- for (int k=0;k<3;k++){
- image->RGBdata[(((j*image->w)+((image->w/2)-i))*3)+k]=image->RGBdata[(((j*image->w)+((image->w/2)+i))*3)+k];
- }
+ }
+ break;
+ case MIRROR_horizR:
+ for (int i=0;i<image.w/2;i++){
+ for (int j=0;j<image.h;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[(((j*image.w)+((image.w/2)-i))*3)+k]=image.RGBdata[(((j*image.w)+((image.w/2)+i))*3)+k];
}
}
- break;
- case MIRROR_vertR:
- for (int i=0;i<image->w;i++){
- for (int j=0;j<image->h/2;j++){
- for (int k=0;k<3;k++){
- image->RGBdata[((((image->h/2-j)*image->w)+i)*3)+k]=image->RGBdata[((((image->h/2+j)*image->w)+i)*3)+k];
- }
+ }
+ break;
+ case MIRROR_vertR:
+ for (int i=0;i<image.w;i++){
+ for (int j=0;j<image.h/2;j++){
+ for (int k=0;k<3;k++){
+ image.RGBdata[((((image.h/2-j)*image.w)+i)*3)+k]=image.RGBdata[((((image.h/2+j)*image.w)+i)*3)+k];
}
}
- break;
- }
- return image;
- }
+ }
+ break;
+ }
+ return &image;
}
return nullptr;
}
private:
- Image *image; //is an image generator
- int mode;
};
class Monochrome: public Image_node {
public:
- Monochrome(){};
- Monochrome(map<string,string> &settings) {
+ Monochrome(){
+ create_image_input("image input","Image input");
+ title="Monochrome";
+ description="Render video greyscale";
+ };
+ Monochrome(map<string,string> &settings):Monochrome() {
base_settings(settings);
};
~Monochrome(){
};
Monochrome* clone(map<string,string> &_settings) { return new Monochrome(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- Image *other=(((Image_node*)image_inputs[0]->connection)->get_output(frame));
- image.setup(other->w,other->h);
- for (int i=0;i<image.w;i++){
- for (int j=0;j<image.h;j++){
- uint8_t luma=0;
- for (int l=0;l<3;l++) luma+=pixels.mono_weights[l][other->RGBdata[(((j*image.w)+i)*3)+l]];
- for (int k=0;k<3;k++) image.RGBdata[(((j*image.w)+i)*3)+k]=luma;
- }
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ for (int i=0;i<image.w;i++){
+ for (int j=0;j<image.h;j++){
+ uint8_t luma=0;
+ for (int l=0;l<3;l++) luma+=pixels.mono_weights[l][in->RGBdata[(((j*image.w)+i)*3)+l]];
+ for (int k=0;k<3;k++) image.RGBdata[(((j*image.w)+i)*3)+k]=luma;
}
- return &image;
- }
+ }
+ return &image;
}
return nullptr;
}
private:
- Image image;
};
class Transform: public Image_node {
//what is the best coordinate system to use?
@@ -933,133 +778,128 @@ namespace Rotor {
//units: pixel or fractional
//aspect: scaled or homogenous
public:
- Transform(){};
- Transform(map<string,string> &settings) {
+ Transform(){
+ create_image_input("image input","Image input");
+ create_parameter("transformX","number","X transformation","Transform X",0.0f);
+ create_parameter("transformY","number","Y transformation","Transform X",0.0f);
+ create_parameter("originX","number","X transformation origin","Origin X",0.5f);
+ create_parameter("originY","number","Y transformation origin","Origin Y",0.5f);
+ create_parameter("rotation","number","Rotation about origin","Rotation",0.0f);
+ create_parameter("scale","number","Scale about origin","Scale",1.0f);
+ title="Transform";
+ description="Apply 2D transformation";
+ };
+ Transform(map<string,string> &settings):Transform() {
base_settings(settings);
- tX=find_setting(settings,"transformX",0.0f);
- tY=find_setting(settings,"transformY",0.0f);
- oX=find_setting(settings,"originX",0.5f);
- oY=find_setting(settings,"originX",0.5f);
- r=find_setting(settings,"rotation",0.0f);
- s=find_setting(settings,"scale",1.0f);
};
~Transform(){
};
- void link_params() {
- for (auto p:parameter_inputs){
- if (p->parameter=="scale") p->receiver=&s;
- if (p->parameter=="rotation") p->receiver=&r;
- if (p->parameter=="transformX") p->receiver=&tX;
- if (p->parameter=="transformY") p->receiver=&tY;
- if (p->parameter=="originX") p->receiver=&oX;
- if (p->parameter=="originY") p->receiver=&oY;
- }
- };
Transform* clone(map<string,string> &_settings) { return new Transform(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- Image *other=(((Image_node*)image_inputs[0]->connection)->get_output(frame));
- if (other) {
- image.setup(other->w,other->h);
- //do opencv transform
- cv::Point2f srcTri[3], dstTri[3];
- cv::Mat rot_mat(2,3,CV_32FC1);
- cv::Mat trans_mat(2,3,CV_32FC1);
+ Image *in=image_inputs[0]->get(frame);
+ if (in){
+ float tX=parameters["transformX"]->value;
+ float tY=parameters["transformY"]->value;
+ float oX=parameters["originX"]->value;
+ float oY=parameters["originY"]->value;
+ float r=parameters["rotation"]->value;
+ float s=parameters["scale"]->value;
+ //do opencv transform
+ cv::Point2f srcTri[3], dstTri[3];
+ cv::Mat rot_mat(2,3,CV_32FC1);
+ cv::Mat trans_mat(2,3,CV_32FC1);
- Image inter;
- inter.setup(other->w,other->h);
- // Compute matrix by creating triangle and transforming
- //is there a better way - combine the 2? Just a bit of geometry
- srcTri[0].x=0;
- srcTri[0].y=0;
- srcTri[1].x=other->w-1;
- srcTri[1].y=0;
- srcTri[2].x=0;
- srcTri[2].y=other->h-1;
- for (int i=0;i<3;i++){
- dstTri[i].x=srcTri[i].x+(tX*other->w);
- dstTri[i].y=srcTri[i].y+(tY*other->h);
- }
- trans_mat=getAffineTransform( srcTri, dstTri );
- warpAffine( other->rgb, inter.rgb, trans_mat, inter.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
+ Image inter;
+ inter.setup(in->w,in->h);
+ // Compute matrix by creating triangle and transforming
+ //is there a better way - combine the 2? Just a bit of geometry
+ srcTri[0].x=0;
+ srcTri[0].y=0;
+ srcTri[1].x=in->w-1;
+ srcTri[1].y=0;
+ srcTri[2].x=0;
+ srcTri[2].y=in->h-1;
+ for (int i=0;i<3;i++){
+ dstTri[i].x=srcTri[i].x+(tX*in->w);
+ dstTri[i].y=srcTri[i].y+(tY*in->h);
+ }
+ trans_mat=getAffineTransform( srcTri, dstTri );
+ warpAffine( in->rgb, inter.rgb, trans_mat, inter.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
- // Compute rotation matrix
- //
- cv::Point centre = cv::Point( oX*other->w, oY*other->h );
+ // Compute rotation matrix
+ //
+ cv::Point centre = cv::Point( oX*in->w, oY*in->h );
- rot_mat = getRotationMatrix2D( centre, r, s );
- // Do the transformation
- //
- warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
- //BORDER_WRAP
+ rot_mat = getRotationMatrix2D( centre, r, s );
+ // Do the transformation
+ //
+ warpAffine( inter.rgb, image.rgb, rot_mat, image.rgb.size(), cv::INTER_LINEAR, cv::BORDER_WRAP);
+ //BORDER_WRAP
- //INTER_NEAREST - a nearest-neighbor interpolation
- //INTER_LINEAR - a bilinear interpolation (used by default)
- //INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
- //INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
- //INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood
+ //INTER_NEAREST - a nearest-neighbor interpolation
+ //INTER_LINEAR - a bilinear interpolation (used by default)
+ //INTER_AREA - resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire’-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method.
+ //INTER_CUBIC - a bicubic interpolation over 4x4 pixel neighborhood
+ //INTER_LANCZOS4 - a Lanczos interpolation over 8x8 pixel neighborhood
- return &image;
- }
+ return &image;
}
- }
return nullptr;
}
private:
- Image image;
- float tX,tY,oX,oY,r,s;
//todo - quality settings
};
class Alpha_merge: public Image_node {
public:
- Alpha_merge(){image=nullptr;};
- Alpha_merge(map<string,string> &settings) {
+ Alpha_merge(){
+ create_image_input("image input","Image input");
+ create_image_input("alpha input","Alpha input");
+ create_parameter("transformX","number","X transformation","Transform X",0.0f);
+ title="Alpha merge";
+ description="Alpha merge two images";
+ };
+ Alpha_merge(map<string,string> &settings):Alpha_merge() {
base_settings(settings);
- image=nullptr;
};
- ~Alpha_merge(){ if (image) delete image;};
+ ~Alpha_merge(){};
Alpha_merge* clone(map<string,string> &_settings) { return new Alpha_merge(_settings);};
Image *output(const Frame_spec &frame){
- if (image_inputs.size()) {
- if (image_inputs[0]->connection){
- //copy incoming image **writable
- if (image) delete image;
- image=(((Image_node*)image_inputs[0]->connection)->get_output(frame))->clone();
- if (image_inputs.size()>1) {
- if (image_inputs[1]->connection) {
- image->alpha_merge(*((Image_node*)image_inputs[1]->connection)->get_output(frame));
- }
- }
- //if there aren't 2 image inputs connected just return the first
- return image;
- }
- }
+ Image *in1=image_inputs[0]->get(frame);
+ if (in1){
+ //copy incoming image **writable
+ Image *in2=image_inputs[1]->get(frame);
+ if (in2) {
+ image=(*in1);
+ image.alpha_merge(*in2);
+ return &image;
+ }
+ //if there aren't 2 image inputs connected just return the first
+ return in1;
+ }
return nullptr;
}
private:
- Image *image; //is an image generator
};
#define VIDEOFRAMES_still 1
#define VIDEOFRAMES_blend 2
class Video_loader: public Image_node {
public:
- Video_loader(){};
- Video_loader(map<string,string> &settings) {
+ Video_loader(){
+ create_parameter("speed","number","video playback speed","Speed",1.0f,0.0f,0.0f);
+ create_parameter("framerate","number","framerate override","Frame rate",0.0f,0.0f,0.0f);
+ create_attribute("filename","name of video file to load","File name","");
+ create_attribute("mode","frame mode","Mode","still",{"still","blend"});
+ title="Video loader";
+ description="Loads a video file";
+ };
+ Video_loader(map<string,string> &settings): Video_loader() {
base_settings(settings);
isLoaded=false;
- filename=find_setting(settings,"filename","");
- speed=find_setting(settings,"speed",1.0f);
- framerate=find_setting(settings,"framerate",0.0f);
- //0.0f signifies to use the internal framerate
- if (filename!="") {
- load(find_setting(settings,"media_path","")+filename);
+ if (attributes["filename"]->value!="") {
+ load(find_setting(settings,"media_path","")+attributes["filename"]->value);
}
- string frame_op=find_setting(settings,"mode","still");
- if (frame_op=="still") mode=VIDEOFRAMES_still;
- if (frame_op=="blend") mode=VIDEOFRAMES_blend;
lastframe=0;
};
~Video_loader(){};
@@ -1069,55 +909,22 @@ namespace Rotor {
bool isLoaded;
private:
libav::decoder player;
- Image image;
- string filename;
- float speed;
- float framerate;
int mode;
int lastframe;
};
- /*
- class CVideo_loader: public Image_node {
-
- // attempt
-
- // /usr/bin/ld: warning: libavcodec.so.53, needed by /usr/lib/gcc/i686-linux-gnu/4.7/../../../../lib/libopencv_highgui.so, may conflict with libavcodec.so.55
- // /usr/bin/ld: warning: libavformat.so.53, needed by /usr/lib/gcc/i686-linux-gnu/4.7/../../../../lib/libopencv_highgui.so, may conflict with libavformat.so.55
- // /usr/bin/ld: warning: libavutil.so.51, needed by /usr/lib/gcc/i686-linux-gnu/4.7/../../../../lib/libopencv_highgui.so, may conflict with libavutil.so.52
-
- // No URL Protocols are registered. Missing call to av_register_all()?
- // libav::Error: file:///mnt/rotor/media/newsins1_360.mp4 Protocol not found
- // Rotor::Video_loader: failed to load /mnt/rotor/media/newsins1_360.mp4
- // 30-07-2013 09:35:31 Rotor: ERROR: could not load newsins1_360.mp4 into video node 03
-
- public:
- CVideo_loader(){};
- CVideo_loader(map<string,string> &settings) {
- base_settings(settings);
- isLoaded=false;
- };
- ~CVideo_loader(){};
- bool load(const string &filename);
- Image *output(const Frame_spec &frame);
- CVideo_loader* clone(map<string,string> &_settings) { return new CVideo_loader(_settings);};
- bool isLoaded;
- private:
- cv::VideoCapture player;
- Image image;
- };
- */
class Video_output: public Image_node {
public:
- Video_output(){};
- Video_output(map<string,string> &settings) {
+ Video_output(){
+ create_image_input("image to output","Image input");
+ title="Video output";
+ description="Outputs to video from here";
+ };
+ Video_output(map<string,string> &settings):Video_output() {
base_settings(settings);
};
~Video_output(){ };
Image *output(const Frame_spec &frame){
- if (image_inputs[0]->connection) {
- return ((Image_node*)(image_inputs[0]->connection))->get_output(frame);
- }
- else return nullptr;
+ return image_inputs[0]->get(frame);
};
Video_output* clone(map<string,string> &_settings) { return new Video_output(_settings);};
bool render(const float duration, const float framerate,const string &output_filename,const string &audio_filename,float& progress,int w,int h);
@@ -1125,7 +932,7 @@ namespace Rotor {
private:
};
- //-------------------------------------------------------------------
+ //-------------------------------------------------------------------
class Node_factory{
public:
Node_factory();
@@ -1146,26 +953,71 @@ namespace Rotor {
void list_nodes(xmlIO XML){
int i=0;
for (auto& type: type_map) { //c++11
- XML.addTag("node");
- XML.addAttribute("node","name",type.first,i);
- if (dynamic_cast<Signal_node*> (type.second)!=nullptr) XML.addAttribute("node","type","signal",i);
- if (dynamic_cast<Image_node*> (type.second)!=nullptr) XML.addAttribute("node","type","image",i);
- XML.pushTag("node",i);
- int j=0;
- for (auto& input: type.second->inputs) {
- XML.addTag("signal_input");
- XML.setValue("signal_input",input->description,j);
- j++;
- }
- if (dynamic_cast<Image_node*> (type.second)!=nullptr) {
- for (auto& input: (dynamic_cast<Image_node*>(type.second))->image_inputs) {
- XML.addTag("image_input");
- XML.setValue("image_input",input->description,j);
+ if (type.second->description!="") { //blank description = internal/ testing node
+ XML.addTag("node");
+ XML.addAttribute("node","type",type.first,i);
+ XML.addAttribute("node","inputs",type.second->duplicate_inputs?"expandable":"fixed",i);
+ XML.addAttribute("node","title",type.second->title,i);
+ XML.addAttribute("node","description",type.second->description,i);
+ if (dynamic_cast<Signal_node*> (type.second)!=nullptr) XML.addAttribute("node","output","signal",i);
+ if (dynamic_cast<Image_node*> (type.second)!=nullptr) XML.addAttribute("node","output","image",i);
+ XML.pushTag("node",i);
+ //if (type.second->description!="") {
+ // XML.addTag("description");
+ // XML.setValue("description",type.second->description,0);
+ //}
+ int j=0;
+ for (auto& input: type.second->inputs) {
+ XML.addTag("signal_input");
+ XML.addAttribute("signal_input","title",input->title,j);
+ XML.addAttribute("signal_input","description",input->description,j);
j++;
}
+ j=0;
+ if (dynamic_cast<Image_node*> (type.second)!=nullptr) {
+ for (auto& input: (dynamic_cast<Image_node*>(type.second))->image_inputs) {
+ XML.addTag("image_input");
+ XML.addAttribute("image_input","title",input->title,j);
+ XML.addAttribute("image_input","description",input->description,j);
+ j++;
+ }
+ }
+ j=0;
+ for (auto& parameter: type.second->parameters) {
+ XML.addTag("parameter");
+ XML.addAttribute("parameter","name",parameter.first,j);
+ XML.addAttribute("parameter","type",parameter.second->type,j);
+ XML.addAttribute("parameter","title",parameter.second->title,j);
+ XML.addAttribute("parameter","description",parameter.second->description,j);
+ XML.addAttribute("parameter","value",parameter.second->value,j);
+ XML.addAttribute("parameter","min",parameter.second->min,j);
+ XML.addAttribute("parameter","max",parameter.second->max,j);
+ j++;
+ }
+ j=0;
+ for (auto& attribute: type.second->attributes) {
+ XML.addTag("attribute");
+ XML.addAttribute("attribute","name",attribute.first,j);
+ XML.addAttribute("attribute","title",attribute.second->title,j);
+ XML.addAttribute("attribute","description",attribute.second->description,j);
+ XML.addAttribute("attribute","value",attribute.second->value,j);
+ if (attribute.second->vals.size()){ //document attribute enumeration
+ XML.addAttribute("attribute","type","enum",j);
+ XML.pushTag("attribute",j);
+ int k=0;
+ for (auto val: attribute.second->vals){
+ XML.addTag("option");
+ XML.addAttribute("option","value",val,k);
+ k++;
+ }
+ XML.popTag();
+ }
+ else XML.addAttribute("attribute","type","string",j);
+ j++;
+ }
+ XML.popTag();
+ i++;
}
- XML.popTag();
- i++;
}
}
private:
@@ -1190,12 +1042,10 @@ namespace Rotor {
Node* find_node(const string &type);
bool signal_render(string &signal_xml,const float framerate);
bool video_render(const string &output_filename,const string &audio_filename,const float framerate,float& progress);
- int load(Poco::UUID uid);
bool load(string data,string media_path);
bool loadFile(string &filename,string media_path);
bool parseXml(string media_path);
bool set_resolution(int w,int h);
- UUID save(); //save to DB, returns UUID of saved graph
bool loaded;
float duration;
const string toString();
@@ -1204,7 +1054,7 @@ namespace Rotor {
Node_factory factory;
int outW,outH;
};
- class Audio_thumbnailer: public Base_audio_processor {
+ class Audio_thumbnailer: public Audio_processor {
public:
Audio_thumbnailer(){
height=128;
@@ -1226,62 +1076,6 @@ namespace Rotor {
int offset;
double scale,accum;
};
- class Render_context: public Poco::Task { //Poco task object
- //manages a 'patchbay'
- //high level interfaces for the wizard
- //and low level interface onto the graph
- public:
- Render_context(const std::string& name): Task(name) {
- audio_thumb=new Audio_thumbnailer();
- state=IDLE;
- output_framerate=25.0f;
- audio_loaded=false;
-
- xmlIO xml;
- if(xml.loadFile("settings.xml") ){
- graph_dir=xml.getAttribute("Rotor","graph_dir","",0);
- media_dir=xml.getAttribute("Rotor","media_dir","",0);
- output_dir=xml.getAttribute("Rotor","output_dir","",0);
- }
-
- else cerr<<"Rotor: settings.xml not found, using defaults"<<endl;
- };
- ~Render_context(){delete audio_thumb;};
- void runTask();
- void add_queue(int item);
- Command_response session_command(const std::vector<std::string>& command);
- void session_command(const std::vector<std::string>& command,xmlIO& XML,HTTPResponse::HTTPStatus& status);
- Render_status get_status();
- void cancel(); //interrupt locking process
- int make_preview(int nodeID, float time); //starts a frame preview - returns status code - how to retrieve?
- bool load_audio(const string &filename,vector<Base_audio_processor*> processors);
- bool _load_audio(const string &filename,vector<Base_audio_processor*> processors);
- Render_requirements get_requirements();
- bool load_video(const string &nodeID,const string &filename);//can be performance or clip
- private:
- int state;
- float progress; //for a locking process: audio analysis or rendering
- //thread only does one thing at once
- std::deque<int> work_queue;
- Poco::Mutex mutex; //lock for access from parent thread
- std::string audio_filename;
- std::string output_filename;
- std::string graph_dir;
- std::string media_dir;
- std::string output_dir;
-
- Audio_thumbnailer *audio_thumb;
- Graph graph;
- Node_factory factory;
- float output_framerate;
- bool audio_loaded;
-
- };
}
-/*
-coding style
-Types begin with capitals 'New_type'
-variables/ instances use lower case with underscore as a seperator
-*/
-#endif
+#endif \ No newline at end of file