/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "effectfield.h" #include #include #include "rendering/renderfunctions.h" #include "global/config.h" #include "effects/effectrow.h" #include "effects/effect.h" #include "undo/undo.h" #include "timeline/clip.h" #include "timeline/sequence.h" #include "global/math.h" #include "global/debug.h" EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) : QObject(parent), type_(t), id_(i), enabled_(true), colspan_(1) { // EffectField MUST be created with a parent. Q_ASSERT(parent != nullptr); Q_ASSERT(!i.isEmpty() || t == EFFECT_FIELD_UI); // Add this field to the parent row specified parent->AddField(this); // Set a very base default value SetValueAt(0, 0); // Connect this field to the effect's changed function connect(this, SIGNAL(Changed()), parent->GetParentEffect(), SLOT(FieldChanged())); } EffectRow *EffectField::GetParentRow() { return static_cast(parent()); } int EffectField::GetColumnSpan() { return colspan_; } void EffectField::SetColumnSpan(int i) { Q_ASSERT(i >= 1); colspan_ = i; } QVariant EffectField::ConvertStringToValue(const QString &s) { return s; } QString EffectField::ConvertValueToString(const QVariant &v) { return v.toString(); } void EffectField::UpdateWidgetValue(QWidget *, double) {} QVariant EffectField::GetValueAt(double timecode) { if (HasKeyframes()) { int before_keyframe; int after_keyframe; double progress; GetKeyframeData(timecode, before_keyframe, after_keyframe, progress); const QVariant& before_data = keyframes.at(before_keyframe).data; switch (type_) { case EFFECT_FIELD_DOUBLE: { double value; if (before_keyframe == after_keyframe) { value = keyframes.at(before_keyframe).data.toDouble(); } else { const EffectKeyframe& before_key = keyframes.at(before_keyframe); const EffectKeyframe& after_key = keyframes.at(after_keyframe); double before_dbl = before_key.data.toDouble(); double after_dbl = after_key.data.toDouble(); if (before_key.type == EFFECT_KEYFRAME_HOLD) { // Hold keyframes will always return the previous keyframe with no interpolation value = before_dbl; } else if (before_key.type == EFFECT_KEYFRAME_BEZIER || after_key.type == EFFECT_KEYFRAME_BEZIER) { // bezier interpolation if (before_key.type == EFFECT_KEYFRAME_BEZIER && after_key.type == EFFECT_KEYFRAME_BEZIER) { // cubic bezier double t = cubic_t_from_x(SecondsToFrame(timecode), before_key.time, before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true), after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false), after_key.time); value = cubic_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl+after_key.pre_handle_y, after_dbl, t); } else if (after_key.type == EFFECT_KEYFRAME_LINEAR) { // quadratic bezier // last keyframe is the bezier one double t = quad_t_from_x(SecondsToFrame(timecode), before_key.time, before_key.time+GetValidKeyframeHandlePosition(before_keyframe, true), after_key.time); value = quad_from_t(before_dbl, before_dbl+before_key.post_handle_y, after_dbl, t); } else { // this keyframe is the bezier one double t = quad_t_from_x(SecondsToFrame(timecode), before_key.time, after_key.time+GetValidKeyframeHandlePosition(after_keyframe, false), after_key.time); value = quad_from_t(before_dbl, after_dbl+after_key.pre_handle_y, after_dbl, t); } } else { // Linear interpolation (default) value = double_lerp(before_dbl, after_dbl, progress); } } persistent_data_ = value; break; } case EFFECT_FIELD_COLOR: { QColor value; if (before_keyframe == after_keyframe) { value = keyframes.at(before_keyframe).data.value(); } else { QColor before_data = keyframes.at(before_keyframe).data.value(); QColor after_data = keyframes.at(after_keyframe).data.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)); } persistent_data_ = value; break; } case EFFECT_FIELD_STRING: case EFFECT_FIELD_BOOL: case EFFECT_FIELD_COMBO: case EFFECT_FIELD_FONT: case EFFECT_FIELD_FILE: persistent_data_ = before_data; break; default: break; } } return persistent_data_; } void EffectField::SetValueAt(double time, const QVariant &value) { if (HasKeyframes()) { // Create keyframe here // Convert seconds timecode to frame long frame_timecode = SecondsToFrame(time); // Check array if a keyframe at this time already exists int keyframe_index = -1; for (int i=0;iGetParentEffect()->parent_clip; return playhead_to_clip_seconds(c, c->sequence->playhead); } long EffectField::NowInFrames() { Clip* c = GetParentRow()->GetParentEffect()->parent_clip; return playhead_to_clip_frame(c, c->sequence->playhead); } void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) { if (enabled) { // Create keyframe from perpetual data EffectKeyframe key; key.time = NowInFrames(); key.data = persistent_data_; key.type = EFFECT_KEYFRAME_LINEAR; keyframes.append(key); ca->append(new KeyframeAdd(this, keyframes.size()-1)); } else { // Convert keyframes to one "perpetual" keyframe // Set first keyframe to whatever the data is now ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(Now()))); // Delete all keyframes for (int i=0;iappend(new KeyframeDelete(this, 0)); } } } const EffectField::EffectFieldType &EffectField::type() { return type_; } const QString &EffectField::id() { return id_; } double EffectField::GetValidKeyframeHandlePosition(int key, bool post) { int comp_key = -1; // find keyframe before or after this one for (int i=0;i keyframes.at(key).time) == post) && (comp_key == -1 || ((keyframes.at(i).time < keyframes.at(comp_key).time) == post))) { // compare with next keyframe for post or previous frame for pre comp_key = i; } } double adjusted_key = post ? keyframes.at(key).post_handle_x : keyframes.at(key).pre_handle_x; // if this is the earliest/latest keyframe, no validation is required if (comp_key == -1) { return adjusted_key; } double comp = keyframes.at(comp_key).time - keyframes.at(key).time; // if comp keyframe is bezier, validate with its accompanying handle if (keyframes.at(comp_key).type == EFFECT_KEYFRAME_BEZIER) { double relative_comp_handle = comp + (post ? keyframes.at(comp_key).pre_handle_x : keyframes.at(comp_key).post_handle_x); // return an average if ((post && keyframes.at(key).post_handle_x > relative_comp_handle) || (!post && keyframes.at(key).pre_handle_x < relative_comp_handle)) { adjusted_key = (adjusted_key + relative_comp_handle)*0.5; } } // don't let handle go beyond the compare keyframe's time if (post == (adjusted_key > comp)) { return comp; } if (post == (adjusted_key < 0)) { return 0; } // original value is valid return adjusted_key; } double EffectField::FrameToSeconds(long frame) { return (double(frame) / GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate); } long EffectField::SecondsToFrame(double seconds) { return qRound(seconds * GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate); } void EffectField::GetKeyframeData(double timecode, 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; long frame = SecondsToFrame(timecode); for (int i=0;i 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) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { // interpolate before = before_keyframe_index; after = after_keyframe_index; progress = (timecode-FrameToSeconds(before_keyframe_time))/(FrameToSeconds(after_keyframe_time)-FrameToSeconds(before_keyframe_time)); } else if (before_keyframe_index > -1) { before = before_keyframe_index; after = before_keyframe_index; } else { before = after_keyframe_index; after = after_keyframe_index; } } bool EffectField::HasKeyframes() { return (GetParentRow()->IsKeyframing() && !keyframes.isEmpty()); } bool EffectField::IsEnabled() { return enabled_; } void EffectField::SetEnabled(bool e) { enabled_ = e; emit EnabledChanged(enabled_); }