sweeping effects rewrite that allows external plugins
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
// TODO port to GLSL?
|
||||
|
||||
CrossDissolveTransition::CrossDissolveTransition() : Transition(VIDEO_DISSOLVE_TRANSITION) {}
|
||||
|
||||
void CrossDissolveTransition::process_transition(double progress) {
|
||||
|
||||
+468
-259
@@ -1,5 +1,6 @@
|
||||
#include "effect.h"
|
||||
|
||||
#include "effects/qpainterwrapper.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
@@ -18,6 +19,15 @@
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "debug.h"
|
||||
#include "io/path.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "effects/internal/transformeffect.h"
|
||||
#include "effects/internal/texteffect.h"
|
||||
#include "effects/internal/solideffect.h"
|
||||
#include "effects/internal/audionoiseeffect.h"
|
||||
#include "effects/internal/toneeffect.h"
|
||||
#include "effects/internal/volumeeffect.h"
|
||||
#include "effects/internal/paneffect.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QGridLayout>
|
||||
@@ -26,99 +36,147 @@
|
||||
#include <QMessageBox>
|
||||
#include <QOpenGLContext>
|
||||
#include <QDir>
|
||||
#include <QPainter>
|
||||
#include <QtMath>
|
||||
|
||||
QVector<EffectMeta> video_effects;
|
||||
QVector<EffectMeta> audio_effects;
|
||||
QMutex effects_loaded;
|
||||
|
||||
QVector<QString> video_effect_names;
|
||||
QVector<QString> audio_effect_names;
|
||||
|
||||
void init_effects() {
|
||||
video_effect_names.resize(VIDEO_EFFECT_COUNT);
|
||||
audio_effect_names.resize(AUDIO_EFFECT_COUNT);
|
||||
|
||||
video_effect_names[VIDEO_TRANSFORM_EFFECT] = "Transform";
|
||||
video_effect_names[VIDEO_SHAKE_EFFECT] = "Shake";
|
||||
video_effect_names[VIDEO_TEXT_EFFECT] = "Text";
|
||||
video_effect_names[VIDEO_SOLID_EFFECT] = "Solid";
|
||||
video_effect_names[VIDEO_INVERT_EFFECT] = "Invert";
|
||||
video_effect_names[VIDEO_CHROMAKEY_EFFECT] = "Chroma Key";
|
||||
video_effect_names[VIDEO_GAUSSIANBLUR_EFFECT] = "Gaussian Blur";
|
||||
video_effect_names[VIDEO_CROP_EFFECT] = "Crop";
|
||||
video_effect_names[VIDEO_FLIP_EFFECT] = "Flip";
|
||||
video_effect_names[VIDEO_BOXBLUR_EFFECT] = "Box Blur";
|
||||
video_effect_names[VIDEO_WAVE_EFFECT] = "Wave";
|
||||
video_effect_names[VIDEO_TEMPERATURE_EFFECT] = "Temperature";
|
||||
|
||||
audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume";
|
||||
audio_effect_names[AUDIO_PAN_EFFECT] = "Pan";
|
||||
audio_effect_names[AUDIO_NOISE_EFFECT] = "Noise";
|
||||
audio_effect_names[AUDIO_TONE_EFFECT] = "Tone";
|
||||
|
||||
dout << "Starting init effect (TODO: multithread this)";
|
||||
QString effects_path = get_effects_dir();
|
||||
QDir effects_dir(effects_path);
|
||||
if (effects_dir.exists()) {
|
||||
QList<QString> entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files);
|
||||
for (int i=0;i<entries.size();i++) {
|
||||
QFile file(effects_path + "/" + entries.at(i));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
dout << "[ERROR] Could not open file";
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(&file);
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "effect") {
|
||||
QString effect_name = "";
|
||||
QString effect_cat = "";
|
||||
int effect_type = EFFECT_TYPE_INVALID;
|
||||
const QXmlStreamAttributes attr = reader.attributes();
|
||||
for (int j=0;j<attr.size();j++) {
|
||||
if (attr.at(j).name() == "name") {
|
||||
effect_name = attr.at(j).value().toString();
|
||||
} else if (attr.at(j).name() == "category") {
|
||||
effect_cat = attr.at(j).value().toString();
|
||||
} else if (attr.at(j).name() == "type") {
|
||||
QString compare = attr.at(j).value().toString().toUpper();
|
||||
if (compare == "VIDEO") {
|
||||
effect_type = EFFECT_TYPE_VIDEO;
|
||||
} else if (compare == "AUDIO") {
|
||||
effect_type = EFFECT_TYPE_AUDIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (effect_type != EFFECT_TYPE_INVALID && !effect_name.isEmpty()) {
|
||||
EffectMeta em;
|
||||
em.name = effect_name;
|
||||
em.category = effect_cat;
|
||||
em.filename = entries.at(i);
|
||||
if (effect_type == EFFECT_TYPE_VIDEO) {
|
||||
video_effects.append(em);
|
||||
} else {
|
||||
audio_effects.append(em);
|
||||
}
|
||||
} else {
|
||||
dout << "[ERROR] Invalid effect found in" << entries.at(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
dout << "Completed init effect (TODO: multithread this)";
|
||||
Effect* create_effect(Clip* c, const EffectMeta* em) {
|
||||
if (!em->filename.isEmpty()) {
|
||||
// load effect from file
|
||||
return new Effect(c, em);
|
||||
} else if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) {
|
||||
// must be an internal effect
|
||||
switch (em->internal) {
|
||||
case EFFECT_INTERNAL_TRANSFORM: return new TransformEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_TEXT: return new TextEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_SOLID: return new SolidEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_NOISE: return new AudioNoiseEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_VOLUME: return new VolumeEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_PAN: return new PanEffect(c, em); break;
|
||||
case EFFECT_INTERNAL_TONE: return new ToneEffect(c, em); break;
|
||||
}
|
||||
} else {
|
||||
dout << "[ERROR] Invalid effect data";
|
||||
QMessageBox::critical(mainWindow, "Invalid effect", "No candidate for effect '" + em->name + "'. This effect may be corrupt. Try reinstalling it or Olive.");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Effect* create_effect(int effect_id, Clip* c) {
|
||||
return NULL;
|
||||
const EffectMeta* get_internal_meta(int internal_id) {
|
||||
for (int i=0;i<audio_effects.size();i++) {
|
||||
if (audio_effects.at(i).internal == internal_id) {
|
||||
return &audio_effects.at(i);
|
||||
}
|
||||
}
|
||||
for (int i=0;i<video_effects.size();i++) {
|
||||
if (video_effects.at(i).internal == internal_id) {
|
||||
return &video_effects.at(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_internal_effects() {
|
||||
EffectMeta em;
|
||||
|
||||
em.name = "Volume";
|
||||
em.internal = EFFECT_INTERNAL_VOLUME;
|
||||
audio_effects.append(em);
|
||||
|
||||
em.name = "Pan";
|
||||
em.internal = EFFECT_INTERNAL_PAN;
|
||||
audio_effects.append(em);
|
||||
|
||||
em.name = "Tone";
|
||||
em.internal = EFFECT_INTERNAL_TONE;
|
||||
audio_effects.append(em);
|
||||
|
||||
em.name = "Noise";
|
||||
em.internal = EFFECT_INTERNAL_NOISE;
|
||||
audio_effects.append(em);
|
||||
|
||||
em.name = "Transform";
|
||||
em.category = "Distort";
|
||||
em.internal = EFFECT_INTERNAL_TRANSFORM;
|
||||
video_effects.append(em);
|
||||
|
||||
em.name = "Text";
|
||||
em.category = "Render";
|
||||
em.internal = EFFECT_INTERNAL_TEXT;
|
||||
video_effects.append(em);
|
||||
|
||||
em.name = "Solid";
|
||||
em.category = "Render";
|
||||
em.internal = EFFECT_INTERNAL_SOLID;
|
||||
video_effects.append(em);
|
||||
}
|
||||
|
||||
void load_shader_effects() {
|
||||
QString effects_path = get_effects_dir();
|
||||
QDir effects_dir(effects_path);
|
||||
if (effects_dir.exists()) {
|
||||
QList<QString> entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files);
|
||||
for (int i=0;i<entries.size();i++) {
|
||||
QFile file(effects_path + "/" + entries.at(i));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
dout << "[ERROR] Could not open" << entries.at(i);
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(&file);
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "effect") {
|
||||
QString effect_name = "";
|
||||
QString effect_cat = "";
|
||||
const QXmlStreamAttributes attr = reader.attributes();
|
||||
for (int j=0;j<attr.size();j++) {
|
||||
if (attr.at(j).name() == "name") {
|
||||
effect_name = attr.at(j).value().toString();
|
||||
} else if (attr.at(j).name() == "category") {
|
||||
effect_cat = attr.at(j).value().toString();
|
||||
}
|
||||
}
|
||||
if (!effect_name.isEmpty()) {
|
||||
EffectMeta em;
|
||||
em.name = effect_name;
|
||||
em.category = effect_cat;
|
||||
em.filename = entries.at(i);
|
||||
em.internal = -1;
|
||||
video_effects.append(em);
|
||||
} else {
|
||||
dout << "[ERROR] Invalid effect found in" << entries.at(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_vst_effects() {
|
||||
|
||||
}
|
||||
|
||||
void init_effects() {
|
||||
dout << "Starting init effect (TODO: multithread this)";
|
||||
effects_loaded.lock();
|
||||
load_internal_effects();
|
||||
load_shader_effects();
|
||||
load_vst_effects();
|
||||
effects_loaded.unlock();
|
||||
dout << "Completed init effect (TODO: multithread this)";
|
||||
}
|
||||
|
||||
double double_lerp(double a, double b, double t) {
|
||||
return ((1.0 - t) * a) + (t * b);
|
||||
}
|
||||
|
||||
Effect::Effect(Clip* c, const EffectMeta &em) :
|
||||
Effect::Effect(Clip* c, const EffectMeta *em) :
|
||||
parent_clip(c),
|
||||
meta(em),
|
||||
enable_shader(false),
|
||||
@@ -126,6 +184,7 @@ Effect::Effect(Clip* c, const EffectMeta &em) :
|
||||
enable_superimpose(false),
|
||||
isOpen(false),
|
||||
glslProgram(NULL),
|
||||
texture(NULL),
|
||||
bound(false)
|
||||
{
|
||||
// set up base UI
|
||||
@@ -138,147 +197,184 @@ Effect::Effect(Clip* c, const EffectMeta &em) :
|
||||
container->setContents(ui);
|
||||
|
||||
// set up UI from effect file
|
||||
container->setText(em.name);
|
||||
QFile effect_file(get_effects_dir() + "/" + em.filename);
|
||||
container->setText(em->name);
|
||||
|
||||
if (effect_file.open(QFile::ReadOnly)) {
|
||||
QXmlStreamReader reader(&effect_file);
|
||||
if (!em->filename.isEmpty()) {
|
||||
QFile effect_file(get_effects_dir() + "/" + em->filename);
|
||||
if (effect_file.open(QFile::ReadOnly)) {
|
||||
QXmlStreamReader reader(&effect_file);
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "row" && reader.isStartElement()) {
|
||||
QString row_name;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "name") {
|
||||
row_name = attr.value().toString();
|
||||
}
|
||||
}
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name);
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
int type = -1;
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "row" && reader.isStartElement()) {
|
||||
QString row_name;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "name") {
|
||||
row_name = attr.value().toString();
|
||||
}
|
||||
}
|
||||
if (!row_name.isEmpty()) {
|
||||
EffectRow* row = add_row(row_name + ":");
|
||||
while (!reader.atEnd() && !(reader.name() == "row" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "field" && reader.isStartElement()) {
|
||||
int type = EFFECT_TYPE_VIDEO;
|
||||
QString id;
|
||||
|
||||
// get field type
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "type") {
|
||||
QString comp = attr.value().toString().toUpper();
|
||||
if (comp == "DOUBLE") {
|
||||
type = EFFECT_FIELD_DOUBLE;
|
||||
} else if (comp == "BOOL") {
|
||||
type = EFFECT_FIELD_BOOL;
|
||||
} else if (comp == "COLOR") {
|
||||
type = EFFECT_FIELD_COLOR;
|
||||
} else if (comp == "COMBO") {
|
||||
type = EFFECT_FIELD_COMBO;
|
||||
} else if (comp == "FONT") {
|
||||
type = EFFECT_FIELD_FONT;
|
||||
} else if (comp == "STRING") {
|
||||
type = EFFECT_FIELD_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
// get field type
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "type") {
|
||||
QString comp = attr.value().toString().toUpper();
|
||||
if (comp == "DOUBLE") {
|
||||
type = EFFECT_FIELD_DOUBLE;
|
||||
} else if (comp == "BOOL") {
|
||||
type = EFFECT_FIELD_BOOL;
|
||||
} else if (comp == "COLOR") {
|
||||
type = EFFECT_FIELD_COLOR;
|
||||
} else if (comp == "COMBO") {
|
||||
type = EFFECT_FIELD_COMBO;
|
||||
} else if (comp == "FONT") {
|
||||
type = EFFECT_FIELD_FONT;
|
||||
} else if (comp == "STRING") {
|
||||
type = EFFECT_FIELD_STRING;
|
||||
}
|
||||
} else if (attr.name() == "id") {
|
||||
id = attr.value().toString();
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "add field" << type;
|
||||
if (type > -1) {
|
||||
EffectField* field = row->add_field(type);
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_double_default_value(attr.value().toDouble());
|
||||
} else if (attr.name() == "min") {
|
||||
field->set_double_minimum_value(attr.value().toDouble());
|
||||
} else if (attr.name() == "max") {
|
||||
field->set_double_maximum_value(attr.value().toDouble());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor color;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "r") {
|
||||
color.setRed(attr.value().toInt());
|
||||
} else if (attr.name() == "g") {
|
||||
color.setGreen(attr.value().toInt());
|
||||
} else if (attr.name() == "b") {
|
||||
color.setBlue(attr.value().toInt());
|
||||
} else if (attr.name() == "rf") {
|
||||
color.setRedF(attr.value().toFloat());
|
||||
} else if (attr.name() == "gf") {
|
||||
color.setGreenF(attr.value().toFloat());
|
||||
} else if (attr.name() == "bf") {
|
||||
color.setBlueF(attr.value().toFloat());
|
||||
} else if (attr.name() == "hex") {
|
||||
color.setNamedColor(attr.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_string_value(attr.value().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_bool_value(attr.value() == "1");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
int combo_index = 0;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
combo_index = attr.value().toInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "option" && reader.isStartElement()) {
|
||||
reader.readNext();
|
||||
field->add_combo_item(reader.text().toString(), 0);
|
||||
}
|
||||
}
|
||||
field->set_combo_index(combo_index);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_font_name(attr.value().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
if (id.isEmpty()) {
|
||||
dout << "[ERROR] Couldn't load field from" << em->filename << "- ID cannot be empty.";
|
||||
} else if (type > -1) {
|
||||
EffectField* field = row->add_field(type);
|
||||
field->id = id;
|
||||
connect(field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
switch (type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_double_default_value(attr.value().toDouble());
|
||||
} else if (attr.name() == "min") {
|
||||
field->set_double_minimum_value(attr.value().toDouble());
|
||||
} else if (attr.name() == "max") {
|
||||
field->set_double_maximum_value(attr.value().toDouble());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
{
|
||||
QColor color;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "r") {
|
||||
color.setRed(attr.value().toInt());
|
||||
} else if (attr.name() == "g") {
|
||||
color.setGreen(attr.value().toInt());
|
||||
} else if (attr.name() == "b") {
|
||||
color.setBlue(attr.value().toInt());
|
||||
} else if (attr.name() == "rf") {
|
||||
color.setRedF(attr.value().toFloat());
|
||||
} else if (attr.name() == "gf") {
|
||||
color.setGreenF(attr.value().toFloat());
|
||||
} else if (attr.name() == "bf") {
|
||||
color.setBlueF(attr.value().toFloat());
|
||||
} else if (attr.name() == "hex") {
|
||||
color.setNamedColor(attr.value());
|
||||
}
|
||||
}
|
||||
field->set_color_value(color);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_string_value(attr.value().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_bool_value(attr.value() == "1");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
{
|
||||
int combo_index = 0;
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
combo_index = attr.value().toInt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
|
||||
reader.readNext();
|
||||
if (reader.name() == "option" && reader.isStartElement()) {
|
||||
reader.readNext();
|
||||
field->add_combo_item(reader.text().toString(), 0);
|
||||
}
|
||||
}
|
||||
field->set_combo_index(combo_index);
|
||||
}
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "default") {
|
||||
field->set_font_name(attr.value().toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == "shader" && reader.isStartElement()) {
|
||||
enable_shader = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "vert") {
|
||||
vertPath = attr.value().toString();
|
||||
} else if (attr.name() == "frag") {
|
||||
fragPath = attr.value().toString();
|
||||
}
|
||||
}
|
||||
} else if (reader.name() == "superimpose" && reader.isStartElement()) {
|
||||
enable_superimpose = true;
|
||||
const QXmlStreamAttributes& attributes = reader.attributes();
|
||||
for (int i=0;i<attributes.size();i++) {
|
||||
const QXmlStreamAttribute& attr = attributes.at(i);
|
||||
if (attr.name() == "script") {
|
||||
QFile script_file = get_effects_dir() + "/" + attr.value().toString();
|
||||
if (script_file.open(QFile::ReadOnly)) {
|
||||
script = script_file.readAll();
|
||||
wrapper_obj = jsEngine.newQObject(&painter_wrapper);
|
||||
} else {
|
||||
dout << "[ERROR] Failed to open superimpose script file for" << em->filename;
|
||||
enable_superimpose = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
|
||||
effect_file.close();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to open effect file" << em.filename;
|
||||
}
|
||||
effect_file.close();
|
||||
} else {
|
||||
dout << "[ERROR] Failed to open effect file" << em->filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Effect::~Effect() {
|
||||
@@ -294,13 +390,14 @@ Effect::~Effect() {
|
||||
void Effect::copy_field_keyframes(Effect* e) {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
e->rows.at(i)->setKeyframing(rows.at(i)->isKeyframing());
|
||||
e->rows.at(i)->keyframe_times = rows.at(i)->keyframe_times;
|
||||
e->rows.at(i)->keyframe_types = rows.at(i)->keyframe_types;
|
||||
EffectRow* copy_row = e->rows.at(i);
|
||||
copy_row->setKeyframing(row->isKeyframing());
|
||||
copy_row->keyframe_times = row->keyframe_times;
|
||||
copy_row->keyframe_types = row->keyframe_types;
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
e->rows.at(i)->field(j)->set_current_data(field->get_current_data());
|
||||
e->rows.at(i)->field(j)->keyframe_data = field->keyframe_data;
|
||||
copy_row->field(j)->set_current_data(field->get_current_data());
|
||||
copy_row->field(j)->keyframe_data = field->keyframe_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,8 +502,30 @@ void Effect::load(QXmlStreamReader& stream) {
|
||||
// read field
|
||||
if (stream.name() == "field" && stream.isStartElement()) {
|
||||
if (field_count < row->fieldCount()) {
|
||||
EffectField* field = row->field(field_count);
|
||||
// match field using ID
|
||||
bool found_field_by_id = false;
|
||||
int field_number = field_count;
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "id") {
|
||||
for (int l=0;l<row->fieldCount();l++) {
|
||||
if (row->field(l)->id == attr.value()) {
|
||||
field_number = l;
|
||||
found_field_by_id = true;
|
||||
dout << "[INFO] Found field by ID";
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO DEPRECATED, only used for backwards compatibility with 180820
|
||||
if (!found_field_by_id) dout << "[INFO] Found field by field number";
|
||||
|
||||
EffectField* field = row->field(field_number);
|
||||
|
||||
// get current field value
|
||||
for (int k=0;k<stream.attributes().size();k++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(k);
|
||||
if (attr.name() == "value") {
|
||||
@@ -455,6 +574,7 @@ void Effect::save(QXmlStreamWriter& stream) {
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
stream.writeStartElement("field"); // field
|
||||
stream.writeAttribute("id", field->id);
|
||||
stream.writeAttribute("value", save_data_to_string(field->type, field->get_current_data()));
|
||||
for (int k=0;k<field->keyframe_data.size();k++) {
|
||||
stream.writeTextElement("key", save_data_to_string(field->type, field->keyframe_data.at(k)));
|
||||
@@ -470,24 +590,34 @@ void Effect::open() {
|
||||
dout << "[WARNING] Tried to open an effect that was already open";
|
||||
close();
|
||||
}
|
||||
if (QOpenGLContext::currentContext() == NULL) {
|
||||
dout << "[WARNING] No current context to create a shader program for - will retry next repaint";
|
||||
if (enable_shader) {
|
||||
if (QOpenGLContext::currentContext() == NULL) {
|
||||
dout << "[WARNING] No current context to create a shader program for - will retry next repaint";
|
||||
} else {
|
||||
glslProgram = new QOpenGLShaderProgram();
|
||||
if (!vertPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, get_effects_dir() + "/" + vertPath);
|
||||
if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, get_effects_dir() + "/" + fragPath);
|
||||
glslProgram->link();
|
||||
isOpen = true;
|
||||
}
|
||||
} else {
|
||||
glslProgram = new QOpenGLShaderProgram();
|
||||
if (!vertPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, vertPath);
|
||||
if (!fragPath.isEmpty()) glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, fragPath);
|
||||
glslProgram->link();
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
if (enable_superimpose) {
|
||||
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::close() {
|
||||
if (!isOpen) {
|
||||
dout << "[WARNING] Tried to close an effect that was already closed";
|
||||
} else {
|
||||
delete glslProgram;
|
||||
}
|
||||
glslProgram = NULL;
|
||||
if (glslProgram != NULL) {
|
||||
delete glslProgram;
|
||||
glslProgram = NULL;
|
||||
}
|
||||
delete_texture();
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
@@ -496,7 +626,7 @@ void Effect::startEffect() {
|
||||
open();
|
||||
dout << "[WARNING] Tried to start a closed effect - opening";
|
||||
}
|
||||
bound = glslProgram->bind();
|
||||
if (enable_shader) bound = glslProgram->bind();
|
||||
}
|
||||
|
||||
void Effect::endEffect() {
|
||||
@@ -505,32 +635,44 @@ void Effect::endEffect() {
|
||||
}
|
||||
|
||||
Effect* Effect::copy(Clip* c) {
|
||||
Effect* copy = create_effect(id, c);
|
||||
Effect* copy = create_effect(c, meta);
|
||||
copy->set_enabled(is_enabled());
|
||||
copy_field_keyframes(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void Effect::process_shader(double) {}
|
||||
void Effect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight());
|
||||
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
if (!field->id.isEmpty()) {
|
||||
switch (field->type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
glslProgram->setUniformValue(field->id.toLatin1().constData(), (GLfloat) field->get_double_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_color_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_STRING: break; // can you even send a string to a uniform value?
|
||||
case EFFECT_FIELD_BOOL:
|
||||
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_bool_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
glslProgram->setUniformValue(field->id.toLatin1().constData(), field->get_combo_index(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_FONT: break; // can you even send a string to a uniform value?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::process_coords(double, GLTextureCoords&) {}
|
||||
GLuint Effect::process_superimpose(double) {return 0;}
|
||||
void Effect::process_audio(double, double, quint8*, int, int) {}
|
||||
|
||||
SuperimposeEffect::SuperimposeEffect(Clip* c, const EffectMeta& e) : Effect(c, e), texture(NULL) {
|
||||
enable_superimpose = true;
|
||||
}
|
||||
|
||||
void SuperimposeEffect::open() {
|
||||
Effect::open();
|
||||
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
}
|
||||
|
||||
void SuperimposeEffect::close() {
|
||||
Effect::close();
|
||||
deleteTexture();
|
||||
}
|
||||
|
||||
GLuint SuperimposeEffect::process_superimpose(double timecode) {
|
||||
GLuint Effect::process_superimpose(double timecode) {
|
||||
bool recreate_texture = false;
|
||||
int width = parent_clip->getWidth();
|
||||
int height = parent_clip->getHeight();
|
||||
@@ -546,7 +688,7 @@ GLuint SuperimposeEffect::process_superimpose(double timecode) {
|
||||
|
||||
if (texture != NULL) {
|
||||
if (recreate_texture || texture->width() != img.width() || texture->height() != img.height()) {
|
||||
deleteTexture();
|
||||
delete_texture();
|
||||
texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
||||
texture->setData(img);
|
||||
} else {
|
||||
@@ -555,17 +697,72 @@ GLuint SuperimposeEffect::process_superimpose(double timecode) {
|
||||
return texture->textureId();
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void SuperimposeEffect::redraw(double) {}
|
||||
void Effect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count) {
|
||||
// only volume/pan, hand off to AU and VST for all other cases
|
||||
|
||||
void SuperimposeEffect::deleteTexture() {
|
||||
delete texture;
|
||||
texture = NULL;
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
|
||||
for (int i=0;i<nb_bytes;i+=2) {
|
||||
qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
|
||||
/*jsEngine.globalObject().setProperty("sample", samp);
|
||||
jsEngine.globalObject().setProperty("volume", row(0)->field(0)->get_double_value(timecode_start+(interval*i), true));
|
||||
QJSValue result = eval.call();
|
||||
samp = result.toInt();*/
|
||||
/*QJSValueList args;
|
||||
args << samples << nb_bytes;*/
|
||||
|
||||
|
||||
samples[i+1] = (quint8) (samp >> 8);
|
||||
samples[i] = (quint8) samp;
|
||||
}
|
||||
}
|
||||
|
||||
bool SuperimposeEffect::valueHasChanged(double timecode) {
|
||||
void Effect::redraw(double timecode) {
|
||||
// run javascript
|
||||
QPainter p(&img);
|
||||
painter_wrapper.img = &img;
|
||||
painter_wrapper.painter = &p;
|
||||
|
||||
jsEngine.globalObject().setProperty("painter", wrapper_obj);
|
||||
jsEngine.globalObject().setProperty("width", parent_clip->getWidth());
|
||||
jsEngine.globalObject().setProperty("height", parent_clip->getHeight());
|
||||
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
for (int j=0;j<row->fieldCount();j++) {
|
||||
EffectField* field = row->field(j);
|
||||
if (!field->id.isEmpty()) {
|
||||
switch (field->type) {
|
||||
case EFFECT_FIELD_DOUBLE:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_double_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_COLOR:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_color_value(timecode).name());
|
||||
break;
|
||||
case EFFECT_FIELD_STRING:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_string_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_BOOL:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_bool_value(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_COMBO:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_combo_index(timecode));
|
||||
break;
|
||||
case EFFECT_FIELD_FONT:
|
||||
jsEngine.globalObject().setProperty(field->id, field->get_font_name(timecode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsEngine.evaluate(script);
|
||||
}
|
||||
|
||||
bool Effect::valueHasChanged(double timecode) {
|
||||
if (cachedValues.size() == 0) {
|
||||
for (int i=0;i<row_count();i++) {
|
||||
EffectRow* crow = row(i);
|
||||
@@ -593,6 +790,13 @@ bool SuperimposeEffect::valueHasChanged(double timecode) {
|
||||
}
|
||||
}
|
||||
|
||||
void Effect::delete_texture() {
|
||||
if (texture != NULL) {
|
||||
delete texture;
|
||||
texture = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Effect Row Definitions */
|
||||
|
||||
EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, int row) :
|
||||
@@ -1118,3 +1322,8 @@ qint16 mix_audio_sample(qint16 a, qint16 b) {
|
||||
mixed_sample = qMax(qMin(mixed_sample, static_cast<qint32>(INT16_MAX)), static_cast<qint32>(INT16_MIN));
|
||||
return static_cast<qint16>(mixed_sample);
|
||||
}
|
||||
|
||||
double log_volume(double linear) {
|
||||
// expects a value between 0 and 1 (or more if amplifying)
|
||||
return (qExp(linear)-1)/(M_E-1);
|
||||
}
|
||||
|
||||
+38
-47
@@ -8,6 +8,8 @@
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QJSEngine>
|
||||
#include <QMutex>
|
||||
class QLabel;
|
||||
class QWidget;
|
||||
class CollapsibleWidget;
|
||||
@@ -22,43 +24,22 @@ class EffectRow;
|
||||
class CheckboxEx;
|
||||
class KeyframeDelete;
|
||||
|
||||
enum VideoEffects {
|
||||
VIDEO_TRANSFORM_EFFECT,
|
||||
VIDEO_SHAKE_EFFECT,
|
||||
VIDEO_TEXT_EFFECT,
|
||||
VIDEO_SOLID_EFFECT,
|
||||
VIDEO_INVERT_EFFECT,
|
||||
VIDEO_CHROMAKEY_EFFECT,
|
||||
VIDEO_GAUSSIANBLUR_EFFECT,
|
||||
VIDEO_CROP_EFFECT,
|
||||
VIDEO_FLIP_EFFECT,
|
||||
VIDEO_BOXBLUR_EFFECT,
|
||||
VIDEO_WAVE_EFFECT,
|
||||
VIDEO_TEMPERATURE_EFFECT,
|
||||
VIDEO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
enum AudioEffects {
|
||||
AUDIO_VOLUME_EFFECT,
|
||||
AUDIO_PAN_EFFECT,
|
||||
AUDIO_NOISE_EFFECT,
|
||||
AUDIO_TONE_EFFECT,
|
||||
AUDIO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
struct EffectMeta {
|
||||
QString name;
|
||||
QString category;
|
||||
QString filename;
|
||||
int internal;
|
||||
};
|
||||
|
||||
extern QVector<EffectMeta> video_effects;
|
||||
extern QVector<EffectMeta> audio_effects;
|
||||
|
||||
extern QVector<QString> video_effect_names; // deprecated
|
||||
extern QVector<QString> audio_effect_names; // deprecated
|
||||
double log_volume(double linear);
|
||||
void init_effects();
|
||||
Effect* create_effect(int effect_id, Clip* c);
|
||||
Effect* create_effect(Clip* c, const EffectMeta *em);
|
||||
const EffectMeta* get_internal_meta(int internal_id);
|
||||
|
||||
extern QMutex effects_loaded;
|
||||
|
||||
#define EFFECT_TYPE_INVALID 0
|
||||
#define EFFECT_TYPE_VIDEO 1
|
||||
@@ -75,6 +56,15 @@ Effect* create_effect(int effect_id, Clip* c);
|
||||
#define EFFECT_KEYFRAME_HOLD 1
|
||||
#define EFFECT_KEYFRAME_BEZIER 2
|
||||
|
||||
#define EFFECT_INTERNAL_TRANSFORM 0
|
||||
#define EFFECT_INTERNAL_TEXT 1
|
||||
#define EFFECT_INTERNAL_SOLID 2
|
||||
#define EFFECT_INTERNAL_NOISE 3
|
||||
#define EFFECT_INTERNAL_VOLUME 4
|
||||
#define EFFECT_INTERNAL_PAN 5
|
||||
#define EFFECT_INTERNAL_TONE 6
|
||||
#define EFFECT_INTERNAL_COUNT 7
|
||||
|
||||
struct GLTextureCoords {
|
||||
int vertexTopLeftX;
|
||||
int vertexTopLeftY;
|
||||
@@ -103,6 +93,7 @@ public:
|
||||
EffectField(EffectRow* parent, int t);
|
||||
EffectRow* parent_row;
|
||||
int type;
|
||||
QString id;
|
||||
|
||||
QVariant get_previous_data();
|
||||
QVariant get_current_data();
|
||||
@@ -193,10 +184,10 @@ private:
|
||||
class Effect : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Effect(Clip* c, const EffectMeta& em);
|
||||
Effect(Clip* c, const EffectMeta* em);
|
||||
~Effect();
|
||||
Clip* parent_clip;
|
||||
const EffectMeta& meta;
|
||||
const EffectMeta* meta;
|
||||
int id;
|
||||
QString name;
|
||||
CollapsibleWidget* container;
|
||||
@@ -210,15 +201,15 @@ public:
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
Effect* copy(Clip* c);
|
||||
Effect* copy(Clip* c);
|
||||
void copy_field_keyframes(Effect *e);
|
||||
|
||||
void load(QXmlStreamReader& stream);
|
||||
void save(QXmlStreamWriter& stream);
|
||||
|
||||
// glsl handling
|
||||
virtual void open();
|
||||
virtual void close();
|
||||
void open();
|
||||
void close();
|
||||
virtual void startEffect();
|
||||
virtual void endEffect();
|
||||
|
||||
@@ -231,38 +222,38 @@ public:
|
||||
|
||||
const char* ffmpeg_filter;
|
||||
|
||||
virtual void process_shader(double timecode);
|
||||
void process_shader(double timecode);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords);
|
||||
virtual GLuint process_superimpose(double timecode);
|
||||
virtual GLuint process_superimpose(double timecode);
|
||||
virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
public slots:
|
||||
void field_changed();
|
||||
protected:
|
||||
// glsl effect
|
||||
QOpenGLShaderProgram* glslProgram;
|
||||
QString vertPath;
|
||||
QString fragPath;
|
||||
bool isOpen;
|
||||
|
||||
// superimpose effect
|
||||
QImage img;
|
||||
QOpenGLTexture* texture;
|
||||
private:
|
||||
// superimpose effect
|
||||
QJSEngine jsEngine;
|
||||
QJSValue wrapper_obj;
|
||||
QString script;
|
||||
|
||||
bool isOpen;
|
||||
QVector<EffectRow*> rows;
|
||||
QGridLayout* ui_layout;
|
||||
QWidget* ui;
|
||||
bool bound;
|
||||
};
|
||||
|
||||
class SuperimposeEffect : public Effect {
|
||||
public:
|
||||
SuperimposeEffect(Clip* c, const EffectMeta& e);
|
||||
virtual void open();
|
||||
virtual void close();
|
||||
virtual GLuint process_superimpose(double timecode);
|
||||
// superimpose functions
|
||||
virtual void redraw(double timecode);
|
||||
protected:
|
||||
QImage img;
|
||||
QOpenGLTexture* texture;
|
||||
void deleteTexture();
|
||||
bool valueHasChanged(double timecode);
|
||||
private:
|
||||
QVector<QVariant> cachedValues;
|
||||
void delete_texture();
|
||||
};
|
||||
|
||||
#endif // EFFECT_H
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Place this folder in the runtime folder of Olive.
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<effect name="Box Blur" category="Blur">
|
||||
<row name="Radius">
|
||||
<field type="double" min="0" default="10" id="radius"/>
|
||||
</row>
|
||||
<row name="Horizontal">
|
||||
<field type="bool" default="1" id="horiz_blur"/>
|
||||
</row>
|
||||
<row name="Vertical">
|
||||
<field type="bool" default="1" id="vert_blur"/>
|
||||
</row>
|
||||
<shader vert="common.vert" frag="boxblur.frag"/>
|
||||
</effect>
|
||||
@@ -0,0 +1,36 @@
|
||||
#version 110
|
||||
|
||||
uniform float left;
|
||||
uniform float top;
|
||||
uniform float right;
|
||||
uniform float bottom;
|
||||
uniform float feather;
|
||||
|
||||
uniform mediump float amount_val;
|
||||
uniform sampler2D myTexture;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 textureColor = texture2D(myTexture, vec2(vTexCoord.x, vTexCoord.y));
|
||||
float alpha = textureColor.a;
|
||||
if (feather == 0.0) {
|
||||
if (vTexCoord.x < (left*0.01) || vTexCoord.y < (top*0.01) || vTexCoord.x > (1.0-(right*0.01)) || vTexCoord.y > (1.0-(bottom*0.01))) {
|
||||
alpha = 0.0;
|
||||
}
|
||||
} else {
|
||||
float f = pow(2.0, 10.0-(feather*0.1));
|
||||
if (left > 0.0) alpha = alpha * clamp(((vTexCoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left
|
||||
if (top > 0.0) alpha = alpha * clamp(((vTexCoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top
|
||||
if (right > 0.0) alpha = alpha * clamp((((1.0-vTexCoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right
|
||||
if (bottom > 0.0) alpha = alpha * clamp((((1.0-vTexCoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom
|
||||
}
|
||||
|
||||
|
||||
|
||||
gl_FragColor = vec4(
|
||||
textureColor.r,
|
||||
textureColor.g,
|
||||
textureColor.b,
|
||||
alpha
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<effect name="Crop" category="Distort">
|
||||
<row name="Left">
|
||||
<field type="double" min="0" default="0" max="100" id="left"/>
|
||||
</row>
|
||||
<row name="Top">
|
||||
<field type="double" min="0" default="0" max="100" id="top"/>
|
||||
</row>
|
||||
<row name="Right">
|
||||
<field type="double" min="0" default="0" max="100" id="right"/>
|
||||
</row>
|
||||
<row name="Bottom">
|
||||
<field type="double" min="0" default="0" max="100" id="bottom"/>
|
||||
</row>
|
||||
<row name="Feather">
|
||||
<field type="double" min="0" default="0" id="feather"/>
|
||||
</row>
|
||||
<shader vert="common.vert" frag="crop.frag"/>
|
||||
</effect>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<effect name="Gaussian Blur" category="Blur">
|
||||
<row name="Radius">
|
||||
<field type="double" min="0" default="10" id="radius"/>
|
||||
</row>
|
||||
<row name="Sigma">
|
||||
<field type="double" min="0" default="5.5" id="sigma"/>
|
||||
</row>
|
||||
<row name="Horizontal">
|
||||
<field type="bool" default="1" id="horiz_blur"/>
|
||||
</row>
|
||||
<row name="Vertical">
|
||||
<field type="bool" default="1" id="vert_blur"/>
|
||||
</row>
|
||||
<shader vert="common.vert" frag="gaussianblur.frag"/>
|
||||
</effect>
|
||||
@@ -1,11 +1,12 @@
|
||||
#version 110
|
||||
|
||||
uniform mediump float amount_val;
|
||||
uniform mediump float amount;
|
||||
uniform sampler2D myTexture;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 textureColor = texture2D(myTexture, vTexCoord);
|
||||
float amount_val = amount * 0.01;
|
||||
gl_FragColor = vec4(
|
||||
textureColor.r+((1.0-textureColor.r-textureColor.r)*amount_val),
|
||||
textureColor.g+((1.0-textureColor.g-textureColor.g)*amount_val),
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<effect name="Invert" category="Color">
|
||||
<row name="Amount">
|
||||
<field type="double" min="0" default="100" max="100" id="amount"/>
|
||||
</row>
|
||||
<shader vert="common.vert" frag="invert.frag"/>
|
||||
</effect>
|
||||
@@ -15,9 +15,9 @@ void main(void) {
|
||||
float y = vTexCoord.y;
|
||||
|
||||
if (vertical) {
|
||||
x -= sin((vTexCoord.y-evolution)*frequency)*intensity;
|
||||
x -= sin((vTexCoord.y-(evolution*0.01))*frequency)*intensity*0.01;
|
||||
} else {
|
||||
y -= sin((vTexCoord.x-evolution)*frequency)*intensity;
|
||||
y -= sin((vTexCoord.x-(evolution*0.01))*frequency)*intensity*0.01;
|
||||
}
|
||||
|
||||
if (y < 0.0 || y > 1.0 || x < 0.0 || x > 1.0) {
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<effect name="Wave" category="Distort">
|
||||
<row name="Frequency">
|
||||
<field type="double" min="0" default="10" id="frequency"/>
|
||||
</row>
|
||||
<row name="Intensity">
|
||||
<field type="double" default="10" id="intensity"/>
|
||||
</row>
|
||||
<row name="Evolution">
|
||||
<field type="double" default="0" id="evolution"/>
|
||||
</row>
|
||||
<row name="Vertical">
|
||||
<field type="bool" default="0" id="vertical"/>
|
||||
</row>
|
||||
<shader vert="common.vert" frag="wave.frag"/>
|
||||
</effect>
|
||||
@@ -3,13 +3,15 @@
|
||||
#include <QDateTime>
|
||||
#include <QtMath>
|
||||
|
||||
AudioNoiseEffect::AudioNoiseEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_NOISE_EFFECT) {
|
||||
AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(20);
|
||||
amount_val->id = "amount";
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL);
|
||||
mix_val->id = "mix";
|
||||
mix_val->set_bool_value(true);
|
||||
|
||||
srand(QDateTime::currentMSecsSinceEpoch());
|
||||
@@ -27,7 +29,7 @@ void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end,
|
||||
qint16 right_noise_sample = rand();
|
||||
|
||||
// set noise volume
|
||||
double vol = qSqrt(amount_val->get_double_value(timecode, true)*0.01);
|
||||
double vol = log_volume( amount_val->get_double_value(timecode, true)*0.01 );
|
||||
left_noise_sample *= vol;
|
||||
right_noise_sample *= vol;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
class AudioNoiseEffect : public Effect {
|
||||
public:
|
||||
AudioNoiseEffect(Clip* c);
|
||||
AudioNoiseEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* amount_val;
|
||||
@@ -8,11 +8,12 @@
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
PanEffect::PanEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_PAN_EFFECT) {
|
||||
PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* pan_row = add_row("Pan:");
|
||||
pan_val = pan_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
pan_val->set_double_minimum_value(-100);
|
||||
pan_val->set_double_maximum_value(100);
|
||||
pan_val->id = "pan";
|
||||
|
||||
// set defaults
|
||||
pan_val->set_double_default_value(0);
|
||||
@@ -23,7 +24,7 @@ PanEffect::PanEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_PAN_EFFECT) {
|
||||
void PanEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end - timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double pval = qSqrt(pan_val->get_double_value(timecode_start+(interval*i), true)*0.01);
|
||||
double pval = log_volume(pan_val->get_double_value(timecode_start+(interval*i), true)*0.01);
|
||||
qint16 left_sample = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
qint16 right_sample = (qint16) (((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
class PanEffect : public Effect {
|
||||
public:
|
||||
PanEffect(Clip* c);
|
||||
PanEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* pan_val;
|
||||
@@ -16,18 +16,23 @@
|
||||
#define SMPTE_STRIP_COUNT 3
|
||||
#define SMPTE_LOWER_BARS 4
|
||||
|
||||
SolidEffect::SolidEffect(Clip* c) : SuperimposeEffect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT) {
|
||||
SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_superimpose = true;
|
||||
|
||||
solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO);
|
||||
solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR);
|
||||
solid_type->add_combo_item("SMPTE Bars", SOLID_TYPE_BARS);
|
||||
solid_type->id = "type";
|
||||
|
||||
solid_color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR);
|
||||
solid_color_field->set_color_value(Qt::red);
|
||||
solid_color_field->id = "color";
|
||||
|
||||
opacity_field = add_row("Opacity:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
opacity_field->set_double_minimum_value(0);
|
||||
opacity_field->set_double_maximum_value(100);
|
||||
opacity_field->set_double_default_value(100);
|
||||
opacity_field->id = "opacity";
|
||||
|
||||
connect(solid_type, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(solid_color_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
@@ -6,10 +6,10 @@
|
||||
class QOpenGLTexture;
|
||||
#include <QImage>
|
||||
|
||||
class SolidEffect : public SuperimposeEffect {
|
||||
class SolidEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SolidEffect(Clip* c);
|
||||
SolidEffect(Clip* c, const EffectMeta *em);
|
||||
EffectField* solid_type;
|
||||
EffectField* solid_color_field;
|
||||
EffectField* opacity_field;
|
||||
@@ -19,19 +19,23 @@
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
|
||||
TextEffect::TextEffect(Clip *c) :
|
||||
SuperimposeEffect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT)
|
||||
TextEffect::TextEffect(Clip *c, const EffectMeta* em) :
|
||||
Effect(c, em)
|
||||
{
|
||||
enable_superimpose = true;
|
||||
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2);
|
||||
text_val->id = "text";
|
||||
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, 2);
|
||||
set_font_combobox->id = "font";
|
||||
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
size_val->id = "size";
|
||||
|
||||
set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
set_color_button->id = "color";
|
||||
|
||||
EffectRow* alignment_row = add_row("Alignment:");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
@@ -39,25 +43,37 @@ TextEffect::TextEffect(Clip *c) :
|
||||
halign_field->add_combo_item("Center", Qt::AlignHCenter);
|
||||
halign_field->add_combo_item("Right", Qt::AlignRight);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
halign_field->id = "halign";
|
||||
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
valign_field->add_combo_item("Top", Qt::AlignTop);
|
||||
valign_field->add_combo_item("Center", Qt::AlignVCenter);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
valign_field->id = "valign";
|
||||
|
||||
word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
word_wrap_field->id = "wordwrap";
|
||||
|
||||
outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
outline_bool->id = "outline";
|
||||
outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
outline_color->id = "outlinecolor";
|
||||
outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
outline_width->id = "outlinewidth";
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
shadow_bool->id = "shadow";
|
||||
shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
shadow_color->id = "shadowcolor";
|
||||
shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_distance->id = "shadowdistance";
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_softness->id = "shadowsoftness";
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_opacity->id = "shadowopacity";
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include <QImage>
|
||||
class QOpenGLTexture;
|
||||
|
||||
class TextEffect : public SuperimposeEffect {
|
||||
class TextEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextEffect(Clip* c);
|
||||
TextEffect(Clip* c, const EffectMeta *em);
|
||||
void redraw(double timecode);
|
||||
|
||||
EffectField* text_val;
|
||||
@@ -8,21 +8,25 @@
|
||||
#include "project/sequence.h"
|
||||
#include "debug.h"
|
||||
|
||||
ToneEffect::ToneEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_TONE_EFFECT), sinX(INT_MIN) {
|
||||
ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) {
|
||||
type_val = add_row("Type:")->add_field(EFFECT_FIELD_COMBO);
|
||||
type_val->id = "type";
|
||||
type_val->add_combo_item("Sine", TONE_TYPE_SINE);
|
||||
|
||||
freq_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
freq_val->id = "frequency";
|
||||
freq_val->set_double_minimum_value(20);
|
||||
freq_val->set_double_maximum_value(20000);
|
||||
freq_val->set_double_default_value(1000);
|
||||
|
||||
amount_val = add_row("Amount:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
amount_val->id = "amount";
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
amount_val->set_double_default_value(25);
|
||||
|
||||
mix_val = add_row("Mix:")->add_field(EFFECT_FIELD_BOOL);
|
||||
mix_val->id = "mix";
|
||||
mix_val->set_bool_value(true);
|
||||
|
||||
connect(freq_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
@@ -35,10 +39,9 @@ void ToneEffect::process_audio(double timecode_start, double timecode_end, quint
|
||||
for (int i=0;i<nb_bytes;i+=4) {
|
||||
double timecode = timecode_start+(interval*i);
|
||||
|
||||
qint16 left_tone_sample = qSin((2*M_PI*sinX*freq_val->get_double_value(timecode, true))/parent_clip->sequence->audio_frequency)*qSqrt((amount_val->get_double_value(timecode, true)*0.01))*INT16_MAX;
|
||||
qint16 left_tone_sample = qSin((2*M_PI*sinX*freq_val->get_double_value(timecode, true))/parent_clip->sequence->audio_frequency)*log_volume(amount_val->get_double_value(timecode, true)*0.01)*INT16_MAX;
|
||||
qint16 right_tone_sample = left_tone_sample;
|
||||
|
||||
|
||||
// mix with source audio
|
||||
if (mix_val->get_bool_value(timecode, true)) {
|
||||
qint16 left_sample = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
class ToneEffect : public Effect {
|
||||
public:
|
||||
ToneEffect(Clip *c);
|
||||
ToneEffect(Clip *c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* type_val;
|
||||
@@ -22,38 +22,48 @@
|
||||
#define BLEND_MODE_MULTIPLY 2
|
||||
#define BLEND_MODE_OVERLAY 3
|
||||
|
||||
TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TRANSFORM_EFFECT) {
|
||||
TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
|
||||
enable_coords = true;
|
||||
|
||||
EffectRow* position_row = add_row("Position:");
|
||||
position_x = position_row->add_field(EFFECT_FIELD_DOUBLE); // position X
|
||||
position_x->id = "posx";
|
||||
position_y = position_row->add_field(EFFECT_FIELD_DOUBLE); // position Y
|
||||
position_y->id = "posy";
|
||||
|
||||
EffectRow* scale_row = add_row("Scale:");
|
||||
scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE); // scale X (and Y is uniform scale is selected)
|
||||
scale_x->id = "scalex";
|
||||
scale_x->set_double_minimum_value(0);
|
||||
scale_x->set_double_maximum_value(3000);
|
||||
scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE); // scale Y (disabled if uniform scale is selected)
|
||||
scale_y->id = "scaley";
|
||||
scale_y->set_double_minimum_value(0);
|
||||
scale_y->set_double_maximum_value(3000);
|
||||
|
||||
EffectRow* uniform_scale_row = add_row("Uniform Scale:");
|
||||
uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL); // uniform scale option
|
||||
uniform_scale_field->id = "uniformscale";
|
||||
|
||||
EffectRow* rotation_row = add_row("Rotation:");
|
||||
rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
rotation->id = "rotation";
|
||||
|
||||
EffectRow* anchor_point_row = add_row("Anchor Point:");
|
||||
anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE); // anchor point X
|
||||
anchor_x_box->id = "anchorx";
|
||||
anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE); // anchor point Y
|
||||
anchor_y_box->id = "anchory";
|
||||
|
||||
EffectRow* opacity_row = add_row("Opacity:");
|
||||
opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE); // opacity
|
||||
opacity->id = "opacity";
|
||||
opacity->set_double_minimum_value(0);
|
||||
opacity->set_double_maximum_value(100);
|
||||
|
||||
EffectRow* blend_mode_row = add_row("Blend Mode:");
|
||||
blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO); // blend mode
|
||||
blend_mode_box->id = "blendmode";
|
||||
blend_mode_box->add_combo_item("Normal", BLEND_MODE_NORMAL);
|
||||
blend_mode_box->add_combo_item("Overlay", BLEND_MODE_OVERLAY);
|
||||
blend_mode_box->add_combo_item("Screen", BLEND_MODE_SCREEN);
|
||||
@@ -6,7 +6,7 @@
|
||||
class TransformEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformEffect(Clip* c);
|
||||
TransformEffect(Clip* c, const EffectMeta* em);
|
||||
void refresh();
|
||||
void process_coords(double timecode, GLTextureCoords& coords);
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
|
||||
VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_EFFECT) {
|
||||
VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) {
|
||||
EffectRow* volume_row = add_row("Volume:");
|
||||
volume_val = volume_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
volume_val->id = "volume";
|
||||
volume_val->set_double_minimum_value(0);
|
||||
// volume_val->set_double_maximum_value(1000);
|
||||
|
||||
// set defaults
|
||||
volume_val->set_double_default_value(100);
|
||||
@@ -23,7 +23,7 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_
|
||||
void VolumeEffect::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int) {
|
||||
double interval = (timecode_end-timecode_start)/nb_bytes;
|
||||
for (int i=0;i<nb_bytes;i+=2) {
|
||||
double vol_val = qSqrt(volume_val->get_double_value(timecode_start+(interval*i), true)*0.01);
|
||||
double vol_val = log_volume(volume_val->get_double_value(timecode_start+(interval*i), true)*0.01);
|
||||
qint32 samp = (qint16) (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF));
|
||||
samp *= vol_val;
|
||||
if (samp > INT16_MAX) {
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
class VolumeEffect : public Effect {
|
||||
public:
|
||||
VolumeEffect(Clip* c);
|
||||
VolumeEffect(Clip* c, const EffectMeta* em);
|
||||
void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count);
|
||||
|
||||
EffectField* volume_val;
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "qpainterwrapper.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include "debug.h"
|
||||
|
||||
// exposes several QPainter functions to QJSEngine
|
||||
|
||||
QPainterWrapper painter_wrapper;
|
||||
|
||||
QPainterWrapper::QPainterWrapper() {}
|
||||
|
||||
QColor get_color_from_string(const QString& s) {
|
||||
dout << s;
|
||||
|
||||
// workaround for alpha
|
||||
if (s.at(0) == '#' && s.length() == 9) {
|
||||
QColor color(s.left(7));
|
||||
color.setAlpha(s.mid(7).toInt(NULL, 16));
|
||||
return color;
|
||||
} else {
|
||||
return QColor(s);
|
||||
}
|
||||
}
|
||||
|
||||
void QPainterWrapper::fill(const QString& c) {
|
||||
img->fill(get_color_from_string(c));
|
||||
}
|
||||
|
||||
void QPainterWrapper::fillRect(int x, int y, int width, int height, const QString& brush) {
|
||||
painter->fillRect(x, y, width, height, get_color_from_string(brush));
|
||||
}
|
||||
|
||||
void QPainterWrapper::drawRect(int x, int y, int width, int height) {
|
||||
painter->drawRect(x, y, width, height);
|
||||
}
|
||||
|
||||
void QPainterWrapper::setPen(const QString& pen) {
|
||||
painter->setPen(get_color_from_string(pen));
|
||||
}
|
||||
|
||||
void QPainterWrapper::setBrush(const QString& brush) {
|
||||
painter->setBrush(get_color_from_string(brush));
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef QPAINTERWRAPPER_H
|
||||
#define QPAINTERWRAPPER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QPainter;
|
||||
|
||||
class QPainterWrapper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QPainterWrapper();
|
||||
QImage* img;
|
||||
QPainter* painter;
|
||||
public slots:
|
||||
void fill(const QString& color);
|
||||
void fillRect(int x, int y, int width, int height, const QString& brush);
|
||||
void drawRect(int x, int y, int width, int height);
|
||||
void setPen(const QString& pen);
|
||||
void setBrush(const QString& brush);
|
||||
};
|
||||
|
||||
extern QPainterWrapper painter_wrapper;
|
||||
|
||||
#endif // QPAINTERWRAPPER_H
|
||||
@@ -1,41 +0,0 @@
|
||||
#include "boxblureffect.h"
|
||||
|
||||
#include "project/clip.h"
|
||||
|
||||
BoxBlurEffect::BoxBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_BOXBLUR_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
radius_val = add_row("Radius:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
// iteration_val = add_row("Iterations:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
horiz_val = add_row("Horizontal:")->add_field(EFFECT_FIELD_BOOL);
|
||||
vert_val = add_row("Vertical:")->add_field(EFFECT_FIELD_BOOL);
|
||||
|
||||
radius_val->set_double_default_value(9);
|
||||
radius_val->set_double_minimum_value(0);
|
||||
|
||||
// iteration_val->set_double_default_value(1);
|
||||
// iteration_val->set_double_minimum_value(0);
|
||||
|
||||
horiz_val->set_bool_value(true);
|
||||
vert_val->set_bool_value(true);
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/boxblureffect.frag";
|
||||
/*vert.compileSourceFile(":/shaders/common.vert");
|
||||
frag.compileSourceFile(":/shaders/boxblureffect.frag");
|
||||
program.addShader(&vert);
|
||||
program.addShader(&frag);
|
||||
program.link();*/
|
||||
|
||||
connect(radius_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
// connect(iteration_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(horiz_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(vert_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void BoxBlurEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight());
|
||||
glslProgram->setUniformValue("radius", (GLfloat) radius_val->get_double_value(timecode));
|
||||
glslProgram->setUniformValue("horiz_blur", horiz_val->get_bool_value(timecode));
|
||||
glslProgram->setUniformValue("vert_blur", vert_val->get_bool_value(timecode));
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef BOXBLUREFFECT_H
|
||||
#define BOXBLUREFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class BoxBlurEffect : public Effect
|
||||
{
|
||||
public:
|
||||
BoxBlurEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* radius_val;
|
||||
EffectField* iteration_val;
|
||||
EffectField* horiz_val;
|
||||
EffectField* vert_val;
|
||||
};
|
||||
|
||||
#endif // BOXBLUREFFECT_H
|
||||
@@ -1,25 +0,0 @@
|
||||
#include "chromakeyeffect.h"
|
||||
|
||||
ChromaKeyEffect::ChromaKeyEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CHROMAKEY_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
color_field = add_row("Color:")->add_field(EFFECT_FIELD_COLOR);
|
||||
tolerance_field = add_row("Tolerance:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
|
||||
color_field->set_color_value(Qt::green);
|
||||
tolerance_field->set_double_default_value(10);
|
||||
|
||||
tolerance_field->set_double_minimum_value(0);
|
||||
tolerance_field->set_double_maximum_value(100);
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/chromakeyeffect.frag";
|
||||
|
||||
connect(color_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(tolerance_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void ChromaKeyEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("keyColor", color_field->get_color_value(timecode));
|
||||
glslProgram->setUniformValue("threshold", (GLfloat) (tolerance_field->get_double_value(timecode)*0.01));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef CHROMAKEYEFFECT_H
|
||||
#define CHROMAKEYEFFECT_H
|
||||
|
||||
#include "effects/effect.h"
|
||||
|
||||
class ChromaKeyEffect : public Effect {
|
||||
public:
|
||||
ChromaKeyEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* color_field;
|
||||
EffectField* tolerance_field;
|
||||
};
|
||||
|
||||
#endif // CHROMAKEYEFFECT_H
|
||||
@@ -1,67 +0,0 @@
|
||||
#include "cropeffect.h"
|
||||
|
||||
CropEffect::CropEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_CROP_EFFECT) {
|
||||
enable_coords = true;
|
||||
|
||||
left_field = add_row("Left:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
top_field = add_row("Top:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
right_field = add_row("Right:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
bottom_field = add_row("Bottom:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
|
||||
left_field->set_double_minimum_value(0);
|
||||
left_field->set_double_maximum_value(100);
|
||||
top_field->set_double_minimum_value(0);
|
||||
top_field->set_double_maximum_value(100);
|
||||
right_field->set_double_minimum_value(0);
|
||||
right_field->set_double_maximum_value(100);
|
||||
bottom_field->set_double_minimum_value(0);
|
||||
bottom_field->set_double_maximum_value(100);
|
||||
|
||||
connect(left_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(top_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(right_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(bottom_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void CropEffect::process_coords(double timecode, GLTextureCoords &coords) {
|
||||
// store initial coord data
|
||||
int left = coords.vertexTopLeftX;
|
||||
int top = coords.vertexTopLeftY;
|
||||
int right = coords.vertexBottomRightX;
|
||||
int bottom = coords.vertexBottomRightY;
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
|
||||
double texLeft = coords.textureTopLeftX;
|
||||
double texTop = coords.textureTopLeftY;
|
||||
double texRight = coords.textureBottomRightX;
|
||||
double texBottom = coords.textureBottomRightY;
|
||||
double texWidth = texRight - texLeft;
|
||||
double texHeight = texBottom - texTop;
|
||||
|
||||
// retrieve values
|
||||
double left_field_value = (left_field->get_double_value(timecode)*0.01);
|
||||
double right_field_value = 1-(right_field->get_double_value(timecode)*0.01);
|
||||
double top_field_value = (top_field->get_double_value(timecode)*0.01);
|
||||
double bottom_field_value = 1-(bottom_field->get_double_value(timecode)*0.01);
|
||||
|
||||
// validate values
|
||||
if (left_field_value > right_field_value) right_field_value = left_field_value;
|
||||
if (top_field_value > bottom_field_value) bottom_field_value = top_field_value;
|
||||
|
||||
// left
|
||||
coords.textureTopLeftX = coords.textureBottomLeftX = texLeft+(left_field_value*texWidth);
|
||||
coords.vertexTopLeftX = coords.vertexBottomLeftX = left+(width*left_field_value);
|
||||
|
||||
// right
|
||||
coords.textureTopRightX = coords.textureBottomRightX = texLeft+(right_field_value*texWidth);
|
||||
coords.vertexTopRightX = coords.vertexBottomRightX = left+(width*right_field_value);
|
||||
|
||||
// top
|
||||
coords.textureTopLeftY = coords.textureTopRightY = texTop+(top_field_value*texHeight);
|
||||
coords.vertexTopLeftY = coords.vertexTopRightY = top+(height*top_field_value);
|
||||
|
||||
// bottom
|
||||
coords.textureBottomLeftY = coords.textureBottomRightY = texTop+(bottom_field_value*texHeight);
|
||||
coords.vertexBottomLeftY = coords.vertexBottomRightY = top+(height*bottom_field_value);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef CROPEFFECT_H
|
||||
#define CROPEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class CropEffect : public Effect
|
||||
{
|
||||
public:
|
||||
CropEffect(Clip* c);
|
||||
void process_coords(double timecode, GLTextureCoords &coords);
|
||||
private:
|
||||
EffectField* left_field;
|
||||
EffectField* top_field;
|
||||
EffectField* right_field;
|
||||
EffectField* bottom_field;
|
||||
};
|
||||
|
||||
#endif // CROPEFFECT_H
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "flipeffect.h"
|
||||
|
||||
FlipEffect::FlipEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_FLIP_EFFECT) {
|
||||
enable_coords = true;
|
||||
|
||||
horizontal_field = add_row("Horizontal:")->add_field(EFFECT_FIELD_BOOL);
|
||||
vertical_field = add_row("Vertical:")->add_field(EFFECT_FIELD_BOOL);
|
||||
|
||||
connect(horizontal_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(vertical_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void FlipEffect::process_coords(double timecode, GLTextureCoords &coords) {
|
||||
if (horizontal_field->get_bool_value(timecode)) {
|
||||
double tlX = coords.textureTopLeftX;
|
||||
double blX = coords.textureBottomLeftX;
|
||||
double trX = coords.textureTopRightX;
|
||||
double brX = coords.textureBottomRightX;
|
||||
coords.textureTopLeftX = trX;
|
||||
coords.textureTopRightX = tlX;
|
||||
coords.textureBottomLeftX = brX;
|
||||
coords.textureBottomRightX = blX;
|
||||
}
|
||||
if (vertical_field->get_bool_value(timecode)) {
|
||||
double tlY = coords.textureTopLeftY;
|
||||
double blY = coords.textureBottomLeftY;
|
||||
double trY = coords.textureTopRightY;
|
||||
double brY = coords.textureBottomRightY;
|
||||
coords.textureTopLeftY = blY;
|
||||
coords.textureTopRightY = brY;
|
||||
coords.textureBottomLeftY = tlY;
|
||||
coords.textureBottomRightY = trY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef FLIPEFFECT_H
|
||||
#define FLIPEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class FlipEffect : public Effect
|
||||
{
|
||||
public:
|
||||
FlipEffect(Clip* c);
|
||||
void process_coords(double timecode, GLTextureCoords &coords);
|
||||
private:
|
||||
EffectField* horizontal_field;
|
||||
EffectField* vertical_field;
|
||||
};
|
||||
|
||||
#endif // FLIPEFFECT_H
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "gaussianblureffect.h"
|
||||
|
||||
#include "project/clip.h"
|
||||
|
||||
#include <QImage>
|
||||
|
||||
GaussianBlurEffect::GaussianBlurEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_GAUSSIANBLUR_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
radius_val = add_row("Radius:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
sigma_val = add_row("Sigma:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
horiz_val = add_row("Horizontal:")->add_field(EFFECT_FIELD_BOOL);
|
||||
vert_val = add_row("Vertical:")->add_field(EFFECT_FIELD_BOOL);
|
||||
|
||||
radius_val->set_double_minimum_value(0);
|
||||
sigma_val->set_double_minimum_value(0);
|
||||
|
||||
radius_val->set_double_default_value(9);
|
||||
sigma_val->set_double_default_value(5.5);
|
||||
|
||||
horiz_val->set_bool_value(true);
|
||||
vert_val->set_bool_value(true);
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/gaussianblureffect.frag";
|
||||
|
||||
connect(radius_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(sigma_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(horiz_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(vert_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void GaussianBlurEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("resolution", parent_clip->getWidth(), parent_clip->getHeight());
|
||||
glslProgram->setUniformValue("radius", (GLfloat) radius_val->get_double_value(timecode));
|
||||
glslProgram->setUniformValue("sigma", (GLfloat) sigma_val->get_double_value(timecode));
|
||||
glslProgram->setUniformValue("horiz_blur", horiz_val->get_bool_value(timecode));
|
||||
glslProgram->setUniformValue("vert_blur", vert_val->get_bool_value(timecode));
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef GAUSSIANBLUREFFECT_H
|
||||
#define GAUSSIANBLUREFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class GaussianBlurEffect : public Effect
|
||||
{
|
||||
public:
|
||||
GaussianBlurEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* radius_val;
|
||||
EffectField* sigma_val;
|
||||
EffectField* horiz_val;
|
||||
EffectField* vert_val;
|
||||
};
|
||||
|
||||
#endif // GAUSSIANBLUREFFECT_H
|
||||
@@ -1,16 +0,0 @@
|
||||
#version 110
|
||||
|
||||
uniform vec4 keyColor;
|
||||
uniform float threshold;
|
||||
uniform sampler2D myTexture;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 textureColor = texture2D(myTexture, vTexCoord);
|
||||
float diff = length(keyColor - textureColor);
|
||||
if (diff < threshold) {
|
||||
gl_FragColor = vec4(0, 0, 0, 0);
|
||||
} else {
|
||||
gl_FragColor = textureColor;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/shaders">
|
||||
<file>gaussianblureffect.frag</file>
|
||||
<file>common.vert</file>
|
||||
<file>inverteffect.frag</file>
|
||||
<file>chromakeyeffect.frag</file>
|
||||
<file>solideffect.frag</file>
|
||||
<file>boxblureffect.frag</file>
|
||||
<file>waveeffect.frag</file>
|
||||
<file>temperatureeffect.frag</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,16 +0,0 @@
|
||||
#version 110
|
||||
|
||||
uniform vec4 solidColor;
|
||||
uniform float amount_val;
|
||||
uniform sampler2D myTexture;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 textureColor = texture2D(myTexture, vTexCoord);
|
||||
gl_FragColor = vec4(
|
||||
textureColor.r+((solidColor.r-textureColor.r)*amount_val),
|
||||
textureColor.g+((solidColor.g-textureColor.g)*amount_val),
|
||||
textureColor.b+((solidColor.b-textureColor.b)*amount_val),
|
||||
textureColor.a
|
||||
);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#version 110
|
||||
|
||||
// adapted from Tanner Helland's temperature algorithm
|
||||
// (http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/)
|
||||
|
||||
uniform float temperature;
|
||||
|
||||
uniform mediump float amount_val;
|
||||
uniform sampler2D myTexture;
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 textureColor = texture2D(myTexture, vTexCoord);
|
||||
|
||||
// red value
|
||||
float red = (temperature <= 66.0) ? 1.0 : min(1.0, max(0.0,
|
||||
(1.2929361861 * pow(temperature - 60.0, -0.1332047592))
|
||||
));
|
||||
|
||||
// green value
|
||||
float green = min(1.0, max(0.0,
|
||||
(temperature <= 66.0) ? (0.3900815788 * log(temperature) - 0.6318414438) : (1.1298908609 * pow(temperature - 60.0, -0.0755148492))
|
||||
));
|
||||
|
||||
// blue value
|
||||
float blue = (temperature >= 66.0) ? 1.0 : min(1.0, max(0.0,
|
||||
(0.5432067891 * log(temperature - 10.0) - 1.1962540891)
|
||||
));
|
||||
|
||||
gl_FragColor = vec4(
|
||||
textureColor.r*red,
|
||||
textureColor.g*green,
|
||||
textureColor.b*blue,
|
||||
textureColor.a
|
||||
);
|
||||
/*gl_FragColor = vec4(
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
1.0
|
||||
);*/
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#include "inverteffect.h"
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
EffectRow* amount_row = add_row("Amount:");
|
||||
amount_val = amount_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
amount_val->set_double_default_value(100);
|
||||
|
||||
connect(amount_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/inverteffect.frag";
|
||||
}
|
||||
|
||||
void InvertEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("amount_val", (GLfloat) (amount_val->get_double_value(timecode)*0.01));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef INVERTEFFECT_H
|
||||
#define INVERTEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class InvertEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvertEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* amount_val;
|
||||
};
|
||||
|
||||
#endif // INVERTEFFECT_H
|
||||
@@ -1,107 +0,0 @@
|
||||
#include "shakeeffect.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QtMath>
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFFECT), inside(false) {
|
||||
enable_coords = true;
|
||||
|
||||
EffectRow* intensity_row = add_row("Intensity:");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
intensity_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* rotation_row = add_row("Rotation:");
|
||||
rotation_val = rotation_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
rotation_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* frequency_row = add_row("Frequency:");
|
||||
frequency_val = frequency_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
frequency_val->set_double_minimum_value(0);
|
||||
|
||||
// set defaults
|
||||
intensity_val->set_double_default_value(50);
|
||||
rotation_val->set_double_default_value(0);
|
||||
frequency_val->set_double_default_value(10);
|
||||
|
||||
refresh();
|
||||
|
||||
connect(intensity_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(rotation_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(frequency_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
}
|
||||
|
||||
void ShakeEffect::refresh() {
|
||||
if (parent_clip->sequence != NULL) {
|
||||
shake_limit = qRound(parent_clip->sequence->frame_rate / frequency_val->get_double_value(-1));
|
||||
shake_progress = shake_limit;
|
||||
next_x = 0;
|
||||
next_y = 0;
|
||||
next_rot = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ShakeEffect::process_coords(double timecode, GLTextureCoords&) {
|
||||
if (shake_progress > shake_limit) {
|
||||
double ival = intensity_val->get_double_value(timecode);
|
||||
if ((int)ival > 0) {
|
||||
prev_x = next_x;
|
||||
prev_y = next_y;
|
||||
next_x = (qrand() % (int) (ival * 2)) - ival;
|
||||
next_y = (qrand() % (int) (ival * 2)) - ival;
|
||||
|
||||
// find perpendicular slope that passes through (mid_x, mid_y)
|
||||
int mid_x = (prev_x + next_x) / 2;
|
||||
int mid_y = (prev_y + next_y) / 2;
|
||||
int slope_num = (next_y-prev_y);
|
||||
if (slope_num > 0) {
|
||||
int slope_den = (next_x-prev_x);
|
||||
int add = (next_x-prev_x)/4;
|
||||
if (inside) add = -add;
|
||||
inside = !inside;
|
||||
perp_x = mid_x + add;
|
||||
perp_y = (-(slope_den / slope_num)) * perp_x + mid_y;
|
||||
} else {
|
||||
perp_x = mid_x;
|
||||
perp_y = mid_y;
|
||||
}
|
||||
} else {
|
||||
prev_x = 0;
|
||||
prev_y = 0;
|
||||
next_x = 0;
|
||||
next_y = 0;
|
||||
offset_x = 0;
|
||||
offset_y = 0;
|
||||
}
|
||||
double rot_val = rotation_val->get_double_value(timecode);
|
||||
if ((int)rot_val > 0) {
|
||||
prev_rot = next_rot;
|
||||
next_rot = (qrand() % (int) (rot_val * 2)) - rot_val;
|
||||
} else {
|
||||
prev_rot = 0;
|
||||
next_rot = 0;
|
||||
offset_rot = 0;
|
||||
}
|
||||
shake_progress = 0;
|
||||
}
|
||||
|
||||
t = (double) shake_progress / (double) shake_limit;
|
||||
|
||||
double oneminust = 1 - t;
|
||||
|
||||
offset_x = (qPow(oneminust, 2)*prev_x) + (2*oneminust*t*perp_x) + (qPow(t, 2) * next_x);
|
||||
offset_y = (qPow(oneminust, 2)*prev_y) + (2*oneminust*t*perp_y) + (qPow(t, 2) * next_y);
|
||||
|
||||
offset_rot = lerp(prev_rot, next_rot, t);
|
||||
|
||||
glTranslatef(offset_x, offset_y, 0);
|
||||
glRotatef(offset_rot, 0, 0, 1);
|
||||
shake_progress++;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
#ifndef SHAKEEFFECT_H
|
||||
#define SHAKEEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class ShakeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShakeEffect(Clip* c);
|
||||
void process_coords(double timecode, GLTextureCoords& coords);
|
||||
|
||||
EffectField* intensity_val;
|
||||
EffectField* rotation_val;
|
||||
EffectField* frequency_val;
|
||||
public slots:
|
||||
void refresh();
|
||||
private:
|
||||
int shake_progress;
|
||||
int shake_limit;
|
||||
int next_x;
|
||||
int next_y;
|
||||
int next_rot;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int offset_rot;
|
||||
int prev_x;
|
||||
int prev_y;
|
||||
int prev_rot;
|
||||
int perp_x;
|
||||
int perp_y;
|
||||
double t;
|
||||
bool inside;
|
||||
};
|
||||
|
||||
#endif // SHAKEEFFECT_H
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "temperatureeffect.h"
|
||||
|
||||
TemperatureEffect::TemperatureEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TEMPERATURE_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
temp_val = add_row("Temperature:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
temp_val->set_double_minimum_value(2000);
|
||||
temp_val->set_double_default_value(5200);
|
||||
temp_val->set_double_maximum_value(40000);
|
||||
|
||||
connect(temp_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/temperatureeffect.frag";
|
||||
}
|
||||
|
||||
void TemperatureEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("temperature", (GLfloat) (temp_val->get_double_value(timecode)*0.01));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef TEMPERATUREEFFECT_H
|
||||
#define TEMPERATUREEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class TemperatureEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TemperatureEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* temp_val;
|
||||
};
|
||||
|
||||
#endif // TEMPERATUREEFFECT_H
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "waveeffect.h"
|
||||
|
||||
WaveEffect::WaveEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_WAVE_EFFECT) {
|
||||
enable_shader = true;
|
||||
|
||||
frequency_val = add_row("Frequency:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
frequency_val->set_double_minimum_value(0);
|
||||
frequency_val->set_double_default_value(10);
|
||||
|
||||
intensity_val = add_row("Intensity:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
intensity_val->set_double_default_value(10);
|
||||
|
||||
evolution_val = add_row("Evolution:")->add_field(EFFECT_FIELD_DOUBLE);
|
||||
evolution_val->set_double_default_value(0);
|
||||
|
||||
vertical_val = add_row("Vertical:")->add_field(EFFECT_FIELD_BOOL);
|
||||
vertical_val->set_bool_value(false);
|
||||
|
||||
connect(frequency_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(intensity_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(evolution_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(vertical_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
vertPath = ":/shaders/common.vert";
|
||||
fragPath = ":/shaders/waveeffect.frag";
|
||||
}
|
||||
|
||||
void WaveEffect::process_shader(double timecode) {
|
||||
glslProgram->setUniformValue("frequency", (GLfloat) (frequency_val->get_double_value(timecode)));
|
||||
glslProgram->setUniformValue("intensity", (GLfloat) (intensity_val->get_double_value(timecode)*0.01));
|
||||
glslProgram->setUniformValue("evolution", (GLfloat) (evolution_val->get_double_value(timecode)*0.01));
|
||||
glslProgram->setUniformValue("vertical", vertical_val->get_bool_value(timecode));
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef WAVEEFFECT_H
|
||||
#define WAVEEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class WaveEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WaveEffect(Clip* c);
|
||||
void process_shader(double timecode);
|
||||
private:
|
||||
EffectField* frequency_val;
|
||||
EffectField* intensity_val;
|
||||
EffectField* evolution_val;
|
||||
|
||||
EffectField* vertical_val;
|
||||
};
|
||||
|
||||
#endif // WAVEEFFECT_H
|
||||
+1
-1
@@ -110,7 +110,7 @@ void Config::save(QString path) {
|
||||
stream.writeStartDocument(); // doc
|
||||
stream.writeStartElement("Configuration"); // configuration
|
||||
|
||||
stream.writeTextElement("Version", SAVE_VERSION);
|
||||
stream.writeTextElement("Version", QString::number(SAVE_VERSION));
|
||||
stream.writeTextElement("SavedLayout", QString::number(saved_layout));
|
||||
stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines));
|
||||
stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms));
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
#define SAVE_VERSION "181030" // YYMMDD
|
||||
#define SAVE_VERSION 181030 // YYMMDD
|
||||
#define MIN_SAVE_VERSION 180820 // lowest compatible project version
|
||||
|
||||
#define TIMECODE_DROP 0
|
||||
#define TIMECODE_NONDROP 1
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui multimedia opengl
|
||||
QT += core gui multimedia opengl qml
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
@@ -59,7 +59,6 @@ SOURCES += \
|
||||
ui/comboboxex.cpp \
|
||||
ui/colorbutton.cpp \
|
||||
dialogs/replaceclipmediadialog.cpp \
|
||||
effects/linearfadetransition.cpp \
|
||||
ui/fontcombobox.cpp \
|
||||
ui/checkboxex.cpp \
|
||||
effects/effect.cpp \
|
||||
@@ -72,7 +71,16 @@ SOURCES += \
|
||||
io/crc32.cpp \
|
||||
dialogs/loaddialog.cpp \
|
||||
debug.cpp \
|
||||
io/path.cpp
|
||||
io/path.cpp \
|
||||
effects/qpainterwrapper.cpp \
|
||||
effects/internal/linearfadetransition.cpp \
|
||||
effects/internal/transformeffect.cpp \
|
||||
effects/internal/solideffect.cpp \
|
||||
effects/internal/texteffect.cpp \
|
||||
effects/internal/audionoiseeffect.cpp \
|
||||
effects/internal/paneffect.cpp \
|
||||
effects/internal/toneeffect.cpp \
|
||||
effects/internal/volumeeffect.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -123,7 +131,15 @@ HEADERS += \
|
||||
io/crc32.h \
|
||||
dialogs/loaddialog.h \
|
||||
debug.h \
|
||||
io/path.h
|
||||
io/path.h \
|
||||
effects/qpainterwrapper.h \
|
||||
effects/internal/transformeffect.h \
|
||||
effects/internal/solideffect.h \
|
||||
effects/internal/texteffect.h \
|
||||
effects/internal/audionoiseeffect.h \
|
||||
effects/internal/paneffect.h \
|
||||
effects/internal/toneeffect.h \
|
||||
effects/internal/volumeeffect.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
@@ -153,5 +169,4 @@ linux {
|
||||
}
|
||||
|
||||
RESOURCES += \
|
||||
icons/icons.qrc \
|
||||
effects/video/glsl/shaders.qrc
|
||||
icons/icons.qrc
|
||||
|
||||
+28
-10
@@ -137,12 +137,15 @@ void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) effects_menu.addAction(action);
|
||||
if (!added) effects_menu.addAction(action);
|
||||
}
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
|
||||
|
||||
effects_menu.exec(QCursor::pos());*/
|
||||
|
||||
effects_loaded.lock();
|
||||
|
||||
video_menu = video;
|
||||
transition_menu = transitions;
|
||||
|
||||
@@ -152,9 +155,8 @@ void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
const EffectMeta& em = effect_list.at(i);
|
||||
QAction* action = new QAction(&effects_menu);
|
||||
action->setText(em.name);
|
||||
action->setData(reinterpret_cast<quintptr>(&em));
|
||||
action->setData(reinterpret_cast<quintptr>(&em));
|
||||
|
||||
// TODO alphabetical ordering
|
||||
QMenu* parent = &effects_menu;
|
||||
if (!em.category.isEmpty()) {
|
||||
bool found = false;
|
||||
@@ -171,17 +173,33 @@ void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
if (!found) {
|
||||
parent = new QMenu(&effects_menu);
|
||||
parent->setTitle(em.category);
|
||||
effects_menu.addMenu(parent);
|
||||
|
||||
bool found = false;
|
||||
for (int i=0;i<effects_menu.actions().size();i++) {
|
||||
QAction* comp_action = effects_menu.actions().at(i);
|
||||
if (comp_action->text() > em.category) {
|
||||
effects_menu.insertMenu(comp_action, parent);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) effects_menu.addMenu(parent);
|
||||
}
|
||||
}
|
||||
|
||||
parent->addAction(action);
|
||||
}
|
||||
bool found = false;
|
||||
for (int i=0;i<parent->actions().size();i++) {
|
||||
QAction* comp_action = parent->actions().at(i);
|
||||
if (comp_action->text() > action->text()) {
|
||||
parent->insertAction(comp_action, action);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) parent->addAction(action);
|
||||
}
|
||||
|
||||
QMenu test_menu(this);
|
||||
test_menu.setTitle("HEY NOW");
|
||||
test_menu.addAction("YOU'RE AN ALL STAR");
|
||||
effects_menu.addMenu(&test_menu);
|
||||
effects_loaded.unlock();
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
|
||||
effects_menu.exec(QCursor::pos());
|
||||
|
||||
+59
-9
@@ -726,7 +726,8 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
stream.readNextStartElement();
|
||||
if (stream.name() == root_search) {
|
||||
if (type == LOAD_TYPE_VERSION) {
|
||||
if (stream.readElementText() != SAVE_VERSION) {
|
||||
int proj_version = stream.readElementText().toInt();
|
||||
if (proj_version < MIN_SAVE_VERSION && proj_version > SAVE_VERSION) {
|
||||
if (QMessageBox::warning(this, "Version Mismatch", "This project was saved in a different version of Olive and may not be fully compatible with this version. Would you like to attempt loading it anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
||||
show_err = false;
|
||||
return false;
|
||||
@@ -760,7 +761,6 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
QTreeWidgetItem* item = new_item();
|
||||
Media* m = new Media();
|
||||
|
||||
// TODO make save/load-able
|
||||
m->using_inout = false;
|
||||
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
@@ -775,7 +775,13 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
m->url = attr.value().toString();
|
||||
} else if (attr.name() == "duration") {
|
||||
m->length = attr.value().toLongLong();
|
||||
}
|
||||
} else if (attr.name() == "using_inout") {
|
||||
m->using_inout = (attr.value() == "1");
|
||||
} else if (attr.name() == "in") {
|
||||
m->in = attr.value().toLong();
|
||||
} else if (attr.name() == "out") {
|
||||
m->out = attr.value().toLong();
|
||||
}
|
||||
}
|
||||
|
||||
set_footage_of_tree(item, m);
|
||||
@@ -937,6 +943,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
stream.readNext();
|
||||
if (stream.name() == "effect" && stream.isStartElement()) {
|
||||
int effect_id = -1;
|
||||
QString effect_name;
|
||||
bool effect_enabled = true;
|
||||
for (int j=0;j<stream.attributes().size();j++) {
|
||||
const QXmlStreamAttribute& attr = stream.attributes().at(j);
|
||||
@@ -944,15 +951,55 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
|
||||
effect_id = attr.value().toInt();
|
||||
} else if (attr.name() == "enabled") {
|
||||
effect_enabled = (attr.value() == "1");
|
||||
} else if (attr.name() == "name") {
|
||||
effect_name = attr.value().toString();
|
||||
}
|
||||
}
|
||||
if (effect_id != -1) {
|
||||
Effect* e = create_effect(effect_id, c);
|
||||
|
||||
// backwards compatibility with 180820
|
||||
if (effect_id != -1) {
|
||||
switch (effect_id) {
|
||||
case 0: effect_name = (c->track < 0) ? "Transform" : "Volume"; break;
|
||||
case 1: effect_name = (c->track < 0) ? "Shake" : "Pan"; break;
|
||||
case 2: effect_name = (c->track < 0) ? "Text" : "Noise"; break;
|
||||
case 3: effect_name = (c->track < 0) ? "Solid" : "Tone"; break;
|
||||
case 4: effect_name = "Invert"; break;
|
||||
case 5: effect_name = "Chroma Key"; break;
|
||||
case 6: effect_name = "Gaussian Blur"; break;
|
||||
case 7: effect_name = "Crop"; break;
|
||||
case 8: effect_name = "Flip"; break;
|
||||
case 9: effect_name = "Box Blur"; break;
|
||||
case 10: effect_name = "Wave"; break;
|
||||
case 11: effect_name = "Temperature"; break;
|
||||
}
|
||||
}
|
||||
|
||||
// wait for effects to be loaded
|
||||
effects_loaded.lock();
|
||||
|
||||
const EffectMeta* meta = NULL;
|
||||
|
||||
// find effect with this name
|
||||
if (!effect_name.isEmpty()) {
|
||||
QVector<EffectMeta>& effect_list = (c->track < 0) ? video_effects : audio_effects;
|
||||
for (int j=0;j<effect_list.size();j++) {
|
||||
if (effect_list.at(j).name == effect_name) {
|
||||
meta = &effect_list.at(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (meta == NULL) {
|
||||
dout << "[WARNING] An effect used by this project is missing. It was not loaded.";
|
||||
} else {
|
||||
Effect* e = create_effect(c, meta);
|
||||
e->set_enabled(effect_enabled);
|
||||
// stream.writeAttribute("enabled", QString::number(e->is_enabled()));
|
||||
e->load(stream);
|
||||
c->effects.append(e);
|
||||
c->effects.append(e);
|
||||
}
|
||||
|
||||
effects_loaded.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1125,6 +1172,9 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
stream.writeAttribute("name", m->name);
|
||||
stream.writeAttribute("url", m->url);
|
||||
stream.writeAttribute("duration", QString::number(m->length));
|
||||
stream.writeAttribute("using_inout", QString::number(m->using_inout));
|
||||
stream.writeAttribute("in", QString::number(m->in));
|
||||
stream.writeAttribute("out", QString::number(m->out));
|
||||
for (int j=0;j<m->video_tracks.size();j++) {
|
||||
MediaStream* ms = m->video_tracks.at(j);
|
||||
stream.writeStartElement("video");
|
||||
@@ -1216,7 +1266,7 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int
|
||||
for (int k=0;k<c->effects.size();k++) {
|
||||
stream.writeStartElement("effect"); // effect
|
||||
Effect* e = c->effects.at(k);
|
||||
stream.writeAttribute("id", QString::number(e->id));
|
||||
stream.writeAttribute("name", e->meta->name);
|
||||
stream.writeAttribute("enabled", QString::number(e->is_enabled()));
|
||||
e->save(stream);
|
||||
stream.writeEndElement(); // effect
|
||||
@@ -1260,7 +1310,7 @@ void Project::save_project(bool autorecovery) {
|
||||
|
||||
stream.writeStartElement("project"); // project
|
||||
|
||||
stream.writeTextElement("version", SAVE_VERSION);
|
||||
stream.writeTextElement("version", QString::number(SAVE_VERSION));
|
||||
|
||||
save_folder(stream, NULL, MEDIA_TYPE_FOLDER, true);
|
||||
|
||||
|
||||
+3
-1
@@ -27,6 +27,8 @@
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QJSEngine>
|
||||
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) {
|
||||
if (source_frame_rate == target_frame_rate) return framenumber;
|
||||
return qFloor(((double)framenumber/source_frame_rate)*target_frame_rate);
|
||||
@@ -385,7 +387,7 @@ bool Timeline::is_clip_selected(Clip* clip, bool containing) {
|
||||
}
|
||||
|
||||
void Timeline::on_snappingButton_toggled(bool checked) {
|
||||
snapping = checked;
|
||||
//snapping = checked;
|
||||
}
|
||||
|
||||
Clip* Timeline::split_clip(ComboAction* ca, int p, long frame) {
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ void AddEffectCommand::undo() {
|
||||
|
||||
void AddEffectCommand::redo() {
|
||||
if (ref == NULL) {
|
||||
ref = new Effect(clip, *meta);
|
||||
ref = create_effect(clip, meta);
|
||||
}
|
||||
clip->effects.append(ref);
|
||||
done = true;
|
||||
|
||||
+22
-22
@@ -19,7 +19,9 @@
|
||||
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
#include "effects/video/solideffect.h"
|
||||
|
||||
#include "effects/internal/solideffect.h"
|
||||
#include "effects/internal/texteffect.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QColor>
|
||||
@@ -623,14 +625,14 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
/*if (c->track < 0) {
|
||||
if (c->track < 0) {
|
||||
// add default video effects
|
||||
c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_TRANSFORM)));
|
||||
} else {
|
||||
// add default audio effects
|
||||
c->effects.append(create_effect(AUDIO_VOLUME_EFFECT, c));
|
||||
c->effects.append(create_effect(AUDIO_PAN_EFFECT, c));
|
||||
}*/
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_VOLUME)));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_PAN)));
|
||||
}
|
||||
}
|
||||
|
||||
panel_timeline->ghosts.clear();
|
||||
@@ -874,41 +876,41 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
|
||||
|
||||
if (c->track < 0) {
|
||||
// default video effects (before custom effects)
|
||||
c->effects.append(create_effect(VIDEO_TRANSFORM_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_TRANSFORM)));
|
||||
c->media_type = MEDIA_TYPE_SOLID;
|
||||
}
|
||||
|
||||
switch (panel_timeline->creating_object) {
|
||||
case ADD_OBJ_TITLE:
|
||||
c->name = "Title";
|
||||
c->effects.append(create_effect(VIDEO_TEXT_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_TEXT)));
|
||||
break;
|
||||
case ADD_OBJ_SOLID:
|
||||
c->name = "Solid Color";
|
||||
c->effects.append(create_effect(VIDEO_SOLID_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_SOLID)));
|
||||
break;
|
||||
case ADD_OBJ_BARS:
|
||||
{
|
||||
c->name = "Bars";
|
||||
Effect* e = create_effect(VIDEO_SOLID_EFFECT, c);
|
||||
Effect* e = create_effect(c, get_internal_meta(EFFECT_INTERNAL_SOLID));
|
||||
e->row(0)->field(0)->set_combo_index(1);
|
||||
c->effects.append(e);
|
||||
c->effects.append(e);
|
||||
}
|
||||
break;
|
||||
case ADD_OBJ_TONE:
|
||||
c->name = "Tone";
|
||||
c->effects.append(create_effect(AUDIO_TONE_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_TONE)));
|
||||
break;
|
||||
case ADD_OBJ_NOISE:
|
||||
c->name = "Noise";
|
||||
c->effects.append(create_effect(AUDIO_NOISE_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_NOISE)));
|
||||
break;
|
||||
}
|
||||
|
||||
if (c->track >= 0) {
|
||||
// default audio effects (after custom effects)
|
||||
c->effects.append(create_effect(AUDIO_VOLUME_EFFECT, c));
|
||||
c->effects.append(create_effect(AUDIO_PAN_EFFECT, c));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_VOLUME)));
|
||||
c->effects.append(create_effect(c, get_internal_meta(EFFECT_INTERNAL_PAN)));
|
||||
c->media_type = MEDIA_TYPE_TONE;
|
||||
}
|
||||
|
||||
@@ -2333,14 +2335,12 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
}
|
||||
|
||||
// Draw edit cursor
|
||||
if (isLiveEditing()) {
|
||||
if (is_track_visible(panel_timeline->cursor_track)) {
|
||||
int cursor_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->cursor_frame);
|
||||
int cursor_y = getScreenPointFromTrack(panel_timeline->cursor_track);
|
||||
if (isLiveEditing() && is_track_visible(panel_timeline->cursor_track)) {
|
||||
int cursor_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->cursor_frame);
|
||||
int cursor_y = getScreenPointFromTrack(panel_timeline->cursor_track);
|
||||
|
||||
p.setPen(Qt::gray);
|
||||
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->calculate_track_height(panel_timeline->cursor_track, -1));
|
||||
}
|
||||
p.setPen(Qt::gray);
|
||||
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->calculate_track_height(panel_timeline->cursor_track, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -366,7 +366,9 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
if (e->enable_shader || e->enable_superimpose) {
|
||||
e->startEffect();
|
||||
//for (int k=0;k<e->getIterations();k++) {
|
||||
e->process_shader(timecode);
|
||||
if (e->enable_shader) {
|
||||
e->process_shader(timecode);
|
||||
}
|
||||
composite_texture = draw_clip(c->fbo[fbo_switcher], composite_texture);
|
||||
if (e->enable_superimpose) {
|
||||
GLuint superimpose_texture = e->process_superimpose(timecode);
|
||||
|
||||
Reference in New Issue
Block a user