#include "effect.h" #include "panels/panels.h" #include "panels/viewer.h" #include "ui/viewerwidget.h" #include "ui/collapsiblewidget.h" #include "panels/project.h" #include "project/undo.h" #include "ui/labelslider.h" #include "ui/colorbutton.h" #include "ui/comboboxex.h" #include "ui/fontcombobox.h" #include "ui/checkboxex.h" #include "project/clip.h" #include "panels/timeline.h" #include "panels/effectcontrols.h" #include "effects/video/transformeffect.h" #include "effects/video/inverteffect.h" #include "effects/video/shakeeffect.h" #include "effects/video/solideffect.h" #include "effects/video/texteffect.h" #include "effects/audio/paneffect.h" #include "effects/audio/volumeeffect.h" #include #include #include #include #include QVector video_effect_names; QVector 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"; audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume"; audio_effect_names[AUDIO_PAN_EFFECT] = "Pan"; } Effect* create_effect(int effect_id, Clip* c) { if (c->track < 0) { switch (effect_id) { case VIDEO_TRANSFORM_EFFECT: return new TransformEffect(c); break; case VIDEO_SHAKE_EFFECT: return new ShakeEffect(c); break; case VIDEO_TEXT_EFFECT: return new TextEffect(c); break; case VIDEO_SOLID_EFFECT: return new SolidEffect(c); break; case VIDEO_INVERT_EFFECT: return new InvertEffect(c); break; } } else { switch (effect_id) { case AUDIO_VOLUME_EFFECT: return new VolumeEffect(c); break; case AUDIO_PAN_EFFECT: return new PanEffect(c); break; } } qDebug() << "[ERROR] Invalid effect ID"; return NULL; } double double_lerp(double a, double b, double t) { return ((1.0 - t) * a) + (t * b); } Effect::Effect(Clip* c, int t, int i) : parent_clip(c), type(t), id(i), enable_image(false), enable_opengl(false) { container = new CollapsibleWidget(); if (type == EFFECT_TYPE_VIDEO) { container->setText(video_effect_names[i]); } else if (type == EFFECT_TYPE_AUDIO) { container->setText(audio_effect_names[i]); } connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); ui = new QWidget(); ui_layout = new QGridLayout(); ui->setLayout(ui_layout); container->setContents(ui); } Effect::~Effect() { for (int i=0;irows.at(i)->keyframing = rows.at(i)->keyframing; e->rows.at(i)->keyframe_times = rows.at(i)->keyframe_times; e->rows.at(i)->keyframe_types = rows.at(i)->keyframe_types; for (int j=0;jfieldCount();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; } } } EffectRow* Effect::add_row(const QString& name) { EffectRow* row = new EffectRow(this, ui_layout, name, rows.size()); rows.append(row); return row; } EffectRow* Effect::row(int i) { return rows.at(i); } int Effect::row_count() { return rows.size(); } void Effect::refresh() {} void Effect::field_changed() { panel_viewer->viewer_widget->update(); } bool Effect::is_enabled() { return container->enabled_check->isChecked(); } void Effect::load(QXmlStreamReader& stream) { stream.readNext(); /*for (int i=0;iatEnd() && !(stream->name() == "effect" && stream->isEndElement())) { stream->readNext(); if (stream->name() == "row" && stream->isStartElement()) { for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); while (!stream->atEnd() && !(stream->name() == "effect" && stream->isEndElement())) { stream->readNext(); if (stream->name() == "field" && stream->isStartElement()) { // read all keyframes QVector keys; while (!stream->atEnd() && (stream->name() == "field" && stream->isEndElement())) { stream->readNext(); if (stream->name() == "key" && stream->isStartElement()) { EffectKeyframe kf; for (int k=0;kattributes().size();k++) { const QXmlStreamAttribute& attr = stream->attributes().at(k); if (attr.name() == "frame") { kf.frame = attr.value().toLong(); } else if (attr.name() == "type") { kf.type = attr.value().toInt(); } else if (attr.name() == "value") { switch (field->type) { case EFFECT_FIELD_DOUBLE: kf.data = attr.value().toDouble(); break; case EFFECT_FIELD_COLOR: { kf.data = QColor(attr.value().toString()); } break; case EFFECT_FIELD_STRING: kf.data = attr.value().toString(); break; case EFFECT_FIELD_BOOL: kf.data = (stream->text() == "1"); break; case EFFECT_FIELD_COMBO: kf.data = (stream->text().toInt()); break; case EFFECT_FIELD_FONT: kf.data = (stream->text().toString()); break; } } } keys.append(kf); } } field->keyframes = keys; break; } } } break; } } }*/ } QString save_data(int type, const QVariant& data) { switch (type) { case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); break; case EFFECT_FIELD_COLOR: return data.value().name(); break; case EFFECT_FIELD_STRING: return data.toString(); break; case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); break; case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); break; case EFFECT_FIELD_FONT: return data.toString(); break; } return QString(); } void Effect::save(QXmlStreamWriter& stream) { for (int i=0;ikeyframing)); for (int j=0;jkeyframe_times.size();j++) { stream.writeStartElement("key"); // key stream.writeAttribute("frame", QString::number(row->keyframe_times.at(j))); stream.writeAttribute("type", QString::number(row->keyframe_types.at(j))); stream.writeEndElement(); // key } stream.writeEndElement(); // keyframes for (int j=0;jfieldCount();j++) { EffectField* field = row->field(j); stream.writeStartElement("field"); // field stream.writeAttribute("value", save_data(field->type, field->get_current_data())); for (int k=0;kkeyframe_data.size();k++) { stream.writeTextElement("key", save_data(field->type, field->keyframe_data.at(k))); } stream.writeEndElement(); // field } stream.writeEndElement(); // row } } Effect* Effect::copy(Clip* c) { Effect* copy = create_effect(id, c); copy_field_keyframes(copy); return copy; } void Effect::process_image(long, QImage&) {} void Effect::process_gl(long, QOpenGLShaderProgram&, int*, int*) {} void Effect::process_audio(uint8_t*, int) {} /* Effect Row Definitions */ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, int row) : parent_effect(parent), keyframing(false), ui(uilayout), name(n), ui_row(row) { label = new QLabel(name); ui->addWidget(label, row, 0); // DEBUG STARTS QPushButton* nkf = new QPushButton(); connect(nkf, SIGNAL(clicked(bool)), this, SLOT(set_keyframe_now())); ui->addWidget(nkf, row, 5); // DEBUG ENDS /*keyframe_enable = new CheckboxEx(); keyframe_enable->setToolTip("Enable Keyframes"); ui->addWidget(keyframe_enable, row, 5);*/ } EffectField* EffectRow::add_field(int type, int colspan) { EffectField* field = new EffectField(this, type); fields.append(field); QWidget* element = field->get_ui_element(); ui->addWidget(element, ui_row, fields.size(), 1, colspan); return field; } EffectRow::~EffectRow() { for (int i=0;iplayhead-parent_effect->parent_clip->timeline_in+parent_effect->parent_clip->clip_in); } void EffectRow::set_keyframe(long time) { keyframing = true; int index = -1; for (int i=0;i 0) ? keyframe_types.last() : EFFECT_KEYFRAME_LINEAR); } for (int i=0;iset_keyframe_data(index); } panel_effect_controls->update_keyframes(); } EffectField* EffectRow::field(int i) { return fields.at(i); } int EffectRow::fieldCount() { return fields.size(); } /* Effect Field Definitions */ EffectField::EffectField(EffectRow *parent, int t) : parent_row(parent), type(t) { switch (t) { case EFFECT_FIELD_DOUBLE: { LabelSlider* ls = new LabelSlider(); ui_element = ls; connect(ls, SIGNAL(valueChanged()), this, SLOT(uiElementChange())); } break; case EFFECT_FIELD_COLOR: { ColorButton* cb = new ColorButton(); ui_element = cb; connect(cb, SIGNAL(color_changed()), this, SLOT(uiElementChange())); } break; case EFFECT_FIELD_STRING: { QTextEdit* edit = new QTextEdit(); edit->setUndoRedoEnabled(true); ui_element = edit; connect(edit, SIGNAL(textChanged()), this, SLOT(uiElementChange())); } break; case EFFECT_FIELD_BOOL: { CheckboxEx* cb = new CheckboxEx(); ui_element = cb; connect(cb, SIGNAL(clicked(bool)), this, SLOT(uiElementChange())); connect(cb, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); } break; case EFFECT_FIELD_COMBO: { ComboBoxEx* cb = new ComboBoxEx(); ui_element = cb; connect(cb, SIGNAL(activated(int)), this, SLOT(uiElementChange())); } break; case EFFECT_FIELD_FONT: { FontCombobox* fcb = new FontCombobox(); ui_element = fcb; connect(fcb, SIGNAL(activated(int)), this, SLOT(uiElementChange())); } break; } } QVariant EffectField::get_current_data() { switch (type) { case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->value(); break; case EFFECT_FIELD_COLOR: return static_cast(ui_element)->get_color(); break; case EFFECT_FIELD_STRING: return static_cast(ui_element)->toPlainText(); break; case EFFECT_FIELD_BOOL: return static_cast(ui_element)->isChecked(); break; case EFFECT_FIELD_COMBO: return static_cast(ui_element)->currentIndex(); break; case EFFECT_FIELD_FONT: return static_cast(ui_element)->currentText(); break; } return QVariant(); } void EffectField::set_current_data(const QVariant& data) { switch (type) { case EFFECT_FIELD_DOUBLE: return static_cast(ui_element)->set_value(data.toDouble(), false); break; case EFFECT_FIELD_COLOR: return static_cast(ui_element)->set_color(data.value()); break; case EFFECT_FIELD_STRING: return static_cast(ui_element)->setPlainText(data.toString()); break; case EFFECT_FIELD_BOOL: return static_cast(ui_element)->setChecked(data.toBool()); break; case EFFECT_FIELD_COMBO: return static_cast(ui_element)->setCurrentIndexEx(data.toInt()); break; case EFFECT_FIELD_FONT: return static_cast(ui_element)->setCurrentTextEx(data.toString()); break; } } void EffectField::set_keyframe_data(int i) { if (i == -1) { keyframe_data.append(get_current_data()); } else { keyframe_data[i] = get_current_data(); } } void EffectField::get_keyframe_data(long frame, int* before, int* after, double* progress) { int before_keyframe_index = -1; int after_keyframe_index = -1; long before_keyframe_time = LONG_MIN; long after_keyframe_time = LONG_MAX; for (int i=0;ikeyframe_times.size();i++) { long eval_keyframe_time = parent_row->keyframe_times.at(i); if (eval_keyframe_time == frame) { *before = i; *after = i; return; } else if (eval_keyframe_time < frame && eval_keyframe_time > before_keyframe_time) { before_keyframe_index = i; before_keyframe_time = eval_keyframe_time; } else if (eval_keyframe_time > frame && eval_keyframe_time < after_keyframe_time) { after_keyframe_index = i; after_keyframe_time = eval_keyframe_time; } } if (type == EFFECT_FIELD_DOUBLE || type == EFFECT_FIELD_COLOR) { if (before_keyframe_index > -1 && after_keyframe_index > -1) { // interpolate *before = before_keyframe_index; *after = after_keyframe_index; *progress = (double)(frame-before_keyframe_time)/(double)(after_keyframe_time-before_keyframe_time); // TODO routines for bezier - currently this is purely linear } else if (before_keyframe_index > -1) { *before = before_keyframe_index; *after = before_keyframe_index; } else if (after_keyframe_index > -1) { *before = after_keyframe_index; *after = after_keyframe_index; } } else { *before = before_keyframe_index; *after = before_keyframe_index; } } void EffectField::validate_keyframe_data(long frame) { if (parent_row->keyframing) { int before_keyframe; int after_keyframe; double progress; get_keyframe_data(frame, &before_keyframe, &after_keyframe, &progress); const QVariant& before_data = keyframe_data.at(before_keyframe); switch (type) { case EFFECT_FIELD_DOUBLE: { double value; if (before_keyframe == after_keyframe) { value = keyframe_data.at(before_keyframe).toDouble(); } else { double before_dbl = keyframe_data.at(before_keyframe).toDouble(); double after_dbl = keyframe_data.at(after_keyframe).toDouble(); value = double_lerp(before_dbl, after_dbl, progress); } static_cast(ui_element)->set_value(value, false); } break; case EFFECT_FIELD_COLOR: { QColor value; if (before_keyframe == after_keyframe) { value = keyframe_data.at(before_keyframe).value(); } else { QColor before_data = keyframe_data.at(before_keyframe).value(); QColor after_data = keyframe_data.at(after_keyframe).value(); value = QColor(lerp(before_data.red(), after_data.red(), progress), lerp(before_data.green(), after_data.green(), progress), lerp(before_data.blue(), after_data.blue(), progress)); } return static_cast(ui_element)->set_color(value); } break; case EFFECT_FIELD_STRING: static_cast(ui_element)->setPlainText(before_data.toString()); break; case EFFECT_FIELD_BOOL: static_cast(ui_element)->setChecked(before_data.toBool()); break; case EFFECT_FIELD_COMBO: static_cast(ui_element)->setCurrentIndexEx(before_data.toInt()); break; case EFFECT_FIELD_FONT: static_cast(ui_element)->setCurrentTextEx(before_data.toString()); break; } } } void EffectField::uiElementChange() { if (parent_row->keyframing) { parent_row->set_keyframe_now(); } emit changed(); } QWidget* EffectField::get_ui_element() { return ui_element; } void EffectField::set_enabled(bool e) { ui_element->setEnabled(e); } double EffectField::get_double_value(long p) { validate_keyframe_data(p); return static_cast(ui_element)->value(); } void EffectField::set_double_value(double v) { static_cast(ui_element)->set_value(v, false); } void EffectField::set_double_default_value(double v) { static_cast(ui_element)->set_default_value(v); } void EffectField::set_double_minimum_value(double v) { static_cast(ui_element)->set_minimum_value(v); } void EffectField::set_double_maximum_value(double v) { static_cast(ui_element)->set_maximum_value(v); } void EffectField::add_combo_item(const QString& name, const QVariant& data) { static_cast(ui_element)->addItem(name, data); } int EffectField::get_combo_index(long p) { validate_keyframe_data(p); return static_cast(ui_element)->currentIndex(); } const QVariant EffectField::get_combo_data(long p) { validate_keyframe_data(p); return static_cast(ui_element)->currentData(); } const QString EffectField::get_combo_string(long p) { validate_keyframe_data(p); return static_cast(ui_element)->currentText(); } void EffectField::set_combo_index(int index) { static_cast(ui_element)->setCurrentIndexEx(index); } void EffectField::set_combo_string(const QString& s) { static_cast(ui_element)->setCurrentTextEx(s); } bool EffectField::get_bool_value(long p) { validate_keyframe_data(p); return static_cast(ui_element)->isChecked(); } void EffectField::set_bool_value(bool b) { return static_cast(ui_element)->setChecked(b); } const QString EffectField::get_string_value(long p) { validate_keyframe_data(p); return static_cast(ui_element)->toPlainText(); } void EffectField::set_string_value(const QString& s) { static_cast(ui_element)->setText(s); } const QString EffectField::get_font_name(long p) { validate_keyframe_data(p); return static_cast(ui_element)->currentText(); } void EffectField::set_font_name(const QString& s) { static_cast(ui_element)->setCurrentText(s); } QColor EffectField::get_color_value(long p) { validate_keyframe_data(p); return static_cast(ui_element)->get_color(); } void EffectField::set_color_value(QColor color) { static_cast(ui_element)->set_color(color); }