From 0e612e717d346325cde41ad7e6056c144f89ec47 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 09:03:08 +1100 Subject: [PATCH 001/133] removed internal blending modes --- debug.cpp | 116 +- effects/internal/blending.frag | 177 +-- effects/internal/transformeffect.cpp | 334 +++--- effects/multiply.blend | 9 + io/config.cpp | 3 +- io/config.h | 8 - main.cpp | 5 +- panels/effectcontrols.cpp | 4 +- panels/timeline.cpp | 8 +- project/effect.cpp | 1535 +++++++++++++------------- project/effect.h | 41 +- project/effectloaders.cpp | 334 +++--- rendering/renderfunctions.cpp | 13 +- 13 files changed, 1190 insertions(+), 1397 deletions(-) create mode 100644 effects/multiply.blend diff --git a/debug.cpp b/debug.cpp index e948b5e51..b64e38ecd 100644 --- a/debug.cpp +++ b/debug.cpp @@ -34,79 +34,79 @@ QFile debug_file; QTextStream debug_stream; void open_debug_file() { - QDir debug_dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - debug_dir.mkpath("."); - if (debug_dir.exists()) { - debug_file.setFileName(debug_dir.path() + "/debug_log"); - if (debug_file.open(QFile::WriteOnly)) { - debug_stream.setDevice(&debug_file); - } else { - qWarning() << "Couldn't open debug log file, debug log will not be saved"; - } + QDir debug_dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + debug_dir.mkpath("."); + if (debug_dir.exists()) { + debug_file.setFileName(debug_dir.path() + "/debug_log"); + if (debug_file.open(QFile::WriteOnly)) { + debug_stream.setDevice(&debug_file); + } else { + qWarning() << "Couldn't open debug log file, debug log will not be saved"; } + } } void close_debug_file() { - if (debug_file.isOpen()) { - debug_file.close(); - } + if (debug_file.isOpen()) { + debug_file.close(); + } } void debug_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - debug_mutex.lock(); - const QByteArray localMsg = msg.toLocal8Bit(); - const QDateTime now = QDateTime::currentDateTime(); - const QByteArray timeRepr(now.toString(Qt::ISODate).toLocal8Bit()); - QString msgTag; - QString fontColor; - switch (type) { - case QtDebugMsg: - msgTag = "DEBUG"; - fontColor = "grey"; - break; - case QtInfoMsg: - msgTag = "INFO"; - fontColor = "blue"; - break; - case QtWarningMsg: - msgTag = "WARNING"; - fontColor = "yellow"; - break; - case QtCriticalMsg: - msgTag = "ERROR"; - fontColor = "red"; - break; - case QtFatalMsg: - msgTag = "FATAL"; - fontColor = "red"; - break; - default: - fprintf(stderr, "Unknown debug msg type"); - fflush(stderr); - break; - }//switch + debug_mutex.lock(); + const QByteArray localMsg = msg.toLocal8Bit(); + const QDateTime now = QDateTime::currentDateTime(); + const QByteArray timeRepr(now.toString(Qt::ISODate).toLocal8Bit()); + QString msgTag; + QString fontColor; + switch (type) { + case QtDebugMsg: + msgTag = "DEBUG"; + fontColor = "grey"; + break; + case QtInfoMsg: + msgTag = "INFO"; + fontColor = "blue"; + break; + case QtWarningMsg: + msgTag = "WARNING"; + fontColor = "yellow"; + break; + case QtCriticalMsg: + msgTag = "ERROR"; + fontColor = "red"; + break; + case QtFatalMsg: + msgTag = "FATAL"; + fontColor = "red"; + break; + default: + fprintf(stderr, "Unknown debug msg type"); + fflush(stderr); + break; + }//switch - /*fprintf(stderr, "%s [%s] %s (%s:%u, %s)\n", timeRepr.data(), msgTag.toLocal8Bit().constData(), localMsg.data(), + /*fprintf(stderr, "%s [%s] %s (%s:%u, %s)\n", timeRepr.data(), msgTag.toLocal8Bit().constData(), localMsg.data(), context.file, context.line, context.function);*/ - fprintf(stderr, "%s [%s] %s\n", timeRepr.data(), msgTag.toLocal8Bit().constData(), localMsg.data()); + fprintf(stderr, "%s [%s] %s\n", timeRepr.data(), msgTag.toLocal8Bit().constData(), localMsg.data()); - if (debug_file.isOpen()) { - debug_stream << QString("[%1] %2 (%3:%4, %5)\n") - .arg(msgTag, localMsg, context.file, QString::number(context.line), context.function); - } - debug_info.prepend(QString("[%2] %3 (%4:%5, %6)
") - .arg(fontColor, msgTag, localMsg, context.file, QString::number(context.line), context.function)); - fflush(stderr); - if (olive::DebugDialog != nullptr && olive::DebugDialog->isVisible()) { - QMetaObject::invokeMethod(olive::DebugDialog, "update_log", Qt::QueuedConnection); - } - debug_mutex.unlock(); + if (debug_file.isOpen()) { + debug_stream << QString("[%1] %2 (%3:%4, %5)\n") + .arg(msgTag, localMsg, context.file, QString::number(context.line), context.function); + } + debug_info.prepend(QString("[%2] %3 (%4:%5, %6)
") + .arg(fontColor, msgTag, localMsg, context.file, QString::number(context.line), context.function)); + fflush(stderr); + if (olive::DebugDialog != nullptr && olive::DebugDialog->isVisible()) { + QMetaObject::invokeMethod(olive::DebugDialog, "update_log", Qt::QueuedConnection); + } + debug_mutex.unlock(); } const QString &get_debug_str() { - return debug_info; + return debug_info; } diff --git a/effects/internal/blending.frag b/effects/internal/blending.frag index c8d14d593..8f4dd3201 100644 --- a/effects/internal/blending.frag +++ b/effects/internal/blending.frag @@ -1,176 +1,13 @@ #version 110 -const int BLEND_MODE_ADD = 0; -const int BLEND_MODE_AVERAGE = 1; -const int BLEND_MODE_COLORBURN = 2; -const int BLEND_MODE_COLORDODGE = 3; -const int BLEND_MODE_DARKEN = 4; -const int BLEND_MODE_DIFFERENCE = 5; -const int BLEND_MODE_EXCLUSION = 6; -const int BLEND_MODE_GLOW = 7; -const int BLEND_MODE_HARDLIGHT = 8; -const int BLEND_MODE_HARDMIX = 9; -const int BLEND_MODE_LIGHTEN = 10; -const int BLEND_MODE_LINEARBURN = 11; -const int BLEND_MODE_LINEARDODGE = 12; -const int BLEND_MODE_LINEARLIGHT = 13; -const int BLEND_MODE_MULTIPLY = 14; -const int BLEND_MODE_NEGATION = 15; -const int BLEND_MODE_NORMAL = 16; -const int BLEND_MODE_OVERLAY = 17; -const int BLEND_MODE_PHOENIX = 18; -const int BLEND_MODE_PINLIGHT = 19; -const int BLEND_MODE_REFLECT = 20; -const int BLEND_MODE_SCREEN = 21; -const int BLEND_MODE_SOFTLIGHT = 22; -const int BLEND_MODE_SUBSTRACT = 23; -const int BLEND_MODE_SUBTRACT = 24; -const int BLEND_MODE_VIVIDLIGHT = 25; +/* + This main stump is combined with another shader loaded externally to produce a blending mode. -uniform sampler2D background; -uniform sampler2D foreground; + For a custom blend mode, the function blend() MUST be available, and use the following syntax: -uniform int blendmode; -uniform float opacity; + blend(vec3 base_color, vec3 blend_color, float opacity) -varying vec2 vTexCoord; - -// adapted from https://github.com/jamieowen/glsl-blend -// and http://www.deepskycolors.com/archivo/2010/04/21/formulas-for-Photoshop-blending-modes.html - -// float blending functions -float blend_color_burn(float base, float blend) { - return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0); -} - -float blend_color_dodge(float base, float blend) { - return (blend==1.0)?blend:min(base/(1.0-blend),1.0); -} - -float blend_vivid_light(float base, float blend) { - return (blend<0.5)?blend_color_burn(base,(2.0*blend)):blend_color_dodge(base,(2.0*(blend-0.5))); -} - -vec3 blend_vivid_light(vec3 base, vec3 blend) { - return vec3(blend_vivid_light(base.r,blend.r),blend_vivid_light(base.g,blend.g),blend_vivid_light(base.b,blend.b)); -} - -float blend_hard_mix(float base, float blend) { - return (blend_vivid_light(base,blend)<0.5)?0.0:1.0; -} - -float blend_lighten(float base, float blend) { - return max(blend,base); -} - -float blend_overlay(float base, float blend) { - return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); -} - -vec3 blend_overlay(vec3 base, vec3 blend) { - return vec3(blend_overlay(base.r,blend.r),blend_overlay(base.g,blend.g),blend_overlay(base.b,blend.b)); -} - -float blend_darken(float base, float blend) { - return min(blend, base); -} - -float blend_linear_burn(float base, float blend) { - return max(base+blend-1.0,0.0); -} - -vec3 blend_linear_burn(vec3 base, vec3 blend) { - return max(base+blend-vec3(1.0),vec3(0.0)); -} - -float blend_linear_dodge(float base, float blend) { - return min(base+blend,1.0); -} - -vec3 blend_linear_dodge(vec3 base, vec3 blend) { - return min(base+blend,vec3(1.0)); -} - -float blend_linear_light(float base, float blend) { - return blend<0.5?blend_linear_burn(base,(2.0*blend)):blend_linear_dodge(base,(2.0*(blend-0.5))); -} - -float blend_pin_light(float base, float blend) { - return (blend<0.5)?blend_darken(base,(2.0*blend)):blend_lighten(base,(2.0*(blend-0.5))); -} - -float blend_reflect(float base, float blend) { - return (blend==1.0)?blend:min(base*base/(1.0-blend),1.0); -} - -vec3 blend_reflect(vec3 base, vec3 blend) { - return vec3(blend_reflect(base.r,blend.r),blend_reflect(base.g,blend.g),blend_reflect(base.b,blend.b)); -} - -float blend_screen(float base, float blend) { - return 1.0-((1.0-base)*(1.0-blend)); -} - -float blend_substract(float base, float blend) { - return max(base+blend-1.0,0.0); -} - -float blend_soft_light(float base, float blend) { - return (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend)); -} - -// RGB blending function, alpha is handled below -vec3 blend(vec3 base, vec3 blend) { - if (blendmode == BLEND_MODE_AVERAGE) { - return (base+blend)/2.0; - } else if (blendmode == BLEND_MODE_COLORBURN) { - return vec3(blend_color_burn(base.r, blend.r), blend_color_burn(base.g, blend.g), blend_color_burn(base.b, blend.b)); - } else if (blendmode == BLEND_MODE_COLORDODGE) { - return vec3(blend_color_dodge(base.r, blend.r), blend_color_dodge(base.g, blend.g), blend_color_dodge(base.b, blend.b)); - } else if (blendmode == BLEND_MODE_DARKEN) { - return vec3(blend_darken(base.r, blend.r), blend_darken(base.g, blend.g), blend_darken(base.b, blend.b)); - } else if (blendmode == BLEND_MODE_DIFFERENCE) { - return abs(base-blend); - } else if (blendmode == BLEND_MODE_EXCLUSION) { - return base+blend-2.0*base*blend; - } else if (blendmode == BLEND_MODE_GLOW) { - return blend_reflect(blend, base); - } else if (blendmode == BLEND_MODE_HARDLIGHT) { - return blend_overlay(blend,base); - } else if (blendmode == BLEND_MODE_HARDMIX) { - return vec3(blend_hard_mix(base.r,blend.r),blend_hard_mix(base.g,blend.g),blend_hard_mix(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_LIGHTEN) { - return vec3(blend_lighten(base.r,blend.r),blend_lighten(base.g,blend.g),blend_lighten(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_LINEARBURN || blendmode == BLEND_MODE_SUBTRACT) { - return blend_linear_burn(base, blend); - } else if (blendmode == BLEND_MODE_LINEARDODGE || blendmode == BLEND_MODE_ADD) { - return blend_linear_dodge(base, blend); - } else if (blendmode == BLEND_MODE_LINEARLIGHT) { - return vec3(blend_linear_light(base.r,blend.r),blend_linear_light(base.g,blend.g),blend_linear_light(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_MULTIPLY) { - return (base * blend); - } else if (blendmode == BLEND_MODE_NEGATION) { - return vec3(1.0)-abs(vec3(1.0)-base-blend); - } else if (blendmode == BLEND_MODE_OVERLAY) { - return blend_overlay(base, blend); - } else if (blendmode == BLEND_MODE_PHOENIX) { - return min(base,blend)-max(base,blend)+vec3(1.0); - } else if (blendmode == BLEND_MODE_PINLIGHT) { - return vec3(blend_pin_light(base.r,blend.r),blend_pin_light(base.g,blend.g),blend_pin_light(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_REFLECT) { - return blend_reflect(base, blend); - } else if (blendmode == BLEND_MODE_SCREEN) { - return vec3(blend_screen(base.r,blend.r),blend_screen(base.g,blend.g),blend_screen(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_SUBSTRACT) { - return max(base+blend-vec3(1.0),vec3(0.0)); - } else if (blendmode == BLEND_MODE_SOFTLIGHT) { - return vec3(blend_soft_light(base.r,blend.r),blend_soft_light(base.g,blend.g),blend_soft_light(base.b,blend.b)); - } else if (blendmode == BLEND_MODE_VIVIDLIGHT) { - return vec3(blend_vivid_light(base.r,blend.r),blend_vivid_light(base.g,blend.g),blend_vivid_light(base.b,blend.b)); - } else { - return blend; - } -} +*/ void main(void) { vec4 bg_color = texture2D(background, vTexCoord); @@ -179,9 +16,6 @@ void main(void) { // blend textures together vec3 composite = blend(bg_color.rgb, fg_color.rgb); - // add foreground and background alpha's together - // float alpha_opac = fg_color.a*opacity; - if (blendmode == BLEND_MODE_OVERLAY || blendmode == BLEND_MODE_LIGHTEN || blendmode == BLEND_MODE_SCREEN @@ -198,7 +32,6 @@ void main(void) { } vec4 full_composite = vec4(composite + bg_color.rgb*(1.0-fg_color.a), bg_color.a + fg_color.a); - // vec4 full_composite = vec4(mix(bg_color.rgb, composite, alpha_opac), bg_color.a + alpha_opac); full_composite = mix(bg_color, full_composite, opacity); diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 07dd08d3f..f4065ba1e 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -44,230 +44,210 @@ #include "ui/viewerwidget.h" TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { - enable_coords = true; + enable_coords = true; - EffectRow* position_row = add_row(tr("Position")); - position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X - position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y + EffectRow* position_row = add_row(tr("Position")); + position_x = position_row->add_field(EFFECT_FIELD_DOUBLE, "posx"); // position X + position_y = position_row->add_field(EFFECT_FIELD_DOUBLE, "posy"); // position Y - EffectRow* scale_row = add_row(tr("Scale")); - scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) - scale_x->set_double_minimum_value(0); - scale_x->set_double_maximum_value(3000); - scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) - scale_y->set_double_minimum_value(0); - scale_y->set_double_maximum_value(3000); + EffectRow* scale_row = add_row(tr("Scale")); + scale_x = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scalex"); // scale X (and Y is uniform scale is selected) + scale_x->set_double_minimum_value(0); + scale_x->set_double_maximum_value(3000); + scale_y = scale_row->add_field(EFFECT_FIELD_DOUBLE, "scaley"); // scale Y (disabled if uniform scale is selected) + scale_y->set_double_minimum_value(0); + scale_y->set_double_maximum_value(3000); - EffectRow* uniform_scale_row = add_row(tr("Uniform Scale")); - uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option + EffectRow* uniform_scale_row = add_row(tr("Uniform Scale")); + uniform_scale_field = uniform_scale_row->add_field(EFFECT_FIELD_BOOL, "uniformscale"); // uniform scale option - EffectRow* rotation_row = add_row(tr("Rotation")); - rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); + EffectRow* rotation_row = add_row(tr("Rotation")); + rotation = rotation_row->add_field(EFFECT_FIELD_DOUBLE, "rotation"); - EffectRow* anchor_point_row = add_row(tr("Anchor Point")); - anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X - anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y + EffectRow* anchor_point_row = add_row(tr("Anchor Point")); + anchor_x_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchorx"); // anchor point X + anchor_y_box = anchor_point_row->add_field(EFFECT_FIELD_DOUBLE, "anchory"); // anchor point Y - EffectRow* opacity_row = add_row(tr("Opacity")); - opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity - opacity->set_double_minimum_value(0); - opacity->set_double_maximum_value(100); + EffectRow* opacity_row = add_row(tr("Opacity")); + opacity = opacity_row->add_field(EFFECT_FIELD_DOUBLE, "opacity"); // opacity + opacity->set_double_minimum_value(0); + opacity->set_double_maximum_value(100); - EffectRow* blend_mode_row = add_row(tr("Blend Mode")); - blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode", 2); // blend mode - blend_mode_box->add_combo_item(tr("Normal"), BLEND_MODE_NORMAL); - blend_mode_box->add_combo_item(tr("Darken"), BLEND_MODE_DARKEN); - blend_mode_box->add_combo_item(tr("Multiply"), BLEND_MODE_MULTIPLY); - blend_mode_box->add_combo_item(tr("Color Burn"), BLEND_MODE_COLORBURN); - blend_mode_box->add_combo_item(tr("Linear Burn"), BLEND_MODE_LINEARBURN); - blend_mode_box->add_combo_item(tr("Lighten"), BLEND_MODE_LIGHTEN); - blend_mode_box->add_combo_item(tr("Screen"), BLEND_MODE_SCREEN); - blend_mode_box->add_combo_item(tr("Color Dodge"), BLEND_MODE_COLORDODGE); - blend_mode_box->add_combo_item(tr("Linear Dodge (Add)"), BLEND_MODE_LINEARDODGE); - blend_mode_box->add_combo_item(tr("Overlay"), BLEND_MODE_OVERLAY); - blend_mode_box->add_combo_item(tr("Soft Light"), BLEND_MODE_SOFTLIGHT); - blend_mode_box->add_combo_item(tr("Hard Light"), BLEND_MODE_HARDLIGHT); - blend_mode_box->add_combo_item(tr("Vivid Light"), BLEND_MODE_VIVIDLIGHT); - blend_mode_box->add_combo_item(tr("Linear Light"), BLEND_MODE_LINEARLIGHT); - blend_mode_box->add_combo_item(tr("Pin Light"), BLEND_MODE_PINLIGHT); - blend_mode_box->add_combo_item(tr("Hard Mix"), BLEND_MODE_HARDMIX); - blend_mode_box->add_combo_item(tr("Difference"), BLEND_MODE_DIFFERENCE); - blend_mode_box->add_combo_item(tr("Exclusion"), BLEND_MODE_EXCLUSION); - blend_mode_box->add_combo_item(tr("Reflect"), BLEND_MODE_REFLECT); -// blend_mode_box->add_combo_item(tr("Subtract"), BLEND_MODE_SUBTRACT); - blend_mode_box->add_combo_item(tr("Substract"), BLEND_MODE_SUBSTRACT); -// blend_mode_box->add_combo_item(tr("Add"), BLEND_MODE_ADD); - blend_mode_box->add_combo_item(tr("Average"), BLEND_MODE_AVERAGE); - blend_mode_box->add_combo_item(tr("Glow"), BLEND_MODE_GLOW); - blend_mode_box->add_combo_item(tr("Negation"), BLEND_MODE_NEGATION); - blend_mode_box->add_combo_item(tr("Phoenix"), BLEND_MODE_PHOENIX); + EffectRow* blend_mode_row = add_row(tr("Blend Mode")); + blend_mode_box = blend_mode_row->add_field(EFFECT_FIELD_COMBO, "blendmode", 2); // blend mode + blend_mode_box->add_combo_item(tr("Normal"), -1); - // set up gizmos - top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); - top_left_gizmo->x_field1 = scale_x; + // add loaded blending modes + for (int i=0;iadd_combo_item(olive::blend_modes.at(i).name, i); + } - top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_center_gizmo->set_cursor(Qt::SizeVerCursor); - top_center_gizmo->y_field1 = scale_x; + // set up gizmos + top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); + top_left_gizmo->x_field1 = scale_x; - top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); - top_right_gizmo->x_field1 = scale_x; + top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_center_gizmo->set_cursor(Qt::SizeVerCursor); + top_center_gizmo->y_field1 = scale_x; - bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); - bottom_left_gizmo->x_field1 = scale_x; + top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); + top_right_gizmo->x_field1 = scale_x; - bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); - bottom_center_gizmo->y_field1 = scale_x; + bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); + bottom_left_gizmo->x_field1 = scale_x; - bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); - bottom_right_gizmo->x_field1 = scale_x; + bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); + bottom_center_gizmo->y_field1 = scale_x; - left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - left_center_gizmo->set_cursor(Qt::SizeHorCursor); - left_center_gizmo->x_field1 = scale_x; + bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); + bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); + bottom_right_gizmo->x_field1 = scale_x; - right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); - right_center_gizmo->set_cursor(Qt::SizeHorCursor); - right_center_gizmo->x_field1 = scale_x; + left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + left_center_gizmo->set_cursor(Qt::SizeHorCursor); + left_center_gizmo->x_field1 = scale_x; - anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); - anchor_gizmo->set_cursor(Qt::SizeAllCursor); - anchor_gizmo->x_field1 = anchor_x_box; - anchor_gizmo->y_field1 = anchor_y_box; - anchor_gizmo->x_field2 = position_x; - anchor_gizmo->y_field2 = position_y; + right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); + right_center_gizmo->set_cursor(Qt::SizeHorCursor); + right_center_gizmo->x_field1 = scale_x; - rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); - rotate_gizmo->color = Qt::green; - rotate_gizmo->set_cursor(Qt::SizeAllCursor); - rotate_gizmo->x_field1 = rotation; + anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); + anchor_gizmo->set_cursor(Qt::SizeAllCursor); + anchor_gizmo->x_field1 = anchor_x_box; + anchor_gizmo->y_field1 = anchor_y_box; + anchor_gizmo->x_field2 = position_x; + anchor_gizmo->y_field2 = position_y; - rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); - rect_gizmo->x_field1 = position_x; - rect_gizmo->y_field1 = position_y; + rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); + rotate_gizmo->color = Qt::green; + rotate_gizmo->set_cursor(Qt::SizeAllCursor); + rotate_gizmo->x_field1 = rotation; - connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); + rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); + rect_gizmo->x_field1 = position_x; + rect_gizmo->y_field1 = position_y; - // set defaults - uniform_scale_field->set_bool_value(true); - blend_mode_box->set_combo_index(0); - set = false; - refresh(); + connect(uniform_scale_field, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); + + // set defaults + uniform_scale_field->set_bool_value(true); + blend_mode_box->set_combo_index(0); + set = false; + refresh(); } void adjust_field(EffectField* field, double old_offset, double new_offset) { - if (field->keyframes.size() > 0) { - for (int i=0;ikeyframes.size();i++) { - field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; - } - } else { - field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); - } + if (field->keyframes.size() > 0) { + for (int i=0;ikeyframes.size();i++) { + field->keyframes[i].data = field->keyframes.at(i).data.toDouble() - old_offset + new_offset; + } + } else { + field->set_current_data(field->get_current_data().toDouble() - old_offset + new_offset); + } } void TransformEffect::refresh() { - if (parent_clip != nullptr && parent_clip->sequence != nullptr) { - double new_default_pos_x = parent_clip->sequence->width/2; - double new_default_pos_y = parent_clip->sequence->height/2; + if (parent_clip != nullptr && parent_clip->sequence != nullptr) { + double new_default_pos_x = parent_clip->sequence->width/2; + double new_default_pos_y = parent_clip->sequence->height/2; - /*if (set) { - adjust_field(position_x, default_pos_x, new_default_pos_x); - adjust_field(position_y, default_pos_y, new_default_pos_y); - }*/ + /*if (set) { + adjust_field(position_x, default_pos_x, new_default_pos_x); + adjust_field(position_y, default_pos_y, new_default_pos_y); + }*/ - double default_pos_x = new_default_pos_x; - double default_pos_y = new_default_pos_y; + double default_pos_x = new_default_pos_x; + double default_pos_y = new_default_pos_y; - position_x->set_double_default_value(default_pos_x); - position_y->set_double_default_value(default_pos_y); - scale_x->set_double_default_value(100); - scale_y->set_double_default_value(100); + position_x->set_double_default_value(default_pos_x); + position_y->set_double_default_value(default_pos_y); + scale_x->set_double_default_value(100); + scale_y->set_double_default_value(100); - anchor_x_box->set_double_default_value(0); - anchor_y_box->set_double_default_value(0); - opacity->set_double_default_value(100); + anchor_x_box->set_double_default_value(0); + anchor_y_box->set_double_default_value(0); + opacity->set_double_default_value(100); - double x_percent_multipler = 200.0 / parent_clip->sequence->width; - double y_percent_multipler = 200.0 / parent_clip->sequence->height; - top_left_gizmo->x_field_multi1 = -x_percent_multipler; - top_left_gizmo->y_field_multi1 = -y_percent_multipler; - top_center_gizmo->y_field_multi1 = -y_percent_multipler; - top_right_gizmo->x_field_multi1 = x_percent_multipler; - top_right_gizmo->y_field_multi1 = -y_percent_multipler; - bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; - bottom_left_gizmo->y_field_multi1 = y_percent_multipler; - bottom_center_gizmo->y_field_multi1 = y_percent_multipler; - bottom_right_gizmo->x_field_multi1 = x_percent_multipler; - bottom_right_gizmo->y_field_multi1 = y_percent_multipler; - left_center_gizmo->x_field_multi1 = -x_percent_multipler; - right_center_gizmo->x_field_multi1 = x_percent_multipler; - rotate_gizmo->x_field_multi1 = x_percent_multipler; + double x_percent_multipler = 200.0 / parent_clip->sequence->width; + double y_percent_multipler = 200.0 / parent_clip->sequence->height; + top_left_gizmo->x_field_multi1 = -x_percent_multipler; + top_left_gizmo->y_field_multi1 = -y_percent_multipler; + top_center_gizmo->y_field_multi1 = -y_percent_multipler; + top_right_gizmo->x_field_multi1 = x_percent_multipler; + top_right_gizmo->y_field_multi1 = -y_percent_multipler; + bottom_left_gizmo->x_field_multi1 = -x_percent_multipler; + bottom_left_gizmo->y_field_multi1 = y_percent_multipler; + bottom_center_gizmo->y_field_multi1 = y_percent_multipler; + bottom_right_gizmo->x_field_multi1 = x_percent_multipler; + bottom_right_gizmo->y_field_multi1 = y_percent_multipler; + left_center_gizmo->x_field_multi1 = -x_percent_multipler; + right_center_gizmo->x_field_multi1 = x_percent_multipler; + rotate_gizmo->x_field_multi1 = x_percent_multipler; - set = true; - } + set = true; + } } void TransformEffect::toggle_uniform_scale(bool enabled) { - scale_y->set_enabled(!enabled); + scale_y->set_enabled(!enabled); - top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; - top_left_gizmo->y_field1 = enabled ? nullptr : scale_y; - top_right_gizmo->y_field1 = enabled ? nullptr : scale_y; - bottom_left_gizmo->y_field1 = enabled ? nullptr : scale_y; - bottom_right_gizmo->y_field1 = enabled ? nullptr : scale_y; + top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; + top_left_gizmo->y_field1 = enabled ? nullptr : scale_y; + top_right_gizmo->y_field1 = enabled ? nullptr : scale_y; + bottom_left_gizmo->y_field1 = enabled ? nullptr : scale_y; + bottom_right_gizmo->y_field1 = enabled ? nullptr : scale_y; } void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) { - // position - glTranslated(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0); + // position + glTranslated(position_x->get_double_value(timecode)-(parent_clip->sequence->width/2), position_y->get_double_value(timecode)-(parent_clip->sequence->height/2), 0); - // anchor point - int anchor_x_offset = qRound(anchor_x_box->get_double_value(timecode)); - int anchor_y_offset = qRound(anchor_y_box->get_double_value(timecode)); - coords.vertexTopLeftX -= anchor_x_offset; - coords.vertexTopRightX -= anchor_x_offset; - coords.vertexBottomLeftX -= anchor_x_offset; - coords.vertexBottomRightX -= anchor_x_offset; - coords.vertexTopLeftY -= anchor_y_offset; - coords.vertexTopRightY -= anchor_y_offset; - coords.vertexBottomLeftY -= anchor_y_offset; - coords.vertexBottomRightY -= anchor_y_offset; + // anchor point + int anchor_x_offset = qRound(anchor_x_box->get_double_value(timecode)); + int anchor_y_offset = qRound(anchor_y_box->get_double_value(timecode)); + coords.vertexTopLeftX -= anchor_x_offset; + coords.vertexTopRightX -= anchor_x_offset; + coords.vertexBottomLeftX -= anchor_x_offset; + coords.vertexBottomRightX -= anchor_x_offset; + coords.vertexTopLeftY -= anchor_y_offset; + coords.vertexTopRightY -= anchor_y_offset; + coords.vertexBottomLeftY -= anchor_y_offset; + coords.vertexBottomRightY -= anchor_y_offset; - // rotation - glRotated(rotation->get_double_value(timecode), 0, 0, 1); + // rotation + glRotated(rotation->get_double_value(timecode), 0, 0, 1); - // scale - double sx = scale_x->get_double_value(timecode)*0.01; - double sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01; - glScaled(sx, sy, 1); + // scale + double sx = scale_x->get_double_value(timecode)*0.01; + double sy = (uniform_scale_field->get_bool_value(timecode)) ? sx : scale_y->get_double_value(timecode)*0.01; + glScaled(sx, sy, 1); - // blend mode - coords.blendmode = blend_mode_box->get_combo_data(timecode).toInt(); + // blend mode + coords.blendmode = blend_mode_box->get_combo_data(timecode).toInt(); - // opacity - coords.opacity *= float(opacity->get_double_value(timecode)*0.01); + // opacity + coords.opacity *= float(opacity->get_double_value(timecode)*0.01); } void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); - left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); + top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); + top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); + bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); + bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); - rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); + rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); - rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); + rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); + rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); + rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); } diff --git a/effects/multiply.blend b/effects/multiply.blend new file mode 100644 index 000000000..0da71e49d --- /dev/null +++ b/effects/multiply.blend @@ -0,0 +1,9 @@ +vec3 blendMultiply(vec3 base, vec3 blend) { + return base*blend; +} + +vec3 blendMultiply(vec3 base, vec3 blend, float opacity) { + return (blendMultiply(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendMultiply) \ No newline at end of file diff --git a/io/config.cpp b/io/config.cpp index 5b09d877b..1490ddcbd 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -285,6 +285,5 @@ void Config::save(QString path) { } RuntimeConfig::RuntimeConfig() : - shaders_are_enabled(true), - disable_blending(false) + shaders_are_enabled(true) {} diff --git a/io/config.h b/io/config.h index a83eb30d7..c9fc41d92 100644 --- a/io/config.h +++ b/io/config.h @@ -561,14 +561,6 @@ struct RuntimeConfig { */ bool shaders_are_enabled; - /** - * @brief Disable blending modes - * - * Some users had difficulty utilizing blending modes (provided by shaders). Set this to **TRUE** to bypass - * shader-based blending modes and utilize standard (less versatile) OpenGL blending instead. - */ - bool disable_blending; - /** * @brief Load an external translation file * diff --git a/main.cpp b/main.cpp index 69f99ee5e..e36f50fe0 100644 --- a/main.cpp +++ b/main.cpp @@ -34,7 +34,7 @@ extern "C" { #include } -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) { olive::Global = std::unique_ptr(new OliveGlobal); bool launch_fullscreen = false; @@ -60,7 +60,6 @@ int main(int argc, char *argv[]) { "\t-f, --fullscreen\tStart in full screen mode\n" "\t--disable-shaders\tDisable OpenGL shaders (for debugging)\n" "\t--no-debug\t\tDisable internal debug log and output directly to console\n" - "\t--disable-blend-modes\tDisable shader-based blending for older GPUs\n" "\t--translation \tSet an external language file to use\n" "\n" "Environment Variables:\n" @@ -75,8 +74,6 @@ int main(int argc, char *argv[]) { olive::CurrentRuntimeConfig.shaders_are_enabled = false; } else if (!strcmp(argv[i], "--no-debug")) { use_internal_logger = false; - } else if (!strcmp(argv[i], "--disable-blend-modes")) { - olive::CurrentRuntimeConfig.disable_blending = true; } else if (!strcmp(argv[i], "--translation")) { if (i + 1 < argc && argv[i + 1][0] != '-') { // load translation file diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 174baa795..1f0088ed1 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -199,8 +199,8 @@ void EffectControls::show_effect_menu(int type, int subtype) { QMenu effects_menu(this); effects_menu.setToolTipsVisible(true); - for (int i=0;isetObjectName("v"); @@ -1803,8 +1803,8 @@ void Timeline::transition_tool_click() { transition_menu.addSeparator(); - for (int i=0;isetObjectName("a"); diff --git a/project/effect.cpp b/project/effect.cpp index a49ae6977..4cf2bdc22 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -67,12 +67,13 @@ #include #include -QVector effects; +QVector olive::effects; +QVector olive::blend_modes; EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { - if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { - // must be an internal effect - switch (em->internal) { + if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { + // must be an internal effect + switch (em->internal) { case EFFECT_INTERNAL_TRANSFORM: return EffectPtr(new TransformEffect(c, em)); case EFFECT_INTERNAL_TEXT: return EffectPtr(new TextEffect(c, em)); case EFFECT_INTERNAL_TIMECODE: return EffectPtr(new TimecodeEffect(c, em)); @@ -90,851 +91,851 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { #ifndef NOFREI0R case EFFECT_INTERNAL_FREI0R: return EffectPtr(new Frei0rEffect(c, em)); #endif - } - } else if (!em->filename.isEmpty()) { - // load effect from file + } + } else if (!em->filename.isEmpty()) { + // load effect from file return EffectPtr(new Effect(c, em)); - } else { - qCritical() << "Invalid effect data"; + } else { + qCritical() << "Invalid effect data"; QMessageBox::critical(olive::MainWindow, - QCoreApplication::translate("Effect", "Invalid effect"), - QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be corrupt. Try reinstalling it or Olive.").arg(em->name)); - } - return nullptr; + QCoreApplication::translate("Effect", "Invalid effect"), + QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be corrupt. Try reinstalling it or Olive.").arg(em->name)); + } + return nullptr; } const EffectMeta* Effect::GetInternalMeta(int internal_id, int type) { - for (int i=0;ienabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); - ui = new QWidget(container); - ui_layout = new QGridLayout(ui); - ui_layout->setSpacing(4); - container->setContents(ui); + // set up base UI + container = new CollapsibleWidget(); + connect(container->enabled_check, SIGNAL(clicked(bool)), this, SLOT(field_changed())); + ui = new QWidget(container); + ui_layout = new QGridLayout(ui); + ui_layout->setSpacing(4); + container->setContents(ui); - connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); + connect(container->title_bar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu(const QPoint&))); - if (em != nullptr) { - // set up UI from effect file - container->setText(em->name); + if (em != nullptr) { + // set up UI from effect file + container->setText(em->name); - if (!em->filename.isEmpty() && em->internal == -1) { - QFile effect_file(em->filename); - if (effect_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&effect_file); + if (!em->filename.isEmpty() && em->internal == -1) { + QFile effect_file(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;ifilename << "- ID cannot be empty."; - } else if (type > -1) { - EffectField* field = row->add_field(type, id); - connect(field, SIGNAL(changed()), this, SLOT(field_changed())); - switch (type) { - case EFFECT_FIELD_DOUBLE: - for (int i=0;iset_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;iset_color_value(color); - } - break; - case EFFECT_FIELD_STRING: - for (int i=0;iset_string_value(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_BOOL: - for (int i=0;iset_bool_value(attr.value() == "1"); - } - } - break; - case EFFECT_FIELD_COMBO: - { - int combo_index = 0; - for (int i=0;iadd_combo_item(reader.text().toString(), 0); - } - } - field->set_combo_index(combo_index); - } - break; - case EFFECT_FIELD_FONT: - for (int i=0;iset_font_name(attr.value().toString()); - } - } - break; - case EFFECT_FIELD_FILE: - for (int i=0;iset_filename(attr.value().toString()); - } - } - break; - } - } - } - } - } - } else if (reader.name() == "shader" && reader.isStartElement()) { - enable_shader = true; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; - } - break; - } - } - }*/ - reader.readNext(); - } + if (id.isEmpty()) { + qCritical() << "Couldn't load field from" << em->filename << "- ID cannot be empty."; + } else if (type > -1) { + EffectField* field = row->add_field(type, id); + connect(field, SIGNAL(changed()), this, SLOT(field_changed())); + switch (type) { + case EFFECT_FIELD_DOUBLE: + for (int i=0;iset_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;iset_color_value(color); + } + break; + case EFFECT_FIELD_STRING: + for (int i=0;iset_string_value(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_BOOL: + for (int i=0;iset_bool_value(attr.value() == "1"); + } + } + break; + case EFFECT_FIELD_COMBO: + { + int combo_index = 0; + for (int i=0;iadd_combo_item(reader.text().toString(), 0); + } + } + field->set_combo_index(combo_index); + } + break; + case EFFECT_FIELD_FONT: + for (int i=0;iset_font_name(attr.value().toString()); + } + } + break; + case EFFECT_FIELD_FILE: + for (int i=0;iset_filename(attr.value().toString()); + } + } + break; + } + } + } + } + } + } else if (reader.name() == "shader" && reader.isStartElement()) { + enable_shader = true; + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename; + enable_superimpose = false; + } + break; + } + } + }*/ + reader.readNext(); + } - effect_file.close(); - } else { - qCritical() << "Failed to open effect file" << em->filename; - } - } - } + effect_file.close(); + } else { + qCritical() << "Failed to open effect file" << em->filename; + } + } + } } Effect::~Effect() { - if (isOpen) { - close(); - } + if (isOpen) { + close(); + } - delete container; + delete container; - for (int i=0;irows.at(i); - copy_row->setKeyframing(row->isKeyframing()); - for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - EffectField* copy_field = copy_row->field(j); - copy_field->keyframes = field->keyframes; - copy_field->set_current_data(field->get_current_data()); - } - } + for (int i=0;irows.at(i); + copy_row->setKeyframing(row->isKeyframing()); + for (int j=0;jfieldCount();j++) { + EffectField* field = row->field(j); + EffectField* copy_field = copy_row->field(j); + copy_field->keyframes = field->keyframes; + copy_field->set_current_data(field->get_current_data()); + } + } } EffectRow* Effect::add_row(const QString& name, bool savable, bool keyframable) { - EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size(), keyframable); - rows.append(row); - return row; + EffectRow* row = new EffectRow(this, savable, ui_layout, name, rows.size(), keyframable); + rows.append(row); + return row; } EffectRow* Effect::row(int i) { - return rows.at(i); + return rows.at(i); } int Effect::row_count() { - return rows.size(); + return rows.size(); } EffectGizmo *Effect::add_gizmo(int type) { - EffectGizmo* gizmo = new EffectGizmo(type); - gizmos.append(gizmo); - return gizmo; + EffectGizmo* gizmo = new EffectGizmo(type); + gizmos.append(gizmo); + return gizmo; } EffectGizmo *Effect::gizmo(int i) { - return gizmos.at(i); + return gizmos.at(i); } int Effect::gizmo_count() { - return gizmos.size(); + return gizmos.size(); } void Effect::refresh() {} void Effect::field_changed() { - panel_sequence_viewer->viewer_widget->frame_update(); - panel_graph_editor->update_panel(); + panel_sequence_viewer->viewer_widget->frame_update(); + panel_graph_editor->update_panel(); } void Effect::show_context_menu(const QPoint& pos) { - if (meta->type == EFFECT_TYPE_EFFECT) { + if (meta->type == EFFECT_TYPE_EFFECT) { QMenu menu(olive::MainWindow); - int index = get_index_in_clip(); + int index = get_index_in_clip(); - menu.addAction(tr("Cu&t"), panel_effect_controls, SLOT(cut())); - menu.addAction(tr("&Copy"), panel_effect_controls, SLOT(copy(bool))); + menu.addAction(tr("Cu&t"), panel_effect_controls, SLOT(cut())); + menu.addAction(tr("&Copy"), panel_effect_controls, SLOT(copy(bool))); - panel_effect_controls->add_effect_paste_action(&menu); + panel_effect_controls->add_effect_paste_action(&menu); - menu.addSeparator(); + menu.addSeparator(); - if (index > 0) { - menu.addAction(tr("Move &Up"), this, SLOT(move_up())); - } + if (index > 0) { + menu.addAction(tr("Move &Up"), this, SLOT(move_up())); + } - if (index < parent_clip->effects.size() - 1) { - menu.addAction(tr("Move &Down"), this, SLOT(move_down())); - } + if (index < parent_clip->effects.size() - 1) { + menu.addAction(tr("Move &Down"), this, SLOT(move_down())); + } - menu.addSeparator(); + menu.addSeparator(); - menu.addAction(tr("D&elete"), this, SLOT(delete_self())); + menu.addAction(tr("D&elete"), this, SLOT(delete_self())); - menu.addSeparator(); + menu.addSeparator(); - menu.addAction(tr("Load Settings From File"), this, SLOT(load_from_file())); + menu.addAction(tr("Load Settings From File"), this, SLOT(load_from_file())); - menu.addAction(tr("Save Settings to File"), this, SLOT(save_to_file())); + menu.addAction(tr("Save Settings to File"), this, SLOT(save_to_file())); - menu.exec(container->title_bar->mapToGlobal(pos)); - } + menu.exec(container->title_bar->mapToGlobal(pos)); + } } void Effect::delete_self() { - EffectDeleteCommand* command = new EffectDeleteCommand(); - command->clips.append(parent_clip); - command->fx.append(get_index_in_clip()); - olive::UndoStack.push(command); - update_ui(true); + EffectDeleteCommand* command = new EffectDeleteCommand(); + command->clips.append(parent_clip); + command->fx.append(get_index_in_clip()); + olive::UndoStack.push(command); + update_ui(true); } void Effect::move_up() { - MoveEffectCommand* command = new MoveEffectCommand(); - command->clip = parent_clip; - command->from = get_index_in_clip(); - command->to = command->from - 1; - olive::UndoStack.push(command); - panel_effect_controls->reload_clips(); - panel_sequence_viewer->viewer_widget->frame_update(); + MoveEffectCommand* command = new MoveEffectCommand(); + command->clip = parent_clip; + command->from = get_index_in_clip(); + command->to = command->from - 1; + olive::UndoStack.push(command); + panel_effect_controls->reload_clips(); + panel_sequence_viewer->viewer_widget->frame_update(); } void Effect::move_down() { - MoveEffectCommand* command = new MoveEffectCommand(); - command->clip = parent_clip; - command->from = get_index_in_clip(); - command->to = command->from + 1; - olive::UndoStack.push(command); - panel_effect_controls->reload_clips(); - panel_sequence_viewer->viewer_widget->frame_update(); + MoveEffectCommand* command = new MoveEffectCommand(); + command->clip = parent_clip; + command->from = get_index_in_clip(); + command->to = command->from + 1; + olive::UndoStack.push(command); + panel_effect_controls->reload_clips(); + panel_sequence_viewer->viewer_widget->frame_update(); } void Effect::save_to_file() { - // save effect settings to file + // save effect settings to file QString file = QFileDialog::getSaveFileName(olive::MainWindow, - tr("Save Effect Settings"), - QString(), - tr("Effect XML Settings %1").arg("(*.xml)")); + tr("Save Effect Settings"), + QString(), + tr("Effect XML Settings %1").arg("(*.xml)")); - // if the user picked a file - if (!file.isEmpty()) { + // if the user picked a file + if (!file.isEmpty()) { // ensure file ends with .xml extension if (!file.endsWith(".xml", Qt::CaseInsensitive)) { file.append(".xml"); } - QFile file_handle(file); - if (file_handle.open(QFile::WriteOnly)) { + QFile file_handle(file); + if (file_handle.open(QFile::WriteOnly)) { - file_handle.write(save_to_string()); + file_handle.write(save_to_string()); - file_handle.close(); - } else { + file_handle.close(); + } else { QMessageBox::critical(olive::MainWindow, - tr("Save Settings Failed"), - tr("Failed to open \"%1\" for writing.").arg(file), - QMessageBox::Ok); - } - } + tr("Save Settings Failed"), + tr("Failed to open \"%1\" for writing.").arg(file), + QMessageBox::Ok); + } + } } void Effect::load_from_file() { - // load effect settings from file + // load effect settings from file QString file = QFileDialog::getOpenFileName(olive::MainWindow, - tr("Load Effect Settings"), - QString(), - tr("Effect XML Settings %1").arg("(*.xml)")); + tr("Load Effect Settings"), + QString(), + tr("Effect XML Settings %1").arg("(*.xml)")); - // if the user picked a file - if (!file.isEmpty()) { - QFile file_handle(file); - if (file_handle.open(QFile::ReadOnly)) { + // if the user picked a file + if (!file.isEmpty()) { + QFile file_handle(file); + if (file_handle.open(QFile::ReadOnly)) { olive::UndoStack.push(new SetEffectData(EffectPtr(this), file_handle.readAll())); - file_handle.close(); + file_handle.close(); - update_ui(false); - } else { + update_ui(false); + } else { QMessageBox::critical(olive::MainWindow, - tr("Load Settings Failed"), - tr("Failed to open \"%1\" for reading.").arg(file), - QMessageBox::Ok); - } - } + tr("Load Settings Failed"), + tr("Failed to open \"%1\" for reading.").arg(file), + QMessageBox::Ok); + } + } } int Effect::get_index_in_clip() { - if (parent_clip != nullptr) { - for (int i=0;ieffects.size();i++) { + if (parent_clip != nullptr) { + for (int i=0;ieffects.size();i++) { if (parent_clip->effects.at(i).get() == this) { - return i; - } - } - } - return -1; + return i; + } + } + } + return -1; } bool Effect::is_enabled() { - return container->enabled_check->isChecked(); + return container->enabled_check->isChecked(); } void Effect::set_enabled(bool b) { - container->enabled_check->setChecked(b); + container->enabled_check->setChecked(b); } QVariant load_data_from_string(int type, const QString& string) { - switch (type) { - case EFFECT_FIELD_DOUBLE: return string.toDouble(); - case EFFECT_FIELD_COLOR: return QColor(string); - case EFFECT_FIELD_BOOL: return (string == "1"); - case EFFECT_FIELD_COMBO: return string.toInt(); - case EFFECT_FIELD_STRING: - case EFFECT_FIELD_FONT: - case EFFECT_FIELD_FILE: - return string; - } - return QVariant(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return string.toDouble(); + case EFFECT_FIELD_COLOR: return QColor(string); + case EFFECT_FIELD_BOOL: return (string == "1"); + case EFFECT_FIELD_COMBO: return string.toInt(); + case EFFECT_FIELD_STRING: + case EFFECT_FIELD_FONT: + case EFFECT_FIELD_FILE: + return string; + } + return QVariant(); } QString save_data_to_string(int type, const QVariant& data) { - switch (type) { - case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); - case EFFECT_FIELD_COLOR: return data.value().name(); - case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); - case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); - case EFFECT_FIELD_STRING: - case EFFECT_FIELD_FONT: - case EFFECT_FIELD_FILE: - return data.toString(); - } - return QString(); + switch (type) { + case EFFECT_FIELD_DOUBLE: return QString::number(data.toDouble()); + case EFFECT_FIELD_COLOR: return data.value().name(); + case EFFECT_FIELD_BOOL: return QString::number(data.toBool()); + case EFFECT_FIELD_COMBO: return QString::number(data.toInt()); + case EFFECT_FIELD_STRING: + case EFFECT_FIELD_FONT: + case EFFECT_FIELD_FILE: + return data.toString(); + } + return QString(); } void Effect::load(QXmlStreamReader& stream) { - int row_count = 0; + int row_count = 0; - QString tag = stream.name().toString(); + QString tag = stream.name().toString(); - while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { - stream.readNext(); - if (stream.name() == "row" && stream.isStartElement()) { - if (row_count < rows.size()) { - EffectRow* row = rows.at(row_count); - int field_count = 0; + while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { + stream.readNext(); + if (stream.name() == "row" && stream.isStartElement()) { + if (row_count < rows.size()) { + EffectRow* row = rows.at(row_count); + int field_count = 0; - while (!stream.atEnd() && !(stream.name() == "row" && stream.isEndElement())) { - stream.readNext(); + while (!stream.atEnd() && !(stream.name() == "row" && stream.isEndElement())) { + stream.readNext(); - // read field - if (stream.name() == "field" && stream.isStartElement()) { - if (field_count < row->fieldCount()) { - // match field using ID - int field_number = field_count; - for (int k=0;kfieldCount();l++) { - if (row->field(l)->id == attr.value()) { - field_number = l; + // read field + if (stream.name() == "field" && stream.isStartElement()) { + if (field_count < row->fieldCount()) { + // match field using ID + int field_number = field_count; + for (int k=0;kfieldCount();l++) { + if (row->field(l)->id == attr.value()) { + field_number = l; // qInfo() << "Found field by ID"; - break; - } - } - break; - } - } + break; + } + } + break; + } + } - EffectField* field = row->field(field_number); + EffectField* field = row->field(field_number); - // get current field value - for (int k=0;kset_current_data(load_data_from_string(field->type, attr.value().toString())); - break; - } - } + // get current field value + for (int k=0;kset_current_data(load_data_from_string(field->type, attr.value().toString())); + break; + } + } - while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { - stream.readNext(); + while (!stream.atEnd() && !(stream.name() == "field" && stream.isEndElement())) { + stream.readNext(); - // read keyframes - if (stream.name() == "key" && stream.isStartElement()) { - row->setKeyframing(true); + // read keyframes + if (stream.name() == "key" && stream.isStartElement()) { + row->setKeyframing(true); - EffectKeyframe key; - for (int k=0;ktype, attr.value().toString()); - } else if (attr.name() == "frame") { - key.time = attr.value().toLong(); - } else if (attr.name() == "type") { - key.type = attr.value().toInt(); - } else if (attr.name() == "prehx") { - key.pre_handle_x = attr.value().toDouble(); - } else if (attr.name() == "prehy") { - key.pre_handle_y = attr.value().toDouble(); - } else if (attr.name() == "posthx") { - key.post_handle_x = attr.value().toDouble(); - } else if (attr.name() == "posthy") { - key.post_handle_y = attr.value().toDouble(); - } - } - field->keyframes.append(key); - } - } - } else { - qCritical() << "Too many fields for effect" << id << "row" << row_count << ". Project might be corrupt. (Got" << field_count << ", expected <" << row->fieldCount()-1 << ")"; - } - field_count++; - } - } + EffectKeyframe key; + for (int k=0;ktype, attr.value().toString()); + } else if (attr.name() == "frame") { + key.time = attr.value().toLong(); + } else if (attr.name() == "type") { + key.type = attr.value().toInt(); + } else if (attr.name() == "prehx") { + key.pre_handle_x = attr.value().toDouble(); + } else if (attr.name() == "prehy") { + key.pre_handle_y = attr.value().toDouble(); + } else if (attr.name() == "posthx") { + key.post_handle_x = attr.value().toDouble(); + } else if (attr.name() == "posthy") { + key.post_handle_y = attr.value().toDouble(); + } + } + field->keyframes.append(key); + } + } + } else { + qCritical() << "Too many fields for effect" << id << "row" << row_count << ". Project might be corrupt. (Got" << field_count << ", expected <" << row->fieldCount()-1 << ")"; + } + field_count++; + } + } - } else { - qCritical() << "Too many rows for effect" << id << ". Project might be corrupt. (Got" << row_count << ", expected <" << rows.size()-1 << ")"; - } - row_count++; - } else if (stream.isStartElement()) { - custom_load(stream); - } - } + } else { + qCritical() << "Too many rows for effect" << id << ". Project might be corrupt. (Got" << row_count << ", expected <" << rows.size()-1 << ")"; + } + row_count++; + } else if (stream.isStartElement()) { + custom_load(stream); + } + } } void Effect::custom_load(QXmlStreamReader &) {} void Effect::save(QXmlStreamWriter& stream) { - stream.writeAttribute("name", meta->category + "/" + meta->name); - stream.writeAttribute("enabled", QString::number(is_enabled())); + stream.writeAttribute("name", meta->category + "/" + meta->name); + stream.writeAttribute("enabled", QString::number(is_enabled())); - for (int i=0;isavable) { - stream.writeStartElement("row"); // row - for (int j=0;jfieldCount();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;kkeyframes.size();k++) { - const EffectKeyframe& key = field->keyframes.at(k); - stream.writeStartElement("key"); - stream.writeAttribute("value", save_data_to_string(field->type, key.data)); - stream.writeAttribute("frame", QString::number(key.time)); - stream.writeAttribute("type", QString::number(key.type)); - stream.writeAttribute("prehx", QString::number(key.pre_handle_x)); - stream.writeAttribute("prehy", QString::number(key.pre_handle_y)); - stream.writeAttribute("posthx", QString::number(key.post_handle_x)); - stream.writeAttribute("posthy", QString::number(key.post_handle_y)); - stream.writeEndElement(); // key - } - stream.writeEndElement(); // field - } - stream.writeEndElement(); // row - } - } + for (int i=0;isavable) { + stream.writeStartElement("row"); // row + for (int j=0;jfieldCount();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;kkeyframes.size();k++) { + const EffectKeyframe& key = field->keyframes.at(k); + stream.writeStartElement("key"); + stream.writeAttribute("value", save_data_to_string(field->type, key.data)); + stream.writeAttribute("frame", QString::number(key.time)); + stream.writeAttribute("type", QString::number(key.type)); + stream.writeAttribute("prehx", QString::number(key.pre_handle_x)); + stream.writeAttribute("prehy", QString::number(key.pre_handle_y)); + stream.writeAttribute("posthx", QString::number(key.post_handle_x)); + stream.writeAttribute("posthy", QString::number(key.post_handle_y)); + stream.writeEndElement(); // key + } + stream.writeEndElement(); // field + } + stream.writeEndElement(); // row + } + } } void Effect::load_from_string(const QByteArray &s) { - // clear existing keyframe data - for (int i=0;isetKeyframing(false); - for (int j=0;jfieldCount();j++) { - EffectField* field = row->field(j); - field->keyframes.clear(); - } - } + // clear existing keyframe data + for (int i=0;isetKeyframing(false); + for (int j=0;jfieldCount();j++) { + EffectField* field = row->field(j); + field->keyframes.clear(); + } + } - // write settings with xml writer - QXmlStreamReader stream(s); + // write settings with xml writer + QXmlStreamReader stream(s); - while (!stream.atEnd()) { - stream.readNext(); + while (!stream.atEnd()) { + stream.readNext(); - // find the effect opening tag - if (stream.name() == "effect" && stream.isStartElement()) { + // find the effect opening tag + if (stream.name() == "effect" && stream.isStartElement()) { - // check the name to see if it matches this effect - const QXmlStreamAttributes& attributes = stream.attributes(); - for (int i=0;ipath.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; - QList effects_paths = get_effects_paths(); - const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; - for (int i=0;ipath.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; + QList effects_paths = get_effects_paths(); + const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; + for (int i=0;iaddShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { - qInfo() << "Vertex shader added successfully"; - } else { - glsl_compiled = false; - qWarning() << "Vertex shader could not be added"; - } - } - if (!fragPath.isEmpty()) { - if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { - qInfo() << "Fragment shader added successfully"; - } else { - glsl_compiled = false; - qWarning() << "Fragment shader could not be added"; - } - } - if (glsl_compiled) { - if (glslProgram->link()) { - qInfo() << "Shader program linked successfully"; - } else { - qWarning() << "Shader program failed to link"; - } - } - isOpen = true; - } - } else { - isOpen = true; - } + if (isOpen) { + qWarning() << "Tried to open an effect that was already open"; + close(); + } + if (olive::CurrentRuntimeConfig.shaders_are_enabled && enable_shader) { + if (QOpenGLContext::currentContext() == nullptr) { + qWarning() << "No current context to create a shader program for - will retry next repaint"; + } else { + glslProgram = new QOpenGLShaderProgram(); + validate_meta_path(); + bool glsl_compiled = true; + if (!vertPath.isEmpty()) { + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { + qInfo() << "Vertex shader added successfully"; + } else { + glsl_compiled = false; + qWarning() << "Vertex shader could not be added"; + } + } + if (!fragPath.isEmpty()) { + if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { + qInfo() << "Fragment shader added successfully"; + } else { + glsl_compiled = false; + qWarning() << "Fragment shader could not be added"; + } + } + if (glsl_compiled) { + if (glslProgram->link()) { + qInfo() << "Shader program linked successfully"; + } else { + qWarning() << "Shader program failed to link"; + } + } + isOpen = true; + } + } else { + isOpen = true; + } } void Effect::close() { - if (!isOpen) { - qWarning() << "Tried to close an effect that was already closed"; - } - delete_texture(); - if (glslProgram != nullptr) { - delete glslProgram; - glslProgram = nullptr; - } - isOpen = false; + if (!isOpen) { + qWarning() << "Tried to close an effect that was already closed"; + } + delete_texture(); + if (glslProgram != nullptr) { + delete glslProgram; + glslProgram = nullptr; + } + isOpen = false; } bool Effect::is_glsl_linked() { - return glslProgram != nullptr && glslProgram->isLinked(); + return glslProgram != nullptr && glslProgram->isLinked(); } void Effect::startEffect() { - if (!isOpen) { - open(); - qWarning() << "Tried to start a closed effect - opening"; - } - if (olive::CurrentRuntimeConfig.shaders_are_enabled - && enable_shader - && glslProgram->isLinked()) { - bound = glslProgram->bind(); - } + if (!isOpen) { + open(); + qWarning() << "Tried to start a closed effect - opening"; + } + if (olive::CurrentRuntimeConfig.shaders_are_enabled + && enable_shader + && glslProgram->isLinked()) { + bound = glslProgram->bind(); + } } void Effect::endEffect() { - if (bound) glslProgram->release(); - bound = false; + if (bound) glslProgram->release(); + bound = false; } int Effect::getIterations() { - return iterations; + return iterations; } void Effect::setIterations(int i) { - iterations = i; + iterations = i; } void Effect::process_image(double, uint8_t *, uint8_t *, int){} EffectPtr Effect::copy(Clip *c) { EffectPtr copy = Effect::Create(c, meta); - copy->set_enabled(is_enabled()); - copy_field_keyframes(copy); - return copy; + copy->set_enabled(is_enabled()); + copy_field_keyframes(copy); + return copy; } void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { glslProgram->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height()); - glslProgram->setUniformValue("time", GLfloat(timecode)); - glslProgram->setUniformValue("iteration", iteration); + glslProgram->setUniformValue("time", GLfloat(timecode)); + glslProgram->setUniformValue("iteration", iteration); - for (int i=0;ifieldCount();j++) { - EffectField* field = row->field(j); - if (!field->id.isEmpty()) { - switch (field->type) { - case EFFECT_FIELD_DOUBLE: - glslProgram->setUniformValue(field->id.toUtf8().constData(), GLfloat(field->get_double_value(timecode))); - break; - case EFFECT_FIELD_COLOR: - glslProgram->setUniformValue( - field->id.toUtf8().constData(), - GLfloat(field->get_color_value(timecode).redF()), - GLfloat(field->get_color_value(timecode).greenF()), - GLfloat(field->get_color_value(timecode).blueF()) - ); - break; - case EFFECT_FIELD_STRING: break; // can you even send a string to a uniform value? - case EFFECT_FIELD_BOOL: - glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_bool_value(timecode)); - break; - case EFFECT_FIELD_COMBO: - glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_combo_index(timecode)); - break; - case EFFECT_FIELD_FONT: break; // can you even send a string to a uniform value? - case EFFECT_FIELD_FILE: break; // can you even send a string to a uniform value? - } - } - } - } + for (int i=0;ifieldCount();j++) { + EffectField* field = row->field(j); + if (!field->id.isEmpty()) { + switch (field->type) { + case EFFECT_FIELD_DOUBLE: + glslProgram->setUniformValue(field->id.toUtf8().constData(), GLfloat(field->get_double_value(timecode))); + break; + case EFFECT_FIELD_COLOR: + glslProgram->setUniformValue( + field->id.toUtf8().constData(), + GLfloat(field->get_color_value(timecode).redF()), + GLfloat(field->get_color_value(timecode).greenF()), + GLfloat(field->get_color_value(timecode).blueF()) + ); + break; + case EFFECT_FIELD_STRING: break; // can you even send a string to a uniform value? + case EFFECT_FIELD_BOOL: + glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_bool_value(timecode)); + break; + case EFFECT_FIELD_COMBO: + glslProgram->setUniformValue(field->id.toUtf8().constData(), field->get_combo_index(timecode)); + break; + case EFFECT_FIELD_FONT: break; // can you even send a string to a uniform value? + case EFFECT_FIELD_FILE: break; // can you even send a string to a uniform value? + } + } + } + } } void Effect::process_coords(double, GLTextureCoords&, int) {} GLuint Effect::process_superimpose(double timecode) { - bool dimensions_changed = false; - bool redrew_image = false; + bool dimensions_changed = false; + bool redrew_image = false; int width = parent_clip->media_width(); int height = parent_clip->media_height(); - if (width != img.width() || height != img.height()) { - img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied); - dimensions_changed = true; - } + if (width != img.width() || height != img.height()) { + img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied); + dimensions_changed = true; + } - if (valueHasChanged(timecode) || dimensions_changed || enable_always_update) { - redraw(timecode); - redrew_image = true; - } + if (valueHasChanged(timecode) || dimensions_changed || enable_always_update) { + redraw(timecode); + redrew_image = true; + } - if (texture == nullptr || texture->width() != img.width() || texture->height() != img.height()) { - delete_texture(); + if (texture == nullptr || texture->width() != img.width() || texture->height() != img.height()) { + delete_texture(); - texture = new QOpenGLTexture(QOpenGLTexture::Target2D); - texture->setSize(img.width(), img.height()); - texture->setFormat(QOpenGLTexture::RGBA8_UNorm); - texture->setMipLevels(texture->maximumMipLevels()); - texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + texture->setSize(img.width(), img.height()); + texture->setFormat(QOpenGLTexture::RGBA8_UNorm); + texture->setMipLevels(texture->maximumMipLevels()); + texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); + texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); - redrew_image = true; - } + redrew_image = true; + } - if (redrew_image) { - texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, img.constBits()); - } + if (redrew_image) { + texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, img.constBits()); + } - return texture->textureId(); + return texture->textureId(); } void Effect::process_audio(double, double, quint8*, int, int) {} @@ -942,158 +943,158 @@ void Effect::process_audio(double, double, quint8*, int, int) {} void Effect::gizmo_draw(double, GLTextureCoords &) {} void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) { - for (int i=0;ix_field1 != nullptr) { - gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); - gizmo->x_field1->make_key_from_change(ca); - } - if (gizmo->y_field1 != nullptr) { - gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); - gizmo->y_field1->make_key_from_change(ca); - } - if (gizmo->x_field2 != nullptr) { - gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); - gizmo->x_field2->make_key_from_change(ca); - } - if (gizmo->y_field2 != nullptr) { - gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); - gizmo->y_field2->make_key_from_change(ca); - } - if (done) olive::UndoStack.push(ca); - break; - } - } + for (int i=0;ix_field1 != nullptr) { + gizmo->x_field1->set_double_value(gizmo->x_field1->get_double_value(timecode) + x_movement*gizmo->x_field_multi1); + gizmo->x_field1->make_key_from_change(ca); + } + if (gizmo->y_field1 != nullptr) { + gizmo->y_field1->set_double_value(gizmo->y_field1->get_double_value(timecode) + y_movement*gizmo->y_field_multi1); + gizmo->y_field1->make_key_from_change(ca); + } + if (gizmo->x_field2 != nullptr) { + gizmo->x_field2->set_double_value(gizmo->x_field2->get_double_value(timecode) + x_movement*gizmo->x_field_multi2); + gizmo->x_field2->make_key_from_change(ca); + } + if (gizmo->y_field2 != nullptr) { + gizmo->y_field2->set_double_value(gizmo->y_field2->get_double_value(timecode) + y_movement*gizmo->y_field_multi2); + gizmo->y_field2->make_key_from_change(ca); + } + if (done) olive::UndoStack.push(ca); + break; + } + } } void Effect::gizmo_world_to_screen() { - GLfloat view_val[16]; - GLfloat projection_val[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, view_val); - glGetFloatv(GL_PROJECTION_MATRIX, projection_val); + GLfloat view_val[16]; + GLfloat projection_val[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, view_val); + glGetFloatv(GL_PROJECTION_MATRIX, projection_val); - QMatrix4x4 view_matrix(view_val); - QMatrix4x4 projection_matrix(projection_val); + QMatrix4x4 view_matrix(view_val); + QMatrix4x4 projection_matrix(projection_val); - for (int i=0;iget_point_count();j++) { - QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); + for (int j=0;jget_point_count();j++) { + QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); - int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); - int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); + int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); + int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); - g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); - } - } + g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); + } + } } bool Effect::are_gizmos_enabled() { - return (gizmos.size() > 0); + return (gizmos.size() > 0); } void Effect::redraw(double) { - /* - // run javascript - QPainter p(&img); - painter_wrapper.img = &img; - painter_wrapper.painter = &p; + /* + // run javascript + QPainter p(&img); + painter_wrapper.img = &img; + painter_wrapper.painter = &p; - jsEngine.globalObject().setProperty("painter", wrapper_obj); + jsEngine.globalObject().setProperty("painter", wrapper_obj); jsEngine.globalObject().setProperty("width", parent_clip->media_width()); jsEngine.globalObject().setProperty("height", parent_clip->media_height()); - for (int i=0;ifieldCount();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; - } - } - } - } + for (int i=0;ifieldCount();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); - */ + jsEngine.evaluate(script); + */ } bool Effect::valueHasChanged(double timecode) { - if (cachedValues.size() == 0) { - for (int i=0;ifieldCount();j++) { - cachedValues.append(crow->field(j)->get_current_data()); - } - } - return true; - } else { - bool changed = false; - int index = 0; - for (int i=0;ifieldCount();j++) { - EffectField* field = crow->field(j); - field->validate_keyframe_data(timecode); - if (cachedValues.at(index) != field->get_current_data()) { - changed = true; - } - cachedValues[index] = field->get_current_data(); - index++; - } - } - return changed; - } + if (cachedValues.size() == 0) { + for (int i=0;ifieldCount();j++) { + cachedValues.append(crow->field(j)->get_current_data()); + } + } + return true; + } else { + bool changed = false; + int index = 0; + for (int i=0;ifieldCount();j++) { + EffectField* field = crow->field(j); + field->validate_keyframe_data(timecode); + if (cachedValues.at(index) != field->get_current_data()) { + changed = true; + } + cachedValues[index] = field->get_current_data(); + index++; + } + } + return changed; + } } void Effect::delete_texture() { - if (texture != nullptr) { - delete texture; - texture = nullptr; - } + if (texture != nullptr) { + delete texture; + texture = nullptr; + } } const EffectMeta* get_meta_from_name(const QString& input) { - int split_index = input.indexOf('/'); - QString category; - if (split_index > -1) { - category = input.left(split_index); - } - QString name = input.mid(split_index + 1); + int split_index = input.indexOf('/'); + QString category; + if (split_index > -1) { + category = input.left(split_index); + } + QString name = input.mid(split_index + 1); - for (int j=0;j(a) + static_cast(b); - mixed_sample = qMax(qMin(mixed_sample, static_cast(INT16_MAX)), static_cast(INT16_MIN)); - return static_cast(mixed_sample); + qint32 mixed_sample = static_cast(a) + static_cast(b); + mixed_sample = qMax(qMin(mixed_sample, static_cast(INT16_MAX)), static_cast(INT16_MIN)); + return static_cast(mixed_sample); } diff --git a/project/effect.h b/project/effect.h index 64224ec97..91600f1d3 100644 --- a/project/effect.h +++ b/project/effect.h @@ -58,7 +58,16 @@ struct EffectMeta { int type; int subtype; }; -extern QVector effects; + +struct BlendMode { + QString name; + QString url; +}; + +namespace olive { + extern QVector effects; + extern QVector blend_modes; +} double log_volume(double linear); @@ -94,36 +103,6 @@ enum EffectInternal { EFFECT_INTERNAL_COUNT }; -enum EffectBlendMode { - BLEND_MODE_ADD, - BLEND_MODE_AVERAGE, - BLEND_MODE_COLORBURN, - BLEND_MODE_COLORDODGE, - BLEND_MODE_DARKEN, - BLEND_MODE_DIFFERENCE, - BLEND_MODE_EXCLUSION, - BLEND_MODE_GLOW, - BLEND_MODE_HARDLIGHT, - BLEND_MODE_HARDMIX, - BLEND_MODE_LIGHTEN, - BLEND_MODE_LINEARBURN, - BLEND_MODE_LINEARDODGE, - BLEND_MODE_LINEARLIGHT, - BLEND_MODE_MULTIPLY, - BLEND_MODE_NEGATION, - BLEND_MODE_NORMAL, - BLEND_MODE_OVERLAY, - BLEND_MODE_PHOENIX, - BLEND_MODE_PINLIGHT, - BLEND_MODE_REFLECT, - BLEND_MODE_SCREEN, - BLEND_MODE_SOFTLIGHT, - BLEND_MODE_SUBSTRACT, - BLEND_MODE_SUBTRACT, - BLEND_MODE_VIVIDLIGHT, - BLEND_MODE_COUNT -}; - struct GLTextureCoords { int grid_size; diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 443caec1e..4162a2a6b 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -39,233 +39,233 @@ typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); #endif void load_internal_effects() { - if (!olive::CurrentRuntimeConfig.shaders_are_enabled) qWarning() << "Shaders are disabled, some effects may be nonfunctional"; + if (!olive::CurrentRuntimeConfig.shaders_are_enabled) qWarning() << "Shaders are disabled, some effects may be nonfunctional"; - EffectMeta em; + EffectMeta em; - // load internal effects - em.path = ":/internalshaders"; + // load internal effects + em.path = ":/internalshaders"; - em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_AUDIO; + em.type = EFFECT_TYPE_EFFECT; + em.subtype = EFFECT_TYPE_AUDIO; - em.name = "Volume"; - em.internal = EFFECT_INTERNAL_VOLUME; - effects.append(em); + em.name = "Volume"; + em.internal = EFFECT_INTERNAL_VOLUME; + olive::effects.append(em); - em.name = "Pan"; - em.internal = EFFECT_INTERNAL_PAN; - effects.append(em); + em.name = "Pan"; + em.internal = EFFECT_INTERNAL_PAN; + olive::effects.append(em); #ifndef NOVST - em.name = "VST Plugin 2.x"; - em.internal = EFFECT_INTERNAL_VST; - effects.append(em); + em.name = "VST Plugin 2.x"; + em.internal = EFFECT_INTERNAL_VST; + olive::effects.append(em); #endif - em.name = "Tone"; - em.internal = EFFECT_INTERNAL_TONE; - effects.append(em); + em.name = "Tone"; + em.internal = EFFECT_INTERNAL_TONE; + olive::effects.append(em); - em.name = "Noise"; - em.internal = EFFECT_INTERNAL_NOISE; - effects.append(em); + em.name = "Noise"; + em.internal = EFFECT_INTERNAL_NOISE; + olive::effects.append(em); - em.name = "Fill Left/Right"; - em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; - effects.append(em); + em.name = "Fill Left/Right"; + em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; + olive::effects.append(em); - em.subtype = EFFECT_TYPE_VIDEO; + em.subtype = EFFECT_TYPE_VIDEO; - em.name = "Transform"; - em.category = "Distort"; - em.internal = EFFECT_INTERNAL_TRANSFORM; - effects.append(em); + em.name = "Transform"; + em.category = "Distort"; + em.internal = EFFECT_INTERNAL_TRANSFORM; + olive::effects.append(em); - em.name = "Corner Pin"; - em.internal = EFFECT_INTERNAL_CORNERPIN; - effects.append(em); + em.name = "Corner Pin"; + em.internal = EFFECT_INTERNAL_CORNERPIN; + olive::effects.append(em); - /*em.name = "Mask"; - em.internal = EFFECT_INTERNAL_MASK; - effects.append(em);*/ + /*em.name = "Mask"; + em.internal = EFFECT_INTERNAL_MASK; + olive::effects.append(em);*/ - em.name = "Shake"; - em.internal = EFFECT_INTERNAL_SHAKE; - effects.append(em); + em.name = "Shake"; + em.internal = EFFECT_INTERNAL_SHAKE; + olive::effects.append(em); - em.name = "Text"; - em.category = "Render"; - em.internal = EFFECT_INTERNAL_TEXT; - effects.append(em); + em.name = "Text"; + em.category = "Render"; + em.internal = EFFECT_INTERNAL_TEXT; + olive::effects.append(em); - em.name = "Timecode"; - em.internal = EFFECT_INTERNAL_TIMECODE; - effects.append(em); + em.name = "Timecode"; + em.internal = EFFECT_INTERNAL_TIMECODE; + olive::effects.append(em); - em.name = "Solid"; - em.internal = EFFECT_INTERNAL_SOLID; - effects.append(em); + em.name = "Solid"; + em.internal = EFFECT_INTERNAL_SOLID; + olive::effects.append(em); - // internal transitions - em.type = EFFECT_TYPE_TRANSITION; - em.category = ""; + // internal transitions + em.type = EFFECT_TYPE_TRANSITION; + em.category = ""; - em.name = "Cross Dissolve"; - em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; - effects.append(em); + em.name = "Cross Dissolve"; + em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; + olive::effects.append(em); - em.subtype = EFFECT_TYPE_AUDIO; + em.subtype = EFFECT_TYPE_AUDIO; - em.name = "Linear Fade"; - em.internal = TRANSITION_INTERNAL_LINEARFADE; - effects.append(em); + em.name = "Linear Fade"; + em.internal = TRANSITION_INTERNAL_LINEARFADE; + olive::effects.append(em); - em.name = "Exponential Fade"; - em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE; - effects.append(em); + em.name = "Exponential Fade"; + em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE; + olive::effects.append(em); - em.name = "Logarithmic Fade"; - em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE; - effects.append(em); + em.name = "Logarithmic Fade"; + em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE; + olive::effects.append(em); } void load_shader_effects() { - QList effects_paths = get_effects_paths(); + QList effects_paths = get_effects_paths(); - for (int h=0;h entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files); - for (int i=0;i entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files); + for (int i=0;istart(); + EffectInit* init_thread = new EffectInit(); + QObject::connect(init_thread, SIGNAL(finished()), init_thread, SLOT(deleteLater())); + init_thread->start(); } #ifndef NOFREI0R void load_frei0r_effects_worker(const QString& dir, EffectMeta& em, QVector& loaded_names) { - QDir search_dir(dir); - if (search_dir.exists()) { - QList entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - for (int j=0;j(LibAddress(effect, "f0r_get_plugin_info")); - if (get_info_func != nullptr) { - f0r_plugin_info_t info; - get_info_func(&info); + QDir search_dir(dir); + if (search_dir.exists()) { + QList entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + for (int j=0;j(LibAddress(effect, "f0r_get_plugin_info")); + if (get_info_func != nullptr) { + f0r_plugin_info_t info; + get_info_func(&info); - if (!loaded_names.contains(info.name) - && info.plugin_type == F0R_PLUGIN_TYPE_FILTER - && info.color_model == F0R_COLOR_MODEL_RGBA8888) { - em.name = info.name; - em.path = dir; - em.filename = entry_list.at(j); - em.tooltip = QString("%1\n%2\n%3\n%4").arg(em.name, info.author, info.explanation, em.filename); + if (!loaded_names.contains(info.name) + && info.plugin_type == F0R_PLUGIN_TYPE_FILTER + && info.color_model == F0R_COLOR_MODEL_RGBA8888) { + em.name = info.name; + em.path = dir; + em.filename = entry_list.at(j); + em.tooltip = QString("%1\n%2\n%3\n%4").arg(em.name, info.author, info.explanation, em.filename); - loaded_names.append(em.name); + loaded_names.append(em.name); - effects.append(em); - } + olive::effects.append(em); + } // qDebug() << "Found:" << info.name << "by" << info.author; - } - LibClose(effect); - } + } + LibClose(effect); + } // qDebug() << search_dir.filePath(entry_list.at(j)); - } - } - } + } + } + } } void load_frei0r_effects() { - QList effect_dirs = get_effects_paths(); + QList effect_dirs = get_effects_paths(); - // add defined paths for frei0r plugins on unix + // add defined paths for frei0r plugins on unix #if defined(__APPLE__) || defined(__linux__) || defined(__HAIKU__) - effect_dirs.prepend("/usr/lib/frei0r-1"); - effect_dirs.prepend("/usr/local/lib/frei0r-1"); - effect_dirs.prepend(QDir::homePath() + "/.frei0r-1/lib"); + effect_dirs.prepend("/usr/lib/frei0r-1"); + effect_dirs.prepend("/usr/local/lib/frei0r-1"); + effect_dirs.prepend(QDir::homePath() + "/.frei0r-1/lib"); #endif - QString env_path(qgetenv("FREI0R_PATH")); - if (!env_path.isEmpty()) effect_dirs.append(env_path); + QString env_path(qgetenv("FREI0R_PATH")); + if (!env_path.isEmpty()) effect_dirs.append(env_path); - QVector loaded_names; + QVector loaded_names; - // search for paths - EffectMeta em; - em.category = "Frei0r"; - em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_VIDEO; - em.internal = EFFECT_INTERNAL_FREI0R; + // search for paths + EffectMeta em; + em.category = "Frei0r"; + em.type = EFFECT_TYPE_EFFECT; + em.subtype = EFFECT_TYPE_VIDEO; + em.internal = EFFECT_INTERNAL_FREI0R; - for (int i=0;ieffects_loaded.lock(); + panel_effect_controls->effects_loaded.lock(); } void EffectInit::run() { - qInfo() << "Initializing effects..."; - load_internal_effects(); - load_shader_effects(); + qInfo() << "Initializing effects..."; + load_internal_effects(); + load_shader_effects(); #ifndef NOFREI0R - load_frei0r_effects(); + load_frei0r_effects(); #endif - panel_effect_controls->effects_loaded.unlock(); - qInfo() << "Finished initializing effects"; + panel_effect_controls->effects_loaded.unlock(); + qInfo() << "Finished initializing effects"; } diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 4f6a08990..681897895 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -399,7 +399,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0; coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0; coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1; - coords.blendmode = BLEND_MODE_NORMAL; + coords.blendmode = -1; coords.opacity = 1.0; // if auto-scale is enabled, auto-scale the clip @@ -514,8 +514,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { - // copy front buffer to back buffer (only if we're using blending modes - which we usually will be) - if (!olive::CurrentRuntimeConfig.disable_blending) { + // copy front buffer to back buffer (only if we're using a blending mode) + if (coords.blendmode >= 0) { if (params.nests.size() > 0) { draw_clip(params.ctx, params.nests.last()->fbo[2]->handle(), params.nests.last()->fbo[0]->texture(), true); } else { @@ -533,8 +533,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // bind front buffer as draw buffer params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, final_fbo); - if (olive::CurrentRuntimeConfig.disable_blending) { - // some GPUs don't like the blending shader, so we provide a pure GL fallback here + // Check if we're using a blend mode (< 0 means no blend mode) + if (coords.blendmode < 0) { params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_1); @@ -543,7 +543,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { full_blit(); params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); + } else { + // load background texture into texture unit 0 params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_2); @@ -572,6 +574,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // unbind texture from texture unit 0 params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); + } // unbind framebuffer From 53f3f03e1d0a5319228be4e4fb58945fbcf3d612 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 09:04:31 +1100 Subject: [PATCH 002/133] fixed closing crash after deleting clips --- mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index 7df65207b..c32ce4536 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -946,6 +946,8 @@ void MainWindow::closeEvent(QCloseEvent *e) { panel_footage_viewer->set_main_sequence(); + olive::UndoStack.clear(); + QString data_dir = get_data_path(); QString config_path = get_config_path(); if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { From c056c9d803c159ca6698eec1eb3ab3bcc8456f14 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 09:48:34 +1100 Subject: [PATCH 003/133] auto-generate blending shader --- effects/internal/blending.frag | 40 -- effects/internal/internalshaders.qrc | 1 - effects/multiply.blend | 3 +- project/effect.cpp | 1 + project/effect.h | 10 +- project/effectloaders.cpp | 89 ++++ project/effectloaders.h | 8 + rendering/renderfunctions.h | 650 +++++++++++++-------------- rendering/renderthread.cpp | 2 +- 9 files changed, 429 insertions(+), 375 deletions(-) delete mode 100644 effects/internal/blending.frag diff --git a/effects/internal/blending.frag b/effects/internal/blending.frag deleted file mode 100644 index 8f4dd3201..000000000 --- a/effects/internal/blending.frag +++ /dev/null @@ -1,40 +0,0 @@ -#version 110 - -/* - This main stump is combined with another shader loaded externally to produce a blending mode. - - For a custom blend mode, the function blend() MUST be available, and use the following syntax: - - blend(vec3 base_color, vec3 blend_color, float opacity) - -*/ - -void main(void) { - vec4 bg_color = texture2D(background, vTexCoord); - vec4 fg_color = texture2D(foreground, vTexCoord); - - // blend textures together - vec3 composite = blend(bg_color.rgb, fg_color.rgb); - - if (blendmode == BLEND_MODE_OVERLAY - || blendmode == BLEND_MODE_LIGHTEN - || blendmode == BLEND_MODE_SCREEN - || blendmode == BLEND_MODE_COLORDODGE - || blendmode == BLEND_MODE_LINEARDODGE - || blendmode == BLEND_MODE_ADD - || blendmode == BLEND_MODE_SOFTLIGHT - || blendmode == BLEND_MODE_NEGATION - || blendmode == BLEND_MODE_AVERAGE - || blendmode == BLEND_MODE_REFLECT - || blendmode == BLEND_MODE_EXCLUSION - || blendmode == BLEND_MODE_DIFFERENCE) { - composite *= fg_color.a; - } - - vec4 full_composite = vec4(composite + bg_color.rgb*(1.0-fg_color.a), bg_color.a + fg_color.a); - - full_composite = mix(bg_color, full_composite, opacity); - - // output to color - gl_FragColor = full_composite; -} \ No newline at end of file diff --git a/effects/internal/internalshaders.qrc b/effects/internal/internalshaders.qrc index 39b1a3aee..ae51e0b27 100644 --- a/effects/internal/internalshaders.qrc +++ b/effects/internal/internalshaders.qrc @@ -1,6 +1,5 @@ - blending.frag common.vert cornerpin.frag cornerpin.vert diff --git a/effects/multiply.blend b/effects/multiply.blend index 0da71e49d..55ed2d341 100644 --- a/effects/multiply.blend +++ b/effects/multiply.blend @@ -6,4 +6,5 @@ vec3 blendMultiply(vec3 base, vec3 blend, float opacity) { return (blendMultiply(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendMultiply) \ No newline at end of file +#pragma glslify: export(blendMultiply) +#olive name Multiply \ No newline at end of file diff --git a/project/effect.cpp b/project/effect.cpp index 4cf2bdc22..fab503d07 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -69,6 +69,7 @@ QVector olive::effects; QVector olive::blend_modes; +QString olive::generated_blending_shader; EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { diff --git a/project/effect.h b/project/effect.h index 91600f1d3..9d699d8dd 100644 --- a/project/effect.h +++ b/project/effect.h @@ -62,11 +62,14 @@ struct EffectMeta { struct BlendMode { QString name; QString url; + QString function_name; }; namespace olive { extern QVector effects; extern QVector blend_modes; + + extern QString generated_blending_shader; } double log_volume(double linear); @@ -260,11 +263,4 @@ private: void validate_meta_path(); }; -class EffectInit : public QThread { -public: - EffectInit(); -protected: - void run(); -}; - #endif // EFFECT_H diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 4162a2a6b..0dd3863d1 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -137,6 +137,7 @@ void load_shader_effects() { const QString& effects_path = effects_paths.at(h); QDir effects_dir(effects_path); if (effects_dir.exists()) { + // Load XML metadata for GLSL shader effects QList entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files); for (int i=0;i blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files); + for (int i=0;ieffects_loaded.lock(); } @@ -266,6 +354,7 @@ void EffectInit::run() { #ifndef NOFREI0R load_frei0r_effects(); #endif + GenerateBlendingShader(); panel_effect_controls->effects_loaded.unlock(); qInfo() << "Finished initializing effects"; } diff --git a/project/effectloaders.h b/project/effectloaders.h index 9a4c2a86e..a5370df41 100644 --- a/project/effectloaders.h +++ b/project/effectloaders.h @@ -22,7 +22,15 @@ #define EFFECTLOADERS_H #include +#include void init_effects(); +class EffectInit : public QThread { +public: + EffectInit(); +protected: + void run(); +}; + #endif // EFFECTLOADERS_H diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 3c1f49734..cde662c6d 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -31,375 +31,375 @@ #include "panels/viewer.h" /** - * @brief The ComposeSequenceParams struct - * - * Struct sent to the compose_sequence() function. - */ + * @brief The ComposeSequenceParams struct + * + * Struct sent to the compose_sequence() function. + */ struct ComposeSequenceParams { - /** - * @brief Reference to the Viewer class that's calling compose_sequence() - * - * Primarily used for calling Viewer::play_wake() when appropriate. - */ - Viewer* viewer; + /** + * @brief Reference to the Viewer class that's calling compose_sequence() + * + * Primarily used for calling Viewer::play_wake() when appropriate. + */ + Viewer* viewer; - /** - * @brief The OpenGL context to use while rendering. - * - * For video rendering, this must be a valid OpenGL context. For audio, this variable is never accessed. - * - * \see ComposeSequenceParams::video - */ - QOpenGLContext* ctx; + /** + * @brief The OpenGL context to use while rendering. + * + * For video rendering, this must be a valid OpenGL context. For audio, this variable is never accessed. + * + * \see ComposeSequenceParams::video + */ + QOpenGLContext* ctx; - /** - * @brief The sequence to compose - * - * In addition to clips, sequences also contain the playhead position so compose_sequence() knows which frame - * to render. - */ - SequencePtr seq; + /** + * @brief The sequence to compose + * + * In addition to clips, sequences also contain the playhead position so compose_sequence() knows which frame + * to render. + */ + SequencePtr seq; - /** - * @brief Array to store the nested sequence hierarchy - * - * Should be left empty. This array gets passed around compose_sequence() as it calls itself recursively to - * handle nested sequences. - */ - QVector nests; + /** + * @brief Array to store the nested sequence hierarchy + * + * Should be left empty. This array gets passed around compose_sequence() as it calls itself recursively to + * handle nested sequences. + */ + QVector nests; - /** - * @brief Set compose mode to video or audio - * - * **TRUE** if this function should render video, **FALSE** if this function should render audio. - */ - bool video; + /** + * @brief Set compose mode to video or audio + * + * **TRUE** if this function should render video, **FALSE** if this function should render audio. + */ + bool video; - /** - * @brief Set to the Effect whose gizmos were chosen to be drawn on screen - * - * The currently active Effect that compose_sequence() will update the gizmos of. - */ - Effect* gizmos; + /** + * @brief Set to the Effect whose gizmos were chosen to be drawn on screen + * + * The currently active Effect that compose_sequence() will update the gizmos of. + */ + Effect* gizmos; - /** - * @brief A variable that compose_sequence() will set to **TRUE** if any of the clips couldn't be shown. - * - * A footage item or shader may not be ready at the time this frame is drawn. If compose_sequence() couldn't draw - * any of the clips in the scene, this variable is set to **TRUE** indicating that the image rendered is a - * "best effort", but not the actual image. - * - * This variable should be checked after compose_sequence() and a repaint should be triggered if it's **TRUE**. - * - * \note This variable is probably bad design and is a relic of an earlier rendering backend. There may be a better - * way to communicate this information. - * - * Additionally, since - * compose_sequence() for video will now always run in a separate thread anyway, there's no real issue with - * stalling it to wait for footage to complete opening or whatever may be lagging behind. A possible side effect - * of this though is that the preview may become less responsive if it's stuck trying to render one frame. With - * the current system, the preview may show incomplete frames occasionally but at least it will show something. - * This may be preferable. See ComposeSequenceParams::single_threaded for a similar function that could be - * removed. - */ - bool texture_failed; + /** + * @brief A variable that compose_sequence() will set to **TRUE** if any of the clips couldn't be shown. + * + * A footage item or shader may not be ready at the time this frame is drawn. If compose_sequence() couldn't draw + * any of the clips in the scene, this variable is set to **TRUE** indicating that the image rendered is a + * "best effort", but not the actual image. + * + * This variable should be checked after compose_sequence() and a repaint should be triggered if it's **TRUE**. + * + * \note This variable is probably bad design and is a relic of an earlier rendering backend. There may be a better + * way to communicate this information. + * + * Additionally, since + * compose_sequence() for video will now always run in a separate thread anyway, there's no real issue with + * stalling it to wait for footage to complete opening or whatever may be lagging behind. A possible side effect + * of this though is that the preview may become less responsive if it's stuck trying to render one frame. With + * the current system, the preview may show incomplete frames occasionally but at least it will show something. + * This may be preferable. See ComposeSequenceParams::single_threaded for a similar function that could be + * removed. + */ + bool texture_failed; - /** - * @brief Run all cachers in the same thread that compose_sequence() is in - * - * Standard behavior is that all clips cache frames in their own thread and signals are sent between - * compose_sequence() and the clip's cacher thread regarding which frames to display and cache without stalling - * the compose_sequence() thread. Setting this to **TRUE** will run all cachers in the same thread creating a - * technically more "perfect" connection between them that will also stall the compose_sequence() thread. Used - * when rendering as timing isn't as important as creating output frames as quickly as possible. - * - * \note Exporting should probably be rewritten without this. While running all the cachers in one thread makes - * it easier to synchronize everything, export performance could probably benefit from keeping them in separate - * threads and syncing up with them. See ComposeSequenceParams::texture_failed for a similar function that could - * be removed. - */ - bool wait_for_mutexes; + /** + * @brief Run all cachers in the same thread that compose_sequence() is in + * + * Standard behavior is that all clips cache frames in their own thread and signals are sent between + * compose_sequence() and the clip's cacher thread regarding which frames to display and cache without stalling + * the compose_sequence() thread. Setting this to **TRUE** will run all cachers in the same thread creating a + * technically more "perfect" connection between them that will also stall the compose_sequence() thread. Used + * when rendering as timing isn't as important as creating output frames as quickly as possible. + * + * \note Exporting should probably be rewritten without this. While running all the cachers in one thread makes + * it easier to synchronize everything, export performance could probably benefit from keeping them in separate + * threads and syncing up with them. See ComposeSequenceParams::texture_failed for a similar function that could + * be removed. + */ + bool wait_for_mutexes; - /** - * @brief Set the current playback speed (adjusted with Shuttle Left/Right) - * - * Only used for audio rendering to determine how many samples to skip in order to play audio at the correct speed. - * - * \see ComposeSequenceParams::video - */ - int playback_speed; + /** + * @brief Set the current playback speed (adjusted with Shuttle Left/Right) + * + * Only used for audio rendering to determine how many samples to skip in order to play audio at the correct speed. + * + * \see ComposeSequenceParams::video + */ + int playback_speed; - /** - * @brief Blending mode shader - * - * Used only for video rendering. Never accessed with audio rendering. - * - * A program containing the current active - * blending mode shader that can be bound during rendering. Must be compiled and linked beforehand. See - * RenderThread::blend_mode_program for how this is properly set up. - * - * \see ComposeSequenceParams::video - */ - QOpenGLShaderProgram* blend_mode_program; + /** + * @brief Blending mode shader + * + * Used only for video rendering. Never accessed with audio rendering. + * + * A program containing the current active + * blending mode shader that can be bound during rendering. Must be compiled and linked beforehand. See + * RenderThread::blend_mode_program for how this is properly set up. + * + * \see ComposeSequenceParams::video + */ + QOpenGLShaderProgram* blend_mode_program; - /** - * @brief Premultiply alpha shader - * - * Used only for video rendering. Never accessed with audio rendering. - * - * compose_sequence()'s internal composition - * expects premultipled alpha, but it will pre-emptively multiply any footage that is not set as already - * premultiplied (see Footage::alpha_is_premultiplied) using this shader. Must be compiled and linked beforehand. - * See RenderThread::premultiply_program for how this is properly set up. - */ - QOpenGLShaderProgram* premultiply_program; + /** + * @brief Premultiply alpha shader + * + * Used only for video rendering. Never accessed with audio rendering. + * + * compose_sequence()'s internal composition + * expects premultipled alpha, but it will pre-emptively multiply any footage that is not set as already + * premultiplied (see Footage::alpha_is_premultiplied) using this shader. Must be compiled and linked beforehand. + * See RenderThread::premultiply_program for how this is properly set up. + */ + QOpenGLShaderProgram* premultiply_program; - /** - * @brief The OpenGL framebuffer object that the final texture to be shown is rendered to. - * - * Used only for video rendering. Never accessed with audio rendering. - * - * When compose_sequence() is rendering the final image, this framebuffer will be bound. - */ - GLuint main_buffer; + /** + * @brief The OpenGL framebuffer object that the final texture to be shown is rendered to. + * + * Used only for video rendering. Never accessed with audio rendering. + * + * When compose_sequence() is rendering the final image, this framebuffer will be bound. + */ + GLuint main_buffer; - /** - * @brief The attachment to the framebuffer in main_buffer - * - * Used only for video rendering. Never accessed with audio rendering. - * - * The OpenGL texture attached to the framebuffer referenced by main_buffer. - */ - GLuint main_attachment; + /** + * @brief The attachment to the framebuffer in main_buffer + * + * Used only for video rendering. Never accessed with audio rendering. + * + * The OpenGL texture attached to the framebuffer referenced by main_buffer. + */ + GLuint main_attachment; - /** - * @brief Backend OpenGL framebuffer 1 used for further processing before rendering to main_buffer - * - * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" - * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. - */ - GLuint backend_buffer1; + /** + * @brief Backend OpenGL framebuffer 1 used for further processing before rendering to main_buffer + * + * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" + * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. + */ + GLuint backend_buffer1; - /** - * @brief Backend OpenGL framebuffer 1's texture attachment - * - * The texture that ComposeSequenceParams::backend_buffer1 renders to. Bound and drawn to - * ComposeSequenceParams::backend_buffer2 to "ping-pong" between them and various shaders. - */ - GLuint backend_attachment1; + /** + * @brief Backend OpenGL framebuffer 1's texture attachment + * + * The texture that ComposeSequenceParams::backend_buffer1 renders to. Bound and drawn to + * ComposeSequenceParams::backend_buffer2 to "ping-pong" between them and various shaders. + */ + GLuint backend_attachment1; - /** - * @brief Backend OpenGL framebuffer 2 used for further processing before rendering to main_buffer - * - * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" - * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. - */ - GLuint backend_buffer2; + /** + * @brief Backend OpenGL framebuffer 2 used for further processing before rendering to main_buffer + * + * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" + * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. + */ + GLuint backend_buffer2; - /** - * @brief Backend OpenGL framebuffer 2's texture attachment - * - * The texture that ComposeSequenceParams::backend_buffer2 renders to. Bound and drawn to - * ComposeSequenceParams::backend_buffer1 to "ping-pong" between them and various shaders. - */ - GLuint backend_attachment2; + /** + * @brief Backend OpenGL framebuffer 2's texture attachment + * + * The texture that ComposeSequenceParams::backend_buffer2 renders to. Bound and drawn to + * ComposeSequenceParams::backend_buffer1 to "ping-pong" between them and various shaders. + */ + GLuint backend_attachment2; - /** - * @brief OpenGL shader containing OpenColorIO shader information - */ - QOpenGLShaderProgram* ocio_shader; + /** + * @brief OpenGL shader containing OpenColorIO shader information + */ + QOpenGLShaderProgram* ocio_shader; - /** - * @brief OpenGL texture containing LUT obtained form OpenColorIO - */ - GLuint ocio_lut_texture; + /** + * @brief OpenGL texture containing LUT obtained form OpenColorIO + */ + GLuint ocio_lut_texture; }; /** - * @brief Compose a frame of a given sequence - * - * For any given Sequence, this function will render the current frame indicated by Sequence::playhead. Will - * automatically open and close clips (memory allocation and file handles) as necessary, communicate with the - * Clip::cacher objects to retrieve upcoming frames and store them in memory, run Effect processing functions, and - * finally composite all the currently active clips together into a final texture. - * - * Will sometimes render a frame incomplete or inaccurately, e.g. if a video file hadn't finished opening by the time - * of the render or a clip's cacher didn't have the requested frame available at the time of the render. If so, - * the `texture_failed` variable of `params` will be set to **TRUE**. Check this after calling compose_sequence() and - * if it is **TRUE**, compose_sequence() should be called again later to attempt another render (unless the Sequence - * is being played, in which case just play the next frame rather than redrawing an old frame). - * - * @param params - * - * A struct of parameters to use while rendering. - * - * @return A reference to the OpenGL texture resulting from the render. Will usually be equal to - * ComposeSequenceParams::main_attachment unless it's rendering a nested sequence, in which case it'll be a reference - * to one of the textures referenced by Clip::fbo. Can be used directly to draw the rendered frame. - */ + * @brief Compose a frame of a given sequence + * + * For any given Sequence, this function will render the current frame indicated by Sequence::playhead. Will + * automatically open and close clips (memory allocation and file handles) as necessary, communicate with the + * Clip::cacher objects to retrieve upcoming frames and store them in memory, run Effect processing functions, and + * finally composite all the currently active clips together into a final texture. + * + * Will sometimes render a frame incomplete or inaccurately, e.g. if a video file hadn't finished opening by the time + * of the render or a clip's cacher didn't have the requested frame available at the time of the render. If so, + * the `texture_failed` variable of `params` will be set to **TRUE**. Check this after calling compose_sequence() and + * if it is **TRUE**, compose_sequence() should be called again later to attempt another render (unless the Sequence + * is being played, in which case just play the next frame rather than redrawing an old frame). + * + * @param params + * + * A struct of parameters to use while rendering. + * + * @return A reference to the OpenGL texture resulting from the render. Will usually be equal to + * ComposeSequenceParams::main_attachment unless it's rendering a nested sequence, in which case it'll be a reference + * to one of the textures referenced by Clip::fbo. Can be used directly to draw the rendered frame. + */ GLuint compose_sequence(ComposeSequenceParams ¶ms); /** - * @brief Convenience wrapper function for compose_sequence() to render audio - * - * Much of the functionality provided (and parameters required) by compose_sequence() is only useful/necessary for - * video rendering. For audio rendering, this function is easier to handle and will correctly set up - * compose_sequence() to render audio without the cumbersome effort of setting up a ComposeSequenceParams object. - * - * @param viewer - * - * The Viewer object calling this function - * - * @param seq - * - * The Sequence whose audio to render. - * - * @param playback_speed - * - * The current playback speed (controlled by Shuttle Left/Right) - * - * @param - * - * Whether to wait for media to open or simply fail if the media is not yet open. This should usually be **FALSE**. - */ + * @brief Convenience wrapper function for compose_sequence() to render audio + * + * Much of the functionality provided (and parameters required) by compose_sequence() is only useful/necessary for + * video rendering. For audio rendering, this function is easier to handle and will correctly set up + * compose_sequence() to render audio without the cumbersome effort of setting up a ComposeSequenceParams object. + * + * @param viewer + * + * The Viewer object calling this function + * + * @param seq + * + * The Sequence whose audio to render. + * + * @param playback_speed + * + * The current playback speed (controlled by Shuttle Left/Right) + * + * @param + * + * Whether to wait for media to open or simply fail if the media is not yet open. This should usually be **FALSE**. + */ void compose_audio(Viewer* viewer, SequencePtr seq, int playback_speed, bool wait_for_mutexes); /** - * @brief Rescale a frame number between two frame rates - * - * Converts a frame number from one frame rate to its equivalent in another frame rate - * - * @param framenumber - * - * The frame number to convert - * - * @param source_frame_rate - * - * Frame rate that the frame number is currently in - * - * @param target_frame_rate - * - * Frame rate to convert to - * - * @return - * - * Rescaled frame number - */ + * @brief Rescale a frame number between two frame rates + * + * Converts a frame number from one frame rate to its equivalent in another frame rate + * + * @param framenumber + * + * The frame number to convert + * + * @param source_frame_rate + * + * Frame rate that the frame number is currently in + * + * @param target_frame_rate + * + * Frame rate to convert to + * + * @return + * + * Rescaled frame number + */ long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); /** - * @brief Get timecode - * - * Get the current clip/media time from the Timeline playhead in seconds. For instance if the playhead was at the start - * of a clip (whose in point wasn't trimmed), this would be 0.0 as it's the start of the clip/media; - * - * @param c - * - * Clip to get the timecode of - * - * @param playhead - * - * Sequence playhead to convert to a clip/media timecode - * - * @return - * - * Timecode in seconds - */ -double get_timecode(Clip *c, long playhead); + * @brief Get timecode + * + * Get the current clip/media time from the Timeline playhead in seconds. For instance if the playhead was at the start + * of a clip (whose in point wasn't trimmed), this would be 0.0 as it's the start of the clip/media; + * + * @param c + * + * Clip to get the timecode of + * + * @param playhead + * + * Sequence playhead to convert to a clip/media timecode + * + * @return + * + * Timecode in seconds + */ +double get_timecode(Clip *c, long playhead); /** - * @brief Convert playhead frame number to a clip frame number - * - * Converts a Timeline playhead to a the current clip's frame. Equivalent to - * `PLAYHEAD - CLIP_TIMELINE_IN + CLIP_MEDIA_IN`. All keyframes are in clip frames. - * - * @param c - * - * The clip to get the current frame number of - * - * @param playhead - * - * The current Timeline frame number - * - * @return - * - * The curren frame number of the clip at `playhead` - */ + * @brief Convert playhead frame number to a clip frame number + * + * Converts a Timeline playhead to a the current clip's frame. Equivalent to + * `PLAYHEAD - CLIP_TIMELINE_IN + CLIP_MEDIA_IN`. All keyframes are in clip frames. + * + * @param c + * + * The clip to get the current frame number of + * + * @param playhead + * + * The current Timeline frame number + * + * @return + * + * The curren frame number of the clip at `playhead` + */ long playhead_to_clip_frame(Clip* c, long playhead); /** - * @brief Converts the playhead to clip seconds - * - * Get the current timecode at the playhead in terms of clip seconds. - * - * FIXME: Possible duplicate of get_timecode()? Will need to research this more. - * - * @param c - * - * Clip to return clip seconds of. - * - * @param playhead - * - * Current Timeline playhead to convert to clip seconds - * - * @return - * - * Clip time in seconds - */ -double playhead_to_clip_seconds(Clip *c, long playhead); + * @brief Converts the playhead to clip seconds + * + * Get the current timecode at the playhead in terms of clip seconds. + * + * FIXME: Possible duplicate of get_timecode()? Will need to research this more. + * + * @param c + * + * Clip to return clip seconds of. + * + * @param playhead + * + * Current Timeline playhead to convert to clip seconds + * + * @return + * + * Clip time in seconds + */ +double playhead_to_clip_seconds(Clip *c, long playhead); /** - * @brief Convert seconds to FFmpeg timestamp - * - * Used for interaction with FFmpeg, converts seconds in a floating-point value to a timestamp in AVStream->time_base - * units. - * - * @param c - * - * Clip to get timestamp of - * - * @param seconds - * - * Clip time in seconds - * - * @return - * - * An FFmpeg-compatible timestamp in AVStream->time_base units. - */ + * @brief Convert seconds to FFmpeg timestamp + * + * Used for interaction with FFmpeg, converts seconds in a floating-point value to a timestamp in AVStream->time_base + * units. + * + * @param c + * + * Clip to get timestamp of + * + * @param seconds + * + * Clip time in seconds + * + * @return + * + * An FFmpeg-compatible timestamp in AVStream->time_base units. + */ int64_t seconds_to_timestamp(Clip* c, double seconds); /** - * @brief Convert Timeline playhead to FFmpeg timestamp - * - * Used for interaction with FFmpeg, converts the Timeline playhead to a timestamp in AVStream->time_base - * units. - * - * @param c - * - * Clip to get timestamp of - * - * @param playhead - * - * Timeline playhead to convert to a timestamp - * - * @return - * - * An FFmpeg-compatible timestamp in AVStream->time_base units. - */ -int64_t playhead_to_timestamp(Clip *c, long playhead); + * @brief Convert Timeline playhead to FFmpeg timestamp + * + * Used for interaction with FFmpeg, converts the Timeline playhead to a timestamp in AVStream->time_base + * units. + * + * @param c + * + * Clip to get timestamp of + * + * @param playhead + * + * Timeline playhead to convert to a timestamp + * + * @return + * + * An FFmpeg-compatible timestamp in AVStream->time_base units. + */ +int64_t playhead_to_timestamp(Clip *c, long playhead); /** - * @brief Close all open clips in a Sequence - * - * Closes any currently open clips on a Sequence and waits for them to close before returning. This may be slow as a - * result on large Sequence objects. If a Clip is a nested Sequence, this function calls itself recursively on that - * Sequence too. - * - * @param s - * - * The Sequence to close all clips on. - */ + * @brief Close all open clips in a Sequence + * + * Closes any currently open clips on a Sequence and waits for them to close before returning. This may be slow as a + * result on large Sequence objects. If a Clip is a nested Sequence, this function calls itself recursively on that + * Sequence too. + * + * @param s + * + * The Sequence to close all clips on. + */ void close_active_clips(SequencePtr s); #endif // RENDERFUNCTIONS_H diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 0c3ff96f3..2bfc54006 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -99,7 +99,7 @@ void RenderThread::run() { blend_mode_program = new QOpenGLShaderProgram(); blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert"); - blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/blending.frag"); + blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader); blend_mode_program->link(); premultiply_program = new QOpenGLShaderProgram(); From a0851afc9650a0a354c692be12ce8d5bb20b21f7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 10:10:52 +1100 Subject: [PATCH 004/133] blending modes are loaded modularly now --- effects/add.blend | 13 +++++++++++ effects/average.blend | 9 ++++++++ effects/color-burn.blend | 13 +++++++++++ effects/color-dodge.blend | 13 +++++++++++ effects/darken.blend | 13 +++++++++++ effects/difference.blend | 9 ++++++++ effects/exclusion.blend | 9 ++++++++ effects/glow.blend | 11 ++++++++++ effects/hard-light.blend | 11 ++++++++++ effects/hard-mix.blend | 15 +++++++++++++ effects/internal/transformeffect.cpp | 3 +++ effects/lighten.blend | 13 +++++++++++ effects/linear-burn.blend | 15 +++++++++++++ effects/linear-dodge.blend | 15 +++++++++++++ effects/linear-light.blend | 16 ++++++++++++++ effects/negation.blend | 9 ++++++++ effects/normal.blend | 9 ++++++++ effects/overlay.blend | 13 +++++++++++ effects/phoenix.blend | 9 ++++++++ effects/pin-light.blend | 16 ++++++++++++++ effects/reflect.blend | 13 +++++++++++ effects/screen.blend | 13 +++++++++++ effects/soft-light.blend | 13 +++++++++++ effects/substract.blend | 13 +++++++++++ effects/subtract.blend | 13 +++++++++++ effects/vivid-light.blend | 16 ++++++++++++++ io/loadthread.cpp | 5 +++-- panels/effectcontrols.cpp | 5 +++-- panels/effectcontrols.h | 2 -- project/effectloaders.cpp | 32 ++++++++++++++++++++-------- project/effectloaders.h | 5 +++++ rendering/renderthread.cpp | 3 +++ 32 files changed, 352 insertions(+), 15 deletions(-) create mode 100644 effects/add.blend create mode 100644 effects/average.blend create mode 100644 effects/color-burn.blend create mode 100644 effects/color-dodge.blend create mode 100644 effects/darken.blend create mode 100644 effects/difference.blend create mode 100644 effects/exclusion.blend create mode 100644 effects/glow.blend create mode 100644 effects/hard-light.blend create mode 100644 effects/hard-mix.blend create mode 100644 effects/lighten.blend create mode 100644 effects/linear-burn.blend create mode 100644 effects/linear-dodge.blend create mode 100644 effects/linear-light.blend create mode 100644 effects/negation.blend create mode 100644 effects/normal.blend create mode 100644 effects/overlay.blend create mode 100644 effects/phoenix.blend create mode 100644 effects/pin-light.blend create mode 100644 effects/reflect.blend create mode 100644 effects/screen.blend create mode 100644 effects/soft-light.blend create mode 100644 effects/substract.blend create mode 100644 effects/subtract.blend create mode 100644 effects/vivid-light.blend diff --git a/effects/add.blend b/effects/add.blend new file mode 100644 index 000000000..2615a387f --- /dev/null +++ b/effects/add.blend @@ -0,0 +1,13 @@ +float blendAdd(float base, float blend) { + return min(base+blend,1.0); +} + +vec3 blendAdd(vec3 base, vec3 blend) { + return min(base+blend,vec3(1.0)); +} + +vec3 blendAdd(vec3 base, vec3 blend, float opacity) { + return (blendAdd(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendAdd) \ No newline at end of file diff --git a/effects/average.blend b/effects/average.blend new file mode 100644 index 000000000..b838757bd --- /dev/null +++ b/effects/average.blend @@ -0,0 +1,9 @@ +vec3 blendAverage(vec3 base, vec3 blend) { + return (base+blend)/2.0; +} + +vec3 blendAverage(vec3 base, vec3 blend, float opacity) { + return (blendAverage(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendAverage) \ No newline at end of file diff --git a/effects/color-burn.blend b/effects/color-burn.blend new file mode 100644 index 000000000..e524511b5 --- /dev/null +++ b/effects/color-burn.blend @@ -0,0 +1,13 @@ +float blendColorBurn(float base, float blend) { + return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0); +} + +vec3 blendColorBurn(vec3 base, vec3 blend) { + return vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b)); +} + +vec3 blendColorBurn(vec3 base, vec3 blend, float opacity) { + return (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendColorBurn) \ No newline at end of file diff --git a/effects/color-dodge.blend b/effects/color-dodge.blend new file mode 100644 index 000000000..38f9fc033 --- /dev/null +++ b/effects/color-dodge.blend @@ -0,0 +1,13 @@ +float blendColorDodge(float base, float blend) { + return (blend==1.0)?blend:min(base/(1.0-blend),1.0); +} + +vec3 blendColorDodge(vec3 base, vec3 blend) { + return vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b)); +} + +vec3 blendColorDodge(vec3 base, vec3 blend, float opacity) { + return (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendColorDodge) \ No newline at end of file diff --git a/effects/darken.blend b/effects/darken.blend new file mode 100644 index 000000000..2c74f2911 --- /dev/null +++ b/effects/darken.blend @@ -0,0 +1,13 @@ +float blendDarken(float base, float blend) { + return min(blend,base); +} + +vec3 blendDarken(vec3 base, vec3 blend) { + return vec3(blendDarken(base.r,blend.r),blendDarken(base.g,blend.g),blendDarken(base.b,blend.b)); +} + +vec3 blendDarken(vec3 base, vec3 blend, float opacity) { + return (blendDarken(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendDarken) \ No newline at end of file diff --git a/effects/difference.blend b/effects/difference.blend new file mode 100644 index 000000000..88810665b --- /dev/null +++ b/effects/difference.blend @@ -0,0 +1,9 @@ +vec3 blendDifference(vec3 base, vec3 blend) { + return abs(base-blend); +} + +vec3 blendDifference(vec3 base, vec3 blend, float opacity) { + return (blendDifference(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendDifference) \ No newline at end of file diff --git a/effects/exclusion.blend b/effects/exclusion.blend new file mode 100644 index 000000000..3eb0b9dfc --- /dev/null +++ b/effects/exclusion.blend @@ -0,0 +1,9 @@ +vec3 blendExclusion(vec3 base, vec3 blend) { + return base+blend-2.0*base*blend; +} + +vec3 blendExclusion(vec3 base, vec3 blend, float opacity) { + return (blendExclusion(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendExclusion) \ No newline at end of file diff --git a/effects/glow.blend b/effects/glow.blend new file mode 100644 index 000000000..ec087790d --- /dev/null +++ b/effects/glow.blend @@ -0,0 +1,11 @@ +#pragma glslify: blendReflect = require(./reflect) + +vec3 blendGlow(vec3 base, vec3 blend) { + return blendReflect(blend,base); +} + +vec3 blendGlow(vec3 base, vec3 blend, float opacity) { + return (blendGlow(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendGlow) \ No newline at end of file diff --git a/effects/hard-light.blend b/effects/hard-light.blend new file mode 100644 index 000000000..4a376d2ec --- /dev/null +++ b/effects/hard-light.blend @@ -0,0 +1,11 @@ +#pragma glslify: blendOverlay = require(./overlay) + +vec3 blendHardLight(vec3 base, vec3 blend) { + return blendOverlay(blend,base); +} + +vec3 blendHardLight(vec3 base, vec3 blend, float opacity) { + return (blendHardLight(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendHardLight) \ No newline at end of file diff --git a/effects/hard-mix.blend b/effects/hard-mix.blend new file mode 100644 index 000000000..3ce420ef9 --- /dev/null +++ b/effects/hard-mix.blend @@ -0,0 +1,15 @@ +#pragma glslify: blendVividLight = require(./vivid-light) + +float blendHardMix(float base, float blend) { + return (blendVividLight(base,blend)<0.5)?0.0:1.0; +} + +vec3 blendHardMix(vec3 base, vec3 blend) { + return vec3(blendHardMix(base.r,blend.r),blendHardMix(base.g,blend.g),blendHardMix(base.b,blend.b)); +} + +vec3 blendHardMix(vec3 base, vec3 blend, float opacity) { + return (blendHardMix(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendHardMix) \ No newline at end of file diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index f4065ba1e..84216f2ab 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -36,6 +36,7 @@ #include "io/math.h" #include "ui/labelslider.h" #include "ui/comboboxex.h" +#include "project/effectloaders.h" #include "panels/project.h" #include "debug.h" @@ -78,9 +79,11 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) blend_mode_box->add_combo_item(tr("Normal"), -1); // add loaded blending modes + olive::effects_loaded.lock(); for (int i=0;iadd_combo_item(olive::blend_modes.at(i).name, i); } + olive::effects_loaded.unlock(); // set up gizmos top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); diff --git a/effects/lighten.blend b/effects/lighten.blend new file mode 100644 index 000000000..2ffaf9616 --- /dev/null +++ b/effects/lighten.blend @@ -0,0 +1,13 @@ +float blendLighten(float base, float blend) { + return max(blend,base); +} + +vec3 blendLighten(vec3 base, vec3 blend) { + return vec3(blendLighten(base.r,blend.r),blendLighten(base.g,blend.g),blendLighten(base.b,blend.b)); +} + +vec3 blendLighten(vec3 base, vec3 blend, float opacity) { + return (blendLighten(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendLighten) \ No newline at end of file diff --git a/effects/linear-burn.blend b/effects/linear-burn.blend new file mode 100644 index 000000000..9c9ec26c1 --- /dev/null +++ b/effects/linear-burn.blend @@ -0,0 +1,15 @@ +float blendLinearBurn(float base, float blend) { + // Note : Same implementation as BlendSubtractf + return max(base+blend-1.0,0.0); +} + +vec3 blendLinearBurn(vec3 base, vec3 blend) { + // Note : Same implementation as BlendSubtract + return max(base+blend-vec3(1.0),vec3(0.0)); +} + +vec3 blendLinearBurn(vec3 base, vec3 blend, float opacity) { + return (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendLinearBurn) \ No newline at end of file diff --git a/effects/linear-dodge.blend b/effects/linear-dodge.blend new file mode 100644 index 000000000..147418f58 --- /dev/null +++ b/effects/linear-dodge.blend @@ -0,0 +1,15 @@ +float blendLinearDodge(float base, float blend) { + // Note : Same implementation as BlendAddf + return min(base+blend,1.0); +} + +vec3 blendLinearDodge(vec3 base, vec3 blend) { + // Note : Same implementation as BlendAdd + return min(base+blend,vec3(1.0)); +} + +vec3 blendLinearDodge(vec3 base, vec3 blend, float opacity) { + return (blendLinearDodge(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendLinearDodge) \ No newline at end of file diff --git a/effects/linear-light.blend b/effects/linear-light.blend new file mode 100644 index 000000000..a66b56c65 --- /dev/null +++ b/effects/linear-light.blend @@ -0,0 +1,16 @@ +#pragma glslify: blendLinearDodge = require(./linear-dodge) +#pragma glslify: blendLinearBurn = require(./linear-burn) + +float blendLinearLight(float base, float blend) { + return blend<0.5?blendLinearBurn(base,(2.0*blend)):blendLinearDodge(base,(2.0*(blend-0.5))); +} + +vec3 blendLinearLight(vec3 base, vec3 blend) { + return vec3(blendLinearLight(base.r,blend.r),blendLinearLight(base.g,blend.g),blendLinearLight(base.b,blend.b)); +} + +vec3 blendLinearLight(vec3 base, vec3 blend, float opacity) { + return (blendLinearLight(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendLinearLight) \ No newline at end of file diff --git a/effects/negation.blend b/effects/negation.blend new file mode 100644 index 000000000..03d5e478b --- /dev/null +++ b/effects/negation.blend @@ -0,0 +1,9 @@ +vec3 blendNegation(vec3 base, vec3 blend) { + return vec3(1.0)-abs(vec3(1.0)-base-blend); +} + +vec3 blendNegation(vec3 base, vec3 blend, float opacity) { + return (blendNegation(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendNegation) \ No newline at end of file diff --git a/effects/normal.blend b/effects/normal.blend new file mode 100644 index 000000000..f66aa9b13 --- /dev/null +++ b/effects/normal.blend @@ -0,0 +1,9 @@ +vec3 blendNormal(vec3 base, vec3 blend) { + return blend; +} + +vec3 blendNormal(vec3 base, vec3 blend, float opacity) { + return (blendNormal(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendNormal) \ No newline at end of file diff --git a/effects/overlay.blend b/effects/overlay.blend new file mode 100644 index 000000000..a5d0aba85 --- /dev/null +++ b/effects/overlay.blend @@ -0,0 +1,13 @@ +float blendOverlay(float base, float blend) { + return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); +} + +vec3 blendOverlay(vec3 base, vec3 blend) { + return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b)); +} + +vec3 blendOverlay(vec3 base, vec3 blend, float opacity) { + return (blendOverlay(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendOverlay) \ No newline at end of file diff --git a/effects/phoenix.blend b/effects/phoenix.blend new file mode 100644 index 000000000..eb16f2c4e --- /dev/null +++ b/effects/phoenix.blend @@ -0,0 +1,9 @@ +vec3 blendPhoenix(vec3 base, vec3 blend) { + return min(base,blend)-max(base,blend)+vec3(1.0); +} + +vec3 blendPhoenix(vec3 base, vec3 blend, float opacity) { + return (blendPhoenix(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendPhoenix) \ No newline at end of file diff --git a/effects/pin-light.blend b/effects/pin-light.blend new file mode 100644 index 000000000..04e4ae7c6 --- /dev/null +++ b/effects/pin-light.blend @@ -0,0 +1,16 @@ +#pragma glslify: blendLighten = require(./lighten) +#pragma glslify: blendDarken = require(./darken) + +float blendPinLight(float base, float blend) { + return (blend<0.5)?blendDarken(base,(2.0*blend)):blendLighten(base,(2.0*(blend-0.5))); +} + +vec3 blendPinLight(vec3 base, vec3 blend) { + return vec3(blendPinLight(base.r,blend.r),blendPinLight(base.g,blend.g),blendPinLight(base.b,blend.b)); +} + +vec3 blendPinLight(vec3 base, vec3 blend, float opacity) { + return (blendPinLight(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendPinLight) \ No newline at end of file diff --git a/effects/reflect.blend b/effects/reflect.blend new file mode 100644 index 000000000..a7b7766aa --- /dev/null +++ b/effects/reflect.blend @@ -0,0 +1,13 @@ +float blendReflect(float base, float blend) { + return (blend==1.0)?blend:min(base*base/(1.0-blend),1.0); +} + +vec3 blendReflect(vec3 base, vec3 blend) { + return vec3(blendReflect(base.r,blend.r),blendReflect(base.g,blend.g),blendReflect(base.b,blend.b)); +} + +vec3 blendReflect(vec3 base, vec3 blend, float opacity) { + return (blendReflect(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendReflect) \ No newline at end of file diff --git a/effects/screen.blend b/effects/screen.blend new file mode 100644 index 000000000..24f345738 --- /dev/null +++ b/effects/screen.blend @@ -0,0 +1,13 @@ +float blendScreen(float base, float blend) { + return 1.0-((1.0-base)*(1.0-blend)); +} + +vec3 blendScreen(vec3 base, vec3 blend) { + return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b)); +} + +vec3 blendScreen(vec3 base, vec3 blend, float opacity) { + return (blendScreen(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendScreen) \ No newline at end of file diff --git a/effects/soft-light.blend b/effects/soft-light.blend new file mode 100644 index 000000000..c9558d1d2 --- /dev/null +++ b/effects/soft-light.blend @@ -0,0 +1,13 @@ +float blendSoftLight(float base, float blend) { + return (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend)); +} + +vec3 blendSoftLight(vec3 base, vec3 blend) { + return vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b)); +} + +vec3 blendSoftLight(vec3 base, vec3 blend, float opacity) { + return (blendSoftLight(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendSoftLight) \ No newline at end of file diff --git a/effects/substract.blend b/effects/substract.blend new file mode 100644 index 000000000..bdc1c65a3 --- /dev/null +++ b/effects/substract.blend @@ -0,0 +1,13 @@ +float blendSubstract(float base, float blend) { + return max(base+blend-1.0,0.0); +} + +vec3 blendSubstract(vec3 base, vec3 blend) { + return max(base+blend-vec3(1.0),vec3(0.0)); +} + +vec3 blendSubstract(vec3 base, vec3 blend, float opacity) { + return (blendSubstract(base, blend) * opacity + blend * (1.0 - opacity)); +} + +#pragma glslify: export(blendSubstract) \ No newline at end of file diff --git a/effects/subtract.blend b/effects/subtract.blend new file mode 100644 index 000000000..2a8f50a31 --- /dev/null +++ b/effects/subtract.blend @@ -0,0 +1,13 @@ +float blendSubtract(float base, float blend) { + return max(base+blend-1.0,0.0); +} + +vec3 blendSubtract(vec3 base, vec3 blend) { + return max(base+blend-vec3(1.0),vec3(0.0)); +} + +vec3 blendSubtract(vec3 base, vec3 blend, float opacity) { + return (blendSubtract(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendSubtract) \ No newline at end of file diff --git a/effects/vivid-light.blend b/effects/vivid-light.blend new file mode 100644 index 000000000..f86ca4922 --- /dev/null +++ b/effects/vivid-light.blend @@ -0,0 +1,16 @@ +#pragma glslify: blendColorDodge = require(./color-dodge) +#pragma glslify: blendColorBurn = require(./color-burn) + +float blendVividLight(float base, float blend) { + return (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5))); +} + +vec3 blendVividLight(vec3 base, vec3 blend) { + return vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b)); +} + +vec3 blendVividLight(vec3 base, vec3 blend, float opacity) { + return (blendVividLight(base, blend) * opacity + base * (1.0 - opacity)); +} + +#pragma glslify: export(blendVividLight) \ No newline at end of file diff --git a/io/loadthread.cpp b/io/loadthread.cpp index 6b2dc103b..d33a6c2a7 100644 --- a/io/loadthread.cpp +++ b/io/loadthread.cpp @@ -32,6 +32,7 @@ #include "rendering/renderfunctions.h" #include "io/previewgenerator.h" #include "effects/internal/voideffect.h" +#include "project/effectloaders.h" #include "debug.h" #include @@ -115,7 +116,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { // Effect loading occurs in another thread, and while it's usually very quick, just for safety we wait here // for all the effects to finish loading - panel_effect_controls->effects_loaded.lock(); + olive::effects_loaded.lock(); const EffectMeta* meta = nullptr; @@ -124,7 +125,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { meta = get_meta_from_name(effect_name); } - panel_effect_controls->effects_loaded.unlock(); + olive::effects_loaded.unlock(); int type; if (tag == "opening") { diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 1f0088ed1..d93849d24 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -39,6 +39,7 @@ #include "ui/collapsiblewidget.h" #include "project/sequence.h" #include "project/undo.h" +#include "project/effectloaders.h" #include "panels/project.h" #include "panels/timeline.h" #include "panels/viewer.h" @@ -194,7 +195,7 @@ void EffectControls::show_effect_menu(int type, int subtype) { effect_menu_type = type; effect_menu_subtype = subtype; - effects_loaded.lock(); + olive::effects_loaded.lock(); QMenu effects_menu(this); effects_menu.setToolTipsVisible(true); @@ -254,7 +255,7 @@ void EffectControls::show_effect_menu(int type, int subtype) { } } - effects_loaded.unlock(); + olive::effects_loaded.unlock(); connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*))); effects_menu.exec(QCursor::pos()); diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index 4cc3a9116..a204a65d0 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -74,8 +74,6 @@ public: ResizableScrollBar* horizontalScrollBar; QScrollBar* verticalScrollBar; - QMutex effects_loaded; - void add_effect_paste_action(QMenu* menu); virtual void Retranslate() override; diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 0dd3863d1..8c96494c0 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -33,6 +33,8 @@ #include +QMutex olive::effects_loaded; + #ifndef NOFREI0R #include typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); @@ -287,15 +289,27 @@ void GenerateBlendingShader() while (!stream.atEnd()) { QString line = stream.readLine(); - if (line.startsWith("#olive name ")) { + if (line.length() > 0 && line.at(0) == '#') { - // The blending mode can specify its own name - olive::blend_modes[i].name = line.mid(12); + if (line.startsWith("#olive name ")) { - } else if (line.startsWith("#pragma glslify: export(")) { + // The blending mode can specify its own name + olive::blend_modes[i].name = line.mid(12); - // Get function name - olive::blend_modes[i].function_name = line.mid(24, line.length()-25); + } else if (line.startsWith("#pragma glslify: export(")) { + + // Get function name + olive::blend_modes[i].function_name = line.mid(24, line.length()-25); + + } else if (line.contains("require")) { + + // This function wanted to include an external shader + + int index_of_last_bracked = line.lastIndexOf('(') + 1; + + qDebug() << "this blend mode wanted:" << line.mid(index_of_last_bracked, line.length() - index_of_last_bracked - 1); + + } } else { @@ -324,7 +338,7 @@ void GenerateBlendingShader() olive::generated_blending_shader.append(QString(" else if (blendmode == %1) {\n").arg(i)); } - olive::generated_blending_shader.append(QString(" return %1(base, blend)\n").arg(olive::blend_modes.at(i).function_name)); + olive::generated_blending_shader.append(QString(" return %1(base, blend);\n").arg(olive::blend_modes.at(i).function_name)); olive::generated_blending_shader.append(" }"); } @@ -344,7 +358,7 @@ void GenerateBlendingShader() } EffectInit::EffectInit() { - panel_effect_controls->effects_loaded.lock(); + olive::effects_loaded.lock(); } void EffectInit::run() { @@ -355,6 +369,6 @@ void EffectInit::run() { load_frei0r_effects(); #endif GenerateBlendingShader(); - panel_effect_controls->effects_loaded.unlock(); + olive::effects_loaded.unlock(); qInfo() << "Finished initializing effects"; } diff --git a/project/effectloaders.h b/project/effectloaders.h index a5370df41..1c09268d4 100644 --- a/project/effectloaders.h +++ b/project/effectloaders.h @@ -23,6 +23,11 @@ #include #include +#include + +namespace olive { + extern QMutex effects_loaded; +} void init_effects(); diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 2bfc54006..402059288 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -32,6 +32,7 @@ namespace OCIO = OCIO_NAMESPACE; #include "rendering/renderfunctions.h" #include "project/sequence.h" +#include "project/effectloaders.h" RenderThread::RenderThread() : gizmos(nullptr), @@ -99,7 +100,9 @@ void RenderThread::run() { blend_mode_program = new QOpenGLShaderProgram(); blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert"); + olive::effects_loaded.lock(); blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader); + olive::effects_loaded.unlock(); blend_mode_program->link(); premultiply_program = new QOpenGLShaderProgram(); From 0d00f8a81f39fb20f932c0940fc4fd0d2cd9d05a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 11:08:55 +1100 Subject: [PATCH 005/133] shader includes function correctly --- project/effect.h | 2 ++ project/effectloaders.cpp | 69 ++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/project/effect.h b/project/effect.h index 9d699d8dd..d97c2214b 100644 --- a/project/effect.h +++ b/project/effect.h @@ -63,6 +63,8 @@ struct BlendMode { QString name; QString url; QString function_name; + + bool loaded; }; namespace olive { diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 8c96494c0..e102f8eff 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -186,6 +186,7 @@ void load_shader_effects() { QList blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files); for (int i=0;i Date: Sun, 10 Mar 2019 12:06:06 +1100 Subject: [PATCH 006/133] pass opacity to shader --- project/effectloaders.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index e102f8eff..920869781 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -360,7 +360,7 @@ void GenerateBlendingShader() // Create monolithic switcher function - olive::generated_blending_shader.append("vec3 blend(vec3 base, vec3 blend) {\n"); + olive::generated_blending_shader.append("vec3 blend(vec3 base, vec3 blend, float opacity) {\n"); for (int i=0;i Date: Sun, 10 Mar 2019 13:02:27 +1100 Subject: [PATCH 007/133] unmultiply before blending --- project/effectloaders.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 920869781..15f9ecc8b 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -381,10 +381,15 @@ void GenerateBlendingShader() "void main() {\n" " vec4 bg_color = texture2D(background, vTexCoord);\n" // Get background texture color " vec4 fg_color = texture2D(foreground, vTexCoord);\n" // Get foreground texture color - " float true_opacity = opacity * fg_color.a;\n" - " vec3 blended_rgb = blend(bg_color.rgb, fg_color.rgb, true_opacity);\n" // Use switcher function above to blend RGBs - " vec4 composite = vec4(blended_rgb, bg_color.a + true_opacity);\n" - " gl_FragColor = composite;\n" + " if (fg_color.a > 0.0) {\n" + " float true_opacity = opacity * fg_color.a;\n" + " vec3 unmultipled_fg = max(vec3(0.0), min(vec3(1.0), fg_color.rgb / fg_color.a));\n" + " vec3 blended_rgb = blend(bg_color.rgb, unmultipled_fg, true_opacity);\n" // Use switcher function above to blend RGBs + " vec4 composite = vec4(blended_rgb, bg_color.a + true_opacity);\n" + " gl_FragColor = composite;\n" + " } else {\n" + " gl_FragColor = bg_color;\n" + " }\n" "}\n"); } From a834c8dc7c07bc7132dd57bd1b401a454226a552 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 13:02:42 +1100 Subject: [PATCH 008/133] added names to blending shaders --- effects/add.blend | 3 ++- effects/average.blend | 3 ++- effects/color-burn.blend | 3 ++- effects/color-dodge.blend | 3 ++- effects/darken.blend | 3 ++- effects/difference.blend | 3 ++- effects/exclusion.blend | 3 ++- effects/glow.blend | 3 ++- effects/hard-light.blend | 3 ++- effects/hard-mix.blend | 3 ++- effects/lighten.blend | 3 ++- effects/linear-burn.blend | 3 ++- effects/linear-dodge.blend | 3 ++- effects/linear-light.blend | 3 ++- effects/negation.blend | 3 ++- effects/normal.blend | 3 ++- effects/overlay.blend | 3 ++- effects/phoenix.blend | 3 ++- effects/pin-light.blend | 3 ++- effects/reflect.blend | 3 ++- effects/screen.blend | 3 ++- effects/soft-light.blend | 3 ++- effects/substract.blend | 3 ++- effects/subtract.blend | 3 ++- effects/vivid-light.blend | 3 ++- 25 files changed, 50 insertions(+), 25 deletions(-) diff --git a/effects/add.blend b/effects/add.blend index 2615a387f..886c2e617 100644 --- a/effects/add.blend +++ b/effects/add.blend @@ -10,4 +10,5 @@ vec3 blendAdd(vec3 base, vec3 blend, float opacity) { return (blendAdd(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendAdd) \ No newline at end of file +#pragma glslify: export(blendAdd) +#olive name Add \ No newline at end of file diff --git a/effects/average.blend b/effects/average.blend index b838757bd..098e734a3 100644 --- a/effects/average.blend +++ b/effects/average.blend @@ -6,4 +6,5 @@ vec3 blendAverage(vec3 base, vec3 blend, float opacity) { return (blendAverage(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendAverage) \ No newline at end of file +#pragma glslify: export(blendAverage) +#olive name Average \ No newline at end of file diff --git a/effects/color-burn.blend b/effects/color-burn.blend index e524511b5..4cea15cd4 100644 --- a/effects/color-burn.blend +++ b/effects/color-burn.blend @@ -10,4 +10,5 @@ vec3 blendColorBurn(vec3 base, vec3 blend, float opacity) { return (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendColorBurn) \ No newline at end of file +#pragma glslify: export(blendColorBurn) +#olive name Color Burn \ No newline at end of file diff --git a/effects/color-dodge.blend b/effects/color-dodge.blend index 38f9fc033..495ff401c 100644 --- a/effects/color-dodge.blend +++ b/effects/color-dodge.blend @@ -10,4 +10,5 @@ vec3 blendColorDodge(vec3 base, vec3 blend, float opacity) { return (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendColorDodge) \ No newline at end of file +#pragma glslify: export(blendColorDodge) +#olive name Color Dodge \ No newline at end of file diff --git a/effects/darken.blend b/effects/darken.blend index 2c74f2911..f5a81931f 100644 --- a/effects/darken.blend +++ b/effects/darken.blend @@ -10,4 +10,5 @@ vec3 blendDarken(vec3 base, vec3 blend, float opacity) { return (blendDarken(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendDarken) \ No newline at end of file +#pragma glslify: export(blendDarken) +#olive name Darken \ No newline at end of file diff --git a/effects/difference.blend b/effects/difference.blend index 88810665b..e65ba45eb 100644 --- a/effects/difference.blend +++ b/effects/difference.blend @@ -6,4 +6,5 @@ vec3 blendDifference(vec3 base, vec3 blend, float opacity) { return (blendDifference(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendDifference) \ No newline at end of file +#pragma glslify: export(blendDifference) +#olive name Difference \ No newline at end of file diff --git a/effects/exclusion.blend b/effects/exclusion.blend index 3eb0b9dfc..292fe17ce 100644 --- a/effects/exclusion.blend +++ b/effects/exclusion.blend @@ -6,4 +6,5 @@ vec3 blendExclusion(vec3 base, vec3 blend, float opacity) { return (blendExclusion(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendExclusion) \ No newline at end of file +#pragma glslify: export(blendExclusion) +#olive name Exclusion \ No newline at end of file diff --git a/effects/glow.blend b/effects/glow.blend index ec087790d..a770cb917 100644 --- a/effects/glow.blend +++ b/effects/glow.blend @@ -8,4 +8,5 @@ vec3 blendGlow(vec3 base, vec3 blend, float opacity) { return (blendGlow(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendGlow) \ No newline at end of file +#pragma glslify: export(blendGlow) +#olive name Glow \ No newline at end of file diff --git a/effects/hard-light.blend b/effects/hard-light.blend index 4a376d2ec..ed917e72d 100644 --- a/effects/hard-light.blend +++ b/effects/hard-light.blend @@ -8,4 +8,5 @@ vec3 blendHardLight(vec3 base, vec3 blend, float opacity) { return (blendHardLight(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendHardLight) \ No newline at end of file +#pragma glslify: export(blendHardLight) +#olive name Hard Light \ No newline at end of file diff --git a/effects/hard-mix.blend b/effects/hard-mix.blend index 3ce420ef9..4ba29a4dc 100644 --- a/effects/hard-mix.blend +++ b/effects/hard-mix.blend @@ -12,4 +12,5 @@ vec3 blendHardMix(vec3 base, vec3 blend, float opacity) { return (blendHardMix(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendHardMix) \ No newline at end of file +#pragma glslify: export(blendHardMix) +#olive name Hard Mix \ No newline at end of file diff --git a/effects/lighten.blend b/effects/lighten.blend index 2ffaf9616..37f3708da 100644 --- a/effects/lighten.blend +++ b/effects/lighten.blend @@ -10,4 +10,5 @@ vec3 blendLighten(vec3 base, vec3 blend, float opacity) { return (blendLighten(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendLighten) \ No newline at end of file +#pragma glslify: export(blendLighten) +#olive name Lighten \ No newline at end of file diff --git a/effects/linear-burn.blend b/effects/linear-burn.blend index 9c9ec26c1..6bbce530c 100644 --- a/effects/linear-burn.blend +++ b/effects/linear-burn.blend @@ -12,4 +12,5 @@ vec3 blendLinearBurn(vec3 base, vec3 blend, float opacity) { return (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendLinearBurn) \ No newline at end of file +#pragma glslify: export(blendLinearBurn) +#olive name Linear Burn \ No newline at end of file diff --git a/effects/linear-dodge.blend b/effects/linear-dodge.blend index 147418f58..1a5cb5709 100644 --- a/effects/linear-dodge.blend +++ b/effects/linear-dodge.blend @@ -12,4 +12,5 @@ vec3 blendLinearDodge(vec3 base, vec3 blend, float opacity) { return (blendLinearDodge(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendLinearDodge) \ No newline at end of file +#pragma glslify: export(blendLinearDodge) +#olive name Linear Dodge \ No newline at end of file diff --git a/effects/linear-light.blend b/effects/linear-light.blend index a66b56c65..97d8b02cc 100644 --- a/effects/linear-light.blend +++ b/effects/linear-light.blend @@ -13,4 +13,5 @@ vec3 blendLinearLight(vec3 base, vec3 blend, float opacity) { return (blendLinearLight(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendLinearLight) \ No newline at end of file +#pragma glslify: export(blendLinearLight) +#olive name Linear Light \ No newline at end of file diff --git a/effects/negation.blend b/effects/negation.blend index 03d5e478b..6b8c812b5 100644 --- a/effects/negation.blend +++ b/effects/negation.blend @@ -6,4 +6,5 @@ vec3 blendNegation(vec3 base, vec3 blend, float opacity) { return (blendNegation(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendNegation) \ No newline at end of file +#pragma glslify: export(blendNegation) +#olive name Negation \ No newline at end of file diff --git a/effects/normal.blend b/effects/normal.blend index f66aa9b13..a3fc1b63b 100644 --- a/effects/normal.blend +++ b/effects/normal.blend @@ -6,4 +6,5 @@ vec3 blendNormal(vec3 base, vec3 blend, float opacity) { return (blendNormal(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendNormal) \ No newline at end of file +#pragma glslify: export(blendNormal) +#olive name Normal \ No newline at end of file diff --git a/effects/overlay.blend b/effects/overlay.blend index a5d0aba85..4500d21be 100644 --- a/effects/overlay.blend +++ b/effects/overlay.blend @@ -10,4 +10,5 @@ vec3 blendOverlay(vec3 base, vec3 blend, float opacity) { return (blendOverlay(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendOverlay) \ No newline at end of file +#pragma glslify: export(blendOverlay) +#olive name Overlay \ No newline at end of file diff --git a/effects/phoenix.blend b/effects/phoenix.blend index eb16f2c4e..096139621 100644 --- a/effects/phoenix.blend +++ b/effects/phoenix.blend @@ -6,4 +6,5 @@ vec3 blendPhoenix(vec3 base, vec3 blend, float opacity) { return (blendPhoenix(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendPhoenix) \ No newline at end of file +#pragma glslify: export(blendPhoenix) +#olive name Phoenix \ No newline at end of file diff --git a/effects/pin-light.blend b/effects/pin-light.blend index 04e4ae7c6..bff45cf31 100644 --- a/effects/pin-light.blend +++ b/effects/pin-light.blend @@ -13,4 +13,5 @@ vec3 blendPinLight(vec3 base, vec3 blend, float opacity) { return (blendPinLight(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendPinLight) \ No newline at end of file +#pragma glslify: export(blendPinLight) +#olive name Pin Light \ No newline at end of file diff --git a/effects/reflect.blend b/effects/reflect.blend index a7b7766aa..75578520b 100644 --- a/effects/reflect.blend +++ b/effects/reflect.blend @@ -10,4 +10,5 @@ vec3 blendReflect(vec3 base, vec3 blend, float opacity) { return (blendReflect(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendReflect) \ No newline at end of file +#pragma glslify: export(blendReflect) +#olive name Reflect \ No newline at end of file diff --git a/effects/screen.blend b/effects/screen.blend index 24f345738..f2c4ce838 100644 --- a/effects/screen.blend +++ b/effects/screen.blend @@ -10,4 +10,5 @@ vec3 blendScreen(vec3 base, vec3 blend, float opacity) { return (blendScreen(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendScreen) \ No newline at end of file +#pragma glslify: export(blendScreen) +#olive name Screen \ No newline at end of file diff --git a/effects/soft-light.blend b/effects/soft-light.blend index c9558d1d2..8a6991214 100644 --- a/effects/soft-light.blend +++ b/effects/soft-light.blend @@ -10,4 +10,5 @@ vec3 blendSoftLight(vec3 base, vec3 blend, float opacity) { return (blendSoftLight(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendSoftLight) \ No newline at end of file +#pragma glslify: export(blendSoftLight) +#olive name Soft Light \ No newline at end of file diff --git a/effects/substract.blend b/effects/substract.blend index bdc1c65a3..2e3804719 100644 --- a/effects/substract.blend +++ b/effects/substract.blend @@ -10,4 +10,5 @@ vec3 blendSubstract(vec3 base, vec3 blend, float opacity) { return (blendSubstract(base, blend) * opacity + blend * (1.0 - opacity)); } -#pragma glslify: export(blendSubstract) \ No newline at end of file +#pragma glslify: export(blendSubstract) +#olive name Substract \ No newline at end of file diff --git a/effects/subtract.blend b/effects/subtract.blend index 2a8f50a31..aa93f2fab 100644 --- a/effects/subtract.blend +++ b/effects/subtract.blend @@ -10,4 +10,5 @@ vec3 blendSubtract(vec3 base, vec3 blend, float opacity) { return (blendSubtract(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendSubtract) \ No newline at end of file +#pragma glslify: export(blendSubtract) +#olive name Subtract \ No newline at end of file diff --git a/effects/vivid-light.blend b/effects/vivid-light.blend index f86ca4922..2f5fc9469 100644 --- a/effects/vivid-light.blend +++ b/effects/vivid-light.blend @@ -13,4 +13,5 @@ vec3 blendVividLight(vec3 base, vec3 blend, float opacity) { return (blendVividLight(base, blend) * opacity + base * (1.0 - opacity)); } -#pragma glslify: export(blendVividLight) \ No newline at end of file +#pragma glslify: export(blendVividLight) +#olive name Vivid Light \ No newline at end of file From 636cf64b3ef4c236249e4955b44605494b0455d1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 10 Mar 2019 13:44:14 +1100 Subject: [PATCH 009/133] no need to compile a shader for multiplying alpha --- rendering/renderfunctions.cpp | 4 ++-- rendering/renderfunctions.h | 12 ------------ rendering/renderthread.cpp | 10 ---------- rendering/renderthread.h | 1 - 4 files changed, 2 insertions(+), 25 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 681897895..bbf0b8b79 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -367,11 +367,11 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { if (!c->media()->to_footage()->alpha_is_premultiplied) { // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline - params.premultiply_program->bind(); + params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); textureID = draw_clip(c->fbo[0], textureID, true); - params.premultiply_program->release(); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); fbo_switcher = true; } diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index cde662c6d..1d7ed9dc5 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -143,18 +143,6 @@ struct ComposeSequenceParams { */ QOpenGLShaderProgram* blend_mode_program; - /** - * @brief Premultiply alpha shader - * - * Used only for video rendering. Never accessed with audio rendering. - * - * compose_sequence()'s internal composition - * expects premultipled alpha, but it will pre-emptively multiply any footage that is not set as already - * premultiplied (see Footage::alpha_is_premultiplied) using this shader. Must be compiled and linked beforehand. - * See RenderThread::premultiply_program for how this is properly set up. - */ - QOpenGLShaderProgram* premultiply_program; - /** * @brief The OpenGL framebuffer object that the final texture to be shown is rendered to. * diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 402059288..8babc9edc 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -39,7 +39,6 @@ RenderThread::RenderThread() : share_ctx(nullptr), ctx(nullptr), blend_mode_program(nullptr), - premultiply_program(nullptr), seq(nullptr), tex_width(-1), tex_height(-1), @@ -104,11 +103,6 @@ void RenderThread::run() { blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader); olive::effects_loaded.unlock(); blend_mode_program->link(); - - premultiply_program = new QOpenGLShaderProgram(); - premultiply_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert"); - premultiply_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/premultiply.frag"); - premultiply_program->link(); } // draw frame @@ -153,7 +147,6 @@ void RenderThread::paint() { params.wait_for_mutexes = true; params.playback_speed = 1; params.blend_mode_program = blend_mode_program; - params.premultiply_program = premultiply_program; params.backend_buffer1 = back_buffer_1.buffer(); params.backend_buffer2 = back_buffer_2.buffer(); params.backend_attachment1 = back_buffer_1.texture(); @@ -280,9 +273,6 @@ void RenderThread::delete_buffers() { void RenderThread::delete_shaders() { delete blend_mode_program; blend_mode_program = nullptr; - - delete premultiply_program; - premultiply_program = nullptr; } void RenderThread::delete_ctx() { diff --git a/rendering/renderthread.h b/rendering/renderthread.h index e18be91d3..7f2c6cdcd 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -92,7 +92,6 @@ private: QOpenGLContext* share_ctx; QOpenGLContext* ctx; QOpenGLShaderProgram* blend_mode_program; - QOpenGLShaderProgram* premultiply_program; FramebufferObject back_buffer_1; FramebufferObject back_buffer_2; From 938224214c17152a6fff334eb50ad2b16dca1810 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 11 Mar 2019 02:25:50 +1100 Subject: [PATCH 010/133] implemented opencolorio --- dialogs/preferencesdialog.cpp | 80 ++++++++++++++++----- dialogs/preferencesdialog.h | 9 ++- io/clipboard.cpp | 4 +- io/config.cpp | 16 +++-- io/config.h | 24 ++++--- rendering/renderfunctions.cpp | 20 ++++-- rendering/renderfunctions.h | 2 + rendering/renderthread.cpp | 126 +++++++++++++++++++++++++++++++++- rendering/renderthread.h | 11 ++- 9 files changed, 243 insertions(+), 49 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 78312b4c2..5625c7bca 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -82,11 +82,6 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) : { setWindowTitle(tr("Preferences")); setup_ui(); - - accurateSeekButton->setChecked(!olive::CurrentConfig.fast_seeking); - fastSeekButton->setChecked(olive::CurrentConfig.fast_seeking); - recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1); - imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats); } PreferencesDialog::~PreferencesDialog() {} @@ -184,6 +179,26 @@ void PreferencesDialog::save() { return; } + // Validate whether the OCIO config path exists + if (enable_color_management->isChecked() && !QFileInfo::exists(ocio_config_file->text())) { + + QString msg_title = tr("Invalid OpenColorIO Configuration File"); + QString msg_body; + + if (ocio_config_file->text().isEmpty()) { + msg_body = tr("You must specify an OpenColorIO configuration file if color management is enabled."); + } else { + msg_body = tr("OpenColorIO configuration file '%1' does not exist.").arg(ocio_config_file->text()); + } + + QMessageBox::critical( + this, + msg_title, + msg_body + ); + return; + } + // Check if any settings will require a restart of Olive if (olive::CurrentConfig.effect_textbox_lines != effect_textbox_lines_field->value() || olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked() @@ -233,7 +248,6 @@ void PreferencesDialog::save() { olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1; olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text(); - olive::CurrentConfig.fast_seeking = fastSeekButton->isChecked(); olive::CurrentConfig.upcoming_queue_size = upcoming_queue_spinbox->value(); olive::CurrentConfig.upcoming_queue_type = upcoming_queue_type->currentIndex(); olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value(); @@ -248,6 +262,9 @@ void PreferencesDialog::save() { olive::CurrentConfig.use_software_fallback = use_software_fallbacks_checkbox->isChecked(); olive::CurrentConfig.language_file = language_combobox->currentData().toString(); + olive::CurrentConfig.enable_color_management = enable_color_management->isChecked(); + olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); + if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) { // we're changing the size of thumbnails and waveforms, so let's delete them and regenerate them next start @@ -429,6 +446,14 @@ void PreferencesDialog::browse_css_file() { } } +void PreferencesDialog::browse_ocio_config() +{ + QString fn = QFileDialog::getOpenFileName(this, tr("Browse for OpenColorIO configuration")); + if (!fn.isEmpty()) { + ocio_config_file->setText(fn); + } +} + void PreferencesDialog::delete_all_previews() { if (QMessageBox::question(this, tr("Delete All Previews"), @@ -437,7 +462,8 @@ void PreferencesDialog::delete_all_previews() { delete_previews(1); QMessageBox::information(this, tr("Previews Deleted"), - tr("All previews deleted succesfully. You may have to re-open your current project for changes to take effect."), + tr("All previews deleted succesfully. You may have to re-open your current project for " + "changes to take effect."), QMessageBox::Ok); } } @@ -577,18 +603,6 @@ void PreferencesDialog::setup_ui() { QWidget* playback_tab = new QWidget(this); QVBoxLayout* playback_tab_layout = new QVBoxLayout(playback_tab); - // Playback -> Seeking - QGroupBox* seeking_group = new QGroupBox(playback_tab); - seeking_group->setTitle(tr("Seeking")); - QVBoxLayout* seeking_group_layout = new QVBoxLayout(seeking_group); - accurateSeekButton = new QRadioButton(seeking_group); - accurateSeekButton->setText(tr("Accurate Seeking\nAlways show the correct frame (visual may pause briefly as correct frame is retrieved)")); - seeking_group_layout->addWidget(accurateSeekButton); - fastSeekButton = new QRadioButton(seeking_group); - fastSeekButton->setText(tr("Fast Seeking\nSeek quickly (may briefly show inaccurate frames when seeking - doesn't affect playback/export)")); - seeking_group_layout->addWidget(fastSeekButton); - playback_tab_layout->addWidget(seeking_group); - // Playback -> Memory Usage QGroupBox* memory_usage_group = new QGroupBox(playback_tab); memory_usage_group->setTitle(tr("Memory Usage")); @@ -673,6 +687,34 @@ void PreferencesDialog::setup_ui() { tabWidget->addTab(audio_tab, tr("Audio")); + // + // COLOR MANAGEMENT + // + + QWidget* color_management_tab = new QWidget(); + + QGridLayout* color_management_layout = new QGridLayout(color_management_tab); + + row = 0; + + enable_color_management = new QCheckBox(tr("Enable Color Management")); + enable_color_management->setChecked(olive::CurrentConfig.enable_color_management); + color_management_layout->addWidget(enable_color_management, row, 0, 1, 3); + + row++; + + color_management_layout->addWidget(new QLabel(tr("OpenColorIO Config File:")), row, 0); + + ocio_config_file = new QLineEdit(); + ocio_config_file->setText(olive::CurrentConfig.ocio_config_path); + color_management_layout->addWidget(ocio_config_file, row, 1); + + QPushButton* ocio_config_browse_btn = new QPushButton(tr("Browse")); + connect(ocio_config_browse_btn, SIGNAL(clicked(bool)), this, SLOT(browse_ocio_config())); + color_management_layout->addWidget(ocio_config_browse_btn, row, 2); + + tabWidget->addTab(color_management_tab, tr("Color Management")); + // Shortcuts QWidget* shortcut_tab = new QWidget(this); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 03d7bd4e5..bae006d5a 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -63,9 +63,12 @@ private slots: bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = nullptr); void load_shortcut_file(); void save_shortcut_file(); - void browse_css_file(); void delete_all_previews(); + // Browse for file functionns + void browse_css_file(); + void browse_ocio_config(); + private: void setup_ui(); void setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent); @@ -77,8 +80,6 @@ private: QLineEdit* custom_css_fn; QLineEdit* imgSeqFormatEdit; QComboBox* recordingComboBox; - QRadioButton* accurateSeekButton; - QRadioButton* fastSeekButton; QTreeWidget* keyboard_tree; QDoubleSpinBox* upcoming_queue_spinbox; QComboBox* upcoming_queue_type; @@ -93,6 +94,8 @@ private: QSpinBox* thumbnail_res_spinbox; QSpinBox* waveform_res_spinbox; QCheckBox* add_default_effects_to_clips; + QCheckBox* enable_color_management; + QLineEdit* ocio_config_file; QVector key_shortcut_actions; QVector key_shortcut_items; diff --git a/io/clipboard.cpp b/io/clipboard.cpp index cd5de7902..f3cf1bd29 100644 --- a/io/clipboard.cpp +++ b/io/clipboard.cpp @@ -29,6 +29,6 @@ QVector clipboard; QVector clipboard_transitions; void clear_clipboard() { - clipboard.clear(); - clipboard_transitions.clear(); + clipboard.clear(); + clipboard_transitions.clear(); } diff --git a/io/config.cpp b/io/config.cpp index 1490ddcbd..b561afabf 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -54,7 +54,6 @@ Config::Config() drop_on_media_to_replace(true), autoscroll(olive::AUTOSCROLL_PAGE_SCROLL), audio_rate(48000), - fast_seeking(false), hover_focus(false), project_view_type(olive::PROJECT_VIEW_TREE), set_name_with_marker(true), @@ -71,7 +70,8 @@ Config::Config() waveform_resolution(64), thumbnail_resolution(120), add_default_effects_to_clips(true), - invert_timeline_scroll_axes(true) + invert_timeline_scroll_axes(true), + enable_color_management(false) {} void Config::load(QString path) { @@ -148,9 +148,6 @@ void Config::load(QString path) { } else if (stream.name() == "AudioRate") { stream.readNext(); audio_rate = stream.text().toInt(); - } else if (stream.name() == "FastSeeking") { - stream.readNext(); - fast_seeking = (stream.text() == "1"); } else if (stream.name() == "HoverFocus") { stream.readNext(); hover_focus = (stream.text() == "1"); @@ -211,6 +208,12 @@ void Config::load(QString path) { } else if (stream.name() == "AddDefaultEffectsToClips") { stream.readNext(); add_default_effects_to_clips = (stream.text() == "1"); + } else if (stream.name() == "EnableColorManagement") { + stream.readNext(); + enable_color_management = (stream.text() == "1"); + } else if (stream.name() == "OCIOConfigPath") { + stream.readNext(); + ocio_config_path = stream.text().toString(); } } } @@ -257,7 +260,6 @@ void Config::save(QString path) { stream.writeTextElement("DropFileOnMediaToReplace", QString::number(drop_on_media_to_replace)); stream.writeTextElement("Autoscroll", QString::number(autoscroll)); stream.writeTextElement("AudioRate", QString::number(audio_rate)); - stream.writeTextElement("FastSeeking", QString::number(fast_seeking)); stream.writeTextElement("HoverFocus", QString::number(hover_focus)); stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker)); @@ -278,6 +280,8 @@ void Config::save(QString path) { stream.writeTextElement("ThumbnailResolution", QString::number(thumbnail_resolution)); stream.writeTextElement("WaveformResolution", QString::number(waveform_resolution)); stream.writeTextElement("AddDefaultEffectsToClips", QString::number(add_default_effects_to_clips)); + stream.writeTextElement("EnableColorManagement", QString::number(enable_color_management)); + stream.writeTextElement("OCIOConfigPath", ocio_config_path); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index c9fc41d92..bd5958749 100644 --- a/io/config.h +++ b/io/config.h @@ -315,16 +315,6 @@ struct Config { */ int audio_rate; - /** - * @brief Enable fast seeking - * - * Olive supports a seek mode that shows frames faster with the risk of briefly showing a "best-effort" frame that - * may not be the accurate frame at that point of the Timeline. This does not affect exporting. - * - * Set to **TRUE** if this mode should be enabled. - */ - bool fast_seeking; - /** * @brief Enable hover focus * @@ -517,6 +507,20 @@ struct Config { */ bool invert_timeline_scroll_axes; + /** + * @brief Enable color managemennt + * + * **TRUE** if color management through OpenColorIO should be enabled + */ + bool enable_color_management; + + /** + * @brief Path to OpenColorIO configuration file + * + * Used if Config::enable_color_management is **TRUE**. + */ + QString ocio_config_path; + /** * @brief Load config from file * diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index bbf0b8b79..f7fcd9b51 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -369,19 +369,27 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); - textureID = draw_clip(c->fbo[0], textureID, true); + textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - fbo_switcher = true; + fbo_switcher = !fbo_switcher; } #ifdef OLIVE_OCIO // convert to linear colorspace - bool linear_convert = true; - if (linear_convert) + if (olive::CurrentConfig.enable_color_management) { + params.ocio_shader->bind(); + params.ocio_shader->setUniformValue("tex1", 0); + params.ocio_shader->setUniformValue("tex2", 2); + + textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); + + params.ocio_shader->release(); + + fbo_switcher = !fbo_switcher; } #endif @@ -718,3 +726,7 @@ void close_active_clips(SequencePtr s) { } } } + +void UpdateOCIOGLState(const ComposeSequenceParams& params) +{ +} diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 1d7ed9dc5..a863197bb 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -390,4 +390,6 @@ int64_t playhead_to_timestamp(Clip *c, long playhead); */ void close_active_clips(SequencePtr s); +void UpdateOCIOGLState(const ComposeSequenceParams ¶ms); + #endif // RENDERFUNCTIONS_H diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 8babc9edc..3002d4e47 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -22,9 +22,10 @@ #include #include -#include #include +#include #include + #ifdef OLIVE_OCIO #include namespace OCIO = OCIO_NAMESPACE; @@ -33,6 +34,7 @@ namespace OCIO = OCIO_NAMESPACE; #include "rendering/renderfunctions.h" #include "project/sequence.h" #include "project/effectloaders.h" +#include "io/config.h" RenderThread::RenderThread() : gizmos(nullptr), @@ -93,8 +95,8 @@ void RenderThread::run() { back_buffer_2.Create(ctx, seq->width, seq->height); } + // If there's no blending mode shader, create it now if (blend_mode_program == nullptr) { - // create shader program to make blending modes work delete_shaders(); blend_mode_program = new QOpenGLShaderProgram(); @@ -105,6 +107,14 @@ void RenderThread::run() { blend_mode_program->link(); } + // If there's no OpenColorIO shader, create it now + if (olive::CurrentConfig.enable_color_management + && (ocio_shader == nullptr || ocio_loaded_config != olive::CurrentConfig.ocio_config_path)) { + destroy_ocio(); + + set_up_ocio(); + } + // draw frame paint(); @@ -132,8 +142,117 @@ const GLuint &RenderThread::get_texture() return front_buffer_switcher ? front_buffer_2.texture() : front_buffer_1.texture(); } +const char * g_fragShaderText = "" +"\n" +"uniform sampler2D tex1;\n" +"uniform sampler3D tex2;\n" +"\n" +"void main()\n" +"{\n" +" vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n" +" gl_FragColor = OCIODisplay(col, tex2);\n" +"}\n"; + void RenderThread::set_up_ocio() { + functions.initializeOpenGLFunctions(); + + // + // SETUP LUT TEXTURE + // + + // Create LUT texture + ctx->functions()->glGenTextures(1, &ocio_lut_texture); + + // Bind texture to GL_TEXTURE_3D and GL_TEXTURE2 + ctx->functions()->glActiveTexture(GL_TEXTURE2); + ctx->functions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); + + // Set texture parameters + ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // Allocate storage for texture + functions.glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + 0, GL_RGB,GL_FLOAT, ocio_lut_data); + + // + // SET UP OCIO DISPLAY + // + + OCIO::ConstConfigRcPtr config; + + // Set current config to the file specified in Config + if (QFileInfo::exists(olive::CurrentConfig.ocio_config_path)) { + + config = OCIO::Config::CreateFromFile(olive::CurrentConfig.ocio_config_path.toUtf8()); + OCIO::SetCurrentConfig(config); + ocio_loaded_config = olive::CurrentConfig.ocio_config_path; + + } else { + + config = OCIO::GetCurrentConfig(); + + } + + const char* display = config->getDefaultDisplay(); + + OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); + transform->setInputColorSpaceName(OCIO::ROLE_SCENE_LINEAR); + transform->setDisplay(display); + transform->setView(config->getDefaultView(display)); + + // + // GET OCIO PROCESSOR + // + + OCIO::ConstProcessorRcPtr processor = OCIO::GetCurrentConfig()->getProcessor(transform); + + // + // SET UP GLSL SHADER + // + + OCIO::GpuShaderDesc shaderDesc; + shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); + shaderDesc.setFunctionName("OCIODisplay"); + shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); + + // + // COMPUTE 3D LUT + // + + processor->getGpuLut3D(ocio_lut_data, shaderDesc); + + glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); + functions.glTexSubImage3D(GL_TEXTURE_3D, 0, + 0, 0, 0, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + GL_RGB,GL_FLOAT, ocio_lut_data); + + QString shader_text = processor->getGpuShaderText(shaderDesc); + shader_text.append("\n"); + shader_text.append(g_fragShaderText); + + ocio_shader = new QOpenGLShaderProgram(); + ocio_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, shader_text); + ocio_shader->link(); + + // Reset active texture to 0 for the rest of the pipeline + ctx->functions()->glActiveTexture(GL_TEXTURE0); +} + +void RenderThread::destroy_ocio() +{ + // Destroy LUT texture + ctx->functions()->glDeleteTextures(1, &ocio_lut_texture); + ocio_lut_texture = 0; + + delete ocio_shader; + ocio_shader = nullptr; } void RenderThread::paint() { @@ -147,6 +266,7 @@ void RenderThread::paint() { params.wait_for_mutexes = true; params.playback_speed = 1; params.blend_mode_program = blend_mode_program; + params.ocio_shader = ocio_shader; params.backend_buffer1 = back_buffer_1.buffer(); params.backend_buffer2 = back_buffer_2.buffer(); params.backend_attachment1 = back_buffer_1.texture(); @@ -279,6 +399,8 @@ void RenderThread::delete_ctx() { if (ctx != nullptr) { delete_shaders(); delete_buffers(); + + destroy_ocio(); } delete ctx; diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 7f2c6cdcd..25aee6ddf 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -28,16 +28,17 @@ #include #include #include +#include #include "project/sequence.h" #include "project/effect.h" #include "rendering/framebufferobject.h" // copied from source code to OCIODisplay -const int LUT3D_EDGE_SIZE = 32; +const int OCIO_LUT3D_EDGE_SIZE = 32; // copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE -const int NUM_3D_ENTRIES = 98304; +const int OCIO_NUM_3D_ENTRIES = 98304; class RenderThread : public QThread { Q_OBJECT @@ -96,9 +97,11 @@ private: FramebufferObject back_buffer_1; FramebufferObject back_buffer_2; - float ocio_lut_data[NUM_3D_ENTRIES]; + // OpenColorIO variables + float ocio_lut_data[OCIO_NUM_3D_ENTRIES]; GLuint ocio_lut_texture; QOpenGLShaderProgram* ocio_shader; + QString ocio_loaded_config; SequencePtr seq; int divider; @@ -110,6 +113,8 @@ private: QString save_fn; GLvoid *pixel_buffer; int pixel_buffer_linesize; + + QOpenGLFunctions_2_0 functions; }; #endif // RENDERTHREAD_H From a8009045800d4956a853d66f5b054e6dd0e1a24e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 11 Mar 2019 02:45:02 +1100 Subject: [PATCH 011/133] inverted macro so OCIO support is enabled by default --- dialogs/preferencesdialog.cpp | 15 ++ effects/internal/frei0reffect.cpp | 254 +++++++++++++++--------------- effects/internal/frei0reffect.h | 26 +-- effects/internal/vsthost.cpp | 4 - olive.pro | 8 +- project/effect.cpp | 4 +- project/effectloaders.cpp | 8 +- rendering/renderfunctions.cpp | 4 +- rendering/renderfunctions.h | 2 + rendering/renderthread.cpp | 10 +- rendering/renderthread.h | 15 +- 11 files changed, 188 insertions(+), 162 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 5625c7bca..d778d20fe 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -451,6 +451,7 @@ void PreferencesDialog::browse_ocio_config() QString fn = QFileDialog::getOpenFileName(this, tr("Browse for OpenColorIO configuration")); if (!fn.isEmpty()) { ocio_config_file->setText(fn); + enable_color_management->setChecked(true); } } @@ -697,6 +698,14 @@ void PreferencesDialog::setup_ui() { row = 0; +#ifdef NO_OCIO + QLabel* no_ocio_available_lbl = new QLabel(tr("Color management is unavailable because Olive was " + "compiled without OpenColorIO support.")); + no_ocio_available_lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + color_management_layout->addWidget(no_ocio_available_lbl, row, 0, 1, 3); + row++; +#endif + enable_color_management = new QCheckBox(tr("Enable Color Management")); enable_color_management->setChecked(olive::CurrentConfig.enable_color_management); color_management_layout->addWidget(enable_color_management, row, 0, 1, 3); @@ -713,6 +722,12 @@ void PreferencesDialog::setup_ui() { connect(ocio_config_browse_btn, SIGNAL(clicked(bool)), this, SLOT(browse_ocio_config())); color_management_layout->addWidget(ocio_config_browse_btn, row, 2); +#ifdef NO_OCIO + enable_color_management->setEnabled(false); + ocio_config_file->setEnabled(false); + ocio_config_browse_btn->setEnabled(false); +#endif + tabWidget->addTab(color_management_tab, tr("Color Management")); // Shortcuts diff --git a/effects/internal/frei0reffect.cpp b/effects/internal/frei0reffect.cpp index 223cd0cce..0ead62100 100644 --- a/effects/internal/frei0reffect.cpp +++ b/effects/internal/frei0reffect.cpp @@ -20,7 +20,7 @@ #include "frei0reffect.h" -#ifndef NOFREI0R +#ifndef NO_FREI0R #include #include @@ -31,184 +31,184 @@ typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int heig typedef int (*f0rInitFunc) (); typedef void (*f0rDeinitFunc) (); typedef void (*f0rUpdateFunc) (f0r_instance_t instance, - double time, const uint32_t* inframe, uint32_t* outframe); + double time, const uint32_t* inframe, uint32_t* outframe); typedef void (*f0rDestructFunc)(f0r_instance_t instance); typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); typedef void (*f0rSetParamValue) (f0r_instance_t instance, - f0r_param_t param, int param_index); + f0r_param_t param, int param_index); Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) : - Effect(c, em), - open(false) + Effect(c, em), + open(false) { - enable_image = true; + enable_image = true; - // Windows DLL loading routine - QString dll_fn = QDir(em->path).filePath(em->filename); + // Windows DLL loading routine + QString dll_fn = QDir(em->path).filePath(em->filename); - handle = LibLoad(dll_fn); - if(handle == nullptr) { - QString dll_error; + handle = LibLoad(dll_fn); + if(handle == nullptr) { + QString dll_error; #ifdef _WIN32 - DWORD dll_err = GetLastError(); - dll_error = QString::number(dll_err); + DWORD dll_err = GetLastError(); + dll_error = QString::number(dll_err); #elif __linux__ - dll_error = dlerror(); + dll_error = dlerror(); #endif - qCritical() << "Failed to load Frei0r plugin" << dll_fn << "-" << dll_error; + qCritical() << "Failed to load Frei0r plugin" << dll_fn << "-" << dll_error; - QString msg_err = tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error); + QString msg_err = tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error); #ifdef _WIN32 - if (dll_err == 193) { + if (dll_err == 193) { #ifdef _WIN64 - msg_err += "\n\n" + tr("NOTE: You can't load 32-bit Frei0r plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive."); + msg_err += "\n\n" + tr("NOTE: You can't load 32-bit Frei0r plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive."); #elif _WIN32 - msg_err += "\n\n" + tr("NOTE: You can't load 64-bit Frei0r plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive."); + msg_err += "\n\n" + tr("NOTE: You can't load 64-bit Frei0r plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive."); #endif - } + } #endif - QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"), msg_err); + QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"), msg_err); - return; - } + return; + } - f0rInitFunc init = reinterpret_cast(LibAddress(handle, "f0r_init")); - init(); + f0rInitFunc init = reinterpret_cast(LibAddress(handle, "f0r_init")); + init(); - construct_module(); + construct_module(); - f0r_plugin_info_t info; - f0rGetPluginInfo info_func = reinterpret_cast(LibAddress(handle, "f0r_get_plugin_info")); - info_func(&info); + f0r_plugin_info_t info; + f0rGetPluginInfo info_func = reinterpret_cast(LibAddress(handle, "f0r_get_plugin_info")); + info_func(&info); - param_count = info.num_params; + param_count = info.num_params; - get_param_info = reinterpret_cast(LibAddress(handle, "f0r_get_param_info")); - for (int i=0;i(LibAddress(handle, "f0r_get_param_info")); + for (int i=0;i= 0 && param_info.type <= F0R_PARAM_STRING) { - EffectRow* row = add_row(param_info.name); - switch (param_info.type) { - case F0R_PARAM_BOOL: - row->add_field(EFFECT_FIELD_BOOL, QString::number(i)); - break; - case F0R_PARAM_DOUBLE: - { - EffectField* f = row->add_field(EFFECT_FIELD_DOUBLE, QString::number(i)); - f->set_double_minimum_value(0); - f->set_double_maximum_value(100); - } - break; - case F0R_PARAM_COLOR: - row->add_field(EFFECT_FIELD_COLOR, QString::number(i)); - break; - case F0R_PARAM_POSITION: - { - EffectField* fx = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1X").arg(QString::number(i))); - fx->set_double_minimum_value(0); - fx->set_double_maximum_value(100); - EffectField* fy = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1Y").arg(QString::number(i))); - fy->set_double_minimum_value(0); - fy->set_double_maximum_value(100); - } - break; - case F0R_PARAM_STRING: - row->add_field(EFFECT_FIELD_STRING, QString::number(i)); - break; - } - } - } + if (param_info.type >= 0 && param_info.type <= F0R_PARAM_STRING) { + EffectRow* row = add_row(param_info.name); + switch (param_info.type) { + case F0R_PARAM_BOOL: + row->add_field(EFFECT_FIELD_BOOL, QString::number(i)); + break; + case F0R_PARAM_DOUBLE: + { + EffectField* f = row->add_field(EFFECT_FIELD_DOUBLE, QString::number(i)); + f->set_double_minimum_value(0); + f->set_double_maximum_value(100); + } + break; + case F0R_PARAM_COLOR: + row->add_field(EFFECT_FIELD_COLOR, QString::number(i)); + break; + case F0R_PARAM_POSITION: + { + EffectField* fx = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1X").arg(QString::number(i))); + fx->set_double_minimum_value(0); + fx->set_double_maximum_value(100); + EffectField* fy = row->add_field(EFFECT_FIELD_DOUBLE, QString("%1Y").arg(QString::number(i))); + fy->set_double_minimum_value(0); + fy->set_double_maximum_value(100); + } + break; + case F0R_PARAM_STRING: + row->add_field(EFFECT_FIELD_STRING, QString::number(i)); + break; + } + } + } } Frei0rEffect::~Frei0rEffect() { - if (handle != nullptr) { - f0rDeinitFunc deinit = reinterpret_cast(LibAddress(handle, "f0r_deinit")); - deinit(); + if (handle != nullptr) { + f0rDeinitFunc deinit = reinterpret_cast(LibAddress(handle, "f0r_deinit")); + deinit(); - LibClose(handle); - } + LibClose(handle); + } } void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *output, int) { - f0rUpdateFunc update_func = reinterpret_cast(LibAddress(handle, "f0r_update")); + f0rUpdateFunc update_func = reinterpret_cast(LibAddress(handle, "f0r_update")); - for (int i=0;i(LibAddress(handle, "f0r_set_param_value")); - switch (param_info.type) { - case F0R_PARAM_BOOL: - { - double b = param_row->field(0)->get_bool_value(timecode); - set_param(instance, &b, i); - } - break; - case F0R_PARAM_DOUBLE: - { - double d = param_row->field(0)->get_double_value(timecode)*0.01; - set_param(instance, &d, i); - } - break; - case F0R_PARAM_COLOR: - { - QColor qcolor = param_row->field(0)->get_color_value(timecode);; + f0rSetParamValue set_param = reinterpret_cast(LibAddress(handle, "f0r_set_param_value")); + switch (param_info.type) { + case F0R_PARAM_BOOL: + { + double b = param_row->field(0)->get_bool_value(timecode); + set_param(instance, &b, i); + } + break; + case F0R_PARAM_DOUBLE: + { + double d = param_row->field(0)->get_double_value(timecode)*0.01; + set_param(instance, &d, i); + } + break; + case F0R_PARAM_COLOR: + { + QColor qcolor = param_row->field(0)->get_color_value(timecode);; - f0r_param_color fcolor; - fcolor.r = float(qcolor.redF()); - fcolor.g = float(qcolor.greenF()); - fcolor.b = float(qcolor.blueF()); + f0r_param_color fcolor; + fcolor.r = float(qcolor.redF()); + fcolor.g = float(qcolor.greenF()); + fcolor.b = float(qcolor.blueF()); - set_param(instance, &fcolor, i); - } - break; - case F0R_PARAM_POSITION: - { - f0r_param_position pos; - pos.x = param_row->field(0)->get_double_value(timecode); - pos.y = param_row->field(1)->get_double_value(timecode); - set_param(instance, &pos, i); - } - break; - case F0R_PARAM_STRING: - { - QByteArray bytes = param_row->field(0)->get_string_value(timecode).toUtf8(); - char* byte_data = bytes.data(); - set_param(instance, &byte_data, i); - } - break; - } - } + set_param(instance, &fcolor, i); + } + break; + case F0R_PARAM_POSITION: + { + f0r_param_position pos; + pos.x = param_row->field(0)->get_double_value(timecode); + pos.y = param_row->field(1)->get_double_value(timecode); + set_param(instance, &pos, i); + } + break; + case F0R_PARAM_STRING: + { + QByteArray bytes = param_row->field(0)->get_string_value(timecode).toUtf8(); + char* byte_data = bytes.data(); + set_param(instance, &byte_data, i); + } + break; + } + } - update_func(instance, timecode, reinterpret_cast(input), reinterpret_cast(output)); + update_func(instance, timecode, reinterpret_cast(input), reinterpret_cast(output)); } void Frei0rEffect::refresh() { - destruct_module(); - construct_module(); + destruct_module(); + construct_module(); } void Frei0rEffect::destruct_module() { - if (open) { - f0rDestructFunc destruct = reinterpret_cast(LibAddress(handle, "f0r_destruct")); - destruct(instance); + if (open) { + f0rDestructFunc destruct = reinterpret_cast(LibAddress(handle, "f0r_destruct")); + destruct(instance); - open = false; - } + open = false; + } } void Frei0rEffect::construct_module() { - f0rConstructFunc construct = reinterpret_cast(LibAddress(handle, "f0r_construct")); + f0rConstructFunc construct = reinterpret_cast(LibAddress(handle, "f0r_construct")); instance = construct(parent_clip->media_width(), parent_clip->media_height()); - open = true; + open = true; } #endif diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h index b6428a8b9..363e4541e 100644 --- a/effects/internal/frei0reffect.h +++ b/effects/internal/frei0reffect.h @@ -21,7 +21,7 @@ #ifndef FREI0REFFECT_H #define FREI0REFFECT_H -#ifndef NOFREI0R +#ifndef NO_FREI0R #include "project/effect.h" @@ -30,25 +30,25 @@ #include "io/crossplatformlib.h" typedef void (*f0rGetParamInfo)(f0r_param_info_t * info, - int param_index ); + int param_index ); class Frei0rEffect : public Effect { - Q_OBJECT + Q_OBJECT public: Frei0rEffect(Clip* c, const EffectMeta* em); - ~Frei0rEffect(); + ~Frei0rEffect(); - virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); + virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); - virtual void refresh(); + virtual void refresh(); private: - ModulePtr handle; - f0r_instance_t instance; - int param_count; - f0rGetParamInfo get_param_info; - void destruct_module(); - void construct_module(); - bool open; + ModulePtr handle; + f0r_instance_t instance; + int param_count; + f0rGetParamInfo get_param_info; + void destruct_module(); + void construct_module(); + bool open; }; #endif diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp index 89fa02cdc..8089dbb85 100644 --- a/effects/internal/vsthost.cpp +++ b/effects/internal/vsthost.cpp @@ -20,8 +20,6 @@ #include "vsthost.h" -#ifndef NOVST - // adapted from http://teragonaudio.com/article/How-to-make-your-own-VST-host.html #include @@ -396,5 +394,3 @@ void VSTHost::change_plugin() { } show_interface_btn->setEnabled(plugin != nullptr); } - -#endif diff --git a/olive.pro b/olive.pro index 5f844a844..60b28a0f5 100644 --- a/olive.pro +++ b/olive.pro @@ -294,7 +294,7 @@ TRANSLATIONS += \ win32 { RC_FILE = packaging/windows/resources.rc LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32 -luser32 - contains(DEFINES, OLIVE_OCIO) { + !contains(DEFINES, NO_OCIO) { LIBS += -lOpenColorIO } } @@ -303,11 +303,17 @@ mac { LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -framework CoreFoundation ICON = packaging/macos/olive.icns INCLUDEPATH = /usr/local/include + !contains(DEFINES, NO_OCIO) { + LIBS += -lOpenColorIO + } } unix:!mac { CONFIG += link_pkgconfig PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample + !contains(DEFINES, NO_OCIO) { + LIBS += -lOpenColorIO + } } unix:!mac:!haiku { LIBS += -ldl diff --git a/project/effect.cpp b/project/effect.cpp index fab503d07..777d3e4ff 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -86,10 +86,8 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { case EFFECT_INTERNAL_SHAKE: return EffectPtr(new ShakeEffect(c, em)); case EFFECT_INTERNAL_CORNERPIN: return EffectPtr(new CornerPinEffect(c, em)); case EFFECT_INTERNAL_FILLLEFTRIGHT: return EffectPtr(new FillLeftRightEffect(c, em)); -#ifndef NOVST case EFFECT_INTERNAL_VST: return EffectPtr(new VSTHost(c, em)); -#endif -#ifndef NOFREI0R +#ifndef NO_FREI0R case EFFECT_INTERNAL_FREI0R: return EffectPtr(new Frei0rEffect(c, em)); #endif } diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 15f9ecc8b..2e1f2d9e9 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -35,7 +35,7 @@ QMutex olive::effects_loaded; -#ifndef NOFREI0R +#ifndef NO_FREI0R #include typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); #endif @@ -59,11 +59,9 @@ void load_internal_effects() { em.internal = EFFECT_INTERNAL_PAN; olive::effects.append(em); -#ifndef NOVST em.name = "VST Plugin 2.x"; em.internal = EFFECT_INTERNAL_VST; olive::effects.append(em); -#endif em.name = "Tone"; em.internal = EFFECT_INTERNAL_TONE; @@ -201,7 +199,7 @@ void init_effects() { init_thread->start(); } -#ifndef NOFREI0R +#ifndef NO_FREI0R void load_frei0r_effects_worker(const QString& dir, EffectMeta& em, QVector& loaded_names) { QDir search_dir(dir); if (search_dir.exists()) { @@ -401,7 +399,7 @@ void EffectInit::run() { qInfo() << "Initializing effects..."; load_internal_effects(); load_shader_effects(); -#ifndef NOFREI0R +#ifndef NO_FREI0R load_frei0r_effects(); #endif GenerateBlendingShader(); diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index f7fcd9b51..b35dd503e 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -29,7 +29,7 @@ extern "C" { #include #include -#ifdef OLIVE_OCIO +#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE; #endif @@ -376,7 +376,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { fbo_switcher = !fbo_switcher; } -#ifdef OLIVE_OCIO +#ifndef NO_OCIO // convert to linear colorspace if (olive::CurrentConfig.enable_color_management) { diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index a863197bb..9258a7e6c 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -193,6 +193,7 @@ struct ComposeSequenceParams { */ GLuint backend_attachment2; +#ifndef NO_OCIO /** * @brief OpenGL shader containing OpenColorIO shader information */ @@ -202,6 +203,7 @@ struct ComposeSequenceParams { * @brief OpenGL texture containing LUT obtained form OpenColorIO */ GLuint ocio_lut_texture; +#endif }; /** diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 3002d4e47..881935cf3 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -26,7 +26,7 @@ #include #include -#ifdef OLIVE_OCIO +#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE; #endif @@ -107,6 +107,7 @@ void RenderThread::run() { blend_mode_program->link(); } +#ifndef NO_OCIO // If there's no OpenColorIO shader, create it now if (olive::CurrentConfig.enable_color_management && (ocio_shader == nullptr || ocio_loaded_config != olive::CurrentConfig.ocio_config_path)) { @@ -114,6 +115,7 @@ void RenderThread::run() { set_up_ocio(); } +#endif // draw frame paint(); @@ -153,6 +155,7 @@ const char * g_fragShaderText = "" " gl_FragColor = OCIODisplay(col, tex2);\n" "}\n"; +#ifndef NO_OCIO void RenderThread::set_up_ocio() { functions.initializeOpenGLFunctions(); @@ -254,6 +257,7 @@ void RenderThread::destroy_ocio() delete ocio_shader; ocio_shader = nullptr; } +#endif void RenderThread::paint() { // set up compose_sequence() parameters @@ -266,7 +270,9 @@ void RenderThread::paint() { params.wait_for_mutexes = true; params.playback_speed = 1; params.blend_mode_program = blend_mode_program; +#ifndef NO_OCIO params.ocio_shader = ocio_shader; +#endif params.backend_buffer1 = back_buffer_1.buffer(); params.backend_buffer2 = back_buffer_2.buffer(); params.backend_attachment1 = back_buffer_1.texture(); @@ -400,7 +406,9 @@ void RenderThread::delete_ctx() { delete_shaders(); delete_buffers(); +#ifndef NO_OCIO destroy_ocio(); +#endif } delete ctx; diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 25aee6ddf..43a77e117 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -72,9 +72,18 @@ private: void delete_buffers(); void delete_shaders(); +#ifndef NO_OCIO + // OpenColorIO functions void set_up_ocio(); void destroy_ocio(); + // OpenColorIO variables + float ocio_lut_data[OCIO_NUM_3D_ENTRIES]; + GLuint ocio_lut_texture; + QOpenGLShaderProgram* ocio_shader; + QString ocio_loaded_config; +#endif + FramebufferObject front_buffer_1; QMutex front_mutex1; @@ -97,12 +106,6 @@ private: FramebufferObject back_buffer_1; FramebufferObject back_buffer_2; - // OpenColorIO variables - float ocio_lut_data[OCIO_NUM_3D_ENTRIES]; - GLuint ocio_lut_texture; - QOpenGLShaderProgram* ocio_shader; - QString ocio_loaded_config; - SequencePtr seq; int divider; int tex_width; From afa89ff452bf980b6a2e70e3be30e3b2b39d6b9c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 11 Mar 2019 03:40:44 +1100 Subject: [PATCH 012/133] added comment regarding blending shaders --- project/effectloaders.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/project/effectloaders.cpp b/project/effectloaders.cpp index 2e1f2d9e9..8f6f97465 100644 --- a/project/effectloaders.cpp +++ b/project/effectloaders.cpp @@ -372,6 +372,12 @@ void GenerateBlendingShader() } // Write the main() function for the shader + // + // NOTE/FIXME: Unfortunately the current blending shaders (from https://github.com/jamieowen/glsl-blend) all seem to + // be calculated for unassociated alpha, while Olive's internal pipeline largely functions in associated alpha. + // Therefore for the blending shaders to work as expected, the alpha has to be unassociated at this stage. + // Naturally, this sucks, but I'm not entirely sure what the solution is apart from rewriting the blending shaders + // or maybe finding a new library. olive::generated_blending_shader.append("\n return blend;\n" // default return value "}\n" From 0b7906f0998483baaaad7cf1a0db678d2861dcb9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 15 Mar 2019 10:16:13 +1100 Subject: [PATCH 013/133] fixed preferences bug that clears the image sequence formats --- dialogs/preferencesdialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index d778d20fe..ebed6299e 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -534,7 +534,7 @@ void PreferencesDialog::setup_ui() { general_layout->addWidget(new QLabel(tr("Image sequence formats:"), this), row, 0); imgSeqFormatEdit = new QLineEdit(general_tab); - + imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats); general_layout->addWidget(imgSeqFormatEdit, row, 1, 1, 4); row++; @@ -545,6 +545,7 @@ void PreferencesDialog::setup_ui() { recordingComboBox = new QComboBox(general_tab); recordingComboBox->addItem(tr("Mono")); recordingComboBox->addItem(tr("Stereo")); + recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1); general_layout->addWidget(recordingComboBox, row, 1, 1, 4); row++; From 8512407ede57e3de6aecb51beae2a7b2d0a17964 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 16 Mar 2019 21:14:32 +1100 Subject: [PATCH 014/133] fixed debug message --- effects/effectloaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index 7d9a4e6a0..0539be03d 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -267,7 +267,7 @@ EffectInit::EffectInit() { } void EffectInit::run() { - qInfo() << "Initializing olive::effects..."; + qInfo() << "Initializing effects..."; load_internal_effects(); load_shader_effects(); #ifndef NOFREI0R From 230ce1840d5f5d77fb7a56b543e8b23ad207837d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 16 Mar 2019 21:25:37 +1100 Subject: [PATCH 015/133] started effect loading earlier to speed up timing --- panels/panels.cpp | 2 -- ui/mainwindow.cpp | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/panels/panels.cpp b/panels/panels.cpp index 30af5c7f2..19b9dbd65 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -24,7 +24,6 @@ #include "timeline/clip.h" #include "effects/transition.h" #include "global/config.h" -#include "effects/effectloaders.h" #include "global/debug.h" #include @@ -90,7 +89,6 @@ void alloc_panels(QWidget* parent) { panel_project = new Project(parent); panel_project->setObjectName("proj_root"); panel_effect_controls = new EffectControls(parent); - EffectInit::StartLoading(); panel_effect_controls->setObjectName("fx_controls"); panel_timeline = new Timeline(parent); panel_timeline->setObjectName("timeline"); diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index eafbfb34f..eef2a5c95 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -57,6 +57,7 @@ #include "rendering/audio.h" #include "rendering/renderfunctions.h" #include "undo/undostack.h" +#include "effects/effectloaders.h" MainWindow* olive::MainWindow; @@ -185,6 +186,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), first_show(true) { + EffectInit::StartLoading(); + olive::cursor::Initialize(); open_debug_file(); From 4925f8f5829fc5286f01c8930d1edbef05093d99 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 01:03:45 +1100 Subject: [PATCH 016/133] completed merge --- rendering/renderfunctions.cpp | 10 ---------- timeline/clip.cpp | 1 - 2 files changed, 11 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index ea4d89ae6..f38817648 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -547,13 +547,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // bind front buffer as draw buffer params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, final_fbo); -<<<<<<< HEAD // Check if we're using a blend mode (< 0 means no blend mode) if (coords.blendmode < 0) { -======= -// if (olive::CurrentRuntimeConfig.disable_blending) { - // some GPUs don't like the blending shader, so we provide a pure GL fallback here ->>>>>>> 445c8299e792c7ea18672491ed905ffd89eaea16 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_1); @@ -562,11 +557,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { full_blit(); params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); -<<<<<<< HEAD -======= - /* ->>>>>>> 445c8299e792c7ea18672491ed905ffd89eaea16 } else { // load background texture into texture unit 0 @@ -599,7 +590,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); } - */ // unbind framebuffer params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 5b4311de6..75afae44b 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -619,7 +619,6 @@ bool Clip::Retrieve() const_cast(using_db_1 ? data_buffer_1 : data_buffer_2)); if (data_buffer_1 != frame->data[0]) { - qDebug() << data_buffer_1 << frame->data[0]; delete [] data_buffer_1; delete [] data_buffer_2; } From 5100ae4c2eecfd0c2b2c5d957b35a53308e5bd30 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 01:45:19 +1100 Subject: [PATCH 017/133] moved clips to custom framebufferobject --- rendering/cacher.cpp | 1 - rendering/exportthread.cpp | 1 - rendering/framebufferobject.cpp | 36 ++++++++++++++++++++++++++++++-- rendering/framebufferobject.h | 10 +++++++-- rendering/renderfunctions.cpp | 37 +++++++++++++++------------------ timeline/clip.cpp | 14 +------------ timeline/clip.h | 3 ++- ui/viewerwidget.cpp | 1 - 8 files changed, 62 insertions(+), 41 deletions(-) diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 35fd02f72..740571810 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -28,7 +28,6 @@ #include -#include #include #include #include diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp index 06cc9970c..da05b6194 100644 --- a/rendering/exportthread.cpp +++ b/rendering/exportthread.cpp @@ -40,7 +40,6 @@ extern "C" { #include #include -#include #include #include diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 10a2cbb96..6ca510443 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -70,12 +70,44 @@ void FramebufferObject::Destroy() ctx_ = nullptr; } -const GLuint &FramebufferObject::buffer() +void FramebufferObject::BindBuffer() const +{ + if (ctx_ == nullptr) { + return; + } + ctx_->functions()->glBindFramebuffer(GL_TEXTURE_2D, buffer_); +} + +void FramebufferObject::ReleaseBuffer() const +{ + if (ctx_ == nullptr) { + return; + } + ctx_->functions()->glBindFramebuffer(GL_TEXTURE_2D, 0); +} + +void FramebufferObject::BindTexture() const +{ + if (ctx_ == nullptr) { + return; + } + ctx_->functions()->glBindTexture(GL_TEXTURE_2D, texture_); +} + +void FramebufferObject::ReleaseTexture() const +{ + if (ctx_ == nullptr) { + return; + } + ctx_->functions()->glBindTexture(GL_TEXTURE_2D, 0); +} + +const GLuint &FramebufferObject::buffer() const { return buffer_; } -const GLuint &FramebufferObject::texture() +const GLuint &FramebufferObject::texture() const { return texture_; } diff --git a/rendering/framebufferobject.h b/rendering/framebufferobject.h index fc5644392..6f2b5764a 100644 --- a/rendering/framebufferobject.h +++ b/rendering/framebufferobject.h @@ -13,8 +13,14 @@ public: void Create(QOpenGLContext* ctx, int width, int height); void Destroy(); - const GLuint& buffer(); - const GLuint& texture(); + const GLuint& buffer() const; + const GLuint& texture() const; + + void BindBuffer() const; + void ReleaseBuffer() const; + + void BindTexture() const; + void ReleaseTexture() const; private: QOpenGLContext* ctx_; GLuint buffer_; diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index f38817648..55f534e59 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -24,7 +24,6 @@ extern "C" { #include } -#include #include #include #include @@ -86,8 +85,8 @@ void draw_clip(QOpenGLContext* ctx, GLuint fbo, GLuint texture, bool clear) { ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } -GLuint draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) { - fbo->bind(); +GLuint draw_clip(const FramebufferObject& fbo, GLuint texture, bool clear) { + fbo.BindBuffer(); if (clear) { glClear(GL_COLOR_BUFFER_BIT); @@ -99,9 +98,9 @@ GLuint draw_clip(QOpenGLFramebufferObject* fbo, GLuint texture, bool clear) { glBindTexture(GL_TEXTURE_2D, 0); - fbo->release(); + fbo.ReleaseBuffer(); - return fbo->texture(); + return fbo.texture(); } void process_effect(Clip* c, @@ -140,7 +139,7 @@ void process_effect(Clip* c, } else { // if the source texture is not already a framebuffer texture, // we'll need to make it one before drawing a superimpose effect on it - if (composite_texture != c->fbo[0]->texture() && composite_texture != c->fbo[1]->texture()) { + if (composite_texture != c->fbo[0].texture() && composite_texture != c->fbo[1].texture()) { draw_clip(c->fbo[!fbo_switcher], composite_texture, true); } @@ -167,10 +166,10 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { playhead = rescale_frame_number(playhead, params.nests.at(i)->sequence->frame_rate, s->frame_rate); } - if (params.video && params.nests.last()->fbo != nullptr) { - params.nests.last()->fbo[0]->bind(); + if (params.video && !params.nests.last()->fbo.isEmpty()) { + params.nests.last()->fbo[0].BindBuffer(); glClear(GL_COLOR_BUFFER_BIT); - final_fbo = params.nests.last()->fbo[0]->handle(); + final_fbo = params.nests.last()->fbo[0].buffer(); } } @@ -325,14 +324,14 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } // prepare framebuffers for backend drawing operations - if (c->fbo == nullptr) { + if (c->fbo.isEmpty()) { // create 3 fbos for nested sequences, 2 for most clips int fbo_count = (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) ? 3 : 2; - c->fbo = new QOpenGLFramebufferObject* [size_t(fbo_count)]; + c->fbo.resize(fbo_count); for (int j=0;jfbo[j] = new QOpenGLFramebufferObject(video_width, video_height); + c->fbo[j].Create(params.ctx, video_width, video_height); } } @@ -473,9 +472,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { GLuint backend_tex_1; GLuint backend_tex_2; if (params.nests.size() > 0) { - back_buffer_1 = params.nests.last()->fbo[1]->handle(); - backend_tex_1 = params.nests.last()->fbo[1]->texture(); - backend_tex_2 = params.nests.last()->fbo[2]->texture(); + back_buffer_1 = params.nests.last()->fbo[1].buffer(); + backend_tex_1 = params.nests.last()->fbo[1].texture(); + backend_tex_2 = params.nests.last()->fbo[2].texture(); } else { back_buffer_1 = params.backend_buffer1; backend_tex_1 = params.backend_attachment1; @@ -531,7 +530,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // copy front buffer to back buffer (only if we're using a blending mode) if (coords.blendmode >= 0) { if (params.nests.size() > 0) { - draw_clip(params.ctx, params.nests.last()->fbo[2]->handle(), params.nests.last()->fbo[0]->texture(), true); + draw_clip(params.ctx, params.nests.last()->fbo[2].buffer(), params.nests.last()->fbo[0].texture(), true); } else { draw_clip(params.ctx, params.backend_buffer2, params.main_attachment, true); } @@ -665,11 +664,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { glPopMatrix(); } -// qDebug() << "compose sequence took" << QDateTime::currentMSecsSinceEpoch() - time; - - if (!params.nests.isEmpty() && params.nests.last()->fbo != nullptr) { + if (!params.nests.isEmpty() && !params.nests.last()->fbo.isEmpty()) { // returns nested clip's texture - return params.nests.last()->fbo[0]->texture(); + return params.nests.last()->fbo[0].texture(); } return 0; diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 75afae44b..5a6c97408 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -56,7 +56,6 @@ Clip::Clip(Sequence* s) : closing_transition = nullptr; undeletable = false; replaced = false; - fbo = nullptr; open_ = false; reset(); @@ -515,18 +514,7 @@ void Clip::Close(bool wait) { } // delete framebuffers - if (fbo != nullptr) { - // delete 3 fbos for nested sequences, 2 for most clips - int fbo_count = (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) ? 3 : 2; - - for (int j=0;j fbo; QOpenGLTexture* texture; long texture_frame; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 581c6a11c..6c0c7238d 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -28,7 +28,6 @@ extern "C" { #include #include #include -#include #include #include #include From eef306ff1e9d928317069810d6ed7c3b95154fcc Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 11:23:30 +1100 Subject: [PATCH 018/133] added try/catch to processor retrieve --- rendering/renderfunctions.cpp | 2 +- rendering/renderthread.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 55f534e59..dcac906b5 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -375,7 +375,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { #ifndef NO_OCIO // convert to linear colorspace - if (olive::CurrentConfig.enable_color_management) + if (olive::CurrentConfig.enable_color_management && params.ocio_shader != nullptr) { params.ocio_shader->bind(); diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index ddb533568..586b85d6f 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -215,7 +215,15 @@ void RenderThread::set_up_ocio() // GET OCIO PROCESSOR // - OCIO::ConstProcessorRcPtr processor = OCIO::GetCurrentConfig()->getProcessor(transform); + OCIO::ConstProcessorRcPtr processor; + try { + processor = OCIO::GetCurrentConfig()->getProcessor(transform); + } catch(OCIO::Exception & e) { + qCritical() << e.what(); + ctx->functions()->glActiveTexture(GL_TEXTURE0); + return; + } + // // SET UP GLSL SHADER From c8496ac2a8fb2a12431a4018778bf3d0552bd063 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 15:53:54 +1100 Subject: [PATCH 019/133] began transition to gles --- effects/internal/internalshaders.qrc | 2 + effects/internal/pipeline.frag | 11 +++++ effects/internal/pipeline.vert | 16 ++++++ main.cpp | 2 + rendering/framebufferobject.cpp | 12 +++-- rendering/renderfunctions.cpp | 18 ++++++- rendering/renderthread.cpp | 6 +-- rendering/renderthread.h | 3 -- ui/viewerwidget.cpp | 73 +++++++++++++++++++++++++++- ui/viewerwidget.h | 6 ++- 10 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 effects/internal/pipeline.frag create mode 100644 effects/internal/pipeline.vert diff --git a/effects/internal/internalshaders.qrc b/effects/internal/internalshaders.qrc index ae51e0b27..52ec17302 100644 --- a/effects/internal/internalshaders.qrc +++ b/effects/internal/internalshaders.qrc @@ -5,5 +5,7 @@ cornerpin.vert premultiply.frag dropshadow.frag + pipeline.frag + pipeline.vert diff --git a/effects/internal/pipeline.frag b/effects/internal/pipeline.frag new file mode 100644 index 000000000..71719c428 --- /dev/null +++ b/effects/internal/pipeline.frag @@ -0,0 +1,11 @@ +#ifdef GL_ES +precision mediump int; +precision mediump float; +#endif + +uniform sampler2D texture; +varying vec2 v_texcoord; + +void main() { + gl_FragColor = texture2D(texture, v_texcoord); +} \ No newline at end of file diff --git a/effects/internal/pipeline.vert b/effects/internal/pipeline.vert new file mode 100644 index 000000000..8dc969907 --- /dev/null +++ b/effects/internal/pipeline.vert @@ -0,0 +1,16 @@ +#ifdef GL_ES +precision mediump int; +precision mediump float; +#endif + +uniform mat4 mvp_matrix; + +attribute vec4 a_position; +attribute vec2 a_texcoord; + +varying vec2 v_texcoord; + +void main() { + gl_Position = mvp_matrix * a_position; + v_texcoord = a_texcoord; +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp index 3a0957357..24ff13bb9 100644 --- a/main.cpp +++ b/main.cpp @@ -109,7 +109,9 @@ int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QSurfaceFormat format; + format.setVersion(3, 2); format.setDepthBufferSize(24); + format.setProfile(QSurfaceFormat::CompatibilityProfile); QSurfaceFormat::setDefaultFormat(format); QApplication a(argc, argv); diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 6ca510443..bb31ce231 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -1,6 +1,8 @@ #include "framebufferobject.h" #include +#include +#include FramebufferObject::FramebufferObject() : buffer_(0), @@ -39,16 +41,16 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - ctx->functions()->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nullptr ); // set texture filtering to bilinear - ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // attach texture to framebuffer - ctx->functions()->glFramebufferTexture2D( + ctx->extraFunctions()->glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0 ); diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index dcac906b5..32477ee9b 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -316,6 +316,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } else { // retrieve ID from c->texture textureID = c->texture->textureId(); + qDebug() << "tex 1" << textureID; } if (textureID == 0) { @@ -359,18 +360,22 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.nests.removeLast(); // compose_sequence() would have written to this clip's fbo[0], so we switch to fbo[1] - fbo_switcher = true; + fbo_switcher = !fbo_switcher; } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { if (!c->media()->to_footage()->alpha_is_premultiplied) { + // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); + qDebug() << "about to draw on" << fbo_switcher; textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); fbo_switcher = !fbo_switcher; + qDebug() << "tex 3" << textureID << fbo_switcher; + } #ifndef NO_OCIO @@ -382,11 +387,14 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.ocio_shader->setUniformValue("tex1", 0); params.ocio_shader->setUniformValue("tex2", 2); + qDebug() << "about to draw on" << fbo_switcher; textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); params.ocio_shader->release(); fbo_switcher = !fbo_switcher; + + qDebug() << "tex 4" << textureID << fbo_switcher; } #endif @@ -414,8 +422,11 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // run through all of the clip's effects for (int j=0;jeffects.size();j++) { + Effect* e = c->effects.at(j).get(); + qDebug() << "about to draw on" << fbo_switcher; process_effect(c, e, timecode, coords, textureID, fbo_switcher, params.texture_failed, kTransitionNone); + qDebug() << "tex 5" << textureID << fbo_switcher; } @@ -457,6 +468,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { + qDebug() << "final texture ID" << textureID; if (textureID > 0) { // set viewport to sequence size params.ctx->functions()->glViewport(0, 0, s->width, s->height); @@ -549,6 +561,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // Check if we're using a blend mode (< 0 means no blend mode) if (coords.blendmode < 0) { + qDebug() << "normal blending"; + params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_1); glColor4f(coords.opacity, coords.opacity, coords.opacity, coords.opacity); @@ -559,6 +573,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } else { + qDebug() << "blending mode?"; + // load background texture into texture unit 0 params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_2); diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 586b85d6f..19ebeed04 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #ifndef NO_OCIO @@ -160,7 +161,6 @@ const char * g_fragShaderText = "" #ifndef NO_OCIO void RenderThread::set_up_ocio() { - functions.initializeOpenGLFunctions(); // // SETUP LUT TEXTURE @@ -181,7 +181,7 @@ void RenderThread::set_up_ocio() ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Allocate storage for texture - functions.glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, + ctx->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, 0, GL_RGB,GL_FLOAT, ocio_lut_data); @@ -241,7 +241,7 @@ void RenderThread::set_up_ocio() processor->getGpuLut3D(ocio_lut_data, shaderDesc); glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); - functions.glTexSubImage3D(GL_TEXTURE_3D, 0, + ctx->extraFunctions()->glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, GL_RGB,GL_FLOAT, ocio_lut_data); diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 7c8cbf8f9..62e4ec2bc 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -28,7 +28,6 @@ #include #include #include -#include #include "timeline/sequence.h" #include "effects/effect.h" @@ -119,8 +118,6 @@ private: QString save_fn; GLvoid *pixel_buffer; int pixel_buffer_linesize; - - QOpenGLFunctions_2_0 functions; }; #endif // RENDERTHREAD_H diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 6c0c7238d..b96d0fc6b 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -27,6 +27,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ extern "C" { #include #include #include +#include +#include #include "panels/panels.h" #include "project/projectelements.h" @@ -68,7 +71,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) : gizmos(nullptr), selected_gizmo(nullptr), x_scroll(0), - y_scroll(0) + y_scroll(0), + pipeline_(nullptr) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -82,6 +86,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) : connect(renderer, SIGNAL(finished()), renderer, SLOT(deleteLater())); window = new ViewerWindow(this); + + projection_.setToIdentity(); } ViewerWidget::~ViewerWidget() { @@ -216,9 +222,14 @@ void ViewerWidget::retry() { } void ViewerWidget::initializeGL() { - initializeOpenGLFunctions(); + context()->functions()->initializeOpenGLFunctions(); connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection); + + pipeline_ = new QOpenGLShaderProgram(); + pipeline_->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/pipeline.vert"); + pipeline_->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/pipeline.frag"); + pipeline_->link(); } void ViewerWidget::frame_update() { @@ -252,10 +263,16 @@ void ViewerWidget::seek_from_click(int x) { void ViewerWidget::context_destroy() { makeCurrent(); + if (viewer->seq != nullptr) { close_active_clips(viewer->seq.get()); } + renderer->delete_ctx(); + + delete pipeline_; + pipeline_ = nullptr; + doneCurrent(); } @@ -539,6 +556,57 @@ void ViewerWidget::draw_gizmos() { } void ViewerWidget::paintGL() { + QOpenGLFunctions* f = context()->functions(); + QOpenGLExtraFunctions* xf = context()->extraFunctions(); + + f->glClearColor(0.0, 0.0, 0.0, 0.0); + + GLfloat vertices[] = { + -1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + }; + + GLfloat tex_coords[] = { + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0 + }; + + f->glViewport(0, 0, width(), height()); + + f->glClear(GL_COLOR_BUFFER_BIT); + + QOpenGLTexture texture(QImage("C:/Users/Matt/Desktop/bliss.png")); + + pipeline_->bind(); + texture.bind(); + + pipeline_->setUniformValue("mvp_matrix", projection_); + pipeline_->setUniformValue("texture", 0); + + GLuint vertex_location = pipeline_->attributeLocation("a_position"); + f->glEnableVertexAttribArray(vertex_location); + f->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + GLuint tex_location = pipeline_->attributeLocation("a_texcoord"); + f->glEnableVertexAttribArray(tex_location); + f->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, tex_coords); + + f->glDrawArrays(GL_TRIANGLES, 0, 6); + + texture.release(); + pipeline_->release(); + + /* if (waveform) { draw_waveform_func(); } else { @@ -615,4 +683,5 @@ void ViewerWidget::paintGL() { renderer->start_render(context(), viewer->seq.get()); } } + */ } diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index f01ae7063..edd5a3938 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -41,7 +41,7 @@ class Viewer; class QOpenGLFramebufferObject; struct GLTextureCoords; -class ViewerWidget : public QOpenGLWidget, QOpenGLFunctions +class ViewerWidget : public QOpenGLWidget { Q_OBJECT public: @@ -91,6 +91,10 @@ private: ViewerWindow* window; double x_scroll; double y_scroll; + + QMatrix4x4 projection_; + QOpenGLShaderProgram* pipeline_; + private slots: void context_destroy(); void retry(); From 05ac0314d7b3dedf44d70b2f2607a91a8696ac76 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 18:19:12 +1100 Subject: [PATCH 020/133] nearly finished gles transition --- effects/effect.cpp | 57 +++--- effects/effect.h | 41 ++-- effects/effectfield.cpp | 4 +- effects/effectgizmo.h | 2 +- effects/internal/cornerpineffect.cpp | 34 ++-- effects/internal/shakeeffect.cpp | 13 +- effects/internal/texteffect.cpp | 4 +- effects/internal/transformeffect.cpp | 49 +++-- olive.pro | 6 +- rendering/framebufferobject.cpp | 14 +- rendering/qopenglshaderprogramptr.cpp | 2 + rendering/qopenglshaderprogramptr.h | 8 + rendering/renderfunctions.cpp | 266 ++++++++++++++------------ rendering/renderfunctions.h | 16 ++ rendering/renderthread.cpp | 74 ++++--- rendering/renderthread.h | 6 +- timeline/clip.cpp | 6 +- ui/viewerwidget.cpp | 129 +++---------- ui/viewerwidget.h | 5 +- 19 files changed, 344 insertions(+), 392 deletions(-) create mode 100644 rendering/qopenglshaderprogramptr.cpp create mode 100644 rendering/qopenglshaderprogramptr.h diff --git a/effects/effect.cpp b/effects/effect.cpp index e5f356cf3..e87bae604 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -120,7 +120,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : parent_clip(c), meta(em), flags_(0), - glslProgram(nullptr), + shader_program_(nullptr), texture(nullptr), isOpen(false), bound(false), @@ -304,9 +304,9 @@ Effect::Effect(Clip* c, const EffectMeta *em) : for (int i=0;ipath.isEmpty() || (vertPath.isEmpty() && fragPath.isEmpty())) return; + if (!meta->path.isEmpty() || (shader_vert_path_.isEmpty() && shader_frag_path_.isEmpty())) return; QList effects_paths = get_effects_paths(); - const QString& test_fn = vertPath.isEmpty() ? fragPath : vertPath; + const QString& test_fn = shader_vert_path_.isEmpty() ? shader_frag_path_ : shader_vert_path_; for (int i=0;i(); validate_meta_path(); bool glsl_compiled = true; - if (!vertPath.isEmpty()) { - if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + vertPath)) { + if (!shader_vert_path_.isEmpty()) { + if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + shader_vert_path_)) { qInfo() << "Vertex shader added successfully"; } else { glsl_compiled = false; qWarning() << "Vertex shader could not be added"; } } - if (!fragPath.isEmpty()) { - if (glslProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + fragPath)) { + if (!shader_frag_path_.isEmpty()) { + if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + shader_frag_path_)) { qInfo() << "Fragment shader added successfully"; } else { glsl_compiled = false; @@ -753,7 +753,7 @@ void Effect::open() { } } if (glsl_compiled) { - if (glslProgram->link()) { + if (shader_program_->link()) { qInfo() << "Shader program linked successfully"; } else { qWarning() << "Shader program failed to link"; @@ -771,15 +771,12 @@ void Effect::close() { qWarning() << "Tried to close an effect that was already closed"; } delete_texture(); - if (glslProgram != nullptr) { - delete glslProgram; - glslProgram = nullptr; - } + shader_program_ = nullptr; isOpen = false; } bool Effect::is_glsl_linked() { - return glslProgram != nullptr && glslProgram->isLinked(); + return shader_program_ != nullptr && shader_program_->isLinked(); } void Effect::startEffect() { @@ -789,13 +786,13 @@ void Effect::startEffect() { } if (olive::CurrentRuntimeConfig.shaders_are_enabled && (Flags() & Effect::ShaderFlag) - && glslProgram->isLinked()) { - bound = glslProgram->bind(); + && shader_program_->isLinked()) { + bound = shader_program_->bind(); } } void Effect::endEffect() { - if (bound) glslProgram->release(); + if (bound) shader_program_->release(); bound = false; } @@ -827,9 +824,9 @@ EffectPtr Effect::copy(Clip *c) { } void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { - glslProgram->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height()); - glslProgram->setUniformValue("time", GLfloat(timecode)); - glslProgram->setUniformValue("iteration", iteration); + shader_program_->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height()); + shader_program_->setUniformValue("time", GLfloat(timecode)); + shader_program_->setUniformValue("iteration", iteration); for (int i=0;i(field); - glslProgram->setUniformValue(double_field->id().toUtf8().constData(), + shader_program_->setUniformValue(double_field->id().toUtf8().constData(), GLfloat(double_field->GetDoubleAt(timecode))); } break; case EffectField::EFFECT_FIELD_COLOR: { ColorField* color_field = static_cast(field); - glslProgram->setUniformValue( + shader_program_->setUniformValue( color_field->id().toUtf8().constData(), GLfloat(color_field->GetColorAt(timecode).redF()), GLfloat(color_field->GetColorAt(timecode).greenF()), @@ -856,10 +853,10 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { } break; case EffectField::EFFECT_FIELD_BOOL: - glslProgram->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool()); + shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool()); break; case EffectField::EFFECT_FIELD_COMBO: - glslProgram->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt()); + shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt()); break; // can you even send a string to a uniform value? @@ -1058,7 +1055,7 @@ void Effect::redraw(double) { } bool Effect::valueHasChanged(double timecode) { - if (cachedValues.size() == 0) { + if (cachedValues.isEmpty()) { for (int i=0;i(); 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)); + 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; diff --git a/effects/effectgizmo.h b/effects/effectgizmo.h index 30332b21d..e39aa34a2 100644 --- a/effects/effectgizmo.h +++ b/effects/effectgizmo.h @@ -45,7 +45,7 @@ class EffectGizmo : public QObject { public: EffectGizmo(Effect* parent, int type); - QVector world_pos; + QVector world_pos; QVector screen_pos; DoubleField* x_field1; diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index db802503b..632d20ae9 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -63,35 +63,31 @@ CornerPinEffect::CornerPinEffect(Clip* c, const EffectMeta *em) : Effect(c, em) bottom_right_gizmo->x_field1 = bottom_right_x; bottom_right_gizmo->y_field1 = bottom_right_y; - vertPath = "cornerpin.vert"; - fragPath = "cornerpin.frag"; + shader_vert_path_ = "cornerpin.vert"; + shader_frag_path_ = "cornerpin.frag"; } void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int) { - coords.vertexTopLeftX += top_left_x->GetDoubleAt(timecode); - coords.vertexTopLeftY += top_left_y->GetDoubleAt(timecode); + coords.vertex_top_left += QVector3D(top_left_x->GetDoubleAt(timecode), top_left_y->GetDoubleAt(timecode), 0.0f); - coords.vertexTopRightX += top_right_x->GetDoubleAt(timecode); - coords.vertexTopRightY += top_right_y->GetDoubleAt(timecode); + coords.vertex_top_right += QVector3D(top_right_x->GetDoubleAt(timecode), top_right_y->GetDoubleAt(timecode), 0.0f); - coords.vertexBottomLeftX += bottom_left_x->GetDoubleAt(timecode); - coords.vertexBottomLeftY += bottom_left_y->GetDoubleAt(timecode); + coords.vertex_bottom_left += QVector3D(bottom_left_x->GetDoubleAt(timecode), bottom_left_y->GetDoubleAt(timecode), 0.0f); - coords.vertexBottomRightX += bottom_right_x->GetDoubleAt(timecode); - coords.vertexBottomRightY += bottom_right_y->GetDoubleAt(timecode); + coords.vertex_bottom_right += QVector3D(bottom_right_x->GetDoubleAt(timecode), bottom_right_y->GetDoubleAt(timecode), 0.0f); } void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords, int) { - glslProgram->setUniformValue("p0", GLfloat(coords.vertexBottomLeftX), GLfloat(coords.vertexBottomLeftY)); - glslProgram->setUniformValue("p1", GLfloat(coords.vertexBottomRightX), GLfloat(coords.vertexBottomRightY)); - glslProgram->setUniformValue("p2", GLfloat(coords.vertexTopLeftX), GLfloat(coords.vertexTopLeftY)); - glslProgram->setUniformValue("p3", GLfloat(coords.vertexTopRightX), GLfloat(coords.vertexTopRightY)); - glslProgram->setUniformValue("perspective", perspective->GetBoolAt(timecode)); + shader_program_->setUniformValue("p0", coords.vertex_bottom_left.x(), coords.vertex_bottom_left.y()); + shader_program_->setUniformValue("p1", coords.vertex_bottom_right.x(), coords.vertex_bottom_right.y()); + shader_program_->setUniformValue("p2", coords.vertex_top_left.x(), coords.vertex_top_left.y()); + shader_program_->setUniformValue("p3", coords.vertex_top_right.x(), coords.vertex_top_right.y()); + shader_program_->setUniformValue("perspective", perspective->GetBoolAt(timecode)); } void CornerPinEffect::gizmo_draw(double, GLTextureCoords &coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + top_left_gizmo->world_pos[0] = coords.vertex_top_left; + top_right_gizmo->world_pos[0] = coords.vertex_top_right; + bottom_right_gizmo->world_pos[0] = coords.vertex_bottom_right; + bottom_left_gizmo->world_pos[0] = coords.vertex_bottom_left; } diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 6467e2bb5..b8b92a496 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -78,15 +78,10 @@ void ShakeEffect::process_coords(double timecode, GLTextureCoords& coords, int) yoff *= multiplier; rotoff *= rotmult; - coords.vertexTopLeftX += xoff; - coords.vertexTopRightX += xoff; - coords.vertexBottomLeftX += xoff; - coords.vertexBottomRightX += xoff; - - coords.vertexTopLeftY += yoff; - coords.vertexTopRightY += yoff; - coords.vertexBottomLeftY += yoff; - coords.vertexBottomRightY += yoff; + coords.vertex_top_left += QVector3D(xoff, yoff, 0.0); + coords.vertex_top_right += QVector3D(xoff, yoff, 0.0); + coords.vertex_bottom_left += QVector3D(xoff, yoff, 0.0); + coords.vertex_bottom_right += QVector3D(xoff, yoff, 0.0); glRotatef(rotoff, 0, 0, 1); } diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index ee183f0b3..5a805461d 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -151,8 +151,8 @@ TextEffect::TextEffect(Clip* c, const EffectMeta* em) : connect(shadow_bool, SIGNAL(Toggled(bool)), this, SLOT(shadow_enable(bool))); connect(outline_bool, SIGNAL(Toggled(bool)), this, SLOT(outline_enable(bool))); - vertPath = "common.vert"; - fragPath = "dropshadow.frag"; + shader_vert_path_ = "common.vert"; + shader_frag_path_ = "dropshadow.frag"; } void TextEffect::redraw(double timecode) { diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index f623661fd..0e766ff1b 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -198,14 +198,10 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i int anchor_x_offset = qRound(anchor_x_box->GetDoubleAt(timecode)); int anchor_y_offset = qRound(anchor_y_box->GetDoubleAt(timecode)); - coords.vertexTopLeftX -= anchor_x_offset; - coords.vertexTopRightX -= anchor_x_offset; - coords.vertexBottomLeftX -= anchor_x_offset; - coords.vertexBottomRightX -= anchor_x_offset; - coords.vertexTopLeftY -= anchor_y_offset; - coords.vertexTopRightY -= anchor_y_offset; - coords.vertexBottomLeftY -= anchor_y_offset; - coords.vertexBottomRightY -= anchor_y_offset; + coords.vertex_top_left -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); + coords.vertex_top_right -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); + coords.vertex_bottom_left -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); + coords.vertex_bottom_right -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); // rotation glRotated(rotation->GetDoubleAt(timecode), 0, 0, 1); @@ -223,19 +219,30 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i } void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) { - top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5)); - top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5)); - bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5)); - bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); - left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5)); + top_left_gizmo->world_pos[0] = coords.vertex_top_left; + top_right_gizmo->world_pos[0] = coords.vertex_top_right; + bottom_right_gizmo->world_pos[0] = coords.vertex_bottom_right; + bottom_left_gizmo->world_pos[0] = coords.vertex_bottom_left; - rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1)); + top_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_top_left.x(), coords.vertex_top_right.x(), 0.5), + lerp(coords.vertex_top_left.y(), coords.vertex_top_right.y(), 0.5), + 0.0f); + right_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_top_right.x(), coords.vertex_bottom_right.x(), 0.5), + lerp(coords.vertex_top_right.y(), coords.vertex_bottom_right.y(), 0.5), + 0.0f); + bottom_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_bottom_right.x(), coords.vertex_bottom_left.x(), 0.5), + lerp(coords.vertex_bottom_right.y(), coords.vertex_bottom_left.y(), 0.5), + 0.0f); + left_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_bottom_left.x(), coords.vertex_top_left.x(), 0.5), + lerp(coords.vertex_bottom_left.y(), coords.vertex_top_left.y(), 0.5), + 0.0f); - rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY); - rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY); - rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY); - rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY); + rotate_gizmo->world_pos[0] = QVector3D(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), + lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1), + 0.0f); + + rect_gizmo->world_pos[0] = coords.vertex_top_left; + rect_gizmo->world_pos[1] = coords.vertex_top_right; + rect_gizmo->world_pos[2] = coords.vertex_bottom_right; + rect_gizmo->world_pos[3] = coords.vertex_bottom_left; } diff --git a/olive.pro b/olive.pro index ad25ff17d..d71a71ddd 100644 --- a/olive.pro +++ b/olive.pro @@ -175,7 +175,8 @@ SOURCES += \ undo/undostack.cpp \ effects/internal/richtexteffect.cpp \ ui/blur.cpp \ - ui/menu.cpp + ui/menu.cpp \ + rendering/qopenglshaderprogramptr.cpp HEADERS += \ ui/mainwindow.h \ @@ -304,7 +305,8 @@ HEADERS += \ undo/undostack.h \ effects/internal/richtexteffect.h \ ui/blur.h \ - ui/menu.h + ui/menu.h \ + rendering/qopenglshaderprogramptr.h FORMS += diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index bb31ce231..cf2f7d0cb 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -41,19 +41,23 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA8, GL_UNSIGNED_BYTE, nullptr + ctx->functions()->glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr ); // set texture filtering to bilinear - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // attach texture to framebuffer ctx->extraFunctions()->glFramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0 + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0 ); + // clear new texture + ctx->functions()->glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); + // release texture ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); diff --git a/rendering/qopenglshaderprogramptr.cpp b/rendering/qopenglshaderprogramptr.cpp new file mode 100644 index 000000000..f5aeb84af --- /dev/null +++ b/rendering/qopenglshaderprogramptr.cpp @@ -0,0 +1,2 @@ +#include "qopenglshaderprogramptr.h" + diff --git a/rendering/qopenglshaderprogramptr.h b/rendering/qopenglshaderprogramptr.h new file mode 100644 index 000000000..25c8fc6ff --- /dev/null +++ b/rendering/qopenglshaderprogramptr.h @@ -0,0 +1,8 @@ +#ifndef QOPENGLSHADERPROGRAMPTR_H +#define QOPENGLSHADERPROGRAMPTR_H + +#include + +using QOpenGLShaderProgramPtr = std::shared_ptr; + +#endif // QOPENGLSHADERPROGRAMPTR_H diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 32477ee9b..fffdd566b 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -39,71 +39,111 @@ namespace OCIO = OCIO_NAMESPACE; #include "effects/effect.h" #include "project/footage.h" #include "effects/transition.h" - #include "ui/collapsiblewidget.h" - #include "rendering/audio.h" - #include "global/math.h" #include "global/config.h" - #include "panels/timeline.h" #include "panels/viewer.h" +#include "qopenglshaderprogramptr.h" -void full_blit() { - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, 1, 0, 1, -1, 1); +GLfloat olive::rendering::blit_vertices[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, - glBegin(GL_QUADS); - glTexCoord2f(0, 0); // top left - glVertex2f(0, 0); // top left - glTexCoord2f(1, 0); // top right - glVertex2f(1, 0); // top right - glTexCoord2f(1, 1); // bottom right - glVertex2f(1, 1); // bottom right - glTexCoord2f(0, 1); // bottom left - glVertex2f(0, 1); // bottom left - glEnd(); + -1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f +}; - glPopMatrix(); +GLfloat olive::rendering::blit_texcoords[] = { + 0.0, 0.0, + 1.0, 0.0, + 1.0, 1.0, + + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0 +}; + +void olive::rendering::Blit(QOpenGLShaderProgram* pipeline) { + QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); + + pipeline->bind(); + + pipeline->setUniformValue("mvp_matrix", QMatrix4x4()); + pipeline->setUniformValue("texture", 0); + + GLuint vertex_location = pipeline->attributeLocation("a_position"); + func->glEnableVertexAttribArray(vertex_location); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, blit_vertices); + + GLuint tex_location = pipeline->attributeLocation("a_texcoord"); + func->glEnableVertexAttribArray(tex_location); + func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, blit_texcoords); + + func->glDrawArrays(GL_TRIANGLES, 0, 6); + + pipeline->release(); } -void draw_clip(QOpenGLContext* ctx, GLuint fbo, GLuint texture, bool clear) { +QOpenGLShaderProgramPtr olive::rendering::GetPipeline() +{ + QOpenGLShaderProgramPtr program = std::make_shared(); + + program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/pipeline.vert"); + program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/pipeline.frag"); + program->link(); + + return program; +} + +void draw_clip(QOpenGLContext* ctx, + QOpenGLShaderProgram* pipeline, + GLuint fbo, + GLuint texture, + bool clear) { ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); if (clear) { - glClear(GL_COLOR_BUFFER_BIT); + ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); } - glBindTexture(GL_TEXTURE_2D, texture); + ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture); - full_blit(); + olive::rendering::Blit(pipeline); - glBindTexture(GL_TEXTURE_2D, 0); + ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } -GLuint draw_clip(const FramebufferObject& fbo, GLuint texture, bool clear) { +GLuint draw_clip(QOpenGLContext* ctx, + QOpenGLShaderProgram* pipeline, + const FramebufferObject& fbo, + GLuint texture, + bool clear) { fbo.BindBuffer(); if (clear) { - glClear(GL_COLOR_BUFFER_BIT); + ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); } - glBindTexture(GL_TEXTURE_2D, texture); + ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture); - full_blit(); + olive::rendering::Blit(pipeline); - glBindTexture(GL_TEXTURE_2D, 0); + ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); fbo.ReleaseBuffer(); return fbo.texture(); } -void process_effect(Clip* c, +void process_effect(QOpenGLContext* ctx, + QOpenGLShaderProgram* pipeline, + Clip* c, Effect* e, double timecode, GLTextureCoords& coords, @@ -121,7 +161,7 @@ void process_effect(Clip* c, if (can_process_shaders && e->is_glsl_linked()) { for (int i=0;igetIterations();i++) { e->process_shader(timecode, coords, i); - composite_texture = draw_clip(c->fbo[fbo_switcher], composite_texture, true); + composite_texture = draw_clip(ctx, pipeline, c->fbo[fbo_switcher], composite_texture, true); fbo_switcher = !fbo_switcher; } } @@ -140,10 +180,10 @@ void process_effect(Clip* c, // if the source texture is not already a framebuffer texture, // we'll need to make it one before drawing a superimpose effect on it if (composite_texture != c->fbo[0].texture() && composite_texture != c->fbo[1].texture()) { - draw_clip(c->fbo[!fbo_switcher], composite_texture, true); + draw_clip(ctx, pipeline, c->fbo[!fbo_switcher], composite_texture, true); } - composite_texture = draw_clip(c->fbo[!fbo_switcher], superimpose_texture, false); + composite_texture = draw_clip(ctx, pipeline, c->fbo[!fbo_switcher], superimpose_texture, false); } } e->endEffect(); @@ -152,8 +192,6 @@ void process_effect(Clip* c, } GLuint compose_sequence(ComposeSequenceParams ¶ms) { -// qint64 time = QDateTime::currentMSecsSinceEpoch(); - GLuint final_fbo = params.main_buffer; Sequence* s = params.seq; @@ -266,16 +304,19 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } } + QMatrix4x4 projection; + if (params.video) { // set default coordinates based on the sequence, with 0 in the direct center - glPushMatrix(); - glLoadIdentity(); + //glPushMatrix(); + //glLoadIdentity(); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); int half_width = s->width/2; int half_height = s->height/2; - glOrtho(-half_width, half_width, -half_height, half_height, -1, 10); + //glOrtho(-half_width, half_width, -half_height, half_height, -1, 10); + projection.ortho(-half_width, half_width, -half_height, half_height, -1, 1); } // loop through current clips @@ -296,9 +337,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // if clip is a video clip if (c->track() < 0) { - // reset OpenGL to full color - glColor4f(1.0, 1.0, 1.0, 1.0); - // textureID variable contains texture to be drawn on screen at the end GLuint textureID = 0; @@ -316,7 +354,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } else { // retrieve ID from c->texture textureID = c->texture->textureId(); - qDebug() << "tex 1" << textureID; } if (textureID == 0) { @@ -339,15 +376,15 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // if clip should actually be shown on screen in this frame if (playhead >= c->timeline_in(true) && playhead < c->timeline_out(true)) { - glPushMatrix(); // simple bool for switching between the two framebuffers bool fbo_switcher = false; - glViewport(0, 0, video_width, video_height); + params.ctx->functions()->glViewport(0, 0, video_width, video_height); if (c->media() != nullptr) { if (c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { + // for a nested sequence, run this function again on that sequence and retrieve the texture // add nested sequence to nest list @@ -361,6 +398,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // compose_sequence() would have written to this clip's fbo[0], so we switch to fbo[1] fbo_switcher = !fbo_switcher; + } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { if (!c->media()->to_footage()->alpha_is_premultiplied) { @@ -368,13 +406,11 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); - qDebug() << "about to draw on" << fbo_switcher; - textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); + textureID = draw_clip(params.ctx, params.pipeline, c->fbo[fbo_switcher], textureID, true); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); fbo_switcher = !fbo_switcher; - qDebug() << "tex 3" << textureID << fbo_switcher; } @@ -387,14 +423,12 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.ocio_shader->setUniformValue("tex1", 0); params.ocio_shader->setUniformValue("tex2", 2); - qDebug() << "about to draw on" << fbo_switcher; - textureID = draw_clip(c->fbo[fbo_switcher], textureID, true); + textureID = draw_clip(params.ctx, params.pipeline, c->fbo[fbo_switcher], textureID, true); params.ocio_shader->release(); fbo_switcher = !fbo_switcher; - qDebug() << "tex 4" << textureID << fbo_switcher; } #endif @@ -403,15 +437,14 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // set up default coordinates for drawing the clip GLTextureCoords coords; - coords.grid_size = 1; - coords.vertexTopLeftX = coords.vertexBottomLeftX = -video_width/2; - coords.vertexTopLeftY = coords.vertexTopRightY = -video_height/2; - coords.vertexTopRightX = coords.vertexBottomRightX = video_width/2; - coords.vertexBottomLeftY = coords.vertexBottomRightY = video_height/2; - coords.vertexBottomLeftZ = coords.vertexBottomRightZ = coords.vertexTopLeftZ = coords.vertexTopRightZ = 1; - coords.textureTopLeftY = coords.textureTopRightY = coords.textureTopLeftX = coords.textureBottomLeftX = 0.0; - coords.textureBottomLeftY = coords.textureBottomRightY = coords.textureTopRightX = coords.textureBottomRightX = 1.0; - coords.textureTopLeftQ = coords.textureTopRightQ = coords.textureTopLeftQ = coords.textureBottomLeftQ = 1; + coords.vertex_top_left = QVector3D(-video_width/2, -video_height/2, 0.0f); + coords.vertex_top_right = QVector3D(video_width/2, -video_height/2, 0.0f); + coords.vertex_bottom_left = QVector3D(-video_width/2, video_height/2, 0.0f); + coords.vertex_bottom_right = QVector3D(video_width/2, video_height/2, 0.0f); + coords.texture_top_left = QVector2D(0.0f, 0.0f); + coords.texture_top_right = QVector2D(1.0f, 0.0f); + coords.texture_bottom_left = QVector2D(0.0f, 1.0f); + coords.texture_bottom_right = QVector2D(1.0f, 1.0f); coords.blendmode = -1; coords.opacity = 1.0; @@ -424,9 +457,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { for (int j=0;jeffects.size();j++) { Effect* e = c->effects.at(j).get(); - qDebug() << "about to draw on" << fbo_switcher; - process_effect(c, e, timecode, coords, textureID, fbo_switcher, params.texture_failed, kTransitionNone); - qDebug() << "tex 5" << textureID << fbo_switcher; + process_effect(params.ctx, params.pipeline, c, e, timecode, coords, textureID, fbo_switcher, params.texture_failed, kTransitionNone); } @@ -435,7 +466,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { if (c->opening_transition != nullptr) { int transition_progress = playhead - c->timeline_in(true); if (transition_progress < c->opening_transition->get_length()) { - process_effect(c, c->opening_transition.get(), double(transition_progress)/double(c->opening_transition->get_length()), coords, textureID, fbo_switcher, params.texture_failed, kTransitionOpening); + process_effect(params.ctx, params.pipeline, c, c->opening_transition.get(), double(transition_progress)/double(c->opening_transition->get_length()), coords, textureID, fbo_switcher, params.texture_failed, kTransitionOpening); } } @@ -443,14 +474,16 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { if (c->closing_transition != nullptr) { int transition_progress = playhead - (c->timeline_out(true) - c->closing_transition->get_length()); if (transition_progress >= 0 && transition_progress < c->closing_transition->get_length()) { - process_effect(c, c->closing_transition.get(), double(transition_progress)/double(c->closing_transition->get_length()), coords, textureID, fbo_switcher, params.texture_failed, kTransitionClosing); + process_effect(params.ctx, params.pipeline, c, c->closing_transition.get(), double(transition_progress)/double(c->closing_transition->get_length()), coords, textureID, fbo_switcher, params.texture_failed, kTransitionClosing); } } // == EFFECT CODE END == - // Check whether the parent clip is auto-scaledc + // Check whether the parent clip is auto-scaled + // TODO redo this + /* if (c->autoscaled() && (video_width != s->width && video_height != s->height)) { @@ -459,6 +492,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { float scale_multiplier = qMin(width_multiplier, height_multiplier); glScalef(scale_multiplier, scale_multiplier, 1); } + */ // Configure effect gizmos if they exist if (params.gizmos != nullptr) { @@ -468,7 +502,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { - qDebug() << "final texture ID" << textureID; if (textureID > 0) { // set viewport to sequence size params.ctx->functions()->glViewport(0, 0, s->width, s->height); @@ -496,32 +529,57 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // render a backbuffer params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, back_buffer_1); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); + params.ctx->functions()->glClearColor(0.0, 0.0, 0.0, 0.0); + params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); // bind final clip texture - glBindTexture(GL_TEXTURE_2D, textureID); + params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, textureID); // set texture filter to bilinear params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // draw clip on screen according to gl coordinates - glBegin(GL_QUADS); + params.pipeline->bind(); - glTexCoord2f(coords.textureTopLeftX, coords.textureTopLeftY); // top left - glVertex2f(coords.vertexTopLeftX, coords.vertexTopLeftY); // top left - glTexCoord2f(coords.textureTopRightX, coords.textureTopRightY); // top right - glVertex2f(coords.vertexTopRightX, coords.vertexTopRightY); // top right - glTexCoord2f(coords.textureBottomRightX, coords.textureBottomRightY); // bottom right - glVertex2f(coords.vertexBottomRightX, coords.vertexBottomRightY); // bottom right - glTexCoord2f(coords.textureBottomLeftX, coords.textureBottomLeftY); // bottom left - glVertex2f(coords.vertexBottomLeftX, coords.vertexBottomLeftY); // bottom left + params.pipeline->setUniformValue("mvp_matrix", projection); + params.pipeline->setUniformValue("texture", 0); - glEnd(); + GLfloat vertices[] = { + coords.vertex_top_left.x(), coords.vertex_top_left.y(), 0.0f, + coords.vertex_top_right.x(), coords.vertex_top_right.y(), 0.0f, + coords.vertex_bottom_right.x(), coords.vertex_bottom_right.y(), 0.0f, + + coords.vertex_top_left.x(), coords.vertex_top_left.y(), 0.0f, + coords.vertex_bottom_left.x(), coords.vertex_bottom_left.y(), 0.0f, + coords.vertex_bottom_right.x(), coords.vertex_bottom_right.y(), 0.0f, + }; + + GLfloat texcoords[] = { + coords.texture_top_left.x(), coords.texture_top_left.y(), + coords.texture_top_right.x(), coords.texture_top_right.y(), + coords.texture_bottom_right.x(), coords.texture_bottom_right.y(), + + coords.texture_top_left.x(), coords.texture_top_left.y(), + + coords.texture_bottom_left.x(), coords.texture_bottom_left.y(), + coords.texture_bottom_right.x(), coords.texture_bottom_right.y(), + }; + + GLuint vertex_location = params.pipeline->attributeLocation("a_position"); + params.ctx->functions()->glEnableVertexAttribArray(vertex_location); + params.ctx->functions()->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + GLuint tex_location = params.pipeline->attributeLocation("a_texcoord"); + params.ctx->functions()->glEnableVertexAttribArray(tex_location); + params.ctx->functions()->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, texcoords); + + params.ctx->functions()->glDrawArrays(GL_TRIANGLES, 0, 6); + + params.pipeline->release(); // release final clip texture - glBindTexture(GL_TEXTURE_2D, 0); + params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -542,9 +600,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // copy front buffer to back buffer (only if we're using a blending mode) if (coords.blendmode >= 0) { if (params.nests.size() > 0) { - draw_clip(params.ctx, params.nests.last()->fbo[2].buffer(), params.nests.last()->fbo[0].texture(), true); + draw_clip(params.ctx, params.pipeline, params.nests.last()->fbo[2].buffer(), params.nests.last()->fbo[0].texture(), true); } else { - draw_clip(params.ctx, params.backend_buffer2, params.main_attachment, true); + draw_clip(params.ctx, params.pipeline, params.backend_buffer2, params.main_attachment, true); } } @@ -561,20 +619,14 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // Check if we're using a blend mode (< 0 means no blend mode) if (coords.blendmode < 0) { - qDebug() << "normal blending"; - params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_1); - glColor4f(coords.opacity, coords.opacity, coords.opacity, coords.opacity); - - full_blit(); + olive::rendering::Blit(params.pipeline); params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); } else { - qDebug() << "blending mode?"; - // load background texture into texture unit 0 params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_2); @@ -590,9 +642,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.blend_mode_program->setUniformValue("background", 0); params.blend_mode_program->setUniformValue("foreground", 1); - glClear(GL_COLOR_BUFFER_BIT); + params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); - full_blit(); + olive::rendering::Blit(params.pipeline); // release blend mode shader params.blend_mode_program->release(); @@ -615,18 +667,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // == END FINAL DRAW ON SEQUENCE BUFFER == } - // prepare gizmos - /* - if ((*params.gizmos) != nullptr - && params.nests.isEmpty() - && ((*params.gizmos) == first_gizmo_effect - || (*params.gizmos) == selected_effect)) { - (*params.gizmos)->gizmo_draw(timecode, coords); // set correct gizmo coords - (*params.gizmos)->gizmo_world_to_screen(); // convert gizmo coords to screen coords - } - */ - - glPopMatrix(); } } else { if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { @@ -646,22 +686,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } } - - /* - // visually update all the keyframe values - if (c->sequence == params.seq) { // only if you can currently see them - double ts = (playhead - c->timeline_in(true) + c->clip_in(true))/s->frame_rate; - for (int i=0;ieffects.size();i++) { - EffectPtr e = c->effects.at(i); - for (int j=0;jrow_count();j++) { - EffectRow* r = e->row(j); - for (int k=0;kfieldCount();k++) { - r->field(k)->validate_keyframe_data(ts); - } - } - } - } - */ } } else { params.texture_failed = true; diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 6e0205bc1..03bffa4e3 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -52,6 +52,13 @@ struct ComposeSequenceParams { */ QOpenGLContext* ctx; + /** + * @brief The OpenGL pipeline used for rendering + * + * \see olive::rendering::GetPipeline(). + */ + QOpenGLShaderProgram* pipeline; + /** * @brief The sequence to compose * @@ -403,4 +410,13 @@ void close_active_clips(Sequence* s); void UpdateOCIOGLState(const ComposeSequenceParams ¶ms); +namespace olive { + namespace rendering { + extern GLfloat blit_vertices[]; + extern GLfloat blit_texcoords[]; + void Blit(QOpenGLShaderProgram* pipeline); + QOpenGLShaderProgramPtr GetPipeline(); + } +} + #endif // RENDERFUNCTIONS_H diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 19ebeed04..9741814bc 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -32,10 +32,10 @@ namespace OCIO = OCIO_NAMESPACE; #endif -#include "rendering/renderfunctions.h" #include "timeline/sequence.h" #include "effects/effectloaders.h" #include "global/config.h" +#include "rendering/renderfunctions.h" RenderThread::RenderThread() : gizmos(nullptr), @@ -102,12 +102,14 @@ void RenderThread::run() { if (blend_mode_program == nullptr) { delete_shaders(); - blend_mode_program = new QOpenGLShaderProgram(); + blend_mode_program = std::make_shared(); blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert"); olive::effects_loaded.lock(); blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader); olive::effects_loaded.unlock(); blend_mode_program->link(); + + pipeline_program = olive::rendering::GetPipeline(); } #ifndef NO_OCIO @@ -148,15 +150,15 @@ const GLuint &RenderThread::get_texture() } const char * g_fragShaderText = "" -"\n" -"uniform sampler2D tex1;\n" -"uniform sampler3D tex2;\n" -"\n" -"void main()\n" -"{\n" -" vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n" -" gl_FragColor = OCIODisplay(col, tex2);\n" -"}\n"; + "\n" + "uniform sampler2D tex1;\n" + "uniform sampler3D tex2;\n" + "\n" + "void main()\n" + "{\n" + " vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n" + " gl_FragColor = OCIODisplay(col, tex2);\n" + "}\n"; #ifndef NO_OCIO void RenderThread::set_up_ocio() @@ -182,8 +184,8 @@ void RenderThread::set_up_ocio() // Allocate storage for texture ctx->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB,GL_FLOAT, ocio_lut_data); + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + 0, GL_RGB,GL_FLOAT, ocio_lut_data); // // SET UP OCIO DISPLAY @@ -240,17 +242,17 @@ void RenderThread::set_up_ocio() processor->getGpuLut3D(ocio_lut_data, shaderDesc); - glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); + ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); ctx->extraFunctions()->glTexSubImage3D(GL_TEXTURE_3D, 0, - 0, 0, 0, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - GL_RGB,GL_FLOAT, ocio_lut_data); + 0, 0, 0, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + GL_RGB,GL_FLOAT, ocio_lut_data); QString shader_text = processor->getGpuShaderText(shaderDesc); shader_text.append("\n"); shader_text.append(g_fragShaderText); - ocio_shader = new QOpenGLShaderProgram(); + ocio_shader = std::make_shared(); ocio_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, shader_text); ocio_shader->link(); @@ -264,7 +266,6 @@ void RenderThread::destroy_ocio() ctx->functions()->glDeleteTextures(1, &ocio_lut_texture); ocio_lut_texture = 0; - delete ocio_shader; ocio_shader = nullptr; } #endif @@ -279,9 +280,10 @@ void RenderThread::paint() { params.texture_failed = false; params.wait_for_mutexes = true; params.playback_speed = 1; - params.blend_mode_program = blend_mode_program; + params.blend_mode_program = blend_mode_program.get(); + params.pipeline = pipeline_program.get(); #ifndef NO_OCIO - params.ocio_shader = ocio_shader; + params.ocio_shader = ocio_shader.get(); #endif params.backend_buffer1 = back_buffer_1.buffer(); params.backend_buffer2 = back_buffer_2.buffer(); @@ -300,15 +302,8 @@ void RenderThread::paint() { // bind framebuffer for drawing ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, params.main_buffer); - glLoadIdentity(); - - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); + ctx->functions()->glClearColor(0.0, 0.0, 0.0, 0.0); + ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); compose_sequence(params); @@ -339,13 +334,13 @@ void RenderThread::paint() { ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, params.main_buffer); // store pixels in buffer - glReadPixels(0, - 0, - pixel_buffer_linesize == 0 ? tex_width : pixel_buffer_linesize, - tex_height, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixel_buffer); + ctx->functions()->glReadPixels(0, + 0, + pixel_buffer_linesize == 0 ? tex_width : pixel_buffer_linesize, + tex_height, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixel_buffer); // release current read buffer ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); @@ -353,9 +348,6 @@ void RenderThread::paint() { pixel_buffer = nullptr; } - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); - // release ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } @@ -410,8 +402,8 @@ void RenderThread::delete_buffers() { } void RenderThread::delete_shaders() { - delete blend_mode_program; blend_mode_program = nullptr; + pipeline_program = nullptr; } void RenderThread::delete_ctx() { diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 62e4ec2bc..29e2d5091 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -32,6 +32,7 @@ #include "timeline/sequence.h" #include "effects/effect.h" #include "rendering/framebufferobject.h" +#include "qopenglshaderprogramptr.h" #ifndef NO_OCIO // copied from source code to OCIODisplay @@ -81,7 +82,7 @@ private: // OpenColorIO variables float ocio_lut_data[OCIO_NUM_3D_ENTRIES]; GLuint ocio_lut_texture; - QOpenGLShaderProgram* ocio_shader; + QOpenGLShaderProgramPtr ocio_shader; QString ocio_loaded_config; #endif @@ -102,7 +103,8 @@ private: QOffscreenSurface surface; QOpenGLContext* share_ctx; QOpenGLContext* ctx; - QOpenGLShaderProgram* blend_mode_program; + QOpenGLShaderProgramPtr blend_mode_program; + QOpenGLShaderProgramPtr pipeline_program; FramebufferObject back_buffer_1; FramebufferObject back_buffer_2; diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 5a6c97408..5c9ebbf07 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -573,7 +573,9 @@ bool Clip::Retrieve() texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); } - glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); // 2 data buffers to ping-pong between bool using_db_1 = true; @@ -611,7 +613,7 @@ bool Clip::Retrieve() delete [] data_buffer_2; } - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); ret = true; } else { diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index b96d0fc6b..35e167882 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -71,8 +71,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : gizmos(nullptr), selected_gizmo(nullptr), x_scroll(0), - y_scroll(0), - pipeline_(nullptr) + y_scroll(0) { setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -80,19 +79,14 @@ ViewerWidget::ViewerWidget(QWidget *parent) : setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu())); - renderer = new RenderThread(); - renderer->start(QThread::HighestPriority); - connect(renderer, SIGNAL(ready()), this, SLOT(queue_repaint())); - connect(renderer, SIGNAL(finished()), renderer, SLOT(deleteLater())); + renderer.start(QThread::HighestPriority); + connect(&renderer, SIGNAL(ready()), this, SLOT(queue_repaint())); window = new ViewerWindow(this); - - projection_.setToIdentity(); } ViewerWidget::~ViewerWidget() { - renderer->cancel(); - delete renderer; + renderer.cancel(); } void ViewerWidget::delete_function() { @@ -174,7 +168,7 @@ void ViewerWidget::save_frame() { fn += selected_ext; } - renderer->start_render(context(), viewer->seq.get(), fn); + renderer.start_render(context(), viewer->seq.get(), fn); } } @@ -226,10 +220,7 @@ void ViewerWidget::initializeGL() { connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection); - pipeline_ = new QOpenGLShaderProgram(); - pipeline_->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/pipeline.vert"); - pipeline_->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/pipeline.frag"); - pipeline_->link(); + pipeline_ = olive::rendering::GetPipeline(); } void ViewerWidget::frame_update() { @@ -239,7 +230,7 @@ void ViewerWidget::frame_update() { update(); } else { doneCurrent(); - renderer->start_render(context(), viewer->seq.get()); + renderer.start_render(context(), viewer->seq.get()); } // render the audio @@ -248,7 +239,7 @@ void ViewerWidget::frame_update() { } RenderThread *ViewerWidget::get_renderer() { - return renderer; + return &renderer; } void ViewerWidget::set_scroll(double x, double y) { @@ -268,9 +259,8 @@ void ViewerWidget::context_destroy() { close_active_clips(viewer->seq.get()); } - renderer->delete_ctx(); + renderer.delete_ctx(); - delete pipeline_; pipeline_ = nullptr; doneCurrent(); @@ -556,86 +546,28 @@ void ViewerWidget::draw_gizmos() { } void ViewerWidget::paintGL() { - QOpenGLFunctions* f = context()->functions(); - QOpenGLExtraFunctions* xf = context()->extraFunctions(); - - f->glClearColor(0.0, 0.0, 0.0, 0.0); - - GLfloat vertices[] = { - -1.0f, -1.0f, -1.0f, - 1.0f, -1.0f, -1.0f, - 1.0f, 1.0f, -1.0f, - - -1.0f, -1.0f, -1.0f, - -1.0f, 1.0f, -1.0f, - 1.0f, 1.0f, -1.0f, - }; - - GLfloat tex_coords[] = { - 0.0, 0.0, - 1.0, 0.0, - 1.0, 1.0, - - 0.0, 0.0, - 0.0, 1.0, - 1.0, 1.0 - }; - - f->glViewport(0, 0, width(), height()); - - f->glClear(GL_COLOR_BUFFER_BIT); - - QOpenGLTexture texture(QImage("C:/Users/Matt/Desktop/bliss.png")); - - pipeline_->bind(); - texture.bind(); - - pipeline_->setUniformValue("mvp_matrix", projection_); - pipeline_->setUniformValue("texture", 0); - - GLuint vertex_location = pipeline_->attributeLocation("a_position"); - f->glEnableVertexAttribArray(vertex_location); - f->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); - - GLuint tex_location = pipeline_->attributeLocation("a_texcoord"); - f->glEnableVertexAttribArray(tex_location); - f->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, tex_coords); - - f->glDrawArrays(GL_TRIANGLES, 0, 6); - - texture.release(); - pipeline_->release(); - - /* if (waveform) { draw_waveform_func(); } else { - const GLuint tex = renderer->get_texture(); - QMutex* tex_lock = renderer->get_texture_mutex(); + const GLuint tex = renderer.get_texture(); + QMutex* tex_lock = renderer.get_texture_mutex(); tex_lock->lock(); + QOpenGLFunctions* f = context()->functions(); + //QOpenGLExtraFunctions* xf = context()->extraFunctions(); + makeCurrent(); // clear to solid black - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); + f->glClearColor(0.0, 0.0, 0.0, 0.0); + f->glClear(GL_COLOR_BUFFER_BIT); - // set color multipler to straight white - glColor4f(1.0, 1.0, 1.0, 1.0); - - glEnable(GL_TEXTURE_2D); - - // set screen coords to widget size - glLoadIdentity(); - glOrtho(-1, 1, -1, 1, -1, 1); // draw texture from render thread - glBindTexture(GL_TEXTURE_2D, tex); - - glBegin(GL_QUADS); + // TODO fix zooming double zoom_factor = container->zoom/(double(width())/double(viewer->seq->width)); double zoom_size = (zoom_factor*2.0) - 2.0; double zoom_left = -zoom_size*x_scroll - 1.0; @@ -643,45 +575,34 @@ void ViewerWidget::paintGL() { double zoom_bottom = -zoom_size*(1.0-y_scroll) - 1.0; double zoom_top = zoom_size*(y_scroll) + 1.0; - //zoom_left *= ar_diff; - //zoom_right *= ar_diff; - glVertex2d(zoom_left, zoom_bottom); - glTexCoord2d(0, 0); - glVertex2d(zoom_left, zoom_top); - glTexCoord2d(1, 0); - glVertex2d(zoom_right, zoom_top); - glTexCoord2d(1, 1); - glVertex2d(zoom_right, zoom_bottom); - glTexCoord2d(0, 1); + f->glViewport(0, 0, width(), height()); - glEnd(); + f->glBindTexture(GL_TEXTURE_2D, tex); - glBindTexture(GL_TEXTURE_2D, 0); + olive::rendering::Blit(pipeline_.get()); + + f->glBindTexture(GL_TEXTURE_2D, 0); // draw title/action safe area if (olive::CurrentConfig.show_title_safe_area) { draw_title_safe_area(); } - gizmos = renderer->gizmos; + gizmos = renderer.gizmos; if (gizmos != nullptr) { draw_gizmos(); } - glDisable(GL_TEXTURE_2D); - glFinish(); - if (window->isVisible()) { window->set_texture(tex, double(viewer->seq->width)/double(viewer->seq->height), tex_lock); } tex_lock->unlock(); - if (renderer->did_texture_fail() && !viewer->playing) { + if (renderer.did_texture_fail() && !viewer->playing) { doneCurrent(); - renderer->start_render(context(), viewer->seq.get()); + renderer.start_render(context(), viewer->seq.get()); } } - */ } diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index edd5a3938..c9d46398b 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -87,13 +87,12 @@ private: int gizmo_x_mvmt; int gizmo_y_mvmt; EffectGizmo* selected_gizmo; - RenderThread* renderer; + RenderThread renderer; ViewerWindow* window; double x_scroll; double y_scroll; - QMatrix4x4 projection_; - QOpenGLShaderProgram* pipeline_; + QOpenGLShaderProgramPtr pipeline_; private slots: void context_destroy(); From 862996e8a342aab87c1b0b2dfac3be046c649922 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 23:49:06 +1100 Subject: [PATCH 021/133] gles transition almost complete --- effects/effect.cpp | 22 ++-- effects/effect.h | 4 +- effects/internal/pipeline.frag | 4 +- effects/internal/shakeeffect.cpp | 7 +- effects/internal/transformeffect.cpp | 10 +- rendering/framebufferobject.cpp | 4 +- rendering/renderfunctions.cpp | 150 +++++++++++++++++++++------ rendering/renderfunctions.h | 5 +- rendering/renderthread.cpp | 91 ++++++++-------- ui/viewerwidget.cpp | 12 +-- ui/viewerwindow.cpp | 100 +++++++++--------- ui/viewerwindow.h | 37 +++---- 12 files changed, 257 insertions(+), 189 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index e87bae604..c0ef931e0 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -735,12 +735,12 @@ void Effect::open() { } else { shader_program_ = std::make_shared(); validate_meta_path(); - bool glsl_compiled = true; + bool shader_compiled = true; if (!shader_vert_path_.isEmpty()) { if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + shader_vert_path_)) { qInfo() << "Vertex shader added successfully"; } else { - glsl_compiled = false; + shader_compiled = false; qWarning() << "Vertex shader could not be added"; } } @@ -748,11 +748,11 @@ void Effect::open() { if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + shader_frag_path_)) { qInfo() << "Fragment shader added successfully"; } else { - glsl_compiled = false; + shader_compiled = false; qWarning() << "Fragment shader could not be added"; } } - if (glsl_compiled) { + if (shader_compiled) { if (shader_program_->link()) { qInfo() << "Shader program linked successfully"; } else { @@ -775,7 +775,7 @@ void Effect::close() { isOpen = false; } -bool Effect::is_glsl_linked() { +bool Effect::is_shader_linked() { return shader_program_ != nullptr && shader_program_->isLinked(); } @@ -983,20 +983,12 @@ void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, doub } } -void Effect::gizmo_world_to_screen() { - GLfloat view_val[16]; - GLfloat projection_val[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, view_val); - glGetFloatv(GL_PROJECTION_MATRIX, projection_val); - - QMatrix4x4 view_matrix(view_val); - QMatrix4x4 projection_matrix(projection_val); - +void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& projection) { for (int i=0;iget_point_count();j++) { - QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (view_matrix * projection_matrix); + QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (matrix * projection); int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); diff --git a/effects/effect.h b/effects/effect.h index 5172d9d33..ffca12806 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -172,7 +172,7 @@ public: bool is_open(); void open(); void close(); - bool is_glsl_linked(); + bool is_shader_linked(); virtual void startEffect(); virtual void endEffect(); @@ -198,7 +198,7 @@ public: virtual void gizmo_draw(double timecode, GLTextureCoords& coords); void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); - void gizmo_world_to_screen(); + void gizmo_world_to_screen(const QMatrix4x4 &matrix, const QMatrix4x4 &projection); bool are_gizmos_enabled(); template diff --git a/effects/internal/pipeline.frag b/effects/internal/pipeline.frag index 71719c428..5875c7e0f 100644 --- a/effects/internal/pipeline.frag +++ b/effects/internal/pipeline.frag @@ -4,8 +4,10 @@ precision mediump float; #endif uniform sampler2D texture; +uniform float opacity; varying vec2 v_texcoord; void main() { - gl_FragColor = texture2D(texture, v_texcoord); + vec4 color = texture2D(texture, v_texcoord)*opacity; + gl_FragColor = color; } \ No newline at end of file diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index b8b92a496..3b4d16ba5 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -78,10 +78,7 @@ void ShakeEffect::process_coords(double timecode, GLTextureCoords& coords, int) yoff *= multiplier; rotoff *= rotmult; - coords.vertex_top_left += QVector3D(xoff, yoff, 0.0); - coords.vertex_top_right += QVector3D(xoff, yoff, 0.0); - coords.vertex_bottom_left += QVector3D(xoff, yoff, 0.0); - coords.vertex_bottom_right += QVector3D(xoff, yoff, 0.0); + coords.matrix.translate(xoff, yoff, 0.0); - glRotatef(rotoff, 0, 0, 1); + coords.matrix.rotate(QQuaternion::fromEulerAngles(0.0f, 0.0f, rotoff)); } diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 0e766ff1b..a96df512a 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -190,9 +190,9 @@ void TransformEffect::toggle_uniform_scale(bool enabled) { void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) { // position - glTranslated(position_x->GetDoubleAt(timecode)-(parent_clip->sequence->width/2), - position_y->GetDoubleAt(timecode)-(parent_clip->sequence->height/2), - 0); + coords.matrix.translate(position_x->GetDoubleAt(timecode)-(parent_clip->sequence->width/2), + position_y->GetDoubleAt(timecode)-(parent_clip->sequence->height/2), + 0); // anchor point int anchor_x_offset = qRound(anchor_x_box->GetDoubleAt(timecode)); @@ -204,12 +204,12 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i coords.vertex_bottom_right -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); // rotation - glRotated(rotation->GetDoubleAt(timecode), 0, 0, 1); + coords.matrix.rotate(QQuaternion::fromEulerAngles(0, 0, rotation->GetDoubleAt(timecode))); // scale double sx = scale_x->GetDoubleAt(timecode)*0.01; double sy = (uniform_scale_field->GetBoolAt(timecode)) ? sx : scale_y->GetDoubleAt(timecode)*0.01; - glScaled(sx, sy, 1); + coords.matrix.scale(sx, sy); // blend mode coords.blendmode = blend_mode_box->GetValueAt(timecode).toInt(); diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index cf2f7d0cb..f55eb4777 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -81,7 +81,7 @@ void FramebufferObject::BindBuffer() const if (ctx_ == nullptr) { return; } - ctx_->functions()->glBindFramebuffer(GL_TEXTURE_2D, buffer_); + ctx_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buffer_); } void FramebufferObject::ReleaseBuffer() const @@ -89,7 +89,7 @@ void FramebufferObject::ReleaseBuffer() const if (ctx_ == nullptr) { return; } - ctx_->functions()->glBindFramebuffer(GL_TEXTURE_2D, 0); + ctx_->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void FramebufferObject::BindTexture() const diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index fffdd566b..753e38528 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -26,6 +26,7 @@ extern "C" { #include #include +#include #include #ifndef NO_OCIO @@ -67,12 +68,23 @@ GLfloat olive::rendering::blit_texcoords[] = { 1.0, 1.0 }; -void olive::rendering::Blit(QOpenGLShaderProgram* pipeline) { +GLfloat olive::rendering::flipped_blit_texcoords[] = { + 0.0, 1.0, + 1.0, 1.0, + 1.0, 0.0, + + 0.0, 1.0, + 0.0, 0.0, + 1.0, 0.0 +}; + +void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatrix4x4 matrix) { + QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); pipeline->bind(); - pipeline->setUniformValue("mvp_matrix", QMatrix4x4()); + pipeline->setUniformValue("mvp_matrix", matrix); pipeline->setUniformValue("texture", 0); GLuint vertex_location = pipeline->attributeLocation("a_position"); @@ -81,21 +93,90 @@ void olive::rendering::Blit(QOpenGLShaderProgram* pipeline) { GLuint tex_location = pipeline->attributeLocation("a_texcoord"); func->glEnableVertexAttribArray(tex_location); - func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, blit_texcoords); + func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, flipped ? flipped_blit_texcoords : blit_texcoords); func->glDrawArrays(GL_TRIANGLES, 0, 6); pipeline->release(); + } -QOpenGLShaderProgramPtr olive::rendering::GetPipeline() +QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code) { QOpenGLShaderProgramPtr program = std::make_shared(); - program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/pipeline.vert"); - program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/pipeline.frag"); + // Generate vertex shader + QString vert_shader = "#ifdef GL_ES\n" + "precision mediump int;\n" + "precision mediump float;\n" + "#endif\n" + "\n" + "uniform mat4 mvp_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_texcoord;\n" + "\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main() {\n" + " gl_Position = mvp_matrix * a_position;\n" + " v_texcoord = a_texcoord;\n" + "}\n"; + + // Generate fragment shader + QString frag_shader = "#ifdef GL_ES\n" + "precision mediump int;\n" + "precision mediump float;\n" + "#endif\n" + "\n" + "uniform sampler2D texture;\n" + "uniform float opacity;\n" + "varying vec2 v_texcoord;\n" + "\n"; + + // Finish the function with the main function + + // Check if additional code was passed to this function, add it here + if (shader_code.isEmpty()) { + + // If not, just add a pure main() function + + frag_shader.append("\n" + "void main() {\n" + " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" + " gl_FragColor = color;\n" + "}\n"); + + } else { + + // If additional code was passed, add it and reference it in main(). + // + // The function in the additional code is expected to be `vec4 process(vec4 color)`. The texture coordinate can be + // acquired through `v_texcoord`. + + frag_shader.append(shader_code); + + frag_shader.append("\n" + "void main() {\n" + " vec4 color = process(texture2D(texture, v_texcoord))*opacity;\n" + " gl_FragColor = color;\n" + "}\n"); + + } + + + + + // Add shaders to program + program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_shader); + program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_shader); program->link(); + // Set opacity default to 100% + program->bind(); + program->setUniformValue("opacity", 1.0f); + program->release(); + return program; } @@ -158,10 +239,10 @@ void process_effect(QOpenGLContext* ctx, bool can_process_shaders = ((e->Flags() & Effect::ShaderFlag) && olive::CurrentRuntimeConfig.shaders_are_enabled); if (can_process_shaders || (e->Flags() & Effect::SuperimposeFlag)) { e->startEffect(); - if (can_process_shaders && e->is_glsl_linked()) { + if (can_process_shaders && e->is_shader_linked()) { for (int i=0;igetIterations();i++) { e->process_shader(timecode, coords, i); - composite_texture = draw_clip(ctx, pipeline, c->fbo[fbo_switcher], composite_texture, true); + composite_texture = draw_clip(ctx, pipeline, c->fbo.at(fbo_switcher), composite_texture, true); fbo_switcher = !fbo_switcher; } } @@ -179,11 +260,11 @@ void process_effect(QOpenGLContext* ctx, } else { // if the source texture is not already a framebuffer texture, // we'll need to make it one before drawing a superimpose effect on it - if (composite_texture != c->fbo[0].texture() && composite_texture != c->fbo[1].texture()) { - draw_clip(ctx, pipeline, c->fbo[!fbo_switcher], composite_texture, true); + if (composite_texture != c->fbo.at(0).texture() && composite_texture != c->fbo.at(1).texture()) { + draw_clip(ctx, pipeline, c->fbo.at(!fbo_switcher), composite_texture, true); } - composite_texture = draw_clip(ctx, pipeline, c->fbo[!fbo_switcher], superimpose_texture, false); + composite_texture = draw_clip(ctx, pipeline, c->fbo.at(!fbo_switcher), superimpose_texture, false); } } e->endEffect(); @@ -205,9 +286,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } if (params.video && !params.nests.last()->fbo.isEmpty()) { - params.nests.last()->fbo[0].BindBuffer(); - glClear(GL_COLOR_BUFFER_BIT); - final_fbo = params.nests.last()->fbo[0].buffer(); + params.nests.last()->fbo.at(0).BindBuffer(); + params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); + final_fbo = params.nests.last()->fbo.at(0).buffer(); } } @@ -308,14 +389,11 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { if (params.video) { // set default coordinates based on the sequence, with 0 in the direct center - //glPushMatrix(); - //glLoadIdentity(); params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); int half_width = s->width/2; int half_height = s->height/2; - //glOrtho(-half_width, half_width, -half_height, half_height, -1, 10); projection.ortho(-half_width, half_width, -half_height, half_height, -1, 1); } @@ -406,7 +484,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); - textureID = draw_clip(params.ctx, params.pipeline, c->fbo[fbo_switcher], textureID, true); + textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -418,15 +496,23 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // convert to linear colorspace if (olive::CurrentConfig.enable_color_management && params.ocio_shader != nullptr) { + + params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE2); + params.ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, params.ocio_lut_texture); + params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE0); + params.ocio_shader->bind(); - params.ocio_shader->setUniformValue("tex1", 0); params.ocio_shader->setUniformValue("tex2", 2); - textureID = draw_clip(params.ctx, params.pipeline, c->fbo[fbo_switcher], textureID, true); + textureID = draw_clip(params.ctx, params.ocio_shader, c->fbo.at(fbo_switcher), textureID, true); params.ocio_shader->release(); + params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE2); + params.ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, 0); + params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE0); + fbo_switcher = !fbo_switcher; } @@ -459,7 +545,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { Effect* e = c->effects.at(j).get(); process_effect(params.ctx, params.pipeline, c, e, timecode, coords, textureID, fbo_switcher, params.texture_failed, kTransitionNone); - } // if the clip has an opening transition, process that now @@ -482,27 +567,29 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // Check whether the parent clip is auto-scaled - // TODO redo this - /* if (c->autoscaled() && (video_width != s->width && video_height != s->height)) { float width_multiplier = float(s->width) / float(video_width); float height_multiplier = float(s->height) / float(video_height); float scale_multiplier = qMin(width_multiplier, height_multiplier); - glScalef(scale_multiplier, scale_multiplier, 1); + + coords.matrix.scale(scale_multiplier, scale_multiplier); } - */ // Configure effect gizmos if they exist if (params.gizmos != nullptr) { - params.gizmos->gizmo_draw(timecode, coords); // set correct gizmo coords - params.gizmos->gizmo_world_to_screen(); // convert gizmo coords to screen coords + // set correct gizmo coords at this matrix + params.gizmos->gizmo_draw(timecode, coords); + + // convert gizmo coords to screen coords + params.gizmos->gizmo_world_to_screen(coords.matrix, projection); } if (textureID > 0) { + // set viewport to sequence size params.ctx->functions()->glViewport(0, 0, s->width, s->height); @@ -542,8 +629,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { // draw clip on screen according to gl coordinates params.pipeline->bind(); - params.pipeline->setUniformValue("mvp_matrix", projection); + params.pipeline->setUniformValue("mvp_matrix", projection * coords.matrix); params.pipeline->setUniformValue("texture", 0); + params.pipeline->setUniformValue("opacity", coords.opacity); GLfloat vertices[] = { coords.vertex_top_left.x(), coords.vertex_top_left.y(), 0.0f, @@ -576,6 +664,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glDrawArrays(GL_TRIANGLES, 0, 6); + params.pipeline->setUniformValue("opacity", 1.0f); + params.pipeline->release(); // release final clip texture @@ -700,10 +790,6 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.viewer->play_wake(); } - if (params.video) { - glPopMatrix(); - } - if (!params.nests.isEmpty() && !params.nests.last()->fbo.isEmpty()) { // returns nested clip's texture return params.nests.last()->fbo[0].texture(); diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 03bffa4e3..d9894eea2 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -414,8 +414,9 @@ namespace olive { namespace rendering { extern GLfloat blit_vertices[]; extern GLfloat blit_texcoords[]; - void Blit(QOpenGLShaderProgram* pipeline); - QOpenGLShaderProgramPtr GetPipeline(); + extern GLfloat flipped_blit_texcoords[]; + void Blit(QOpenGLShaderProgram* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); + QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); } } diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 9741814bc..f0ad91e69 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -149,47 +149,26 @@ const GLuint &RenderThread::get_texture() return front_buffer_switcher ? front_buffer_2.texture() : front_buffer_1.texture(); } -const char * g_fragShaderText = "" - "\n" - "uniform sampler2D tex1;\n" - "uniform sampler3D tex2;\n" - "\n" - "void main()\n" - "{\n" - " vec4 col = texture2D(tex1, gl_TexCoord[0].st);\n" - " gl_FragColor = OCIODisplay(col, tex2);\n" - "}\n"; - #ifndef NO_OCIO void RenderThread::set_up_ocio() { - - // - // SETUP LUT TEXTURE - // - // Create LUT texture - ctx->functions()->glGenTextures(1, &ocio_lut_texture); + ctx->extraFunctions()->glGenTextures(1, &ocio_lut_texture); - // Bind texture to GL_TEXTURE_3D and GL_TEXTURE2 - ctx->functions()->glActiveTexture(GL_TEXTURE2); - ctx->functions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); + // Bind LUT + ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); // Set texture parameters - ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ctx->functions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // Allocate storage for texture ctx->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB,GL_FLOAT, ocio_lut_data); - - // - // SET UP OCIO DISPLAY - // + 0, GL_RGB,GL_FLOAT, nullptr); OCIO::ConstConfigRcPtr config; @@ -208,30 +187,32 @@ void RenderThread::set_up_ocio() const char* display = config->getDefaultDisplay(); + OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); transform->setInputColorSpaceName(OCIO::ROLE_SCENE_LINEAR); transform->setDisplay(display); transform->setView(config->getDefaultView(display)); - // - // GET OCIO PROCESSOR - // OCIO::ConstProcessorRcPtr processor; + OCIO::GpuShaderDesc shaderDesc; + + // Get processor for this configuration + try { - processor = OCIO::GetCurrentConfig()->getProcessor(transform); + processor = config->getProcessor(transform); + + + } catch(OCIO::Exception & e) { qCritical() << e.what(); - ctx->functions()->glActiveTexture(GL_TEXTURE0); return; } - // // SET UP GLSL SHADER // - OCIO::GpuShaderDesc shaderDesc; shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); shaderDesc.setFunctionName("OCIODisplay"); shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); @@ -242,22 +223,31 @@ void RenderThread::set_up_ocio() processor->getGpuLut3D(ocio_lut_data, shaderDesc); - ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); + + // Upload LUT data to texture ctx->extraFunctions()->glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - GL_RGB,GL_FLOAT, ocio_lut_data); + GL_RGB, GL_FLOAT, ocio_lut_data); - QString shader_text = processor->getGpuShaderText(shaderDesc); - shader_text.append("\n"); - shader_text.append(g_fragShaderText); - ocio_shader = std::make_shared(); - ocio_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, shader_text); - ocio_shader->link(); + // Create OCIO shader code + QString shader_text(processor->getGpuShaderText(shaderDesc)); + shader_text.append("\n" + "uniform sampler3D tex2;\n" + "\n" + "vec4 process(vec4 col) {\n" + " return OCIODisplay(col, tex2);\n" + "}\n"); + + + // Get pipeline-based shader to inject OCIO shader into + ocio_shader = olive::rendering::GetPipeline(shader_text); + + + // Release LUT + ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, 0); - // Reset active texture to 0 for the rest of the pipeline - ctx->functions()->glActiveTexture(GL_TEXTURE0); } void RenderThread::destroy_ocio() @@ -284,6 +274,7 @@ void RenderThread::paint() { params.pipeline = pipeline_program.get(); #ifndef NO_OCIO params.ocio_shader = ocio_shader.get(); + params.ocio_lut_texture = ocio_lut_texture; #endif params.backend_buffer1 = back_buffer_1.buffer(); params.backend_buffer2 = back_buffer_2.buffer(); @@ -299,6 +290,8 @@ void RenderThread::paint() { QMutex& active_mutex = front_buffer_switcher ? front_mutex1 : front_mutex2; active_mutex.lock(); + ctx->functions()->glEnable(GL_BLEND); + // bind framebuffer for drawing ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, params.main_buffer); @@ -310,6 +303,8 @@ void RenderThread::paint() { // flush changes ctx->functions()->glFinish(); + ctx->functions()->glDisable(GL_BLEND); + texture_failed = params.texture_failed; active_mutex.unlock(); @@ -321,7 +316,7 @@ void RenderThread::paint() { } else { ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, params.main_buffer); QImage img(tex_width, tex_height, QImage::Format_RGBA8888); - glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + ctx->functions()->glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); img.save(save_fn); ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); save_fn = ""; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 35e167882..7c6bb8681 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -567,20 +567,20 @@ void ViewerWidget::paintGL() { // draw texture from render thread - // TODO fix zooming double zoom_factor = container->zoom/(double(width())/double(viewer->seq->width)); double zoom_size = (zoom_factor*2.0) - 2.0; - double zoom_left = -zoom_size*x_scroll - 1.0; - double zoom_right = zoom_size*(1.0-x_scroll) + 1.0; - double zoom_bottom = -zoom_size*(1.0-y_scroll) - 1.0; - double zoom_top = zoom_size*(y_scroll) + 1.0; + QMatrix4x4 matrix; + if (zoom_factor > 1.0) { + matrix.translate((-(x_scroll-0.5))*zoom_size, (y_scroll-0.5)*zoom_size); + matrix.scale(zoom_factor); + } f->glViewport(0, 0, width(), height()); f->glBindTexture(GL_TEXTURE_2D, tex); - olive::rendering::Blit(pipeline_.get()); + olive::rendering::Blit(pipeline_.get(), true, matrix); f->glBindTexture(GL_TEXTURE_2D, 0); diff --git a/ui/viewerwindow.cpp b/ui/viewerwindow.cpp index 69bb74053..922b1a4cc 100644 --- a/ui/viewerwindow.cpp +++ b/ui/viewerwindow.cpp @@ -28,27 +28,26 @@ #include #include #include - #include -#include "mainwindow.h" +#include "rendering/renderfunctions.h" ViewerWindow::ViewerWindow(QWidget *parent) : QOpenGLWidget(parent, Qt::Window), - texture(0), - mutex(nullptr), - show_fullscreen_msg(false) + texture_(0), + mutex_(nullptr), + show_fullscreen_msg_(false) { setMouseTracking(true); - fullscreen_msg_timer.setInterval(2000); - connect(&fullscreen_msg_timer, SIGNAL(timeout()), this, SLOT(fullscreen_msg_timeout())); + fullscreen_msg_timer_.setInterval(2000); + connect(&fullscreen_msg_timer_, SIGNAL(timeout()), this, SLOT(fullscreen_msg_timeout())); } void ViewerWindow::set_texture(GLuint t, double iar, QMutex* imutex) { - texture = t; - ar = iar; - mutex = imutex; + texture_ = t; + ar_ = iar; + mutex_ = imutex; update(); } @@ -59,71 +58,66 @@ void ViewerWindow::keyPressEvent(QKeyEvent *e) { } void ViewerWindow::mousePressEvent(QMouseEvent *e) { - if (show_fullscreen_msg && fullscreen_msg_rect.contains(e->pos())) { + if (show_fullscreen_msg_ && fullscreen_msg_rect_.contains(e->pos())) { hide(); } } void ViewerWindow::mouseMoveEvent(QMouseEvent *) { - fullscreen_msg_timer.start(); - if (!show_fullscreen_msg) { - show_fullscreen_msg = true; + fullscreen_msg_timer_.start(); + if (!show_fullscreen_msg_) { + show_fullscreen_msg_ = true; update(); } } +void ViewerWindow::initializeGL() +{ + pipeline_ = olive::rendering::GetPipeline(); +} + void ViewerWindow::paintGL() { - if (texture > 0) { - if (mutex != nullptr) mutex->lock(); + if (texture_ > 0) { + if (mutex_ != nullptr) mutex_->lock(); - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_TEXTURE_2D); + QOpenGLFunctions* f = context()->functions(); + //QOpenGLExtraFunctions* xf = context()->extraFunctions(); - glBindTexture(GL_TEXTURE_2D, texture); + makeCurrent(); - glLoadIdentity(); - glOrtho(0, 1, 0, 1, -1, 1); + // clear to solid black + f->glClearColor(0.0, 0.0, 0.0, 0.0); + f->glClear(GL_COLOR_BUFFER_BIT); - glBegin(GL_QUADS); - double top = 0; - double left = 0; - double right = 1; - double bottom = 1; + // draw texture from render thread - double widget_ar = double(width()) / double(height()); - if (widget_ar > ar) { - double width = 1.0 * ar / widget_ar; - left = (1.0 - width)*0.5; - right = left + width; + QMatrix4x4 matrix; + + double widget_ar = (double(width()) / double(height())); + if (widget_ar > ar_) { + matrix.scale(ar_ / widget_ar, 1.0); } else { - double height = 1.0 / ar * widget_ar; - top = (1.0 - height)*0.5; - bottom = top + height; + matrix.scale(1.0f, widget_ar / ar_); } - glVertex2d(left, top); - glTexCoord2d(0, 0); - glVertex2d(left, bottom); - glTexCoord2d(1, 0); - glVertex2d(right, bottom); - glTexCoord2d(1, 1); - glVertex2d(right, top); - glTexCoord2d(0, 1); - glEnd(); + f->glViewport(0, 0, width(), height()); - glBindTexture(GL_TEXTURE_2D, 0); + f->glBindTexture(GL_TEXTURE_2D, texture_); - glDisable(GL_TEXTURE_2D); + olive::rendering::Blit(pipeline_.get(), true, matrix); - if (mutex != nullptr) mutex->unlock(); + f->glBindTexture(GL_TEXTURE_2D, 0); + + + + if (mutex_ != nullptr) mutex_->unlock(); } - if (show_fullscreen_msg) { + if (show_fullscreen_msg_) { QPainter p(this); QFont f = p.font(); @@ -143,21 +137,21 @@ void ViewerWindow::paintGL() { int rect_padding = 8; - fullscreen_msg_rect = QRect(text_x-rect_padding, + fullscreen_msg_rect_ = QRect(text_x-rect_padding, fm.height()-rect_padding, text_width+rect_padding+rect_padding, fm.height()+rect_padding+rect_padding); - p.drawRect(fullscreen_msg_rect); + p.drawRect(fullscreen_msg_rect_); p.drawText(text_x, text_y, fs_str); } } void ViewerWindow::fullscreen_msg_timeout() { - fullscreen_msg_timer.stop(); - if (show_fullscreen_msg) { - show_fullscreen_msg = false; + fullscreen_msg_timer_.stop(); + if (show_fullscreen_msg_) { + show_fullscreen_msg_ = false; update(); } } diff --git a/ui/viewerwindow.h b/ui/viewerwindow.h index ccba77452..7ae9c0cc4 100644 --- a/ui/viewerwindow.h +++ b/ui/viewerwindow.h @@ -23,33 +23,34 @@ #include #include +#include -class QMutex; -class QMenu; -class QShortcut; +#include "rendering/qopenglshaderprogramptr.h" class ViewerWindow : public QOpenGLWidget { - Q_OBJECT + Q_OBJECT public: - ViewerWindow(QWidget *parent); - void set_texture(GLuint t, double iar, QMutex *imutex); + ViewerWindow(QWidget *parent); + void set_texture(GLuint t, double iar, QMutex *imutex); protected: - virtual void keyPressEvent(QKeyEvent*) override; - virtual void mousePressEvent(QMouseEvent*) override; - virtual void mouseMoveEvent(QMouseEvent*) override; + virtual void keyPressEvent(QKeyEvent*) override; + virtual void mousePressEvent(QMouseEvent*) override; + virtual void mouseMoveEvent(QMouseEvent*) override; - virtual void paintGL() override; + virtual void initializeGL() override; + virtual void paintGL() override; private: - GLuint texture; - double ar; - QMutex* mutex; + GLuint texture_; + double ar_; + QMutex* mutex_; + QOpenGLShaderProgramPtr pipeline_; - // exit full screen message - QTimer fullscreen_msg_timer; - bool show_fullscreen_msg; - QRect fullscreen_msg_rect; + // exit full screen message + QTimer fullscreen_msg_timer_; + bool show_fullscreen_msg_; + QRect fullscreen_msg_rect_; private slots: - void fullscreen_msg_timeout(); + void fullscreen_msg_timeout(); }; #endif // VIEWERWINDOW_H From 69a02cb7c1f3630b16555f441f8c05be3da86f3e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 17 Mar 2019 23:51:06 +1100 Subject: [PATCH 022/133] pipeline is generated programatically now --- effects/internal/internalshaders.qrc | 2 -- effects/internal/pipeline.frag | 13 ------------- effects/internal/pipeline.vert | 16 ---------------- 3 files changed, 31 deletions(-) delete mode 100644 effects/internal/pipeline.frag delete mode 100644 effects/internal/pipeline.vert diff --git a/effects/internal/internalshaders.qrc b/effects/internal/internalshaders.qrc index 52ec17302..ae51e0b27 100644 --- a/effects/internal/internalshaders.qrc +++ b/effects/internal/internalshaders.qrc @@ -5,7 +5,5 @@ cornerpin.vert premultiply.frag dropshadow.frag - pipeline.frag - pipeline.vert diff --git a/effects/internal/pipeline.frag b/effects/internal/pipeline.frag deleted file mode 100644 index 5875c7e0f..000000000 --- a/effects/internal/pipeline.frag +++ /dev/null @@ -1,13 +0,0 @@ -#ifdef GL_ES -precision mediump int; -precision mediump float; -#endif - -uniform sampler2D texture; -uniform float opacity; -varying vec2 v_texcoord; - -void main() { - vec4 color = texture2D(texture, v_texcoord)*opacity; - gl_FragColor = color; -} \ No newline at end of file diff --git a/effects/internal/pipeline.vert b/effects/internal/pipeline.vert deleted file mode 100644 index 8dc969907..000000000 --- a/effects/internal/pipeline.vert +++ /dev/null @@ -1,16 +0,0 @@ -#ifdef GL_ES -precision mediump int; -precision mediump float; -#endif - -uniform mat4 mvp_matrix; - -attribute vec4 a_position; -attribute vec2 a_texcoord; - -varying vec2 v_texcoord; - -void main() { - gl_Position = mvp_matrix * a_position; - v_texcoord = a_texcoord; -}; \ No newline at end of file From 17b5b0f2cf147e30286e32b8e9300f89b596026c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 18 Mar 2019 02:34:11 +1100 Subject: [PATCH 023/133] completed transition to gles 3.0 --- effects/effect.cpp | 20 +- effects/effectgizmo.h | 4 +- effects/internal/transformeffect.cpp | 32 +-- rendering/renderfunctions.cpp | 12 +- ui/viewerwidget.cpp | 307 ++++++++++++++++++--------- ui/viewerwidget.h | 1 + 6 files changed, 250 insertions(+), 126 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index c0ef931e0..9d3552244 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -838,7 +838,7 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { { DoubleField* double_field = static_cast(field); shader_program_->setUniformValue(double_field->id().toUtf8().constData(), - GLfloat(double_field->GetDoubleAt(timecode))); + GLfloat(double_field->GetDoubleAt(timecode))); } break; case EffectField::EFFECT_FIELD_COLOR: @@ -988,12 +988,22 @@ void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& p EffectGizmo* g = gizmos.at(i); for (int j=0;jget_point_count();j++) { - QVector4D screen_pos = QVector4D(g->world_pos[j].x(), g->world_pos[j].y(), 0, 1.0) * (matrix * projection); - int adjusted_sx1 = qRound(((screen_pos.x()*0.5f)+0.5f)*parent_clip->sequence->width); - int adjusted_sy1 = qRound((1.0f-((screen_pos.y()*0.5f)+0.5f))*parent_clip->sequence->height); + QMatrix4x4 matrix2 = matrix; +// matrix2.flipCoordinates(); + + QMatrix4x4 projection2 = projection; + projection2.flipCoordinates(); + + QVector3D screen_pos = g->world_pos.at(j).project(matrix2, + projection2, + QRect(0, + 0, + parent_clip->sequence->width, + parent_clip->sequence->height)); + + g->screen_pos[j] = QPoint(screen_pos.x(), screen_pos.y()); - g->screen_pos[j] = QPoint(adjusted_sx1, adjusted_sy1); } } } diff --git a/effects/effectgizmo.h b/effects/effectgizmo.h index e39aa34a2..e32732e6c 100644 --- a/effects/effectgizmo.h +++ b/effects/effectgizmo.h @@ -27,8 +27,8 @@ enum GizmoType { GIZMO_TYPE_TARGET }; -#define GIZMO_DOT_SIZE 2.5 -#define GIZMO_TARGET_SIZE 5.0 +#define GIZMO_DOT_SIZE 2.5f +#define GIZMO_TARGET_SIZE 5.0f #include #include diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index a96df512a..8852240e3 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -218,28 +218,30 @@ void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, i coords.opacity *= float(opacity->GetDoubleAt(timecode)*0.01); } +QVector3D LerpVector3D(const QVector3D& a, const QVector3D& b, float t) { + return QVector3D( + float_lerp(a.x(), b.x(), t), + float_lerp(a.y(), b.y(), t), + float_lerp(a.z(), b.z(), t) + ); +} + void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) { top_left_gizmo->world_pos[0] = coords.vertex_top_left; top_right_gizmo->world_pos[0] = coords.vertex_top_right; bottom_right_gizmo->world_pos[0] = coords.vertex_bottom_right; bottom_left_gizmo->world_pos[0] = coords.vertex_bottom_left; - top_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_top_left.x(), coords.vertex_top_right.x(), 0.5), - lerp(coords.vertex_top_left.y(), coords.vertex_top_right.y(), 0.5), - 0.0f); - right_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_top_right.x(), coords.vertex_bottom_right.x(), 0.5), - lerp(coords.vertex_top_right.y(), coords.vertex_bottom_right.y(), 0.5), - 0.0f); - bottom_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_bottom_right.x(), coords.vertex_bottom_left.x(), 0.5), - lerp(coords.vertex_bottom_right.y(), coords.vertex_bottom_left.y(), 0.5), - 0.0f); - left_center_gizmo->world_pos[0] = QVector3D(lerp(coords.vertex_bottom_left.x(), coords.vertex_top_left.x(), 0.5), - lerp(coords.vertex_bottom_left.y(), coords.vertex_top_left.y(), 0.5), - 0.0f); + top_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_top_left, coords.vertex_top_right, 0.5); + right_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_top_right, coords.vertex_bottom_right, 0.5); + bottom_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_bottom_right, coords.vertex_bottom_left, 0.5); + left_center_gizmo->world_pos[0] = LerpVector3D(coords.vertex_bottom_left, coords.vertex_top_left, 0.5); - rotate_gizmo->world_pos[0] = QVector3D(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), - lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1), - 0.0f); + rotate_gizmo->world_pos[0] = QVector3D( + float_lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), 0.5f), + float_lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1f), + 0.0f + ); rect_gizmo->world_pos[0] = coords.vertex_top_left; rect_gizmo->world_pos[1] = coords.vertex_top_right; diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 753e38528..7df931d05 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -131,6 +131,8 @@ QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code "\n" "uniform sampler2D texture;\n" "uniform float opacity;\n" + "uniform bool color_only;\n" + "uniform vec4 color_only_color;\n" "varying vec2 v_texcoord;\n" "\n"; @@ -143,8 +145,12 @@ QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code frag_shader.append("\n" "void main() {\n" - " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" - " gl_FragColor = color;\n" + " if (color_only) {\n" + " gl_FragColor = color_only_color;" + " } else {\n" + " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" + " gl_FragColor = color;\n" + " }\n" "}\n"); } else { @@ -583,7 +589,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.gizmos->gizmo_draw(timecode, coords); // convert gizmo coords to screen coords - params.gizmos->gizmo_world_to_screen(coords.matrix, projection); + params.gizmos->gizmo_world_to_screen(projection, coords.matrix); } diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 7c6bb8681..1da659e98 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -252,6 +252,22 @@ void ViewerWidget::seek_from_click(int x) { viewer->seek(getFrameFromScreenPoint(waveform_zoom, x+waveform_scroll)); } +QMatrix4x4 ViewerWidget::get_matrix() +{ + QMatrix4x4 matrix; + + double zoom_factor = container->zoom/(double(width())/double(viewer->seq->width)); + + if (zoom_factor > 1.0) { + double zoom_size = (zoom_factor*2.0) - 2.0; + + matrix.translate((-(x_scroll-0.5))*zoom_size, (y_scroll-0.5)*zoom_size); + matrix.scale(zoom_factor); + } + + return matrix; +} + void ViewerWidget::context_destroy() { makeCurrent(); @@ -409,140 +425,238 @@ void ViewerWidget::draw_waveform_func() { } void ViewerWidget::draw_title_safe_area() { - double halfWidth = 0.5; - double halfHeight = 0.5; - double viewportAr = (double) width() / (double) height(); - double halfAr = viewportAr*0.5; + QOpenGLFunctions* func = context()->functions(); + pipeline_->bind(); + + + float ar = float(width()) / float(height()); + + float horizontal_cross_size = 0.05f / ar; + + // Set matrix to 0.0 -> 1.0 on both axes + QMatrix4x4 matrix; + matrix.ortho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); + + // adjust the horizontal center cross by the aspect ratio to appear "square" if (olive::CurrentConfig.use_custom_title_safe_ratio && olive::CurrentConfig.custom_title_safe_ratio > 0) { - if (olive::CurrentConfig.custom_title_safe_ratio > viewportAr) { - halfHeight = (olive::CurrentConfig.custom_title_safe_ratio/viewportAr)*0.5; + if (ar > olive::CurrentConfig.custom_title_safe_ratio) { + matrix.translate(((ar - olive::CurrentConfig.custom_title_safe_ratio) / 2.0) / ar, 0.0f); + matrix.scale(olive::CurrentConfig.custom_title_safe_ratio / ar, 1.0f); } else { - halfWidth = (viewportAr/olive::CurrentConfig.custom_title_safe_ratio)*0.5; + matrix.translate(0.0f, (((olive::CurrentConfig.custom_title_safe_ratio - ar) / 2.0) / olive::CurrentConfig.custom_title_safe_ratio)); + matrix.scale(1.0f, ar / olive::CurrentConfig.custom_title_safe_ratio); } + + horizontal_cross_size *= ar/olive::CurrentConfig.custom_title_safe_ratio; } - glLoadIdentity(); - glOrtho(-halfWidth, halfWidth, halfHeight, -halfHeight, 0, 1); + float adjusted_cross_x1 = 0.5f - horizontal_cross_size; + float adjusted_cross_x2 = 0.5f + horizontal_cross_size; - glColor4f(0.66f, 0.66f, 0.66f, 1.0f); - glBegin(GL_LINES); + pipeline_->setUniformValue("mvp_matrix", matrix); + pipeline_->setUniformValue("color_only", true); + pipeline_->setUniformValue("color_only_color", QColor(192, 192, 192, 255)); - // action safe rectangle - glVertex2d(-0.45, -0.45); - glVertex2d(0.45, -0.45); - glVertex2d(0.45, -0.45); - glVertex2d(0.45, 0.45); - glVertex2d(0.45, 0.45); - glVertex2d(-0.45, 0.45); - glVertex2d(-0.45, 0.45); - glVertex2d(-0.45, -0.45); - // title safe rectangle - glVertex2d(-0.4, -0.4); - glVertex2d(0.4, -0.4); - glVertex2d(0.4, -0.4); - glVertex2d(0.4, 0.4); - glVertex2d(0.4, 0.4); - glVertex2d(-0.4, 0.4); - glVertex2d(-0.4, 0.4); - glVertex2d(-0.4, -0.4); - // horizontal centers - glVertex2d(-0.45, 0); - glVertex2d(-0.375, 0); - glVertex2d(0.45, 0); - glVertex2d(0.375, 0); + GLfloat vertices[] = { + // action safe lines + 0.05f, 0.05f, 0.0f, + 0.95f, 0.05f, 0.0f, - // vertical centers - glVertex2d(0, -0.45); - glVertex2d(0, -0.375); - glVertex2d(0, 0.45); - glVertex2d(0, 0.375); + 0.95f, 0.05f, 0.0f, + 0.95f, 0.95f, 0.0f, - glEnd(); + 0.95f, 0.95f, 0.0f, + 0.05f, 0.95f, 0.0f, - // center cross - glLoadIdentity(); - glOrtho(-halfAr, halfAr, 0.5, -0.5, -1, 1); + 0.05f, 0.95f, 0.0f, + 0.05f, 0.05f, 0.0f, - glBegin(GL_LINES); + // title safe lines + 0.1f, 0.1f, 0.0f, + 0.9f, 0.1f, 0.0f, - glVertex2d(-0.05, 0); - glVertex2d(0.05, 0); - glVertex2d(0, -0.05); - glVertex2d(0, 0.05); + 0.9f, 0.1f, 0.0f, + 0.9f, 0.9f, 0.0f, + + 0.9f, 0.9f, 0.0f, + 0.1f, 0.9f, 0.0f, + + 0.1f, 0.9f, 0.0f, + 0.1f, 0.1f, 0.0f, + + // side-center markers + 0.05f, 0.5f, 0.0f, + 0.125f, 0.5f, 0.0f, + + 0.95f, 0.5f, 0.0f, + 0.875f, 0.5f, 0.0f, + + 0.5f, 0.05f, 0.0f, + 0.5f, 0.125f, 0.0f, + + 0.5f, 0.95f, 0.0f, + 0.5f, 0.875f, 0.0f, + + // horizontal center cross marker + adjusted_cross_x1, 0.5f, 0.0f, + adjusted_cross_x2, 0.5f, 0.0f, + + // vertical center cross marker + 0.5f, 0.45f, 0.0f, + 0.5f, 0.55f, 0.0f + }; + + GLuint vertex_location = pipeline_->attributeLocation("a_position"); + func->glEnableVertexAttribArray(vertex_location); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); + + func->glDrawArrays(GL_LINES, 0, 28); + + pipeline_->setUniformValue("color_only", false); + + pipeline_->release(); - glEnd(); } void ViewerWidget::draw_gizmos() { - float color[4]; - glGetFloatv(GL_CURRENT_COLOR, color); + QOpenGLFunctions* func = context()->functions(); - double dot_size = GIZMO_DOT_SIZE / double(width()) * viewer->seq->width; - double target_size = GIZMO_TARGET_SIZE / double(width()) * viewer->seq->width; + pipeline_->bind(); double zoom_factor = container->zoom/(double(width())/double(viewer->seq->width)); - glPushMatrix(); - glLoadIdentity(); + QMatrix4x4 matrix; + matrix.ortho(0, viewer->seq->width, 0, viewer->seq->height, -1, 1); + matrix.scale(zoom_factor, zoom_factor); + matrix.translate(-(viewer->seq->width-(width()/container->zoom))*x_scroll, + -((viewer->seq->height-(height()/container->zoom))*(1.0-y_scroll))); - glOrtho(0, viewer->seq->width, 0, viewer->seq->height, -1, 10); - glScaled(zoom_factor, zoom_factor, 0.0); - glTranslated(-(viewer->seq->width-(width()/container->zoom))*x_scroll, - -((viewer->seq->height-(height()/container->zoom))*(1.0-y_scroll)), - 0); + pipeline_->setUniformValue("mvp_matrix", matrix); + pipeline_->setUniformValue("color_only", true); + pipeline_->setUniformValue("color_only_color", QColor(255, 255, 255, 255)); + + float size_diff = float(viewer->seq->width) / float(width()); + float dot_size = GIZMO_DOT_SIZE * size_diff; + float target_size = GIZMO_TARGET_SIZE * size_diff; + + QVector vertices; - float gizmo_z = 0.0f; for (int j=0;jgizmo_count();j++) { + EffectGizmo* g = gizmos->gizmo(j); - glColor4d(g->color.redF(), g->color.greenF(), g->color.blueF(), 1.0); + switch (g->get_type()) { case GIZMO_TYPE_DOT: // draw dot - glBegin(GL_QUADS); - glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()-dot_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()-dot_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()+dot_size, g->screen_pos[0].y()+dot_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()-dot_size, g->screen_pos[0].y()+dot_size, gizmo_z); - glEnd(); + + vertices.append(g->screen_pos[0].x()-dot_size); + vertices.append(g->screen_pos[0].y()-dot_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()+dot_size); + vertices.append(g->screen_pos[0].y()-dot_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()+dot_size); + vertices.append(g->screen_pos[0].y()+dot_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()-dot_size); + vertices.append(g->screen_pos[0].y()+dot_size); + vertices.append(0.0f); + break; - case GIZMO_TYPE_POLY: // draw lines - glBegin(GL_LINES); + case GIZMO_TYPE_POLY: // draw lines for a polygon + for (int k=1;kget_point_count();k++) { - glVertex3f(g->screen_pos[k-1].x(), g->screen_pos[k-1].y(), gizmo_z); - glVertex3f(g->screen_pos[k].x(), g->screen_pos[k].y(), gizmo_z); + + vertices.append(g->screen_pos[k-1].x()); + vertices.append(g->screen_pos[k-1].y()); + vertices.append(0.0f); + + vertices.append(g->screen_pos[k].x()); + vertices.append(g->screen_pos[k].y()); + vertices.append(0.0f); + } - glVertex3f(g->screen_pos[g->get_point_count()-1].x(), g->screen_pos[g->get_point_count()-1].y(), gizmo_z); - glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y(), gizmo_z); - glEnd(); + + vertices.append(g->screen_pos[g->get_point_count()-1].x()); + vertices.append(g->screen_pos[g->get_point_count()-1].y()); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()); + vertices.append(g->screen_pos[0].y()); + vertices.append(0.0f); + break; case GIZMO_TYPE_TARGET: // draw target - glBegin(GL_LINES); - glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()-target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z); + vertices.append(g->screen_pos[0].x()-target_size); + vertices.append(g->screen_pos[0].y()-target_size); + vertices.append(0.0f); - glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y()+target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z); + vertices.append(g->screen_pos[0].x()+target_size); + vertices.append(g->screen_pos[0].y()-target_size); + vertices.append(0.0f); - glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()+target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y()-target_size, gizmo_z); + vertices.append(g->screen_pos[0].x()+target_size); + vertices.append(g->screen_pos[0].y()-target_size); + vertices.append(0.0f); - glVertex3f(g->screen_pos[0].x()-target_size, g->screen_pos[0].y(), gizmo_z); - glVertex3f(g->screen_pos[0].x()+target_size, g->screen_pos[0].y(), gizmo_z); + vertices.append(g->screen_pos[0].x()+target_size); + vertices.append(g->screen_pos[0].y()+target_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()+target_size); + vertices.append(g->screen_pos[0].y()+target_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()-target_size); + vertices.append(g->screen_pos[0].y()+target_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()-target_size); + vertices.append(g->screen_pos[0].y()+target_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()-target_size); + vertices.append(g->screen_pos[0].y()-target_size); + vertices.append(0.0f); + + + + vertices.append(g->screen_pos[0].x()-target_size); + vertices.append(g->screen_pos[0].y()); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()+target_size); + vertices.append(g->screen_pos[0].y()); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()); + vertices.append(g->screen_pos[0].y()-target_size); + vertices.append(0.0f); + + vertices.append(g->screen_pos[0].x()); + vertices.append(g->screen_pos[0].y()+target_size); + vertices.append(0.0f); - glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()-target_size, gizmo_z); - glVertex3f(g->screen_pos[0].x(), g->screen_pos[0].y()+target_size, gizmo_z); - glEnd(); break; } } - glPopMatrix(); - glColor4f(color[0], color[1], color[2], color[3]); + GLuint vertex_location = pipeline_->attributeLocation("a_position"); + func->glEnableVertexAttribArray(vertex_location); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices.constData()); + + func->glDrawArrays(GL_LINES, 0, vertices.size() / 3); + + pipeline_->setUniformValue("color_only", false); + + pipeline_->release(); + } void ViewerWidget::paintGL() { @@ -567,20 +681,11 @@ void ViewerWidget::paintGL() { // draw texture from render thread - double zoom_factor = container->zoom/(double(width())/double(viewer->seq->width)); - double zoom_size = (zoom_factor*2.0) - 2.0; - - QMatrix4x4 matrix; - if (zoom_factor > 1.0) { - matrix.translate((-(x_scroll-0.5))*zoom_size, (y_scroll-0.5)*zoom_size); - matrix.scale(zoom_factor); - } - f->glViewport(0, 0, width(), height()); f->glBindTexture(GL_TEXTURE_2D, tex); - olive::rendering::Blit(pipeline_.get(), true, matrix); + olive::rendering::Blit(pipeline_.get(), true, get_matrix()); f->glBindTexture(GL_TEXTURE_2D, 0); diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index c9d46398b..2ae8460da 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -81,6 +81,7 @@ private: void move_gizmos(QMouseEvent *event, bool done); bool dragging; void seek_from_click(int x); + QMatrix4x4 get_matrix(); Effect* gizmos; int drag_start_x; int drag_start_y; From 60302178a5d2bc6cf21e4b35b569f25359c0d177 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 18 Mar 2019 14:45:42 +1100 Subject: [PATCH 024/133] use qvector3d project --- effects/effect.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 9d3552244..6cdd7bf4d 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -989,14 +989,8 @@ void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& p for (int j=0;jget_point_count();j++) { - QMatrix4x4 matrix2 = matrix; -// matrix2.flipCoordinates(); - - QMatrix4x4 projection2 = projection; - projection2.flipCoordinates(); - - QVector3D screen_pos = g->world_pos.at(j).project(matrix2, - projection2, + QVector3D screen_pos = g->world_pos.at(j).project(matrix, + projection, QRect(0, 0, parent_clip->sequence->width, From 4c8d6ff135588f89e300fdc22da5839dff49a1ec Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 18:57:24 +1100 Subject: [PATCH 025/133] gles3 core complete --- main.cpp | 2 +- rendering/renderfunctions.cpp | 51 +++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index 24ff13bb9..9bb140109 100644 --- a/main.cpp +++ b/main.cpp @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { QSurfaceFormat format; format.setVersion(3, 2); format.setDepthBufferSize(24); - format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setProfile(QSurfaceFormat::CoreProfile); QSurfaceFormat::setDefaultFormat(format); QApplication a(argc, argv); diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 7df931d05..6a766800e 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -27,6 +27,8 @@ extern "C" { #include #include #include +#include +#include #include #ifndef NO_OCIO @@ -80,6 +82,22 @@ GLfloat olive::rendering::flipped_blit_texcoords[] = { void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatrix4x4 matrix) { + QOpenGLVertexArrayObject m_vao; + m_vao.create(); + m_vao.bind(); + + QOpenGLBuffer m_vbo; + m_vbo.create(); + m_vbo.bind(); + m_vbo.allocate(blit_vertices, 18 * sizeof(GLfloat)); + m_vbo.release(); + + QOpenGLBuffer m_vbo2; + m_vbo2.create(); + m_vbo2.bind(); + m_vbo2.allocate(flipped ? flipped_blit_texcoords : blit_texcoords, 12 * sizeof(GLfloat)); + m_vbo2.release(); + QOpenGLFunctions* func = QOpenGLContext::currentContext()->functions(); pipeline->bind(); @@ -88,12 +106,16 @@ void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatri pipeline->setUniformValue("texture", 0); GLuint vertex_location = pipeline->attributeLocation("a_position"); + m_vbo.bind(); func->glEnableVertexAttribArray(vertex_location); - func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, blit_vertices); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, 0); + m_vbo.release(); GLuint tex_location = pipeline->attributeLocation("a_texcoord"); + m_vbo2.bind(); func->glEnableVertexAttribArray(tex_location); - func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, flipped ? flipped_blit_texcoords : blit_texcoords); + func->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, 0); + m_vbo2.release(); func->glDrawArrays(GL_TRIANGLES, 0, 6); @@ -655,18 +677,37 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { coords.texture_bottom_right.x(), coords.texture_bottom_right.y(), coords.texture_top_left.x(), coords.texture_top_left.y(), - coords.texture_bottom_left.x(), coords.texture_bottom_left.y(), coords.texture_bottom_right.x(), coords.texture_bottom_right.y(), }; + QOpenGLVertexArrayObject vao; + vao.create(); + vao.bind(); + + QOpenGLBuffer vertex_buffer; + vertex_buffer.create(); + vertex_buffer.bind(); + vertex_buffer.allocate(vertices, 18 * sizeof(GLfloat)); + vertex_buffer.release(); + + QOpenGLBuffer texcoord_buffer; + texcoord_buffer.create(); + texcoord_buffer.bind(); + texcoord_buffer.allocate(texcoords, 12 * sizeof(GLfloat)); + texcoord_buffer.release(); + GLuint vertex_location = params.pipeline->attributeLocation("a_position"); + vertex_buffer.bind(); params.ctx->functions()->glEnableVertexAttribArray(vertex_location); - params.ctx->functions()->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); + params.ctx->functions()->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, 0); + vertex_buffer.release(); GLuint tex_location = params.pipeline->attributeLocation("a_texcoord"); + texcoord_buffer.bind(); params.ctx->functions()->glEnableVertexAttribArray(tex_location); - params.ctx->functions()->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, texcoords); + params.ctx->functions()->glVertexAttribPointer(tex_location, 2, GL_FLOAT, GL_FALSE, 0, 0); + texcoord_buffer.release(); params.ctx->functions()->glDrawArrays(GL_TRIANGLES, 0, 6); From 53aadb9f08672ff7b26579ec2405d30f90e9e8ef Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 19:19:29 +1100 Subject: [PATCH 026/133] fixed broken macro --- effects/internal/frei0reffect.cpp | 2 +- effects/internal/frei0reffect.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/effects/internal/frei0reffect.cpp b/effects/internal/frei0reffect.cpp index e9f4de89d..620c147ad 100644 --- a/effects/internal/frei0reffect.cpp +++ b/effects/internal/frei0reffect.cpp @@ -20,7 +20,7 @@ #include "frei0reffect.h" -#ifndef NO_FREI0R +#ifndef NOFREI0R #include #include diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h index af7bad066..6f7356b28 100644 --- a/effects/internal/frei0reffect.h +++ b/effects/internal/frei0reffect.h @@ -21,7 +21,7 @@ #ifndef FREI0REFFECT_H #define FREI0REFFECT_H -#ifndef NO_FREI0R +#ifndef NOFREI0R #include From 47aef20891f83e58adfee69054699886eacc0e1c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 19:21:05 +1100 Subject: [PATCH 027/133] added version header to shader pipeline --- rendering/renderfunctions.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 6a766800e..e617a42b9 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -128,7 +128,9 @@ QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code QOpenGLShaderProgramPtr program = std::make_shared(); // Generate vertex shader - QString vert_shader = "#ifdef GL_ES\n" + QString vert_shader = "#version 110\n" + "\n" + "#ifdef GL_ES\n" "precision mediump int;\n" "precision mediump float;\n" "#endif\n" @@ -146,7 +148,9 @@ QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code "}\n"; // Generate fragment shader - QString frag_shader = "#ifdef GL_ES\n" + QString frag_shader = "#version 110\n" + "\n" + "#ifdef GL_ES\n" "precision mediump int;\n" "precision mediump float;\n" "#endif\n" From b29a05bbda8a5de9b5a1094bd15c22479d7c1781 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 21:05:13 +1100 Subject: [PATCH 028/133] reimplemented tsa and gizmos --- ui/viewerwidget.cpp | 39 ++++++++++++++++++++++++++++++++++----- ui/viewerwidget.h | 4 ++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 1da659e98..73fe329f0 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -41,7 +41,6 @@ extern "C" { #include #include #include -#include #include "panels/panels.h" #include "project/projectelements.h" @@ -62,6 +61,8 @@ extern "C" { #include "ui/menu.h" #include "mainwindow.h" +const int kTitleActionSafeVertexSize = 84; + ViewerWidget::ViewerWidget(QWidget *parent) : QOpenGLWidget(parent), waveform(false), @@ -221,6 +222,13 @@ void ViewerWidget::initializeGL() { connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection); pipeline_ = olive::rendering::GetPipeline(); + + vao_.create(); + + title_safe_area_buffer_.create(); + title_safe_area_buffer_.bind(); + title_safe_area_buffer_.allocate(nullptr, kTitleActionSafeVertexSize * sizeof(GLfloat)); + title_safe_area_buffer_.release(); } void ViewerWidget::frame_update() { @@ -277,6 +285,10 @@ void ViewerWidget::context_destroy() { renderer.delete_ctx(); + title_safe_area_buffer_.destroy(); + + vao_.destroy(); + pipeline_ = nullptr; doneCurrent(); @@ -509,14 +521,23 @@ void ViewerWidget::draw_title_safe_area() { 0.5f, 0.55f, 0.0f }; + vao_.bind(); + + title_safe_area_buffer_.bind(); + title_safe_area_buffer_.write(0, vertices, kTitleActionSafeVertexSize * sizeof(GLfloat)); + GLuint vertex_location = pipeline_->attributeLocation("a_position"); func->glEnableVertexAttribArray(vertex_location); - func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, 0); func->glDrawArrays(GL_LINES, 0, 28); pipeline_->setUniformValue("color_only", false); + title_safe_area_buffer_.release(); + + vao_.release(); + pipeline_->release(); } @@ -647,14 +668,23 @@ void ViewerWidget::draw_gizmos() { } } + vao_.bind(); + + QOpenGLBuffer vertex_buffer; + vertex_buffer.create(); + vertex_buffer.bind(); + vertex_buffer.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + GLuint vertex_location = pipeline_->attributeLocation("a_position"); func->glEnableVertexAttribArray(vertex_location); - func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, vertices.constData()); + func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, 0); func->glDrawArrays(GL_LINES, 0, vertices.size() / 3); pipeline_->setUniformValue("color_only", false); + vao_.release(); + pipeline_->release(); } @@ -669,10 +699,10 @@ void ViewerWidget::paintGL() { tex_lock->lock(); QOpenGLFunctions* f = context()->functions(); - //QOpenGLExtraFunctions* xf = context()->extraFunctions(); makeCurrent(); + // clear to solid black f->glClearColor(0.0, 0.0, 0.0, 0.0); f->glClear(GL_COLOR_BUFFER_BIT); @@ -680,7 +710,6 @@ void ViewerWidget::paintGL() { // draw texture from render thread - f->glViewport(0, 0, width(), height()); f->glBindTexture(GL_TEXTURE_2D, tex); diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index 2ae8460da..580bf3a3f 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "timeline/clip.h" #include "project/footage.h" @@ -94,6 +96,8 @@ private: double y_scroll; QOpenGLShaderProgramPtr pipeline_; + QOpenGLVertexArrayObject vao_; + QOpenGLBuffer title_safe_area_buffer_; private slots: void context_destroy(); From 1ff46c009b73685bade54322e6d482ba412fae41 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 21:56:54 +1100 Subject: [PATCH 029/133] persistent data buffers in viewerwidget --- ui/timelinewidget.cpp | 5 +++++ ui/viewerwidget.cpp | 45 ++++++++++++++++++++++++++++++++++++------- ui/viewerwidget.h | 1 + 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 2de81a528..9b74f6fc3 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -2814,6 +2814,11 @@ void draw_waveform(ClipPtr clip, const FootageStream* ms, long media_length, QPa int offset_range_min = qMin(offset_range_start, offset_range_end); int offset_range_max = qMax(offset_range_start, offset_range_end); + // Break if we're about to draw from an index that doesn't exist + if (offset_range_min+1 >= ms->audio_preview.size()) { + break; + } + qint8 min = qint8(qRound(double(ms->audio_preview.at(offset_range_min)) / 128.0 * (channel_height/2))); qint8 max = qint8(qRound(double(ms->audio_preview.at(offset_range_min+1)) / 128.0 * (channel_height/2))); diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 73fe329f0..d4351e644 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -229,6 +229,8 @@ void ViewerWidget::initializeGL() { title_safe_area_buffer_.bind(); title_safe_area_buffer_.allocate(nullptr, kTitleActionSafeVertexSize * sizeof(GLfloat)); title_safe_area_buffer_.release(); + + gizmo_buffer_.create(); } void ViewerWidget::frame_update() { @@ -287,6 +289,10 @@ void ViewerWidget::context_destroy() { title_safe_area_buffer_.destroy(); + if (gizmo_buffer_.isCreated()) { + gizmo_buffer_.destroy(); + } + vao_.destroy(); pipeline_ = nullptr; @@ -555,10 +561,14 @@ void ViewerWidget::draw_gizmos() { matrix.translate(-(viewer->seq->width-(width()/container->zoom))*x_scroll, -((viewer->seq->height-(height()/container->zoom))*(1.0-y_scroll))); + // Set transformation matrix pipeline_->setUniformValue("mvp_matrix", matrix); + + // Set pipeline shader to draw full white pipeline_->setUniformValue("color_only", true); pipeline_->setUniformValue("color_only_color", QColor(255, 255, 255, 255)); + // Set up constants for gizmo sizes float size_diff = float(viewer->seq->width) / float(width()); float dot_size = GIZMO_DOT_SIZE * size_diff; float target_size = GIZMO_TARGET_SIZE * size_diff; @@ -570,7 +580,9 @@ void ViewerWidget::draw_gizmos() { EffectGizmo* g = gizmos->gizmo(j); switch (g->get_type()) { - case GIZMO_TYPE_DOT: // draw dot + case GIZMO_TYPE_DOT: + + // Draw standard square dot vertices.append(g->screen_pos[0].x()-dot_size); vertices.append(g->screen_pos[0].y()-dot_size); @@ -589,7 +601,9 @@ void ViewerWidget::draw_gizmos() { vertices.append(0.0f); break; - case GIZMO_TYPE_POLY: // draw lines for a polygon + case GIZMO_TYPE_POLY: + + // Draw an arbitrary polygon with lines for (int k=1;kget_point_count();k++) { @@ -612,7 +626,9 @@ void ViewerWidget::draw_gizmos() { vertices.append(0.0f); break; - case GIZMO_TYPE_TARGET: // draw target + case GIZMO_TYPE_TARGET: + + // Draw "target" gizmo (square with two lines through the middle) vertices.append(g->screen_pos[0].x()-target_size); vertices.append(g->screen_pos[0].y()-target_size); @@ -670,15 +686,30 @@ void ViewerWidget::draw_gizmos() { vao_.bind(); - QOpenGLBuffer vertex_buffer; - vertex_buffer.create(); - vertex_buffer.bind(); - vertex_buffer.allocate(vertices.constData(), vertices.size() * sizeof(GLfloat)); + // The gizmo buffer may have been destroyed or not created yet, ensure it's created here + if (!gizmo_buffer_.isCreated() && !gizmo_buffer_.create()) { + return; + } + + gizmo_buffer_.bind(); + + // Get the total byte size of the vertex array + int gizmo_buffer_desired_size = vertices.size() * sizeof(GLfloat); + + // Determine if the gizmo count has changed, and if so reallocate the buffer + if (gizmo_buffer_.size() != gizmo_buffer_desired_size) { + gizmo_buffer_.allocate(vertices.constData(), gizmo_buffer_desired_size); + } else { + gizmo_buffer_.write(0, vertices.constData(), gizmo_buffer_desired_size); + } + GLuint vertex_location = pipeline_->attributeLocation("a_position"); func->glEnableVertexAttribArray(vertex_location); func->glVertexAttribPointer(vertex_location, 3, GL_FLOAT, GL_FALSE, 0, 0); + gizmo_buffer_.release(); + func->glDrawArrays(GL_LINES, 0, vertices.size() / 3); pipeline_->setUniformValue("color_only", false); diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index 580bf3a3f..d171dd73d 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -97,6 +97,7 @@ private: QOpenGLShaderProgramPtr pipeline_; QOpenGLVertexArrayObject vao_; + QOpenGLBuffer gizmo_buffer_; QOpenGLBuffer title_safe_area_buffer_; private slots: From 0a5c3fe36e5fe8235b98658e37ded2da13b0ada8 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 19 Mar 2019 22:23:08 +1100 Subject: [PATCH 030/133] fixed gizmos --- effects/effect.cpp | 4 +++- rendering/renderfunctions.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 6cdd7bf4d..fc3748328 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -989,6 +989,8 @@ void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& p for (int j=0;jget_point_count();j++) { + // Convert the world point from the gizmo into a screen point relative to the sequence's dimensions + QVector3D screen_pos = g->world_pos.at(j).project(matrix, projection, QRect(0, @@ -996,7 +998,7 @@ void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& p parent_clip->sequence->width, parent_clip->sequence->height)); - g->screen_pos[j] = QPoint(screen_pos.x(), screen_pos.y()); + g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->sequence->height-screen_pos.y()); } } diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index e617a42b9..c2515e4a2 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -615,7 +615,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.gizmos->gizmo_draw(timecode, coords); // convert gizmo coords to screen coords - params.gizmos->gizmo_world_to_screen(projection, coords.matrix); + params.gizmos->gizmo_world_to_screen(coords.matrix, projection); } From 7d337e6de2ab416f73d8898b2477b30ccba13a2a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 01:42:40 +1100 Subject: [PATCH 031/133] two step process and configurable colorspaces --- dialogs/mediapropertiesdialog.cpp | 34 +++++- dialogs/mediapropertiesdialog.h | 19 +-- dialogs/preferencesdialog.cpp | 193 ++++++++++++++++++++++++++---- dialogs/preferencesdialog.h | 27 ++++- global/config.cpp | 3 +- global/config.h | 31 ++++- panels/project.cpp | 9 +- project/footage.cpp | 22 ++-- project/footage.h | 7 +- project/loadthread.cpp | 4 +- rendering/renderfunctions.cpp | 174 +++++++++++++++++++++++---- rendering/renderfunctions.h | 52 +++----- rendering/renderthread.cpp | 187 +++++++++++------------------ rendering/renderthread.h | 13 +- timeline/clip.cpp | 9 +- timeline/clip.h | 5 + ui/mainwindow.cpp | 14 +++ 17 files changed, 559 insertions(+), 244 deletions(-) diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index 3ea790c55..7e35ed5be 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -30,6 +30,10 @@ #include #include #include +#ifndef NO_OCIO +#include +namespace OCIO = OCIO_NAMESPACE; +#endif #include "project/footage.h" #include "project/media.h" @@ -103,7 +107,7 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : // premultiplied alpha mode premultiply_alpha_setting = new QCheckBox(tr("Alpha is Premultiplied"), this); - premultiply_alpha_setting->setChecked(f->alpha_is_premultiplied); + premultiply_alpha_setting->setChecked(f->alpha_is_associated); grid->addWidget(premultiply_alpha_setting, row, 0); row++; @@ -128,6 +132,28 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : grid->addWidget(interlacing_box, row, 1); row++; + +#ifndef NO_OCIO + color_management = new QComboBox(this); + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + for (int i=0;igetNumColorSpaces();i++) { + QString colorspace = config->getColorSpaceNameByIndex(i); + + color_management->addItem(colorspace); + + if (colorspace == f->colorspace) { + color_management->setCurrentIndex(i); + } + } + + grid->addWidget(new QLabel(tr("Color Space:")), row, 0); + grid->addWidget(color_management, row, 1); + + row++; +#endif + } name_box = new QLineEdit(item->get_name(), this); @@ -192,9 +218,13 @@ void MediaPropertiesDialog::accept() { } // set premultiplied alpha - f->alpha_is_premultiplied = premultiply_alpha_setting->isChecked(); + f->alpha_is_associated = premultiply_alpha_setting->isChecked(); } +#ifndef NO_OCIO + f->colorspace = color_management->currentText(); +#endif + // set name MediaRename* mr = new MediaRename(item, name_box->text()); diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index 8f24eee72..e8ff9492a 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -32,18 +32,19 @@ #include "project/media.h" class MediaPropertiesDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - MediaPropertiesDialog(QWidget *parent, Media* i); + MediaPropertiesDialog(QWidget *parent, Media* i); private: - QComboBox* interlacing_box; - QLineEdit* name_box; - Media* item; - QListWidget* track_list; - QDoubleSpinBox* conform_fr; - QCheckBox* premultiply_alpha_setting; + QComboBox* interlacing_box; + QLineEdit* name_box; + Media* item; + QListWidget* track_list; + QDoubleSpinBox* conform_fr; + QCheckBox* premultiply_alpha_setting; + QComboBox* color_management; private slots: - void accept(); + void accept(); }; #endif // MEDIAPROPERTIESDIALOG_H diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 94a2533f5..24ff80e64 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -20,13 +20,6 @@ #include "preferencesdialog.h" -#include "global/global.h" -#include "global/config.h" -#include "global/path.h" -#include "rendering/audio.h" -#include "panels/panels.h" -#include "ui/mainwindow.h" - #include #include #include @@ -51,6 +44,13 @@ #include #include +#include "global/global.h" +#include "global/config.h" +#include "global/path.h" +#include "rendering/audio.h" +#include "panels/panels.h" +#include "ui/mainwindow.h" + KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a) : QKeySequenceEdit(parent), action(a) { setKeySequence(action->shortcut()); @@ -108,12 +108,28 @@ void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* } } -void PreferencesDialog::delete_previews(char type) { - if (type != 't' && type != 'w' && type != 1) return; +void PreferencesDialog::delete_previews(PreviewDeleteTypes type) { + char delete_char = 0; + + switch (type) { + case DELETE_WAVEFORMS: + delete_char = 'w'; + break; + case DELETE_THUMBNAILS: + delete_char = 't'; + break; + case DELETE_BOTH: + delete_char = 1; + break; + case DELETE_NONE: + break; + } + + if (delete_char != 't' && delete_char != 'w' && delete_char != 1) return; QDir preview_path(get_data_path() + "/previews"); - if (type == 1) { + if (delete_char == 1) { // indiscriminately delete everything preview_path.removeRecursively(); } else { @@ -134,13 +150,85 @@ void PreferencesDialog::delete_previews(char type) { // thumbnails will have a 't' towards the end of the filenames, waveforms will have a 'w' // if they match the type of preview we're deleting, remove them - if (preview_file_str.at(identifier_char_index) == type) { + if (preview_file_str.at(identifier_char_index) == delete_char) { QFile::remove(preview_path.filePath(preview_file_str)); } } } } +void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) +{ + // Get current display name (if the config is empty, get the current default display) + QString current_display = olive::CurrentConfig.ocio_display; + if (current_display.isEmpty()) { + current_display = config->getDefaultDisplay(); + } + + // Populate the display menu + ocio_display->clear(); + for (int i=0;igetNumDisplays();i++) { + ocio_display->addItem(config->getDisplay(i)); + + // Check if this index is the currently selected + if (config->getDisplay(i) == current_display) { + ocio_display->setCurrentIndex(i); + } + } + + update_ocio_view_menu(config); + + // Populate the look menu + ocio_look->clear(); + ocio_look->addItem(tr("(None)"), QString()); + for (int i=0;igetNumLooks();i++) { + const char* look = config->getLookNameByIndex(i); + + ocio_look->addItem(look, look); + + if (look == olive::CurrentConfig.ocio_look) { + ocio_look->setCurrentIndex(i); + } + } +} + +void PreferencesDialog::update_ocio_view_menu(OCIO::ConstConfigRcPtr config) +{ + + // Get views for the current display set in `ocio_display` + QString display = ocio_display->currentText(); + + // Get current view + QString current_view = olive::CurrentConfig.ocio_view; + if (current_view.isEmpty()) { + current_view = config->getDefaultView(display.toUtf8()); + } + + // Populate the view menu + int ocio_view_count = config->getNumViews(display.toUtf8()); + ocio_view->clear(); + for (int i=0;igetView(display.toUtf8(), i); + + ocio_view->addItem(view); + + if (current_view == view) { + ocio_view->setCurrentIndex(i); + } + } +} + +void PreferencesDialog::update_ocio_config(const QString &s) +{ + if (!s.isEmpty() && QFileInfo::exists(s)) { + try { + OCIO::ConstConfigRcPtr file_config = OCIO::Config::CreateFromFile(s.toUtf8()); + + populate_ocio_menus(file_config); + } catch (OCIO::Exception& e) {} + } +} + void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) { QList menus = menubar->actions(); @@ -209,9 +297,9 @@ void PreferencesDialog::save() { if (olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked() || olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value() -#ifdef Q_OS_WIN32 + #ifdef Q_OS_WIN32 || olive::CurrentConfig.use_native_menu_styling != native_menus->isChecked() -#endif + #endif || olive::CurrentConfig.style != static_cast(ui_style->currentData().toInt())) { // any changes to these settings will require a restart - ask the user if we should do one now or later @@ -274,7 +362,31 @@ void PreferencesDialog::save() { olive::CurrentConfig.language_file = language_combobox->currentData().toString(); olive::CurrentConfig.enable_color_management = enable_color_management->isChecked(); - olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); + +#ifndef NO_OCIO + if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { + try { + OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8())); + + olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + } + + } + + olive::CurrentConfig.ocio_display = ocio_display->currentText(); + olive::CurrentConfig.ocio_view = ocio_view->currentText(); + + // We use data here instead of text because there's a "(None)" option with an empty string + olive::CurrentConfig.ocio_look = ocio_look->currentData().toString(); + + olive::CurrentRuntimeConfig.ocio_config_date = QDateTime::currentMSecsSinceEpoch(); +#endif + olive::CurrentConfig.style = static_cast(ui_style->currentData().toInt()); #ifdef Q_OS_WIN @@ -287,14 +399,14 @@ void PreferencesDialog::save() { // we're changing the size of thumbnails and waveforms, so let's delete them and regenerate them next start // delete nothing - char delete_match = 0; + PreviewDeleteTypes delete_type = DELETE_NONE; if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()) { // delete existing thumbnails olive::CurrentConfig.thumbnail_resolution = thumbnail_res_spinbox->value(); // delete only thumbnails - delete_match = 't'; + delete_type = DELETE_THUMBNAILS; } if (olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) { @@ -302,16 +414,16 @@ void PreferencesDialog::save() { olive::CurrentConfig.waveform_resolution = waveform_res_spinbox->value(); // if we're already deleting thumbnails - if (delete_match == 't') { + if (delete_type == DELETE_THUMBNAILS) { // delete all - delete_match = 1; + delete_type = DELETE_BOTH; } else { // just delete waveforms - delete_match = 'w'; + delete_type = DELETE_WAVEFORMS; } } - delete_previews(delete_match); + delete_previews(delete_type); } // Save keyboard shortcuts @@ -476,12 +588,17 @@ void PreferencesDialog::browse_ocio_config() } } +void PreferencesDialog::update_ocio_view_menu() +{ + update_ocio_view_menu(OCIO::GetCurrentConfig()); +} + void PreferencesDialog::delete_all_previews() { if (QMessageBox::question(this, tr("Delete All Previews"), tr("Are you sure you want to delete all previews?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - delete_previews(1); + delete_previews(DELETE_BOTH); QMessageBox::information(this, tr("Previews Deleted"), tr("All previews deleted succesfully. You may have to re-open your current project for " @@ -772,32 +889,64 @@ void PreferencesDialog::setup_ui() { #ifdef NO_OCIO QLabel* no_ocio_available_lbl = new QLabel(tr("Color management is unavailable because Olive was " - "compiled without OpenColorIO support.")); + "compiled without OpenColorIO support.")); no_ocio_available_lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); color_management_layout->addWidget(no_ocio_available_lbl, row, 0, 1, 3); row++; #endif + // COLOR MANAGEMENT -> Enable Color Management enable_color_management = new QCheckBox(tr("Enable Color Management")); enable_color_management->setChecked(olive::CurrentConfig.enable_color_management); color_management_layout->addWidget(enable_color_management, row, 0, 1, 3); row++; + // COLOR MANAGEMENT -> OpenColorIO Config File color_management_layout->addWidget(new QLabel(tr("OpenColorIO Config File:")), row, 0); ocio_config_file = new QLineEdit(); ocio_config_file->setText(olive::CurrentConfig.ocio_config_path); + connect(ocio_config_file, SIGNAL(textChanged(const QString &)), this, SLOT(update_ocio_config(const QString&))); color_management_layout->addWidget(ocio_config_file, row, 1); QPushButton* ocio_config_browse_btn = new QPushButton(tr("Browse")); connect(ocio_config_browse_btn, SIGNAL(clicked(bool)), this, SLOT(browse_ocio_config())); color_management_layout->addWidget(ocio_config_browse_btn, row, 2); + row++; + + // COLOR MANAGEMENT -> Display + ocio_display = new QComboBox(); + connect(ocio_display, SIGNAL(currentIndexChanged(int)), this, SLOT(update_ocio_view_menu())); + color_management_layout->addWidget(new QLabel("Display:"), row, 0); + color_management_layout->addWidget(ocio_display, row, 1); + + row++; + + // COLOR MANAGEMENT -> View + ocio_view = new QComboBox(); + color_management_layout->addWidget(new QLabel("View:"), row, 0); + color_management_layout->addWidget(ocio_view, row, 1); + + row++; + + // COLOR MANAGEMENT -> Look + ocio_look = new QComboBox(); + color_management_layout->addWidget(new QLabel("Look:"), row, 0); + color_management_layout->addWidget(ocio_look, row, 1); + + row++; + #ifdef NO_OCIO enable_color_management->setEnabled(false); ocio_config_file->setEnabled(false); ocio_config_browse_btn->setEnabled(false); + ocio_display->setEnabled(false); + ocio_view->setEnabled(false); + ocio_look->setEnabled(false); +#else + populate_ocio_menus(OCIO::GetCurrentConfig()); #endif tabWidget->addTab(color_management_tab, tr("Color Management")); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 9e01ee236..da3439f74 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -33,6 +33,10 @@ #include #include #include +#ifndef NO_OCIO +#include +namespace OCIO = OCIO_NAMESPACE; +#endif #include "timeline/sequence.h" @@ -71,10 +75,22 @@ private slots: void browse_css_file(); void browse_ocio_config(); + // OCIO function + void update_ocio_view_menu(); + void update_ocio_view_menu(OCIO::ConstConfigRcPtr config); + void update_ocio_config(const QString&); + private: void setup_ui(); void setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent); + enum PreviewDeleteTypes { + DELETE_NONE, + DELETE_THUMBNAILS, + DELETE_WAVEFORMS, + DELETE_BOTH + }; + // used to delete previews // type can be: 't' for thumbnails, 'w' for waveforms, or 1 for all /** @@ -82,9 +98,11 @@ private: * * @param type * - * The types of previews to + * The types of previews to delete. */ - void delete_previews(char type); + void delete_previews(PreviewDeleteTypes type); + + void populate_ocio_menus(OCIO::ConstConfigRcPtr config); QLineEdit* custom_css_fn; QLineEdit* imgSeqFormatEdit; @@ -103,8 +121,13 @@ private: QSpinBox* thumbnail_res_spinbox; QSpinBox* waveform_res_spinbox; QCheckBox* add_default_effects_to_clips; + QCheckBox* enable_color_management; QLineEdit* ocio_config_file; + QComboBox* ocio_display; + QComboBox* ocio_view; + QComboBox* ocio_look; + QComboBox* ui_style; Sequence sequence_settings; #ifdef Q_OS_WIN diff --git a/global/config.cpp b/global/config.cpp index 9e79abb71..ba6691cd8 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -299,5 +299,6 @@ void Config::save(QString path) { } RuntimeConfig::RuntimeConfig() : - shaders_are_enabled(true) + shaders_are_enabled(true), + ocio_config_date(QDateTime::currentMSecsSinceEpoch()) {} diff --git a/global/config.h b/global/config.h index fbcd9425e..c4ab32b79 100644 --- a/global/config.h +++ b/global/config.h @@ -519,10 +519,31 @@ struct Config { /** * @brief Path to OpenColorIO configuration file * - * Used if Config::enable_color_management is **TRUE**. + * Used if Config::enable_color_management is true. */ QString ocio_config_path; + /** + * @brief OpenColorIO Display + * + * Used if Config::enable_color_management is true + */ + QString ocio_display; + + /** + * @brief OpenColorIO View + * + * Used if Config::enable_color_management is true + */ + QString ocio_view; + + /** + * @brief OpenColorIO Look + * + * Used if Config::enable_color_management is true + */ + QString ocio_look; + /** * @brief Style to use when theming Olive. * @@ -587,6 +608,14 @@ struct RuntimeConfig { * Overrides Config::language_file and sets the path to a language file to use. */ QString external_translation_file; + + /** + * @brief OpenColorIO Configuration Time + * + * A crude but quick way of determining whether the OCIO config has changed and if the rendering threads need to + * re-create their OCIO shaders. Not intended to be saved - could be moved + */ + qint64 ocio_config_date; }; namespace olive { diff --git a/panels/project.cpp b/panels/project.cpp index dfacd8488..12d167d34 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -871,7 +871,12 @@ void Project::process_file_list(QStringList& files, bool recursive, MediaPtr rep item = std::make_shared(parent); } - m = FootagePtr(new Footage()); + m = std::make_shared(); + + // Edge case for PNGs that standardized unassociated alpha + if (file.endsWith("png", Qt::CaseInsensitive)) { + m->alpha_is_associated = false; + } m->using_inout = false; m->url = file; @@ -1107,7 +1112,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, stream.writeAttribute("in", QString::number(f->in)); stream.writeAttribute("out", QString::number(f->out)); stream.writeAttribute("speed", QString::number(f->speed)); - stream.writeAttribute("alphapremul", QString::number(f->alpha_is_premultiplied)); + stream.writeAttribute("alphapremul", QString::number(f->alpha_is_associated)); stream.writeAttribute("startnumber", QString::number(f->start_number)); stream.writeAttribute("proxy", QString::number(f->proxy)); diff --git a/project/footage.cpp b/project/footage.cpp index bde8c4c20..d2b36f442 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -27,17 +27,17 @@ #include "project/previewgenerator.h" #include "timeline/clip.h" -Footage::Footage() { - ready = (false); - preview_gen = (nullptr); - invalid = (false); - in = (0); - out = (0); - speed = (1.0); - alpha_is_premultiplied = (false); - proxy = (false); - start_number = 0; - +Footage::Footage() : + ready(false), + preview_gen(nullptr), + invalid(false), + in(0), + out(0), + speed(1.0), + alpha_is_associated(true), + proxy(false), + start_number(0) +{ ready_lock.lock(); } diff --git a/project/footage.h b/project/footage.h index 04ff51706..bc93cc4dc 100644 --- a/project/footage.h +++ b/project/footage.h @@ -81,9 +81,14 @@ struct Footage { bool ready; bool invalid; double speed; - bool alpha_is_premultiplied; + bool alpha_is_associated; int start_number; +#ifndef NO_OCIO + // color management + QString colorspace; +#endif + // proxy config bool proxy; QString proxy_path; diff --git a/project/loadthread.cpp b/project/loadthread.cpp index 2661f1d40..eeac43e03 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -280,7 +280,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { int folder = 0; MediaPtr item = std::make_shared(); - FootagePtr f(new Footage()); + FootagePtr f = std::make_shared(); f->using_inout = false; @@ -347,7 +347,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } else if (attr.name() == "speed") { f->speed = attr.value().toDouble(); } else if (attr.name() == "alphapremul") { - f->alpha_is_premultiplied = (attr.value() == "1"); + f->alpha_is_associated = (attr.value() == "1"); } else if (attr.name() == "proxy") { f->proxy = (attr.value() == "1"); } else if (attr.name() == "proxypath") { diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index c2515e4a2..2751987c5 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -31,11 +31,6 @@ extern "C" { #include #include -#ifndef NO_OCIO -#include -namespace OCIO = OCIO_NAMESPACE; -#endif - #include "timeline/clip.h" #include "timeline/sequence.h" #include "project/media.h" @@ -80,6 +75,14 @@ GLfloat olive::rendering::flipped_blit_texcoords[] = { 1.0, 0.0 }; +#ifndef NO_OCIO +// copied from source code to OCIODisplay +const int OCIO_LUT3D_EDGE_SIZE = 32; + +// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE +const int OCIO_NUM_3D_ENTRIES = 98304; +#endif + void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatrix4x4 matrix) { QOpenGLVertexArrayObject m_vao; @@ -237,6 +240,7 @@ GLuint draw_clip(QOpenGLContext* ctx, const FramebufferObject& fbo, GLuint texture, bool clear) { + fbo.BindBuffer(); if (clear) { @@ -252,6 +256,7 @@ GLuint draw_clip(QOpenGLContext* ctx, fbo.ReleaseBuffer(); return fbo.texture(); + } void process_effect(QOpenGLContext* ctx, @@ -305,7 +310,7 @@ void process_effect(QOpenGLContext* ctx, } GLuint compose_sequence(ComposeSequenceParams ¶ms) { - GLuint final_fbo = params.main_buffer; + GLuint final_fbo = params.video ? params.main_buffer->buffer() : 0; Sequence* s = params.seq; long playhead = s->playhead; @@ -511,7 +516,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - if (!c->media()->to_footage()->alpha_is_premultiplied) { + if (!c->media()->to_footage()->alpha_is_associated) { // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); @@ -525,27 +530,52 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } #ifndef NO_OCIO - // convert to linear colorspace - if (olive::CurrentConfig.enable_color_management && params.ocio_shader != nullptr) + // Convert frame from source to linear colorspace + if (olive::CurrentConfig.enable_color_management) { - params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE2); - params.ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, params.ocio_lut_texture); - params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE0); + if (c->ocio_shader == nullptr) { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); - params.ocio_shader->bind(); + QString input_cs = OCIO::ROLE_SCENE_LINEAR; - params.ocio_shader->setUniformValue("tex2", 2); + if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - textureID = draw_clip(params.ctx, params.ocio_shader, c->fbo.at(fbo_switcher), textureID, true); + if (!c->media()->to_footage()->colorspace.isEmpty()) { - params.ocio_shader->release(); + input_cs = c->media()->to_footage()->colorspace; - params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE2); - params.ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, 0); - params.ctx->extraFunctions()->glActiveTexture(GL_TEXTURE0); + } else { - fbo_switcher = !fbo_switcher; + // If this is a footage clip, try to guess the color space from the filename + QString guess_colorspace = config->parseColorSpaceFromString(c->media()->to_footage()->url.toUtf8()); + + if (!guess_colorspace.isEmpty()) { + input_cs = guess_colorspace; + } + + } + + } + + try { + OCIO::ConstProcessorRcPtr processor = config->getProcessor(input_cs.toUtf8(), + OCIO::ROLE_SCENE_LINEAR); + + c->ocio_shader = olive::rendering::SetupOCIO(params.ctx, c->ocio_lut_texture, processor); + } catch (OCIO::Exception& e) { + qWarning() << e.what(); + } + } + + if (c->ocio_shader != nullptr) { + textureID = olive::rendering::OCIOBlit(c->ocio_shader.get(), + c->ocio_lut_texture, + c->fbo.at(fbo_switcher), + textureID); + + fbo_switcher = !fbo_switcher; + } } #endif @@ -640,9 +670,9 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { backend_tex_1 = params.nests.last()->fbo[1].texture(); backend_tex_2 = params.nests.last()->fbo[2].texture(); } else { - back_buffer_1 = params.backend_buffer1; - backend_tex_1 = params.backend_attachment1; - backend_tex_2 = params.backend_attachment2; + back_buffer_1 = params.backend_buffer1->buffer(); + backend_tex_1 = params.backend_buffer1->texture(); + backend_tex_2 = params.backend_buffer2->texture(); } // render a backbuffer @@ -743,7 +773,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { if (params.nests.size() > 0) { draw_clip(params.ctx, params.pipeline, params.nests.last()->fbo[2].buffer(), params.nests.last()->fbo[0].texture(), true); } else { - draw_clip(params.ctx, params.pipeline, params.backend_buffer2, params.main_attachment, true); + draw_clip(params.ctx, params.pipeline, params.backend_buffer2->buffer(), params.main_buffer->texture(), true); } } @@ -909,6 +939,100 @@ void close_active_clips(Sequence* s) { } } -void UpdateOCIOGLState(const ComposeSequenceParams& params) +#ifndef NO_OCIO +QOpenGLShaderProgramPtr olive::rendering::SetupOCIO(QOpenGLContext* ctx, + GLuint& lut_texture, + OCIO::ConstProcessorRcPtr processor) { + + QOpenGLExtraFunctions* xf = ctx->extraFunctions(); + + // Create LUT texture + xf->glGenTextures(1, &lut_texture); + + // Bind LUT + xf->glBindTexture(GL_TEXTURE_3D, lut_texture); + + // Set texture parameters + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // Allocate storage for texture + xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + 0, GL_RGB,GL_FLOAT, nullptr); + + // + // SET UP GLSL SHADER + // + + OCIO::GpuShaderDesc shaderDesc; + shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); + shaderDesc.setFunctionName("OCIODisplay"); + shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); + + // + // COMPUTE 3D LUT + // + + GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; + processor->getGpuLut3D(ocio_lut_data, shaderDesc); + + // Upload LUT data to texture + xf->glTexSubImage3D(GL_TEXTURE_3D, 0, + 0, 0, 0, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + GL_RGB, GL_FLOAT, ocio_lut_data); + + delete [] ocio_lut_data; + + // Create OCIO shader code + QString shader_text(processor->getGpuShaderText(shaderDesc)); + shader_text.append("\n" + "uniform sampler3D tex2;\n" + "\n" + "vec4 process(vec4 col) {\n" + " return OCIODisplay(col, tex2);\n" + "}\n"); + + + // Get pipeline-based shader to inject OCIO shader into + QOpenGLShaderProgramPtr shader = olive::rendering::GetPipeline(shader_text); + + // Release LUT + xf->glBindTexture(GL_TEXTURE_3D, 0); + + return shader; } + +GLuint olive::rendering::OCIOBlit(QOpenGLShaderProgram *pipeline, + GLuint lut, + const FramebufferObject& fbo, + GLuint texture) +{ + QOpenGLContext* ctx = QOpenGLContext::currentContext(); + QOpenGLExtraFunctions* xf = ctx->extraFunctions(); + + xf->glActiveTexture(GL_TEXTURE2); + xf->glBindTexture(GL_TEXTURE_3D, lut); + xf->glActiveTexture(GL_TEXTURE0); + + pipeline->bind(); + + pipeline->setUniformValue("tex2", 2); + + //textureID = draw_clip(params.ctx, pipeline, c->fbo.at(fbo_switcher), textureID, true); + GLuint textureID = draw_clip(ctx, pipeline, fbo, texture, true); + + pipeline->release(); + + xf->glActiveTexture(GL_TEXTURE2); + xf->glBindTexture(GL_TEXTURE_3D, 0); + xf->glActiveTexture(GL_TEXTURE0); + + return textureID; +} +#endif diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index d9894eea2..6d81b5795 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -24,6 +24,10 @@ #include #include #include +#ifndef NO_OCIO +#include +namespace OCIO = OCIO_NAMESPACE; +#endif #include "timeline/sequence.h" #include "effects/effect.h" @@ -168,16 +172,7 @@ struct ComposeSequenceParams { * * When compose_sequence() is rendering the final image, this framebuffer will be bound. */ - GLuint main_buffer; - - /** - * @brief The attachment to the framebuffer in main_buffer - * - * Used only for video rendering. Never accessed with audio rendering. - * - * The OpenGL texture attached to the framebuffer referenced by main_buffer. - */ - GLuint main_attachment; + const FramebufferObject* main_buffer; /** * @brief Backend OpenGL framebuffer 1 used for further processing before rendering to main_buffer @@ -185,15 +180,7 @@ struct ComposeSequenceParams { * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. */ - GLuint backend_buffer1; - - /** - * @brief Backend OpenGL framebuffer 1's texture attachment - * - * The texture that ComposeSequenceParams::backend_buffer1 renders to. Bound and drawn to - * ComposeSequenceParams::backend_buffer2 to "ping-pong" between them and various shaders. - */ - GLuint backend_attachment1; + const FramebufferObject* backend_buffer1; /** * @brief Backend OpenGL framebuffer 2 used for further processing before rendering to main_buffer @@ -201,25 +188,7 @@ struct ComposeSequenceParams { * In some situations, compose_sequence() will do some processing through shaders that requires "ping-ponging" * between framebuffers. backend_buffer1 and backend_buffer2 are used for this purpose. */ - GLuint backend_buffer2; - - /** - * @brief Backend OpenGL framebuffer 2's texture attachment - * - * The texture that ComposeSequenceParams::backend_buffer2 renders to. Bound and drawn to - * ComposeSequenceParams::backend_buffer1 to "ping-pong" between them and various shaders. - */ - GLuint backend_attachment2; - - /** - * @brief OpenGL shader containing OpenColorIO shader information - */ - QOpenGLShaderProgram* ocio_shader; - - /** - * @brief OpenGL texture containing LUT obtained form OpenColorIO - */ - GLuint ocio_lut_texture; + const FramebufferObject* backend_buffer2; }; /** @@ -417,6 +386,13 @@ namespace olive { extern GLfloat flipped_blit_texcoords[]; void Blit(QOpenGLShaderProgram* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); +#ifndef NO_OCIO + QOpenGLShaderProgramPtr SetupOCIO(QOpenGLContext *ctx, GLuint &lut_texture, OCIO::ConstProcessorRcPtr processor); + GLuint OCIOBlit(QOpenGLShaderProgram *pipeline, + GLuint lut, + const FramebufferObject& fbo, + GLuint texture); +#endif } } diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index f0ad91e69..a4b0dc7ac 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -47,10 +47,13 @@ RenderThread::RenderThread() : tex_height(-1), queued(false), texture_failed(false), + #ifndef NO_OCIO ocio_lut_texture(0), ocio_shader(nullptr), + #endif running(true), - front_buffer_switcher(false) + front_buffer_switcher(false), + ocio_config_date(0) { surface.create(); } @@ -85,6 +88,9 @@ void RenderThread::run() { } // create any buffers that don't yet exist + if (!composite_buffer.IsCreated()) { + composite_buffer.Create(ctx, seq->width, seq->height); + } if (!front_buffer_1.IsCreated()) { front_buffer_1.Create(ctx, seq->width, seq->height); } @@ -113,9 +119,11 @@ void RenderThread::run() { } #ifndef NO_OCIO - // If there's no OpenColorIO shader, create it now + // If there's no OpenColorIO shader or the configuration has changed, (re-)create it now if (olive::CurrentConfig.enable_color_management - && (ocio_shader == nullptr || ocio_loaded_config != olive::CurrentConfig.ocio_config_path)) { + && (ocio_shader == nullptr || ocio_config_date != olive::CurrentRuntimeConfig.ocio_config_date)) { + ocio_config_date = olive::CurrentRuntimeConfig.ocio_config_date; + destroy_ocio(); set_up_ocio(); @@ -152,102 +160,43 @@ const GLuint &RenderThread::get_texture() #ifndef NO_OCIO void RenderThread::set_up_ocio() { - // Create LUT texture - ctx->extraFunctions()->glGenTextures(1, &ocio_lut_texture); - // Bind LUT - ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, ocio_lut_texture); - - // Set texture parameters - ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - ctx->extraFunctions()->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Allocate storage for texture - ctx->extraFunctions()->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB,GL_FLOAT, nullptr); - - OCIO::ConstConfigRcPtr config; - - // Set current config to the file specified in Config - if (QFileInfo::exists(olive::CurrentConfig.ocio_config_path)) { - - config = OCIO::Config::CreateFromFile(olive::CurrentConfig.ocio_config_path.toUtf8()); - OCIO::SetCurrentConfig(config); - ocio_loaded_config = olive::CurrentConfig.ocio_config_path; - - } else { - - config = OCIO::GetCurrentConfig(); + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + // Get current OCIO display from Config (or defaults if there is no setting) + QString display = olive::CurrentConfig.ocio_display; + if (display.isEmpty()) { + display = config->getDefaultDisplay(); } - const char* display = config->getDefaultDisplay(); - + QString view = olive::CurrentConfig.ocio_view; + if (view.isEmpty()) { + view = config->getDefaultView(display.toUtf8()); + } + // Get current display stats OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); transform->setInputColorSpaceName(OCIO::ROLE_SCENE_LINEAR); - transform->setDisplay(display); - transform->setView(config->getDefaultView(display)); + transform->setDisplay(display.toUtf8()); + transform->setView(view.toUtf8()); - - OCIO::ConstProcessorRcPtr processor; - OCIO::GpuShaderDesc shaderDesc; - - // Get processor for this configuration + if (!olive::CurrentConfig.ocio_look.isEmpty()) { + transform->setLooksOverride(olive::CurrentConfig.ocio_look.toUtf8()); + transform->setLooksOverrideEnabled(true); + } try { - processor = config->getProcessor(transform); + // Using the current configuration, try to get an OCIO processor with a corresponding input and output colorspace + OCIO::ConstProcessorRcPtr processor = config->getProcessor(transform); + // Create a OCIO shader with this processor + ocio_shader = olive::rendering::SetupOCIO(ctx, ocio_lut_texture, processor); } catch(OCIO::Exception & e) { qCritical() << e.what(); return; } - - // - // SET UP GLSL SHADER - // - - shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); - shaderDesc.setFunctionName("OCIODisplay"); - shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); - - // - // COMPUTE 3D LUT - // - - processor->getGpuLut3D(ocio_lut_data, shaderDesc); - - - // Upload LUT data to texture - ctx->extraFunctions()->glTexSubImage3D(GL_TEXTURE_3D, 0, - 0, 0, 0, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - GL_RGB, GL_FLOAT, ocio_lut_data); - - - // Create OCIO shader code - QString shader_text(processor->getGpuShaderText(shaderDesc)); - shader_text.append("\n" - "uniform sampler3D tex2;\n" - "\n" - "vec4 process(vec4 col) {\n" - " return OCIODisplay(col, tex2);\n" - "}\n"); - - - // Get pipeline-based shader to inject OCIO shader into - ocio_shader = olive::rendering::GetPipeline(shader_text); - - - // Release LUT - ctx->extraFunctions()->glBindTexture(GL_TEXTURE_3D, 0); - } void RenderThread::destroy_ocio() @@ -272,38 +221,43 @@ void RenderThread::paint() { params.playback_speed = 1; params.blend_mode_program = blend_mode_program.get(); params.pipeline = pipeline_program.get(); -#ifndef NO_OCIO - params.ocio_shader = ocio_shader.get(); - params.ocio_lut_texture = ocio_lut_texture; -#endif - params.backend_buffer1 = back_buffer_1.buffer(); - params.backend_buffer2 = back_buffer_2.buffer(); - params.backend_attachment1 = back_buffer_1.texture(); - params.backend_attachment2 = back_buffer_2.texture(); - params.main_buffer = front_buffer_switcher ? front_buffer_1.buffer() : front_buffer_2.buffer(); - params.main_attachment = front_buffer_switcher ? front_buffer_1.texture() : front_buffer_2.texture(); + params.backend_buffer1 = &back_buffer_1; + params.backend_buffer2 = &back_buffer_2; + params.main_buffer = &composite_buffer; // get currently selected gizmos gizmos = seq->GetSelectedGizmo(); params.gizmos = gizmos; + QOpenGLFunctions* f = ctx->functions(); + + f->glEnable(GL_BLEND); + + // bind composite framebuffer for drawing + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, composite_buffer.buffer()); + + // Clear framebuffer to nothing + f->glClearColor(0.0, 0.0, 0.0, 0.0); + f->glClear(GL_COLOR_BUFFER_BIT); + + // Compose the current frame + compose_sequence(params); + + // Copy composite buffer to front buffer + // First lock the appropriate mutex for exclusivity QMutex& active_mutex = front_buffer_switcher ? front_mutex1 : front_mutex2; active_mutex.lock(); - ctx->functions()->glEnable(GL_BLEND); - - // bind framebuffer for drawing - ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, params.main_buffer); - - ctx->functions()->glClearColor(0.0, 0.0, 0.0, 0.0); - ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); - - compose_sequence(params); + // Convert linear frame to display color space + olive::rendering::OCIOBlit(ocio_shader.get(), + ocio_lut_texture, + front_buffer_switcher ? front_buffer_1 : front_buffer_2, + composite_buffer.texture()); // flush changes - ctx->functions()->glFinish(); + f->glFinish(); - ctx->functions()->glDisable(GL_BLEND); + f->glDisable(GL_BLEND); texture_failed = params.texture_failed; @@ -314,11 +268,11 @@ void RenderThread::paint() { // texture failed, try again queued = true; } else { - ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, params.main_buffer); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, composite_buffer.buffer()); QImage img(tex_width, tex_height, QImage::Format_RGBA8888); - ctx->functions()->glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); + f->glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); img.save(save_fn); - ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); save_fn = ""; } } @@ -326,25 +280,25 @@ void RenderThread::paint() { if (pixel_buffer != nullptr) { // set main framebuffer to the current read buffer - ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, params.main_buffer); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, composite_buffer.buffer()); // store pixels in buffer - ctx->functions()->glReadPixels(0, - 0, - pixel_buffer_linesize == 0 ? tex_width : pixel_buffer_linesize, - tex_height, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixel_buffer); + f->glReadPixels(0, + 0, + pixel_buffer_linesize == 0 ? tex_width : pixel_buffer_linesize, + tex_height, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixel_buffer); // release current read buffer - ctx->functions()->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); pixel_buffer = nullptr; } // release - ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void RenderThread::start_render(QOpenGLContext *share, @@ -390,6 +344,7 @@ void RenderThread::cancel() { } void RenderThread::delete_buffers() { + composite_buffer.Destroy(); front_buffer_1.Destroy(); front_buffer_2.Destroy(); back_buffer_1.Destroy(); diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 29e2d5091..e959c98d4 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -34,14 +34,6 @@ #include "rendering/framebufferobject.h" #include "qopenglshaderprogramptr.h" -#ifndef NO_OCIO -// copied from source code to OCIODisplay -const int OCIO_LUT3D_EDGE_SIZE = 32; - -// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE -const int OCIO_NUM_3D_ENTRIES = 98304; -#endif - class RenderThread : public QThread { Q_OBJECT public: @@ -80,10 +72,9 @@ private: void destroy_ocio(); // OpenColorIO variables - float ocio_lut_data[OCIO_NUM_3D_ENTRIES]; GLuint ocio_lut_texture; QOpenGLShaderProgramPtr ocio_shader; - QString ocio_loaded_config; + qint64 ocio_config_date; #endif FramebufferObject front_buffer_1; @@ -92,6 +83,8 @@ private: FramebufferObject front_buffer_2; QMutex front_mutex2; + FramebufferObject composite_buffer; + bool front_buffer_switcher; QWaitCondition wait_cond_; diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 5c9ebbf07..8690cb6c5 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -516,6 +516,11 @@ void Clip::Close(bool wait) { // delete framebuffers fbo.clear(); +#ifndef NO_OCIO + // delete OCIO shader + ocio_shader = nullptr; +#endif + if (UsesCacher()) { cacher.Close(wait); } else { @@ -605,8 +610,8 @@ bool Clip::Retrieve() } texture->setData(QOpenGLTexture::RGBA, - QOpenGLTexture::UInt8, - const_cast(using_db_1 ? data_buffer_1 : data_buffer_2)); + QOpenGLTexture::UInt8, + const_cast(using_db_1 ? data_buffer_1 : data_buffer_2)); if (data_buffer_1 != frame->data[0]) { delete [] data_buffer_1; diff --git a/timeline/clip.h b/timeline/clip.h index ef47e623b..0d00cf09b 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -157,6 +157,11 @@ public: QOpenGLTexture* texture; long texture_frame; +#ifndef NO_OCIO + QOpenGLShaderProgramPtr ocio_shader; + GLuint ocio_lut_texture; +#endif + private: // timeline variables (should be copied in copy()) bool enabled_; diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 8b74ebc63..0b514fbad 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -273,6 +273,20 @@ MainWindow::MainWindow(QWidget *parent) : olive::icon::Initialize(); +#ifndef NO_OCIO + // Load OpenColorIO configuration if set + if (olive::CurrentConfig.enable_color_management && !olive::CurrentConfig.ocio_config_path.isEmpty()) { + try { + OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(olive::CurrentConfig.ocio_config_path.toUtf8())); + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + } + } +#endif + alloc_panels(this); // populate menu bars From f9591e4f1e898b5a63214a38b0d3f4328eb86fac Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 11:26:44 +1100 Subject: [PATCH 032/133] fixed gcc compile --- dialogs/preferencesdialog.cpp | 4 ++++ dialogs/preferencesdialog.h | 4 ++++ effects/effectgizmo.h | 1 + rendering/qopenglshaderprogramptr.h | 1 + rendering/renderfunctions.cpp | 4 ++++ rendering/renderthread.cpp | 18 +++++++++++++++--- 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 24ff80e64..e306463df 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -157,6 +157,7 @@ void PreferencesDialog::delete_previews(PreviewDeleteTypes type) { } } +#ifndef NO_OCIO void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) { // Get current display name (if the config is empty, get the current default display) @@ -228,6 +229,7 @@ void PreferencesDialog::update_ocio_config(const QString &s) } catch (OCIO::Exception& e) {} } } +#endif void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) { QList menus = menubar->actions(); @@ -588,10 +590,12 @@ void PreferencesDialog::browse_ocio_config() } } +#ifndef NO_OCIO void PreferencesDialog::update_ocio_view_menu() { update_ocio_view_menu(OCIO::GetCurrentConfig()); } +#endif void PreferencesDialog::delete_all_previews() { if (QMessageBox::question(this, diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index da3439f74..3d502854d 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -76,9 +76,11 @@ private slots: void browse_ocio_config(); // OCIO function +#ifndef NO_OCIO void update_ocio_view_menu(); void update_ocio_view_menu(OCIO::ConstConfigRcPtr config); void update_ocio_config(const QString&); +#endif private: void setup_ui(); @@ -102,7 +104,9 @@ private: */ void delete_previews(PreviewDeleteTypes type); +#ifndef NO_OCIO void populate_ocio_menus(OCIO::ConstConfigRcPtr config); +#endif QLineEdit* custom_css_fn; QLineEdit* imgSeqFormatEdit; diff --git a/effects/effectgizmo.h b/effects/effectgizmo.h index e32732e6c..115676609 100644 --- a/effects/effectgizmo.h +++ b/effects/effectgizmo.h @@ -35,6 +35,7 @@ enum GizmoType { #include #include #include +#include #include class DoubleField; diff --git a/rendering/qopenglshaderprogramptr.h b/rendering/qopenglshaderprogramptr.h index 25c8fc6ff..d0b9152d0 100644 --- a/rendering/qopenglshaderprogramptr.h +++ b/rendering/qopenglshaderprogramptr.h @@ -2,6 +2,7 @@ #define QOPENGLSHADERPROGRAMPTR_H #include +#include using QOpenGLShaderProgramPtr = std::shared_ptr; diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 2751987c5..ece03b568 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -1013,6 +1013,10 @@ GLuint olive::rendering::OCIOBlit(QOpenGLShaderProgram *pipeline, const FramebufferObject& fbo, GLuint texture) { + if (pipeline == nullptr) { + return 0; + } + QOpenGLContext* ctx = QOpenGLContext::currentContext(); QOpenGLExtraFunctions* xf = ctx->extraFunctions(); diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index a4b0dc7ac..1130f1f83 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -52,8 +52,10 @@ RenderThread::RenderThread() : ocio_shader(nullptr), #endif running(true), - front_buffer_switcher(false), - ocio_config_date(0) +#ifndef NO_OCIO + ocio_config_date(0), +#endif + front_buffer_switcher(false) { surface.create(); } @@ -248,11 +250,21 @@ void RenderThread::paint() { QMutex& active_mutex = front_buffer_switcher ? front_mutex1 : front_mutex2; active_mutex.lock(); + FramebufferObject& buffer = front_buffer_switcher ? front_buffer_1 : front_buffer_2; + // Convert linear frame to display color space +#ifndef NO_OCIO olive::rendering::OCIOBlit(ocio_shader.get(), ocio_lut_texture, - front_buffer_switcher ? front_buffer_1 : front_buffer_2, + buffer, composite_buffer.texture()); +#else + buffer.BindBuffer(); + composite_buffer.BindTexture(); + olive::rendering::Blit(pipeline_program.get()); + composite_buffer.ReleaseTexture(); + buffer.ReleaseBuffer(); +#endif // flush changes f->glFinish(); From 47532a4e737ea9e75a3dc4713516a2cb5849d464 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 13:51:46 +1100 Subject: [PATCH 033/133] fixed composition when color management is off --- rendering/renderthread.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 076cf635e..740cab9b6 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -252,20 +252,32 @@ void RenderThread::paint() { FramebufferObject& buffer = front_buffer_switcher ? front_buffer_1 : front_buffer_2; - // Convert linear frame to display color space + // Blit the composite buffer to one of the front buffers + bool standard_blit = true; + #ifndef NO_OCIO - olive::rendering::OCIOBlit(ocio_shader.get(), - ocio_lut_texture, - buffer, - composite_buffer.texture()); + // If we're color managing, conver the linear composited frame to display color space + if (olive::CurrentConfig.enable_color_management && ocio_shader != nullptr) { + olive::rendering::OCIOBlit(ocio_shader.get(), + ocio_lut_texture, + buffer, + composite_buffer.texture()); + + standard_blit = false; + } #else - buffer.BindBuffer(); - composite_buffer.BindTexture(); - olive::rendering::Blit(pipeline_program.get()); - composite_buffer.ReleaseTexture(); - buffer.ReleaseBuffer(); + #endif + // If we're not color managing, just blit normally + if (standard_blit) { + buffer.BindBuffer(); + composite_buffer.BindTexture(); + olive::rendering::Blit(pipeline_program.get()); + composite_buffer.ReleaseTexture(); + buffer.ReleaseBuffer(); + } + // flush changes f->glFinish(); From 55b33d8a33ce942da3ea5150a84a1862e6019bd1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 14:29:20 +1100 Subject: [PATCH 034/133] fixed gpl message header --- dialogs/aboutdialog.cpp | 24 ++-- dialogs/aboutdialog.h | 24 ++-- dialogs/actionsearch.cpp | 24 ++-- dialogs/actionsearch.h | 56 ++++----- dialogs/advancedvideodialog.cpp | 24 ++-- dialogs/advancedvideodialog.h | 24 ++-- dialogs/clippropertiesdialog.cpp | 20 ++++ dialogs/clippropertiesdialog.h | 20 ++++ dialogs/debugdialog.cpp | 24 ++-- dialogs/debugdialog.h | 32 +++--- dialogs/demonotice.cpp | 76 ++++++------- dialogs/demonotice.h | 28 ++--- dialogs/exportdialog.cpp | 24 ++-- dialogs/exportdialog.h | 24 ++-- dialogs/loaddialog.cpp | 24 ++-- dialogs/loaddialog.h | 24 ++-- dialogs/mediapropertiesdialog.cpp | 24 ++-- dialogs/mediapropertiesdialog.h | 24 ++-- dialogs/newsequencedialog.cpp | 24 ++-- dialogs/newsequencedialog.h | 24 ++-- dialogs/preferencesdialog.cpp | 24 ++-- dialogs/preferencesdialog.h | 24 ++-- dialogs/proxydialog.cpp | 24 ++-- dialogs/proxydialog.h | 24 ++-- dialogs/replaceclipmediadialog.cpp | 24 ++-- dialogs/replaceclipmediadialog.h | 32 +++--- dialogs/speeddialog.cpp | 24 ++-- dialogs/speeddialog.h | 24 ++-- dialogs/texteditdialog.cpp | 24 ++-- dialogs/texteditdialog.h | 24 ++-- effects/effect.cpp | 24 ++-- effects/effect.h | 24 ++-- effects/effectfield.cpp | 25 ++--- effects/effectfield.h | 24 ++-- effects/effectfields.h | 20 ++++ effects/effectgizmo.cpp | 24 ++-- effects/effectgizmo.h | 24 ++-- effects/effectloaders.cpp | 24 ++-- effects/effectloaders.h | 24 ++-- effects/effectrow.cpp | 24 ++-- effects/effectrow.h | 24 ++-- effects/fields/boolfield.cpp | 20 ++++ effects/fields/boolfield.h | 20 ++++ effects/fields/buttonfield.cpp | 20 ++++ effects/fields/buttonfield.h | 20 ++++ effects/fields/colorfield.cpp | 20 ++++ effects/fields/colorfield.h | 20 ++++ effects/fields/combofield.cpp | 20 ++++ effects/fields/combofield.h | 20 ++++ effects/fields/doublefield.cpp | 20 ++++ effects/fields/doublefield.h | 20 ++++ effects/fields/filefield.cpp | 20 ++++ effects/fields/filefield.h | 20 ++++ effects/fields/fontfield.cpp | 20 ++++ effects/fields/fontfield.h | 20 ++++ effects/fields/labelfield.cpp | 20 ++++ effects/fields/labelfield.h | 20 ++++ effects/fields/stringfield.cpp | 20 ++++ effects/fields/stringfield.h | 20 ++++ effects/internal/audionoiseeffect.cpp | 37 +++--- effects/internal/audionoiseeffect.h | 24 ++-- effects/internal/cornerpineffect.cpp | 24 ++-- effects/internal/cornerpineffect.h | 24 ++-- effects/internal/crossdissolvetransition.cpp | 24 ++-- effects/internal/crossdissolvetransition.h | 24 ++-- effects/internal/cubetransition.h | 24 ++-- effects/internal/dropshadoweffect.cpp | 20 ++++ effects/internal/dropshadoweffect.h | 22 +++- .../internal/exponentialfadetransition.cpp | 68 +++++------ effects/internal/exponentialfadetransition.h | 24 ++-- effects/internal/fillleftrighteffect.cpp | 24 ++-- effects/internal/fillleftrighteffect.h | 24 ++-- effects/internal/frei0reffect.cpp | 24 ++-- effects/internal/frei0reffect.h | 24 ++-- effects/internal/linearfadetransition.cpp | 48 ++++---- effects/internal/linearfadetransition.h | 24 ++-- .../internal/logarithmicfadetransition.cpp | 48 ++++---- effects/internal/logarithmicfadetransition.h | 24 ++-- effects/internal/paneffect.cpp | 24 ++-- effects/internal/paneffect.h | 24 ++-- effects/internal/richtexteffect.cpp | 20 ++++ effects/internal/richtexteffect.h | 20 ++++ effects/internal/shakeeffect.cpp | 24 ++-- effects/internal/shakeeffect.h | 24 ++-- effects/internal/solideffect.cpp | 24 ++-- effects/internal/solideffect.h | 24 ++-- effects/internal/texteffect.cpp | 25 ++--- effects/internal/texteffect.h | 24 ++-- effects/internal/timecodeeffect.cpp | 25 ++--- effects/internal/timecodeeffect.h | 24 ++-- effects/internal/toneeffect.cpp | 24 ++-- effects/internal/toneeffect.h | 24 ++-- effects/internal/transformeffect.cpp | 24 ++-- effects/internal/transformeffect.h | 24 ++-- effects/internal/voideffect.cpp | 24 ++-- effects/internal/voideffect.h | 24 ++-- effects/internal/volumeeffect.cpp | 24 ++-- effects/internal/volumeeffect.h | 24 ++-- effects/internal/vsthost.cpp | 24 ++-- effects/internal/vsthost.h | 24 ++-- effects/keyframe.cpp | 24 ++-- effects/keyframe.h | 24 ++-- effects/transition.cpp | 24 ++-- effects/transition.h | 24 ++-- global/config.cpp | 24 ++-- global/config.h | 24 ++-- global/crossplatformlib.cpp | 36 +++--- global/crossplatformlib.h | 40 +++---- global/debug.cpp | 24 ++-- global/debug.h | 24 ++-- global/global.cpp | 24 ++-- global/global.h | 24 ++-- global/math.cpp | 68 +++++------ global/math.h | 24 ++-- global/path.cpp | 24 ++-- global/path.h | 24 ++-- main.cpp | 24 ++-- olive.pro | 2 - panels/effectcontrols.cpp | 24 ++-- panels/effectcontrols.h | 24 ++-- panels/grapheditor.cpp | 24 ++-- panels/grapheditor.h | 24 ++-- panels/panels.cpp | 24 ++-- panels/panels.h | 24 ++-- panels/project.cpp | 24 ++-- panels/project.h | 24 ++-- panels/timeline.cpp | 24 ++-- panels/timeline.h | 24 ++-- panels/viewer.cpp | 24 ++-- panels/viewer.h | 24 ++-- project/clipboard.cpp | 24 ++-- project/clipboard.h | 24 ++-- project/footage.cpp | 24 ++-- project/footage.h | 24 ++-- project/loadthread.cpp | 24 ++-- project/loadthread.h | 24 ++-- project/media.cpp | 24 ++-- project/media.h | 24 ++-- project/previewgenerator.cpp | 24 ++-- project/previewgenerator.h | 24 ++-- project/projectelements.h | 24 ++-- project/projectfilter.cpp | 24 ++-- project/projectfilter.h | 34 +++--- project/projectmodel.cpp | 24 ++-- project/projectmodel.h | 24 ++-- project/proxygenerator.cpp | 24 ++-- project/proxygenerator.h | 24 ++-- project/sourcescommon.cpp | 24 ++-- project/sourcescommon.h | 24 ++-- rendering/audio.cpp | 24 ++-- rendering/audio.h | 24 ++-- rendering/cacher.cpp | 24 ++-- rendering/cacher.h | 24 ++-- rendering/clipqueue.cpp | 24 ++-- rendering/clipqueue.h | 24 ++-- rendering/exportthread.cpp | 24 ++-- rendering/exportthread.h | 24 ++-- rendering/framebufferobject.cpp | 20 ++++ rendering/framebufferobject.h | 20 ++++ rendering/qopenglshaderprogramptr.cpp | 20 ++++ rendering/qopenglshaderprogramptr.h | 20 ++++ rendering/renderfunctions.cpp | 24 ++-- rendering/renderfunctions.h | 24 ++-- rendering/renderthread.cpp | 28 ++--- rendering/renderthread.h | 24 ++-- timeline/clip.cpp | 24 ++-- timeline/clip.h | 24 ++-- timeline/marker.cpp | 24 ++-- timeline/marker.h | 24 ++-- timeline/selection.h | 38 +++---- timeline/sequence.cpp | 24 ++-- timeline/sequence.h | 24 ++-- ui/audiomonitor.cpp | 24 ++-- ui/audiomonitor.h | 24 ++-- ui/blur.cpp | 20 ++++ ui/blur.h | 20 ++++ ui/checkboxex.cpp | 24 ++-- ui/checkboxex.h | 30 ++--- ui/clickablelabel.cpp | 30 ++--- ui/clickablelabel.h | 34 +++--- ui/collapsiblewidget.cpp | 24 ++-- ui/collapsiblewidget.h | 24 ++-- ui/colorbutton.cpp | 24 ++-- ui/colorbutton.h | 24 ++-- ui/comboboxex.cpp | 24 ++-- ui/comboboxex.h | 24 ++-- ui/cursors.cpp | 24 ++-- ui/cursors.h | 24 ++-- ui/effectui.cpp | 20 ++++ ui/effectui.h | 20 ++++ ui/embeddedfilechooser.cpp | 24 ++-- ui/embeddedfilechooser.h | 24 ++-- ui/focusfilter.cpp | 24 ++-- ui/focusfilter.h | 24 ++-- ui/fontcombobox.cpp | 35 ------ ui/fontcombobox.h | 36 ------ ui/graphview.cpp | 24 ++-- ui/graphview.h | 24 ++-- ui/icons.cpp | 20 ++++ ui/icons.h | 20 ++++ ui/keyframedrawing.cpp | 24 ++-- ui/keyframedrawing.h | 24 ++-- ui/keyframenavigator.cpp | 24 ++-- ui/keyframenavigator.h | 24 ++-- ui/keyframeview.cpp | 24 ++-- ui/keyframeview.h | 24 ++-- ui/labelslider.cpp | 24 ++-- ui/mainwindow.cpp | 24 ++-- ui/mainwindow.h | 24 ++-- ui/mediaiconservice.cpp | 24 ++-- ui/mediaiconservice.h | 24 ++-- ui/menu.cpp | 20 ++++ ui/menu.h | 20 ++++ ui/menuhelper.cpp | 24 ++-- ui/menuhelper.h | 24 ++-- ui/panel.cpp | 24 ++-- ui/panel.h | 24 ++-- ui/rectangleselect.cpp | 30 ++--- ui/rectangleselect.h | 24 ++-- ui/resizablescrollbar.cpp | 24 ++-- ui/resizablescrollbar.h | 24 ++-- ui/scrollarea.cpp | 24 ++-- ui/scrollarea.h | 24 ++-- ui/sourceiconview.cpp | 24 ++-- ui/sourceiconview.h | 24 ++-- ui/sourcetable.cpp | 24 ++-- ui/sourcetable.h | 24 ++-- ui/styling.cpp | 20 ++++ ui/styling.h | 20 ++++ ui/texteditex.cpp | 24 ++-- ui/texteditex.h | 24 ++-- ui/timelineheader.cpp | 24 ++-- ui/timelineheader.h | 106 +++++++++--------- ui/timelinetools.h | 24 ++-- ui/timelinewidget.cpp | 24 ++-- ui/timelinewidget.h | 24 ++-- ui/updatenotification.cpp | 20 ++++ ui/updatenotification.h | 20 ++++ ui/viewercontainer.cpp | 24 ++-- ui/viewercontainer.h | 66 +++++------ ui/viewerwidget.cpp | 24 ++-- ui/viewerwidget.h | 24 ++-- ui/viewerwindow.cpp | 24 ++-- ui/viewerwindow.h | 24 ++-- undo/comboaction.cpp | 20 ++++ undo/comboaction.h | 20 ++++ undo/undo.cpp | 24 ++-- undo/undo.h | 24 ++-- undo/undostack.cpp | 20 ++++ undo/undostack.h | 20 ++++ 250 files changed, 3557 insertions(+), 2730 deletions(-) delete mode 100644 ui/fontcombobox.cpp delete mode 100644 ui/fontcombobox.h diff --git a/dialogs/aboutdialog.cpp b/dialogs/aboutdialog.cpp index 711c11e73..61d0f6fe8 100644 --- a/dialogs/aboutdialog.cpp +++ b/dialogs/aboutdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/aboutdialog.h b/dialogs/aboutdialog.h index 4f4e49134..f083ce15f 100644 --- a/dialogs/aboutdialog.h +++ b/dialogs/aboutdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/actionsearch.cpp b/dialogs/actionsearch.cpp index c112061dd..e26b5f80b 100644 --- a/dialogs/actionsearch.cpp +++ b/dialogs/actionsearch.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/actionsearch.h b/dialogs/actionsearch.h index 5d9632899..bb151744f 100644 --- a/dialogs/actionsearch.h +++ b/dialogs/actionsearch.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -27,38 +27,38 @@ #include class ActionSearchList : public QListWidget { - Q_OBJECT + Q_OBJECT public: - ActionSearchList(QWidget* parent); + ActionSearchList(QWidget* parent); protected: - void mouseDoubleClickEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); signals: - void dbl_click(); + void dbl_click(); }; class ActionSearch : public QDialog { - Q_OBJECT + Q_OBJECT public: - ActionSearch(QWidget* parent = nullptr); + ActionSearch(QWidget* parent = nullptr); private slots: - void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr); - void perform_action(); - void move_selection_up(); - void move_selection_down(); + void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr); + void perform_action(); + void move_selection_up(); + void move_selection_down(); private: - ActionSearchList* list_widget; + ActionSearchList* list_widget; }; class ActionSearchEntry : public QLineEdit { - Q_OBJECT + Q_OBJECT public: - ActionSearchEntry(QWidget* parent); + ActionSearchEntry(QWidget* parent); protected: - void keyPressEvent(QKeyEvent * event); + void keyPressEvent(QKeyEvent * event); signals: - void moveSelectionUp(); - void moveSelectionDown(); + void moveSelectionUp(); + void moveSelectionDown(); }; #endif // ACTIONSEARCH_H diff --git a/dialogs/advancedvideodialog.cpp b/dialogs/advancedvideodialog.cpp index 15309695b..757d3da8f 100644 --- a/dialogs/advancedvideodialog.cpp +++ b/dialogs/advancedvideodialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/advancedvideodialog.h b/dialogs/advancedvideodialog.h index e393c062d..249134b51 100644 --- a/dialogs/advancedvideodialog.h +++ b/dialogs/advancedvideodialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/clippropertiesdialog.cpp b/dialogs/clippropertiesdialog.cpp index a767e5dc6..85e006efd 100644 --- a/dialogs/clippropertiesdialog.cpp +++ b/dialogs/clippropertiesdialog.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "clippropertiesdialog.h" #include diff --git a/dialogs/clippropertiesdialog.h b/dialogs/clippropertiesdialog.h index 8fd830978..64fb027d9 100644 --- a/dialogs/clippropertiesdialog.h +++ b/dialogs/clippropertiesdialog.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef CLIPPROPERTIESDIALOG_H #define CLIPPROPERTIESDIALOG_H diff --git a/dialogs/debugdialog.cpp b/dialogs/debugdialog.cpp index 6205d661a..817ee452d 100644 --- a/dialogs/debugdialog.cpp +++ b/dialogs/debugdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/debugdialog.h b/dialogs/debugdialog.h index 3f9d84d75..7259eef37 100644 --- a/dialogs/debugdialog.h +++ b/dialogs/debugdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,17 +25,17 @@ #include class DebugDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - DebugDialog(QWidget* parent = 0); + DebugDialog(QWidget* parent = 0); void Retranslate(); public slots: - void update_log(); + void update_log(); protected: virtual void changeEvent(QEvent* e) override; virtual void showEvent(QShowEvent* event) override; private: - QTextEdit* textEdit; + QTextEdit* textEdit; }; namespace olive { diff --git a/dialogs/demonotice.cpp b/dialogs/demonotice.cpp index 3f2cc9169..017ae5b25 100644 --- a/dialogs/demonotice.cpp +++ b/dialogs/demonotice.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,38 +25,38 @@ #include DemoNotice::DemoNotice(QWidget *parent) : - QDialog(parent) + QDialog(parent) { setWindowTitle(tr("Welcome to Olive!")); - QVBoxLayout* vlayout = new QVBoxLayout(this); + QVBoxLayout* vlayout = new QVBoxLayout(this); - QHBoxLayout* layout = new QHBoxLayout(); - layout->setMargin(10); - layout->setSpacing(20); + QHBoxLayout* layout = new QHBoxLayout(); + layout->setMargin(10); + layout->setSpacing(20); - QLabel* icon = new QLabel("" - "

" - "", this); - layout->addWidget(icon); + QLabel* icon = new QLabel("" + "

" + "", this); + layout->addWidget(icon); - QLabel* text = new QLabel("

" - "" - + tr("Welcome to Olive!") - + "

" - + tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.") - + "

" - + tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("www.olivevideoeditor.org") - + "

" - + tr("Thank you for trying Olive and we hope you enjoy it!") - + "

", this); - text->setWordWrap(true); - layout->addWidget(text); + QLabel* text = new QLabel("

" + "" + + tr("Welcome to Olive!") + + "

" + + tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.") + + "

" + + tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("www.olivevideoeditor.org") + + "

" + + tr("Thank you for trying Olive and we hope you enjoy it!") + + "

", this); + text->setWordWrap(true); + layout->addWidget(text); - vlayout->addLayout(layout); + vlayout->addLayout(layout); - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); - buttons->setCenterButtons(true); - connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); - vlayout->addWidget(buttons); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); + buttons->setCenterButtons(true); + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + vlayout->addWidget(buttons); } diff --git a/dialogs/demonotice.h b/dialogs/demonotice.h index b50a690fe..b42a48b60 100644 --- a/dialogs/demonotice.h +++ b/dialogs/demonotice.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,9 +25,9 @@ class DemoNotice : public QDialog { - Q_OBJECT + Q_OBJECT public: - explicit DemoNotice(QWidget *parent = 0); + explicit DemoNotice(QWidget *parent = 0); }; #endif // DEMONOTICE_H diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index be924d870..231ecc5ad 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/exportdialog.h b/dialogs/exportdialog.h index 909912c55..d4fc500a2 100644 --- a/dialogs/exportdialog.h +++ b/dialogs/exportdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/loaddialog.cpp b/dialogs/loaddialog.cpp index 92327eabb..dc4127cda 100644 --- a/dialogs/loaddialog.cpp +++ b/dialogs/loaddialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/loaddialog.h b/dialogs/loaddialog.h index 63586d226..96bb9ef1a 100644 --- a/dialogs/loaddialog.h +++ b/dialogs/loaddialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index 155539d57..150336c8b 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index e8ff9492a..c153e826a 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index 47ead1336..dfe53177b 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/newsequencedialog.h b/dialogs/newsequencedialog.h index b71bdc099..7b456db80 100644 --- a/dialogs/newsequencedialog.h +++ b/dialogs/newsequencedialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index e306463df..ad669341f 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 804b8b051..437ec28ac 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/proxydialog.cpp b/dialogs/proxydialog.cpp index bcc0a0745..2c918f78e 100644 --- a/dialogs/proxydialog.cpp +++ b/dialogs/proxydialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/proxydialog.h b/dialogs/proxydialog.h index 3a7fd579a..7e2cd5f33 100644 --- a/dialogs/proxydialog.h +++ b/dialogs/proxydialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp index a8aec4cfd..9afd37a1f 100644 --- a/dialogs/replaceclipmediadialog.cpp +++ b/dialogs/replaceclipmediadialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/replaceclipmediadialog.h b/dialogs/replaceclipmediadialog.h index 7b1867a71..48925423c 100644 --- a/dialogs/replaceclipmediadialog.h +++ b/dialogs/replaceclipmediadialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -29,15 +29,15 @@ #include "project/projectelements.h" class ReplaceClipMediaDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: ReplaceClipMediaDialog(QWidget* parent, Media* old_media); private slots: - void replace(); + void replace(); private: Media* media; - QTreeView* tree; - QCheckBox* use_same_media_in_points; + QTreeView* tree; + QCheckBox* use_same_media_in_points; }; #endif // REPLACECLIPMEDIADIALOG_H diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index c0a08d652..35d9de06c 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/speeddialog.h b/dialogs/speeddialog.h index ac68c51dc..3d7246eb6 100644 --- a/dialogs/speeddialog.h +++ b/dialogs/speeddialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/texteditdialog.cpp b/dialogs/texteditdialog.cpp index 185b7c357..4cf77828b 100644 --- a/dialogs/texteditdialog.cpp +++ b/dialogs/texteditdialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/texteditdialog.h b/dialogs/texteditdialog.h index c2ced7d2e..9ab3eac79 100644 --- a/dialogs/texteditdialog.h +++ b/dialogs/texteditdialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effect.cpp b/effects/effect.cpp index fc3748328..4a8f29f7f 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effect.h b/effects/effect.h index ffca12806..6123bb348 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 77a9466ba..0e08e6048 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -28,7 +28,6 @@ #include "ui/texteditex.h" #include "ui/checkboxex.h" #include "ui/comboboxex.h" -#include "ui/fontcombobox.h" #include "ui/embeddedfilechooser.h" #include "rendering/renderfunctions.h" diff --git a/effects/effectfield.h b/effects/effectfield.h index 9d5dae487..30b74e53e 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectfields.h b/effects/effectfields.h index 1a6e1777a..dacd3b7d4 100644 --- a/effects/effectfields.h +++ b/effects/effectfields.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef EFFECTFIELDS_H #define EFFECTFIELDS_H diff --git a/effects/effectgizmo.cpp b/effects/effectgizmo.cpp index 1c71afdc5..cac508a4a 100644 --- a/effects/effectgizmo.cpp +++ b/effects/effectgizmo.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectgizmo.h b/effects/effectgizmo.h index 115676609..371b5aea9 100644 --- a/effects/effectgizmo.h +++ b/effects/effectgizmo.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index 6da98726e..1d635d664 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectloaders.h b/effects/effectloaders.h index 66c4b0a6b..ea7294358 100644 --- a/effects/effectloaders.h +++ b/effects/effectloaders.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index 06b998d66..33a9cab60 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/effectrow.h b/effects/effectrow.h index 723a368ab..5248a997c 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index 7fc7c2a4d..f4db7d85d 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "boolfield.h" #include diff --git a/effects/fields/boolfield.h b/effects/fields/boolfield.h index 5af612530..5228a65c0 100644 --- a/effects/fields/boolfield.h +++ b/effects/fields/boolfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef BOOLFIELD_H #define BOOLFIELD_H diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index 23cc860af..77025b17a 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "buttonfield.h" #include diff --git a/effects/fields/buttonfield.h b/effects/fields/buttonfield.h index 940e458e5..47f2e5890 100644 --- a/effects/fields/buttonfield.h +++ b/effects/fields/buttonfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef BUTTONFIELD_H #define BUTTONFIELD_H diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index a70135af4..66433db10 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "colorfield.h" #include diff --git a/effects/fields/colorfield.h b/effects/fields/colorfield.h index ab32e4b52..3625c97d9 100644 --- a/effects/fields/colorfield.h +++ b/effects/fields/colorfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef COLORFIELD_H #define COLORFIELD_H diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index ad43bb4f5..f5b803679 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "combofield.h" #include diff --git a/effects/fields/combofield.h b/effects/fields/combofield.h index c2b1cb295..95a70e5e3 100644 --- a/effects/fields/combofield.h +++ b/effects/fields/combofield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef COMBOFIELD_H #define COMBOFIELD_H diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index ecbadc827..9e0527a7d 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "doublefield.h" #include "effects/effectrow.h" diff --git a/effects/fields/doublefield.h b/effects/fields/doublefield.h index 9b2882f4e..454734c03 100644 --- a/effects/fields/doublefield.h +++ b/effects/fields/doublefield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef DOUBLEFIELD_H #define DOUBLEFIELD_H diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index bf5125ae8..4bb04dd26 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "filefield.h" #include "ui/embeddedfilechooser.h" diff --git a/effects/fields/filefield.h b/effects/fields/filefield.h index 88c3f0237..1b6870c35 100644 --- a/effects/fields/filefield.h +++ b/effects/fields/filefield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef FILEFIELD_H #define FILEFIELD_H diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 8ee06877a..9479fb60c 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "fontfield.h" #include diff --git a/effects/fields/fontfield.h b/effects/fields/fontfield.h index 9df3e76ae..32c48c56e 100644 --- a/effects/fields/fontfield.h +++ b/effects/fields/fontfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef FONTFIELD_H #define FONTFIELD_H diff --git a/effects/fields/labelfield.cpp b/effects/fields/labelfield.cpp index b3eccfa6d..231c1ee08 100644 --- a/effects/fields/labelfield.cpp +++ b/effects/fields/labelfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "labelfield.h" #include diff --git a/effects/fields/labelfield.h b/effects/fields/labelfield.h index 67b4728f4..c8322ad92 100644 --- a/effects/fields/labelfield.h +++ b/effects/fields/labelfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef LABELFIELD_H #define LABELFIELD_H diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 55b59752d..bccdaf8df 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "stringfield.h" #include diff --git a/effects/fields/stringfield.h b/effects/fields/stringfield.h index 01eb26e9d..ba25e26c9 100644 --- a/effects/fields/stringfield.h +++ b/effects/fields/stringfield.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef STRINGFIELD_H #define STRINGFIELD_H diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index 3f51bcf21..0e59d04b0 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -1,20 +1,23 @@ -/* - * Olive. Olive is a free non-linear video editor for Windows, macOS, and Linux. - * Copyright (C) 2018 {{ organization }} - * - * 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 . - */ +/*** + + 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 "audionoiseeffect.h" #include diff --git a/effects/internal/audionoiseeffect.h b/effects/internal/audionoiseeffect.h index dab29be78..74c8ad9cd 100644 --- a/effects/internal/audionoiseeffect.h +++ b/effects/internal/audionoiseeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 632d20ae9..814a45891 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/cornerpineffect.h b/effects/internal/cornerpineffect.h index ffee7311b..3a2acc525 100644 --- a/effects/internal/cornerpineffect.h +++ b/effects/internal/cornerpineffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/crossdissolvetransition.cpp b/effects/internal/crossdissolvetransition.cpp index a90fbbeff..9fa94e2e4 100644 --- a/effects/internal/crossdissolvetransition.cpp +++ b/effects/internal/crossdissolvetransition.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/crossdissolvetransition.h b/effects/internal/crossdissolvetransition.h index 42c91492e..4d1e8f2c4 100644 --- a/effects/internal/crossdissolvetransition.h +++ b/effects/internal/crossdissolvetransition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/cubetransition.h b/effects/internal/cubetransition.h index fb3bdc512..f79280dec 100644 --- a/effects/internal/cubetransition.h +++ b/effects/internal/cubetransition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/dropshadoweffect.cpp b/effects/internal/dropshadoweffect.cpp index c7c0d61b6..934a76b04 100644 --- a/effects/internal/dropshadoweffect.cpp +++ b/effects/internal/dropshadoweffect.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "dropshadoweffect.h" DropShadowEffect::DropShadowEffect() { diff --git a/effects/internal/dropshadoweffect.h b/effects/internal/dropshadoweffect.h index 704053c59..5492f09a3 100644 --- a/effects/internal/dropshadoweffect.h +++ b/effects/internal/dropshadoweffect.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef DROPSHADOWEFFECT_H #define DROPSHADOWEFFECT_H @@ -8,4 +28,4 @@ public: DropShadowEffect(); }; -#endif // DROPSHADOWEFFECT_H \ No newline at end of file +#endif // DROPSHADOWEFFECT_H diff --git a/effects/internal/exponentialfadetransition.cpp b/effects/internal/exponentialfadetransition.cpp index ca7c367ca..470964f86 100644 --- a/effects/internal/exponentialfadetransition.cpp +++ b/effects/internal/exponentialfadetransition.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,31 +25,31 @@ ExponentialFadeTransition::ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} void ExponentialFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; + double interval = (timecode_end-timecode_start)/nb_bytes; - for (int i=0;i> 8); - samples[i] = (quint8) samp; - } + samples[i+1] = (quint8) (samp >> 8); + samples[i] = (quint8) samp; + } } diff --git a/effects/internal/exponentialfadetransition.h b/effects/internal/exponentialfadetransition.h index 8aeaccdc0..d2fadd73a 100644 --- a/effects/internal/exponentialfadetransition.h +++ b/effects/internal/exponentialfadetransition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/fillleftrighteffect.cpp b/effects/internal/fillleftrighteffect.cpp index ff0ebb68d..40e934c57 100644 --- a/effects/internal/fillleftrighteffect.cpp +++ b/effects/internal/fillleftrighteffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/fillleftrighteffect.h b/effects/internal/fillleftrighteffect.h index 9300bcb72..1eba500d3 100644 --- a/effects/internal/fillleftrighteffect.h +++ b/effects/internal/fillleftrighteffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/frei0reffect.cpp b/effects/internal/frei0reffect.cpp index 620c147ad..c070e801f 100644 --- a/effects/internal/frei0reffect.cpp +++ b/effects/internal/frei0reffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h index 6f7356b28..4551d63b7 100644 --- a/effects/internal/frei0reffect.h +++ b/effects/internal/frei0reffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/linearfadetransition.cpp b/effects/internal/linearfadetransition.cpp index 0c3917928..0693874b6 100644 --- a/effects/internal/linearfadetransition.cpp +++ b/effects/internal/linearfadetransition.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -23,21 +23,21 @@ LinearFadeTransition::LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} void LinearFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; + double interval = (timecode_end-timecode_start)/nb_bytes; - for (int i=0;i> 8); - samples[i] = (quint8) samp; - } + samples[i+1] = (quint8) (samp >> 8); + samples[i] = (quint8) samp; + } } diff --git a/effects/internal/linearfadetransition.h b/effects/internal/linearfadetransition.h index bfb94c414..ed3fabd2f 100644 --- a/effects/internal/linearfadetransition.h +++ b/effects/internal/linearfadetransition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/logarithmicfadetransition.cpp b/effects/internal/logarithmicfadetransition.cpp index e4b3bd664..5012330fc 100644 --- a/effects/internal/logarithmicfadetransition.cpp +++ b/effects/internal/logarithmicfadetransition.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,21 +25,21 @@ LogarithmicFadeTransition::LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} void LogarithmicFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; + double interval = (timecode_end-timecode_start)/nb_bytes; - for (int i=0;i> 8); - samples[i] = (quint8) samp; - } + samples[i+1] = (quint8) (samp >> 8); + samples[i] = (quint8) samp; + } } diff --git a/effects/internal/logarithmicfadetransition.h b/effects/internal/logarithmicfadetransition.h index e4b178881..6983bcaf2 100644 --- a/effects/internal/logarithmicfadetransition.h +++ b/effects/internal/logarithmicfadetransition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 987d6528f..0d85e8c9c 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/paneffect.h b/effects/internal/paneffect.h index 5e767e04e..aac24dd6d 100644 --- a/effects/internal/paneffect.h +++ b/effects/internal/paneffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/richtexteffect.cpp b/effects/internal/richtexteffect.cpp index 34ab317bb..cc8b822d3 100644 --- a/effects/internal/richtexteffect.cpp +++ b/effects/internal/richtexteffect.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "richtexteffect.h" #include diff --git a/effects/internal/richtexteffect.h b/effects/internal/richtexteffect.h index 2a8a7978b..bdf484270 100644 --- a/effects/internal/richtexteffect.h +++ b/effects/internal/richtexteffect.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef RICHTEXTEFFECT_H #define RICHTEXTEFFECT_H diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 3b4d16ba5..32b103969 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/shakeeffect.h b/effects/internal/shakeeffect.h index b199f493b..fb0c56586 100644 --- a/effects/internal/shakeeffect.h +++ b/effects/internal/shakeeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index db6f7434c..c8359fb7b 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/solideffect.h b/effects/internal/solideffect.h index 26843fe71..d7220583a 100644 --- a/effects/internal/solideffect.h +++ b/effects/internal/solideffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 5a805461d..1818981ee 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -39,7 +39,6 @@ #include "timeline/sequence.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" -#include "ui/fontcombobox.h" #include "ui/blur.h" #include "global/config.h" diff --git a/effects/internal/texteffect.h b/effects/internal/texteffect.h index 05b9286a8..2a0a136a6 100644 --- a/effects/internal/texteffect.h +++ b/effects/internal/texteffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 43b195bc0..1bbb97cc3 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -40,7 +40,6 @@ #include "panels/viewer.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" -#include "ui/fontcombobox.h" #include "global/config.h" TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) : diff --git a/effects/internal/timecodeeffect.h b/effects/internal/timecodeeffect.h index d51565940..4b5756fa0 100644 --- a/effects/internal/timecodeeffect.h +++ b/effects/internal/timecodeeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index a1758f86e..b88cfbedc 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/toneeffect.h b/effects/internal/toneeffect.h index a14f49a98..12e118201 100644 --- a/effects/internal/toneeffect.h +++ b/effects/internal/toneeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 8852240e3..75d9ccc70 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index 43aad2b26..ba05c6614 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/voideffect.cpp b/effects/internal/voideffect.cpp index bf3192cd6..435d32e94 100644 --- a/effects/internal/voideffect.cpp +++ b/effects/internal/voideffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/voideffect.h b/effects/internal/voideffect.h index a0c49c7ba..cfd93141a 100644 --- a/effects/internal/voideffect.h +++ b/effects/internal/voideffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index e01d5241c..bccb4c496 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/volumeeffect.h b/effects/internal/volumeeffect.h index 80e0adc70..2922d041e 100644 --- a/effects/internal/volumeeffect.h +++ b/effects/internal/volumeeffect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp index d293bced1..9f124dff2 100644 --- a/effects/internal/vsthost.cpp +++ b/effects/internal/vsthost.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/internal/vsthost.h b/effects/internal/vsthost.h index 5dc06a0cf..2354d1977 100644 --- a/effects/internal/vsthost.h +++ b/effects/internal/vsthost.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/keyframe.cpp b/effects/keyframe.cpp index d73518cc3..83f580207 100644 --- a/effects/keyframe.cpp +++ b/effects/keyframe.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/keyframe.h b/effects/keyframe.h index 241d28ef8..4a076bc34 100644 --- a/effects/keyframe.h +++ b/effects/keyframe.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/transition.cpp b/effects/transition.cpp index d3a9d40cf..e7f3585e3 100644 --- a/effects/transition.cpp +++ b/effects/transition.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/effects/transition.h b/effects/transition.h index 8895d9dd7..01a47cd8b 100644 --- a/effects/transition.h +++ b/effects/transition.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/config.cpp b/global/config.cpp index ba6691cd8..370251f23 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/config.h b/global/config.h index c4ab32b79..7a1f82fdf 100644 --- a/global/config.h +++ b/global/config.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/crossplatformlib.cpp b/global/crossplatformlib.cpp index f5b7d486b..b6f7a2655 100644 --- a/global/crossplatformlib.cpp +++ b/global/crossplatformlib.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -24,19 +24,19 @@ ModulePtr LibLoad(const QString &filename) { #ifdef _WIN32 - LPCWSTR dll_fn_w = reinterpret_cast(filename.utf16()); - return LoadLibrary(dll_fn_w); + LPCWSTR dll_fn_w = reinterpret_cast(filename.utf16()); + return LoadLibrary(dll_fn_w); #elif defined(__linux__) || defined(__APPLE__) || defined(__HAIKU__) - return dlopen(filename.toUtf8(), RTLD_LAZY); + return dlopen(filename.toUtf8(), RTLD_LAZY); #else - qWarning() << "Olive doesn't know how to open dynamic libraries on this platform, external libraries will not be functional"; - return nullptr; + qWarning() << "Olive doesn't know how to open dynamic libraries on this platform, external libraries will not be functional"; + return nullptr; #endif } QStringList LibFilter() { #ifdef _WIN32 - return QStringList("*.dll"); + return QStringList("*.dll"); #elif defined(__linux__) || defined(__APPLE__) || defined(__HAIKU__) return {"*.so", "*.dylib"}; #endif diff --git a/global/crossplatformlib.h b/global/crossplatformlib.h index bd1935a7d..068f49604 100644 --- a/global/crossplatformlib.h +++ b/global/crossplatformlib.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -24,15 +24,15 @@ #include #ifdef _WIN32 - #include - #define LibAddress GetProcAddress - #define LibClose FreeModule - #define ModulePtr HMODULE + #include + #define LibAddress GetProcAddress + #define LibClose FreeModule + #define ModulePtr HMODULE #elif defined(__linux__) || defined(__APPLE__) || defined(__HAIKU__) - #include - #define LibAddress dlsym - #define LibClose dlclose - #define ModulePtr void* + #include + #define LibAddress dlsym + #define LibClose dlclose + #define ModulePtr void* #endif ModulePtr LibLoad(const QString& filename); diff --git a/global/debug.cpp b/global/debug.cpp index b64e38ecd..6755c7901 100644 --- a/global/debug.cpp +++ b/global/debug.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/debug.h b/global/debug.h index 791a12cc6..b6b4be132 100644 --- a/global/debug.h +++ b/global/debug.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/global.cpp b/global/global.cpp index 16a59c9c6..79f3e0c79 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/global.h b/global/global.h index 6a9d57617..0bc1552b5 100644 --- a/global/global.h +++ b/global/global.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/math.cpp b/global/math.cpp index 31c630d01..805d1de01 100644 --- a/global/math.cpp +++ b/global/math.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -26,51 +26,51 @@ #include "debug.h" int lerp(int a, int b, double t) { - return qRound(((1.0 - t) * a) + (t * b)); + return qRound(((1.0 - t) * a) + (t * b)); } float float_lerp(float a, float b, float t) { - return ((1.0F - t) * a) + (t * b); + return ((1.0F - t) * a) + (t * b); } double double_lerp(double a, double b, double t) { - return ((1.0 - t) * a) + (t * b); + return ((1.0 - t) * a) + (t * b); } double quad_from_t(double a, double b, double c, double t) { - return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c; + return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c; } double quad_t_from_x(double x, double a, double b, double c) { - return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); - // alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); + return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); + // alt: return (a - b - qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c); } double cubic_from_t(double a, double b, double c, double d, double t) { - return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d; + return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d; } double cubic_t_from_x(double x_target, double a, double b, double c, double d) { - double tolerance = 0.0001; + double tolerance = 0.0001; - double lower = 0.0; - double upper = 1.0; + double lower = 0.0; + double upper = 1.0; - double percent = 0.5; - double x = cubic_from_t(a, b, c, d, percent); + double percent = 0.5; + double x = cubic_from_t(a, b, c, d, percent); - while (qAbs(x_target - x) > tolerance) { - if (x_target > x) { - lower = percent; - } else { - upper = percent; - } + while (qAbs(x_target - x) > tolerance) { + if (x_target > x) { + lower = percent; + } else { + upper = percent; + } - percent = (upper + lower) / 2.0; - x = cubic_from_t(a, b, c, d, percent); - } + percent = (upper + lower) / 2.0; + x = cubic_from_t(a, b, c, d, percent); + } - return percent; + return percent; } double amplitude_to_db(double amplitude) { diff --git a/global/math.h b/global/math.h index 9e6a7a45a..37bd85198 100644 --- a/global/math.h +++ b/global/math.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/path.cpp b/global/path.cpp index c69d35d35..8ba47a541 100644 --- a/global/path.cpp +++ b/global/path.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/global/path.h b/global/path.h index 09313ec11..f3ea5a659 100644 --- a/global/path.h +++ b/global/path.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/main.cpp b/main.cpp index 9bb140109..20c52026a 100644 --- a/main.cpp +++ b/main.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/olive.pro b/olive.pro index d71a71ddd..96c32b374 100644 --- a/olive.pro +++ b/olive.pro @@ -84,7 +84,6 @@ SOURCES += \ ui/comboboxex.cpp \ ui/colorbutton.cpp \ dialogs/replaceclipmediadialog.cpp \ - ui/fontcombobox.cpp \ ui/checkboxex.cpp \ ui/keyframeview.cpp \ ui/texteditex.cpp \ @@ -210,7 +209,6 @@ HEADERS += \ ui/comboboxex.h \ ui/colorbutton.h \ dialogs/replaceclipmediadialog.h \ - ui/fontcombobox.h \ ui/checkboxex.h \ ui/keyframeview.h \ ui/texteditex.h \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 599375220..f03930ee1 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index 2188920f8..0b6c6ce4b 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index bf07f4a31..c598cc5d5 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/grapheditor.h b/panels/grapheditor.h index d7fb2fdc7..577a80b11 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/panels.cpp b/panels/panels.cpp index 19b9dbd65..663aeab00 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/panels.h b/panels/panels.h index 161699fde..52a32da15 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/project.cpp b/panels/project.cpp index 12d167d34..49a8f1129 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/project.h b/panels/project.h index 4ee1d779f..753b46908 100644 --- a/panels/project.h +++ b/panels/project.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 044cf42c2..4f59548d1 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/timeline.h b/panels/timeline.h index 028c8977f..27c3aa15e 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 9b1685bb5..fb67a1bde 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/panels/viewer.h b/panels/viewer.h index 8376fee6d..166b7fa38 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/clipboard.cpp b/project/clipboard.cpp index 47a13385b..d8ee9de8d 100644 --- a/project/clipboard.cpp +++ b/project/clipboard.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/clipboard.h b/project/clipboard.h index 4ca8c580d..0de6c8f16 100644 --- a/project/clipboard.h +++ b/project/clipboard.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/footage.cpp b/project/footage.cpp index d2b36f442..693bc6b94 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/footage.h b/project/footage.h index bc93cc4dc..ff709950b 100644 --- a/project/footage.h +++ b/project/footage.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/loadthread.cpp b/project/loadthread.cpp index eeac43e03..6ac436e8d 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/loadthread.h b/project/loadthread.h index 84badc682..5bb8d43f6 100644 --- a/project/loadthread.h +++ b/project/loadthread.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/media.cpp b/project/media.cpp index 4a1762fac..2fbce1165 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/media.h b/project/media.h index 77d96519b..85fa14528 100644 --- a/project/media.h +++ b/project/media.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/previewgenerator.cpp b/project/previewgenerator.cpp index 3597e744b..4ef532b8e 100644 --- a/project/previewgenerator.cpp +++ b/project/previewgenerator.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/previewgenerator.h b/project/previewgenerator.h index 8b273be64..9f7ddea25 100644 --- a/project/previewgenerator.h +++ b/project/previewgenerator.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/projectelements.h b/project/projectelements.h index 6868b19c3..c83feaaa5 100644 --- a/project/projectelements.h +++ b/project/projectelements.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/projectfilter.cpp b/project/projectfilter.cpp index 5bc330364..e7691007a 100644 --- a/project/projectfilter.cpp +++ b/project/projectfilter.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/projectfilter.h b/project/projectfilter.h index b03865cc8..2fbfad9e4 100644 --- a/project/projectfilter.h +++ b/project/projectfilter.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -24,17 +24,17 @@ #include class ProjectFilter : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT public: - ProjectFilter(QObject *parent = nullptr); + ProjectFilter(QObject *parent = nullptr); // are sequences visible - bool get_show_sequences(); + bool get_show_sequences(); public slots: // set whether sequences are visible - void set_show_sequences(bool b); + void set_show_sequences(bool b); // update search filter void update_search_filter(const QString& s); @@ -47,7 +47,7 @@ protected: private: // internal variable for whether to show sequences - bool show_sequences; + bool show_sequences; // search filter variable QString search_filter; diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index 8c4c66f34..ab09e2da2 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/projectmodel.h b/project/projectmodel.h index 17edade63..59c6f5b6d 100644 --- a/project/projectmodel.h +++ b/project/projectmodel.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/proxygenerator.cpp b/project/proxygenerator.cpp index 6a6cd11ce..7a8ee8b6f 100644 --- a/project/proxygenerator.cpp +++ b/project/proxygenerator.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/proxygenerator.h b/project/proxygenerator.h index 964e03ee2..cc3da033e 100644 --- a/project/proxygenerator.h +++ b/project/proxygenerator.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index 2d6bd9b5f..a5591ed78 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/project/sourcescommon.h b/project/sourcescommon.h index 191b43fbb..b013b7032 100644 --- a/project/sourcescommon.h +++ b/project/sourcescommon.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/audio.cpp b/rendering/audio.cpp index 02fdbf150..b54677fdb 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/audio.h b/rendering/audio.h index 2a3488b53..c478839c5 100644 --- a/rendering/audio.h +++ b/rendering/audio.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 5e78c5c40..784ee3b87 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/cacher.h b/rendering/cacher.h index 57fca0e75..2dcb1c91f 100644 --- a/rendering/cacher.h +++ b/rendering/cacher.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/clipqueue.cpp b/rendering/clipqueue.cpp index e1e6e2fe2..ea9278a5b 100644 --- a/rendering/clipqueue.cpp +++ b/rendering/clipqueue.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/clipqueue.h b/rendering/clipqueue.h index 700ed285e..7534fb09f 100644 --- a/rendering/clipqueue.h +++ b/rendering/clipqueue.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp index 31d78f954..f102e146f 100644 --- a/rendering/exportthread.cpp +++ b/rendering/exportthread.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/exportthread.h b/rendering/exportthread.h index ab30f6da8..0f451b2cd 100644 --- a/rendering/exportthread.h +++ b/rendering/exportthread.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index f55eb4777..222fc59aa 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "framebufferobject.h" #include diff --git a/rendering/framebufferobject.h b/rendering/framebufferobject.h index 6f2b5764a..0636dc64a 100644 --- a/rendering/framebufferobject.h +++ b/rendering/framebufferobject.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef FRAMEBUFFEROBJECT_H #define FRAMEBUFFEROBJECT_H diff --git a/rendering/qopenglshaderprogramptr.cpp b/rendering/qopenglshaderprogramptr.cpp index f5aeb84af..049273bce 100644 --- a/rendering/qopenglshaderprogramptr.cpp +++ b/rendering/qopenglshaderprogramptr.cpp @@ -1,2 +1,22 @@ +/*** + + 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 "qopenglshaderprogramptr.h" diff --git a/rendering/qopenglshaderprogramptr.h b/rendering/qopenglshaderprogramptr.h index d0b9152d0..e75bab495 100644 --- a/rendering/qopenglshaderprogramptr.h +++ b/rendering/qopenglshaderprogramptr.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef QOPENGLSHADERPROGRAMPTR_H #define QOPENGLSHADERPROGRAMPTR_H diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index ece03b568..b7cd99347 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 86752e7ad..8b39cd6e2 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 740cab9b6..1e9f86591 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -52,9 +52,9 @@ RenderThread::RenderThread() : ocio_shader(nullptr), #endif running(true), -#ifndef NO_OCIO + #ifndef NO_OCIO ocio_config_date(0), -#endif + #endif front_buffer_switcher(false) { surface.create(); diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 184657f92..74c86cfaf 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 8690cb6c5..57e5c60fc 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/clip.h b/timeline/clip.h index 0d00cf09b..a09eae6c0 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/marker.cpp b/timeline/marker.cpp index afcf495a2..c816cf10c 100644 --- a/timeline/marker.cpp +++ b/timeline/marker.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/marker.h b/timeline/marker.h index abbb27e0a..f9226f00b 100644 --- a/timeline/marker.h +++ b/timeline/marker.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/selection.h b/timeline/selection.h index 79c3a79cb..eaa67372b 100644 --- a/timeline/selection.h +++ b/timeline/selection.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -22,15 +22,15 @@ #define SELECTION_H struct Selection { - long in; - long out; - int track; + long in; + long out; + int track; - long old_in; - long old_out; - int old_track; + long old_in; + long old_out; + int old_track; - bool trim_in; + bool trim_in; }; #endif // SELECTION_H diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index da2e95bf7..3df810087 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/timeline/sequence.h b/timeline/sequence.h index 559564e3b..25ccd188d 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp index 239deff00..f40042ba7 100644 --- a/ui/audiomonitor.cpp +++ b/ui/audiomonitor.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/audiomonitor.h b/ui/audiomonitor.h index 39a40d39a..8ce9da55f 100644 --- a/ui/audiomonitor.h +++ b/ui/audiomonitor.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/blur.cpp b/ui/blur.cpp index ecdf7442a..c13ede7d6 100644 --- a/ui/blur.cpp +++ b/ui/blur.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "blur.h" void olive::ui::blur(QImage& result, const QRect& rect, int radius, bool alphaOnly) { diff --git a/ui/blur.h b/ui/blur.h index 6e56ea032..ca2b930d8 100644 --- a/ui/blur.h +++ b/ui/blur.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef BLUR_H #define BLUR_H diff --git a/ui/checkboxex.cpp b/ui/checkboxex.cpp index 0682e8b52..e76c12985 100644 --- a/ui/checkboxex.cpp +++ b/ui/checkboxex.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/checkboxex.h b/ui/checkboxex.h index eb3db0f01..b5d20fd17 100644 --- a/ui/checkboxex.h +++ b/ui/checkboxex.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -25,11 +25,11 @@ class CheckboxEx : public QCheckBox { - Q_OBJECT + Q_OBJECT public: - CheckboxEx(QWidget* parent = 0); + CheckboxEx(QWidget* parent = 0); private slots: - void checkbox_command(); + void checkbox_command(); }; #endif // CHECKBOXEX_H diff --git a/ui/clickablelabel.cpp b/ui/clickablelabel.cpp index 86f7f7de2..1b8352fa9 100644 --- a/ui/clickablelabel.cpp +++ b/ui/clickablelabel.cpp @@ -1,33 +1,33 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ #include "clickablelabel.h" ClickableLabel::ClickableLabel(QWidget *parent, Qt::WindowFlags f) : - QLabel(parent, f) + QLabel(parent, f) {} ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : - QLabel(text, parent, f) + QLabel(text, parent, f) {} void ClickableLabel::mousePressEvent(QMouseEvent *) { - emit clicked(); + emit clicked(); } diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h index a1dd67786..aba75c6ec 100644 --- a/ui/clickablelabel.h +++ b/ui/clickablelabel.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -29,13 +29,13 @@ * Simple QLabel-derived class that emits a clicked() signal when the widget receives a mouse press event. */ class ClickableLabel : public QLabel { - Q_OBJECT + Q_OBJECT public: - ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); - ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0); - void mousePressEvent(QMouseEvent *ev); + ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); + ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0); + void mousePressEvent(QMouseEvent *ev); signals: - void clicked(); + void clicked(); }; #endif // CLICKABLELABEL_H diff --git a/ui/collapsiblewidget.cpp b/ui/collapsiblewidget.cpp index e81c3dbc8..3fc7177a8 100644 --- a/ui/collapsiblewidget.cpp +++ b/ui/collapsiblewidget.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/collapsiblewidget.h b/ui/collapsiblewidget.h index 8aa67ebb4..d0fadad68 100644 --- a/ui/collapsiblewidget.h +++ b/ui/collapsiblewidget.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/colorbutton.cpp b/ui/colorbutton.cpp index 9423b616c..39eba687c 100644 --- a/ui/colorbutton.cpp +++ b/ui/colorbutton.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/colorbutton.h b/ui/colorbutton.h index e67ca8cd5..2359cbf9f 100644 --- a/ui/colorbutton.h +++ b/ui/colorbutton.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/comboboxex.cpp b/ui/comboboxex.cpp index 3306ffbc1..8cb994129 100644 --- a/ui/comboboxex.cpp +++ b/ui/comboboxex.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/comboboxex.h b/ui/comboboxex.h index e3b7bb6b3..f045f18ef 100644 --- a/ui/comboboxex.h +++ b/ui/comboboxex.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/cursors.cpp b/ui/cursors.cpp index 5116a1b1b..977c52c63 100644 --- a/ui/cursors.cpp +++ b/ui/cursors.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/cursors.h b/ui/cursors.h index 0e72f8fe7..1c6a3a1cc 100644 --- a/ui/cursors.h +++ b/ui/cursors.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/effectui.cpp b/ui/effectui.cpp index feeb0334b..e26a35c4c 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "effectui.h" #include diff --git a/ui/effectui.h b/ui/effectui.h index 53f04da6d..a1871061b 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef EFFECTUI_H #define EFFECTUI_H diff --git a/ui/embeddedfilechooser.cpp b/ui/embeddedfilechooser.cpp index 4a5bfbec2..f19505703 100644 --- a/ui/embeddedfilechooser.cpp +++ b/ui/embeddedfilechooser.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/embeddedfilechooser.h b/ui/embeddedfilechooser.h index 8022022ee..f67596325 100644 --- a/ui/embeddedfilechooser.h +++ b/ui/embeddedfilechooser.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index b50ef0ae3..834e4525c 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/focusfilter.h b/ui/focusfilter.h index 645553860..1fae39ede 100644 --- a/ui/focusfilter.h +++ b/ui/focusfilter.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/fontcombobox.cpp b/ui/fontcombobox.cpp deleted file mode 100644 index c8e10d920..000000000 --- a/ui/fontcombobox.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*** - - 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 "fontcombobox.h" - -#include - -FontCombobox::FontCombobox(QWidget* parent) : QComboBox(parent) { - addItems(QFontDatabase().families()); - - value = currentText(); - - connect(this, SIGNAL(currentTextChanged(QString)), this, SLOT(updateInternals())); -} - -void FontCombobox::updateInternals() { - value = currentText(); -} diff --git a/ui/fontcombobox.h b/ui/fontcombobox.h deleted file mode 100644 index 54a7b0823..000000000 --- a/ui/fontcombobox.h +++ /dev/null @@ -1,36 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef FONTCOMBOBOX_H -#define FONTCOMBOBOX_H - -#include - -class FontCombobox : public QComboBox { - Q_OBJECT -public: - FontCombobox(QWidget* parent = nullptr); -private slots: - void updateInternals(); -private: - QString value; -}; - -#endif // FONTCOMBOBOX_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 3e13075cd..9f1ef26ef 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/graphview.h b/ui/graphview.h index 285048882..da1bf77ae 100644 --- a/ui/graphview.h +++ b/ui/graphview.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/icons.cpp b/ui/icons.cpp index 5cf63f81e..28b88835d 100644 --- a/ui/icons.cpp +++ b/ui/icons.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "icons.h" #include diff --git a/ui/icons.h b/ui/icons.h index 3153434dd..e78180dbd 100644 --- a/ui/icons.h +++ b/ui/icons.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef ICONS_H #define ICONS_H diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 568ab3533..895319b7a 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/keyframedrawing.h b/ui/keyframedrawing.h index 2c15e893f..f94103532 100644 --- a/ui/keyframedrawing.h +++ b/ui/keyframedrawing.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/keyframenavigator.cpp b/ui/keyframenavigator.cpp index 9a60dd4cc..9849eb5f1 100644 --- a/ui/keyframenavigator.cpp +++ b/ui/keyframenavigator.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/keyframenavigator.h b/ui/keyframenavigator.h index 3811b3906..85d718519 100644 --- a/ui/keyframenavigator.h +++ b/ui/keyframenavigator.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 7e3b6c679..201d9cfb8 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 645861ec8..534438f66 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 93fcbf698..9c1dcd285 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 0b514fbad..ed7516040 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/mainwindow.h b/ui/mainwindow.h index e06987228..749088190 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/mediaiconservice.cpp b/ui/mediaiconservice.cpp index 9563c54e1..ad0626cf0 100644 --- a/ui/mediaiconservice.cpp +++ b/ui/mediaiconservice.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/mediaiconservice.h b/ui/mediaiconservice.h index f817696da..3b3062e05 100644 --- a/ui/mediaiconservice.h +++ b/ui/mediaiconservice.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/menu.cpp b/ui/menu.cpp index 20a6990ca..a5c877a9a 100644 --- a/ui/menu.cpp +++ b/ui/menu.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "menu.h" #include "global/global.h" diff --git a/ui/menu.h b/ui/menu.h index 6b33e60e3..a4f36bdc0 100644 --- a/ui/menu.h +++ b/ui/menu.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef MENU_H #define MENU_H diff --git a/ui/menuhelper.cpp b/ui/menuhelper.cpp index 9a637861e..9553eada1 100644 --- a/ui/menuhelper.cpp +++ b/ui/menuhelper.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/menuhelper.h b/ui/menuhelper.h index 24b50db7b..d568c3862 100644 --- a/ui/menuhelper.h +++ b/ui/menuhelper.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/panel.cpp b/ui/panel.cpp index ed54ea43d..ce74c1437 100644 --- a/ui/panel.cpp +++ b/ui/panel.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/panel.h b/ui/panel.h index c4c9f2cbe..b0141cec4 100644 --- a/ui/panel.h +++ b/ui/panel.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/rectangleselect.cpp b/ui/rectangleselect.cpp index 033c9c47d..8e13650a2 100644 --- a/ui/rectangleselect.cpp +++ b/ui/rectangleselect.cpp @@ -1,27 +1,27 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ #include "rectangleselect.h" void draw_selection_rectangle(QPainter& painter, const QRect& rect) { - painter.setPen(QColor(204, 204, 204)); - painter.setBrush(QColor(0, 0, 0, 32)); - painter.drawRect(rect); + painter.setPen(QColor(204, 204, 204)); + painter.setBrush(QColor(0, 0, 0, 32)); + painter.drawRect(rect); } diff --git a/ui/rectangleselect.h b/ui/rectangleselect.h index 9cc11f078..45d0d4d5d 100644 --- a/ui/rectangleselect.h +++ b/ui/rectangleselect.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/resizablescrollbar.cpp b/ui/resizablescrollbar.cpp index 785f2eba8..a339a0c9f 100644 --- a/ui/resizablescrollbar.cpp +++ b/ui/resizablescrollbar.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/resizablescrollbar.h b/ui/resizablescrollbar.h index ef1453bc2..1a861f7bf 100644 --- a/ui/resizablescrollbar.h +++ b/ui/resizablescrollbar.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/scrollarea.cpp b/ui/scrollarea.cpp index 4249a39f3..579f3fb5f 100644 --- a/ui/scrollarea.cpp +++ b/ui/scrollarea.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/scrollarea.h b/ui/scrollarea.h index 667fe53d1..1c2ee5082 100644 --- a/ui/scrollarea.h +++ b/ui/scrollarea.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/sourceiconview.cpp b/ui/sourceiconview.cpp index 66c62e6cf..3016ad774 100644 --- a/ui/sourceiconview.cpp +++ b/ui/sourceiconview.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/sourceiconview.h b/ui/sourceiconview.h index e561a7a6f..cd8acbe84 100644 --- a/ui/sourceiconview.h +++ b/ui/sourceiconview.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index a421cd80b..c173182b9 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/sourcetable.h b/ui/sourcetable.h index 251d87776..8668036a3 100644 --- a/ui/sourcetable.h +++ b/ui/sourcetable.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/styling.cpp b/ui/styling.cpp index 2c890a3ef..bf1ae3d2b 100644 --- a/ui/styling.cpp +++ b/ui/styling.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "styling.h" #include "global/config.h" diff --git a/ui/styling.h b/ui/styling.h index c8a1b7343..e41a9df9b 100644 --- a/ui/styling.h +++ b/ui/styling.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef STYLING_H #define STYLING_H diff --git a/ui/texteditex.cpp b/ui/texteditex.cpp index 3400217fc..2fe4b5be3 100644 --- a/ui/texteditex.cpp +++ b/ui/texteditex.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/texteditex.h b/ui/texteditex.h index 0b4802a30..64ae84310 100644 --- a/ui/texteditex.h +++ b/ui/texteditex.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index 0f1bb5aa7..eb39f9632 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/timelineheader.h b/ui/timelineheader.h index 780a05d97..596365d02 100644 --- a/ui/timelineheader.h +++ b/ui/timelineheader.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -30,68 +30,68 @@ bool center_scroll_to_playhead(QScrollBar* bar, double zoom, long playhead); class TimelineHeader : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit TimelineHeader(QWidget *parent = 0); - void set_in_point(long p); - void set_out_point(long p); + explicit TimelineHeader(QWidget *parent = 0); + void set_in_point(long p); + void set_out_point(long p); - Viewer* viewer; + Viewer* viewer; - bool snapping; + bool snapping; - void show_text(bool enable); - double get_zoom(); - void delete_markers(); - void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset); + void show_text(bool enable); + double get_zoom(); + void delete_markers(); + void set_scrollbar_max(QScrollBar* bar, long sequence_end_frame, int offset); public slots: - void update_zoom(double z); - void set_scroll(int); - void set_visible_in(long i); - void show_context_menu(const QPoint &pos); - void resized_scroll_listener(double d); + void update_zoom(double z); + void set_scroll(int); + void set_visible_in(long i); + void show_context_menu(const QPoint &pos); + void resized_scroll_listener(double d); protected: - void paintEvent(QPaintEvent*); - void mousePressEvent(QMouseEvent*); - void mouseMoveEvent(QMouseEvent*); - void mouseReleaseEvent(QMouseEvent*); - void focusOutEvent(QFocusEvent*); + void paintEvent(QPaintEvent*); + void mousePressEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void focusOutEvent(QFocusEvent*); private: - void update_parents(); + void update_parents(); - bool dragging; + bool dragging; - bool resizing_workarea; - bool resizing_workarea_in; - long temp_workarea_in; - long temp_workarea_out; - long sequence_end; + bool resizing_workarea; + bool resizing_workarea_in; + long temp_workarea_in; + long temp_workarea_out; + long sequence_end; - double zoom; + double zoom; - long in_visible; + long in_visible; - void set_playhead(int mouse_x); + void set_playhead(int mouse_x); - int get_marker_offset(); + int get_marker_offset(); - QFontMetrics fm; + QFontMetrics fm; - int drag_start; - bool dragging_markers; - QVector selected_markers; - QVector selected_marker_original_times; + int drag_start; + bool dragging_markers; + QVector selected_markers; + QVector selected_marker_original_times; - long getHeaderFrameFromScreenPoint(int x); - int getHeaderScreenPointFromFrame(long frame); + long getHeaderFrameFromScreenPoint(int x); + int getHeaderScreenPointFromFrame(long frame); - int scroll; + int scroll; - int height_actual; - bool text_enabled; + int height_actual; + bool text_enabled; signals: }; diff --git a/ui/timelinetools.h b/ui/timelinetools.h index de54fa8a2..b13f4764d 100644 --- a/ui/timelinetools.h +++ b/ui/timelinetools.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 9b74f6fc3..8981fbc14 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h index 2965dd37c..0a8f55527 100644 --- a/ui/timelinewidget.h +++ b/ui/timelinewidget.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/updatenotification.cpp b/ui/updatenotification.cpp index 595a6b51f..a2fdc5275 100644 --- a/ui/updatenotification.cpp +++ b/ui/updatenotification.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "updatenotification.h" #include diff --git a/ui/updatenotification.h b/ui/updatenotification.h index 8a8f87e78..16c4c2f74 100644 --- a/ui/updatenotification.h +++ b/ui/updatenotification.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef UPDATENOTIFICATION_H #define UPDATENOTIFICATION_H diff --git a/ui/viewercontainer.cpp b/ui/viewercontainer.cpp index 9f9d62367..ea7320131 100644 --- a/ui/viewercontainer.cpp +++ b/ui/viewercontainer.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/viewercontainer.h b/ui/viewercontainer.h index 1724c1d16..8a5da99fe 100644 --- a/ui/viewercontainer.h +++ b/ui/viewercontainer.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ @@ -28,42 +28,42 @@ class QScrollBar; class ViewerContainer : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit ViewerContainer(QWidget *parent = 0); - ~ViewerContainer(); + explicit ViewerContainer(QWidget *parent = 0); + ~ViewerContainer(); - bool fit; - double zoom; + bool fit; + double zoom; - void dragScrollPress(const QPoint&); - void dragScrollMove(const QPoint&); - void parseWheelEvent(QWheelEvent* event); + void dragScrollPress(const QPoint&); + void dragScrollMove(const QPoint&); + void parseWheelEvent(QWheelEvent* event); - Viewer* viewer; - ViewerWidget* child; - void adjust(); + Viewer* viewer; + ViewerWidget* child; + void adjust(); - // manually moves scrollbars into the correct position - void adjust_scrollbars(); + // manually moves scrollbars into the correct position + void adjust_scrollbars(); protected: - void resizeEvent(QResizeEvent *event); + void resizeEvent(QResizeEvent *event); signals: public slots: private slots: - void scroll_changed(); + void scroll_changed(); private: - int drag_start_x; - int drag_start_y; - int horiz_start; - int vert_start; - QScrollBar* horizontal_scrollbar; - QScrollBar* vertical_scrollbar; + int drag_start_x; + int drag_start_y; + int horiz_start; + int vert_start; + QScrollBar* horizontal_scrollbar; + QScrollBar* vertical_scrollbar; }; #endif // VIEWERCONTAINER_H diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 5d8617abd..8ab1ae44b 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index d171dd73d..863446562 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/viewerwindow.cpp b/ui/viewerwindow.cpp index 922b1a4cc..f6148fc61 100644 --- a/ui/viewerwindow.cpp +++ b/ui/viewerwindow.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/ui/viewerwindow.h b/ui/viewerwindow.h index 7ae9c0cc4..ad46fee40 100644 --- a/ui/viewerwindow.h +++ b/ui/viewerwindow.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/undo/comboaction.cpp b/undo/comboaction.cpp index e0d9be038..16035fb01 100644 --- a/undo/comboaction.cpp +++ b/undo/comboaction.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "comboaction.h" ComboAction::ComboAction() {} diff --git a/undo/comboaction.h b/undo/comboaction.h index 083e82b6d..d5ed2e7af 100644 --- a/undo/comboaction.h +++ b/undo/comboaction.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef COMBOACTION_H #define COMBOACTION_H diff --git a/undo/undo.cpp b/undo/undo.cpp index 172755afb..78d521165 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/undo/undo.h b/undo/undo.h index d55ed478d..da8fa46b2 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/undo/undostack.cpp b/undo/undostack.cpp index 2edc0f0b1..5eb605390 100644 --- a/undo/undostack.cpp +++ b/undo/undostack.cpp @@ -1,3 +1,23 @@ +/*** + + 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 "undostack.h" QUndoStack olive::UndoStack; diff --git a/undo/undostack.h b/undo/undostack.h index 72086a823..faf872eee 100644 --- a/undo/undostack.h +++ b/undo/undostack.h @@ -1,3 +1,23 @@ +/*** + + 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 . + +***/ + #ifndef UNDOSTACK_H #define UNDOSTACK_H From c5a64adc40b276455c0337a4deb5017f1464d202 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 14:49:07 +1100 Subject: [PATCH 035/133] excised frei0r effect --- effects/effect.cpp | 4 - effects/effect.h | 4 +- effects/effectloaders.cpp | 84 +----------- effects/internal/frei0reffect.cpp | 219 ------------------------------ effects/internal/frei0reffect.h | 55 -------- olive.pro | 2 - timeline/clip.cpp | 6 +- 7 files changed, 10 insertions(+), 364 deletions(-) delete mode 100644 effects/internal/frei0reffect.cpp delete mode 100644 effects/internal/frei0reffect.h diff --git a/effects/effect.cpp b/effects/effect.cpp index 4a8f29f7f..387a374bb 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -66,7 +66,6 @@ #include "effects/internal/cornerpineffect.h" #include "effects/internal/vsthost.h" #include "effects/internal/fillleftrighteffect.h" -#include "effects/internal/frei0reffect.h" #include "effects/internal/richtexteffect.h" QVector olive::effects; @@ -89,9 +88,6 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { case EFFECT_INTERNAL_FILLLEFTRIGHT: return std::make_shared(c, em); #ifndef NOVST case EFFECT_INTERNAL_VST: return std::make_shared(c, em); -#endif -#ifndef NOFREI0R - case EFFECT_INTERNAL_FREI0R: return std::make_shared(c, em); #endif case EFFECT_INTERNAL_RICHTEXT: return std::make_shared(c, em); } diff --git a/effects/effect.h b/effects/effect.h index 6123bb348..89d4809bd 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -108,7 +108,6 @@ enum EffectInternal { EFFECT_INTERNAL_FILLLEFTRIGHT, EFFECT_INTERNAL_VST, EFFECT_INTERNAL_CORNERPIN, - EFFECT_INTERNAL_FREI0R, EFFECT_INTERNAL_RICHTEXT, EFFECT_INTERNAL_COUNT }; @@ -179,8 +178,7 @@ public: enum VideoEffectFlags { ShaderFlag = 0x1, CoordsFlag = 0x2, - SuperimposeFlag = 0x4, - ImageFlag = 0x8 + SuperimposeFlag = 0x4 }; int Flags(); void SetFlags(int flags); diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index 1d635d664..ee694a73f 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -20,6 +20,10 @@ #include "effectloaders.h" +#include +#include +#include + #include "effects/effect.h" #include "effects/transition.h" #include "global/path.h" @@ -28,16 +32,6 @@ #include "global/crossplatformlib.h" #include "global/config.h" -#include -#include - -#include - -#ifndef NOFREI0R -#include -typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); -#endif - QMutex olive::effects_loaded; void load_internal_effects() { @@ -206,73 +200,6 @@ void EffectInit::StartLoading() { init_thread->start(); } -#ifndef NOFREI0R -void load_frei0r_effects_worker(const QString& dir, EffectMeta& em, QVector& loaded_names) { - QDir search_dir(dir); - if (search_dir.exists()) { - QList entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); - for (int j=0;j(LibAddress(effect, "f0r_get_plugin_info")); - if (get_info_func != nullptr) { - f0r_plugin_info_t info; - get_info_func(&info); - - if (!loaded_names.contains(info.name) - && info.plugin_type == F0R_PLUGIN_TYPE_FILTER - && info.color_model == F0R_COLOR_MODEL_RGBA8888) { - em.name = info.name; - em.path = dir; - em.filename = entry_list.at(j); - em.tooltip = QString("%1\n%2\n%3\n%4").arg(em.name, info.author, info.explanation, em.filename); - - loaded_names.append(em.name); - - olive::effects.append(em); - } -// qDebug() << "Found:" << info.name << "by" << info.author; - } - LibClose(effect); - } -// qDebug() << search_dir.filePath(entry_list.at(j)); - } - } - } -} - -void load_frei0r_effects() { - QList effect_dirs = get_effects_paths(); - - // add defined paths for frei0r plugins on unix -#if defined(__APPLE__) || defined(__linux__) || defined(__HAIKU__) - effect_dirs.prepend("/usr/lib/frei0r-1"); - effect_dirs.prepend("/usr/local/lib/frei0r-1"); - effect_dirs.prepend(QDir::homePath() + "/.frei0r-1/lib"); -#endif - - QString env_path(qgetenv("FREI0R_PATH")); - if (!env_path.isEmpty()) effect_dirs.append(env_path); - - QVector loaded_names; - - // search for paths - EffectMeta em; - em.category = "Frei0r"; - em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_VIDEO; - em.internal = EFFECT_INTERNAL_FREI0R; - - for (int i=0;i. - -***/ - -#include "frei0reffect.h" - -#ifndef NOFREI0R - -#include -#include - -#include "timeline/clip.h" - -typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int height); -typedef int (*f0rInitFunc) (); -typedef void (*f0rDeinitFunc) (); -typedef void (*f0rUpdateFunc) (f0r_instance_t instance, - double time, const uint32_t* inframe, uint32_t* outframe); -typedef void (*f0rDestructFunc)(f0r_instance_t instance); -typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info); -typedef void (*f0rSetParamValue) (f0r_instance_t instance, - f0r_param_t param, int param_index); - -Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) : - Effect(c, em), - open(false) -{ - SetFlags(ImageFlag); - - // Windows DLL loading routine - QString dll_fn = QDir(em->path).filePath(em->filename); - - handle = LibLoad(dll_fn); - if(handle == nullptr) { - QString dll_error; - -#ifdef _WIN32 - DWORD dll_err = GetLastError(); - dll_error = QString::number(dll_err); -#elif __linux__ - dll_error = dlerror(); -#endif - qCritical() << "Failed to load Frei0r plugin" << dll_fn << "-" << dll_error; - - QString msg_err = tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error); - -#ifdef _WIN32 - if (dll_err == 193) { -#ifdef _WIN64 - msg_err += "\n\n" + tr("NOTE: You can't load 32-bit Frei0r plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive."); -#elif _WIN32 - msg_err += "\n\n" + tr("NOTE: You can't load 64-bit Frei0r plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive."); -#endif - } -#endif - - QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"), msg_err); - - return; - } - - f0rInitFunc init = reinterpret_cast(LibAddress(handle, "f0r_init")); - init(); - - construct_module(); - - f0r_plugin_info_t info; - f0rGetPluginInfo info_func = reinterpret_cast(LibAddress(handle, "f0r_get_plugin_info")); - info_func(&info); - - param_count = info.num_params; - - get_param_info = reinterpret_cast(LibAddress(handle, "f0r_get_param_info")); - for (int i=0;i= 0 && param_info.type <= F0R_PARAM_STRING) { - EffectRow* row = new EffectRow(this, param_info.name); - switch (param_info.type) { - case F0R_PARAM_BOOL: - new BoolField(row, QString::number(i)); - break; - case F0R_PARAM_DOUBLE: - { - DoubleField* f = new DoubleField(row, QString::number(i)); - f->SetMinimum(0); - f->SetMaximum(100); - } - break; - case F0R_PARAM_COLOR: - new ColorField(row, QString::number(i)); - break; - case F0R_PARAM_POSITION: - { - DoubleField* fx = new DoubleField(row, QString("%1X").arg(QString::number(i))); - fx->SetMinimum(0); - fx->SetMaximum(100); - DoubleField* fy = new DoubleField(row, QString("%1Y").arg(QString::number(i))); - fy->SetMinimum(0); - fy->SetMaximum(100); - } - break; - case F0R_PARAM_STRING: - new StringField(row, QString::number(i)); - break; - } - } - } -} - -Frei0rEffect::~Frei0rEffect() { - if (handle != nullptr) { - f0rDeinitFunc deinit = reinterpret_cast(LibAddress(handle, "f0r_deinit")); - deinit(); - - LibClose(handle); - } -} - -void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *output, int) { - f0rUpdateFunc update_func = reinterpret_cast(LibAddress(handle, "f0r_update")); - - for (int i=0;i(LibAddress(handle, "f0r_set_param_value")); - switch (param_info.type) { - case F0R_PARAM_BOOL: - { - double b = param_row->Field(0)->GetValueAt(timecode).toBool(); - - set_param(instance, &b, i); - } - break; - case F0R_PARAM_DOUBLE: - { - double d = param_row->Field(0)->GetValueAt(timecode).toDouble()*0.01; - - set_param(instance, &d, i); - } - break; - case F0R_PARAM_COLOR: - { - QColor qcolor = param_row->Field(0)->GetValueAt(timecode).value(); - - f0r_param_color fcolor; - fcolor.r = float(qcolor.redF()); - fcolor.g = float(qcolor.greenF()); - fcolor.b = float(qcolor.blueF()); - - set_param(instance, &fcolor, i); - } - break; - case F0R_PARAM_POSITION: - { - f0r_param_position pos; - - pos.x = param_row->Field(0)->GetValueAt(timecode).toDouble(); - pos.y = param_row->Field(1)->GetValueAt(timecode).toDouble(); - - set_param(instance, &pos, i); - } - break; - case F0R_PARAM_STRING: - { - QByteArray bytes = param_row->Field(0)->GetValueAt(timecode).toString().toUtf8(); - - char* byte_data = bytes.data(); - set_param(instance, &byte_data, i); - } - break; - } - } - - update_func(instance, timecode, reinterpret_cast(input), reinterpret_cast(output)); -} - -void Frei0rEffect::refresh() { - destruct_module(); - construct_module(); -} - -void Frei0rEffect::destruct_module() { - if (open) { - f0rDestructFunc destruct = reinterpret_cast(LibAddress(handle, "f0r_destruct")); - destruct(instance); - - open = false; - } -} - -void Frei0rEffect::construct_module() { - f0rConstructFunc construct = reinterpret_cast(LibAddress(handle, "f0r_construct")); - instance = construct(parent_clip->media_width(), parent_clip->media_height()); - - open = true; -} - -#endif diff --git a/effects/internal/frei0reffect.h b/effects/internal/frei0reffect.h deleted file mode 100644 index 4551d63b7..000000000 --- a/effects/internal/frei0reffect.h +++ /dev/null @@ -1,55 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef FREI0REFFECT_H -#define FREI0REFFECT_H - -#ifndef NOFREI0R - -#include - -#include "effects/effect.h" -#include "global/crossplatformlib.h" - -typedef void (*f0rGetParamInfo)(f0r_param_info_t * info, - int param_index ); - -class Frei0rEffect : public Effect { - Q_OBJECT -public: - Frei0rEffect(Clip* c, const EffectMeta* em); - ~Frei0rEffect(); - - virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); - - virtual void refresh(); -private: - ModulePtr handle; - f0r_instance_t instance; - int param_count; - f0rGetParamInfo get_param_info; - void destruct_module(); - void construct_module(); - bool open; -}; - -#endif - -#endif // FREI0REFFECT_H diff --git a/olive.pro b/olive.pro index 96c32b374..f0f948fa0 100644 --- a/olive.pro +++ b/olive.pro @@ -133,7 +133,6 @@ SOURCES += \ dialogs/debugdialog.cpp \ ui/viewerwindow.cpp \ project/projectfilter.cpp \ - effects/internal/frei0reffect.cpp \ effects/effectloaders.cpp \ global/crossplatformlib.cpp \ effects/internal/vsthost.cpp \ @@ -260,7 +259,6 @@ HEADERS += \ dialogs/debugdialog.h \ ui/viewerwindow.h \ project/projectfilter.h \ - effects/internal/frei0reffect.h \ effects/effectloaders.h \ global/crossplatformlib.h \ effects/internal/vsthost.h \ diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 57e5c60fc..036fd99ca 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -582,6 +582,7 @@ bool Clip::Retrieve() f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); + /* // 2 data buffers to ping-pong between bool using_db_1 = true; uint8_t* data_buffer_1 = frame->data[0]; @@ -608,15 +609,18 @@ bool Clip::Retrieve() using_db_1 = !using_db_1; } } + */ texture->setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, - const_cast(using_db_1 ? data_buffer_1 : data_buffer_2)); + const_cast(frame->data[0])); + /* if (data_buffer_1 != frame->data[0]) { delete [] data_buffer_1; delete [] data_buffer_2; } + */ f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); From 4c1bc0d3c8d9b3ab0bfc71bd9a762e7ba57681a2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 15:04:14 +1100 Subject: [PATCH 036/133] changed texture formats to float --- rendering/framebufferobject.cpp | 6 +++++- timeline/clip.cpp | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 222fc59aa..c88ad1406 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -61,8 +61,12 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - ctx->functions()->glTexImage2D( + /*ctx->functions()->glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr + );*/ + + ctx->functions()->glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr ); // set texture filtering to bilinear diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 036fd99ca..240830155 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -572,10 +572,10 @@ bool Clip::Retrieve() // composition texture->setSize(cacher.media_width(), cacher.media_height()); - texture->setFormat(QOpenGLTexture::RGBA8_UNorm); + texture->setFormat(QOpenGLTexture::RGBA32F); texture->setMipLevels(texture->maximumMipLevels()); texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float32); } QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); From 9b2b7bc9495dc6d3742d4c9859d52c8b9093bf0f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 15:40:11 +1100 Subject: [PATCH 037/133] dropped QOpenGLTexture for raw textures --- effects/effect.cpp | 30 +++++++----- effects/effect.h | 4 +- rendering/cacher.cpp | 2 - rendering/framebufferobject.cpp | 2 +- rendering/renderfunctions.cpp | 9 +++- timeline/clip.cpp | 84 ++++++++++++--------------------- timeline/clip.h | 3 +- 7 files changed, 60 insertions(+), 74 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 387a374bb..7648345d3 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -117,7 +117,9 @@ Effect::Effect(Clip* c, const EffectMeta *em) : meta(em), flags_(0), shader_program_(nullptr), - texture(nullptr), + texture(0), + tex_width_(0), + tex_height_(0), isOpen(false), bound(false), iterations(1), @@ -886,24 +888,26 @@ GLuint Effect::process_superimpose(double timecode) { redrew_image = true; } - if (texture == nullptr || texture->width() != img.width() || texture->height() != img.height()) { + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + + if (texture == 0 || tex_width_ != img.width() || tex_height_ != img.height()) { delete_texture(); - texture = new QOpenGLTexture(QOpenGLTexture::Target2D); - texture->setSize(img.width(), img.height()); - texture->setFormat(QOpenGLTexture::RGBA8_UNorm); - texture->setMipLevels(texture->maximumMipLevels()); - texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8); + tex_width_ = img.width(); + tex_height_ = img.height(); - redrew_image = true; + f->glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA8, tex_width_, tex_height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits() + ); + + redrew_image = false; } if (redrew_image) { - texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, img.constBits()); + f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_width_, tex_height_, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits()); } - return texture->textureId(); + return texture; } void Effect::process_audio(double, double, quint8*, int, int) {} @@ -1080,8 +1084,8 @@ bool Effect::valueHasChanged(double timecode) { } void Effect::delete_texture() { - delete texture; - texture = nullptr; + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture); + texture = 0; } const EffectMeta* get_meta_from_name(const QString& input) { diff --git a/effects/effect.h b/effects/effect.h index 89d4809bd..8fc3918cb 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -229,7 +229,9 @@ protected: // superimpose effect QImage img; - QOpenGLTexture* texture; + GLuint texture; + int tex_width_; + int tex_height_; // enable effect to update constantly virtual bool AlwaysUpdate(); diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 784ee3b87..308440b25 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -1124,8 +1124,6 @@ void Cacher::CloseWorker() { avformat_close_input(&formatCtx); } - clip->reset(); - qInfo() << "Clip closed on track" << clip->track(); } diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index c88ad1406..c69995064 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -66,7 +66,7 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) );*/ ctx->functions()->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr + GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr ); // set texture filtering to bilinear diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index b7cd99347..2d2d0b1e7 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -468,7 +468,7 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { params.texture_failed = true; } else { // retrieve ID from c->texture - textureID = c->texture->textureId(); + textureID = c->texture; } if (textureID == 0) { @@ -530,6 +530,13 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } #ifndef NO_OCIO + + // Convert texture to float + if (textureID != c->fbo.at(0).texture() && textureID != c->fbo.at(1).texture()) { + textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); + fbo_switcher = !fbo_switcher; + } + // Convert frame from source to linear colorspace if (olive::CurrentConfig.enable_color_management) { diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 240830155..99b2612e6 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -57,8 +57,7 @@ Clip::Clip(Sequence* s) : undeletable = false; replaced = false; open_ = false; - - reset(); + texture = 0; } ClipPtr Clip::copy(Sequence* s) { @@ -196,10 +195,6 @@ void Clip::move(ComboAction* ca, long iin, long iout, long iclip_in, int itrack, } } -void Clip::reset() { - texture = nullptr; -} - void Clip::reset_audio() { if (UsesCacher()) { cacher.ResetAudio(); @@ -503,8 +498,8 @@ void Clip::Close(bool wait) { } // destroy opengl texture in main thread - delete texture; - texture = nullptr; + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture); + texture = 0; // close all effects for (int i=0;icontains(frame)) { + bool allocate_data = false; + // check if the opengl texture exists yet, create it if not - if (texture == nullptr) { - texture = new QOpenGLTexture(QOpenGLTexture::Target2D); + if (texture == 0) { + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure - // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the - // composition - texture->setSize(cacher.media_width(), cacher.media_height()); + // create texture object + f->glGenTextures(1, &texture); + + f->glBindTexture(GL_TEXTURE_2D, texture); + + // set texture filtering to bilinear + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // queue an allocation ahead + allocate_data = true; - texture->setFormat(QOpenGLTexture::RGBA32F); - texture->setMipLevels(texture->maximumMipLevels()); - texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); - texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float32); } QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); - /* - // 2 data buffers to ping-pong between - bool using_db_1 = true; - uint8_t* data_buffer_1 = frame->data[0]; - uint8_t* data_buffer_2 = nullptr; + int video_width = cacher.media_width(); + int video_height = cacher.media_height(); - int frame_size = frame->linesize[0]*frame->height; - - for (int i=0;iFlags() & Effect::ImageFlag) && e->IsEnabled()) { - if (data_buffer_1 == frame->data[0]) { - data_buffer_1 = new uint8_t[frame_size]; - data_buffer_2 = new uint8_t[frame_size]; - - memcpy(data_buffer_1, frame->data[0], frame_size); - } - - e->process_image(get_timecode(this, cacher_frame), - using_db_1 ? data_buffer_1 : data_buffer_2, - using_db_1 ? data_buffer_2 : data_buffer_1, - frame_size - ); - - using_db_1 = !using_db_1; - } + if (allocate_data) { + // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure + // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the + // composition + f->glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA8, video_width, video_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0] + ); + } else { + f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0]); } - */ - texture->setData(QOpenGLTexture::RGBA, - QOpenGLTexture::UInt8, - const_cast(frame->data[0])); - - /* - if (data_buffer_1 != frame->data[0]) { - delete [] data_buffer_1; - delete [] data_buffer_2; - } - */ + f->glBindTexture(GL_TEXTURE_2D, 0); f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); diff --git a/timeline/clip.h b/timeline/clip.h index a09eae6c0..ca3f56eb6 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -116,7 +116,6 @@ public: AVRational time_base(); void reset_audio(); - void reset(); void refresh(); long length(); @@ -154,7 +153,7 @@ public: // video playback variables QVector fbo; - QOpenGLTexture* texture; + GLuint texture; long texture_frame; #ifndef NO_OCIO From 9c920cfcc3357229208cdc8b537b0a09151451b7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 16:05:10 +1100 Subject: [PATCH 038/133] added settings for bit depth --- dialogs/preferencesdialog.cpp | 21 +++++++++++++++ main.cpp | 4 +++ olive.pro | 6 +++-- rendering/bitdepths.cpp | 51 +++++++++++++++++++++++++++++++++++ rendering/bitdepths.h | 42 +++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 rendering/bitdepths.cpp create mode 100644 rendering/bitdepths.h diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index ad669341f..cd9d90baf 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -48,6 +48,7 @@ #include "global/config.h" #include "global/path.h" #include "rendering/audio.h" +#include "rendering/bitdepths.h" #include "panels/panels.h" #include "ui/mainwindow.h" @@ -942,6 +943,26 @@ void PreferencesDialog::setup_ui() { row++; + // COLOR MANAGEMENT -> Playback Bit Depth + QComboBox* playback_bit_depth = new QComboBox(); + for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); + } + color_management_layout->addWidget(new QLabel("Playback Bit Depth:"), row, 0); + color_management_layout->addWidget(playback_bit_depth, row, 1); + + row++; + + // COLOR MANAGEMENT -> Rendering Bit Depth + QComboBox* rendering_bit_depth = new QComboBox(); + for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); + } + color_management_layout->addWidget(new QLabel("Rendering Bit Depth:"), row, 0); + color_management_layout->addWidget(rendering_bit_depth, row, 1); + + row++; + #ifdef NO_OCIO enable_color_management->setEnabled(false); ocio_config_file->setEnabled(false); diff --git a/main.cpp b/main.cpp index 20c52026a..4a2e5fc4b 100644 --- a/main.cpp +++ b/main.cpp @@ -24,6 +24,7 @@ #include "global/config.h" #include "global/global.h" #include "panels/timeline.h" +#include "rendering/bitdepths.h" #include "ui/mediaiconservice.h" #include "ui/mainwindow.h" @@ -131,6 +132,9 @@ int main(int argc, char *argv[]) { // multiply track height constants by the current DPI scale olive::timeline::MultiplyTrackSizesByDPI(); + // set up rendering bit depths + olive::rendering::InitializeBitDepths(); + // connect main window's first paint to global's init finished function QObject::connect(&w, SIGNAL(finished_first_paint()), olive::Global.get(), SLOT(finished_initialize())); diff --git a/olive.pro b/olive.pro index f0f948fa0..3f04c492c 100644 --- a/olive.pro +++ b/olive.pro @@ -174,7 +174,8 @@ SOURCES += \ effects/internal/richtexteffect.cpp \ ui/blur.cpp \ ui/menu.cpp \ - rendering/qopenglshaderprogramptr.cpp + rendering/qopenglshaderprogramptr.cpp \ + rendering/bitdepths.cpp HEADERS += \ ui/mainwindow.h \ @@ -302,7 +303,8 @@ HEADERS += \ effects/internal/richtexteffect.h \ ui/blur.h \ ui/menu.h \ - rendering/qopenglshaderprogramptr.h + rendering/qopenglshaderprogramptr.h \ + rendering/bitdepths.h FORMS += diff --git a/rendering/bitdepths.cpp b/rendering/bitdepths.cpp new file mode 100644 index 000000000..b7bf217f3 --- /dev/null +++ b/rendering/bitdepths.cpp @@ -0,0 +1,51 @@ +/*** + + 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 "bitdepths.h" + +#include + +namespace olive { +namespace rendering { + +QVector bit_depths; + +void InitializeBitDepths() { + BitDepthInfo bdi; + + bdi.name = QCoreApplication::translate("bitdepths", "8-bit"); + bdi.pixel_type = GL_UNSIGNED_BYTE; + bdi.internal_format = GL_RGBA8; + bit_depths.append(bdi); + + bdi.name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); + bdi.pixel_type = GL_HALF_FLOAT; + bdi.internal_format = GL_RGBA16F; + bit_depths.append(bdi); + + bdi.name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); + bdi.pixel_type = GL_FLOAT; + bdi.internal_format = GL_RGBA32F; + bit_depths.append(bdi); +} + +} +} + diff --git a/rendering/bitdepths.h b/rendering/bitdepths.h new file mode 100644 index 000000000..ab1b07e8a --- /dev/null +++ b/rendering/bitdepths.h @@ -0,0 +1,42 @@ +/*** + + 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 . + +***/ + +#ifndef BITDEPTHS_H +#define BITDEPTHS_H + +#include +#include +#include + +namespace olive { + namespace rendering { + struct BitDepthInfo { + QString name; + GLuint pixel_type; + GLuint internal_format; + }; + + extern QVector bit_depths; + + void InitializeBitDepths(); + } +} + +#endif // BITDEPTHS_H From 262e93460f679b30973d834b0e58b0321378bae1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 20 Mar 2019 23:38:44 +1100 Subject: [PATCH 039/133] fix clip close crash --- rendering/renderfunctions.cpp | 2 ++ timeline/clip.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 2d2d0b1e7..235dc881c 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -565,6 +565,8 @@ GLuint compose_sequence(ComposeSequenceParams ¶ms) { } + qDebug() << "Input colorspace:" << input_cs; + try { OCIO::ConstProcessorRcPtr processor = config->getProcessor(input_cs.toUtf8(), OCIO::ROLE_SCENE_LINEAR); diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 99b2612e6..cc9ba2ec7 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -498,8 +498,10 @@ void Clip::Close(bool wait) { } // destroy opengl texture in main thread - QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture); - texture = 0; + if (texture > 0) { + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture); + texture = 0; + } // close all effects for (int i=0;i Date: Thu, 21 Mar 2019 18:01:52 +1100 Subject: [PATCH 040/133] moved associated function after linearize --- rendering/renderfunctions.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 4d5a78d0b..fc6c242d1 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -316,6 +316,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { long playhead = s->playhead; if (!params.nests.isEmpty()) { + for (int i=0;imedia()->to_sequence().get(); playhead += params.nests.at(i)->clip_in(true) - params.nests.at(i)->timeline_in(true); @@ -327,6 +328,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); final_fbo = params.nests.last()->fbo.at(0).buffer(); } + } int audio_track_count = 0; @@ -516,19 +518,6 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - if (!c->media()->to_footage()->alpha_is_associated) { - - // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline - params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); - - textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); - - params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - fbo_switcher = !fbo_switcher; - - } - #ifndef NO_OCIO // Convert texture to float @@ -589,6 +578,19 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } #endif + if (!c->media()->to_footage()->alpha_is_associated) { + + // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline + params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); + + textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); + + params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + fbo_switcher = !fbo_switcher; + + } + } } From e2c5c87a0ad16e9ef9029df648f96cb9ecb1a58b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 24 Mar 2019 23:15:10 +1100 Subject: [PATCH 041/133] fixed superimpose crash --- effects/effect.cpp | 14 +++++++++----- effects/effect.h | 3 ++- rendering/renderfunctions.cpp | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 77ae3b223..68d082300 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -123,7 +123,8 @@ Effect::Effect(Clip* c, const EffectMeta *em) : bound(false), iterations(1), enabled_(true), - expanded_(true) + expanded_(true), + texture_ctx(nullptr) { if (em != nullptr) { // set up UI from effect file @@ -881,7 +882,7 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { void Effect::process_coords(double, GLTextureCoords&, int) {} -GLuint Effect::process_superimpose(double timecode) { +GLuint Effect::process_superimpose(QOpenGLContext* ctx, double timecode) { bool dimensions_changed = false; bool redrew_image = false; @@ -898,7 +899,7 @@ GLuint Effect::process_superimpose(double timecode) { redrew_image = true; } - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + QOpenGLFunctions* f = ctx->functions(); if (texture == 0 || tex_width_ != img.width() || tex_height_ != img.height()) { delete_texture(); @@ -1109,8 +1110,11 @@ bool Effect::valueHasChanged(double timecode) { } void Effect::delete_texture() { - QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &texture); - texture = 0; + if (texture_ctx != nullptr) { + texture_ctx->functions()->glDeleteTextures(1, &texture); + texture = 0; + texture_ctx = nullptr; + } } const EffectMeta* get_meta_from_name(const QString& input) { diff --git a/effects/effect.h b/effects/effect.h index 71d456589..9fbc462f8 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -191,7 +191,7 @@ public: virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); virtual void process_shader(double timecode, GLTextureCoords&, int iteration); virtual void process_coords(double timecode, GLTextureCoords& coords, int data); - virtual GLuint process_superimpose(double timecode); + virtual GLuint process_superimpose(QOpenGLContext *ctx, double timecode); virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); virtual void gizmo_draw(double timecode, GLTextureCoords& coords); @@ -231,6 +231,7 @@ protected: // superimpose effect QImage img; GLuint texture; + QOpenGLContext* texture_ctx; int tex_width_; int tex_height_; diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index fc6c242d1..27aedecec 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -284,7 +284,7 @@ void process_effect(QOpenGLContext* ctx, } } if (e->Flags() & Effect::SuperimposeFlag) { - GLuint superimpose_texture = e->process_superimpose(timecode); + GLuint superimpose_texture = e->process_superimpose(ctx, timecode); if (superimpose_texture == 0) { qWarning() << "Superimpose texture was nullptr, retrying..."; From ef837f06c126b5ebc759b00c2c89b6600f80267a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 25 Mar 2019 13:54:41 +1100 Subject: [PATCH 042/133] proper alpha treatment --- olive.pro | 6 +- rendering/framebufferobject.cpp | 9 +- rendering/renderfunctions.cpp | 192 ++------------------------- rendering/renderfunctions.h | 3 +- rendering/renderthread.cpp | 5 +- rendering/shadergenerators.cpp | 225 ++++++++++++++++++++++++++++++++ rendering/shadergenerators.h | 37 ++++++ timeline/clip.cpp | 71 ++++++---- timeline/clip.h | 2 +- ui/viewerwidget.cpp | 3 +- ui/viewerwindow.cpp | 3 +- 11 files changed, 331 insertions(+), 225 deletions(-) create mode 100644 rendering/shadergenerators.cpp create mode 100644 rendering/shadergenerators.h diff --git a/olive.pro b/olive.pro index 43cede51f..299cf59b4 100644 --- a/olive.pro +++ b/olive.pro @@ -176,7 +176,8 @@ SOURCES += \ rendering/bitdepths.cpp \ timeline/mediaimportdata.cpp \ dialogs/autocutsilencedialog.cpp \ - ui/columnedgridlayout.cpp + ui/columnedgridlayout.cpp \ + rendering/shadergenerators.cpp HEADERS += \ ui/mainwindow.h \ @@ -306,7 +307,8 @@ HEADERS += \ rendering/bitdepths.h \ timeline/mediaimportdata.h \ dialogs/autocutsilencedialog.h \ - ui/columnedgridlayout.h + ui/columnedgridlayout.h \ + rendering/shadergenerators.h FORMS += diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index c69995064..2e51ab151 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -24,6 +24,9 @@ #include #include +// TODO take this from Config rather than having a constant +const GLuint kPixelFormat = GL_RGBA16F; + FramebufferObject::FramebufferObject() : buffer_(0), texture_(0), @@ -61,12 +64,8 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - /*ctx->functions()->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr - );*/ - ctx->functions()->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr + GL_TEXTURE_2D, 0, kPixelFormat, width, height, 0, GL_RGBA, GL_FLOAT, nullptr ); // set texture filtering to bilinear diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 0a0aee87c..e64097165 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -44,6 +44,7 @@ extern "C" { #include "panels/timeline.h" #include "panels/viewer.h" #include "qopenglshaderprogramptr.h" +#include "shadergenerators.h" GLfloat olive::rendering::blit_vertices[] = { -1.0f, -1.0f, 0.0f, @@ -75,14 +76,6 @@ GLfloat olive::rendering::flipped_blit_texcoords[] = { 1.0, 0.0 }; -#ifndef NO_OCIO -// copied from source code to OCIODisplay -const int OCIO_LUT3D_EDGE_SIZE = 32; - -// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE -const int OCIO_NUM_3D_ENTRIES = 98304; -#endif - void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatrix4x4 matrix) { QOpenGLVertexArrayObject m_vao; @@ -126,95 +119,6 @@ void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatri } -QOpenGLShaderProgramPtr olive::rendering::GetPipeline(const QString& shader_code) -{ - QOpenGLShaderProgramPtr program = std::make_shared(); - - // Generate vertex shader - QString vert_shader = "#version 110\n" - "\n" - "#ifdef GL_ES\n" - "precision mediump int;\n" - "precision mediump float;\n" - "#endif\n" - "\n" - "uniform mat4 mvp_matrix;\n" - "\n" - "attribute vec4 a_position;\n" - "attribute vec2 a_texcoord;\n" - "\n" - "varying vec2 v_texcoord;\n" - "\n" - "void main() {\n" - " gl_Position = mvp_matrix * a_position;\n" - " v_texcoord = a_texcoord;\n" - "}\n"; - - // Generate fragment shader - QString frag_shader = "#version 110\n" - "\n" - "#ifdef GL_ES\n" - "precision mediump int;\n" - "precision mediump float;\n" - "#endif\n" - "\n" - "uniform sampler2D texture;\n" - "uniform float opacity;\n" - "uniform bool color_only;\n" - "uniform vec4 color_only_color;\n" - "varying vec2 v_texcoord;\n" - "\n"; - - // Finish the function with the main function - - // Check if additional code was passed to this function, add it here - if (shader_code.isEmpty()) { - - // If not, just add a pure main() function - - frag_shader.append("\n" - "void main() {\n" - " if (color_only) {\n" - " gl_FragColor = color_only_color;" - " } else {\n" - " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" - " gl_FragColor = color;\n" - " }\n" - "}\n"); - - } else { - - // If additional code was passed, add it and reference it in main(). - // - // The function in the additional code is expected to be `vec4 process(vec4 color)`. The texture coordinate can be - // acquired through `v_texcoord`. - - frag_shader.append(shader_code); - - frag_shader.append("\n" - "void main() {\n" - " vec4 color = process(texture2D(texture, v_texcoord))*opacity;\n" - " gl_FragColor = color;\n" - "}\n"); - - } - - - - - // Add shaders to program - program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_shader); - program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_shader); - program->link(); - - // Set opacity default to 100% - program->bind(); - program->setUniformValue("opacity", 1.0f); - program->release(); - - return program; -} - void draw_clip(QOpenGLContext* ctx, QOpenGLShaderProgram* pipeline, GLuint fbo, @@ -560,7 +464,13 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { OCIO::ConstProcessorRcPtr processor = config->getProcessor(input_cs.toUtf8(), OCIO::ROLE_SCENE_LINEAR); - c->ocio_shader = olive::rendering::SetupOCIO(params.ctx, c->ocio_lut_texture, processor); + olive::shader::AlphaAssociateMode associate_mode = (c->media()->to_footage()->alpha_is_associated) + ? olive::shader::DisassociateAndReassociate : olive::shader::Associate; + + c->ocio_shader = olive::shader::SetupOCIO(params.ctx, + c->ocio_lut_texture, + processor, + associate_mode); } catch (OCIO::Exception& e) { qWarning() << e.what(); } @@ -578,19 +488,6 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } #endif - if (!c->media()->to_footage()->alpha_is_associated) { - - // alpha is not premultiplied, we'll need to multiply it for the rest of the pipeline - params.ctx->functions()->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ZERO, GL_ONE, GL_ZERO); - - textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); - - params.ctx->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - fbo_switcher = !fbo_switcher; - - } - } } @@ -696,9 +593,8 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, textureID); // set texture filter to bilinear - //params.ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); - //params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + params.ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); + params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // draw clip on screen according to gl coordinates @@ -953,74 +849,6 @@ void close_active_clips(Sequence* s) { } #ifndef NO_OCIO -QOpenGLShaderProgramPtr olive::rendering::SetupOCIO(QOpenGLContext* ctx, - GLuint& lut_texture, - OCIO::ConstProcessorRcPtr processor) -{ - - QOpenGLExtraFunctions* xf = ctx->extraFunctions(); - - // Create LUT texture - xf->glGenTextures(1, &lut_texture); - - // Bind LUT - xf->glBindTexture(GL_TEXTURE_3D, lut_texture); - - // Set texture parameters - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Allocate storage for texture - xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - 0, GL_RGB,GL_FLOAT, nullptr); - - // - // SET UP GLSL SHADER - // - - OCIO::GpuShaderDesc shaderDesc; - shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); - shaderDesc.setFunctionName("OCIODisplay"); - shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); - - // - // COMPUTE 3D LUT - // - - GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; - processor->getGpuLut3D(ocio_lut_data, shaderDesc); - - // Upload LUT data to texture - xf->glTexSubImage3D(GL_TEXTURE_3D, 0, - 0, 0, 0, - OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, - GL_RGB, GL_FLOAT, ocio_lut_data); - - delete [] ocio_lut_data; - - // Create OCIO shader code - QString shader_text(processor->getGpuShaderText(shaderDesc)); - shader_text.append("\n" - "uniform sampler3D tex2;\n" - "\n" - "vec4 process(vec4 col) {\n" - " return OCIODisplay(col, tex2);\n" - "}\n"); - - - // Get pipeline-based shader to inject OCIO shader into - QOpenGLShaderProgramPtr shader = olive::rendering::GetPipeline(shader_text); - - // Release LUT - xf->glBindTexture(GL_TEXTURE_3D, 0); - - return shader; -} - GLuint olive::rendering::OCIOBlit(QOpenGLShaderProgram *pipeline, GLuint lut, const FramebufferObject& fbo, diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 08ab77afa..a4f5c2877 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -389,9 +389,8 @@ namespace olive { extern GLfloat blit_texcoords[]; extern GLfloat flipped_blit_texcoords[]; void Blit(QOpenGLShaderProgram* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); - QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); + #ifndef NO_OCIO - QOpenGLShaderProgramPtr SetupOCIO(QOpenGLContext *ctx, GLuint &lut_texture, OCIO::ConstProcessorRcPtr processor); GLuint OCIOBlit(QOpenGLShaderProgram *pipeline, GLuint lut, const FramebufferObject& fbo, diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 552958e94..d1ab0b22f 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -36,6 +36,7 @@ namespace OCIO = OCIO_NAMESPACE::v1; #include "effects/effectloaders.h" #include "global/config.h" #include "rendering/renderfunctions.h" +#include "rendering/shadergenerators.h" RenderThread::RenderThread() : gizmos(nullptr), @@ -117,7 +118,7 @@ void RenderThread::run() { olive::effects_loaded.unlock(); blend_mode_program->link(); - pipeline_program = olive::rendering::GetPipeline(); + pipeline_program = olive::shader::GetPipeline(); } #ifndef NO_OCIO @@ -193,7 +194,7 @@ void RenderThread::set_up_ocio() OCIO::ConstProcessorRcPtr processor = config->getProcessor(transform); // Create a OCIO shader with this processor - ocio_shader = olive::rendering::SetupOCIO(ctx, ocio_lut_texture, processor); + ocio_shader = olive::shader::SetupOCIO(ctx, ocio_lut_texture, processor, olive::shader::NoAssociate); } catch(OCIO::Exception & e) { qCritical() << e.what(); diff --git a/rendering/shadergenerators.cpp b/rendering/shadergenerators.cpp new file mode 100644 index 000000000..dde856ba6 --- /dev/null +++ b/rendering/shadergenerators.cpp @@ -0,0 +1,225 @@ +#include "shadergenerators.h" + +#include + +QOpenGLShaderProgramPtr olive::shader::GetPipeline(const QString& shader_code) +{ + QOpenGLShaderProgramPtr program = std::make_shared(); + + // Generate vertex shader + QString vert_shader = "#version 110\n" + "\n" + "#ifdef GL_ES\n" + "precision mediump int;\n" + "precision mediump float;\n" + "#endif\n" + "\n" + "uniform mat4 mvp_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_texcoord;\n" + "\n" + "varying vec2 v_texcoord;\n" + "\n" + "void main() {\n" + " gl_Position = mvp_matrix * a_position;\n" + " v_texcoord = a_texcoord;\n" + "}\n"; + + // Generate fragment shader + QString frag_shader = "#version 110\n" + "\n" + "#ifdef GL_ES\n" + "precision mediump int;\n" + "precision mediump float;\n" + "#endif\n" + "\n" + "uniform sampler2D texture;\n" + "uniform float opacity;\n" + "uniform bool color_only;\n" + "uniform vec4 color_only_color;\n" + "varying vec2 v_texcoord;\n" + "\n"; + + // Finish the function with the main function + + // Check if additional code was passed to this function, add it here + if (shader_code.isEmpty()) { + + // If not, just add a pure main() function + + frag_shader.append("\n" + "void main() {\n" + " if (color_only) {\n" + " gl_FragColor = color_only_color;" + " } else {\n" + " vec4 color = texture2D(texture, v_texcoord)*opacity;\n" + " gl_FragColor = color;\n" + " }\n" + "}\n"); + + } else { + + // If additional code was passed, add it and reference it in main(). + // + // The function in the additional code is expected to be `vec4 process(vec4 color)`. The texture coordinate can be + // acquired through `v_texcoord`. + + frag_shader.append(shader_code); + + frag_shader.append("\n" + "void main() {\n" + " vec4 color = process(texture2D(texture, v_texcoord))*opacity;\n" + " gl_FragColor = color;\n" + "}\n"); + + } + + + + + // Add shaders to program + program->addShaderFromSourceCode(QOpenGLShader::Vertex, vert_shader); + program->addShaderFromSourceCode(QOpenGLShader::Fragment, frag_shader); + program->link(); + + // Set opacity default to 100% + program->bind(); + program->setUniformValue("opacity", 1.0f); + program->release(); + + return program; +} + +QString olive::shader::GetAlphaDisassociateFunction(const QString &function_name) +{ + return QString("vec4 %1(vec4 col) {\n" + " if (col.a > 0.0) {\n" + " return vec4(col.rgb / col.a, col.a);" + " }\n" + " return col;\n" + "}\n").arg(function_name); +} + +QString olive::shader::GetAlphaReassociateFunction(const QString &function_name) +{ + return QString("vec4 %1(vec4 col) {\n" + " if (col.a > 0.0) {\n" + " return vec4(col.rgb * col.a, col.a);" + " }\n" + " return col;\n" + "}\n").arg(function_name); +} + +QString olive::shader::GetAlphaAssociateFunction(const QString &function_name) +{ + return QString("vec4 %1(vec4 col) {\n" + " return vec4(col.rgb * col.a, col.a);\n" + "}\n").arg(function_name); +} + +#ifndef NO_OCIO + +// copied from source code to OCIODisplay +const int OCIO_LUT3D_EDGE_SIZE = 32; + +// copied from source code to OCIODisplay, expanded from 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE +const int OCIO_NUM_3D_ENTRIES = 98304; + +QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, + GLuint& lut_texture, + OCIO::ConstProcessorRcPtr processor, + AlphaAssociateMode alpha_associate_mode) +{ + + QOpenGLExtraFunctions* xf = ctx->extraFunctions(); + + // Create LUT texture + xf->glGenTextures(1, &lut_texture); + + // Bind LUT + xf->glBindTexture(GL_TEXTURE_3D, lut_texture); + + // Set texture parameters + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + xf->glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // Allocate storage for texture + xf->glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB16F_ARB, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + 0, GL_RGB,GL_FLOAT, nullptr); + + // + // SET UP GLSL SHADER + // + + OCIO::GpuShaderDesc shaderDesc; + shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); + shaderDesc.setFunctionName("OCIODisplay"); + shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); + + // + // COMPUTE 3D LUT + // + + GLfloat* ocio_lut_data = new GLfloat[OCIO_NUM_3D_ENTRIES]; + processor->getGpuLut3D(ocio_lut_data, shaderDesc); + + // Upload LUT data to texture + xf->glTexSubImage3D(GL_TEXTURE_3D, 0, + 0, 0, 0, + OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, OCIO_LUT3D_EDGE_SIZE, + GL_RGB, GL_FLOAT, ocio_lut_data); + + delete [] ocio_lut_data; + + // Create OCIO shader code + QString shader_text(processor->getGpuShaderText(shaderDesc)); + + QString ocio_call_func; + + // Enforce alpha association + switch (alpha_associate_mode) { + case Associate: + // If alpha is not already associated, we can just associate after OCIO + + // Add associate function + shader_text.append(GetAlphaAssociateFunction("assoc")); + + // Make OCIO call pass through associate function + ocio_call_func = "assoc(OCIODisplay(col, tex2));"; + break; + case DisassociateAndReassociate: + // If alpha is already associated, we'll need to disassociate and reassociate + shader_text.append("\n"); + shader_text.append(GetAlphaDisassociateFunction("disassoc")); + shader_text.append(GetAlphaReassociateFunction("reassoc")); + + // Make OCIO call pass through disassociate and reassociate function + ocio_call_func = "reassoc(OCIODisplay(disassoc(col), tex2));"; + break; + default: + // No association + ocio_call_func = "OCIODisplay(col, tex2);"; + } + + shader_text.append(QString("\n" + "uniform sampler3D tex2;\n" + "\n" + "vec4 process(vec4 col) {\n" + " return %1\n" + "}\n").arg(ocio_call_func)); + + + // Get pipeline-based shader to inject OCIO shader into + QOpenGLShaderProgramPtr shader = olive::shader::GetPipeline(shader_text); + + // Release LUT + xf->glBindTexture(GL_TEXTURE_3D, 0); + + return shader; +} +#endif diff --git a/rendering/shadergenerators.h b/rendering/shadergenerators.h new file mode 100644 index 000000000..eacb51e83 --- /dev/null +++ b/rendering/shadergenerators.h @@ -0,0 +1,37 @@ +#ifndef SHADERGENERATORS_H +#define SHADERGENERATORS_H + +#include "qopenglshaderprogramptr.h" +#include "framebufferobject.h" + +#ifndef NO_OCIO +#include +namespace OCIO = OCIO_NAMESPACE::v1; +#endif + +namespace olive { +namespace shader { + +QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); + +#ifndef NO_OCIO +enum AlphaAssociateMode { + NoAssociate, + Associate, + DisassociateAndReassociate +}; + +QOpenGLShaderProgramPtr SetupOCIO(QOpenGLContext *ctx, + GLuint &lut_texture, + OCIO::ConstProcessorRcPtr processor, + AlphaAssociateMode alpha_associate_mode); +#endif + +QString GetAlphaDisassociateFunction(const QString& function_name); +QString GetAlphaReassociateFunction(const QString& function_name); +QString GetAlphaAssociateFunction(const QString& function_name); + +} +} + +#endif // SHADERGENERATORS_H diff --git a/timeline/clip.cpp b/timeline/clip.cpp index a2b5fd4fe..79b79de46 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -474,7 +474,7 @@ void Clip::Open() { } // reset variable used to optimize uploading frame data - texture_frame = -1; + texture_timestamp = -1; if (UsesCacher()) { // cacher will unlock open_lock @@ -558,47 +558,60 @@ bool Clip::Retrieve() if (frame != nullptr && cacher.queue()->contains(frame)) { - bool allocate_data = false; + //if (frame->pts != texture_timestamp) { + + bool allocate_data = false; - // check if the opengl texture exists yet, create it if not - if (texture == 0) { QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - // create texture object - f->glGenTextures(1, &texture); + // check if the opengl texture exists yet, create it if not + if (texture == 0) { - f->glBindTexture(GL_TEXTURE_2D, texture); + // create texture object + f->glGenTextures(1, &texture); - // set texture filtering to bilinear - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glBindTexture(GL_TEXTURE_2D, texture); - // queue an allocation ahead - allocate_data = true; + // set texture filtering to bilinear + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } + // queue an allocation ahead + allocate_data = true; - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + } else { - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); + f->glBindTexture(GL_TEXTURE_2D, texture); - int video_width = cacher.media_width(); - int video_height = cacher.media_height(); + } - if (allocate_data) { - // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure - // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the - // composition - f->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA8, video_width, video_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0] - ); - } else { - f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0]); - } + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); - f->glBindTexture(GL_TEXTURE_2D, 0); + int video_width = cacher.media_width(); + int video_height = cacher.media_height(); - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + if (allocate_data) { + + // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure + // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the + // composition + f->glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA8, video_width, video_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0] + ); + + } else { + + f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0]); + + } + + f->glBindTexture(GL_TEXTURE_2D, 0); + + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + texture_timestamp = frame->pts; + + //} ret = true; } else { diff --git a/timeline/clip.h b/timeline/clip.h index fa82abcf2..993141ebf 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -155,7 +155,7 @@ public: // video playback variables QVector fbo; GLuint texture; - long texture_frame; + int64_t texture_timestamp; #ifndef NO_OCIO QOpenGLShaderProgramPtr ocio_shader; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 81099abf3..488fc9678 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -57,6 +57,7 @@ extern "C" { #include "ui/timelinewidget.h" #include "rendering/renderfunctions.h" #include "rendering/renderthread.h" +#include "rendering/shadergenerators.h" #include "ui/viewerwindow.h" #include "ui/menu.h" #include "mainwindow.h" @@ -217,7 +218,7 @@ void ViewerWidget::initializeGL() { connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(context_destroy()), Qt::DirectConnection); - pipeline_ = olive::rendering::GetPipeline(); + pipeline_ = olive::shader::GetPipeline(); vao_.create(); diff --git a/ui/viewerwindow.cpp b/ui/viewerwindow.cpp index a6c5308eb..43b0fc595 100644 --- a/ui/viewerwindow.cpp +++ b/ui/viewerwindow.cpp @@ -31,6 +31,7 @@ #include #include "rendering/renderfunctions.h" +#include "rendering/shadergenerators.h" #include "ui/mainwindow.h" ViewerWindow::ViewerWindow(QWidget *parent) : @@ -109,7 +110,7 @@ void ViewerWindow::mouseMoveEvent(QMouseEvent *) { void ViewerWindow::initializeGL() { - pipeline_ = olive::rendering::GetPipeline(); + pipeline_ = olive::shader::GetPipeline(); } void ViewerWindow::paintGL() { From 5d5cd2d5958d17dc4930e62824f89b761a1d8d27 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 12:10:20 +1100 Subject: [PATCH 043/133] fixed some license indentation --- dialogs/autocutsilencedialog.cpp | 24 ++++++++++++------------ dialogs/autocutsilencedialog.h | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/dialogs/autocutsilencedialog.cpp b/dialogs/autocutsilencedialog.cpp index b1d801b99..4a877965f 100644 --- a/dialogs/autocutsilencedialog.cpp +++ b/dialogs/autocutsilencedialog.cpp @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ diff --git a/dialogs/autocutsilencedialog.h b/dialogs/autocutsilencedialog.h index 32c572e16..37fe57a61 100644 --- a/dialogs/autocutsilencedialog.h +++ b/dialogs/autocutsilencedialog.h @@ -1,20 +1,20 @@ /*** - Olive - Non-Linear Video Editor - Copyright (C) 2019 Olive Team + 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 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. + 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 . + You should have received a copy of the GNU General Public License + along with this program. If not, see . ***/ From 3083e3920bb385f6dd5743fda37120d1782da6cc Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 12:13:32 +1100 Subject: [PATCH 044/133] improved autocut silence dialog --- dialogs/autocutsilencedialog.cpp | 27 ++++++++++----------------- dialogs/autocutsilencedialog.h | 4 ++-- global/global.cpp | 2 +- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/dialogs/autocutsilencedialog.cpp b/dialogs/autocutsilencedialog.cpp index 4a877965f..66f6110ab 100644 --- a/dialogs/autocutsilencedialog.cpp +++ b/dialogs/autocutsilencedialog.cpp @@ -31,7 +31,7 @@ #include "panels/panels.h" #include "panels/timeline.h" -AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector clips) : +AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector clips) : QDialog(parent), clips_(clips) { @@ -119,10 +119,12 @@ void AutoCutSilenceDialog::accept() { } void AutoCutSilenceDialog::cut_silence() { + ComboAction* ca = new ComboAction(); + // Loop over clips provided to this dialog for (int j=0;jclips.at(clips_.at(j)).get(); // Check if this clip is an audio footage clip if (clip->track() >= 0 @@ -200,23 +202,14 @@ void AutoCutSilenceDialog::cut_silence() { } } - ComboAction* ca = new ComboAction(); - - // NO GOOD VERY BAD TEST CODE - int clip_index = -1; - for (int i=0;iclips.size();i++) { - if (olive::ActiveSequence->clips.at(i).get() == clip) { - clip_index = i; - break; - } - } - - Q_ASSERT(clip_index > -1); - - panel_timeline->split_clip_at_positions(ca, clip_index, split_positions); - olive::UndoStack.push(ca); + panel_timeline->split_clip_at_positions(ca, clips_.at(j), split_positions); } + } + if (ca->hasActions()) { + olive::UndoStack.push(ca); + } else { + delete ca; } } diff --git a/dialogs/autocutsilencedialog.h b/dialogs/autocutsilencedialog.h index 37fe57a61..3b379f5ae 100644 --- a/dialogs/autocutsilencedialog.h +++ b/dialogs/autocutsilencedialog.h @@ -31,7 +31,7 @@ class AutoCutSilenceDialog : public QDialog { Q_OBJECT public: - AutoCutSilenceDialog(QWidget* parent, QVector clips); + AutoCutSilenceDialog(QWidget* parent, QVector clips); public slots: virtual int exec() override; private slots: @@ -39,7 +39,7 @@ private slots: private: void cut_silence(); - QVector clips_; + QVector clips_; LabelSlider* attack_threshold; LabelSlider* release_threshold; diff --git a/global/global.cpp b/global/global.cpp index 4c1913df8..f91c6b91b 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -441,7 +441,7 @@ void OliveGlobal::open_speed_dialog() { void OliveGlobal::open_autocut_silence_dialog() { if (CheckForActiveSequence()) { - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = olive::ActiveSequence->SelectedClipIndexes(); if (selected_clips.isEmpty()) { QMessageBox::critical(olive::MainWindow, From d8acef21a8fdaaa81d3571c2d401ee9485a6f507 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 21:42:45 +1100 Subject: [PATCH 045/133] better support for various bit rates --- dialogs/exportdialog.cpp | 8 +- dialogs/mediapropertiesdialog.cpp | 20 +-- dialogs/mediapropertiesdialog.h | 2 +- dialogs/preferencesdialog.cpp | 254 +++++++++++++++------------- dialogs/preferencesdialog.h | 8 +- effects/effect.cpp | 4 +- effects/effectfield.cpp | 1 + effects/internal/timecodeeffect.cpp | 2 +- global/config.cpp | 15 +- global/config.h | 17 +- global/global.cpp | 12 +- global/global.h | 17 +- global/timing.cpp | 169 ++++++++++++++++++ global/timing.h | 137 +++++++++++++++ olive.pro | 21 +-- panels/effectcontrols.cpp | 4 +- panels/project.h | 3 - panels/timeline.cpp | 1 + panels/viewer.cpp | 174 +++---------------- panels/viewer.h | 10 +- project/footage.cpp | 24 +++ project/footage.h | 10 +- project/loadthread.cpp | 3 + project/media.cpp | 2 +- project/sourcescommon.cpp | 4 +- rendering/audio.cpp | 3 +- rendering/audio.h | 1 - rendering/bitdepths.cpp | 33 ++-- rendering/bitdepths.h | 35 +++- rendering/cacher.cpp | 34 +++- rendering/cacher.h | 15 ++ rendering/exportthread.cpp | 8 +- rendering/framebufferobject.cpp | 21 ++- rendering/renderfunctions.cpp | 126 ++++---------- rendering/renderfunctions.h | 142 ---------------- rendering/renderthread.cpp | 38 +---- rendering/renderthread.h | 9 +- rendering/shadergenerators.cpp | 50 +++--- rendering/shadergenerators.h | 13 +- timeline/clip.cpp | 11 +- timeline/clip.h | 5 - timeline/sequence.cpp | 12 +- timeline/sequence.h | 15 +- ui/focusfilter.cpp | 4 +- ui/labelslider.cpp | 2 +- ui/mainwindow.cpp | 6 +- ui/menuhelper.cpp | 2 +- ui/timelineheader.cpp | 3 +- ui/timelinewidget.cpp | 1 + ui/viewerwidget.cpp | 5 +- undo/undo.cpp | 8 +- 51 files changed, 812 insertions(+), 712 deletions(-) create mode 100644 global/timing.cpp create mode 100644 global/timing.h diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index ac4ba0a73..4b36513f9 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -351,8 +351,8 @@ void ExportDialog::export_thread_finished() { prep_ui_for_render(false); // Move OpenGL context back to the sequence viewer - panel_sequence_viewer->viewer_widget->makeCurrent(); - panel_sequence_viewer->viewer_widget->initializeGL(); + panel_sequence_viewer->viewer_widget()->makeCurrent(); + panel_sequence_viewer->viewer_widget()->initializeGL(); // Update the application UI update_ui(false); @@ -573,9 +573,9 @@ void ExportDialog::StartExport() { panel_effect_controls->Clear(); // Close all currently open clips - close_active_clips(olive::ActiveSequence.get()); + olive::ActiveSequence->Close(); - olive::Global->set_rendering_state(true); + olive::Global->set_export_state(true); olive::Global->save_autorecovery_file(); diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index 150336c8b..45832cf79 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -30,10 +30,8 @@ #include #include #include -#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE::v1; -#endif #include "project/footage.h" #include "project/media.h" @@ -133,26 +131,26 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : row++; -#ifndef NO_OCIO - color_management = new QComboBox(this); + input_color_space = new QComboBox(this); OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + QString footage_colorspace = f->Colorspace(); + for (int i=0;igetNumColorSpaces();i++) { QString colorspace = config->getColorSpaceNameByIndex(i); - color_management->addItem(colorspace); + input_color_space->addItem(colorspace); - if (colorspace == f->colorspace) { - color_management->setCurrentIndex(i); + if (colorspace == footage_colorspace) { + input_color_space->setCurrentIndex(i); } } grid->addWidget(new QLabel(tr("Color Space:")), row, 0); - grid->addWidget(color_management, row, 1); + grid->addWidget(input_color_space, row, 1); row++; -#endif } @@ -221,9 +219,7 @@ void MediaPropertiesDialog::accept() { f->alpha_is_associated = premultiply_alpha_setting->isChecked(); } -#ifndef NO_OCIO - f->colorspace = color_management->currentText(); -#endif + f->SetColorspace(input_color_space->currentText()); // set name MediaRename* mr = new MediaRename(item, name_box->text()); diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index f18dcee9a..8f9d7ebd5 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -83,7 +83,7 @@ private: */ QCheckBox* premultiply_alpha_setting; - QComboBox* color_management; + QComboBox* input_color_space; private slots: /** * @brief Overrided accept function for saving the properties back to the Media class diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index e6ecf7405..9bf70b3b9 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -168,7 +168,6 @@ void PreferencesDialog::delete_previews(PreviewDeleteTypes type) { } } -#ifndef NO_OCIO void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) { // Get current display name (if the config is empty, get the current default display) @@ -240,7 +239,6 @@ void PreferencesDialog::update_ocio_config(const QString &s) } catch (OCIO::Exception& e) {} } } -#endif void PreferencesDialog::AddBoolPair(QCheckBox *ui, bool *value, bool restart_required) { @@ -279,6 +277,8 @@ void PreferencesDialog::accept() { bool reinit_audio = false; bool reload_language = false; bool reload_effects = false; + bool reset_ocio_shaders = false; + bool reset_render_threads = false; // Validate whether the specified CSS file exists if (!custom_css_fn->text().isEmpty() && !QFileInfo::exists(custom_css_fn->text())) { @@ -290,31 +290,45 @@ void PreferencesDialog::accept() { return; } - // Validate whether the OCIO config path exists - if (enable_color_management->isChecked() && !QFileInfo::exists(ocio_config_file->text())) { + // Validate whether the chosen OCIO configuration file + if (enable_color_management->isChecked()) { - QString msg_title = tr("Invalid OpenColorIO Configuration File"); - QString msg_body; + // Check whether the file exists + if (!QFileInfo::exists(ocio_config_file->text())) { + + QString msg_title = tr("Invalid OpenColorIO Configuration File"); + QString msg_body; + + if (ocio_config_file->text().isEmpty()) { + msg_body = tr("You must specify an OpenColorIO configuration file if color management is enabled."); + } else { + msg_body = tr("OpenColorIO configuration file '%1' does not exist.").arg(ocio_config_file->text()); + } + + QMessageBox::critical( + this, + msg_title, + msg_body + ); + return; + + } else if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { + + // Check whether OCIO can load it + try { + OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + return; + } - if (ocio_config_file->text().isEmpty()) { - msg_body = tr("You must specify an OpenColorIO configuration file if color management is enabled."); - } else { - msg_body = tr("OpenColorIO configuration file '%1' does not exist.").arg(ocio_config_file->text()); } - - QMessageBox::critical( - this, - msg_title, - msg_body - ); - return; - } - - // Validate whether the effects panel should refresh itself - if (olive::CurrentConfig.effect_textbox_lines != effect_textbox_lines_field->value()) { - reload_effects = true; } + // Validate whether one of the bool options requires a restart bool bool_requires_restart = false; for (int i=0;ivalue() || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value() @@ -355,20 +369,8 @@ void PreferencesDialog::accept() { } - // Audio settings may require the audio device to be re-initiated. - if (olive::CurrentConfig.preferred_audio_output != audio_output_devices->currentData().toString() - || olive::CurrentConfig.preferred_audio_input != audio_input_devices->currentData().toString() - || olive::CurrentConfig.audio_rate != audio_sample_rate->currentData().toInt()) { - reinit_audio = true; - } - // see if the language file should be reloaded (not necessary if the app is restarting anyway) - if (!restart_after_saving - && olive::CurrentConfig.language_file != language_combobox->currentData().toString()) { - reload_language = true; - } - - // save settings from UI to backend + // Everything checks out, start saving settings from the UI to the backend olive::CurrentConfig.css_path = custom_css_fn->text(); olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1; olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text(); @@ -377,53 +379,66 @@ void PreferencesDialog::accept() { olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value(); olive::CurrentConfig.previous_queue_type = previous_queue_type->currentIndex(); + // Audio settings may require the audio device to be re-initiated. + if (olive::CurrentConfig.preferred_audio_output != audio_output_devices->currentData().toString() + || olive::CurrentConfig.preferred_audio_input != audio_input_devices->currentData().toString() + || olive::CurrentConfig.audio_rate != audio_sample_rate->currentData().toInt()) { + reinit_audio = true; + } olive::CurrentConfig.preferred_audio_output = audio_output_devices->currentData().toString(); olive::CurrentConfig.preferred_audio_input = audio_input_devices->currentData().toString(); olive::CurrentConfig.audio_rate = audio_sample_rate->currentData().toInt(); olive::CurrentConfig.effect_textbox_lines = effect_textbox_lines_field->value(); + + // see if the language file should be reloaded (not necessary if the app is restarting anyway) + if (!restart_after_saving + && olive::CurrentConfig.language_file != language_combobox->currentData().toString()) { + reload_language = true; + } olive::CurrentConfig.language_file = language_combobox->currentData().toString(); - olive::CurrentConfig.enable_color_management = enable_color_management->isChecked(); - -#ifndef NO_OCIO - if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { - try { - OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8())); - - olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); - } catch (OCIO::Exception& e) { - QMessageBox::critical(this, - tr("OpenColorIO Config Error"), - tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), - QMessageBox::Ok); - } - + // Check whether OCIO settings will require a reset of the render threads + if (olive::CurrentConfig.playback_bit_depth != playback_bit_depth->currentIndex() + || olive::CurrentConfig.export_bit_depth != export_bit_depth->currentIndex()) { + reset_render_threads = true; } - + if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text() + || olive::CurrentConfig.ocio_display != ocio_display->currentText() + || olive::CurrentConfig.ocio_view != ocio_view->currentText() + || olive::CurrentConfig.ocio_look != ocio_look->currentData().toString()) { + reset_ocio_shaders = true; + } + if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { + OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8())); + olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); + } + olive::CurrentConfig.enable_color_management = enable_color_management->isChecked(); + olive::CurrentConfig.playback_bit_depth = playback_bit_depth->currentIndex(); + olive::CurrentConfig.export_bit_depth = export_bit_depth->currentIndex(); olive::CurrentConfig.ocio_display = ocio_display->currentText(); olive::CurrentConfig.ocio_view = ocio_view->currentText(); // We use data here instead of text because there's a "(None)" option with an empty string olive::CurrentConfig.ocio_look = ocio_look->currentData().toString(); - olive::CurrentRuntimeConfig.ocio_config_date = QDateTime::currentMSecsSinceEpoch(); -#endif - + // Set default sequence options olive::CurrentConfig.default_sequence_width = default_sequence.width; olive::CurrentConfig.default_sequence_height = default_sequence.height; olive::CurrentConfig.default_sequence_framerate = default_sequence.frame_rate; olive::CurrentConfig.default_sequence_audio_frequency = default_sequence.audio_frequency; olive::CurrentConfig.default_sequence_audio_channel_layout = default_sequence.audio_layout; + // Set all bool options for (int i=0;iisChecked(); } + // Set new style olive::CurrentConfig.style = static_cast(ui_style->currentData().toInt()); - // Check if the thumbnail or waveform icon + // Check if the thumbnail or waveform icon fields have changed, we may need to recreate the previews if so if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) { // we're changing the size of thumbnails and waveforms, so let's delete them and regenerate them next start @@ -461,29 +476,46 @@ void PreferencesDialog::accept() { key_shortcut_fields.at(i)->set_action_shortcut(); } - // Audio settings may require the audio device to be re-initiated. - if (reinit_audio) { - init_audio(); - } - - if (reload_effects) { - panel_effect_controls->Reload(); - } - - // reload language file if it changed - if (reload_language) { - olive::Global->load_translation_from_config(); - } - QDialog::accept(); if (restart_after_saving) { + // since we already ran can_close_project(), bypass checking again by running set_modified(false) olive::Global->set_modified(false); olive::MainWindow->close(); QProcess::startDetached(QApplication::applicationFilePath(), { olive::ActiveProjectFilename }); + } else { + + // Audio settings may require the audio device to be re-initiated. + if (reinit_audio) { + init_audio(); + } + + if (reload_effects) { + panel_effect_controls->Reload(); + } + + // reload language file if it changed + if (reload_language) { + olive::Global->load_translation_from_config(); + } + + if (reset_render_threads) { + if (panel_footage_viewer->seq != nullptr) { + panel_footage_viewer->seq->Close(); + } + panel_footage_viewer->viewer_widget()->get_renderer()->delete_ctx(); + if (panel_sequence_viewer->seq != nullptr) { + panel_sequence_viewer->seq->Close(); + } + panel_sequence_viewer->viewer_widget()->get_renderer()->delete_ctx(); + } else if (reset_ocio_shaders) { + panel_footage_viewer->viewer_widget()->get_renderer()->destroy_ocio(); + panel_sequence_viewer->viewer_widget()->get_renderer()->destroy_ocio(); + } + } } @@ -618,12 +650,10 @@ void PreferencesDialog::browse_ocio_config() } } -#ifndef NO_OCIO void PreferencesDialog::update_ocio_view_menu() { update_ocio_view_menu(OCIO::GetCurrentConfig()); } -#endif void PreferencesDialog::delete_all_previews() { if (QMessageBox::question(this, @@ -988,87 +1018,77 @@ void PreferencesDialog::setup_ui() { row = 0; -#ifdef NO_OCIO - QLabel* no_ocio_available_lbl = new QLabel(tr("Color management is unavailable because Olive was " - "compiled without OpenColorIO support.")); - no_ocio_available_lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); - color_management_layout->addWidget(no_ocio_available_lbl, row, 0, 1, 3); - row++; -#endif - // COLOR MANAGEMENT -> Enable Color Management enable_color_management = new QCheckBox(tr("Enable Color Management")); enable_color_management->setChecked(olive::CurrentConfig.enable_color_management); - color_management_layout->addWidget(enable_color_management, row, 0, 1, 3); + color_management_layout->addWidget(enable_color_management, row, 0); row++; + QGroupBox* opencolorio_groupbox = new QGroupBox(); + QGridLayout* opencolorio_groupbox_layout = new QGridLayout(opencolorio_groupbox); + // COLOR MANAGEMENT -> OpenColorIO Config File - color_management_layout->addWidget(new QLabel(tr("OpenColorIO Config File:")), row, 0); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("OpenColorIO Config File:")), 0, 0); ocio_config_file = new QLineEdit(); ocio_config_file->setText(olive::CurrentConfig.ocio_config_path); connect(ocio_config_file, SIGNAL(textChanged(const QString &)), this, SLOT(update_ocio_config(const QString&))); - color_management_layout->addWidget(ocio_config_file, row, 1); + opencolorio_groupbox_layout->addWidget(ocio_config_file, 0, 1, 1, 4); QPushButton* ocio_config_browse_btn = new QPushButton(tr("Browse")); connect(ocio_config_browse_btn, SIGNAL(clicked(bool)), this, SLOT(browse_ocio_config())); - color_management_layout->addWidget(ocio_config_browse_btn, row, 2); - - row++; + opencolorio_groupbox_layout->addWidget(ocio_config_browse_btn, 0, 5); // COLOR MANAGEMENT -> Display ocio_display = new QComboBox(); connect(ocio_display, SIGNAL(currentIndexChanged(int)), this, SLOT(update_ocio_view_menu())); - color_management_layout->addWidget(new QLabel("Display:"), row, 0); - color_management_layout->addWidget(ocio_display, row, 1); - - row++; + opencolorio_groupbox_layout->addWidget(new QLabel(tr("Display:")), 1, 0); + opencolorio_groupbox_layout->addWidget(ocio_display, 1, 1); // COLOR MANAGEMENT -> View ocio_view = new QComboBox(); - color_management_layout->addWidget(new QLabel("View:"), row, 0); - color_management_layout->addWidget(ocio_view, row, 1); - - row++; + opencolorio_groupbox_layout->addWidget(new QLabel(tr("View:")), 1, 2); + opencolorio_groupbox_layout->addWidget(ocio_view, 1, 3); // COLOR MANAGEMENT -> Look ocio_look = new QComboBox(); - color_management_layout->addWidget(new QLabel("Look:"), row, 0); - color_management_layout->addWidget(ocio_look, row, 1); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("Look:")), 1, 4); + opencolorio_groupbox_layout->addWidget(ocio_look, 1, 5); + + color_management_layout->addWidget(opencolorio_groupbox, row, 0); row++; - // COLOR MANAGEMENT -> Playback Bit Depth - QComboBox* playback_bit_depth = new QComboBox(); + // COLOR MANAGEMENT -> Bit Depth + QGroupBox* bit_depth_groupbox = new QGroupBox(tr("Bit Depth")); + QGridLayout* bit_depth_groupbox_layout = new QGridLayout(bit_depth_groupbox); + + // COLOR MANAGEMENT -> Bit Depth -> Playback + playback_bit_depth = new QComboBox(); for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); } - color_management_layout->addWidget(new QLabel("Playback Bit Depth:"), row, 0); - color_management_layout->addWidget(playback_bit_depth, row, 1); + playback_bit_depth->setCurrentIndex(olive::CurrentConfig.playback_bit_depth); + bit_depth_groupbox_layout->addWidget(new QLabel(tr("Playback (Offline):")), 0, 0); + bit_depth_groupbox_layout->addWidget(playback_bit_depth, 0, 1); - row++; - - // COLOR MANAGEMENT -> Rendering Bit Depth - QComboBox* rendering_bit_depth = new QComboBox(); + // COLOR MANAGEMENT -> Bit Depth -> Export + export_bit_depth = new QComboBox(); for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); + export_bit_depth->addItem(olive::rendering::bit_depths.at(i).name, i); } - color_management_layout->addWidget(new QLabel("Rendering Bit Depth:"), row, 0); - color_management_layout->addWidget(rendering_bit_depth, row, 1); + export_bit_depth->setCurrentIndex(olive::CurrentConfig.export_bit_depth); + bit_depth_groupbox_layout->addWidget(new QLabel(tr("Export (Online):")), 0, 2); + bit_depth_groupbox_layout->addWidget(export_bit_depth, 0, 3); - row++; + color_management_layout->addWidget(bit_depth_groupbox, row, 0); + + + + //row++; -#ifdef NO_OCIO - enable_color_management->setEnabled(false); - ocio_config_file->setEnabled(false); - ocio_config_browse_btn->setEnabled(false); - ocio_display->setEnabled(false); - ocio_view->setEnabled(false); - ocio_look->setEnabled(false); -#else populate_ocio_menus(OCIO::GetCurrentConfig()); -#endif tabWidget->addTab(color_management_tab, tr("Color Management")); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 157cf01d8..a7cb00c9a 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -33,10 +33,8 @@ #include #include #include -#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE::v1; -#endif #include "timeline/sequence.h" @@ -125,11 +123,9 @@ private slots: void browse_ocio_config(); // OCIO function -#ifndef NO_OCIO void update_ocio_view_menu(); void update_ocio_view_menu(OCIO::ConstConfigRcPtr config); void update_ocio_config(const QString&); -#endif /** * @brief Shows a NewSequenceDialog attached to default_sequence @@ -188,9 +184,7 @@ private: */ void delete_previews(PreviewDeleteTypes type); -#ifndef NO_OCIO void populate_ocio_menus(OCIO::ConstConfigRcPtr config); -#endif /** * @brief UI widget for editing the CSS filename @@ -273,6 +267,8 @@ private: QComboBox* ocio_display; QComboBox* ocio_view; QComboBox* ocio_look; + QComboBox* playback_bit_depth; + QComboBox* export_bit_depth; /** * @brief UI widget for selecting the current UI style diff --git a/effects/effect.cpp b/effects/effect.cpp index 5ee89d7dc..38b9f2344 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -424,7 +424,7 @@ void Effect::move_up() { command->to = command->from - 1; olive::UndoStack.push(command); panel_effect_controls->Reload(); - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } void Effect::move_down() { @@ -439,7 +439,7 @@ void Effect::move_down() { command->to = command->from + 1; olive::UndoStack.push(command); panel_effect_controls->Reload(); - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } void Effect::save_to_file() { diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index f06e94533..61d6a0431 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -25,6 +25,7 @@ #include "rendering/renderfunctions.h" #include "global/config.h" +#include "global/timing.h" #include "effects/effectrow.h" #include "effects/effect.h" #include "undo/undo.h" diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 519cdefcb..84276fc34 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -37,7 +37,7 @@ #include "ui/collapsiblewidget.h" #include "timeline/clip.h" #include "timeline/sequence.h" -#include "panels/viewer.h" +#include "global/timing.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" #include "global/config.h" diff --git a/global/config.cpp b/global/config.cpp index 295fdac05..e28ccbe1d 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -79,7 +79,9 @@ Config::Config() default_sequence_height(1080), default_sequence_framerate(29.97), default_sequence_audio_frequency(48000), - default_sequence_audio_channel_layout(3) + default_sequence_audio_channel_layout(3), + playback_bit_depth(olive::rendering::PIX_FMT_RGBA16F), + export_bit_depth(olive::rendering::PIX_FMT_RGBA32F) {} void Config::load(QString path) { @@ -246,6 +248,12 @@ void Config::load(QString path) { } else if (stream.name() == "DefaultSequenceAudioLayout") { stream.readNext(); default_sequence_audio_channel_layout = stream.text().toInt(); + } else if (stream.name() == "PlaybackBitDepth") { + stream.readNext(); + playback_bit_depth = stream.text().toInt(); + } else if (stream.name() == "ExportBitDepth") { + stream.readNext(); + export_bit_depth = stream.text().toInt(); } } } @@ -322,6 +330,8 @@ void Config::save(QString path) { stream.writeTextElement("DefaultSequenceFrameRate", QString::number(default_sequence_framerate)); stream.writeTextElement("DefaultSequenceAudioFrequency", QString::number(default_sequence_audio_frequency)); stream.writeTextElement("DefaultSequenceAudioLayout", QString::number(default_sequence_audio_channel_layout)); + stream.writeTextElement("PlaybackBitDepth", QString::number(playback_bit_depth)); + stream.writeTextElement("ExportBitDepth", QString::number(export_bit_depth)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc @@ -329,6 +339,5 @@ void Config::save(QString path) { } RuntimeConfig::RuntimeConfig() : - shaders_are_enabled(true), - ocio_config_date(QDateTime::currentMSecsSinceEpoch()) + shaders_are_enabled(true) {} diff --git a/global/config.h b/global/config.h index 3d192cdad..2f9ebd9e6 100644 --- a/global/config.h +++ b/global/config.h @@ -593,6 +593,16 @@ struct Config { */ int default_sequence_audio_channel_layout; + /** + * @brief Playback bit depth (an index of olive::rendering::bit_depths) + */ + int playback_bit_depth; + + /** + * @brief Export bit depth (an index of olive::rendering::bit_depths) + */ + int export_bit_depth; + /** * @brief Load config from file * @@ -644,13 +654,6 @@ struct RuntimeConfig { */ QString external_translation_file; - /** - * @brief OpenColorIO Configuration Time - * - * A crude but quick way of determining whether the OCIO config has changed and if the rendering threads need to - * re-create their OCIO shaders. Not intended to be saved - could be moved - */ - qint64 ocio_config_date; }; namespace olive { diff --git a/global/global.cpp b/global/global.cpp index f91c6b91b..a32015b26 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -52,7 +52,8 @@ QString olive::ActiveProjectFilename; QString olive::AppName; OliveGlobal::OliveGlobal() : - changed_since_last_autorecovery(false) + changed_since_last_autorecovery(false), + rendering_(false) { // sets current app name QString version_id; @@ -108,8 +109,13 @@ void OliveGlobal::check_for_autorecovery_file() { } } -void OliveGlobal::set_rendering_state(bool rendering) { - audio_rendering = rendering; +bool OliveGlobal::is_exporting() +{ + return rendering_; +} + +void OliveGlobal::set_export_state(bool rendering) { + rendering_ = rendering; if (rendering) { autorecovery_timer.stop(); } else { diff --git a/global/global.h b/global/global.h index ab5f342a4..050a28bc8 100644 --- a/global/global.h +++ b/global/global.h @@ -74,6 +74,16 @@ public: */ void check_for_autorecovery_file(); + /** + * @brief Get whether the project is currently being rendered or not. Useful for determining whether to treat the + * render as online or offline. + * + * @return + * + * TRUE if the project is being exported, FALSE if not. + */ + bool is_exporting(); + /** * @brief Set the application state depending on if the user is exporting a video * @@ -90,7 +100,7 @@ public: * * **TRUE** if Olive is about to export a video. **FALSE** if Olive has finished exporting. */ - void set_rendering_state(bool rendering); + void set_export_state(bool rendering); /** * @brief Set the application's "modified" state @@ -423,6 +433,11 @@ private: */ bool changed_since_last_autorecovery; + /** + * @brief Internal variable for rendering state (set by set_rendering_state() and accessed by is_rendering() ). + */ + bool rendering_; + private slots: }; diff --git a/global/timing.cpp b/global/timing.cpp new file mode 100644 index 000000000..b14315734 --- /dev/null +++ b/global/timing.cpp @@ -0,0 +1,169 @@ +#include "timing.h" + +#include "timeline/sequence.h" +#include "timeline/clip.h" +#include "global/config.h" + +double get_timecode(Clip* c, long playhead) { + return double(playhead_to_clip_frame(c, playhead))/c->sequence->frame_rate; +} + +long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { + return qRound((double(framenumber)/source_frame_rate)*target_frame_rate); +} + +long playhead_to_clip_frame(Clip* c, long playhead) { + return (qMax(0L, playhead - c->timeline_in(true)) + c->clip_in(true)); +} + +double playhead_to_clip_seconds(Clip* c, long playhead) { + // returns time in seconds + long clip_frame = playhead_to_clip_frame(c, playhead); + + if (c->reversed()) { + clip_frame = c->media_length() - clip_frame - 1; + } + + double secs = (double(clip_frame)/c->sequence->frame_rate)*c->speed().value; + if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { + secs *= c->media()->to_footage()->speed; + } + + return secs; +} + +int64_t seconds_to_timestamp(Clip *c, double seconds) { + return qRound64(seconds * av_q2d(av_inv_q(c->time_base()))); +} + +int64_t playhead_to_timestamp(Clip* c, long playhead) { + return seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead)); +} + +bool frame_rate_is_droppable(double rate) { + return (qFuzzyCompare(rate, 23.976) + || qFuzzyCompare(rate, 29.97) + || qFuzzyCompare(rate, 59.94)); +} + +long timecode_to_frame(const QString& s, int view, double frame_rate) { + QList list = s.split(QRegExp("[:;]")); + + if (view == olive::kTimecodeFrames || (list.size() == 1 && view != olive::kTimecodeMilliseconds)) { + return s.toLong(); + } + + int frRound = qRound(frame_rate); + int hours, minutes, seconds, frames; + + if (view == olive::kTimecodeMilliseconds) { + long milliseconds = s.toLong(); + + hours = milliseconds/3600000; + milliseconds -= (hours*3600000); + minutes = milliseconds/60000; + milliseconds -= (minutes*60000); + seconds = milliseconds/1000; + milliseconds -= (seconds*1000); + frames = qRound64((milliseconds*0.001)*frame_rate); + + seconds = qRound64(seconds * frame_rate); + minutes = qRound64(minutes * frame_rate * 60); + hours = qRound64(hours * frame_rate * 3600); + } else { + hours = ((list.size() > 0) ? list.at(0).toInt() : 0) * frRound * 3600; + minutes = ((list.size() > 1) ? list.at(1).toInt() : 0) * frRound * 60; + seconds = ((list.size() > 2) ? list.at(2).toInt() : 0) * frRound; + frames = (list.size() > 3) ? list.at(3).toInt() : 0; + } + + int f = (frames + seconds + minutes + hours); + + if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(frame_rate)) { + // return drop + int d; + int m; + + int dropFrames = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes + int framesPerMinute = (qRound(frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames + + d = f / framesPer10Minutes; + f -= dropFrames*9*d; + + m = f % framesPer10Minutes; + + if (m > dropFrames) { + f -= (dropFrames * ((m - dropFrames) / framesPerMinute)); + } + } + + // return non-drop + return f; +} + +QString frame_to_timecode(long f, int view, double frame_rate) { + if (view == olive::kTimecodeFrames) { + return QString::number(f); + } + + // return timecode + int hours = 0; + int mins = 0; + int secs = 0; + int frames = 0; + QString token = ":"; + + if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(frame_rate)) { + //CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE + //Code by David Heidelberger, adapted from Andrew Duncan, further adapted for Olive by Olive Team + //Given an int called framenumber and a double called framerate + //Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. + + int d; + int m; + + int dropFrames = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int framesPerHour = qRound(frame_rate*60*60); //Number of frqRound64ames in an hour + int framesPer24Hours = framesPerHour*24; //Number of frames in a day - timecode rolls over after 24 hours + int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes + int framesPerMinute = (qRound(frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames + + //If framenumber is greater than 24 hrs, next operation will rollover clock + f = f % framesPer24Hours; // % is the modulus operator, which returns a remainder. a % b = the remainder of a/b + + d = f / framesPer10Minutes; // \ means integer division, which is a/b without a remainder. Some languages you could use floor(a/b) + m = f % framesPer10Minutes; + + //In the original post, the next line read m>1, which only worked for 29.97. Jean-Baptiste Mardelle correctly pointed out that m should be compared to dropFrames. + if (m > dropFrames) { + f = f + (dropFrames*9*d) + dropFrames * ((m - dropFrames) / framesPerMinute); + } else { + f = f + dropFrames*9*d; + } + + int frRound = qRound(frame_rate); + frames = f % frRound; + secs = (f / frRound) % 60; + mins = ((f / frRound) / 60) % 60; + hours = (((f / frRound) / 60) / 60); + + token = ";"; + } else { + // non-drop timecode + + int int_fps = qRound(frame_rate); + hours = f / (3600 * int_fps); + mins = f / (60*int_fps) % 60; + secs = f/int_fps % 60; + frames = f%int_fps; + } + if (view == olive::kTimecodeMilliseconds) { + return QString::number((hours*3600000)+(mins*60000)+(secs*1000)+qCeil(frames*1000/frame_rate)); + } + return QString(QString::number(hours).rightJustified(2, '0') + + ":" + QString::number(mins).rightJustified(2, '0') + + ":" + QString::number(secs).rightJustified(2, '0') + + token + QString::number(frames).rightJustified(2, '0') + ); +} diff --git a/global/timing.h b/global/timing.h new file mode 100644 index 000000000..1744b8c92 --- /dev/null +++ b/global/timing.h @@ -0,0 +1,137 @@ +#ifndef TIMING_H +#define TIMING_H + +#include +#include + +class Clip; + +/** + * @brief Get timecode + * + * Get the current clip/media time from the Timeline playhead in seconds. For instance if the playhead was at the start + * of a clip (whose in point wasn't trimmed), this would be 0.0 as it's the start of the clip/media; + * + * @param c + * + * Clip to get the timecode of + * + * @param playhead + * + * Sequence playhead to convert to a clip/media timecode + * + * @return + * + * Timecode in seconds + */ +double get_timecode(Clip *c, long playhead); + +/** + * @brief Rescale a frame number between two frame rates + * + * Converts a frame number from one frame rate to its equivalent in another frame rate + * + * @param framenumber + * + * The frame number to convert + * + * @param source_frame_rate + * + * Frame rate that the frame number is currently in + * + * @param target_frame_rate + * + * Frame rate to convert to + * + * @return + * + * Rescaled frame number + */ +long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); + +/** + * @brief Convert playhead frame number to a clip frame number + * + * Converts a Timeline playhead to a the current clip's frame. Equivalent to + * `PLAYHEAD - CLIP_TIMELINE_IN + CLIP_MEDIA_IN`. All keyframes are in clip frames. + * + * @param c + * + * The clip to get the current frame number of + * + * @param playhead + * + * The current Timeline frame number + * + * @return + * + * The curren frame number of the clip at `playhead` + */ +long playhead_to_clip_frame(Clip* c, long playhead); + +/** + * @brief Converts the playhead to clip seconds + * + * Get the current timecode at the playhead in terms of clip seconds. + * + * FIXME: Possible duplicate of get_timecode()? Will need to research this more. + * + * @param c + * + * Clip to return clip seconds of. + * + * @param playhead + * + * Current Timeline playhead to convert to clip seconds + * + * @return + * + * Clip time in seconds + */ +double playhead_to_clip_seconds(Clip *c, long playhead); + +/** + * @brief Convert seconds to FFmpeg timestamp + * + * Used for interaction with FFmpeg, converts seconds in a floating-point value to a timestamp in AVStream->time_base + * units. + * + * @param c + * + * Clip to get timestamp of + * + * @param seconds + * + * Clip time in seconds + * + * @return + * + * An FFmpeg-compatible timestamp in AVStream->time_base units. + */ +int64_t seconds_to_timestamp(Clip* c, double seconds); + +/** + * @brief Convert Timeline playhead to FFmpeg timestamp + * + * Used for interaction with FFmpeg, converts the Timeline playhead to a timestamp in AVStream->time_base + * units. + * + * @param c + * + * Clip to get timestamp of + * + * @param playhead + * + * Timeline playhead to convert to a timestamp + * + * @return + * + * An FFmpeg-compatible timestamp in AVStream->time_base units. + */ +int64_t playhead_to_timestamp(Clip *c, long playhead); + +bool frame_rate_is_droppable(double rate); +long timecode_to_frame(const QString& s, int view, double frame_rate); +QString frame_to_timecode(long f, int view, double frame_rate); + +#endif // TIMING_H diff --git a/olive.pro b/olive.pro index 299cf59b4..58b62e807 100644 --- a/olive.pro +++ b/olive.pro @@ -177,7 +177,8 @@ SOURCES += \ timeline/mediaimportdata.cpp \ dialogs/autocutsilencedialog.cpp \ ui/columnedgridlayout.cpp \ - rendering/shadergenerators.cpp + rendering/shadergenerators.cpp \ + global/timing.cpp HEADERS += \ ui/mainwindow.h \ @@ -308,7 +309,8 @@ HEADERS += \ timeline/mediaimportdata.h \ dialogs/autocutsilencedialog.h \ ui/columnedgridlayout.h \ - rendering/shadergenerators.h + rendering/shadergenerators.h \ + global/timing.h FORMS += @@ -327,27 +329,18 @@ TRANSLATIONS += \ win32 { RC_FILE = packaging/windows/resources.rc - LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lopengl32 -luser32 - !contains(DEFINES, NO_OCIO) { - LIBS += -lOpenColorIO - } + LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -lopengl32 -luser32 } mac { - LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -framework CoreFoundation + LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -framework CoreFoundation ICON = packaging/macos/olive.icns INCLUDEPATH = /usr/local/include - !contains(DEFINES, NO_OCIO) { - LIBS += -lOpenColorIO - } } unix:!mac { CONFIG += link_pkgconfig - PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample - !contains(DEFINES, NO_OCIO) { - LIBS += -lOpenColorIO - } + PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample OpenColorIO } RESOURCES += \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index d83e6639d..454ffd884 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -127,7 +127,7 @@ void EffectControls::menu_select(QAction* q) { update_ui(true); } else { Reload(); - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } } @@ -305,7 +305,7 @@ void EffectControls::deselect_all_effects(QWidget* sender) { } if (panel_sequence_viewer != nullptr) { - panel_sequence_viewer->viewer_widget->update(); + panel_sequence_viewer->viewer_widget()->update(); } } diff --git a/panels/project.h b/panels/project.h index 44914600c..f8ea096f2 100644 --- a/panels/project.h +++ b/panels/project.h @@ -40,9 +40,6 @@ #include "ui/sourcetable.h" -#define LOAD_TYPE_VERSION 69 -#define LOAD_TYPE_URL 70 - extern QString autorecovery_filename; extern QStringList recent_projects; diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 30ae02da1..66469146b 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -53,6 +53,7 @@ #include "ui/mainwindow.h" #include "undo/undostack.h" #include "global/debug.h" +#include "global/timing.h" #include "ui/menu.h" int olive::timeline::kTrackDefaultHeight = 40; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 7b6bc1710..1bde2749a 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -43,6 +43,7 @@ extern "C" { #include "timeline/clip.h" #include "panels/panels.h" #include "global/config.h" +#include "global/timing.h" #include "project/footage.h" #include "project/media.h" #include "undo/undo.h" @@ -77,8 +78,8 @@ Viewer::Viewer(QWidget *parent) : headers->snapping = false; headers->show_text(false); viewer_container->viewer = this; - viewer_widget = viewer_container->child; - viewer_widget->viewer = this; + viewer_widget_ = viewer_container->child; + viewer_widget_->viewer = this; set_media(nullptr); current_timecode_slider->setEnabled(false); @@ -93,15 +94,13 @@ Viewer::Viewer(QWidget *parent) : connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update())); connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update())); connect(horizontal_bar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int))); - connect(horizontal_bar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int))); + connect(horizontal_bar, SIGNAL(valueChanged(int)), viewer_widget_, SLOT(set_waveform_scroll(int))); connect(horizontal_bar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); update_playhead_timecode(0); update_end_timecode(); } -Viewer::~Viewer() {} - void Viewer::Retranslate() { /// Viewer panels are retranslated through the MainWindow to differentiate Media and Sequence Viewers // update_window_title(); @@ -109,7 +108,7 @@ void Viewer::Retranslate() { bool Viewer::is_focused() { return headers->hasFocus() - || viewer_widget->hasFocus() + || viewer_widget_->hasFocus() || go_to_start_button->hasFocus() || prev_frame_button->hasFocus() || play_button->hasFocus() @@ -146,132 +145,6 @@ void Viewer::reset_all_audio() { clear_audio_ibuffer(); } -long timecode_to_frame(const QString& s, int view, double frame_rate) { - QList list = s.split(QRegExp("[:;]")); - - if (view == olive::kTimecodeFrames || (list.size() == 1 && view != olive::kTimecodeMilliseconds)) { - return s.toLong(); - } - - int frRound = qRound(frame_rate); - int hours, minutes, seconds, frames; - - if (view == olive::kTimecodeMilliseconds) { - long milliseconds = s.toLong(); - - hours = milliseconds/3600000; - milliseconds -= (hours*3600000); - minutes = milliseconds/60000; - milliseconds -= (minutes*60000); - seconds = milliseconds/1000; - milliseconds -= (seconds*1000); - frames = qRound64((milliseconds*0.001)*frame_rate); - - seconds = qRound64(seconds * frame_rate); - minutes = qRound64(minutes * frame_rate * 60); - hours = qRound64(hours * frame_rate * 3600); - } else { - hours = ((list.size() > 0) ? list.at(0).toInt() : 0) * frRound * 3600; - minutes = ((list.size() > 1) ? list.at(1).toInt() : 0) * frRound * 60; - seconds = ((list.size() > 2) ? list.at(2).toInt() : 0) * frRound; - frames = (list.size() > 3) ? list.at(3).toInt() : 0; - } - - int f = (frames + seconds + minutes + hours); - - if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(frame_rate)) { - // return drop - int d; - int m; - - int dropFrames = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate - int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes - int framesPerMinute = (qRound(frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames - - d = f / framesPer10Minutes; - f -= dropFrames*9*d; - - m = f % framesPer10Minutes; - - if (m > dropFrames) { - f -= (dropFrames * ((m - dropFrames) / framesPerMinute)); - } - } - - // return non-drop - return f; -} - -QString frame_to_timecode(long f, int view, double frame_rate) { - if (view == olive::kTimecodeFrames) { - return QString::number(f); - } - - // return timecode - int hours = 0; - int mins = 0; - int secs = 0; - int frames = 0; - QString token = ":"; - - if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(frame_rate)) { - //CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE - //Code by David Heidelberger, adapted from Andrew Duncan, further adapted for Olive by Olive Team - //Given an int called framenumber and a double called framerate - //Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. - - int d; - int m; - - int dropFrames = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate - int framesPerHour = qRound(frame_rate*60*60); //Number of frqRound64ames in an hour - int framesPer24Hours = framesPerHour*24; //Number of frames in a day - timecode rolls over after 24 hours - int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes - int framesPerMinute = (qRound(frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames - - //If framenumber is greater than 24 hrs, next operation will rollover clock - f = f % framesPer24Hours; // % is the modulus operator, which returns a remainder. a % b = the remainder of a/b - - d = f / framesPer10Minutes; // \ means integer division, which is a/b without a remainder. Some languages you could use floor(a/b) - m = f % framesPer10Minutes; - - //In the original post, the next line read m>1, which only worked for 29.97. Jean-Baptiste Mardelle correctly pointed out that m should be compared to dropFrames. - if (m > dropFrames) { - f = f + (dropFrames*9*d) + dropFrames * ((m - dropFrames) / framesPerMinute); - } else { - f = f + dropFrames*9*d; - } - - int frRound = qRound(frame_rate); - frames = f % frRound; - secs = (f / frRound) % 60; - mins = ((f / frRound) / 60) % 60; - hours = (((f / frRound) / 60) / 60); - - token = ";"; - } else { - // non-drop timecode - - int int_fps = qRound(frame_rate); - hours = f / (3600 * int_fps); - mins = f / (60*int_fps) % 60; - secs = f/int_fps % 60; - frames = f%int_fps; - } - if (view == olive::kTimecodeMilliseconds) { - return QString::number((hours*3600000)+(mins*60000)+(secs*1000)+qCeil(frames*1000/frame_rate)); - } - return QString(QString::number(hours).rightJustified(2, '0') + - ":" + QString::number(mins).rightJustified(2, '0') + - ":" + QString::number(secs).rightJustified(2, '0') + - token + QString::number(frames).rightJustified(2, '0') - ); -} - -bool frame_rate_is_droppable(double rate) { - return (qFuzzyCompare(rate, 23.976) || qFuzzyCompare(rate, 29.97) || qFuzzyCompare(rate, 59.94)); -} - void Viewer::seek(long p) { pause(); if (main_sequence) { @@ -499,7 +372,7 @@ void Viewer::update_header_zoom() { minimum_zoom = (sequenceEndFrame > 0) ? ((double) headers->width() / (double) sequenceEndFrame) : 1; headers->update_zoom(qMax(headers->get_zoom(), minimum_zoom)); set_sb_max(); - viewer_widget->waveform_zoom = headers->get_zoom(); + viewer_widget_->waveform_zoom = headers->get_zoom(); } else { headers->update(); } @@ -519,6 +392,11 @@ int Viewer::get_playback_speed() { return playback_speed; } +ViewerWidget *Viewer::viewer_widget() +{ + return viewer_widget_; +} + void Viewer::set_marker() { set_marker_internal(seq.get()); } @@ -527,13 +405,13 @@ void Viewer::resizeEvent(QResizeEvent *e) { QDockWidget::resizeEvent(e); if (seq != nullptr) { set_sb_max(); - viewer_widget->update(); + viewer_widget_->update(); } } void Viewer::update_viewer() { update_header_zoom(); - viewer_widget->frame_update(); + viewer_widget_->frame_update(); if (seq != nullptr) { update_playhead_timecode(seq->playhead); } @@ -616,9 +494,9 @@ void Viewer::update_window_title() { void Viewer::set_zoom_value(double d) { headers->update_zoom(d); - if (viewer_widget->waveform) { - viewer_widget->waveform_zoom = d; - viewer_widget->update(); + if (viewer_widget_->waveform) { + viewer_widget_->waveform_zoom = d; + viewer_widget_->update(); } if (seq != nullptr) { set_sb_max(); @@ -843,10 +721,10 @@ void Viewer::set_media(Media* m) { new_sequence->clips.append(c); if (footage->video_tracks.size() == 0) { - viewer_widget->waveform = true; - viewer_widget->waveform_clip = c; - viewer_widget->waveform_ms = &audio_stream; - viewer_widget->frame_update(); + viewer_widget_->waveform = true; + viewer_widget_->waveform_clip = c; + viewer_widget_->waveform_ms = &audio_stream; + viewer_widget_->frame_update(); } } else { new_sequence->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; @@ -926,7 +804,7 @@ void Viewer::drag_audio_only() } void Viewer::clean_created_seq() { - viewer_widget->waveform = false; + viewer_widget_->waveform = false; if (created_sequence) { // TODO delete undo commands referencing this sequence to avoid crashes @@ -950,11 +828,11 @@ void Viewer::set_sequence(bool main, SequencePtr s) { reset_all_audio(); - viewer_widget->wait_until_render_is_paused(); + viewer_widget_->wait_until_render_is_paused(); // If we had a current sequence open, close it if (seq != nullptr) { - close_active_clips(seq.get()); + seq->Close(); } clean_created_seq(); @@ -969,8 +847,8 @@ void Viewer::set_sequence(bool main, SequencePtr s) { headers->setEnabled(!null_sequence); current_timecode_slider->setEnabled(!null_sequence); - viewer_widget->setEnabled(!null_sequence); - viewer_widget->setVisible(!null_sequence); + viewer_widget_->setEnabled(!null_sequence); + viewer_widget_->setVisible(!null_sequence); go_to_start_button->setEnabled(!null_sequence); prev_frame_button->setEnabled(!null_sequence); play_button->setEnabled(!null_sequence); @@ -1001,7 +879,7 @@ void Viewer::set_sequence(bool main, SequencePtr s) { update_header_zoom(); - viewer_widget->frame_update(); + viewer_widget_->frame_update(); update(); } diff --git a/panels/viewer.h b/panels/viewer.h index 061623fe0..c7343f56d 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -37,17 +37,12 @@ #include "ui/labelslider.h" #include "ui/resizablescrollbar.h" -bool frame_rate_is_droppable(double rate); -long timecode_to_frame(const QString& s, int view, double frame_rate); -QString frame_to_timecode(long f, int view, double frame_rate); - class Viewer : public Panel { Q_OBJECT public: explicit Viewer(QWidget *parent = nullptr); - ~Viewer(); bool is_focused(); bool is_main_sequence(); @@ -90,7 +85,7 @@ public: int get_playback_speed(); - ViewerWidget* viewer_widget; + ViewerWidget* viewer_widget(); Media* media; SequencePtr seq; @@ -134,7 +129,6 @@ private slots: void drag_audio_only(); private: - void update_window_title(); void clean_created_seq(); void set_sequence(bool main, SequencePtr s); @@ -155,6 +149,8 @@ private: void setup_ui(); + ViewerWidget* viewer_widget_; + ResizableScrollBar* horizontal_bar; ViewerContainer* viewer_container; LabelSlider* current_timecode_slider; diff --git a/project/footage.cpp b/project/footage.cpp index 88c600589..631c9ae49 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +namespace OCIO = OCIO_NAMESPACE::v1; #include "project/previewgenerator.h" #include "timeline/clip.h" @@ -45,6 +47,28 @@ Footage::~Footage() { reset(); } +QString Footage::Colorspace() +{ + if (!colorspace_.isEmpty()) { + return colorspace_; + } + + // If this footage has no color space set, try to guess the color space from the filename + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + QString guess_colorspace = config->parseColorSpaceFromString(url.toUtf8()); + + if (!guess_colorspace.isEmpty()) { + return guess_colorspace; + } + + return OCIO::ROLE_SCENE_LINEAR; +} + +void Footage::SetColorspace(const QString &cs) +{ + colorspace_ = cs; +} + void Footage::reset() { if (preview_gen != nullptr) { preview_gen->cancel(); diff --git a/project/footage.h b/project/footage.h index dcd84b009..2678769d3 100644 --- a/project/footage.h +++ b/project/footage.h @@ -65,7 +65,8 @@ struct FootageStream { QVector audio_preview; }; -struct Footage { +class Footage { +public: Footage(); ~Footage(); @@ -82,10 +83,9 @@ struct Footage { bool alpha_is_associated; int start_number; -#ifndef NO_OCIO // color management - QString colorspace; -#endif + QString Colorspace(); + void SetColorspace(const QString& cs); // proxy config bool proxy; @@ -107,6 +107,8 @@ struct Footage { long get_length_in_frames(double frame_rate); FootageStream *get_stream_from_file_index(bool video, int index); void reset(); +private: + QString colorspace_; }; using FootagePtr = std::shared_ptr; diff --git a/project/loadthread.cpp b/project/loadthread.cpp index 65d869bd2..ca1c49cd5 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -34,6 +34,9 @@ #include #include +const int LOAD_TYPE_VERSION = 100; +const int LOAD_TYPE_URL = 101; + LoadThread::LoadThread(const QString& filename, bool autorecovery) : filename_(filename), autorecovery_(autorecovery), diff --git a/project/media.cpp b/project/media.cpp index 1c9546bb2..1e77305a2 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -33,11 +33,11 @@ extern "C" { #include "undo/undo.h" #include "undo/undostack.h" #include "global/config.h" -#include "panels/viewer.h" #include "panels/project.h" #include "ui/icons.h" #include "projectmodel.h" #include "global/debug.h" +#include "global/timing.h" QString get_interlacing_name(int interlacing) { switch (interlacing) { diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index 27103830c..d9967ecbb 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -432,7 +432,7 @@ void SourcesCommon::clear_proxies_from_selected() { if (olive::ActiveSequence != nullptr) { // close all clips so we can delete any proxies requested to be deleted - close_active_clips(olive::ActiveSequence.get()); + olive::ActiveSequence->Close(); } // delete proxies requested to be deleted @@ -442,7 +442,7 @@ void SourcesCommon::clear_proxies_from_selected() { if (olive::ActiveSequence != nullptr) { // update viewer (will re-open active clips with original media) - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } olive::Global->set_modified(true); diff --git a/rendering/audio.cpp b/rendering/audio.cpp index b54677fdb..a6d189e7b 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -52,7 +52,6 @@ QAudioInput* audio_input = nullptr; QFile output_recording; bool recording = false; -bool audio_rendering = false; int audio_rendering_rate = 0; qint8 audio_ibuffer[audio_ibuffer_size]; @@ -154,7 +153,7 @@ void clear_audio_ibuffer() { } int current_audio_freq() { - return audio_rendering ? audio_rendering_rate : audio_output->format().sampleRate(); + return olive::Global->is_exporting() ? audio_rendering_rate : audio_output->format().sampleRate(); } qint64 get_buffer_offset_from_frame(double framerate, long frame) { diff --git a/rendering/audio.h b/rendering/audio.h index c478839c5..1c7ebb0a8 100644 --- a/rendering/audio.h +++ b/rendering/audio.h @@ -61,7 +61,6 @@ extern long audio_ibuffer_frame; extern double audio_ibuffer_timecode; extern bool audio_scrub; extern bool recording; -extern bool audio_rendering; extern int audio_rendering_rate; void clear_audio_ibuffer(); diff --git a/rendering/bitdepths.cpp b/rendering/bitdepths.cpp index b7bf217f3..bad6dc6f7 100644 --- a/rendering/bitdepths.cpp +++ b/rendering/bitdepths.cpp @@ -28,22 +28,29 @@ namespace rendering { QVector bit_depths; void InitializeBitDepths() { - BitDepthInfo bdi; - bdi.name = QCoreApplication::translate("bitdepths", "8-bit"); - bdi.pixel_type = GL_UNSIGNED_BYTE; - bdi.internal_format = GL_RGBA8; - bit_depths.append(bdi); + bit_depths.resize(PIX_FMT_COUNT); - bdi.name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); - bdi.pixel_type = GL_HALF_FLOAT; - bdi.internal_format = GL_RGBA16F; - bit_depths.append(bdi); + bit_depths[PIX_FMT_RGBA8].name = QCoreApplication::translate("bitdepths", "8-bit"); + bit_depths[PIX_FMT_RGBA8].internal_format = GL_RGBA8; + bit_depths[PIX_FMT_RGBA8].pixel_format = GL_RGBA; + bit_depths[PIX_FMT_RGBA8].pixel_type = GL_UNSIGNED_BYTE; + + bit_depths[PIX_FMT_RGBA16].name = QCoreApplication::translate("bitdepths", "16-bit Integer"); + bit_depths[PIX_FMT_RGBA16].internal_format = GL_RGBA16UI; + bit_depths[PIX_FMT_RGBA16].pixel_format = GL_RGBA_INTEGER; + bit_depths[PIX_FMT_RGBA16].pixel_type = GL_UNSIGNED_SHORT; + + bit_depths[PIX_FMT_RGBA16F].name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); + bit_depths[PIX_FMT_RGBA16F].internal_format = GL_RGBA16F; + bit_depths[PIX_FMT_RGBA16F].pixel_format = GL_RGBA; + bit_depths[PIX_FMT_RGBA16F].pixel_type = GL_HALF_FLOAT; + + bit_depths[PIX_FMT_RGBA32F].name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); + bit_depths[PIX_FMT_RGBA32F].internal_format = GL_RGBA32F; + bit_depths[PIX_FMT_RGBA32F].pixel_format = GL_RGBA; + bit_depths[PIX_FMT_RGBA32F].pixel_type = GL_FLOAT; - bdi.name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); - bdi.pixel_type = GL_FLOAT; - bdi.internal_format = GL_RGBA32F; - bit_depths.append(bdi); } } diff --git a/rendering/bitdepths.h b/rendering/bitdepths.h index ab1b07e8a..67393440b 100644 --- a/rendering/bitdepths.h +++ b/rendering/bitdepths.h @@ -26,17 +26,34 @@ #include namespace olive { - namespace rendering { - struct BitDepthInfo { - QString name; - GLuint pixel_type; - GLuint internal_format; - }; +namespace rendering { - extern QVector bit_depths; +struct BitDepthInfo { + QString name; + GLint internal_format; + GLenum pixel_format; + GLenum pixel_type; +}; - void InitializeBitDepths(); - } +/** + * @brief The OlivePixelFormat enum + * + * Olive's internal supported pixel formats. With the exception of OLIVE_PIX_FMT_COUNT, these must all + * be defined in InitializeBitDepths(). + */ +enum PixelFormat { + PIX_FMT_RGBA8, + PIX_FMT_RGBA16, + PIX_FMT_RGBA16F, + PIX_FMT_RGBA32F, + PIX_FMT_COUNT +}; + +extern QVector bit_depths; + +void InitializeBitDepths(); + +} } #endif // BITDEPTHS_H diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 117ac8f26..2ae5dfd52 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -33,10 +33,11 @@ #include #include +#include "panels/panels.h" #include "project/projectelements.h" #include "rendering/audio.h" #include "rendering/renderfunctions.h" -#include "panels/panels.h" +#include "global/timing.h" #include "global/config.h" #include "global/debug.h" #include "ui/mainwindow.h" @@ -44,7 +45,6 @@ // Enable verbose audio messages - good for debugging reversed audio //#define AUDIOWARNINGS -const AVPixelFormat kDestPixFmt = AV_PIX_FMT_RGBA; const AVSampleFormat kDestSampleFmt = AV_SAMPLE_FMT_S16; double bytes_to_seconds(int nb_bytes, int nb_channels, int sample_rate) { @@ -858,8 +858,6 @@ Cacher::Cacher(Clip* c) : {} void Cacher::OpenWorker() { - qint64 time_start = QDateTime::currentMSecsSinceEpoch(); - // set some defaults for the audio cacher if (clip->track() >= 0) { audio_reset_ = false; @@ -988,7 +986,26 @@ void Cacher::OpenWorker() { last_filter = yadif_filter; } - const char* chosen_format = av_get_pix_fmt_name(kDestPixFmt); + AVPixelFormat possible_pix_fmts[] = { + AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGBA64, + AV_PIX_FMT_NONE + }; + + AVPixelFormat pix_fmt = avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, + static_cast(stream->codecpar->format), + 1, + nullptr); + + if (pix_fmt == AV_PIX_FMT_RGBA) { + qDebug() << "This is an 8-bit image."; + media_pixel_format_ = olive::rendering::PIX_FMT_RGBA8; + } else { + qDebug() << "This is an HDR image."; + media_pixel_format_ = olive::rendering::PIX_FMT_RGBA16; + } + + const char* chosen_format = av_get_pix_fmt_name(pix_fmt); snprintf(filter_args, sizeof(filter_args), "pix_fmts=%s", chosen_format); AVFilterContext* format_conv; @@ -1091,7 +1108,7 @@ void Cacher::OpenWorker() { frame_ = av_frame_alloc(); } - qInfo() << "Clip opened on track" << clip->track() << "(took" << (QDateTime::currentMSecsSinceEpoch() - time_start) << "ms)"; + qInfo() << "Clip opened on track" << clip->track(); is_valid_state_ = true; } @@ -1336,6 +1353,11 @@ ClipQueue *Cacher::queue() return &queue_; } +const olive::rendering::PixelFormat &Cacher::media_pixel_format() +{ + return media_pixel_format_; +} + int Cacher::RetrieveFrameFromDecoder(AVFrame* f) { int result = 0; int receive_ret; diff --git a/rendering/cacher.h b/rendering/cacher.h index f413ce3b2..0cda89d61 100644 --- a/rendering/cacher.h +++ b/rendering/cacher.h @@ -43,6 +43,7 @@ extern "C" { #include #include "rendering/clipqueue.h" +#include "rendering/bitdepths.h" class Clip; @@ -254,6 +255,15 @@ public: */ ClipQueue* queue(); + /** + * @brief Retrieve OpenGL information about this media's bit depth + * + * @return + * + * A olive::rendering::PixelFormat value corresponding to a member of olive::rendering::bit_depths. + */ + const olive::rendering::PixelFormat& media_pixel_format(); + private: /** * @brief Reference to the parent clip. Set in the constructor and never changed during this object's lifetime. @@ -582,6 +592,11 @@ private: * @brief Internal function using the Cacher's known information to determine whether this media is playing in reverse */ bool IsReversed(); + + /** + * @brief Internal struct holding bit depth information for the current media + */ + olive::rendering::PixelFormat media_pixel_format_; }; #endif // CACHER_H diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp index e5f48d2df..148d26271 100644 --- a/rendering/exportthread.cpp +++ b/rendering/exportthread.cpp @@ -413,10 +413,10 @@ void ExportThread::Export() long remaining_frames, frame_count = 1; // Use Sequence Viewer's render thread - TODO separate this into a new render thread for background rendering - RenderThread* renderer = panel_sequence_viewer->viewer_widget->get_renderer(); + RenderThread* renderer = panel_sequence_viewer->viewer_widget()->get_renderer(); // Override connection from RenderThread - disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint())); + disconnect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget(), SLOT(queue_repaint())); connect(renderer, SIGNAL(ready()), this, SLOT(wake())); // Lock mutex (used for synchronization with RenderThread) @@ -548,7 +548,7 @@ void ExportThread::Export() // Restore original connection from RenderThread disconnect(renderer, SIGNAL(ready()), this, SLOT(wake())); - connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget, SLOT(queue_repaint())); + connect(renderer, SIGNAL(ready()), panel_sequence_viewer->viewer_widget(), SLOT(queue_repaint())); mutex.unlock(); @@ -559,7 +559,7 @@ void ExportThread::Export() if (params_.video_enabled) vpkt_alloc = true; if (params_.audio_enabled) apkt_alloc = true; - olive::Global->set_rendering_state(false); + olive::Global->set_export_state(false); // If audio is enabled, flush the rest of the audio out of swresample if (params_.audio_enabled) { diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 2e51ab151..8f9eed3d7 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -24,8 +24,9 @@ #include #include -// TODO take this from Config rather than having a constant -const GLuint kPixelFormat = GL_RGBA16F; +#include "global/config.h" +#include "global/global.h" +#include "bitdepths.h" FramebufferObject::FramebufferObject() : buffer_(0), @@ -64,8 +65,22 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture + const olive::rendering::BitDepthInfo& bit_depth = olive::rendering::bit_depths.at(olive::Global->is_exporting() ? + olive::CurrentConfig.export_bit_depth : + olive::CurrentConfig.playback_bit_depth); + + qDebug() << "hello" << bit_depth.name; + ctx->functions()->glTexImage2D( - GL_TEXTURE_2D, 0, kPixelFormat, width, height, 0, GL_RGBA, GL_FLOAT, nullptr + GL_TEXTURE_2D, + 0, + bit_depth.internal_format, + width, + height, + 0, + bit_depth.pixel_format, + bit_depth.pixel_type, + nullptr ); // set texture filtering to bilinear diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index e64097165..a11057980 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -40,9 +40,9 @@ extern "C" { #include "ui/collapsiblewidget.h" #include "rendering/audio.h" #include "global/math.h" +#include "global/timing.h" #include "global/config.h" #include "panels/timeline.h" -#include "panels/viewer.h" #include "qopenglshaderprogramptr.h" #include "shadergenerators.h" @@ -365,6 +365,20 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { int video_width = c->media_width(); int video_height = c->media_height(); + // prepare framebuffers for backend drawing operations + if (c->fbo.isEmpty()) { + // create 3 fbos for nested sequences, 2 for most clips + int fbo_count = (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) ? 3 : 2; + + c->fbo.resize(fbo_count); + + for (int j=0;jfbo[j].Create(params.ctx, video_width, video_height); + } + } + + bool convert_frame_to_internal = false; + // if media is footage if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { @@ -378,19 +392,13 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } if (textureID == 0) { + qWarning() << "Failed to create texture"; - } - } - // prepare framebuffers for backend drawing operations - if (c->fbo.isEmpty()) { - // create 3 fbos for nested sequences, 2 for most clips - int fbo_count = (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) ? 3 : 2; + } else { - c->fbo.resize(fbo_count); + convert_frame_to_internal = true; - for (int j=0;jfbo[j].Create(params.ctx, video_width, video_height); } } @@ -422,60 +430,43 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { -#ifndef NO_OCIO - - // Convert texture to float - if (textureID != c->fbo.at(0).texture() && textureID != c->fbo.at(1).texture()) { - textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); - fbo_switcher = !fbo_switcher; - } - // Convert frame from source to linear colorspace if (olive::CurrentConfig.enable_color_management) { - if (c->ocio_shader == nullptr) { - OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + // Convert texture to sequence's internal format + if (textureID != c->fbo.at(0).texture() && textureID != c->fbo.at(1).texture()) { + textureID = draw_clip(params.ctx, params.pipeline, c->fbo.at(fbo_switcher), textureID, true); + fbo_switcher = !fbo_switcher; + } + // Check if this clip has an OCIO shader set up or not + if (c->ocio_shader == nullptr) { + + + // Set default input colorspace QString input_cs = OCIO::ROLE_SCENE_LINEAR; if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - - if (!c->media()->to_footage()->colorspace.isEmpty()) { - - input_cs = c->media()->to_footage()->colorspace; - - } else { - - // If this is a footage clip, try to guess the color space from the filename - QString guess_colorspace = config->parseColorSpaceFromString(c->media()->to_footage()->url.toUtf8()); - - if (!guess_colorspace.isEmpty()) { - input_cs = guess_colorspace; - } - - } - + input_cs = c->media()->to_footage()->Colorspace(); } - qDebug() << "Input colorspace:" << input_cs; - + // Try to get a shader based on the input color space to scene linear try { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); OCIO::ConstProcessorRcPtr processor = config->getProcessor(input_cs.toUtf8(), OCIO::ROLE_SCENE_LINEAR); - olive::shader::AlphaAssociateMode associate_mode = (c->media()->to_footage()->alpha_is_associated) - ? olive::shader::DisassociateAndReassociate : olive::shader::Associate; - c->ocio_shader = olive::shader::SetupOCIO(params.ctx, c->ocio_lut_texture, processor, - associate_mode); + c->media()->to_footage()->alpha_is_associated); } catch (OCIO::Exception& e) { qWarning() << e.what(); } } + // Ensure we got a shader, and if so, blit with it if (c->ocio_shader != nullptr) { textureID = olive::rendering::OCIOBlit(c->ocio_shader.get(), c->ocio_lut_texture, @@ -486,8 +477,6 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } } -#endif - } } @@ -801,54 +790,6 @@ void olive::rendering::compose_audio(Viewer* viewer, Sequence* seq, int playback compose_sequence(params); } -long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { - return qRound((double(framenumber)/source_frame_rate)*target_frame_rate); -} - -double get_timecode(Clip* c, long playhead) { - return double(playhead_to_clip_frame(c, playhead))/c->sequence->frame_rate; -} - -long playhead_to_clip_frame(Clip* c, long playhead) { - return (qMax(0L, playhead - c->timeline_in(true)) + c->clip_in(true)); -} - -double playhead_to_clip_seconds(Clip* c, long playhead) { - // returns time in seconds - long clip_frame = playhead_to_clip_frame(c, playhead); - - if (c->reversed()) { - clip_frame = c->media_length() - clip_frame - 1; - } - - double secs = (double(clip_frame)/c->sequence->frame_rate)*c->speed().value; - if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - secs *= c->media()->to_footage()->speed; - } - - return secs; -} - -int64_t seconds_to_timestamp(Clip *c, double seconds) { - return qRound64(seconds * av_q2d(av_inv_q(c->time_base()))); -} - -int64_t playhead_to_timestamp(Clip* c, long playhead) { - return seconds_to_timestamp(c, playhead_to_clip_seconds(c, playhead)); -} - -void close_active_clips(Sequence* s) { - if (s != nullptr) { - for (int i=0;iclips.size();i++) { - Clip* c = s->clips.at(i).get(); - if (c != nullptr) { - c->Close(true); - } - } - } -} - -#ifndef NO_OCIO GLuint olive::rendering::OCIOBlit(QOpenGLShaderProgram *pipeline, GLuint lut, const FramebufferObject& fbo, @@ -880,4 +821,3 @@ GLuint olive::rendering::OCIOBlit(QOpenGLShaderProgram *pipeline, return textureID; } -#endif diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index a4f5c2877..228608762 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -24,10 +24,8 @@ #include #include #include -#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE::v1; -#endif #include "timeline/sequence.h" #include "effects/effect.h" @@ -244,143 +242,6 @@ void compose_audio(Viewer* viewer, Sequence *seq, int playback_speed, bool wait_ } } -/** - * @brief Rescale a frame number between two frame rates - * - * Converts a frame number from one frame rate to its equivalent in another frame rate - * - * @param framenumber - * - * The frame number to convert - * - * @param source_frame_rate - * - * Frame rate that the frame number is currently in - * - * @param target_frame_rate - * - * Frame rate to convert to - * - * @return - * - * Rescaled frame number - */ -long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate); - -/** - * @brief Get timecode - * - * Get the current clip/media time from the Timeline playhead in seconds. For instance if the playhead was at the start - * of a clip (whose in point wasn't trimmed), this would be 0.0 as it's the start of the clip/media; - * - * @param c - * - * Clip to get the timecode of - * - * @param playhead - * - * Sequence playhead to convert to a clip/media timecode - * - * @return - * - * Timecode in seconds - */ -double get_timecode(Clip *c, long playhead); - -/** - * @brief Convert playhead frame number to a clip frame number - * - * Converts a Timeline playhead to a the current clip's frame. Equivalent to - * `PLAYHEAD - CLIP_TIMELINE_IN + CLIP_MEDIA_IN`. All keyframes are in clip frames. - * - * @param c - * - * The clip to get the current frame number of - * - * @param playhead - * - * The current Timeline frame number - * - * @return - * - * The curren frame number of the clip at `playhead` - */ -long playhead_to_clip_frame(Clip* c, long playhead); - -/** - * @brief Converts the playhead to clip seconds - * - * Get the current timecode at the playhead in terms of clip seconds. - * - * FIXME: Possible duplicate of get_timecode()? Will need to research this more. - * - * @param c - * - * Clip to return clip seconds of. - * - * @param playhead - * - * Current Timeline playhead to convert to clip seconds - * - * @return - * - * Clip time in seconds - */ -double playhead_to_clip_seconds(Clip *c, long playhead); - -/** - * @brief Convert seconds to FFmpeg timestamp - * - * Used for interaction with FFmpeg, converts seconds in a floating-point value to a timestamp in AVStream->time_base - * units. - * - * @param c - * - * Clip to get timestamp of - * - * @param seconds - * - * Clip time in seconds - * - * @return - * - * An FFmpeg-compatible timestamp in AVStream->time_base units. - */ -int64_t seconds_to_timestamp(Clip* c, double seconds); - -/** - * @brief Convert Timeline playhead to FFmpeg timestamp - * - * Used for interaction with FFmpeg, converts the Timeline playhead to a timestamp in AVStream->time_base - * units. - * - * @param c - * - * Clip to get timestamp of - * - * @param playhead - * - * Timeline playhead to convert to a timestamp - * - * @return - * - * An FFmpeg-compatible timestamp in AVStream->time_base units. - */ -int64_t playhead_to_timestamp(Clip *c, long playhead); - -/** - * @brief Close all open clips in a Sequence - * - * Closes any currently open clips on a Sequence and waits for them to close before returning. This may be slow as a - * result on large Sequence objects. If a Clip is a nested Sequence, this function calls itself recursively on that - * Sequence too. - * - * @param s - * - * The Sequence to close all clips on. - */ -void close_active_clips(Sequence* s); - void UpdateOCIOGLState(const ComposeSequenceParams ¶ms); namespace olive { @@ -389,13 +250,10 @@ namespace olive { extern GLfloat blit_texcoords[]; extern GLfloat flipped_blit_texcoords[]; void Blit(QOpenGLShaderProgram* pipeline, bool flipped = false, QMatrix4x4 matrix = QMatrix4x4()); - -#ifndef NO_OCIO GLuint OCIOBlit(QOpenGLShaderProgram *pipeline, GLuint lut, const FramebufferObject& fbo, GLuint texture); -#endif } } diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index d1ab0b22f..ab839350e 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -27,10 +27,8 @@ #include #include -#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE::v1; -#endif #include "timeline/sequence.h" #include "effects/effectloaders.h" @@ -48,14 +46,10 @@ RenderThread::RenderThread() : tex_height(-1), queued(false), texture_failed(false), - #ifndef NO_OCIO ocio_lut_texture(0), ocio_shader(nullptr), - #endif running(true), - #ifndef NO_OCIO ocio_config_date(0), - #endif front_buffer_switcher(false) { surface.create(); @@ -121,17 +115,12 @@ void RenderThread::run() { pipeline_program = olive::shader::GetPipeline(); } -#ifndef NO_OCIO // If there's no OpenColorIO shader or the configuration has changed, (re-)create it now - if (olive::CurrentConfig.enable_color_management - && (ocio_shader == nullptr || ocio_config_date != olive::CurrentRuntimeConfig.ocio_config_date)) { - ocio_config_date = olive::CurrentRuntimeConfig.ocio_config_date; - + if (olive::CurrentConfig.enable_color_management && ocio_shader == nullptr) { destroy_ocio(); set_up_ocio(); } -#endif // draw frame paint(); @@ -160,7 +149,6 @@ const GLuint &RenderThread::get_texture() return front_buffer_switcher ? front_buffer_2.texture() : front_buffer_1.texture(); } -#ifndef NO_OCIO void RenderThread::set_up_ocio() { @@ -194,7 +182,7 @@ void RenderThread::set_up_ocio() OCIO::ConstProcessorRcPtr processor = config->getProcessor(transform); // Create a OCIO shader with this processor - ocio_shader = olive::shader::SetupOCIO(ctx, ocio_lut_texture, processor, olive::shader::NoAssociate); + ocio_shader = olive::shader::SetupOCIO(ctx, ocio_lut_texture, processor, true); } catch(OCIO::Exception & e) { qCritical() << e.what(); @@ -205,12 +193,12 @@ void RenderThread::set_up_ocio() void RenderThread::destroy_ocio() { // Destroy LUT texture - ctx->functions()->glDeleteTextures(1, &ocio_lut_texture); + if (ocio_lut_texture > 0) { + ctx->functions()->glDeleteTextures(1, &ocio_lut_texture); + } ocio_lut_texture = 0; - ocio_shader = nullptr; } -#endif void RenderThread::paint() { // set up compose_sequence() parameters @@ -254,29 +242,24 @@ void RenderThread::paint() { FramebufferObject& buffer = front_buffer_switcher ? front_buffer_1 : front_buffer_2; // Blit the composite buffer to one of the front buffers - bool standard_blit = true; -#ifndef NO_OCIO // If we're color managing, conver the linear composited frame to display color space if (olive::CurrentConfig.enable_color_management && ocio_shader != nullptr) { + olive::rendering::OCIOBlit(ocio_shader.get(), ocio_lut_texture, buffer, composite_buffer.texture()); - standard_blit = false; - } -#else + } else { -#endif - - // If we're not color managing, just blit normally - if (standard_blit) { + // If we're not color managing, just blit normally buffer.BindBuffer(); composite_buffer.BindTexture(); olive::rendering::Blit(pipeline_program.get()); composite_buffer.ReleaseTexture(); buffer.ReleaseBuffer(); + } // flush changes @@ -404,10 +387,7 @@ void RenderThread::delete_ctx() { if (ctx != nullptr) { delete_shaders(); delete_buffers(); - -#ifndef NO_OCIO destroy_ocio(); -#endif } delete ctx; diff --git a/rendering/renderthread.h b/rendering/renderthread.h index da6259b62..89e33f521 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -60,23 +60,20 @@ public: public slots: // cleanup functions void delete_ctx(); + void delete_buffers(); + void delete_shaders(); + void destroy_ocio(); signals: void ready(); private: - // cleanup functions - void delete_buffers(); - void delete_shaders(); -#ifndef NO_OCIO // OpenColorIO functions void set_up_ocio(); - void destroy_ocio(); // OpenColorIO variables GLuint ocio_lut_texture; QOpenGLShaderProgramPtr ocio_shader; qint64 ocio_config_date; -#endif FramebufferObject front_buffer_1; QMutex front_mutex1; diff --git a/rendering/shadergenerators.cpp b/rendering/shadergenerators.cpp index dde856ba6..6a6490d63 100644 --- a/rendering/shadergenerators.cpp +++ b/rendering/shadergenerators.cpp @@ -118,8 +118,6 @@ QString olive::shader::GetAlphaAssociateFunction(const QString &function_name) "}\n").arg(function_name); } -#ifndef NO_OCIO - // copied from source code to OCIODisplay const int OCIO_LUT3D_EDGE_SIZE = 32; @@ -129,7 +127,7 @@ const int OCIO_NUM_3D_ENTRIES = 98304; QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, GLuint& lut_texture, OCIO::ConstProcessorRcPtr processor, - AlphaAssociateMode alpha_associate_mode) + bool alpha_is_associated) { QOpenGLExtraFunctions* xf = ctx->extraFunctions(); @@ -157,8 +155,9 @@ QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, // OCIO::GpuShaderDesc shaderDesc; + const char* ocio_func_name = "OCIODisplay"; shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0); - shaderDesc.setFunctionName("OCIODisplay"); + shaderDesc.setFunctionName(ocio_func_name); shaderDesc.setLut3DEdgeLen(OCIO_LUT3D_EDGE_SIZE); // @@ -179,39 +178,45 @@ QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, // Create OCIO shader code QString shader_text(processor->getGpuShaderText(shaderDesc)); - QString ocio_call_func; + QString shader_call; // Enforce alpha association - switch (alpha_associate_mode) { - case Associate: + if (alpha_is_associated) { + + // If alpha is already associated, we'll need to disassociate and reassociate + shader_text.append("\n"); + + QString disassociate_func_name = "disassoc"; + shader_text.append(GetAlphaDisassociateFunction(disassociate_func_name)); + + QString reassociate_func_name = "reassoc"; + shader_text.append(GetAlphaReassociateFunction(reassociate_func_name)); + + // Make OCIO call pass through disassociate and reassociate function + shader_call = QString("%3(%1(%2(col), tex2));").arg(ocio_func_name, + disassociate_func_name, + reassociate_func_name); + + } else { + // If alpha is not already associated, we can just associate after OCIO // Add associate function - shader_text.append(GetAlphaAssociateFunction("assoc")); + QString associate_func_name = "assoc"; + shader_text.append(GetAlphaAssociateFunction(associate_func_name)); // Make OCIO call pass through associate function - ocio_call_func = "assoc(OCIODisplay(col, tex2));"; - break; - case DisassociateAndReassociate: - // If alpha is already associated, we'll need to disassociate and reassociate - shader_text.append("\n"); - shader_text.append(GetAlphaDisassociateFunction("disassoc")); - shader_text.append(GetAlphaReassociateFunction("reassoc")); + shader_call = QString("%2(%1(col, tex2));").arg(ocio_func_name, associate_func_name); - // Make OCIO call pass through disassociate and reassociate function - ocio_call_func = "reassoc(OCIODisplay(disassoc(col), tex2));"; - break; - default: - // No association - ocio_call_func = "OCIODisplay(col, tex2);"; } + // Add process() function, which GetPipeline() will call if specified shader_text.append(QString("\n" "uniform sampler3D tex2;\n" "\n" "vec4 process(vec4 col) {\n" " return %1\n" - "}\n").arg(ocio_call_func)); + "}\n").arg(shader_call)); // Get pipeline-based shader to inject OCIO shader into @@ -222,4 +227,3 @@ QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, return shader; } -#endif diff --git a/rendering/shadergenerators.h b/rendering/shadergenerators.h index eacb51e83..5fe5a7370 100644 --- a/rendering/shadergenerators.h +++ b/rendering/shadergenerators.h @@ -3,29 +3,18 @@ #include "qopenglshaderprogramptr.h" #include "framebufferobject.h" - -#ifndef NO_OCIO #include namespace OCIO = OCIO_NAMESPACE::v1; -#endif namespace olive { namespace shader { QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); -#ifndef NO_OCIO -enum AlphaAssociateMode { - NoAssociate, - Associate, - DisassociateAndReassociate -}; - QOpenGLShaderProgramPtr SetupOCIO(QOpenGLContext *ctx, GLuint &lut_texture, OCIO::ConstProcessorRcPtr processor, - AlphaAssociateMode alpha_associate_mode); -#endif + bool alpha_is_associated); QString GetAlphaDisassociateFunction(const QString& function_name); QString GetAlphaReassociateFunction(const QString& function_name); diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 79b79de46..36f5bec8c 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -35,6 +35,7 @@ #include "project/clipboard.h" #include "undo/undo.h" #include "global/debug.h" +#include "global/timing.h" const int kRGBAComponentCount = 4; @@ -492,7 +493,7 @@ void Clip::Close(bool wait) { open_ = false; if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) { - close_active_clips(media()->to_sequence().get()); + media()->to_sequence()->Close(); } // destroy opengl texture in main thread @@ -511,10 +512,8 @@ void Clip::Close(bool wait) { // delete framebuffers fbo.clear(); -#ifndef NO_OCIO // delete OCIO shader ocio_shader = nullptr; -#endif if (UsesCacher()) { cacher.Close(wait); @@ -590,18 +589,20 @@ bool Clip::Retrieve() int video_width = cacher.media_width(); int video_height = cacher.media_height(); + const olive::rendering::BitDepthInfo& bit_depth_info = olive::rendering::bit_depths.at(cacher.media_pixel_format()); + if (allocate_data) { // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the // composition f->glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA8, video_width, video_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0] + GL_TEXTURE_2D, 0, bit_depth_info.internal_format, video_width, video_height, 0, bit_depth_info.pixel_format, bit_depth_info.pixel_type, frame->data[0] ); } else { - f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, frame->data[0]); + f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, bit_depth_info.pixel_format, bit_depth_info.pixel_type, frame->data[0]); } diff --git a/timeline/clip.h b/timeline/clip.h index 993141ebf..1dae4828d 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -39,11 +39,6 @@ #include "marker.h" -extern "C" { -#include -#include -} - struct ClipSpeed { ClipSpeed(); double value; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 3df810087..8d3b06ecb 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -34,8 +34,6 @@ Sequence::Sequence() : { } -Sequence::~Sequence() {} - SequencePtr Sequence::copy() { SequencePtr s = std::make_shared(); s->name = QCoreApplication::translate("Sequence", "%1 (copy)").arg(name); @@ -75,6 +73,16 @@ long Sequence::getEndFrame() { return end; } +void Sequence::Close() +{ + for (int i=0;iClose(true); + } + } +} + void Sequence::RefreshClips(Media *m) { for (int i=0;i SelectedClips(bool containing = true); QVector SelectedClipIndexes(); diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index 834e4525c..18b6a1c5e 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -93,9 +93,9 @@ void FocusFilter::go_to_end() { void FocusFilter::set_viewer_fullscreen() { if (get_focused_panel() == panel_footage_viewer) { - panel_footage_viewer->viewer_widget->set_fullscreen(); + panel_footage_viewer->viewer_widget()->set_fullscreen(); } else { - panel_sequence_viewer->viewer_widget->set_fullscreen(); + panel_sequence_viewer->viewer_widget()->set_fullscreen(); } } diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 9c1dcd285..77043a8b3 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -26,7 +26,7 @@ #include #include "undo/undo.h" -#include "panels/viewer.h" +#include "global/timing.h" #include "global/config.h" #include "global/math.h" #include "global/debug.h" diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 74bf60fd7..731520496 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -271,7 +271,6 @@ MainWindow::MainWindow(QWidget *parent) : olive::icon::Initialize(); -#ifndef NO_OCIO // Load OpenColorIO configuration if set if (olive::CurrentConfig.enable_color_management && !olive::CurrentConfig.ocio_config_path.isEmpty()) { try { @@ -283,7 +282,6 @@ MainWindow::MainWindow(QWidget *parent) : QMessageBox::Ok); } } -#endif alloc_panels(this); @@ -971,8 +969,8 @@ void MainWindow::closeEvent(QCloseEvent *e) { olive::Global->set_sequence(nullptr); - panel_footage_viewer->viewer_widget->close_window(); - panel_sequence_viewer->viewer_widget->close_window(); + panel_footage_viewer->viewer_widget()->close_window(); + panel_sequence_viewer->viewer_widget()->close_window(); panel_footage_viewer->set_main_sequence(); diff --git a/ui/menuhelper.cpp b/ui/menuhelper.cpp index 9553eada1..0e0f448f0 100644 --- a/ui/menuhelper.cpp +++ b/ui/menuhelper.cpp @@ -242,7 +242,7 @@ void MenuHelper::set_titlesafe_from_menu() { } - panel_sequence_viewer->viewer_widget->update(); + panel_sequence_viewer->viewer_widget()->update(); } void MenuHelper::set_autoscroll() { diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index e16a3d223..81816492a 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -28,11 +28,10 @@ #include "mainwindow.h" #include "panels/panels.h" -#include "panels/timeline.h" #include "timeline/sequence.h" #include "undo/undo.h" #include "project/media.h" -#include "panels/viewer.h" +#include "global/timing.h" #include "global/config.h" #include "global/global.h" #include "ui/menu.h" diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 7bfa44f96..00ce56aca 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -39,6 +39,7 @@ #include "project/projectelements.h" #include "rendering/audio.h" #include "global/config.h" +#include "global/timing.h" #include "ui/sourcetable.h" #include "ui/sourceiconview.h" #include "undo/undo.h" diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 488fc9678..3261a25cc 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -49,6 +49,7 @@ extern "C" { #include "global/config.h" #include "global/debug.h" #include "global/math.h" +#include "global/timing.h" #include "ui/collapsiblewidget.h" #include "undo/undo.h" #include "project/media.h" @@ -278,10 +279,6 @@ QMatrix4x4 ViewerWidget::get_matrix() void ViewerWidget::context_destroy() { makeCurrent(); - if (viewer->seq != nullptr) { - close_active_clips(viewer->seq.get()); - } - renderer.delete_ctx(); title_safe_area_buffer_.destroy(); diff --git a/undo/undo.cpp b/undo/undo.cpp index ba073b3fa..dcefa41ca 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -675,13 +675,13 @@ void SetClipProperty::MainLoop(bool undo) void SetClipProperty::doUndo() { MainLoop(true); - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } void SetClipProperty::doRedo() { MainLoop(false); - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } AddMarkerAction::AddMarkerAction(QVector* m, long t, QString n) { @@ -896,7 +896,7 @@ void CloseAllClipsCommand::doUndo() { } void CloseAllClipsCommand::doRedo() { - close_active_clips(olive::ActiveSequence.get()); + olive::ActiveSequence->Close(); } UpdateFootageTooltip::UpdateFootageTooltip(Media *i) { @@ -1112,7 +1112,7 @@ void UpdateViewer::doUndo() { } void UpdateViewer::doRedo() { - panel_sequence_viewer->viewer_widget->frame_update(); + panel_sequence_viewer->viewer_widget()->frame_update(); } SetEffectData::SetEffectData(Effect *e, const QByteArray &s) { From 6b40a4ccf8cec58be2f64a74fa4bfb08481c32c6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 21:58:47 +1100 Subject: [PATCH 046/133] hdr video can be used in the pipeline without conversion to rgba8888 --- rendering/bitdepths.cpp | 8 ++++++-- rendering/bitdepths.h | 1 + rendering/framebufferobject.cpp | 2 -- timeline/clip.cpp | 6 ++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rendering/bitdepths.cpp b/rendering/bitdepths.cpp index bad6dc6f7..f2a623088 100644 --- a/rendering/bitdepths.cpp +++ b/rendering/bitdepths.cpp @@ -35,21 +35,25 @@ void InitializeBitDepths() { bit_depths[PIX_FMT_RGBA8].internal_format = GL_RGBA8; bit_depths[PIX_FMT_RGBA8].pixel_format = GL_RGBA; bit_depths[PIX_FMT_RGBA8].pixel_type = GL_UNSIGNED_BYTE; + bit_depths[PIX_FMT_RGBA8].bytes_per_pixel = 4; bit_depths[PIX_FMT_RGBA16].name = QCoreApplication::translate("bitdepths", "16-bit Integer"); - bit_depths[PIX_FMT_RGBA16].internal_format = GL_RGBA16UI; - bit_depths[PIX_FMT_RGBA16].pixel_format = GL_RGBA_INTEGER; + bit_depths[PIX_FMT_RGBA16].internal_format = GL_RGBA16; + bit_depths[PIX_FMT_RGBA16].pixel_format = GL_RGBA; bit_depths[PIX_FMT_RGBA16].pixel_type = GL_UNSIGNED_SHORT; + bit_depths[PIX_FMT_RGBA16].bytes_per_pixel = 8; bit_depths[PIX_FMT_RGBA16F].name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); bit_depths[PIX_FMT_RGBA16F].internal_format = GL_RGBA16F; bit_depths[PIX_FMT_RGBA16F].pixel_format = GL_RGBA; bit_depths[PIX_FMT_RGBA16F].pixel_type = GL_HALF_FLOAT; + bit_depths[PIX_FMT_RGBA16F].bytes_per_pixel = 8; bit_depths[PIX_FMT_RGBA32F].name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); bit_depths[PIX_FMT_RGBA32F].internal_format = GL_RGBA32F; bit_depths[PIX_FMT_RGBA32F].pixel_format = GL_RGBA; bit_depths[PIX_FMT_RGBA32F].pixel_type = GL_FLOAT; + bit_depths[PIX_FMT_RGBA32F].bytes_per_pixel = 16; } diff --git a/rendering/bitdepths.h b/rendering/bitdepths.h index 67393440b..0cbd70bb5 100644 --- a/rendering/bitdepths.h +++ b/rendering/bitdepths.h @@ -33,6 +33,7 @@ struct BitDepthInfo { GLint internal_format; GLenum pixel_format; GLenum pixel_type; + int bytes_per_pixel; }; /** diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 8f9eed3d7..00360ca8f 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -69,8 +69,6 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) olive::CurrentConfig.export_bit_depth : olive::CurrentConfig.playback_bit_depth); - qDebug() << "hello" << bit_depth.name; - ctx->functions()->glTexImage2D( GL_TEXTURE_2D, 0, diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 36f5bec8c..3f0a6b516 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -37,8 +37,6 @@ #include "global/debug.h" #include "global/timing.h" -const int kRGBAComponentCount = 4; - Clip::Clip(Sequence* s) : sequence(s), cacher(this), @@ -584,13 +582,13 @@ bool Clip::Retrieve() } - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/kRGBAComponentCount); - int video_width = cacher.media_width(); int video_height = cacher.media_height(); const olive::rendering::BitDepthInfo& bit_depth_info = olive::rendering::bit_depths.at(cacher.media_pixel_format()); + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/bit_depth_info.bytes_per_pixel); + if (allocate_data) { // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure From 091889fbe07ce1b9a3ff6fa3292c345d56ced0de Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 22:32:24 +1100 Subject: [PATCH 047/133] show loading icon for proxy generation --- dialogs/preferencesdialog.cpp | 104 +++++++++++++--------- dialogs/preferencesdialog.h | 13 +++ global/config.cpp | 4 +- main.cpp | 4 +- olive.pro | 8 +- project/media.cpp | 11 ++- project/media.h | 2 + project/proxygenerator.cpp | 31 ++++--- rendering/bitdepths.cpp | 62 ------------- rendering/cacher.cpp | 6 +- rendering/cacher.h | 6 +- rendering/framebuffercollection.cpp | 68 ++++++++++++++ rendering/framebuffercollection.h | 27 ++++++ rendering/framebufferobject.cpp | 8 +- rendering/pixelformats.cpp | 60 +++++++++++++ rendering/{bitdepths.h => pixelformats.h} | 12 ++- timeline/clip.cpp | 25 +++++- ui/mediaiconservice.cpp | 5 +- ui/mediaiconservice.h | 2 +- 19 files changed, 310 insertions(+), 148 deletions(-) delete mode 100644 rendering/bitdepths.cpp create mode 100644 rendering/framebuffercollection.cpp create mode 100644 rendering/framebuffercollection.h create mode 100644 rendering/pixelformats.cpp rename rendering/{bitdepths.h => pixelformats.h} (82%) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 9bf70b3b9..ca1a85ebb 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -48,7 +48,7 @@ #include "global/config.h" #include "global/path.h" #include "rendering/audio.h" -#include "rendering/bitdepths.h" +#include "rendering/pixelformats.h" #include "panels/panels.h" #include "ui/columnedgridlayout.h" #include "ui/mainwindow.h" @@ -170,36 +170,62 @@ void PreferencesDialog::delete_previews(PreviewDeleteTypes type) { void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) { - // Get current display name (if the config is empty, get the current default display) - QString current_display = olive::CurrentConfig.ocio_display; - if (current_display.isEmpty()) { - current_display = config->getDefaultDisplay(); - } + if (config == nullptr) { - // Populate the display menu - ocio_display->clear(); - for (int i=0;igetNumDisplays();i++) { - ocio_display->addItem(config->getDisplay(i)); + // Just clear everything + ocio_display->clear(); + ocio_view->clear(); + ocio_look->clear(); - // Check if this index is the currently selected - if (config->getDisplay(i) == current_display) { - ocio_display->setCurrentIndex(i); + } else { + + // Get current display name (if the config is empty, get the current default display) + QString current_display = olive::CurrentConfig.ocio_display; + if (current_display.isEmpty()) { + current_display = config->getDefaultDisplay(); } - } - update_ocio_view_menu(config); + // Populate the display menu + ocio_display->clear(); + for (int i=0;igetNumDisplays();i++) { + ocio_display->addItem(config->getDisplay(i)); - // Populate the look menu - ocio_look->clear(); - ocio_look->addItem(tr("(None)"), QString()); - for (int i=0;igetNumLooks();i++) { - const char* look = config->getLookNameByIndex(i); - - ocio_look->addItem(look, look); - - if (look == olive::CurrentConfig.ocio_look) { - ocio_look->setCurrentIndex(i); + // Check if this index is the currently selected + if (config->getDisplay(i) == current_display) { + ocio_display->setCurrentIndex(i); + } } + + update_ocio_view_menu(config); + + // Populate the look menu + ocio_look->clear(); + ocio_look->addItem(tr("(None)"), QString()); + for (int i=0;igetNumLooks();i++) { + const char* look = config->getLookNameByIndex(i); + + ocio_look->addItem(look, look); + + if (look == olive::CurrentConfig.ocio_look) { + ocio_look->setCurrentIndex(i); + } + } + + } +} + +OCIO::ConstConfigRcPtr PreferencesDialog::TestOCIOConfig(const QString &url) +{ + // Check whether OCIO can load it + try { + OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); + return config; + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + return nullptr; } } @@ -231,13 +257,13 @@ void PreferencesDialog::update_ocio_view_menu(OCIO::ConstConfigRcPtr config) void PreferencesDialog::update_ocio_config(const QString &s) { - if (!s.isEmpty() && QFileInfo::exists(s)) { - try { - OCIO::ConstConfigRcPtr file_config = OCIO::Config::CreateFromFile(s.toUtf8()); + OCIO::ConstConfigRcPtr file_config = nullptr; - populate_ocio_menus(file_config); - } catch (OCIO::Exception& e) {} + if (!s.isEmpty() && QFileInfo::exists(s)) { + file_config = TestOCIOConfig(s); } + + populate_ocio_menus(file_config); } void PreferencesDialog::AddBoolPair(QCheckBox *ui, bool *value, bool restart_required) @@ -315,13 +341,9 @@ void PreferencesDialog::accept() { } else if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { // Check whether OCIO can load it - try { - OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); - } catch (OCIO::Exception& e) { - QMessageBox::critical(this, - tr("OpenColorIO Config Error"), - tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), - QMessageBox::Ok); + OCIO::ConstConfigRcPtr file_config = TestOCIOConfig(ocio_config_file->text().toUtf8()); + + if (file_config == nullptr) { return; } @@ -1066,8 +1088,8 @@ void PreferencesDialog::setup_ui() { // COLOR MANAGEMENT -> Bit Depth -> Playback playback_bit_depth = new QComboBox(); - for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); + for (int i=0;iaddItem(olive::pixel_formats.at(i).name, i); } playback_bit_depth->setCurrentIndex(olive::CurrentConfig.playback_bit_depth); bit_depth_groupbox_layout->addWidget(new QLabel(tr("Playback (Offline):")), 0, 0); @@ -1075,8 +1097,8 @@ void PreferencesDialog::setup_ui() { // COLOR MANAGEMENT -> Bit Depth -> Export export_bit_depth = new QComboBox(); - for (int i=0;iaddItem(olive::rendering::bit_depths.at(i).name, i); + for (int i=0;iaddItem(olive::pixel_formats.at(i).name, i); } export_bit_depth->setCurrentIndex(olive::CurrentConfig.export_bit_depth); bit_depth_groupbox_layout->addWidget(new QLabel(tr("Export (Online):")), 0, 2); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index a7cb00c9a..eb6c5663c 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -301,6 +301,19 @@ private: */ QVector key_shortcut_fields; + /** + * @brief Tests an OpenColorIO configuration file to determine whether it's valid and throws a messagebox if not + * + * @param url + * + * URL to the OpenColorIO configuration file. + * + * @return + * + * A OCIO::ConstConfigRcPtr config pointer if the configuration file is valid, nullptr if not. + */ + OCIO::ConstConfigRcPtr TestOCIOConfig(const QString& url); + /** * @brief Add an automated QCheckBox+boolean value pair * diff --git a/global/config.cpp b/global/config.cpp index e28ccbe1d..760fb789a 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -80,8 +80,8 @@ Config::Config() default_sequence_framerate(29.97), default_sequence_audio_frequency(48000), default_sequence_audio_channel_layout(3), - playback_bit_depth(olive::rendering::PIX_FMT_RGBA16F), - export_bit_depth(olive::rendering::PIX_FMT_RGBA32F) + playback_bit_depth(olive::PIX_FMT_RGBA16F), + export_bit_depth(olive::PIX_FMT_RGBA32F) {} void Config::load(QString path) { diff --git a/main.cpp b/main.cpp index db19302bc..395ac1813 100644 --- a/main.cpp +++ b/main.cpp @@ -24,7 +24,7 @@ #include "global/config.h" #include "global/global.h" #include "panels/timeline.h" -#include "rendering/bitdepths.h" +#include "rendering/pixelformats.h" #include "ui/mediaiconservice.h" #include "ui/mainwindow.h" @@ -133,7 +133,7 @@ int main(int argc, char *argv[]) { olive::timeline::MultiplyTrackSizesByDPI(); // set up rendering bit depths - olive::rendering::InitializeBitDepths(); + olive::InitializePixelFormats(); // connect main window's first paint to global's init finished function QObject::connect(&w, SIGNAL(finished_first_paint()), olive::Global.get(), SLOT(finished_initialize()), Qt::QueuedConnection); diff --git a/olive.pro b/olive.pro index 58b62e807..5f3e23a85 100644 --- a/olive.pro +++ b/olive.pro @@ -173,12 +173,12 @@ SOURCES += \ ui/blur.cpp \ ui/menu.cpp \ rendering/qopenglshaderprogramptr.cpp \ - rendering/bitdepths.cpp \ timeline/mediaimportdata.cpp \ dialogs/autocutsilencedialog.cpp \ ui/columnedgridlayout.cpp \ rendering/shadergenerators.cpp \ - global/timing.cpp + global/timing.cpp \ + rendering/pixelformats.cpp HEADERS += \ ui/mainwindow.h \ @@ -305,12 +305,12 @@ HEADERS += \ ui/blur.h \ ui/menu.h \ rendering/qopenglshaderprogramptr.h \ - rendering/bitdepths.h \ timeline/mediaimportdata.h \ dialogs/autocutsilencedialog.h \ ui/columnedgridlayout.h \ rendering/shadergenerators.h \ - global/timing.h + global/timing.h \ + rendering/pixelformats.h FORMS += diff --git a/project/media.cpp b/project/media.cpp index 1e77305a2..7a908f7ef 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -63,7 +63,8 @@ QString get_channel_layout_name(int channels, uint64_t layout) { Media::Media() : root(false), - type(-1) + type(-1), + disable_thumbnail_(false) { } @@ -230,6 +231,11 @@ void Media::set_name(const QString &n) { } } +void Media::disable_thumbnail(bool disable) +{ + disable_thumbnail_ = disable; +} + double Media::get_frame_rate(int stream) { switch (get_type()) { case MEDIA_TYPE_FOOTAGE: @@ -308,7 +314,8 @@ QVariant Media::data(int column, int role) { if (column == 0) { if (get_type() == MEDIA_TYPE_FOOTAGE) { Footage* f = to_footage(); - if (f->video_tracks.size() > 0 + if (!disable_thumbnail_ + && f->video_tracks.size() > 0 && f->video_tracks.at(0).preview_done) { return QIcon(QPixmap::fromImage(f->video_tracks.at(0).video_preview)); } diff --git a/project/media.h b/project/media.h index 7462b28f9..a604e2180 100644 --- a/project/media.h +++ b/project/media.h @@ -60,6 +60,7 @@ public: int get_type(); const QString& get_name(); void set_name(const QString& n); + void disable_thumbnail(bool disable); double get_frame_rate(int stream = -1); int get_sampling_rate(int stream = -1); @@ -95,6 +96,7 @@ private: QString folder_name; QString tooltip; QIcon icon; + bool disable_thumbnail_; }; #endif // MEDIA_H diff --git a/project/proxygenerator.cpp b/project/proxygenerator.cpp index 7a8ee8b6f..f90f5699d 100644 --- a/project/proxygenerator.cpp +++ b/project/proxygenerator.cpp @@ -20,23 +20,24 @@ #include "proxygenerator.h" -#include "global/path.h" -#include "project/previewgenerator.h" -#include "ui/mainwindow.h" - -#include -#include -#include -#include - -#include - extern "C" { #include #include #include } +#include +#include +#include +#include +#include + +#include "global/path.h" +#include "project/previewgenerator.h" +#include "ui/mediaiconservice.h" +#include "ui/mainwindow.h" + +// TODO provide more codecs than just this one enum AVCodecID temp_enc_codec = AV_CODEC_ID_PRORES; ProxyGenerator::ProxyGenerator() : cancelled(false) {} @@ -123,7 +124,7 @@ void ProxyGenerator::transcode(const ProxyInfo& info) { enc_ctx->width = qFloor(dec_ctx->width*info.size_multiplier); enc_ctx->height = qFloor(dec_ctx->height*info.size_multiplier); enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio; - enc_ctx->pix_fmt = enc_codec->pix_fmts[0]; + enc_ctx->pix_fmt = avcodec_find_best_pix_fmt_of_list(enc_codec->pix_fmts, dec_ctx->pix_fmt, 1, nullptr); enc_ctx->framerate = dec_ctx->framerate; enc_ctx->time_base = in_stream->time_base; out_stream->time_base = in_stream->time_base; @@ -356,9 +357,15 @@ void ProxyGenerator::run() { // set skip to false skip = false; + // set media icon to animated loading icon + olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_LOADING); + // transcode proxy transcode(info); + // set media icon back to video + olive::media_icon_service->SetMediaIcon(info.media, ICON_TYPE_VIDEO); + // we're finished with this proxy, remove it proxy_queue.removeFirst(); diff --git a/rendering/bitdepths.cpp b/rendering/bitdepths.cpp deleted file mode 100644 index f2a623088..000000000 --- a/rendering/bitdepths.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*** - - 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 "bitdepths.h" - -#include - -namespace olive { -namespace rendering { - -QVector bit_depths; - -void InitializeBitDepths() { - - bit_depths.resize(PIX_FMT_COUNT); - - bit_depths[PIX_FMT_RGBA8].name = QCoreApplication::translate("bitdepths", "8-bit"); - bit_depths[PIX_FMT_RGBA8].internal_format = GL_RGBA8; - bit_depths[PIX_FMT_RGBA8].pixel_format = GL_RGBA; - bit_depths[PIX_FMT_RGBA8].pixel_type = GL_UNSIGNED_BYTE; - bit_depths[PIX_FMT_RGBA8].bytes_per_pixel = 4; - - bit_depths[PIX_FMT_RGBA16].name = QCoreApplication::translate("bitdepths", "16-bit Integer"); - bit_depths[PIX_FMT_RGBA16].internal_format = GL_RGBA16; - bit_depths[PIX_FMT_RGBA16].pixel_format = GL_RGBA; - bit_depths[PIX_FMT_RGBA16].pixel_type = GL_UNSIGNED_SHORT; - bit_depths[PIX_FMT_RGBA16].bytes_per_pixel = 8; - - bit_depths[PIX_FMT_RGBA16F].name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); - bit_depths[PIX_FMT_RGBA16F].internal_format = GL_RGBA16F; - bit_depths[PIX_FMT_RGBA16F].pixel_format = GL_RGBA; - bit_depths[PIX_FMT_RGBA16F].pixel_type = GL_HALF_FLOAT; - bit_depths[PIX_FMT_RGBA16F].bytes_per_pixel = 8; - - bit_depths[PIX_FMT_RGBA32F].name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); - bit_depths[PIX_FMT_RGBA32F].internal_format = GL_RGBA32F; - bit_depths[PIX_FMT_RGBA32F].pixel_format = GL_RGBA; - bit_depths[PIX_FMT_RGBA32F].pixel_type = GL_FLOAT; - bit_depths[PIX_FMT_RGBA32F].bytes_per_pixel = 16; - -} - -} -} - diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 2ae5dfd52..917f79b6f 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -999,10 +999,10 @@ void Cacher::OpenWorker() { if (pix_fmt == AV_PIX_FMT_RGBA) { qDebug() << "This is an 8-bit image."; - media_pixel_format_ = olive::rendering::PIX_FMT_RGBA8; + media_pixel_format_ = olive::PIX_FMT_RGBA8; } else { qDebug() << "This is an HDR image."; - media_pixel_format_ = olive::rendering::PIX_FMT_RGBA16; + media_pixel_format_ = olive::PIX_FMT_RGBA16; } const char* chosen_format = av_get_pix_fmt_name(pix_fmt); @@ -1353,7 +1353,7 @@ ClipQueue *Cacher::queue() return &queue_; } -const olive::rendering::PixelFormat &Cacher::media_pixel_format() +const olive::PixelFormat &Cacher::media_pixel_format() { return media_pixel_format_; } diff --git a/rendering/cacher.h b/rendering/cacher.h index 0cda89d61..0b215a673 100644 --- a/rendering/cacher.h +++ b/rendering/cacher.h @@ -43,7 +43,7 @@ extern "C" { #include #include "rendering/clipqueue.h" -#include "rendering/bitdepths.h" +#include "rendering/pixelformats.h" class Clip; @@ -262,7 +262,7 @@ public: * * A olive::rendering::PixelFormat value corresponding to a member of olive::rendering::bit_depths. */ - const olive::rendering::PixelFormat& media_pixel_format(); + const olive::PixelFormat& media_pixel_format(); private: /** @@ -596,7 +596,7 @@ private: /** * @brief Internal struct holding bit depth information for the current media */ - olive::rendering::PixelFormat media_pixel_format_; + olive::PixelFormat media_pixel_format_; }; #endif // CACHER_H diff --git a/rendering/framebuffercollection.cpp b/rendering/framebuffercollection.cpp new file mode 100644 index 000000000..f8f3ed2b0 --- /dev/null +++ b/rendering/framebuffercollection.cpp @@ -0,0 +1,68 @@ +#include "framebuffercollection.h" + +FramebufferCollection::FramebufferCollection() +{ + +} + +void FramebufferCollection::Create(QOpenGLContext* ctx, + int width, + int height, + int count) +{ + Q_ASSERT(count > 1); + + fbo_.resize(count); + for (int i=0;i + +#include "framebufferobject.h" + +class FramebufferCollection +{ +public: + FramebufferCollection(); + + void Create(QOpenGLContext *ctx, int width, int height, int count); + void Destroy(); + + GLuint CurrentTexture(); + const FramebufferObject& CurrentFramebuffer(); + const FramebufferObject& NextFramebuffer(); + bool TextureBelongsToCollection(GLuint tex); + + bool IsCreated(); +private: + QVector fbo_; + int fbo_index_; +}; + +#endif // FRAMEBUFFERCOLLECTION_H diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 00360ca8f..a8a0980d8 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -26,7 +26,7 @@ #include "global/config.h" #include "global/global.h" -#include "bitdepths.h" +#include "pixelformats.h" FramebufferObject::FramebufferObject() : buffer_(0), @@ -65,9 +65,9 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) ctx->functions()->glBindTexture(GL_TEXTURE_2D, texture_); // allocate storage for texture - const olive::rendering::BitDepthInfo& bit_depth = olive::rendering::bit_depths.at(olive::Global->is_exporting() ? - olive::CurrentConfig.export_bit_depth : - olive::CurrentConfig.playback_bit_depth); + const olive::PixelFormatInfo& bit_depth = olive::pixel_formats.at(olive::Global->is_exporting() ? + olive::CurrentConfig.export_bit_depth : + olive::CurrentConfig.playback_bit_depth); ctx->functions()->glTexImage2D( GL_TEXTURE_2D, diff --git a/rendering/pixelformats.cpp b/rendering/pixelformats.cpp new file mode 100644 index 000000000..6f970e5d6 --- /dev/null +++ b/rendering/pixelformats.cpp @@ -0,0 +1,60 @@ +/*** + + 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 "pixelformats.h" + +#include + +namespace olive { + +QVector pixel_formats; + +void InitializePixelFormats() { + + pixel_formats.resize(PIX_FMT_COUNT); + + pixel_formats[PIX_FMT_RGBA8].name = QCoreApplication::translate("bitdepths", "8-bit"); + pixel_formats[PIX_FMT_RGBA8].internal_format = GL_RGBA8; + pixel_formats[PIX_FMT_RGBA8].pixel_format = GL_RGBA; + pixel_formats[PIX_FMT_RGBA8].pixel_type = GL_UNSIGNED_BYTE; + pixel_formats[PIX_FMT_RGBA8].bytes_per_pixel = 4; + + pixel_formats[PIX_FMT_RGBA16].name = QCoreApplication::translate("bitdepths", "16-bit Integer"); + pixel_formats[PIX_FMT_RGBA16].internal_format = GL_RGBA16; + pixel_formats[PIX_FMT_RGBA16].pixel_format = GL_RGBA; + pixel_formats[PIX_FMT_RGBA16].pixel_type = GL_UNSIGNED_SHORT; + pixel_formats[PIX_FMT_RGBA16].bytes_per_pixel = 8; + + pixel_formats[PIX_FMT_RGBA16F].name = QCoreApplication::translate("bitdepths", "Half-Float (16-bit)"); + pixel_formats[PIX_FMT_RGBA16F].internal_format = GL_RGBA16F; + pixel_formats[PIX_FMT_RGBA16F].pixel_format = GL_RGBA; + pixel_formats[PIX_FMT_RGBA16F].pixel_type = GL_HALF_FLOAT; + pixel_formats[PIX_FMT_RGBA16F].bytes_per_pixel = 8; + + pixel_formats[PIX_FMT_RGBA32F].name = QCoreApplication::translate("bitdepths", "Full-Float (32-bit)"); + pixel_formats[PIX_FMT_RGBA32F].internal_format = GL_RGBA32F; + pixel_formats[PIX_FMT_RGBA32F].pixel_format = GL_RGBA; + pixel_formats[PIX_FMT_RGBA32F].pixel_type = GL_FLOAT; + pixel_formats[PIX_FMT_RGBA32F].bytes_per_pixel = 16; + +} + +} + diff --git a/rendering/bitdepths.h b/rendering/pixelformats.h similarity index 82% rename from rendering/bitdepths.h rename to rendering/pixelformats.h index 0cbd70bb5..2e913c531 100644 --- a/rendering/bitdepths.h +++ b/rendering/pixelformats.h @@ -26,9 +26,8 @@ #include namespace olive { -namespace rendering { -struct BitDepthInfo { +struct PixelFormatInfo { QString name; GLint internal_format; GLenum pixel_format; @@ -37,10 +36,10 @@ struct BitDepthInfo { }; /** - * @brief The OlivePixelFormat enum + * @brief The PixelFormat enum * * Olive's internal supported pixel formats. With the exception of OLIVE_PIX_FMT_COUNT, these must all - * be defined in InitializeBitDepths(). + * be defined in InitializePixelFormats(). */ enum PixelFormat { PIX_FMT_RGBA8, @@ -50,11 +49,10 @@ enum PixelFormat { PIX_FMT_COUNT }; -extern QVector bit_depths; +extern QVector pixel_formats; -void InitializeBitDepths(); +void InitializePixelFormats(); -} } #endif // BITDEPTHS_H diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 3f0a6b516..c5c62f9e5 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -585,9 +585,9 @@ bool Clip::Retrieve() int video_width = cacher.media_width(); int video_height = cacher.media_height(); - const olive::rendering::BitDepthInfo& bit_depth_info = olive::rendering::bit_depths.at(cacher.media_pixel_format()); + const olive::PixelFormatInfo& pix_fmt_info = olive::pixel_formats.at(cacher.media_pixel_format()); - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/bit_depth_info.bytes_per_pixel); + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/pix_fmt_info.bytes_per_pixel); if (allocate_data) { @@ -595,12 +595,29 @@ bool Clip::Retrieve() // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the // composition f->glTexImage2D( - GL_TEXTURE_2D, 0, bit_depth_info.internal_format, video_width, video_height, 0, bit_depth_info.pixel_format, bit_depth_info.pixel_type, frame->data[0] + GL_TEXTURE_2D, + 0, + pix_fmt_info.internal_format, + video_width, + video_height, + 0, + pix_fmt_info.pixel_format, + pix_fmt_info.pixel_type, + frame->data[0] ); } else { - f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, video_width, video_height, bit_depth_info.pixel_format, bit_depth_info.pixel_type, frame->data[0]); + f->glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + video_width, + video_height, + pix_fmt_info.pixel_format, + pix_fmt_info.pixel_type, + frame->data[0] + ); } diff --git a/ui/mediaiconservice.cpp b/ui/mediaiconservice.cpp index ad0626cf0..955d31db0 100644 --- a/ui/mediaiconservice.cpp +++ b/ui/mediaiconservice.cpp @@ -37,12 +37,13 @@ MediaIconService::MediaIconService() { throbber_pixmap_ = QPixmap(":/icons/throbber.png"); } -void MediaIconService::SetMediaIcon(Media *media, int icon_type) { +void MediaIconService::SetMediaIcon(Media *media, IconType icon_type) { // if this icon is already part of the throbber animation loop, remove it if (throbber_items_.contains(media)) { throbber_lock_.lock(); throbber_items_.removeAll(media); + media->disable_thumbnail(false); throbber_lock_.unlock(); @@ -66,6 +67,8 @@ void MediaIconService::SetMediaIcon(Media *media, int icon_type) { case ICON_TYPE_LOADING: throbber_items_.append(media); + media->disable_thumbnail(true); + // if the animation timer isn't running, start it if (!throbber_animator_.isActive()) { // set starting frame to 0 diff --git a/ui/mediaiconservice.h b/ui/mediaiconservice.h index 3b3062e05..70ed586fd 100644 --- a/ui/mediaiconservice.h +++ b/ui/mediaiconservice.h @@ -40,7 +40,7 @@ class MediaIconService : public QObject { public: MediaIconService(); public slots: - void SetMediaIcon(Media* media, int icon_type); + void SetMediaIcon(Media* media, IconType icon_type); signals: void IconChanged(); private slots: From 10f8a4b76a4df6521109c9b9298d011632fd4a8d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 22:37:05 +1100 Subject: [PATCH 048/133] updated image saving for fully premultiplied pipeline --- rendering/renderthread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index ab839350e..f9699a220 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -277,7 +277,7 @@ void RenderThread::paint() { queued = true; } else { f->glBindFramebuffer(GL_READ_FRAMEBUFFER, composite_buffer.buffer()); - QImage img(tex_width, tex_height, QImage::Format_RGBA8888); + QImage img(tex_width, tex_height, QImage::Format_RGBA8888_Premultiplied); f->glReadPixels(0, 0, tex_width, tex_height, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); img.save(save_fn); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); From cab38b5e90c53666001ece0abc9bd98a2799ab2c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 26 Mar 2019 22:48:26 +1100 Subject: [PATCH 049/133] added option to always use originals rather than proxies on export --- dialogs/exportdialog.cpp | 2 +- dialogs/preferencesdialog.cpp | 16 +++++++++++++--- global/config.cpp | 7 ++++++- global/config.h | 5 +++++ rendering/cacher.cpp | 4 +++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 4b36513f9..b667ef3d3 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -658,7 +658,7 @@ void ExportDialog::comp_type_changed(int) { break; case COMPRESSION_TYPE_CFR: videoBitrateLabel->setText(tr("Quality (CRF):")); - videobitrateSpinbox->setValue(36); + videobitrateSpinbox->setValue(23); videobitrateSpinbox->setMaximum(51); videobitrateSpinbox->setToolTip(tr("Quality Factor:\n\n0 = lossless\n17-18 = visually lossless (compressed, but unnoticeable)\n23 = high quality\n51 = lowest quality possible")); break; diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index ca1a85ebb..8a23b3275 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -778,17 +778,27 @@ void PreferencesDialog::setup_ui() { row++; + QHBoxLayout* misc_general = new QHBoxLayout(); + // General -> Use Software Fallbacks When Possible QCheckBox* use_software_fallbacks_checkbox = new QCheckBox(tr("Use Software Fallbacks When Possible")); AddBoolPair(use_software_fallbacks_checkbox, &olive::CurrentConfig.use_software_fallback, true); - general_layout->addWidget(use_software_fallbacks_checkbox, row, 0, 1, 4); + misc_general->addWidget(use_software_fallbacks_checkbox); - row++; + // General -> Don't Use Proxies When Exporting + QCheckBox* dont_use_proxies_when_exporting = new QCheckBox(tr("Don't Use Proxies When Exporting")); + dont_use_proxies_when_exporting->setToolTip(tr("Use originals instead of proxies when exporting")); + AddBoolPair(dont_use_proxies_when_exporting, &olive::CurrentConfig.dont_use_proxies_on_export); + misc_general->addWidget(dont_use_proxies_when_exporting); // General -> Default Sequence Settings QPushButton* default_sequence_settings = new QPushButton(tr("Default Sequence Settings")); connect(default_sequence_settings, SIGNAL(clicked(bool)), this, SLOT(edit_default_sequence_settings())); - general_layout->addWidget(default_sequence_settings); + misc_general->addWidget(default_sequence_settings); + + general_layout->addLayout(misc_general, row, 0, 1, 5); + + row++; tabWidget->addTab(general_tab, tr("General")); diff --git a/global/config.cpp b/global/config.cpp index 760fb789a..aaaaa7987 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -81,7 +81,8 @@ Config::Config() default_sequence_audio_frequency(48000), default_sequence_audio_channel_layout(3), playback_bit_depth(olive::PIX_FMT_RGBA16F), - export_bit_depth(olive::PIX_FMT_RGBA32F) + export_bit_depth(olive::PIX_FMT_RGBA32F), + dont_use_proxies_on_export(true) {} void Config::load(QString path) { @@ -254,6 +255,9 @@ void Config::load(QString path) { } else if (stream.name() == "ExportBitDepth") { stream.readNext(); export_bit_depth = stream.text().toInt(); + } else if (stream.name() == "DontUseProxiesOnExport") { + stream.readNext(); + dont_use_proxies_on_export = (stream.text() == "1"); } } } @@ -332,6 +336,7 @@ void Config::save(QString path) { stream.writeTextElement("DefaultSequenceAudioLayout", QString::number(default_sequence_audio_channel_layout)); stream.writeTextElement("PlaybackBitDepth", QString::number(playback_bit_depth)); stream.writeTextElement("ExportBitDepth", QString::number(export_bit_depth)); + stream.writeTextElement("DontUseProxiesOnExport", QString::number(dont_use_proxies_on_export)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/global/config.h b/global/config.h index 2f9ebd9e6..ef5372b16 100644 --- a/global/config.h +++ b/global/config.h @@ -603,6 +603,11 @@ struct Config { */ int export_bit_depth; + /** + * @brief Don't use proxies on export (use originals instead) + */ + bool dont_use_proxies_on_export; + /** * @brief Load config from file * diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 917f79b6f..e56915bee 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -39,6 +39,7 @@ #include "rendering/renderfunctions.h" #include "global/timing.h" #include "global/config.h" +#include "global/global.h" #include "global/debug.h" #include "ui/mainwindow.h" @@ -888,7 +889,8 @@ void Cacher::OpenWorker() { QByteArray ba; // do we have a proxy? - if (m->proxy + if ((!olive::Global->is_exporting() || !olive::CurrentConfig.dont_use_proxies_on_export) + && m->proxy && !m->proxy_path.isEmpty() && QFileInfo::exists(m->proxy_path)) { ba = m->proxy_path.toUtf8(); From fe9eb8754f1ecd7a0afef7406adc57fbb19b5b6c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 27 Mar 2019 00:15:20 +1100 Subject: [PATCH 050/133] can set default input color space --- dialogs/mediapropertiesdialog.h | 3 ++ dialogs/preferencesdialog.cpp | 31 ++++++++++++++++---- dialogs/preferencesdialog.h | 1 + effects/effect.cpp | 41 ++++++++++++++++----------- effects/effect.h | 3 +- global/config.cpp | 16 +++++++++++ global/config.h | 7 +++++ olive.pro | 1 - project/footage.cpp | 3 +- rendering/pixelformats.h | 23 +++++++++------ rendering/qopenglshaderprogramptr.cpp | 22 -------------- rendering/renderfunctions.cpp | 10 +++++-- rendering/shadergenerators.cpp | 17 +++++------ rendering/shadergenerators.h | 2 +- 14 files changed, 112 insertions(+), 68 deletions(-) delete mode 100644 rendering/qopenglshaderprogramptr.cpp diff --git a/dialogs/mediapropertiesdialog.h b/dialogs/mediapropertiesdialog.h index 8f9d7ebd5..5232384ae 100644 --- a/dialogs/mediapropertiesdialog.h +++ b/dialogs/mediapropertiesdialog.h @@ -83,6 +83,9 @@ private: */ QCheckBox* premultiply_alpha_setting; + /** + * @brief Setting for this media's color space + */ QComboBox* input_color_space; private slots: /** diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 8a23b3275..b39a7f8a4 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -174,11 +174,24 @@ void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) // Just clear everything ocio_display->clear(); + ocio_default_input->clear(); ocio_view->clear(); ocio_look->clear(); } else { + // Get input color spaces for setting the default input color space + ocio_default_input->clear(); + for (int i=0;igetNumColorSpaces();i++) { + QString colorspace = config->getColorSpaceNameByIndex(i); + + ocio_default_input->addItem(colorspace); + + if (colorspace == olive::CurrentConfig.ocio_default_input_colorspace) { + ocio_default_input->setCurrentIndex(i); + } + } + // Get current display name (if the config is empty, get the current default display) QString current_display = olive::CurrentConfig.ocio_display; if (current_display.isEmpty()) { @@ -439,6 +452,7 @@ void PreferencesDialog::accept() { olive::CurrentConfig.playback_bit_depth = playback_bit_depth->currentIndex(); olive::CurrentConfig.export_bit_depth = export_bit_depth->currentIndex(); olive::CurrentConfig.ocio_display = ocio_display->currentText(); + olive::CurrentConfig.ocio_default_input_colorspace = ocio_default_input->currentText(); olive::CurrentConfig.ocio_view = ocio_view->currentText(); // We use data here instead of text because there's a "(None)" option with an empty string @@ -1072,21 +1086,26 @@ void PreferencesDialog::setup_ui() { connect(ocio_config_browse_btn, SIGNAL(clicked(bool)), this, SLOT(browse_ocio_config())); opencolorio_groupbox_layout->addWidget(ocio_config_browse_btn, 0, 5); + // COLOR MANAGEMENT -> Default Input Color Space + ocio_default_input = new QComboBox(); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("Default Input Color Space:")), 1, 0); + opencolorio_groupbox_layout->addWidget(ocio_default_input, 1, 1, 1, 5); + // COLOR MANAGEMENT -> Display ocio_display = new QComboBox(); connect(ocio_display, SIGNAL(currentIndexChanged(int)), this, SLOT(update_ocio_view_menu())); - opencolorio_groupbox_layout->addWidget(new QLabel(tr("Display:")), 1, 0); - opencolorio_groupbox_layout->addWidget(ocio_display, 1, 1); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("Display:")), 2, 0); + opencolorio_groupbox_layout->addWidget(ocio_display, 2, 1); // COLOR MANAGEMENT -> View ocio_view = new QComboBox(); - opencolorio_groupbox_layout->addWidget(new QLabel(tr("View:")), 1, 2); - opencolorio_groupbox_layout->addWidget(ocio_view, 1, 3); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("View:")), 2, 2); + opencolorio_groupbox_layout->addWidget(ocio_view, 2, 3); // COLOR MANAGEMENT -> Look ocio_look = new QComboBox(); - opencolorio_groupbox_layout->addWidget(new QLabel(tr("Look:")), 1, 4); - opencolorio_groupbox_layout->addWidget(ocio_look, 1, 5); + opencolorio_groupbox_layout->addWidget(new QLabel(tr("Look:")), 2, 4); + opencolorio_groupbox_layout->addWidget(ocio_look, 2, 5); color_management_layout->addWidget(opencolorio_groupbox, row, 0); diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index eb6c5663c..721a2b505 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -264,6 +264,7 @@ private: QCheckBox* enable_color_management; QLineEdit* ocio_config_file; + QComboBox* ocio_default_input; QComboBox* ocio_display; QComboBox* ocio_view; QComboBox* ocio_look; diff --git a/effects/effect.cpp b/effects/effect.cpp index 38b9f2344..4e09326ad 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -52,6 +52,7 @@ #include "global/config.h" #include "transition.h" #include "undo/undostack.h" +#include "rendering/shadergenerators.h" #include "effects/internal/transformeffect.h" #include "effects/internal/texteffect.h" @@ -743,8 +744,23 @@ void Effect::open() { if (QOpenGLContext::currentContext() == nullptr) { qWarning() << "No current context to create a shader program for - will retry next repaint"; } else { - shader_program_ = std::make_shared(); validate_meta_path(); + + QString frag_shader_str; + QString frag_file_url = QDir(meta->path).filePath(shader_frag_path_); + QFile frag_file(frag_file_url); + if (frag_file.open(QFile::ReadOnly)) { + frag_shader_str = frag_file.readAll(); + frag_file.close(); + } else { + qWarning() << "Failed to open" << frag_file_url; + } + + if (!frag_shader_str.isEmpty()) { + shader_program_ = olive::shader::GetPipeline("process", frag_shader_str); + } + + /* bool shader_compiled = true; if (!shader_vert_path_.isEmpty()) { if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + shader_vert_path_)) { @@ -769,6 +785,7 @@ void Effect::open() { qWarning() << "Shader program failed to link"; } } + */ isOpen = true; } } else { @@ -789,21 +806,9 @@ bool Effect::is_shader_linked() { return shader_program_ != nullptr && shader_program_->isLinked(); } -void Effect::startEffect() { - if (!isOpen) { - open(); - qWarning() << "Tried to start a closed effect - opening"; - } - if (olive::CurrentRuntimeConfig.shaders_are_enabled - && (Flags() & Effect::ShaderFlag) - && shader_program_->isLinked()) { - bound = shader_program_->bind(); - } -} - -void Effect::endEffect() { - if (bound) shader_program_->release(); - bound = false; +QOpenGLShaderProgram *Effect::GetShaderPipeline() +{ + return shader_program_.get(); } int Effect::Flags() @@ -834,6 +839,8 @@ EffectPtr Effect::copy(Clip *c) { } void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { + shader_program_->bind(); + shader_program_->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height()); shader_program_->setUniformValue("time", GLfloat(timecode)); shader_program_->setUniformValue("iteration", iteration); @@ -879,6 +886,8 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { } } } + + shader_program_->release(); } void Effect::process_coords(double, GLTextureCoords&, int) {} diff --git a/effects/effect.h b/effects/effect.h index 9fbc462f8..2e3252d8b 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -172,8 +172,7 @@ public: void open(); void close(); bool is_shader_linked(); - virtual void startEffect(); - virtual void endEffect(); + QOpenGLShaderProgram* GetShaderPipeline(); enum VideoEffectFlags { ShaderFlag = 0x1, diff --git a/global/config.cpp b/global/config.cpp index aaaaa7987..228d8d9de 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -228,6 +228,18 @@ void Config::load(QString path) { } else if (stream.name() == "OCIOConfigPath") { stream.readNext(); ocio_config_path = stream.text().toString(); + } else if (stream.name() == "OCIODisplay") { + stream.readNext(); + ocio_display = stream.text().toString(); + } else if (stream.name() == "OCIOView") { + stream.readNext(); + ocio_view = stream.text().toString(); + } else if (stream.name() == "OCIOLook") { + stream.readNext(); + ocio_look = stream.text().toString(); + } else if (stream.name() == "OCIODefaultInput") { + stream.readNext(); + ocio_default_input_colorspace = stream.text().toString(); } else if (stream.name() == "Style") { stream.readNext(); style = static_cast(stream.text().toInt()); @@ -327,6 +339,10 @@ void Config::save(QString path) { stream.writeTextElement("AddDefaultEffectsToClips", QString::number(add_default_effects_to_clips)); stream.writeTextElement("EnableColorManagement", QString::number(enable_color_management)); stream.writeTextElement("OCIOConfigPath", ocio_config_path); + stream.writeTextElement("OCIODisplay", ocio_display); + stream.writeTextElement("OCIOView", ocio_view); + stream.writeTextElement("OCIOLook", ocio_look); + stream.writeTextElement("OCIODefaultInput", ocio_default_input_colorspace); stream.writeTextElement("Style", QString::number(style)); stream.writeTextElement("NativeMenuStyling", QString::number(use_native_menu_styling)); stream.writeTextElement("DefaultSequenceWidth", QString::number(default_sequence_width)); diff --git a/global/config.h b/global/config.h index ef5372b16..3d7109e1c 100644 --- a/global/config.h +++ b/global/config.h @@ -554,6 +554,13 @@ struct Config { */ QString ocio_look; + /** + * @brief OpenColorIO Default Input Colorspace + * + * The colorspace to default to if no colorspace can be determined from the filename or a manual setting. + */ + QString ocio_default_input_colorspace; + /** * @brief Style to use when theming Olive. * diff --git a/olive.pro b/olive.pro index 5f3e23a85..83f56d5c2 100644 --- a/olive.pro +++ b/olive.pro @@ -172,7 +172,6 @@ SOURCES += \ effects/internal/richtexteffect.cpp \ ui/blur.cpp \ ui/menu.cpp \ - rendering/qopenglshaderprogramptr.cpp \ timeline/mediaimportdata.cpp \ dialogs/autocutsilencedialog.cpp \ ui/columnedgridlayout.cpp \ diff --git a/project/footage.cpp b/project/footage.cpp index 631c9ae49..61c37b13c 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -28,6 +28,7 @@ namespace OCIO = OCIO_NAMESPACE::v1; #include "project/previewgenerator.h" #include "timeline/clip.h" +#include "global/config.h" Footage::Footage() : ready(false), @@ -61,7 +62,7 @@ QString Footage::Colorspace() return guess_colorspace; } - return OCIO::ROLE_SCENE_LINEAR; + return olive::CurrentConfig.ocio_default_input_colorspace; } void Footage::SetColorspace(const QString &cs) diff --git a/rendering/pixelformats.h b/rendering/pixelformats.h index 2e913c531..37fe34850 100644 --- a/rendering/pixelformats.h +++ b/rendering/pixelformats.h @@ -27,14 +27,6 @@ namespace olive { -struct PixelFormatInfo { - QString name; - GLint internal_format; - GLenum pixel_format; - GLenum pixel_type; - int bytes_per_pixel; -}; - /** * @brief The PixelFormat enum * @@ -49,6 +41,21 @@ enum PixelFormat { PIX_FMT_COUNT }; +/** + * @brief The PixelFormatInfo struct + * + * A struct of information pertaining to each enum PixelFormat. Primarily this is a means of retrieving OpenGL texture + * information for different pixel formats/bit depths. Using the values in pixel_formats is always recommended over + * manually using OpenGL constants (e.g. GL_RGBA or GL_RGBA32F) directly. + */ +struct PixelFormatInfo { + QString name; + GLint internal_format; + GLenum pixel_format; + GLenum pixel_type; + int bytes_per_pixel; +}; + extern QVector pixel_formats; void InitializePixelFormats(); diff --git a/rendering/qopenglshaderprogramptr.cpp b/rendering/qopenglshaderprogramptr.cpp deleted file mode 100644 index 049273bce..000000000 --- a/rendering/qopenglshaderprogramptr.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/*** - - 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 "qopenglshaderprogramptr.h" - diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index a11057980..d32e00bdb 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -101,6 +101,7 @@ void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatri pipeline->setUniformValue("mvp_matrix", matrix); pipeline->setUniformValue("texture", 0); + GLuint vertex_location = pipeline->attributeLocation("a_position"); m_vbo.bind(); func->glEnableVertexAttribArray(vertex_location); @@ -179,11 +180,15 @@ void process_effect(QOpenGLContext* ctx, } bool can_process_shaders = ((e->Flags() & Effect::ShaderFlag) && olive::CurrentRuntimeConfig.shaders_are_enabled); if (can_process_shaders || (e->Flags() & Effect::SuperimposeFlag)) { - e->startEffect(); + + if (!e->is_open()) { + e->open(); + } + if (can_process_shaders && e->is_shader_linked()) { for (int i=0;igetIterations();i++) { e->process_shader(timecode, coords, i); - composite_texture = draw_clip(ctx, pipeline, c->fbo.at(fbo_switcher), composite_texture, true); + composite_texture = draw_clip(ctx, e->GetShaderPipeline(), c->fbo.at(fbo_switcher), composite_texture, true); fbo_switcher = !fbo_switcher; } } @@ -208,7 +213,6 @@ void process_effect(QOpenGLContext* ctx, composite_texture = draw_clip(ctx, pipeline, c->fbo.at(!fbo_switcher), superimpose_texture, false); } } - e->endEffect(); } } } diff --git a/rendering/shadergenerators.cpp b/rendering/shadergenerators.cpp index 6a6490d63..a9b90c6bc 100644 --- a/rendering/shadergenerators.cpp +++ b/rendering/shadergenerators.cpp @@ -2,7 +2,7 @@ #include -QOpenGLShaderProgramPtr olive::shader::GetPipeline(const QString& shader_code) +QOpenGLShaderProgramPtr olive::shader::GetPipeline(const QString& function_name, const QString& shader_code) { QOpenGLShaderProgramPtr program = std::make_shared(); @@ -62,16 +62,16 @@ QOpenGLShaderProgramPtr olive::shader::GetPipeline(const QString& shader_code) // If additional code was passed, add it and reference it in main(). // - // The function in the additional code is expected to be `vec4 process(vec4 color)`. The texture coordinate can be + // The function in the additional code is expected to be `vec4 function_name(vec4 color)`. The texture coordinate can be // acquired through `v_texcoord`. frag_shader.append(shader_code); - frag_shader.append("\n" + frag_shader.append(QString("\n" "void main() {\n" - " vec4 color = process(texture2D(texture, v_texcoord))*opacity;\n" + " vec4 color = %1(texture2D(texture, v_texcoord))*opacity;\n" " gl_FragColor = color;\n" - "}\n"); + "}\n").arg(function_name)); } @@ -211,16 +211,17 @@ QOpenGLShaderProgramPtr olive::shader::SetupOCIO(QOpenGLContext* ctx, } // Add process() function, which GetPipeline() will call if specified + QString process_function_name = "process"; shader_text.append(QString("\n" "uniform sampler3D tex2;\n" "\n" - "vec4 process(vec4 col) {\n" + "vec4 %2(vec4 col) {\n" " return %1\n" - "}\n").arg(shader_call)); + "}\n").arg(shader_call, process_function_name)); // Get pipeline-based shader to inject OCIO shader into - QOpenGLShaderProgramPtr shader = olive::shader::GetPipeline(shader_text); + QOpenGLShaderProgramPtr shader = olive::shader::GetPipeline(process_function_name, shader_text); // Release LUT xf->glBindTexture(GL_TEXTURE_3D, 0); diff --git a/rendering/shadergenerators.h b/rendering/shadergenerators.h index 5fe5a7370..02123afc6 100644 --- a/rendering/shadergenerators.h +++ b/rendering/shadergenerators.h @@ -9,7 +9,7 @@ namespace OCIO = OCIO_NAMESPACE::v1; namespace olive { namespace shader { -QOpenGLShaderProgramPtr GetPipeline(const QString &shader_code = QString()); +QOpenGLShaderProgramPtr GetPipeline(const QString &function_name = QString(), const QString &shader_code = QString()); QOpenGLShaderProgramPtr SetupOCIO(QOpenGLContext *ctx, GLuint &lut_texture, From ad1013e60bd98c2cce30ed84f4eb1d8a759803eb Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 27 Mar 2019 01:05:05 +1100 Subject: [PATCH 051/133] ported some glsl effects to gles --- effects/effect.cpp | 14 +++++++++- effects/effect.h | 1 + effects/shaders/boxblur.frag | 22 ++++++---------- effects/shaders/boxblur.xml | 2 +- effects/shaders/chromaticaberration.frag | 19 +++++--------- effects/shaders/crop.frag | 26 ++++++------------- effects/shaders/directionalblur.frag | 12 +++------ effects/shaders/{ => discard}/common.frag | 0 effects/shaders/{ => discard}/common.vert | 0 effects/shaders/discard/invert.frag | 7 +++++ effects/shaders/{ => discard}/invert.xml | 0 effects/shaders/{ => discard}/posterize.frag | 10 ++----- effects/shaders/{ => discard}/posterize.xml | 0 effects/shaders/discard/volumetriclight.frag | 15 +++++++++++ .../shaders/{ => discard}/volumetriclight.xml | 0 effects/shaders/gaussianblur.frag | 19 +++++--------- effects/shaders/invert.frag | 13 ---------- effects/shaders/noise.frag | 24 ++++------------- effects/shaders/pixelate.frag | 12 +++------ effects/shaders/radialblur.frag | 11 +++----- effects/shaders/ripple.frag | 13 +++------- effects/shaders/sphere.frag | 17 +++++------- effects/shaders/swirl.frag | 12 +++------ effects/shaders/volumetriclight.frag | 19 -------------- effects/shaders/wave.frag | 25 +++++------------- panels/project.cpp | 1 + project/loadthread.cpp | 2 ++ timeline/clip.cpp | 4 +++ 28 files changed, 112 insertions(+), 188 deletions(-) rename effects/shaders/{ => discard}/common.frag (100%) rename effects/shaders/{ => discard}/common.vert (100%) create mode 100644 effects/shaders/discard/invert.frag rename effects/shaders/{ => discard}/invert.xml (100%) rename effects/shaders/{ => discard}/posterize.frag (59%) rename effects/shaders/{ => discard}/posterize.xml (100%) create mode 100644 effects/shaders/discard/volumetriclight.frag rename effects/shaders/{ => discard}/volumetriclight.xml (100%) delete mode 100644 effects/shaders/invert.frag delete mode 100644 effects/shaders/volumetriclight.frag diff --git a/effects/effect.cpp b/effects/effect.cpp index 4e09326ad..bc2592cae 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -307,6 +307,8 @@ Effect::Effect(Clip* c, const EffectMeta *em) : shader_frag_path_ = attr.value().toString(); } else if (attr.name() == "iterations") { setIterations(attr.value().toInt()); + } else if (attr.name() == "function") { + shader_function_name_ = attr.value().toString(); } } }/* else if (reader.name() == "superimpose" && reader.isStartElement()) { @@ -757,7 +759,17 @@ void Effect::open() { } if (!frag_shader_str.isEmpty()) { - shader_program_ = olive::shader::GetPipeline("process", frag_shader_str); + + QString shader_func; + + if (!shader_function_name_.isEmpty()) { + shader_func = shader_function_name_; + } else { + shader_func = "process"; + } + + shader_program_ = olive::shader::GetPipeline(shader_func, frag_shader_str); + } /* diff --git a/effects/effect.h b/effects/effect.h index 2e3252d8b..6871009a7 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -226,6 +226,7 @@ protected: QOpenGLShaderProgramPtr shader_program_; QString shader_vert_path_; QString shader_frag_path_; + QString shader_function_name_; // superimpose effect QImage img; diff --git a/effects/shaders/boxblur.frag b/effects/shaders/boxblur.frag index 3da0380bc..a9479c135 100644 --- a/effects/shaders/boxblur.frag +++ b/effects/shaders/boxblur.frag @@ -1,16 +1,10 @@ -#version 110 - -uniform sampler2D image; - uniform float radius; -uniform vec2 resolution; uniform bool horiz_blur; uniform bool vert_blur; uniform int iteration; +uniform vec2 resolution; -varying vec2 vTexCoord; - -void main(void) { +vec4 process(vec4 col) { float rad = ceil(radius); float divider = 1.0 / rad; @@ -19,15 +13,15 @@ void main(void) { if (iteration == 0 && horiz_blur && !radius_is_zero) { for (float x=-rad+0.5;x<=rad;x+=2.0) { - color += texture2D(image, (vec2(gl_FragCoord.x+x, gl_FragCoord.y))/resolution)*(divider); + color += texture2D(texture, vec2(gl_FragCoord.x+x, gl_FragCoord.y)/resolution)*(divider); } - gl_FragColor = color; + return color; } else if (iteration == 1 && vert_blur && !radius_is_zero) { for (float x=-rad+0.5;x<=rad;x+=2.0) { - color += texture2D(image, (vec2(gl_FragCoord.x, gl_FragCoord.y+x))/resolution)*(divider); + color += texture2D(texture, vec2(gl_FragCoord.x, gl_FragCoord.y+x)/resolution)*(divider); } - gl_FragColor = color; + return color; } else { - gl_FragColor = texture2D(image, vTexCoord); + return col; } -} \ No newline at end of file +} diff --git a/effects/shaders/boxblur.xml b/effects/shaders/boxblur.xml index a6c0cb491..59bf40ca2 100644 --- a/effects/shaders/boxblur.xml +++ b/effects/shaders/boxblur.xml @@ -9,5 +9,5 @@ - + \ No newline at end of file diff --git a/effects/shaders/chromaticaberration.frag b/effects/shaders/chromaticaberration.frag index ff9ddfa96..1b795f348 100644 --- a/effects/shaders/chromaticaberration.frag +++ b/effects/shaders/chromaticaberration.frag @@ -1,22 +1,17 @@ -#version 110 - -uniform vec2 resolution; - -uniform sampler2D tex; -varying vec2 vTexCoord; - uniform float red_amount; uniform float green_amount; uniform float blue_amount; -void main(void) { +uniform vec2 resolution; + +vec4 process(vec4 col) { vec2 rOffset = vec2(red_amount*0.01)/resolution; vec2 gOffset = vec2(green_amount*0.01)/resolution; vec2 bOffset = vec2(blue_amount*0.01)/resolution; - vec4 rValue = texture2D(tex, vTexCoord - rOffset); - vec4 gValue = texture2D(tex, vTexCoord - gOffset); - vec4 bValue = texture2D(tex, vTexCoord - bOffset); + vec4 rValue = texture2D(texture, v_texcoord - rOffset); + vec4 gValue = texture2D(texture, v_texcoord - gOffset); + vec4 bValue = texture2D(texture, v_texcoord - bOffset); - gl_FragColor = vec4(rValue.r, gValue.g, bValue.b, texture2D(tex, vTexCoord).a); + return vec4(rValue.r, gValue.g, bValue.b, col.a); } \ No newline at end of file diff --git a/effects/shaders/crop.frag b/effects/shaders/crop.frag index 7fed98944..062f22c2f 100644 --- a/effects/shaders/crop.frag +++ b/effects/shaders/crop.frag @@ -1,5 +1,3 @@ -#version 110 - uniform float left; uniform float top; uniform float right; @@ -7,28 +5,20 @@ 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; +vec4 process(vec4 col) { + float alpha = col.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))) { + if (v_texcoord.x < (left*0.01) || v_texcoord.y < (top*0.01) || v_texcoord.x > (1.0-(right*0.01)) || v_texcoord.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 + if (left > 0.0) alpha = alpha * clamp(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left + if (top > 0.0) alpha = alpha * clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top + if (right > 0.0) alpha = alpha * clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right + if (bottom > 0.0) alpha = alpha * clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom } - gl_FragColor = vec4( - textureColor.r*alpha, - textureColor.g*alpha, - textureColor.b*alpha, - alpha - ); + return vec4(col.rgb*alpha, alpha); } \ No newline at end of file diff --git a/effects/shaders/directionalblur.frag b/effects/shaders/directionalblur.frag index e04d350ed..86c8ac079 100644 --- a/effects/shaders/directionalblur.frag +++ b/effects/shaders/directionalblur.frag @@ -1,15 +1,11 @@ -#version 110 - #define M_PI 3.1415926535897932384626433832795 -uniform sampler2D image; - uniform float angle; // degrees uniform float length; uniform vec2 resolution; -void main(void) { +vec4 process(vec4 col) { if (length > 0.0) { float ceillen = ceil(length); float radians = (angle*M_PI)/180.0; @@ -21,10 +17,10 @@ void main(void) { for (float i=-ceillen+0.5;i<=ceillen;i+=2.0) { float y = sin_angle * i; float x = cos_angle * i; - color += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider); + color += texture2D(texture, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider); } - gl_FragColor = color; + return color; } else { - gl_FragColor = texture2D(image, gl_FragCoord.xy/resolution); + return col; } } \ No newline at end of file diff --git a/effects/shaders/common.frag b/effects/shaders/discard/common.frag similarity index 100% rename from effects/shaders/common.frag rename to effects/shaders/discard/common.frag diff --git a/effects/shaders/common.vert b/effects/shaders/discard/common.vert similarity index 100% rename from effects/shaders/common.vert rename to effects/shaders/discard/common.vert diff --git a/effects/shaders/discard/invert.frag b/effects/shaders/discard/invert.frag new file mode 100644 index 000000000..bcfa87612 --- /dev/null +++ b/effects/shaders/discard/invert.frag @@ -0,0 +1,7 @@ +uniform float amount; + +vec4 process(vec4 col) { + float amount_val = amount * 0.01; + vec3 color = col.rgb+((vec3(1.0)-col.rgb-col.rgb)*vec3(amount_val)); + return vec4(color, col.a); +} \ No newline at end of file diff --git a/effects/shaders/invert.xml b/effects/shaders/discard/invert.xml similarity index 100% rename from effects/shaders/invert.xml rename to effects/shaders/discard/invert.xml diff --git a/effects/shaders/posterize.frag b/effects/shaders/discard/posterize.frag similarity index 59% rename from effects/shaders/posterize.frag rename to effects/shaders/discard/posterize.frag index a3144450b..85ee2c1dc 100644 --- a/effects/shaders/posterize.frag +++ b/effects/shaders/discard/posterize.frag @@ -1,19 +1,13 @@ -#version 110 - -uniform sampler2D sceneTex; // 0 uniform float gamma_cent; // 0.6 uniform float numColors; // 8.0 -varying vec2 vTexCoord; - -void main() { +vec4 process(vec4 color) { float gamma = gamma_cent*0.01; - vec4 color = texture2D(sceneTex, vTexCoord); vec3 c = color.rgb; c = pow(c, vec3(gamma, gamma, gamma)); c = c * numColors; c = floor(c); c = c / numColors; c = pow(c, vec3(1.0/gamma)); - gl_FragColor = vec4(c, color.a); + return vec4(c, color.a); } \ No newline at end of file diff --git a/effects/shaders/posterize.xml b/effects/shaders/discard/posterize.xml similarity index 100% rename from effects/shaders/posterize.xml rename to effects/shaders/discard/posterize.xml diff --git a/effects/shaders/discard/volumetriclight.frag b/effects/shaders/discard/volumetriclight.frag new file mode 100644 index 000000000..bf27e17d7 --- /dev/null +++ b/effects/shaders/discard/volumetriclight.frag @@ -0,0 +1,15 @@ +uniform float amount; + +uniform vec2 resolution; // screen resolution + +vec4 process(vec4 col) { + float alpha = texture2D(texture, v_texcoord)[3]; + + vec3 p = gl_FragCoord.xyz/vec3(resolution, 1.0)-.5; + vec4 color = texture2D(texture,.5+(p.xy*=.992)); + vec3 o = color.rgb; + for (float i=0.;i 0.0) { vec2 distance = vec2((gl_FragCoord.x - (resolution.x/2.0) - center_x), (gl_FragCoord.y - (resolution.y/2.0) - center_y)); @@ -28,10 +25,10 @@ void main(void) { for (float i=-limit+0.5;i<=limit;i+=2.0) { float y = sin_angle * i; float x = cos_angle * i; - color += texture2D(image, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider); + color += texture2D(texture, vec2(gl_FragCoord.x+x, gl_FragCoord.y+y)/resolution)*(divider); } - gl_FragColor = color; + return color; } else { - gl_FragColor = texture2D(image, gl_FragCoord.xy/resolution); + return col; } } \ No newline at end of file diff --git a/effects/shaders/ripple.frag b/effects/shaders/ripple.frag index 03c91a5ce..b9fc951f0 100644 --- a/effects/shaders/ripple.frag +++ b/effects/shaders/ripple.frag @@ -1,7 +1,3 @@ -#version 110 - -varying vec2 vTexCoord; - uniform float speed; uniform float intensity; uniform float frequency; @@ -12,10 +8,9 @@ uniform bool stretch; uniform vec2 resolution; // Screen resolution uniform float time; // time in seconds -uniform sampler2D tex0; // scene buffer -void main(void) { - vec2 texCoord = vTexCoord; +vec4 process(vec4 col) { + vec2 texCoord = v_texcoord; vec2 center = vec2(1.0); if (!stretch) { @@ -29,6 +24,6 @@ void main(void) { vec2 p = 2.0 * texCoord - center; float len = length(p); - vec2 uv = vTexCoord + (p/len)*cos((frequency*0.01)*(len*12.0-real_time*(speed*0.05)))*(intensity*0.0005); - gl_FragColor = texture2D(tex0,uv); + vec2 uv = v_texcoord + (p/len)*cos((frequency*0.01)*(len*12.0-real_time*(speed*0.05)))*(intensity*0.0005); + return texture2D(texture, uv); } \ No newline at end of file diff --git a/effects/shaders/sphere.frag b/effects/shaders/sphere.frag index 2445b37a4..6496dd5b3 100644 --- a/effects/shaders/sphere.frag +++ b/effects/shaders/sphere.frag @@ -1,9 +1,4 @@ -#version 110 - -varying vec2 vTexCoord; - uniform vec2 resolution; // Screen resolution -uniform sampler2D tex0; // scene buffer uniform float xoff; uniform float yoff; @@ -12,8 +7,8 @@ uniform bool tile; uniform bool hide_edges; uniform bool stretch; -void main(void) { - vec2 texCoord = vTexCoord; +vec4 process(vec4 col) { + vec2 texCoord = v_texcoord; vec2 offset = vec2(1.0); @@ -28,7 +23,7 @@ void main(void) { } vec2 p = 2.0 * texCoord - offset; - vec2 adj_tc = 2.0 * vTexCoord - 1.0; + vec2 adj_tc = 2.0 * v_texcoord - 1.0; float r = dot(p,p); if (r > 1.0) discard; float f = (1.0-sqrt(1.0-r))/(r); @@ -39,7 +34,7 @@ void main(void) { uv.x = mod(uv.x, 1.0); uv.y = mod(uv.y, 1.0); } else if (hide_edges && (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)) { - discard; + return vec4(0.0); } - gl_FragColor = vec4(texture2D(tex0,uv)); -} \ No newline at end of file + return vec4(texture2D(texture,uv)); +} diff --git a/effects/shaders/swirl.frag b/effects/shaders/swirl.frag index e820e3394..94facd048 100644 --- a/effects/shaders/swirl.frag +++ b/effects/shaders/swirl.frag @@ -1,5 +1,3 @@ -#version 110 - // Swirl effect parameters uniform float radius; uniform float angle; @@ -7,13 +5,10 @@ uniform float center_x; uniform float center_y; uniform vec2 resolution; -uniform sampler2D myTexture; -varying vec2 vTexCoord; - -void main(void) { +vec4 process(vec4 col) { vec2 center = vec2((resolution.x*0.5)+center_x, (resolution.y*0.5)+center_y); - vec2 uv = vTexCoord.st; + vec2 uv = v_texcoord.st; vec2 tc = uv * resolution; tc -= center; @@ -26,6 +21,5 @@ void main(void) { tc = vec2(dot(tc, vec2(c, -s)), dot(tc, vec2(s, c))); } tc += center; - vec3 color = texture2D(myTexture, tc / resolution).rgb; - gl_FragColor = vec4(color, 1.0); + return texture2D(texture, tc / resolution); } \ No newline at end of file diff --git a/effects/shaders/volumetriclight.frag b/effects/shaders/volumetriclight.frag deleted file mode 100644 index cfeeb9252..000000000 --- a/effects/shaders/volumetriclight.frag +++ /dev/null @@ -1,19 +0,0 @@ -#version 110 - -uniform float amount; - -uniform sampler2D tex0; -uniform vec2 resolution; // screen resolution -varying vec2 vTexCoord; -#define T texture2D(tex0,.5+(p.xy*=.992)) - -void main() { - float alpha = texture2D(tex0, vTexCoord)[3]; - - vec3 p = gl_FragCoord.xyz/vec3(resolution, 1.0)-.5; - vec3 o = T.rgb; - for (float i=0.;i 1.0 || x < 0.0 || x > 1.0) { - discard; + return vec4(0.0); } else { - vec4 textureColor = texture2D(myTexture, vec2(x, y)); - gl_FragColor = vec4( - textureColor.r, - textureColor.g, - textureColor.b, - textureColor.a - ); + return texture2D(texture, vec2(x, y)); } } \ No newline at end of file diff --git a/panels/project.cpp b/panels/project.cpp index c0ad3439a..697265aa9 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -1115,6 +1115,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, stream.writeAttribute("speed", QString::number(f->speed)); stream.writeAttribute("alphapremul", QString::number(f->alpha_is_associated)); stream.writeAttribute("startnumber", QString::number(f->start_number)); + stream.writeAttribute("colorspace", f->Colorspace()); stream.writeAttribute("proxy", QString::number(f->proxy)); stream.writeAttribute("proxypath", f->proxy_path); diff --git a/project/loadthread.cpp b/project/loadthread.cpp index ca1c49cd5..20ddc66d6 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -356,6 +356,8 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { f->proxy_path = attr.value().toString(); } else if (attr.name() == "startnumber") { f->start_number = attr.value().toInt(); + } else if (attr.name() == "colorspace") { + f->SetColorspace(attr.value().toString()); } } diff --git a/timeline/clip.cpp b/timeline/clip.cpp index c5c62f9e5..a6242a7fe 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -573,6 +573,10 @@ bool Clip::Retrieve() f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // set texture wrapping to clamp + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // queue an allocation ahead allocate_data = true; From 8b400d7b31188577219122d07421a195ddc3392b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 27 Mar 2019 01:14:45 +1100 Subject: [PATCH 052/133] removed old unused blend mode files --- effects/add.blend | 14 ------ effects/average.blend | 10 ---- effects/color-burn.blend | 14 ------ effects/color-dodge.blend | 14 ------ effects/darken.blend | 14 ------ effects/difference.blend | 10 ---- effects/effectloaders.cpp | 93 +++++++++++++++++++++++--------------- effects/exclusion.blend | 10 ---- effects/glow.blend | 12 ----- effects/hard-light.blend | 12 ----- effects/hard-mix.blend | 16 ------- effects/lighten.blend | 14 ------ effects/linear-burn.blend | 16 ------- effects/linear-dodge.blend | 16 ------- effects/linear-light.blend | 17 ------- effects/multiply.blend | 10 ---- effects/negation.blend | 10 ---- effects/normal.blend | 10 ---- effects/overlay.blend | 14 ------ effects/phoenix.blend | 10 ---- effects/pin-light.blend | 17 ------- effects/reflect.blend | 14 ------ effects/screen.blend | 14 ------ effects/soft-light.blend | 14 ------ effects/substract.blend | 14 ------ effects/subtract.blend | 14 ------ effects/vivid-light.blend | 17 ------- 27 files changed, 57 insertions(+), 383 deletions(-) delete mode 100644 effects/add.blend delete mode 100644 effects/average.blend delete mode 100644 effects/color-burn.blend delete mode 100644 effects/color-dodge.blend delete mode 100644 effects/darken.blend delete mode 100644 effects/difference.blend delete mode 100644 effects/exclusion.blend delete mode 100644 effects/glow.blend delete mode 100644 effects/hard-light.blend delete mode 100644 effects/hard-mix.blend delete mode 100644 effects/lighten.blend delete mode 100644 effects/linear-burn.blend delete mode 100644 effects/linear-dodge.blend delete mode 100644 effects/linear-light.blend delete mode 100644 effects/multiply.blend delete mode 100644 effects/negation.blend delete mode 100644 effects/normal.blend delete mode 100644 effects/overlay.blend delete mode 100644 effects/phoenix.blend delete mode 100644 effects/pin-light.blend delete mode 100644 effects/reflect.blend delete mode 100644 effects/screen.blend delete mode 100644 effects/soft-light.blend delete mode 100644 effects/substract.blend delete mode 100644 effects/subtract.blend delete mode 100644 effects/vivid-light.blend diff --git a/effects/add.blend b/effects/add.blend deleted file mode 100644 index 886c2e617..000000000 --- a/effects/add.blend +++ /dev/null @@ -1,14 +0,0 @@ -float blendAdd(float base, float blend) { - return min(base+blend,1.0); -} - -vec3 blendAdd(vec3 base, vec3 blend) { - return min(base+blend,vec3(1.0)); -} - -vec3 blendAdd(vec3 base, vec3 blend, float opacity) { - return (blendAdd(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendAdd) -#olive name Add \ No newline at end of file diff --git a/effects/average.blend b/effects/average.blend deleted file mode 100644 index 098e734a3..000000000 --- a/effects/average.blend +++ /dev/null @@ -1,10 +0,0 @@ -vec3 blendAverage(vec3 base, vec3 blend) { - return (base+blend)/2.0; -} - -vec3 blendAverage(vec3 base, vec3 blend, float opacity) { - return (blendAverage(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendAverage) -#olive name Average \ No newline at end of file diff --git a/effects/color-burn.blend b/effects/color-burn.blend deleted file mode 100644 index 4cea15cd4..000000000 --- a/effects/color-burn.blend +++ /dev/null @@ -1,14 +0,0 @@ -float blendColorBurn(float base, float blend) { - return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0); -} - -vec3 blendColorBurn(vec3 base, vec3 blend) { - return vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b)); -} - -vec3 blendColorBurn(vec3 base, vec3 blend, float opacity) { - return (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendColorBurn) -#olive name Color Burn \ No newline at end of file diff --git a/effects/color-dodge.blend b/effects/color-dodge.blend deleted file mode 100644 index 495ff401c..000000000 --- a/effects/color-dodge.blend +++ /dev/null @@ -1,14 +0,0 @@ -float blendColorDodge(float base, float blend) { - return (blend==1.0)?blend:min(base/(1.0-blend),1.0); -} - -vec3 blendColorDodge(vec3 base, vec3 blend) { - return vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b)); -} - -vec3 blendColorDodge(vec3 base, vec3 blend, float opacity) { - return (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendColorDodge) -#olive name Color Dodge \ No newline at end of file diff --git a/effects/darken.blend b/effects/darken.blend deleted file mode 100644 index f5a81931f..000000000 --- a/effects/darken.blend +++ /dev/null @@ -1,14 +0,0 @@ -float blendDarken(float base, float blend) { - return min(blend,base); -} - -vec3 blendDarken(vec3 base, vec3 blend) { - return vec3(blendDarken(base.r,blend.r),blendDarken(base.g,blend.g),blendDarken(base.b,blend.b)); -} - -vec3 blendDarken(vec3 base, vec3 blend, float opacity) { - return (blendDarken(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendDarken) -#olive name Darken \ No newline at end of file diff --git a/effects/difference.blend b/effects/difference.blend deleted file mode 100644 index e65ba45eb..000000000 --- a/effects/difference.blend +++ /dev/null @@ -1,10 +0,0 @@ -vec3 blendDifference(vec3 base, vec3 blend) { - return abs(base-blend); -} - -vec3 blendDifference(vec3 base, vec3 blend, float opacity) { - return (blendDifference(base, blend) * opacity + base * (1.0 - opacity)); -} - -#pragma glslify: export(blendDifference) -#olive name Difference \ No newline at end of file diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index c1c682068..131db86f5 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -133,50 +133,71 @@ void load_internal_effects() { void load_shader_effects_worker(const QString& effects_path) { QDir effects_dir(effects_path); if (effects_dir.exists()) { - QList entries = effects_dir.entryList(QStringList("*.xml"), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + + QList entries = effects_dir.entryList({"*.xml", "*.blend"}, + QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); + for (int i=0;i blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files); + for (int i=0;i Date: Wed, 27 Mar 2019 04:04:07 +1100 Subject: [PATCH 053/133] fixed gcc compile issues --- dialogs/preferencesdialog.cpp | 12 ++++++------ effects/effect.cpp | 1 + ui/viewerwindow.h | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index b39a7f8a4..03a2ce45a 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -170,7 +170,7 @@ void PreferencesDialog::delete_previews(PreviewDeleteTypes type) { void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) { - if (config == nullptr) { + if (!config) { // Just clear everything ocio_display->clear(); @@ -230,16 +230,16 @@ void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) OCIO::ConstConfigRcPtr PreferencesDialog::TestOCIOConfig(const QString &url) { // Check whether OCIO can load it + OCIO::ConstConfigRcPtr config; try { - OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); - return config; + config = OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); } catch (OCIO::Exception& e) { QMessageBox::critical(this, tr("OpenColorIO Config Error"), tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), QMessageBox::Ok); - return nullptr; } + return config; } void PreferencesDialog::update_ocio_view_menu(OCIO::ConstConfigRcPtr config) @@ -270,7 +270,7 @@ void PreferencesDialog::update_ocio_view_menu(OCIO::ConstConfigRcPtr config) void PreferencesDialog::update_ocio_config(const QString &s) { - OCIO::ConstConfigRcPtr file_config = nullptr; + OCIO::ConstConfigRcPtr file_config; if (!s.isEmpty() && QFileInfo::exists(s)) { file_config = TestOCIOConfig(s); @@ -356,7 +356,7 @@ void PreferencesDialog::accept() { // Check whether OCIO can load it OCIO::ConstConfigRcPtr file_config = TestOCIOConfig(ocio_config_file->text().toUtf8()); - if (file_config == nullptr) { + if (!file_config) { return; } diff --git a/effects/effect.cpp b/effects/effect.cpp index bc2592cae..e142ad890 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -69,6 +69,7 @@ #include "effects/internal/richtexteffect.h" QVector olive::effects; +QVector olive::blend_modes; QString olive::generated_blending_shader; EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { diff --git a/ui/viewerwindow.h b/ui/viewerwindow.h index 0da945bee..2f5dec7f7 100644 --- a/ui/viewerwindow.h +++ b/ui/viewerwindow.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "rendering/qopenglshaderprogramptr.h" From 7f06330815ef1db5ac0636d4a26f6d8a7887ac8d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 28 Mar 2019 19:38:40 +1100 Subject: [PATCH 054/133] some rendering and effect fixes --- effects/shaders/crop.frag | 26 ++++++++++++++++++++------ effects/shaders/flip.frag | 19 +++++-------------- effects/shaders/gaussianblur.frag | 6 ------ rendering/renderfunctions.cpp | 12 +++++++----- rendering/renderthread.cpp | 1 + 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/effects/shaders/crop.frag b/effects/shaders/crop.frag index 062f22c2f..574360312 100644 --- a/effects/shaders/crop.frag +++ b/effects/shaders/crop.frag @@ -7,18 +7,32 @@ uniform float feather; uniform mediump float amount_val; vec4 process(vec4 col) { - float alpha = col.a; + float alpha = 1.0; + + if (feather == 0.0) { if (v_texcoord.x < (left*0.01) || v_texcoord.y < (top*0.01) || v_texcoord.x > (1.0-(right*0.01)) || v_texcoord.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(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left - if (top > 0.0) alpha = alpha * clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top - if (right > 0.0) alpha = alpha * clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right - if (bottom > 0.0) alpha = alpha * clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom + + if (left > 0.0) { + alpha = clamp(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left + } + + if (top > 0.0) { + alpha = clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top + } + + if (right > 0.0) { + alpha = clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right + } + + if (bottom > 0.0) { + alpha = clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom + } } - return vec4(col.rgb*alpha, alpha); + return col * alpha; } \ No newline at end of file diff --git a/effects/shaders/flip.frag b/effects/shaders/flip.frag index 6a55cc8ca..6bdb05a9c 100644 --- a/effects/shaders/flip.frag +++ b/effects/shaders/flip.frag @@ -1,23 +1,14 @@ -#version 110 - uniform bool horiz; uniform bool vert; -uniform sampler2D myTexture; -varying vec2 vTexCoord; +vec4 process(vec4 col) { + float x = v_texcoord.x; + float y = v_texcoord.y; -void main(void) { - float x = vTexCoord.x; - float y = vTexCoord.y; + if (!horiz && !vert) return col; if (horiz) x = 1.0 - x; if (vert) y = 1.0 - y; - vec4 textureColor = texture2D(myTexture, vec2(x, y)); - gl_FragColor = vec4( - textureColor.r, - textureColor.g, - textureColor.b, - textureColor.a - ); + return texture2D(texture, vec2(x, y)); } \ No newline at end of file diff --git a/effects/shaders/gaussianblur.frag b/effects/shaders/gaussianblur.frag index ab015051f..e1d08a367 100644 --- a/effects/shaders/gaussianblur.frag +++ b/effects/shaders/gaussianblur.frag @@ -1,11 +1,5 @@ #define M_PI 3.1415926535897932384626433832795 -<<<<<<< HEAD -======= -uniform sampler2D image; - -//uniform float radius; ->>>>>>> master uniform float sigma; uniform vec2 resolution; uniform bool horiz_blur; diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 806ab318d..69b098372 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -573,16 +573,22 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // use clip textures for nested sequences, otherwise use main frame buffers GLuint back_buffer_1; + GLuint back_buffer_2; GLuint backend_tex_1; GLuint backend_tex_2; + GLuint comp_texture; if (params.nests.size() > 0) { back_buffer_1 = params.nests.last()->fbo[1].buffer(); + back_buffer_2 = params.nests.last()->fbo[2].buffer(); backend_tex_1 = params.nests.last()->fbo[1].texture(); backend_tex_2 = params.nests.last()->fbo[2].texture(); + comp_texture = params.nests.last()->fbo[0].texture(); } else { back_buffer_1 = params.backend_buffer1->buffer(); + back_buffer_2 = params.backend_buffer2->buffer(); backend_tex_1 = params.backend_buffer1->texture(); backend_tex_2 = params.backend_buffer2->texture(); + comp_texture = params.main_buffer->texture(); } // render a backbuffer @@ -679,11 +685,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // copy front buffer to back buffer (only if we're using a blending mode) if (coords.blendmode >= 0) { - if (params.nests.size() > 0) { - draw_clip(params.ctx, params.pipeline, params.nests.last()->fbo[2].buffer(), params.nests.last()->fbo[0].texture(), true); - } else { - draw_clip(params.ctx, params.pipeline, params.backend_buffer2->buffer(), params.main_buffer->texture(), true); - } + draw_clip(params.ctx, params.pipeline, back_buffer_2, comp_texture, true); } diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index f9699a220..352224d36 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -255,6 +255,7 @@ void RenderThread::paint() { // If we're not color managing, just blit normally buffer.BindBuffer(); + f->glClear(GL_COLOR_BUFFER_BIT); composite_buffer.BindTexture(); olive::rendering::Blit(pipeline_program.get()); composite_buffer.ReleaseTexture(); From 870df9798304965dc18870c0d8e6918e4c55c200 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 28 Mar 2019 20:36:20 +1100 Subject: [PATCH 055/133] multiply multiplier in crop --- effects/shaders/crop.frag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/effects/shaders/crop.frag b/effects/shaders/crop.frag index 574360312..084466485 100644 --- a/effects/shaders/crop.frag +++ b/effects/shaders/crop.frag @@ -18,19 +18,19 @@ vec4 process(vec4 col) { float f = pow(2.0, 10.0-(feather*0.1)); if (left > 0.0) { - alpha = clamp(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left + alpha *= clamp(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left } if (top > 0.0) { - alpha = clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top + alpha *= clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top } if (right > 0.0) { - alpha = clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right + alpha *= clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right } if (bottom > 0.0) { - alpha = clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom + alpha *= clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom } } From 86bd06795abd249fe7ddefee45944f9187c6f02c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 28 Mar 2019 21:04:15 +1100 Subject: [PATCH 056/133] use resolution for feather values --- effects/shaders/crop.frag | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/effects/shaders/crop.frag b/effects/shaders/crop.frag index 084466485..340f6fdaf 100644 --- a/effects/shaders/crop.frag +++ b/effects/shaders/crop.frag @@ -3,6 +3,7 @@ uniform float top; uniform float right; uniform float bottom; uniform float feather; +uniform vec2 resolution; uniform mediump float amount_val; @@ -15,23 +16,26 @@ vec4 process(vec4 col) { alpha = 0.0; } } else { - float f = pow(2.0, 10.0-(feather*0.1)); + + float f = pow(2.0, 10.0-(feather*0.25)); if (left > 0.0) { - alpha *= clamp(((v_texcoord.x+(0.5/f))-(left*0.01))*f, 0.0, 1.0); // left + alpha *= clamp(((resolution.x*v_texcoord.x+(0.5/f))-(left*0.01*resolution.x))*f, 0.0, 1.0); // left } if (top > 0.0) { - alpha *= clamp(((v_texcoord.y+(0.5/f))-(top*0.01))*f, 0.0, 1.0); // top + alpha *= clamp(((resolution.y*v_texcoord.y+(0.5/f))-(top*0.01*resolution.y))*f, 0.0, 1.0); // top } if (right > 0.0) { - alpha *= clamp((((1.0-v_texcoord.x)+(0.5/f))-(right*0.01))*f, 0.0, 1.0); // right + alpha *= clamp(((resolution.x*(1.0-v_texcoord.x)+(0.5/f))-(right*0.01*resolution.x))*f, 0.0, 1.0); // right } if (bottom > 0.0) { - alpha *= clamp((((1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01))*f, 0.0, 1.0); // bottom + alpha *= clamp(((resolution.y*(1.0-v_texcoord.y)+(0.5/f))-(bottom*0.01*resolution.y))*f, 0.0, 1.0); // bottom } + + } return col * alpha; From 31bb8627153c7ce8152173882dfa9975729b8af9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 28 Mar 2019 21:43:05 +1100 Subject: [PATCH 057/133] ported more effects to gles --- effects/shaders/bulge.frag | 12 +++----- effects/shaders/dropshadow.xml.disabled | 16 ----------- .../{ => need checking}/chromakey.frag | 9 ++---- .../shaders/{ => need checking}/chromakey.xml | 0 .../{ => need checking}/colorcorrection.frag | 0 .../{ => need checking}/colorcorrection.xml | 0 .../shaders/{ => need checking}/colorsel.frag | 9 ++---- .../shaders/{ => need checking}/colorsel.xml | 0 .../{discard => need checking}/common.frag | 0 .../{discard => need checking}/common.vert | 0 .../{ => need checking}/crossstitch.frag | 0 .../{ => need checking}/crossstitch.xml | 0 .../shaders/{ => need checking}/emboss.frag | 0 .../shaders/{ => need checking}/emboss.xml | 0 .../{ => need checking}/findedges.frag | 0 .../findedges.xml.disabled | 0 .../shaders/{ => need checking}/fisheye.frag | 12 +++----- .../shaders/{ => need checking}/fisheye.xml | 0 .../{ => need checking}/huesatbri.frag | 15 ++-------- .../shaders/{ => need checking}/huesatbri.xml | 0 .../{discard => need checking}/invert.frag | 0 .../{discard => need checking}/invert.xml | 0 .../shaders/{ => need checking}/lumakey.frag | 0 .../shaders/{ => need checking}/lumakey.xml | 0 .../{discard => need checking}/posterize.frag | 0 .../{discard => need checking}/posterize.xml | 0 .../shaders/{ => need checking}/toonify.frag | 0 .../shaders/{ => need checking}/toonify.xml | 0 .../volumetriclight.frag | 0 .../volumetriclight.xml | 0 effects/shaders/tile.frag | 28 ++++--------------- effects/shaders/vignette.frag | 17 ++++------- ui/collapsiblewidget.cpp | 1 - ui/graphview.cpp | 2 +- ui/keyframeview.cpp | 2 +- ui/rectangleselect.cpp | 2 +- ui/rectangleselect.h | 8 +++++- ui/resizablescrollbar.h | 28 +++++++++---------- ui/timelinewidget.cpp | 2 +- 39 files changed, 50 insertions(+), 113 deletions(-) delete mode 100644 effects/shaders/dropshadow.xml.disabled rename effects/shaders/{ => need checking}/chromakey.frag (89%) rename effects/shaders/{ => need checking}/chromakey.xml (100%) rename effects/shaders/{ => need checking}/colorcorrection.frag (100%) rename effects/shaders/{ => need checking}/colorcorrection.xml (100%) rename effects/shaders/{ => need checking}/colorsel.frag (89%) rename effects/shaders/{ => need checking}/colorsel.xml (100%) rename effects/shaders/{discard => need checking}/common.frag (100%) rename effects/shaders/{discard => need checking}/common.vert (100%) rename effects/shaders/{ => need checking}/crossstitch.frag (100%) rename effects/shaders/{ => need checking}/crossstitch.xml (100%) rename effects/shaders/{ => need checking}/emboss.frag (100%) rename effects/shaders/{ => need checking}/emboss.xml (100%) rename effects/shaders/{ => need checking}/findedges.frag (100%) rename effects/shaders/{ => need checking}/findedges.xml.disabled (100%) rename effects/shaders/{ => need checking}/fisheye.frag (67%) rename effects/shaders/{ => need checking}/fisheye.xml (100%) rename effects/shaders/{ => need checking}/huesatbri.frag (77%) rename effects/shaders/{ => need checking}/huesatbri.xml (100%) rename effects/shaders/{discard => need checking}/invert.frag (100%) rename effects/shaders/{discard => need checking}/invert.xml (100%) rename effects/shaders/{ => need checking}/lumakey.frag (100%) rename effects/shaders/{ => need checking}/lumakey.xml (100%) rename effects/shaders/{discard => need checking}/posterize.frag (100%) rename effects/shaders/{discard => need checking}/posterize.xml (100%) rename effects/shaders/{ => need checking}/toonify.frag (100%) rename effects/shaders/{ => need checking}/toonify.xml (100%) rename effects/shaders/{discard => need checking}/volumetriclight.frag (100%) rename effects/shaders/{discard => need checking}/volumetriclight.xml (100%) diff --git a/effects/shaders/bulge.frag b/effects/shaders/bulge.frag index 3569eaa4d..68b55b2ca 100644 --- a/effects/shaders/bulge.frag +++ b/effects/shaders/bulge.frag @@ -1,7 +1,3 @@ -#version 120 - -uniform sampler2D tex0; -varying vec2 vTexCoord; const float PI = 3.1415926535; uniform vec2 resolution; @@ -18,15 +14,15 @@ vec2 distort(vec2 p, vec2 offset) { return 0.5 * (p + 1.0); } -void main(void) { +vec4 process(vec4 col) { vec2 offset = vec2(xoff/resolution.x, yoff/resolution.y); - vec2 xy = 2.0 * vTexCoord - 1.0 - offset; + vec2 xy = 2.0 * v_texcoord - 1.0 - offset; vec2 uv; float d = length(xy); uv = distort(xy, offset); if (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0) { - gl_FragColor = texture2D(tex0, uv); + return texture2D(texture, uv); } else { - discard; + return vec4(0.0); } } \ No newline at end of file diff --git a/effects/shaders/dropshadow.xml.disabled b/effects/shaders/dropshadow.xml.disabled deleted file mode 100644 index 9a5278d4d..000000000 --- a/effects/shaders/dropshadow.xml.disabled +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/effects/shaders/chromakey.frag b/effects/shaders/need checking/chromakey.frag similarity index 89% rename from effects/shaders/chromakey.frag rename to effects/shaders/need checking/chromakey.frag index 4f57d8b1e..4f8e5ea0e 100644 --- a/effects/shaders/chromakey.frag +++ b/effects/shaders/need checking/chromakey.frag @@ -4,9 +4,6 @@ feel free to use or modify at will*/ /*the follwing three functions convert RGB into YCbCr in the same manner as in JPEG images*/ -uniform sampler2D tex; -varying vec2 vTexCoord; - uniform vec3 key_color; uniform float tola; uniform float tolb; @@ -36,12 +33,10 @@ float colorclose(float Cb_p,float Cr_p,float Cb_key,float Cr_key,float tola,floa return (1.0); } -void main(void) { +vec4 process(vec4 texture_color) { float cb_key = rgb2cb(key_color); float cr_key = rgb2cr(key_color); - vec4 texture_color = texture2D(tex, vTexCoord); - float cb = rgb2cb(texture_color.rgb); float cr = rgb2cr(texture_color.rgb); float mask = colorclose(cb, cr, cb_key, cr_key, (tola/100.0), (tolb/100.0)); @@ -61,5 +56,5 @@ void main(void) { } else if (mode == 2) { // original } - gl_FragColor = texture_color; + return texture_color; } \ No newline at end of file diff --git a/effects/shaders/chromakey.xml b/effects/shaders/need checking/chromakey.xml similarity index 100% rename from effects/shaders/chromakey.xml rename to effects/shaders/need checking/chromakey.xml diff --git a/effects/shaders/colorcorrection.frag b/effects/shaders/need checking/colorcorrection.frag similarity index 100% rename from effects/shaders/colorcorrection.frag rename to effects/shaders/need checking/colorcorrection.frag diff --git a/effects/shaders/colorcorrection.xml b/effects/shaders/need checking/colorcorrection.xml similarity index 100% rename from effects/shaders/colorcorrection.xml rename to effects/shaders/need checking/colorcorrection.xml diff --git a/effects/shaders/colorsel.frag b/effects/shaders/need checking/colorsel.frag similarity index 89% rename from effects/shaders/colorsel.frag rename to effects/shaders/need checking/colorsel.frag index 903e4a0f8..dc4b7f280 100644 --- a/effects/shaders/colorsel.frag +++ b/effects/shaders/need checking/colorsel.frag @@ -2,10 +2,6 @@ Based on Edward Cannon's Simple Chroma Key (adaptation by Olive Team) RGB to HSV based on MattKC's toonify source code Feel free to modify and use at will */ -#version 110 - -uniform sampler2D tex; -varying vec2 vTexCoord; uniform float loc; uniform float hic; @@ -57,8 +53,7 @@ bool isNotIncreasingSequence(float a, float b, float c) { return (c < b || a > b); } -void main(void) { - vec4 texture_color = texture2D(tex,vTexCoord); +vec4 process(vec4 texture_color) { vec3 color = texture_color.rgb; float toCheck = 0.0; @@ -79,5 +74,5 @@ void main(void) { } texture_color.a = isNotIncreasingSequence(loc, toCheck, hic) ? (invert ? texture_color.a : 0.0) : (invert ? 0.0 : texture_color.a); texture_color.rgb *= texture_color.a; - gl_FragColor = texture_color; + return texture_color; } diff --git a/effects/shaders/colorsel.xml b/effects/shaders/need checking/colorsel.xml similarity index 100% rename from effects/shaders/colorsel.xml rename to effects/shaders/need checking/colorsel.xml diff --git a/effects/shaders/discard/common.frag b/effects/shaders/need checking/common.frag similarity index 100% rename from effects/shaders/discard/common.frag rename to effects/shaders/need checking/common.frag diff --git a/effects/shaders/discard/common.vert b/effects/shaders/need checking/common.vert similarity index 100% rename from effects/shaders/discard/common.vert rename to effects/shaders/need checking/common.vert diff --git a/effects/shaders/crossstitch.frag b/effects/shaders/need checking/crossstitch.frag similarity index 100% rename from effects/shaders/crossstitch.frag rename to effects/shaders/need checking/crossstitch.frag diff --git a/effects/shaders/crossstitch.xml b/effects/shaders/need checking/crossstitch.xml similarity index 100% rename from effects/shaders/crossstitch.xml rename to effects/shaders/need checking/crossstitch.xml diff --git a/effects/shaders/emboss.frag b/effects/shaders/need checking/emboss.frag similarity index 100% rename from effects/shaders/emboss.frag rename to effects/shaders/need checking/emboss.frag diff --git a/effects/shaders/emboss.xml b/effects/shaders/need checking/emboss.xml similarity index 100% rename from effects/shaders/emboss.xml rename to effects/shaders/need checking/emboss.xml diff --git a/effects/shaders/findedges.frag b/effects/shaders/need checking/findedges.frag similarity index 100% rename from effects/shaders/findedges.frag rename to effects/shaders/need checking/findedges.frag diff --git a/effects/shaders/findedges.xml.disabled b/effects/shaders/need checking/findedges.xml.disabled similarity index 100% rename from effects/shaders/findedges.xml.disabled rename to effects/shaders/need checking/findedges.xml.disabled diff --git a/effects/shaders/fisheye.frag b/effects/shaders/need checking/fisheye.frag similarity index 67% rename from effects/shaders/fisheye.frag rename to effects/shaders/need checking/fisheye.frag index 18689ccc8..a8b57b7a1 100644 --- a/effects/shaders/fisheye.frag +++ b/effects/shaders/need checking/fisheye.frag @@ -1,15 +1,12 @@ -#version 120 -uniform sampler2D tex0; -varying vec2 vTexCoord; #define M_PI 3.1415926535897932384626433832795 uniform float size; -void main(void) { +vec4 process(vec4 col) { float maxFactor = 2.0 - (size * 0.01); vec2 uv; - vec2 xy = 2.0 * vTexCoord - 1.0; + vec2 xy = 2.0 * v_texcoord - 1.0; float d = length(xy); if (d < (2.0-maxFactor)) { d = length(xy * maxFactor); @@ -20,8 +17,7 @@ void main(void) { uv.x = r * cos(phi) + 0.5; uv.y = r * sin(phi) + 0.5; } else { - uv = vTexCoord; + uv = v_texcoord; } - vec4 c = texture2D(tex0, uv); - gl_FragColor = c; + return texture2D(texture, uv); } \ No newline at end of file diff --git a/effects/shaders/fisheye.xml b/effects/shaders/need checking/fisheye.xml similarity index 100% rename from effects/shaders/fisheye.xml rename to effects/shaders/need checking/fisheye.xml diff --git a/effects/shaders/huesatbri.frag b/effects/shaders/need checking/huesatbri.frag similarity index 77% rename from effects/shaders/huesatbri.frag rename to effects/shaders/need checking/huesatbri.frag index 21f0fcecb..0582fa501 100644 --- a/effects/shaders/huesatbri.frag +++ b/effects/shaders/need checking/huesatbri.frag @@ -1,12 +1,7 @@ -#version 110 - uniform float hue; uniform float saturation; uniform float brightness; -uniform sampler2D myTexture; -varying vec2 vTexCoord; - vec3 rgb2hsv(vec3 c) { vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); @@ -23,9 +18,7 @@ vec3 hsv2rgb(vec3 c) { return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } -void main(void) { - vec4 tex_color = texture2D(myTexture, vTexCoord); - +vec4 process(vec4 tex_color) { vec3 hsv = rgb2hsv(tex_color.rgb); hsv.r += (hue/360.0); hsv.g *= (saturation*0.01); @@ -33,10 +26,8 @@ void main(void) { vec3 rgb = hsv2rgb(hsv); - gl_FragColor = vec4( - rgb.r, - rgb.g, - rgb.b, + return vec4( + rgb.rgb, tex_color.a ); } \ No newline at end of file diff --git a/effects/shaders/huesatbri.xml b/effects/shaders/need checking/huesatbri.xml similarity index 100% rename from effects/shaders/huesatbri.xml rename to effects/shaders/need checking/huesatbri.xml diff --git a/effects/shaders/discard/invert.frag b/effects/shaders/need checking/invert.frag similarity index 100% rename from effects/shaders/discard/invert.frag rename to effects/shaders/need checking/invert.frag diff --git a/effects/shaders/discard/invert.xml b/effects/shaders/need checking/invert.xml similarity index 100% rename from effects/shaders/discard/invert.xml rename to effects/shaders/need checking/invert.xml diff --git a/effects/shaders/lumakey.frag b/effects/shaders/need checking/lumakey.frag similarity index 100% rename from effects/shaders/lumakey.frag rename to effects/shaders/need checking/lumakey.frag diff --git a/effects/shaders/lumakey.xml b/effects/shaders/need checking/lumakey.xml similarity index 100% rename from effects/shaders/lumakey.xml rename to effects/shaders/need checking/lumakey.xml diff --git a/effects/shaders/discard/posterize.frag b/effects/shaders/need checking/posterize.frag similarity index 100% rename from effects/shaders/discard/posterize.frag rename to effects/shaders/need checking/posterize.frag diff --git a/effects/shaders/discard/posterize.xml b/effects/shaders/need checking/posterize.xml similarity index 100% rename from effects/shaders/discard/posterize.xml rename to effects/shaders/need checking/posterize.xml diff --git a/effects/shaders/toonify.frag b/effects/shaders/need checking/toonify.frag similarity index 100% rename from effects/shaders/toonify.frag rename to effects/shaders/need checking/toonify.frag diff --git a/effects/shaders/toonify.xml b/effects/shaders/need checking/toonify.xml similarity index 100% rename from effects/shaders/toonify.xml rename to effects/shaders/need checking/toonify.xml diff --git a/effects/shaders/discard/volumetriclight.frag b/effects/shaders/need checking/volumetriclight.frag similarity index 100% rename from effects/shaders/discard/volumetriclight.frag rename to effects/shaders/need checking/volumetriclight.frag diff --git a/effects/shaders/discard/volumetriclight.xml b/effects/shaders/need checking/volumetriclight.xml similarity index 100% rename from effects/shaders/discard/volumetriclight.xml rename to effects/shaders/need checking/volumetriclight.xml diff --git a/effects/shaders/tile.frag b/effects/shaders/tile.frag index 5c076b647..c7194a246 100644 --- a/effects/shaders/tile.frag +++ b/effects/shaders/tile.frag @@ -1,33 +1,14 @@ -#version 110 - -varying vec2 vTexCoord; -uniform sampler2D tex0; // scene buffer - uniform float scale; uniform float centerx; uniform float centery; uniform bool mirrorx; uniform bool mirrory; -void main(void) { - vec2 texCoord = vTexCoord; - - /*vec2 coord = vec2( - mod(vTexCoord.x*tilecount, 1.0), - mod(vTexCoord.y*tilecount, 1.0) - ); - - if (mirrorx && mod(vTexCoord.x*tilecount, 2.0) > 1.0) { - coord.x = 1.0 - coord.x; - } - - if (mirrory && mod(vTexCoord.y*tilecount, 2.0) > 1.0) { - coord.y = 1.0 - coord.y; - }*/ - +vec4 process(vec4 col) { + float adj_scale = scale*0.01; - vec2 scaled_coords = (vTexCoord/adj_scale); + vec2 scaled_coords = (v_texcoord/adj_scale); vec2 coord = scaled_coords-vec2(0.5/adj_scale, 0.5/adj_scale)+vec2(0.5, 0.5)+vec2(-centerx*0.01, -centery*0.01); vec2 modcoord = mod(coord, 1.0); @@ -39,5 +20,6 @@ void main(void) { modcoord.y = 1.0 - modcoord.y; } - gl_FragColor = vec4(texture2D(tex0, modcoord)); + return vec4(texture2D(texture, modcoord)); + } \ No newline at end of file diff --git a/effects/shaders/vignette.frag b/effects/shaders/vignette.frag index df200f234..86d13b14b 100644 --- a/effects/shaders/vignette.frag +++ b/effects/shaders/vignette.frag @@ -1,20 +1,14 @@ -#version 110 - -uniform sampler2D sceneTex; // 0 uniform float lensRadiusX; uniform float lensRadiusY; uniform bool circular; uniform vec2 resolution; // uniform vec2 lensRadius; // 0.45, 0.38 -varying vec2 vTexCoord; - -void main(void) { +vec4 process(vec4 c) { if (lensRadiusX == 0.0) { - discard; + return vec4(0.0); } - vec4 c = texture2D(sceneTex, vTexCoord); - vec2 vignetteCoord = vTexCoord; + vec2 vignetteCoord = v_texcoord; if (circular) { float ar = (resolution.x/resolution.y); vignetteCoord.x *= ar; @@ -22,6 +16,5 @@ void main(void) { } float dist = distance(vignetteCoord, vec2(0.5,0.5)); float size = (lensRadiusX*0.01); - c *= smoothstep(size, size*0.99*(1.0-lensRadiusY*0.01), dist); - gl_FragColor = c; -} \ No newline at end of file + return vec4(c.rgb * smoothstep(size, size*0.99*(1.0-lensRadiusY*0.01), dist), c.a); +} diff --git a/ui/collapsiblewidget.cpp b/ui/collapsiblewidget.cpp index 078ef0ef5..4132078b4 100644 --- a/ui/collapsiblewidget.cpp +++ b/ui/collapsiblewidget.cpp @@ -108,7 +108,6 @@ void CollapsibleWidget::SetContents(QWidget* c) { contents = c; if (!existing) { layout->addWidget(contents); - connect(enabled_check, SIGNAL(toggled(bool)), contents, SLOT(setEnabled(bool))); connect(collapse_button, SIGNAL(clicked()), this, SLOT(on_visible_change())); } } diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 9f1ef26ef..8f77b43ba 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -347,7 +347,7 @@ void GraphView::paintEvent(QPaintEvent *) { p.drawLine(playhead_x, 0, playhead_x, height()); if (rect_select) { - draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); + olive::ui::DrawSelectionRectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); p.setBrush(Qt::NoBrush); } } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 201d9cfb8..409294548 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -182,7 +182,7 @@ void KeyframeView::paintEvent(QPaintEvent*) { } if (select_rect) { - draw_selection_rectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); + olive::ui::DrawSelectionRectangle(p, QRect(rect_select_x, rect_select_y, rect_select_w, rect_select_h)); } /*if (mouseover && mouseover_row < rowY.size()) { diff --git a/ui/rectangleselect.cpp b/ui/rectangleselect.cpp index 8e13650a2..8c865ebf2 100644 --- a/ui/rectangleselect.cpp +++ b/ui/rectangleselect.cpp @@ -20,7 +20,7 @@ #include "rectangleselect.h" -void draw_selection_rectangle(QPainter& painter, const QRect& rect) { +void olive::ui::DrawSelectionRectangle(QPainter& painter, const QRect& rect) { painter.setPen(QColor(204, 204, 204)); painter.setBrush(QColor(0, 0, 0, 32)); painter.drawRect(rect); diff --git a/ui/rectangleselect.h b/ui/rectangleselect.h index 45d0d4d5d..10764ffa9 100644 --- a/ui/rectangleselect.h +++ b/ui/rectangleselect.h @@ -23,6 +23,9 @@ #include +namespace olive { +namespace ui { + /** * @brief Routine for drawing a drag selection rectangle for any given QPainter * @@ -34,6 +37,9 @@ * * Rectangle to draw */ -void draw_selection_rectangle(QPainter& painter, const QRect& rect); +void DrawSelectionRectangle(QPainter& painter, const QRect& rect); + +} +} #endif // RECTANGLESELECT_H diff --git a/ui/resizablescrollbar.h b/ui/resizablescrollbar.h index 1a861f7bf..571ada493 100644 --- a/ui/resizablescrollbar.h +++ b/ui/resizablescrollbar.h @@ -25,25 +25,25 @@ class ResizableScrollBar : public QScrollBar { - Q_OBJECT + Q_OBJECT public: - ResizableScrollBar(QWidget * parent = 0); - bool is_resizing(); + ResizableScrollBar(QWidget * parent = nullptr); + bool is_resizing(); signals: - void resize_move(double i); + void resize_move(double i); protected: - void resizeEvent(QResizeEvent *event) override; - void mousePressEvent(QMouseEvent *) override; - void mouseMoveEvent(QMouseEvent *) override; - void mouseReleaseEvent(QMouseEvent *) override; + void resizeEvent(QResizeEvent *event) override; + void mousePressEvent(QMouseEvent *) override; + void mouseMoveEvent(QMouseEvent *) override; + void mouseReleaseEvent(QMouseEvent *) override; private: - bool resize_init; - bool resize_proc; - int resize_start; - bool resize_top; + bool resize_init; + bool resize_proc; + int resize_start; + bool resize_top; - int resize_start_max; - int resize_start_width; + int resize_start_max; + int resize_start_width; }; #endif // RESIZABLESCROLLBAR_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 00ce56aca..8922f3450 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -3269,7 +3269,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) { rect_select.translate(0, height()); } - draw_selection_rectangle(p, rect_select); + olive::ui::DrawSelectionRectangle(p, rect_select); } // Draw ghosts From 1fdac776d402d15b29a37451d31cf121dd87bb36 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 29 Mar 2019 23:47:15 +1100 Subject: [PATCH 058/133] began timeline rewrite --- olive.pro | 771 +++++++++++++++++---------------- panels/timeline.cpp | 40 +- panels/timeline.h | 40 +- timeline/ghost.h | 32 ++ timeline/timelinefunctions.cpp | 6 + timeline/timelinefunctions.h | 31 ++ ui/timelinewidget.cpp | 87 ++-- undo/undo.cpp | 87 ++-- undo/undo.h | 4 +- 9 files changed, 565 insertions(+), 533 deletions(-) create mode 100644 timeline/ghost.h create mode 100644 timeline/timelinefunctions.cpp create mode 100644 timeline/timelinefunctions.h diff --git a/olive.pro b/olive.pro index fcde7f036..a47ec0107 100644 --- a/olive.pro +++ b/olive.pro @@ -1,384 +1,387 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2018-05-11T10:31:59 -# -#------------------------------------------------- - -QT += core gui multimedia opengl svg - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -mac { - TARGET = Olive -} -!mac { - TARGET = olive-editor -} -TEMPLATE = app - -# The following define makes your compiler emit warnings if you use -# any feature of Qt which has been marked as deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if you use deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -# Tries to get the current Git short hash -system("which git") { - GITPATH = $$PWD - - win32 { - GITPATH = $$system(cygpath $$PWD) - } - - GITHASHVAR = $$system(git --git-dir=\"$$GITPATH/.git\" --work-tree=\"$$GITPATH\" log -1 --format=%h) - - # Fallback for Ubuntu/Launchpad (extracts Git hash from debian/changelog rather than Git repo) - # (see https://answers.launchpad.net/launchpad/+question/678556) - isEmpty(GITHASHVAR) { - GITHASHVAR = $$system(sh $$PWD/debian/gitfromlog.sh $$PWD/debian/changelog) - } - - DEFINES += GITHASH=\\"\"$$GITHASHVAR\\"\" -} - -CONFIG += c++11 - -SOURCES += \ - main.cpp \ - ui/mainwindow.cpp \ - panels/project.cpp \ - panels/effectcontrols.cpp \ - panels/viewer.cpp \ - panels/timeline.cpp \ - ui/sourcetable.cpp \ - dialogs/aboutdialog.cpp \ - ui/timelinewidget.cpp \ - project/media.cpp \ - project/footage.cpp \ - timeline/sequence.cpp \ - timeline/clip.cpp \ - global/config.cpp \ - dialogs/newsequencedialog.cpp \ - ui/viewerwidget.cpp \ - ui/viewercontainer.cpp \ - dialogs/exportdialog.cpp \ - ui/collapsiblewidget.cpp \ - panels/panels.cpp \ - rendering/exportthread.cpp \ - ui/timelineheader.cpp \ - project/previewgenerator.cpp \ - ui/labelslider.cpp \ - dialogs/preferencesdialog.cpp \ - ui/audiomonitor.cpp \ - undo/undo.cpp \ - ui/scrollarea.cpp \ - ui/comboboxex.cpp \ - ui/colorbutton.cpp \ - dialogs/replaceclipmediadialog.cpp \ - ui/keyframeview.cpp \ - ui/texteditex.cpp \ - dialogs/demonotice.cpp \ - timeline/marker.cpp \ - dialogs/speeddialog.cpp \ - dialogs/mediapropertiesdialog.cpp \ - project/projectmodel.cpp \ - project/loadthread.cpp \ - dialogs/loaddialog.cpp \ - global/debug.cpp \ - global/path.cpp \ - effects/internal/linearfadetransition.cpp \ - effects/internal/transformeffect.cpp \ - effects/internal/solideffect.cpp \ - effects/internal/texteffect.cpp \ - effects/internal/timecodeeffect.cpp \ - effects/internal/audionoiseeffect.cpp \ - effects/internal/paneffect.cpp \ - effects/internal/toneeffect.cpp \ - effects/internal/volumeeffect.cpp \ - effects/internal/crossdissolvetransition.cpp \ - effects/internal/shakeeffect.cpp \ - effects/internal/exponentialfadetransition.cpp \ - effects/internal/logarithmicfadetransition.cpp \ - effects/internal/cornerpineffect.cpp \ - global/math.cpp \ - effects/effect.cpp \ - effects/effectrow.cpp \ - effects/effectgizmo.cpp \ - project/clipboard.cpp \ - ui/resizablescrollbar.cpp \ - ui/sourceiconview.cpp \ - project/sourcescommon.cpp \ - ui/keyframenavigator.cpp \ - panels/grapheditor.cpp \ - ui/graphview.cpp \ - ui/keyframedrawing.cpp \ - ui/clickablelabel.cpp \ - effects/keyframe.cpp \ - ui/rectangleselect.cpp \ - dialogs/actionsearch.cpp \ - ui/embeddedfilechooser.cpp \ - effects/internal/fillleftrighteffect.cpp \ - effects/internal/voideffect.cpp \ - dialogs/texteditdialog.cpp \ - dialogs/debugdialog.cpp \ - ui/viewerwindow.cpp \ - project/projectfilter.cpp \ - effects/effectloaders.cpp \ - effects/internal/vsthost.cpp \ - ui/flowlayout.cpp \ - dialogs/proxydialog.cpp \ - project/proxygenerator.cpp \ - dialogs/advancedvideodialog.cpp \ - ui/cursors.cpp \ - ui/menuhelper.cpp \ - global/global.cpp \ - ui/focusfilter.cpp \ - undo/comboaction.cpp \ - ui/mediaiconservice.cpp \ - ui/panel.cpp \ - effects/internal/dropshadoweffect.cpp \ - rendering/renderfunctions.cpp \ - rendering/renderthread.cpp \ - rendering/cacher.cpp \ - rendering/clipqueue.cpp \ - rendering/audio.cpp \ - dialogs/clippropertiesdialog.cpp \ - rendering/framebufferobject.cpp \ - ui/updatenotification.cpp \ - ui/icons.cpp \ - effects/fields/doublefield.cpp \ - effects/fields/fontfield.cpp \ - effects/effectfield.cpp \ - effects/fields/colorfield.cpp \ - effects/fields/stringfield.cpp \ - effects/fields/boolfield.cpp \ - effects/fields/combofield.cpp \ - effects/fields/filefield.cpp \ - effects/fields/labelfield.cpp \ - effects/fields/buttonfield.cpp \ - ui/effectui.cpp \ - effects/transition.cpp \ - ui/styling.cpp \ - undo/undostack.cpp \ - effects/internal/richtexteffect.cpp \ - ui/blur.cpp \ - ui/menu.cpp \ - timeline/mediaimportdata.cpp \ - dialogs/autocutsilencedialog.cpp \ - ui/columnedgridlayout.cpp \ - rendering/shadergenerators.cpp \ - global/timing.cpp \ - rendering/pixelformats.cpp - -HEADERS += \ - ui/mainwindow.h \ - panels/project.h \ - panels/effectcontrols.h \ - panels/viewer.h \ - panels/timeline.h \ - ui/sourcetable.h \ - dialogs/aboutdialog.h \ - ui/timelinewidget.h \ - project/media.h \ - project/footage.h \ - timeline/sequence.h \ - timeline/clip.h \ - global/config.h \ - dialogs/newsequencedialog.h \ - ui/viewerwidget.h \ - ui/viewercontainer.h \ - dialogs/exportdialog.h \ - ui/collapsiblewidget.h \ - panels/panels.h \ - rendering/exportthread.h \ - ui/timelinetools.h \ - ui/timelineheader.h \ - project/previewgenerator.h \ - ui/labelslider.h \ - dialogs/preferencesdialog.h \ - ui/audiomonitor.h \ - undo/undo.h \ - ui/scrollarea.h \ - ui/comboboxex.h \ - ui/colorbutton.h \ - dialogs/replaceclipmediadialog.h \ - ui/keyframeview.h \ - ui/texteditex.h \ - dialogs/demonotice.h \ - timeline/marker.h \ - timeline/selection.h \ - dialogs/speeddialog.h \ - dialogs/mediapropertiesdialog.h \ - project/projectmodel.h \ - project/loadthread.h \ - dialogs/loaddialog.h \ - global/debug.h \ - global/path.h \ - effects/internal/transformeffect.h \ - effects/internal/solideffect.h \ - effects/internal/texteffect.h \ - effects/internal/timecodeeffect.h \ - effects/internal/audionoiseeffect.h \ - effects/internal/paneffect.h \ - effects/internal/toneeffect.h \ - effects/internal/volumeeffect.h \ - effects/internal/shakeeffect.h \ - effects/internal/linearfadetransition.h \ - effects/internal/crossdissolvetransition.h \ - effects/internal/exponentialfadetransition.h \ - effects/internal/logarithmicfadetransition.h \ - effects/internal/cornerpineffect.h \ - global/math.h \ - effects/effect.h \ - effects/effectrow.h \ - effects/internal/cubetransition.h \ - effects/effectgizmo.h \ - project/clipboard.h \ - ui/resizablescrollbar.h \ - ui/sourceiconview.h \ - project/sourcescommon.h \ - ui/keyframenavigator.h \ - panels/grapheditor.h \ - ui/graphview.h \ - ui/keyframedrawing.h \ - ui/clickablelabel.h \ - effects/keyframe.h \ - ui/rectangleselect.h \ - dialogs/actionsearch.h \ - ui/embeddedfilechooser.h \ - effects/internal/fillleftrighteffect.h \ - effects/internal/voideffect.h \ - dialogs/texteditdialog.h \ - dialogs/debugdialog.h \ - ui/viewerwindow.h \ - project/projectfilter.h \ - effects/effectloaders.h \ - effects/internal/vsthost.h \ - ui/flowlayout.h \ - dialogs/proxydialog.h \ - project/proxygenerator.h \ - dialogs/advancedvideodialog.h \ - ui/cursors.h \ - ui/menuhelper.h \ - global/global.h \ - project/projectelements.h \ - ui/focusfilter.h \ - undo/comboaction.h \ - ui/mediaiconservice.h \ - ui/panel.h \ - effects/internal/dropshadoweffect.h \ - rendering/renderfunctions.h \ - rendering/renderthread.h \ - rendering/clipqueue.h \ - rendering/cacher.h \ - rendering/audio.h \ - dialogs/clippropertiesdialog.h \ - rendering/framebufferobject.h \ - ui/updatenotification.h \ - ui/icons.h \ - effects/fields/doublefield.h \ - effects/fields/fontfield.h \ - effects/effectfield.h \ - effects/effectfields.h \ - effects/fields/stringfield.h \ - effects/fields/filefield.h \ - effects/fields/labelfield.h \ - ui/effectui.h \ - effects/fields/boolfield.h \ - effects/fields/buttonfield.h \ - effects/fields/colorfield.h \ - effects/fields/combofield.h \ - effects/transition.h \ - ui/styling.h \ - undo/undostack.h \ - effects/internal/richtexteffect.h \ - ui/blur.h \ - ui/menu.h \ - rendering/qopenglshaderprogramptr.h \ - timeline/mediaimportdata.h \ - dialogs/autocutsilencedialog.h \ - ui/columnedgridlayout.h \ - rendering/shadergenerators.h \ - global/timing.h \ - rendering/pixelformats.h - -FORMS += - -TRANSLATIONS += \ - ts/olive_de.ts \ - ts/olive_es.ts \ - ts/olive_fr.ts \ - ts/olive_it.ts \ - ts/olive_cs.ts \ - ts/olive_ar.ts \ - ts/olive_ru.ts \ - ts/olive_uk.ts \ - ts/olive_bs.ts \ - ts/olive_sr.ts \ - ts/olive_id.ts - -win32 { - CONFIG(debug, debug|release) { - CONFIG += console - } - - RC_FILE = packaging/windows/resources.rc - LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -lopengl32 -luser32 -} - -mac { - LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -framework CoreFoundation - ICON = packaging/macos/olive.icns - INCLUDEPATH = /usr/local/include -} - -unix:!mac { - CONFIG += link_pkgconfig - PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample OpenColorIO -} - -RESOURCES += \ - icons/icons.qrc \ - effects/internal/internalshaders.qrc \ - cursors/cursors.qrc - -unix:!mac:isEmpty(PREFIX) { - PREFIX = /usr/local -} - -unix:!mac:target.path = $$PREFIX/bin - -effects.files = $$PWD/effects/shaders/* -unix:!mac:effects.path = $$PREFIX/share/olive-editor/effects - -translations.files = $$PWD/ts/*.qm -unix:!mac:translations.path = $$PREFIX/share/olive-editor/ts - -unix:!mac { - metainfo.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.appdata.xml - metainfo.path = $$PREFIX/share/metainfo - desktop.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.desktop - desktop.path = $$PREFIX/share/applications - mime.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.xml - mime.path = $$PREFIX/share/mime/packages - icon16.files = $$PWD/packaging/linux/icons/16x16/org.olivevideoeditor.Olive.png - icon16.path = $$PREFIX/share/icons/hicolor/16x16/apps - icon32.files = $$PWD/packaging/linux/icons/32x32/org.olivevideoeditor.Olive.png - icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps - icon48.files = $$PWD/packaging/linux/icons/48x48/org.olivevideoeditor.Olive.png - icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps - icon64.files = $$PWD/packaging/linux/icons/64x64/org.olivevideoeditor.Olive.png - icon64.path = $$PREFIX/share/icons/hicolor/64x64/apps - icon128.files = $$PWD/packaging/linux/icons/128x128/org.olivevideoeditor.Olive.png - icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps - icon256.files = $$PWD/packaging/linux/icons/256x256/org.olivevideoeditor.Olive.png - icon256.path = $$PREFIX/share/icons/hicolor/256x256/apps - icon512.files = $$PWD/packaging/linux/icons/512x512/org.olivevideoeditor.Olive.png - icon512.path = $$PREFIX/share/icons/hicolor/512x512/apps - INSTALLS += target effects translations metainfo desktop mime icon16 icon32 icon48 icon64 icon128 icon256 icon512 -} +#------------------------------------------------- +# +# Project created by QtCreator 2018-05-11T10:31:59 +# +#------------------------------------------------- + +QT += core gui multimedia opengl svg + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +mac { + TARGET = Olive +} +!mac { + TARGET = olive-editor +} +TEMPLATE = app + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +# Tries to get the current Git short hash +system("which git") { + GITPATH = $$PWD + + win32 { + GITPATH = $$system(cygpath $$PWD) + } + + GITHASHVAR = $$system(git --git-dir=\"$$GITPATH/.git\" --work-tree=\"$$GITPATH\" log -1 --format=%h) + + # Fallback for Ubuntu/Launchpad (extracts Git hash from debian/changelog rather than Git repo) + # (see https://answers.launchpad.net/launchpad/+question/678556) + isEmpty(GITHASHVAR) { + GITHASHVAR = $$system(sh $$PWD/debian/gitfromlog.sh $$PWD/debian/changelog) + } + + DEFINES += GITHASH=\\"\"$$GITHASHVAR\\"\" +} + +CONFIG += c++11 + +SOURCES += \ + main.cpp \ + ui/mainwindow.cpp \ + panels/project.cpp \ + panels/effectcontrols.cpp \ + panels/viewer.cpp \ + panels/timeline.cpp \ + ui/sourcetable.cpp \ + dialogs/aboutdialog.cpp \ + ui/timelinewidget.cpp \ + project/media.cpp \ + project/footage.cpp \ + timeline/sequence.cpp \ + timeline/clip.cpp \ + global/config.cpp \ + dialogs/newsequencedialog.cpp \ + ui/viewerwidget.cpp \ + ui/viewercontainer.cpp \ + dialogs/exportdialog.cpp \ + ui/collapsiblewidget.cpp \ + panels/panels.cpp \ + rendering/exportthread.cpp \ + ui/timelineheader.cpp \ + project/previewgenerator.cpp \ + ui/labelslider.cpp \ + dialogs/preferencesdialog.cpp \ + ui/audiomonitor.cpp \ + undo/undo.cpp \ + ui/scrollarea.cpp \ + ui/comboboxex.cpp \ + ui/colorbutton.cpp \ + dialogs/replaceclipmediadialog.cpp \ + ui/keyframeview.cpp \ + ui/texteditex.cpp \ + dialogs/demonotice.cpp \ + timeline/marker.cpp \ + dialogs/speeddialog.cpp \ + dialogs/mediapropertiesdialog.cpp \ + project/projectmodel.cpp \ + project/loadthread.cpp \ + dialogs/loaddialog.cpp \ + global/debug.cpp \ + global/path.cpp \ + effects/internal/linearfadetransition.cpp \ + effects/internal/transformeffect.cpp \ + effects/internal/solideffect.cpp \ + effects/internal/texteffect.cpp \ + effects/internal/timecodeeffect.cpp \ + effects/internal/audionoiseeffect.cpp \ + effects/internal/paneffect.cpp \ + effects/internal/toneeffect.cpp \ + effects/internal/volumeeffect.cpp \ + effects/internal/crossdissolvetransition.cpp \ + effects/internal/shakeeffect.cpp \ + effects/internal/exponentialfadetransition.cpp \ + effects/internal/logarithmicfadetransition.cpp \ + effects/internal/cornerpineffect.cpp \ + global/math.cpp \ + effects/effect.cpp \ + effects/effectrow.cpp \ + effects/effectgizmo.cpp \ + project/clipboard.cpp \ + ui/resizablescrollbar.cpp \ + ui/sourceiconview.cpp \ + project/sourcescommon.cpp \ + ui/keyframenavigator.cpp \ + panels/grapheditor.cpp \ + ui/graphview.cpp \ + ui/keyframedrawing.cpp \ + ui/clickablelabel.cpp \ + effects/keyframe.cpp \ + ui/rectangleselect.cpp \ + dialogs/actionsearch.cpp \ + ui/embeddedfilechooser.cpp \ + effects/internal/fillleftrighteffect.cpp \ + effects/internal/voideffect.cpp \ + dialogs/texteditdialog.cpp \ + dialogs/debugdialog.cpp \ + ui/viewerwindow.cpp \ + project/projectfilter.cpp \ + effects/effectloaders.cpp \ + effects/internal/vsthost.cpp \ + ui/flowlayout.cpp \ + dialogs/proxydialog.cpp \ + project/proxygenerator.cpp \ + dialogs/advancedvideodialog.cpp \ + ui/cursors.cpp \ + ui/menuhelper.cpp \ + global/global.cpp \ + ui/focusfilter.cpp \ + undo/comboaction.cpp \ + ui/mediaiconservice.cpp \ + ui/panel.cpp \ + effects/internal/dropshadoweffect.cpp \ + rendering/renderfunctions.cpp \ + rendering/renderthread.cpp \ + rendering/cacher.cpp \ + rendering/clipqueue.cpp \ + rendering/audio.cpp \ + dialogs/clippropertiesdialog.cpp \ + rendering/framebufferobject.cpp \ + ui/updatenotification.cpp \ + ui/icons.cpp \ + effects/fields/doublefield.cpp \ + effects/fields/fontfield.cpp \ + effects/effectfield.cpp \ + effects/fields/colorfield.cpp \ + effects/fields/stringfield.cpp \ + effects/fields/boolfield.cpp \ + effects/fields/combofield.cpp \ + effects/fields/filefield.cpp \ + effects/fields/labelfield.cpp \ + effects/fields/buttonfield.cpp \ + ui/effectui.cpp \ + effects/transition.cpp \ + ui/styling.cpp \ + undo/undostack.cpp \ + effects/internal/richtexteffect.cpp \ + ui/blur.cpp \ + ui/menu.cpp \ + timeline/mediaimportdata.cpp \ + dialogs/autocutsilencedialog.cpp \ + ui/columnedgridlayout.cpp \ + rendering/shadergenerators.cpp \ + global/timing.cpp \ + rendering/pixelformats.cpp \ + timeline/timelinefunctions.cpp + +HEADERS += \ + ui/mainwindow.h \ + panels/project.h \ + panels/effectcontrols.h \ + panels/viewer.h \ + panels/timeline.h \ + ui/sourcetable.h \ + dialogs/aboutdialog.h \ + ui/timelinewidget.h \ + project/media.h \ + project/footage.h \ + timeline/sequence.h \ + timeline/clip.h \ + global/config.h \ + dialogs/newsequencedialog.h \ + ui/viewerwidget.h \ + ui/viewercontainer.h \ + dialogs/exportdialog.h \ + ui/collapsiblewidget.h \ + panels/panels.h \ + rendering/exportthread.h \ + ui/timelinetools.h \ + ui/timelineheader.h \ + project/previewgenerator.h \ + ui/labelslider.h \ + dialogs/preferencesdialog.h \ + ui/audiomonitor.h \ + undo/undo.h \ + ui/scrollarea.h \ + ui/comboboxex.h \ + ui/colorbutton.h \ + dialogs/replaceclipmediadialog.h \ + ui/keyframeview.h \ + ui/texteditex.h \ + dialogs/demonotice.h \ + timeline/marker.h \ + timeline/selection.h \ + dialogs/speeddialog.h \ + dialogs/mediapropertiesdialog.h \ + project/projectmodel.h \ + project/loadthread.h \ + dialogs/loaddialog.h \ + global/debug.h \ + global/path.h \ + effects/internal/transformeffect.h \ + effects/internal/solideffect.h \ + effects/internal/texteffect.h \ + effects/internal/timecodeeffect.h \ + effects/internal/audionoiseeffect.h \ + effects/internal/paneffect.h \ + effects/internal/toneeffect.h \ + effects/internal/volumeeffect.h \ + effects/internal/shakeeffect.h \ + effects/internal/linearfadetransition.h \ + effects/internal/crossdissolvetransition.h \ + effects/internal/exponentialfadetransition.h \ + effects/internal/logarithmicfadetransition.h \ + effects/internal/cornerpineffect.h \ + global/math.h \ + effects/effect.h \ + effects/effectrow.h \ + effects/internal/cubetransition.h \ + effects/effectgizmo.h \ + project/clipboard.h \ + ui/resizablescrollbar.h \ + ui/sourceiconview.h \ + project/sourcescommon.h \ + ui/keyframenavigator.h \ + panels/grapheditor.h \ + ui/graphview.h \ + ui/keyframedrawing.h \ + ui/clickablelabel.h \ + effects/keyframe.h \ + ui/rectangleselect.h \ + dialogs/actionsearch.h \ + ui/embeddedfilechooser.h \ + effects/internal/fillleftrighteffect.h \ + effects/internal/voideffect.h \ + dialogs/texteditdialog.h \ + dialogs/debugdialog.h \ + ui/viewerwindow.h \ + project/projectfilter.h \ + effects/effectloaders.h \ + effects/internal/vsthost.h \ + ui/flowlayout.h \ + dialogs/proxydialog.h \ + project/proxygenerator.h \ + dialogs/advancedvideodialog.h \ + ui/cursors.h \ + ui/menuhelper.h \ + global/global.h \ + project/projectelements.h \ + ui/focusfilter.h \ + undo/comboaction.h \ + ui/mediaiconservice.h \ + ui/panel.h \ + effects/internal/dropshadoweffect.h \ + rendering/renderfunctions.h \ + rendering/renderthread.h \ + rendering/clipqueue.h \ + rendering/cacher.h \ + rendering/audio.h \ + dialogs/clippropertiesdialog.h \ + rendering/framebufferobject.h \ + ui/updatenotification.h \ + ui/icons.h \ + effects/fields/doublefield.h \ + effects/fields/fontfield.h \ + effects/effectfield.h \ + effects/effectfields.h \ + effects/fields/stringfield.h \ + effects/fields/filefield.h \ + effects/fields/labelfield.h \ + ui/effectui.h \ + effects/fields/boolfield.h \ + effects/fields/buttonfield.h \ + effects/fields/colorfield.h \ + effects/fields/combofield.h \ + effects/transition.h \ + ui/styling.h \ + undo/undostack.h \ + effects/internal/richtexteffect.h \ + ui/blur.h \ + ui/menu.h \ + rendering/qopenglshaderprogramptr.h \ + timeline/mediaimportdata.h \ + dialogs/autocutsilencedialog.h \ + ui/columnedgridlayout.h \ + rendering/shadergenerators.h \ + global/timing.h \ + rendering/pixelformats.h \ + timeline/ghost.h \ + timeline/timelinefunctions.h + +FORMS += + +TRANSLATIONS += \ + ts/olive_de.ts \ + ts/olive_es.ts \ + ts/olive_fr.ts \ + ts/olive_it.ts \ + ts/olive_cs.ts \ + ts/olive_ar.ts \ + ts/olive_ru.ts \ + ts/olive_uk.ts \ + ts/olive_bs.ts \ + ts/olive_sr.ts \ + ts/olive_id.ts + +win32 { + CONFIG(debug, debug|release) { + CONFIG += console + } + + RC_FILE = packaging/windows/resources.rc + LIBS += -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -lopengl32 -luser32 +} + +mac { + LIBS += -L/usr/local/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample -lOpenColorIO -framework CoreFoundation + ICON = packaging/macos/olive.icns + INCLUDEPATH = /usr/local/include +} + +unix:!mac { + CONFIG += link_pkgconfig + PKGCONFIG += libavutil libavformat libavcodec libavfilter libswscale libswresample OpenColorIO +} + +RESOURCES += \ + icons/icons.qrc \ + effects/internal/internalshaders.qrc \ + cursors/cursors.qrc + +unix:!mac:isEmpty(PREFIX) { + PREFIX = /usr/local +} + +unix:!mac:target.path = $$PREFIX/bin + +effects.files = $$PWD/effects/shaders/* +unix:!mac:effects.path = $$PREFIX/share/olive-editor/effects + +translations.files = $$PWD/ts/*.qm +unix:!mac:translations.path = $$PREFIX/share/olive-editor/ts + +unix:!mac { + metainfo.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.appdata.xml + metainfo.path = $$PREFIX/share/metainfo + desktop.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.desktop + desktop.path = $$PREFIX/share/applications + mime.files = $$PWD/packaging/linux/org.olivevideoeditor.Olive.xml + mime.path = $$PREFIX/share/mime/packages + icon16.files = $$PWD/packaging/linux/icons/16x16/org.olivevideoeditor.Olive.png + icon16.path = $$PREFIX/share/icons/hicolor/16x16/apps + icon32.files = $$PWD/packaging/linux/icons/32x32/org.olivevideoeditor.Olive.png + icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps + icon48.files = $$PWD/packaging/linux/icons/48x48/org.olivevideoeditor.Olive.png + icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps + icon64.files = $$PWD/packaging/linux/icons/64x64/org.olivevideoeditor.Olive.png + icon64.path = $$PREFIX/share/icons/hicolor/64x64/apps + icon128.files = $$PWD/packaging/linux/icons/128x128/org.olivevideoeditor.Olive.png + icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps + icon256.files = $$PWD/packaging/linux/icons/256x256/org.olivevideoeditor.Olive.png + icon256.path = $$PREFIX/share/icons/hicolor/256x256/apps + icon512.files = $$PWD/packaging/linux/icons/512x512/org.olivevideoeditor.Olive.png + icon512.path = $$PREFIX/share/icons/hicolor/512x512/apps + INSTALLS += target effects translations metainfo desktop mime icon16 icon32 icon48 icon64 icon128 icon256 icon512 +} diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 66469146b..a6ff725bc 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -77,7 +77,7 @@ Timeline::Timeline(QWidget *parent) : moving_proc(false), move_insert(false), trim_target(-1), - trim_type(TRIM_NONE), + trim_type(olive::timeline::TRIM_NONE), splitting(false), importing(false), importing_files(false), @@ -285,7 +285,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector if (can_import) { Ghost g; g.clip = -1; - g.trim_type = TRIM_NONE; + g.trim_type = olive::timeline::TRIM_NONE; g.old_clip_in = g.clip_in = default_clip_in; g.media = medium; g.in = entry_point; @@ -1012,13 +1012,6 @@ ClipPtr Timeline::split_clip(ComboAction* ca, bool transitions, int p, long fram } bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool relink) { - // see if we split this clip before - if (split_cache.contains(clip)) { - return false; - } - - split_cache.append(clip); - Clip* c = olive::ActiveSequence->clips.at(clip).get(); if (c != nullptr) { QVector pre_clips; @@ -1040,15 +1033,12 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool // find linked clips of old clip for (int i=0;ilinked.size();i++) { int l = c->linked.at(i); - if (!split_cache.contains(l)) { - Clip* link = olive::ActiveSequence->clips.at(l).get(); - if ((original_clip_is_selected && link->IsSelected()) || !original_clip_is_selected) { - split_cache.append(l); - ClipPtr s = split_clip(ca, true, l, frame); - if (s != nullptr) { - pre_clips.append(l); - post_clips.append(s); - } + Clip* link = olive::ActiveSequence->clips.at(l).get(); + if ((original_clip_is_selected && link->IsSelected()) || !original_clip_is_selected) { + ClipPtr s = split_clip(ca, true, l, frame); + if (s != nullptr) { + pre_clips.append(l); + post_clips.append(s); } } } @@ -1292,7 +1282,6 @@ void Timeline::paste(bool insert) { } } if (insert) { - split_cache.clear(); split_all_clips_at_point(ca, olive::ActiveSequence->playhead); ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); } else { @@ -1570,7 +1559,6 @@ bool Timeline::split_all_clips_at_point(ComboAction* ca, long point) { void Timeline::split_at_playhead() { ComboAction* ca = new ComboAction(); bool split_selected = false; - split_cache.clear(); if (olive::ActiveSequence->selections.size() > 0) { // see if whole clips are selected @@ -1825,29 +1813,29 @@ void Timeline::add_btn_click() { QAction* titleMenuItem = new QAction(&add_menu); titleMenuItem->setText(tr("Title...")); - titleMenuItem->setData(ADD_OBJ_TITLE); + titleMenuItem->setData(olive::timeline::ADD_OBJ_TITLE); add_menu.addAction(titleMenuItem); QAction* solidMenuItem = new QAction(&add_menu); solidMenuItem->setText(tr("Solid Color...")); - solidMenuItem->setData(ADD_OBJ_SOLID); + solidMenuItem->setData(olive::timeline::ADD_OBJ_SOLID); add_menu.addAction(solidMenuItem); QAction* barsMenuItem = new QAction(&add_menu); barsMenuItem->setText(tr("Bars...")); - barsMenuItem->setData(ADD_OBJ_BARS); + barsMenuItem->setData(olive::timeline::ADD_OBJ_BARS); add_menu.addAction(barsMenuItem); add_menu.addSeparator(); QAction* toneMenuItem = new QAction(&add_menu); toneMenuItem->setText(tr("Tone...")); - toneMenuItem->setData(ADD_OBJ_TONE); + toneMenuItem->setData(olive::timeline::ADD_OBJ_TONE); add_menu.addAction(toneMenuItem); QAction* noiseMenuItem = new QAction(&add_menu); noiseMenuItem->setText(tr("Noise...")); - noiseMenuItem->setData(ADD_OBJ_NOISE); + noiseMenuItem->setData(olive::timeline::ADD_OBJ_NOISE); add_menu.addAction(noiseMenuItem); connect(&add_menu, SIGNAL(triggered(QAction*)), this, SLOT(add_menu_item(QAction*))); @@ -1874,7 +1862,7 @@ void Timeline::record_btn_click() { QMessageBox::Ok); } else { creating = true; - creating_object = ADD_OBJ_AUDIO; + creating_object = olive::timeline::ADD_OBJ_AUDIO; olive::MainWindow->statusBar()->showMessage( tr("Click on the timeline where you want to start recording (drag to limit the recording to a certain timeframe)"), 10000); diff --git a/panels/timeline.h b/panels/timeline.h index cd64c75a5..4dfb2cb1e 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -30,26 +30,14 @@ #include "timeline/selection.h" #include "timeline/clip.h" #include "timeline/mediaimportdata.h" +#include "timeline/ghost.h" #include "undo/undo.h" #include "ui/timelineheader.h" #include "ui/resizablescrollbar.h" #include "ui/audiomonitor.h" #include "ui/panel.h" -enum CreateObjects { - ADD_OBJ_TITLE, - ADD_OBJ_SOLID, - ADD_OBJ_BARS, - ADD_OBJ_TONE, - ADD_OBJ_NOISE, - ADD_OBJ_AUDIO -}; -enum TrimType { - TRIM_NONE, - TRIM_IN, - TRIM_OUT -}; namespace olive { namespace timeline { @@ -77,30 +65,7 @@ long getFrameFromScreenPoint(double zoom, int x); bool selection_contains_transition(const Selection& s, Clip *c, int type); void ripple_clips(ComboAction *ca, Sequence *s, long point, long length, const QVector& ignore = QVector()); -struct Ghost { - int clip; - long in; - long out; - int track; - long clip_in; - long old_in; - long old_out; - int old_track; - long old_clip_in; - - // importing variables - Media* media; - int media_stream; - - // other variables - long ghost_length; - long media_length; - TrimType trim_type; - - // transition trimming - TransitionPtr transition; -}; class Timeline : public Panel { @@ -179,13 +144,12 @@ public: // trimming int trim_target; - TrimType trim_type; + olive::timeline::TrimType trim_type; int transition_select; // splitting bool splitting; QVector split_tracks; - QVector split_cache; // importing bool importing; diff --git a/timeline/ghost.h b/timeline/ghost.h new file mode 100644 index 000000000..4ee9a3806 --- /dev/null +++ b/timeline/ghost.h @@ -0,0 +1,32 @@ +#ifndef GHOST_H +#define GHOST_H + +#include "effects/transition.h" +#include "timelinefunctions.h" + +struct Ghost { + int clip; + long in; + long out; + int track; + long clip_in; + + long old_in; + long old_out; + int old_track; + long old_clip_in; + + // importing variables + Media* media; + int media_stream; + + // other variables + long ghost_length; + long media_length; + olive::timeline::TrimType trim_type; + + // transition trimming + TransitionPtr transition; +}; + +#endif // GHOST_H diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp new file mode 100644 index 000000000..b3f3b4b86 --- /dev/null +++ b/timeline/timelinefunctions.cpp @@ -0,0 +1,6 @@ +#include "timelinefunctions.h" + +TimelineFunctions::TimelineFunctions() +{ + +} diff --git a/timeline/timelinefunctions.h b/timeline/timelinefunctions.h new file mode 100644 index 000000000..b42ce8b50 --- /dev/null +++ b/timeline/timelinefunctions.h @@ -0,0 +1,31 @@ +#ifndef TIMELINEFUNCTIONS_H +#define TIMELINEFUNCTIONS_H + +namespace olive { +namespace timeline { + +enum CreateObjects { + ADD_OBJ_TITLE, + ADD_OBJ_SOLID, + ADD_OBJ_BARS, + ADD_OBJ_TONE, + ADD_OBJ_NOISE, + ADD_OBJ_AUDIO +}; + +enum TrimType { + TRIM_NONE, + TRIM_IN, + TRIM_OUT +}; + +} +} + +class TimelineFunctions +{ +public: + TimelineFunctions(); +}; + +#endif // TIMELINEFUNCTIONS_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 8922f3450..e6e90fa58 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -466,8 +466,6 @@ void insert_clips(ComboAction* ca) { } } - panel_timeline->split_cache.clear(); - for (int i=0;iclips.size();i++) { ClipPtr c = olive::ActiveSequence->clips.at(i); if (c != nullptr) { @@ -624,14 +622,14 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { if (panel_timeline->creating) { int comp = 0; switch (panel_timeline->creating_object) { - case ADD_OBJ_TITLE: - case ADD_OBJ_SOLID: - case ADD_OBJ_BARS: + case olive::timeline::ADD_OBJ_TITLE: + case olive::timeline::ADD_OBJ_SOLID: + case olive::timeline::ADD_OBJ_BARS: comp = -1; break; - case ADD_OBJ_TONE: - case ADD_OBJ_NOISE: - case ADD_OBJ_AUDIO: + case olive::timeline::ADD_OBJ_TONE: + case olive::timeline::ADD_OBJ_NOISE: + case olive::timeline::ADD_OBJ_AUDIO: comp = 1; break; } @@ -644,7 +642,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { g.track = g.old_track = panel_timeline->drag_track_start; g.transition = nullptr; g.clip = -1; - g.trim_type = TRIM_OUT; + g.trim_type = olive::timeline::TRIM_OUT; panel_timeline->ghosts.append(g); panel_timeline->moving_init = true; @@ -1003,7 +1001,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { if (panel_timeline->ghosts.size() > 0) { const Ghost& g = panel_timeline->ghosts.at(0); - if (panel_timeline->creating_object == ADD_OBJ_AUDIO) { + if (panel_timeline->creating_object == olive::timeline::ADD_OBJ_AUDIO) { olive::MainWindow->statusBar()->clearMessage(); panel_sequence_viewer->cue_recording(qMin(g.in, g.out), qMax(g.in, g.out), g.track); panel_timeline->creating = false; @@ -1038,15 +1036,15 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { } switch (panel_timeline->creating_object) { - case ADD_OBJ_TITLE: + case olive::timeline::ADD_OBJ_TITLE: c->set_name(tr("Title")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_RICHTEXT, EFFECT_TYPE_EFFECT))); break; - case ADD_OBJ_SOLID: + case olive::timeline::ADD_OBJ_SOLID: c->set_name(tr("Solid Color")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_SOLID, EFFECT_TYPE_EFFECT))); break; - case ADD_OBJ_BARS: + case olive::timeline::ADD_OBJ_BARS: { c->set_name(tr("Bars")); EffectPtr e = Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_SOLID, EFFECT_TYPE_EFFECT)); @@ -1058,11 +1056,11 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { c->effects.append(e); } break; - case ADD_OBJ_TONE: + case olive::timeline::ADD_OBJ_TONE: c->set_name(tr("Tone")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TONE, EFFECT_TYPE_EFFECT))); break; - case ADD_OBJ_NOISE: + case olive::timeline::ADD_OBJ_NOISE: c->set_name(tr("Noise")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_NOISE, EFFECT_TYPE_EFFECT))); break; @@ -1110,7 +1108,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { long ripple_length; long ripple_point = LONG_MAX; - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { // it's assumed that all the ghosts rippled by the same length, so we just take the difference of the // first ghost here @@ -1136,19 +1134,19 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { // for the same reason that we pushed selections forward above, for in trimming, // we push the ghosts forward here - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { ignore_clips.append(g.clip); panel_timeline->ghosts[i].in += ripple_length; panel_timeline->ghosts[i].out += ripple_length; } // find the earliest ripple point - long comp_point = (panel_timeline->trim_type == TRIM_IN) ? g.old_in : g.old_out; + long comp_point = (panel_timeline->trim_type == olive::timeline::TRIM_IN) ? g.old_in : g.old_out; ripple_point = qMin(ripple_point, comp_point); } // if this was out trimming, flip the direction of the ripple - if (panel_timeline->trim_type == TRIM_OUT) ripple_length = -ripple_length; + if (panel_timeline->trim_type == olive::timeline::TRIM_OUT) ripple_length = -ripple_length; // finally, ripple everything ripple_clips(ca, olive::ActiveSequence.get(), ripple_point, ripple_length, ignore_clips); @@ -1281,7 +1279,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { if (g.transition->secondary_clip != nullptr) { // if this is a shared transition - if (g.in != g.old_in && g.trim_type == TRIM_NONE) { + if (g.in != g.old_in && g.trim_type == olive::timeline::TRIM_NONE) { long movement = g.in - g.old_in; // check if the transition is going to extend the out point (opening clip) @@ -1508,7 +1506,6 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { if (split) { push_undo = true; } - panel_timeline->split_cache.clear(); } // remove duplicate selections @@ -1641,7 +1638,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // snap ghost's in point if ((panel_timeline->tool != TIMELINE_TOOL_TRANSITION && panel_timeline->trim_target == -1) - || g.trim_type == TRIM_IN + || g.trim_type == olive::timeline::TRIM_IN || panel_timeline->transition_tool_open_clip > -1) { fm = g.old_in + frame_diff; if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { @@ -1652,7 +1649,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // snap ghost's out point if ((panel_timeline->tool != TIMELINE_TOOL_TRANSITION && panel_timeline->trim_target == -1) - || g.trim_type == TRIM_OUT + || g.trim_type == olive::timeline::TRIM_OUT || panel_timeline->transition_tool_close_clip > -1) { fm = g.old_out + frame_diff; if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { @@ -1706,8 +1703,8 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { validator += g.ghost_length; if (validator > g.media_length) frame_diff += validator - g.media_length; } - } else if (g.trim_type != TRIM_NONE) { - if (g.trim_type == TRIM_IN) { + } else if (g.trim_type != olive::timeline::TRIM_NONE) { + if (g.trim_type == olive::timeline::TRIM_IN) { // prevent clip/transition length from being less than 1 frame long validator = g.ghost_length - frame_diff; if (validator < 1) frame_diff -= (1 - validator); @@ -1742,7 +1739,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Clip* otc = g.transition->parent_clip; Clip* ctc = g.transition->secondary_clip; - if (g.trim_type == TRIM_IN) { + if (g.trim_type == olive::timeline::TRIM_IN) { frame_diff -= g.transition->get_true_length(); } else { frame_diff += g.transition->get_true_length(); @@ -1756,7 +1753,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { validate_transitions(ctc, kTransitionClosing, frame_diff); frame_diff = -frame_diff; - if (g.trim_type == TRIM_IN) { + if (g.trim_type == olive::timeline::TRIM_IN) { frame_diff += g.transition->get_true_length(); } else { frame_diff -= g.transition->get_true_length(); @@ -1769,7 +1766,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { ClipPtr post = post_clips.at(j); // prevent any rippled clip from going below 0 - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { validator = post->timeline_in() - frame_diff; if (validator < 0) frame_diff += validator; } @@ -1778,7 +1775,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { for (int k=0;ktrack() == post->track()) { - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { validator = post->timeline_in() - frame_diff - pre->timeline_out(); if (validator < 0) frame_diff += validator; } else { @@ -1878,7 +1875,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (effective_tool == TIMELINE_TOOL_SLIP) { g.clip_in = g.old_clip_in - frame_diff; - } else if (g.trim_type != TRIM_NONE) { + } else if (g.trim_type != olive::timeline::TRIM_NONE) { long ghost_diff = frame_diff; // prevent trimming clips from overlapping each other @@ -1886,7 +1883,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { const Ghost& comp = panel_timeline->ghosts.at(j); if (i != j && g.track == comp.track) { long validator; - if (g.trim_type == TRIM_IN && comp.out < g.out) { + if (g.trim_type == olive::timeline::TRIM_IN && comp.out < g.out) { validator = (g.old_in + ghost_diff) - comp.out; if (validator < 0) ghost_diff -= validator; } else if (comp.in > g.in) { @@ -1898,10 +1895,10 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // apply changes if (g.transition != nullptr && g.transition->secondary_clip != nullptr) { - if (g.trim_type == TRIM_IN) ghost_diff = -ghost_diff; + if (g.trim_type == olive::timeline::TRIM_IN) ghost_diff = -ghost_diff; g.in = g.old_in - ghost_diff; g.out = g.old_out + ghost_diff; - } else if (g.trim_type == TRIM_IN) { + } else if (g.trim_type == olive::timeline::TRIM_IN) { g.in = g.old_in + ghost_diff; g.clip_in = g.old_clip_in + ghost_diff; } else { @@ -1950,7 +1947,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { for (int i=0;iselections.size();i++) { Selection& s = olive::ActiveSequence->selections[i]; if (panel_timeline->trim_target > -1) { - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { s.in = s.old_in + frame_diff; } else { s.out = s.old_out + frame_diff; @@ -1994,7 +1991,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (g != nullptr) { tip += " " + tr("Duration:") + " "; long len = (g->old_out-g->old_in); - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { len -= frame_diff; } else { len += frame_diff; @@ -2256,7 +2253,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { for (int i=0;ighosts[i]; - g.trim_type = TRIM_NONE; // the selected clips will be moving, not trimming + g.trim_type = olive::timeline::TRIM_NONE; // the selected clips will be moving, not trimming ClipPtr ghost_clip = olive::ActiveSequence->clips.at(g.clip); @@ -2279,7 +2276,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { Ghost gh; gh.transition = nullptr; gh.clip = j; - gh.trim_type = is_in ? TRIM_IN : TRIM_OUT; + gh.trim_type = is_in ? olive::timeline::TRIM_IN : olive::timeline::TRIM_OUT; panel_timeline->ghosts.append(gh); } } @@ -2304,7 +2301,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { for (int i=0;ighosts.size();i++) { ClipPtr c = olive::ActiveSequence->clips.at(panel_timeline->ghosts.at(i).clip); - if (panel_timeline->trim_type == TRIM_IN) { + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { axis = qMin(axis, c->timeline_in()); } else { axis = qMin(axis, c->timeline_out()); @@ -2535,7 +2532,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { panel_timeline->transition_select = kTransitionNone; // we also default to no trimming which may be changed later in this function - panel_timeline->trim_type = TRIM_NONE; + panel_timeline->trim_type = olive::timeline::TRIM_NONE; // set currently trimming clip to -1 (aka null) panel_timeline->trim_target = -1; @@ -2588,7 +2585,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { // if so, this is the point we'll make active for now (unless we find a closer one later) panel_timeline->trim_target = i; - panel_timeline->trim_type = TRIM_IN; + panel_timeline->trim_type = olive::timeline::TRIM_IN; closeness = nc; found = true; @@ -2606,7 +2603,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { // if so, this is the point we'll make active for now (unless we find a closer one later) panel_timeline->trim_target = i; - panel_timeline->trim_type = TRIM_OUT; + panel_timeline->trim_type = olive::timeline::TRIM_OUT; closeness = nc; found = true; @@ -2630,7 +2627,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { int nc = qAbs(transition_point - 1 - panel_timeline->cursor_frame); if (nc < closeness) { panel_timeline->trim_target = i; - panel_timeline->trim_type = TRIM_OUT; + panel_timeline->trim_type = olive::timeline::TRIM_OUT; panel_timeline->transition_select = kTransitionOpening; closeness = nc; found = true; @@ -2651,7 +2648,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { int nc = qAbs(transition_point + 1 - panel_timeline->cursor_frame); if (nc < closeness) { panel_timeline->trim_target = i; - panel_timeline->trim_type = TRIM_IN; + panel_timeline->trim_type = olive::timeline::TRIM_IN; panel_timeline->transition_select = kTransitionClosing; closeness = nc; found = true; @@ -2666,7 +2663,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { // if the cursor is indeed on a clip edge, we set the cursor accordingly if (found) { - if (panel_timeline->trim_type == TRIM_IN) { // if we're trimming an IN point + if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { // if we're trimming an IN point setCursor(panel_timeline->tool == TIMELINE_TOOL_RIPPLE ? olive::cursor::LeftRipple : olive::cursor::LeftTrim); } else { // if we're trimming an OUT point setCursor(panel_timeline->tool == TIMELINE_TOOL_RIPPLE ? olive::cursor::RightRipple : olive::cursor::RightTrim); @@ -2742,7 +2739,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { g.track = c->track(); g.clip = primary; g.media_stream = primary_type; - g.trim_type = TRIM_NONE; + g.trim_type = olive::timeline::TRIM_NONE; panel_timeline->ghosts.append(g); diff --git a/undo/undo.cpp b/undo/undo.cpp index dcefa41ca..a8baada80 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -42,20 +42,20 @@ #include "project/previewgenerator.h" #include "ui/mainwindow.h" -MoveClipAction::MoveClipAction(Clip *c, long iin, long iout, long iclip_in, int itrack, bool irelative) { - clip = c; - - old_in = c->timeline_in(); - old_out = c->timeline_out(); - old_clip_in = c->clip_in(); - old_track = c->track(); - - new_in = iin; - new_out = iout; - new_clip_in = iclip_in; - new_track = itrack; - - relative = irelative; +MoveClipAction::MoveClipAction(Clip *c, long iin, long iout, long iclip_in, int itrack, bool irelative) : + clip(c), + old_in(c->timeline_in()), + old_out(c->timeline_out()), + old_clip_in(c->clip_in()), + old_track(c->track()), + new_in(iin), + new_out(iout), + new_clip_in(iclip_in), + new_track(itrack), + relative(irelative), + done(false) +{ + doRedo(); } void MoveClipAction::doUndo() { @@ -70,19 +70,23 @@ void MoveClipAction::doUndo() { clip->set_clip_in(old_clip_in); clip->set_track(old_track); } + done = false; } void MoveClipAction::doRedo() { - if (relative) { - clip->set_timeline_in(clip->timeline_in() + new_in); - clip->set_timeline_out(clip->timeline_out() + new_out); - clip->set_clip_in(clip->clip_in() + new_clip_in); - clip->set_track(clip->track() + new_track); - } else { - clip->set_timeline_in(new_in); - clip->set_timeline_out(new_out); - clip->set_clip_in(new_clip_in); - clip->set_track(new_track); + if (!done) { + if (relative) { + clip->set_timeline_in(clip->timeline_in() + new_in); + clip->set_timeline_out(clip->timeline_out() + new_out); + clip->set_clip_in(clip->clip_in() + new_clip_in); + clip->set_track(clip->track() + new_track); + } else { + clip->set_timeline_in(new_in); + clip->set_timeline_out(new_out); + clip->set_clip_in(new_clip_in); + clip->set_track(new_track); + } + done = true; } } @@ -348,14 +352,15 @@ void DeleteMediaCommand::doRedo() { olive::project_model.removeChild(parent, item.get()); } -AddClipCommand::AddClipCommand(Sequence *s, QVector& add) { - link_offset_ = 0; - seq = s; - clips = add; +AddClipCommand::AddClipCommand(Sequence *s, QVector& add) : + link_offset_(0), + seq(s), + clips(add), + done_(false) +{ + doRedo(); } -AddClipCommand::~AddClipCommand() {} - void AddClipCommand::doUndo() { // clear effects panel panel_graph_editor->set_row(nullptr); @@ -383,23 +388,27 @@ void AddClipCommand::doUndo() { seq->clips.removeLast(); } + done_ = false; } void AddClipCommand::doRedo() { - link_offset_ = seq->clips.size(); - for (int i=0;iclips.size(); + for (int i=0;ilinked.size();j++) { + original->linked[j] += link_offset_; + } - // offset all links by the current clip size - for (int j=0;jlinked.size();j++) { - original->linked[j] += link_offset_; } + seq->clips.append(original); } - - seq->clips.append(original); + done_ = true; } } diff --git a/undo/undo.h b/undo/undo.h index 3e05dc1b7..73b5c43e1 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -94,6 +94,8 @@ private: int new_track; bool relative; + + bool done; }; class RippleAction : public OliveAction { @@ -229,13 +231,13 @@ private: class AddClipCommand : public OliveAction { public: AddClipCommand(Sequence* s, QVector& add); - virtual ~AddClipCommand() override; virtual void doUndo() override; virtual void doRedo() override; private: Sequence* seq; QVector clips; int link_offset_; + bool done_; }; class LinkCommand : public OliveAction { From 114606c28e517d3ae287219f18586c639a87cd9d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 30 Mar 2019 03:09:43 +1100 Subject: [PATCH 059/133] rewrite work continued --- olive.pro | 8 +- panels/effectcontrols.cpp | 4 +- panels/project.cpp | 189 ++++++-------------------------------- panels/project.h | 2 - panels/timeline.cpp | 8 +- panels/timeline.h | 25 ----- panels/viewer.cpp | 45 +++++---- panels/viewer.h | 4 +- project/media.cpp | 2 +- project/projectmodel.cpp | 40 +++++++- project/projectmodel.h | 7 ++ rendering/cacher.cpp | 2 +- timeline/clip.cpp | 136 ++++++++++++++++++++++----- timeline/clip.h | 21 +++-- timeline/marker.cpp | 10 +- timeline/marker.h | 6 +- timeline/selection.h | 2 - timeline/sequence.cpp | 180 ++++++++++++++++-------------------- timeline/sequence.h | 21 +++-- timeline/track.cpp | 174 +++++++++++++++++++++++++++++++++++ timeline/track.h | 79 ++++++++++++++++ timeline/tracklist.cpp | 50 ++++++++++ timeline/tracklist.h | 28 ++++++ ui/timelinewidget.cpp | 34 +++---- ui/timelinewidget.h | 12 +-- 25 files changed, 692 insertions(+), 397 deletions(-) create mode 100644 timeline/track.cpp create mode 100644 timeline/track.h create mode 100644 timeline/tracklist.cpp create mode 100644 timeline/tracklist.h diff --git a/olive.pro b/olive.pro index a47ec0107..cdff79126 100644 --- a/olive.pro +++ b/olive.pro @@ -174,7 +174,9 @@ SOURCES += \ rendering/shadergenerators.cpp \ global/timing.cpp \ rendering/pixelformats.cpp \ - timeline/timelinefunctions.cpp + timeline/timelinefunctions.cpp \ + timeline/track.cpp \ + timeline/tracklist.cpp HEADERS += \ ui/mainwindow.h \ @@ -308,7 +310,9 @@ HEADERS += \ global/timing.h \ rendering/pixelformats.h \ timeline/ghost.h \ - timeline/timelinefunctions.h + timeline/timelinefunctions.h \ + timeline/track.h \ + timeline/tracklist.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 454ffd884..49a45db7b 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -646,11 +646,11 @@ void EffectControls::Load() { } } if (c->opening_transition != nullptr - && (whole_clip_is_selected || c->sequence->IsTransitionSelected(c->opening_transition.get()))) { + && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->opening_transition.get()))) { effects_to_open.append(c->opening_transition.get()); } if (c->closing_transition != nullptr - && (whole_clip_is_selected || c->sequence->IsTransitionSelected(c->closing_transition.get()))) { + && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->closing_transition.get()))) { effects_to_open.append(c->closing_transition.get()); } diff --git a/panels/project.cpp b/panels/project.cpp index 697265aa9..c75a1ab35 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -492,21 +492,6 @@ MediaPtr Project::item_to_media_ptr(const QModelIndex &index) { return raw_ptr->parentItem()->get_shared_ptr(raw_ptr); } -void Project::get_all_media_from_table(QList& items, QList& list, int search_type) { - for (int i=0;iget_type() == MEDIA_TYPE_FOLDER) { - QList children; - for (int j=0;jchildCount();j++) { - children.append(item->child(j)); - } - get_all_media_from_table(children, list, search_type); - } else if (search_type == item->get_type() || search_type == -1) { - list.append(item); - } - } -} - bool Project::IsToolbarVisible() { return toolbar_widget->isVisible(); @@ -550,25 +535,22 @@ void Project::delete_selected_media() { // check if media is in use QVector parents; - QList sequence_items; - QList all_top_level_items; - for (int i=0;i 0) { + QVector all_sequences = olive::project_model.GetAllSequences(); + if (all_sequences.size() > 0) { - QList media_items; - get_all_media_from_table(items, media_items, MEDIA_TYPE_FOOTAGE); + QVector all_footage = olive::project_model.GetAllFootage(); - for (int i=0;ito_footage(); bool confirm_delete = false; - for (int j=0;jto_sequence().get(); - for (int k=0;kclips.size();k++) { - ClipPtr c = s->clips.at(k); + for (int j=0;jto_sequence().get(); + QVector sequence_clips = s->GetAllClips(); + + for (int k=0;kmedia() == item) { if (!confirm_delete) { // we found a reference, so we know we'll need to ask if the user wants to delete it @@ -608,13 +590,13 @@ void Project::delete_selected_media() { parent = parent->parentItem(); } - j = sequence_items.size(); - k = s->clips.size(); + j = all_sequences.size(); + k = sequence_clips.size(); } else if (confirm.clickedButton() == abort_button) { // break out of loop - i = media_items.size(); - j = sequence_items.size(); - k = s->clips.size(); + i = all_footage.size(); + j = all_sequences.size(); + k = sequence_clips.size(); remove = false; } @@ -636,7 +618,9 @@ void Project::delete_selected_media() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - if (olive::ActiveSequence != nullptr) olive::ActiveSequence->selections.clear(); + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->ClearSelections(); + } // remove media and parents for (int m=0;mset_media(nullptr); } } else if (items.at(i)->get_type() == MEDIA_TYPE_FOOTAGE) { - if (panel_footage_viewer->seq != nullptr) { - for (int j=0;jseq->clips.size();j++) { - ClipPtr c = panel_footage_viewer->seq->clips.at(j); - if (c != nullptr && c->media() == items.at(i)) { - panel_footage_viewer->set_media(nullptr); - break; - } - } + if (panel_footage_viewer->media == items.at(i)) { + panel_footage_viewer->set_media(nullptr); } } } @@ -1023,8 +1001,9 @@ void Project::delete_clips_using_selected_media() { ComboAction* ca = new ComboAction(); bool deleted = false; QModelIndexList items = get_current_selected(); - for (int i=0;iclips.size();i++) { - const ClipPtr& c = olive::ActiveSequence->clips.at(i); + QVector sequence_clips = olive::ActiveSequence->GetAllClips(); + for (int i=0;iupdate(); } -void save_marker(QXmlStreamWriter& stream, const Marker& m) { - stream.writeStartElement("marker"); - stream.writeAttribute("frame", QString::number(m.frame)); - stream.writeAttribute("name", m.name); - stream.writeEndElement(); -} - void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex& parent) { for (int i=0;imarkers.size();j++) { - save_marker(stream, f->markers.at(j)); + f->markers.at(j).Save(stream); } stream.writeEndElement(); // footage @@ -1156,114 +1128,7 @@ void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, s->save_id = sequence_id; sequence_id++; } else { - stream.writeStartElement("sequence"); - stream.writeAttribute("id", QString::number(s->save_id)); - stream.writeAttribute("folder", QString::number(folder)); - stream.writeAttribute("name", s->name); - stream.writeAttribute("width", QString::number(s->width)); - stream.writeAttribute("height", QString::number(s->height)); - stream.writeAttribute("framerate", QString::number(s->frame_rate, 'f', 10)); - stream.writeAttribute("afreq", QString::number(s->audio_frequency)); - stream.writeAttribute("alayout", QString::number(s->audio_layout)); - if (s == olive::ActiveSequence.get()) { - stream.writeAttribute("open", "1"); - } - stream.writeAttribute("workarea", QString::number(s->using_workarea)); - stream.writeAttribute("workareaIn", QString::number(s->workarea_in)); - stream.writeAttribute("workareaOut", QString::number(s->workarea_out)); - - QVector transition_save_cache; - QVector transition_clip_save_cache; - - for (int j=0;jclips.size();j++) { - const ClipPtr& c = s->clips.at(j); - if (c != nullptr) { - stream.writeStartElement("clip"); // clip - stream.writeAttribute("id", QString::number(j)); - stream.writeAttribute("enabled", QString::number(c->enabled())); - stream.writeAttribute("name", c->name()); - stream.writeAttribute("clipin", QString::number(c->clip_in())); - stream.writeAttribute("in", QString::number(c->timeline_in())); - stream.writeAttribute("out", QString::number(c->timeline_out())); - stream.writeAttribute("track", QString::number(c->track())); - - stream.writeAttribute("r", QString::number(c->color().red())); - stream.writeAttribute("g", QString::number(c->color().green())); - stream.writeAttribute("b", QString::number(c->color().blue())); - - stream.writeAttribute("autoscale", QString::number(c->autoscaled())); - stream.writeAttribute("speed", QString::number(c->speed().value, 'f', 10)); - stream.writeAttribute("maintainpitch", QString::number(c->speed().maintain_audio_pitch)); - stream.writeAttribute("reverse", QString::number(c->reversed())); - - if (c->media() != nullptr) { - stream.writeAttribute("type", QString::number(c->media()->get_type())); - switch (c->media()->get_type()) { - case MEDIA_TYPE_FOOTAGE: - stream.writeAttribute("media", QString::number(c->media()->to_footage()->save_id)); - stream.writeAttribute("stream", QString::number(c->media_stream_index())); - break; - case MEDIA_TYPE_SEQUENCE: - stream.writeAttribute("sequence", QString::number(c->media()->to_sequence()->save_id)); - break; - } - } - - // save markers - // only necessary for null media clips, since media has its own markers - if (c->media() == nullptr) { - for (int k=0;kget_markers().size();k++) { - save_marker(stream, c->get_markers().at(k)); - } - } - - // save clip links - stream.writeStartElement("linked"); // linked - for (int k=0;klinked.size();k++) { - stream.writeStartElement("link"); // link - stream.writeAttribute("id", QString::number(c->linked.at(k))); - stream.writeEndElement(); // link - } - stream.writeEndElement(); // linked - - // save opening and closing transitions - for (int t=kTransitionOpening;t<=kTransitionClosing;t++) { - TransitionPtr transition = (t == kTransitionOpening) ? c->opening_transition : c->closing_transition; - - if (transition != nullptr) { - stream.writeStartElement((t == kTransitionOpening) ? "opening" : "closing"); - - // check if this is a shared transition and the transition has already been saved - int transition_cache_index = transition_save_cache.indexOf(transition); - - if (transition_cache_index > -1) { - // if so, just save a reference to the other clip - stream.writeAttribute("shared", - QString::number(transition_clip_save_cache.at(transition_cache_index))); - } else { - // otherwise save the whole transition - transition->save(stream); - transition_save_cache.append(transition); - transition_clip_save_cache.append(j); - } - - stream.writeEndElement(); // opening - } - } - - for (int k=0;keffects.size();k++) { - stream.writeStartElement("effect"); // effect - c->effects.at(k)->save(stream); - stream.writeEndElement(); // effect - } - - stream.writeEndElement(); // clip - } - } - for (int j=0;jmarkers.size();j++) { - save_marker(stream, s->markers.at(j)); - } - stream.writeEndElement(); + s->Save(stream); } } } diff --git a/panels/project.h b/panels/project.h index f8ea096f2..7d959a3e4 100644 --- a/panels/project.h +++ b/panels/project.h @@ -81,8 +81,6 @@ public: QModelIndexList get_current_selected(); - void get_all_media_from_table(QList &items, QList &list, int type = -1); - bool IsToolbarVisible(); bool IsProjectWidget(QObject *child); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index a6ff725bc..390b10553 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -56,10 +56,6 @@ #include "global/timing.h" #include "ui/menu.h" -int olive::timeline::kTrackDefaultHeight = 40; -int olive::timeline::kTrackMinHeight = 30; -int olive::timeline::kTrackHeightIncrement = 10; - Timeline::Timeline(QWidget *parent) : Panel(parent), cursor_frame(0), @@ -119,8 +115,6 @@ Timeline::Timeline(QWidget *parent) : Retranslate(); } -Timeline::~Timeline() {} - void Timeline::Retranslate() { toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)"); toolEditButton->setToolTip(tr("Edit Tool") + " (X)"); @@ -270,7 +264,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector break; case MEDIA_TYPE_SEQUENCE: s = medium->to_sequence().get(); - sequence_length = s->getEndFrame(); + sequence_length = s->GetEndFrame(); if (seq != nullptr) sequence_length = rescale_frame_number(sequence_length, s->frame_rate, seq->frame_rate); can_import = (s != seq && sequence_length != 0); if (s->using_workarea) { diff --git a/panels/timeline.h b/panels/timeline.h index 4dfb2cb1e..f1ce05260 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -38,28 +38,6 @@ #include "ui/panel.h" - -namespace olive { - namespace timeline { - const int kGhostThickness = 2; - const int kClipTextPadding = 3; - - /** - * @brief Set default track sizes - * - * Olive has a few default constants used for adjusting track heights in the Timeline. For HiDPI, it makes - * sense to multiply these by the current DPI scale. It uses a variable from QApplication to do this multiplication, - * which means the QApplication instance needs to be instantiated before these are calculated. Therefore, call this - * function ONCE after QApplication is created to multiply the track heights correctly. - */ - void MultiplyTrackSizesByDPI(); - - extern int kTrackDefaultHeight; - extern int kTrackMinHeight; - extern int kTrackHeightIncrement; - } -} - int getScreenPointFromFrame(double zoom, long frame); long getFrameFromScreenPoint(double zoom, int x); bool selection_contains_transition(const Selection& s, Clip *c, int type); @@ -72,7 +50,6 @@ class Timeline : public Panel Q_OBJECT public: explicit Timeline(QWidget *parent = nullptr); - virtual ~Timeline() override; bool focused(); void multiply_zoom(double m); @@ -255,8 +232,6 @@ private: long rc_ripple_min; long rc_ripple_max; - QVector track_heights; - QWidget* timeline_area; TimelineWidget* video_area; TimelineWidget* audio_area; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index e824ff347..fd60fd994 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -127,12 +127,15 @@ void Viewer::reset_all_audio() { // reset all clip audio if (seq != nullptr) { long last_frame = 0; - for (int i=0;iclips.size();i++) { - ClipPtr c = seq->clips.at(i); - if (c != nullptr) { - c->reset_audio(); - last_frame = qMax(last_frame, c->timeline_out()); - } + + QVector all_clips = seq->GetAllClips(); + for (int i=0;ireset_audio(); + last_frame = qMax(last_frame, c->timeline_out()); + } audio_ibuffer_frame = seq->playhead; @@ -149,7 +152,7 @@ void Viewer::seek(long p) { if (main_sequence) { seq->playhead = p; } else { - seq->playhead = qMin(seq->getEndFrame(), qMax(0L, p)); + seq->playhead = qMin(seq->GetEndFrame(), qMax(0L, p)); } bool update_fx = false; if (main_sequence) { @@ -171,7 +174,7 @@ void Viewer::go_to_start() { } void Viewer::go_to_end() { - if (seq != nullptr) seek(seq->getEndFrame()); + if (seq != nullptr) seek(seq->GetEndFrame()); } void Viewer::close_media() { @@ -206,7 +209,7 @@ void Viewer::go_to_out() { } } -void Viewer::cue_recording(long start, long end, int track) { +void Viewer::cue_recording(long start, long end, Track* track) { recording_start = start; recording_end = end; recording_track = track; @@ -257,7 +260,7 @@ void Viewer::play(bool in_to_out) { if (panel_sequence_viewer->playing) panel_sequence_viewer->pause(); if (panel_footage_viewer->playing) panel_footage_viewer->pause(); - long sequence_end_frame = seq->getEndFrame(); + long sequence_end_frame = seq->GetEndFrame(); if (sequence_end_frame == 0) { return; } @@ -353,12 +356,12 @@ void Viewer::update_playhead_timecode(long p) { } void Viewer::update_end_timecode() { - end_timecode->setText((seq == nullptr) ? frame_to_timecode(0, olive::CurrentConfig.timecode_view, 30) : frame_to_timecode(seq->getEndFrame(), olive::CurrentConfig.timecode_view, seq->frame_rate)); + end_timecode->setText((seq == nullptr) ? frame_to_timecode(0, olive::CurrentConfig.timecode_view, 30) : frame_to_timecode(seq->GetEndFrame(), olive::CurrentConfig.timecode_view, seq->frame_rate)); } void Viewer::update_header_zoom() { if (seq != nullptr) { - long sequenceEndFrame = seq->getEndFrame(); + long sequenceEndFrame = seq->GetEndFrame(); if (cached_end_frame != sequenceEndFrame) { minimum_zoom = (sequenceEndFrame > 0) ? ((double) headers->width() / (double) sequenceEndFrame) : 1; headers->update_zoom(qMax(headers->get_zoom(), minimum_zoom)); @@ -431,7 +434,7 @@ void Viewer::clear_in() { void Viewer::clear_out() { if (seq != nullptr && seq->using_workarea) { - olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, seq->workarea_in, seq->getEndFrame())); + olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, seq->workarea_in, seq->GetEndFrame())); update_parents(); } } @@ -497,7 +500,7 @@ void Viewer::set_zoom_value(double d) { } void Viewer::set_sb_max() { - headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); + headers->set_scrollbar_max(horizontal_bar, seq->GetEndFrame(), headers->width()); } void Viewer::set_playback_speed(int s) { @@ -517,7 +520,7 @@ long Viewer::get_seq_in() { long Viewer::get_seq_out() { return ((olive::CurrentConfig.loop || playing_in_to_out) && seq->using_workarea && previous_playhead < seq->workarea_out) ? seq->workarea_out - : seq->getEndFrame(); + : seq->GetEndFrame(); } void Viewer::setup_ui() { @@ -689,10 +692,11 @@ void Viewer::set_media(Media* m) { // FIXME: Move this magic number to Config c->set_timeline_out(150); } - c->set_track(-1); + Track* track = new_sequence->GetTrackList(Track::kTypeVideo)->First(); + c->set_track(track); c->set_clip_in(0); c->refresh(); - new_sequence->clips.append(c); + track->AddClip(c); } else { new_sequence->width = olive::CurrentConfig.default_sequence_width; new_sequence->height = olive::CurrentConfig.default_sequence_height; @@ -706,10 +710,11 @@ void Viewer::set_media(Media* m) { c->set_media(media, audio_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate)); - c->set_track(0); + Track* track = new_sequence->GetTrackList(Track::kTypeAudio)->First(); + c->set_track(track); c->set_clip_in(0); c->refresh(); - new_sequence->clips.append(c); + track->AddClip(c); if (footage->video_tracks.size() == 0) { viewer_widget_->waveform = true; @@ -756,7 +761,7 @@ void Viewer::timer_update() { pause(); } } else if (playback_speed > 0) { - long end_frame = seq->getEndFrame(); + long end_frame = seq->GetEndFrame(); if ((olive::CurrentConfig.auto_seek_to_beginning || previous_playhead < end_frame) && seq->playhead >= end_frame) { pause(); } diff --git a/panels/viewer.h b/panels/viewer.h index f6bb29f1e..e488a9696 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -72,12 +72,12 @@ public: QTimer playback_updater; - void cue_recording(long start, long end, int track); + void cue_recording(long start, long end, Track *track); void uncue_recording(); bool is_recording_cued(); long recording_start; long recording_end; - int recording_track; + Track* recording_track; void reset_all_audio(); void update_parents(bool reload_fx = false); diff --git a/project/media.cpp b/project/media.cpp index 7a908f7ef..53914a4c5 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -293,7 +293,7 @@ int Media::columnCount() const { QString Media::GetStringDuration() { if (get_type() == MEDIA_TYPE_SEQUENCE) { Sequence* s = to_sequence().get(); - return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate); + return frame_to_timecode(s->GetEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate); } if (get_type() == MEDIA_TYPE_FOOTAGE) { Footage* f = to_footage(); diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index 7903ad89d..57099ac35 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -50,7 +50,7 @@ void ProjectModel::destroy_root() { panel_footage_viewer->set_media(nullptr); } - root_item_ = std::make_shared(); + root_item_ = nullptr; } void ProjectModel::clear() { @@ -196,6 +196,44 @@ void ProjectModel::set_icon(Media* m, const QIcon &ico) { emit dataChanged(index, index); } +QVector ProjectModel::GetAllSequences() +{ + return GetAllMediaOfType(MEDIA_TYPE_SEQUENCE); +} + +QVector ProjectModel::GetAllFootage() +{ + return GetAllMediaOfType(MEDIA_TYPE_FOOTAGE); +} + +QVector ProjectModel::GetAllFolders() +{ + return GetAllMediaOfType(MEDIA_TYPE_FOLDER); +} + +QVector ProjectModel::GetAllMediaOfType(int search_type) +{ + QVector media_list; + RecurseTree(root_item_.get(), media_list, search_type); + return media_list; +} + +void ProjectModel::RecurseTree(Media* parent, QVector& list, int search_type) +{ + for (int i=0;ichildCount();i++) { + + Media* child = parent->child(i); + + if (child->get_type() == search_type) { + list.append(child); + } + + if (child->childCount() > 0) { + RecurseTree(child, list, search_type); + } + } +} + void ProjectModel::appendChild(Media* parent, MediaPtr child) { QModelIndex row_start; diff --git a/project/projectmodel.h b/project/projectmodel.h index 59c6f5b6d..282d17625 100644 --- a/project/projectmodel.h +++ b/project/projectmodel.h @@ -58,8 +58,15 @@ public: int childCount(Media* parent = nullptr); void set_icon(Media* m, const QIcon &ico); + QVector GetAllSequences(); + QVector GetAllFootage(); + QVector GetAllFolders(); + private: MediaPtr root_item_; + + QVector GetAllMediaOfType(int search_type); + void RecurseTree(Media* parent, QVector &list, int search_type); }; namespace olive { diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 5a58744d9..f85cf15a4 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -144,7 +144,7 @@ void Cacher::CacheAudioWorker() { } if (temp_reverse) { - long seq_end = olive::ActiveSequence->getEndFrame(); + long seq_end = olive::ActiveSequence->GetEndFrame(); timeline_in = seq_end - timeline_in; timeline_out = seq_end - timeline_out; target_frame = seq_end - target_frame; diff --git a/timeline/clip.cpp b/timeline/clip.cpp index b90b9be84..c7b33e13e 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -37,14 +37,13 @@ #include "global/debug.h" #include "global/timing.h" -Clip::Clip(Sequence* s) : - sequence(s), +Clip::Clip(Track *s) : + track_(s), cacher(this), enabled_(true), clip_in_(0), timeline_in_(0), timeline_out_(0), - track_(0), media_(nullptr), reverse_(false), autoscale_(olive::CurrentConfig.autoscale_by_default), @@ -57,7 +56,7 @@ Clip::Clip(Sequence* s) : { } -ClipPtr Clip::copy(Sequence* s) { +ClipPtr Clip::copy(Track* s) { ClipPtr copy = std::make_shared(s); copy->set_enabled(enabled()); @@ -76,7 +75,7 @@ ClipPtr Clip::copy(Sequence* s) { copy->effects.append(effects.at(i)->copy(copy.get())); } - copy->set_cached_frame_rate((this->sequence == nullptr) ? cached_frame_rate() : this->sequence->frame_rate); + copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->frame_rate); copy->refresh(); @@ -85,25 +84,36 @@ ClipPtr Clip::copy(Sequence* s) { bool Clip::IsActiveAt(long timecode) { - // these buffers allow clips to be opened and prepared well before they're displayed - // as well as closed a little after they're not needed anymore - int open_buffer = qCeil(this->sequence->frame_rate*2); - int close_buffer = qCeil(this->sequence->frame_rate); - - return enabled() - && timeline_in(true) < timecode + open_buffer - && timeline_out(true) > timecode - close_buffer + && timeline_in(true) < timecode + && timeline_out(true) > timecode && timecode - timeline_in(true) + clip_in(true) < media_length(); } bool Clip::IsSelected(bool containing) { - if (this->sequence == nullptr) { + if (this->track_ == nullptr) { return false; } - return this->sequence->IsClipSelected(this, containing); + return this->track_->IsClipSelected(this, containing); +} + +bool Clip::IsTransitionSelected(TransitionType type) +{ + switch (type) { + case kTransitionOpening: + return track_->IsTransitionSelected(opening_transition.get()); + case kTransitionClosing: + return track_->IsTransitionSelected(closing_transition.get()); + default: + return false; + } +} + +Track::Type Clip::type() +{ + return track()->type(); } const QColor &Clip::color() @@ -132,7 +142,7 @@ FootageStream *Clip::media_stream() { if (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) { - return media()->to_footage()->get_stream_from_file_index(track() < 0, media_stream_index()); + return media()->to_footage()->get_stream_from_file_index(type() == Track::kTypeVideo, media_stream_index()); } return nullptr; @@ -212,9 +222,9 @@ void Clip::refresh() { if (replaced && media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) { Footage* m = media()->to_footage(); - if (track() < 0 && m->video_tracks.size() > 0) { + if (type() == Track::kTypeVideo && m->video_tracks.size() > 0) { set_media(media(), m->video_tracks.at(0).file_index); - } else if (track() >= 0 && m->audio_tracks.size() > 0) { + } else if (type() == Track::kTypeAudio && m->audio_tracks.size() > 0) { set_media(media(), m->audio_tracks.at(0).file_index); } } @@ -247,8 +257,88 @@ Clip::~Clip() { if (IsOpen()) { Close(true); } +} + +void Clip::Save(QXmlStreamWriter &stream) +{ + stream.writeAttribute("enabled", QString::number(enabled())); + stream.writeAttribute("name", name()); + stream.writeAttribute("clipin", QString::number(clip_in())); + stream.writeAttribute("in", QString::number(timeline_in())); + stream.writeAttribute("out", QString::number(timeline_out())); + stream.writeAttribute("track", QString::number(track())); + + stream.writeAttribute("r", QString::number(color().red())); + stream.writeAttribute("g", QString::number(color().green())); + stream.writeAttribute("b", QString::number(color().blue())); + + stream.writeAttribute("autoscale", QString::number(autoscaled())); + stream.writeAttribute("speed", QString::number(speed().value, 'f', 10)); + stream.writeAttribute("maintainpitch", QString::number(speed().maintain_audio_pitch)); + stream.writeAttribute("reverse", QString::number(reversed())); + + if (c->media() != nullptr) { + stream.writeAttribute("type", QString::number(media()->get_type())); + switch (c->media()->get_type()) { + case MEDIA_TYPE_FOOTAGE: + stream.writeAttribute("media", QString::number(media()->to_footage()->save_id)); + stream.writeAttribute("stream", QString::number(media_stream_index())); + break; + case MEDIA_TYPE_SEQUENCE: + stream.writeAttribute("sequence", QString::number(media()->to_sequence()->save_id)); + break; + } + } + + // save markers + // only necessary for null media clips, since media has its own markers + if (media() == nullptr) { + for (int k=0;k -1) { + // if so, just save a reference to the other clip + stream.writeAttribute("shared", + QString::number(transition_clip_save_cache.at(transition_cache_index))); + } else { + // otherwise save the whole transition + transition->save(stream); + transition_save_cache.append(transition); + transition_clip_save_cache.append(j); + } + + stream.writeEndElement(); // opening + } + } + + for (int k=0;ksave(stream); + stream.writeEndElement(); // effect + } + - effects.clear(); } long Clip::clip_in(bool with_transition) { @@ -346,12 +436,12 @@ AVRational Clip::time_base() return cacher.media_time_base(); } -int Clip::track() +Track *Clip::track() { return track_; } -void Clip::set_track(int t) +void Clip::set_track(Track *t) { track_ = t; } @@ -362,7 +452,7 @@ long Clip::length() { } double Clip::media_frame_rate() { - Q_ASSERT(track_ < 0); + Q_ASSERT(type() == Track::kTypeVideo); if (media_ != nullptr) { double rate = media_->get_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; @@ -394,7 +484,7 @@ long Clip::media_length() { case MEDIA_TYPE_SEQUENCE: { Sequence* s = media_->to_sequence().get(); - return rescale_frame_number(s->getEndFrame(), s->frame_rate, fr); + return rescale_frame_number(s->GetEndFrame(), s->frame_rate, fr); } } } diff --git a/timeline/clip.h b/timeline/clip.h index 1dae4828d..eb3fccbf7 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -36,8 +36,8 @@ #include "project/media.h" #include "project/footage.h" #include "rendering/framebufferobject.h" - #include "marker.h" +#include "track.h" struct ClipSpeed { ClipSpeed(); @@ -47,16 +47,19 @@ struct ClipSpeed { using ClipPtr = std::shared_ptr; -class Sequence; - class Clip { public: - Clip(Sequence *s); + Clip(Track *s); ~Clip(); - ClipPtr copy(Sequence *s); + ClipPtr copy(Track *s); + + void Save(QXmlStreamWriter& stream); bool IsActiveAt(long timecode); bool IsSelected(bool containing = true); + bool IsTransitionSelected(TransitionType type); + + Track::Type type(); const QColor& color(); void set_color(int r, int g, int b); @@ -91,8 +94,8 @@ public: long timeline_out(bool with_transition = false); void set_timeline_out(long t); - int track(); - void set_track(int t); + Track* track(); + void set_track(Track* t); bool reversed(); void set_reversed(bool r); @@ -117,7 +120,7 @@ public: long length(); void refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points); - Sequence* sequence; + Track* parent_; // markers QVector& get_markers(); @@ -159,11 +162,11 @@ public: private: // timeline variables (should be copied in copy()) + Track* track_; bool enabled_; long clip_in_; long timeline_in_; long timeline_out_; - int track_; QString name_; Media* media_; int media_stream_; diff --git a/timeline/marker.cpp b/timeline/marker.cpp index c816cf10c..18fa068b9 100644 --- a/timeline/marker.cpp +++ b/timeline/marker.cpp @@ -32,7 +32,7 @@ #include #include -void draw_marker(QPainter &p, int x, int y, int bottom, bool selected) { +void Marker::Draw(QPainter &p, int x, int y, int bottom, bool selected) { const QPoint points[5] = { QPoint(x, bottom), QPoint(x + MARKER_SIZE, bottom - MARKER_SIZE), @@ -126,3 +126,11 @@ void set_marker_internal(Sequence *seq) { set_marker_internal(seq, clips); } + +void Marker::Save(QXmlStreamWriter &stream) const +{ + stream.writeStartElement("marker"); + stream.writeAttribute("frame", QString::number(frame)); + stream.writeAttribute("name", name); + stream.writeEndElement(); +} diff --git a/timeline/marker.h b/timeline/marker.h index f9226f00b..9e88bf598 100644 --- a/timeline/marker.h +++ b/timeline/marker.h @@ -25,6 +25,7 @@ #include #include +#include #include class Sequence; @@ -33,9 +34,10 @@ using SequencePtr = std::shared_ptr; struct Marker { long frame; QString name; -}; -void draw_marker(QPainter& p, int x, int y, int bottom, bool selected); + void Save(QXmlStreamWriter& stream) const; + static void Draw(QPainter& p, int x, int y, int bottom, bool selected); +}; void set_marker_internal(Sequence *seq, const QVector& clips); void set_marker_internal(Sequence* seq); diff --git a/timeline/selection.h b/timeline/selection.h index eaa67372b..3f66905f8 100644 --- a/timeline/selection.h +++ b/timeline/selection.h @@ -24,11 +24,9 @@ struct Selection { long in; long out; - int track; long old_in; long old_out; - int old_track; bool trim_in; }; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 8d3b06ecb..ae474e07d 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -32,6 +32,12 @@ Sequence::Sequence() : workarea_out(0), wrapper_sequence(false) { + // Set up tracks + track_lists.resize(Track::kTypeCount); + + for (int i=0;i(this, i); + } } SequencePtr Sequence::copy() { @@ -44,16 +50,8 @@ SequencePtr Sequence::copy() { s->audio_layout = audio_layout; // deep copy all of the sequence's clips - s->clips.resize(clips.size()); - for (int i=0;iclips[i] = nullptr; - } else { - ClipPtr copy = c->copy(s.get()); - copy->linked = c->linked; - s->clips[i] = copy; - } + for (int i=0;itrack_lists[i] = track_lists.at(i).copy(s.get()); } // copy all of the sequence's markers @@ -62,41 +60,91 @@ SequencePtr Sequence::copy() { return s; } -long Sequence::getEndFrame() { - long end = 0; - for (int j=0;jtimeline_out() > end) { - end = c->timeline_out(); - } +void Sequence::Save(QXmlStreamWriter &stream) +{ + stream.writeStartElement("sequence"); + stream.writeAttribute("id", QString::number(save_id)); + stream.writeAttribute("name", name); + stream.writeAttribute("width", QString::number(width)); + stream.writeAttribute("height", QString::number(height)); + stream.writeAttribute("framerate", QString::number(frame_rate, 'f', 10)); + stream.writeAttribute("afreq", QString::number(audio_frequency)); + stream.writeAttribute("alayout", QString::number(audio_layout)); + if (this == olive::ActiveSequence.get()) { + stream.writeAttribute("open", "1"); } - return end; + stream.writeAttribute("workarea", QString::number(using_workarea)); + stream.writeAttribute("workareaIn", QString::number(workarea_in)); + stream.writeAttribute("workareaOut", QString::number(workarea_out)); + + QVector transition_save_cache; + QVector transition_clip_save_cache; + + for (int j=0;jSave(stream); + } + + + for (int j=0;jGetEndFrame(), end_frame); + } + + return end_frame; +} + +QVector Sequence::GetAllClips() +{ + QVector all_clips; + + for (int i=0;iGetAllClips()); + } + + return all_clips; +} + +TrackList *Sequence::GetTrackList(Track::Type type) +{ + return track_lists.at(type).get(); } void Sequence::Close() { - for (int i=0;iClose(true); - } + QVector all_clips = GetAllClips(); + + for (int i=0;iClose(true); } } -void Sequence::RefreshClips(Media *m) { - for (int i=0;imedia() == m)) { + QVector all_clips = GetAllClips(); + + for (int i=0;imedia() == m) { c->Close(true); c->refresh(); } } + } QVector Sequence::SelectedClips(bool containing) { + QVector all_clips = GetAllClips(); + QVector selected_clips; for (int i=0;itrack() == s.track && ((clip->timeline_in() >= s.in && clip->timeline_out() <= s.out) - || (!containing && !(clip->timeline_in() < s.in && clip->timeline_out() < s.in) - && !(clip->timeline_in() > s.in && clip->timeline_out() > s.in)))) { - return true; - } + for (int i=0;iClearSelections(); } - return false; -} - -bool Sequence::IsTransitionSelected(Transition *t) -{ - if (t == nullptr) { - return false; - } - - Clip* c = t->parent_clip; - - int transition_track = t->parent_clip->track(); - long transition_in_point; - long transition_out_point; - - // Get positions of the transition on the timeline - - if (t == c->opening_transition.get()) { - transition_in_point = c->timeline_in(); - transition_out_point = c->timeline_in() + t->get_true_length(); - - if (t->secondary_clip != nullptr) { - transition_in_point -= t->get_true_length(); - } - } else { - transition_in_point = c->timeline_out() - t->get_true_length(); - transition_out_point = c->timeline_out(); - - if (t->secondary_clip != nullptr) { - transition_out_point += t->get_true_length(); - } - } - - // See if there's a selection matching this - for (int i=0;i= transition_out_point - && selections.at(i).track == transition_track) { - return true; - } - } - - return false; -} - -void Sequence::getTrackLimits(int* video_tracks, int* audio_tracks) { - int vt = 0; - int at = 0; - for (int j=0;jtrack() < 0 && c->track() < vt) { // video clip - vt = c->track(); - } else if (c->track() > at) { - at = c->track(); - } - } - } - if (video_tracks != nullptr) *video_tracks = vt; - if (audio_tracks != nullptr) *audio_tracks = at; } // static variable for the currently active sequence diff --git a/timeline/sequence.h b/timeline/sequence.h index 901d37428..0998858ef 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -27,21 +27,27 @@ #include "clip.h" #include "marker.h" #include "selection.h" +#include "tracklist.h" -class Sequence { +class Sequence : public QObject { + Q_OBJECT public: Sequence(); SequencePtr copy(); + void Save(QXmlStreamWriter& stream); + QString name; - void getTrackLimits(int* video_tracks, int* audio_tracks); - long getEndFrame(); int width; int height; double frame_rate; int audio_frequency; int audio_layout; + long GetEndFrame(); + QVector GetAllClips(); + TrackList* GetTrackList(Track::Type type); + /** * @brief Close all open clips in a Sequence * @@ -55,17 +61,17 @@ public: */ void Close(); - void RefreshClips(Media* m = nullptr); + void RefreshClipsUsingMedia(Media* m = nullptr); QVector SelectedClips(bool containing = true); QVector SelectedClipIndexes(); Effect* GetSelectedGizmo(); - bool IsClipSelected(int clip_index, bool containing = true); bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); - QVector selections; + void ClearSelections(); + long playhead; bool using_workarea; @@ -77,7 +83,8 @@ public: int save_id; QVector markers; - QVector clips; +private: + QVector track_lists; }; using SequencePtr = std::shared_ptr; diff --git a/timeline/track.cpp b/timeline/track.cpp new file mode 100644 index 000000000..2b6d4d52f --- /dev/null +++ b/timeline/track.cpp @@ -0,0 +1,174 @@ +#include "track.h" + +#include "timeline/clip.h" + +int olive::timeline::kTrackDefaultHeight = 40; +int olive::timeline::kTrackMinHeight = 30; +int olive::timeline::kTrackHeightIncrement = 10; + +Track::Track(TrackList* parent, Type type) : + parent_(parent), + type_(type) +{ +} + +Track *Track::copy(TrackList *parent) +{ + Track* t = new Track(parent, type_); + + t->ResizeClipArray(ClipCount()); + for (int i=0;icopy(t); + copy->linked = c->linked; + t->clips_[i] = copy; + } + + return t; +} + +void Track::Save(QXmlStreamWriter &stream) +{ + stream.writeStartElement("track"); + + for (int j=0;jSave(stream); + + stream.writeEndElement(); // clip + } + } + + stream.writeEndElement(); // track +} + +Track::Type Track::type() +{ + return type_; +} + +int Track::height() +{ + return height_; +} + +void Track::set_height(int h) +{ + height_ = h; +} + +void Track::AddClip(ClipPtr clip) +{ + clips_.append(clip); + if (clip->track() != nullptr) { + clip->track()->RemoveClip(clip.get()); + } + clip->set_track(this); +} + +void Track::RemoveClip(int i) +{ + clips_.removeAt(i); +} + +void Track::RemoveClip(Clip *c) +{ + for (int i=0;i Track::GetAllClips() +{ + return clips_; +} + +bool Track::IsClipSelected(int clip_index, bool containing) +{ + return IsClipSelected(clips_.at(clip_index).get(), containing); +} + +bool Track::IsClipSelected(Clip *clip, bool containing) +{ + for (int i=0;itimeline_in() >= s.in && clip->timeline_out() <= s.out) + || (!containing && !(clip->timeline_in() < s.in && clip->timeline_out() < s.in) + && !(clip->timeline_in() > s.in && clip->timeline_out() > s.in)))) { + return true; + } + } + return false; +} + +bool Track::IsTransitionSelected(Transition *t) +{ + if (t == nullptr) { + return false; + } + + Clip* c = t->parent_clip; + + long transition_in_point; + long transition_out_point; + + // Get positions of the transition on the timeline + + if (t == c->opening_transition.get()) { + transition_in_point = c->timeline_in(); + transition_out_point = c->timeline_in() + t->get_true_length(); + + if (t->secondary_clip != nullptr) { + transition_in_point -= t->get_true_length(); + } + } else { + transition_in_point = c->timeline_out() - t->get_true_length(); + transition_out_point = c->timeline_out(); + + if (t->secondary_clip != nullptr) { + transition_out_point += t->get_true_length(); + } + } + + // See if there's a selection matching this + for (int i=0;i= transition_out_point) { + return true; + } + } + + return false; +} + +void Track::ClearSelections() +{ + selections_.clear(); +} + +long Track::GetEndFrame() +{ + long end_frame = 0; + + for (int i=0;itimeline_out(true)); + } + } + + return end_frame; +} diff --git a/timeline/track.h b/timeline/track.h new file mode 100644 index 000000000..43d90735e --- /dev/null +++ b/timeline/track.h @@ -0,0 +1,79 @@ +#ifndef TRACK_H +#define TRACK_H + +#include +#include + +#include "effects/effect.h" + +namespace olive { + namespace timeline { + const int kGhostThickness = 2; + const int kClipTextPadding = 3; + + /** + * @brief Set default track sizes + * + * Olive has a few default constants used for adjusting track heights in the Timeline. For HiDPI, it makes + * sense to multiply these by the current DPI scale. It uses a variable from QApplication to do this multiplication, + * which means the QApplication instance needs to be instantiated before these are calculated. Therefore, call this + * function ONCE after QApplication is created to multiply the track heights correctly. + */ + void MultiplyTrackSizesByDPI(); + + extern int kTrackDefaultHeight; + extern int kTrackMinHeight; + extern int kTrackHeightIncrement; + } +} + +class TrackList; + +class Track : public QObject +{ + Q_OBJECT +public: + enum Type { + kTypeVideo, + kTypeAudio, + kTypeSubtitle, + kTypeCount + }; + + Track(TrackList* parent, Type type); + Track* copy(TrackList* parent); + + void Save(QXmlStreamWriter& stream); + + Type type(); + + int height(); + void set_height(int h); + + void AddClip(ClipPtr clip); + int ClipCount(); + ClipPtr GetClip(int i); + void RemoveClip(int i); + void RemoveClip(Clip* c); + QVector GetAllClips(); + QVector GetSelectedClips(); + + bool IsClipSelected(int clip_index, bool containing = true); + bool IsClipSelected(Clip* clip, bool containing = true); + bool IsTransitionSelected(Transition* t); + + void ClearSelections(); + + long GetEndFrame(); +private: + void ResizeClipArray(int new_size); + + TrackList* parent_; + Type type_; + int height_; + QVector clips_; + QVector effects_; + QVector selections_; +}; + +#endif // TRACK_H diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp new file mode 100644 index 000000000..1f4bcf469 --- /dev/null +++ b/timeline/tracklist.cpp @@ -0,0 +1,50 @@ +#include "tracklist.h" + +TrackList::TrackList(Sequence *parent, Track::Type type) : + QObject(parent), + type_(type) +{ + // Ensure we have at least one track + AddTrack(); +} + +TrackListPtr TrackList::copy(Sequence *parent) +{ + TrackListPtr t = std::make_shared(parent, type_); + + t->ResizeTrackArray(tracks_.size()); + for (int i=0;itracks_[i] = tracks_.at(i)->copy(t.get()); + } + + return t; +} + +void TrackList::AddTrack() +{ + TrackPtr track = std::make_shared(this, type_); + tracks_.append(track); +} + +void TrackList::RemoveTrack(int i) +{ + if (tracks_.size() == 1) { + return; + } + tracks_.removeAt(i); +} + +Track *TrackList::First() +{ + return tracks_.first().get(); +} + +QVector TrackList::tracks() +{ + return tracks_; +} + +Sequence *TrackList::GetParent() +{ + return static_cast(parent()); +} diff --git a/timeline/tracklist.h b/timeline/tracklist.h new file mode 100644 index 000000000..599a76bf3 --- /dev/null +++ b/timeline/tracklist.h @@ -0,0 +1,28 @@ +#ifndef TRACKLIST_H +#define TRACKLIST_H + +#include "track.h" + +class TrackList : public QObject +{ + Q_OBJECT +public: + TrackList(Sequence* parent, Track::Type type); + TrackList* copy(Sequence* parent); + + void AddTrack(); + void RemoveTrack(int i); + Track* First(); + QVector tracks(); + + Sequence* GetParent(); + + +private: + void ResizeTrackArray(int i); + + Track::Type type_; + QVector tracks_; +}; + +#endif // TRACKLIST_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index e6e90fa58..7e1873ca7 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -58,6 +58,7 @@ #include "global/debug.h" #include "effects/effect.h" #include "effects/internal/solideffect.h" +#include "timeline/track.h" #define MAX_TEXT_WIDTH 20 #define TRANSITION_BETWEEN_RANGE 40 @@ -123,7 +124,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) { bool audio_clips_are_selected = false; for (int i=0;itrack() < 0) { + if (selected_clips.at(i)->type() == Track::kTypeVideo) { video_clips_are_selected = true; } else { audio_clips_are_selected = true; @@ -200,30 +201,22 @@ void TimelineWidget::toggle_autoscale() { } void TimelineWidget::tooltip_timer_timeout() { - if (olive::ActiveSequence != nullptr) { - if (tooltip_clip < olive::ActiveSequence->clips.size()) { - ClipPtr c = olive::ActiveSequence->clips.at(tooltip_clip); - if (c != nullptr) { - QToolTip::showText(QCursor::pos(), - tr("%1\nStart: %2\nEnd: %3\nDuration: %4").arg( - c->name(), - frame_to_timecode(c->timeline_in(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), - frame_to_timecode(c->timeline_out(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), - frame_to_timecode(c->length(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate) - )); - } - } + if (tooltip_clip != nullptr) { + QToolTip::showText(QCursor::pos(), + tr("%1\nStart: %2\nEnd: %3\nDuration: %4").arg( + tooltip_clip->name(), + frame_to_timecode(tooltip_clip->timeline_in(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), + frame_to_timecode(tooltip_clip->timeline_out(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), + frame_to_timecode(tooltip_clip->length(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate) + )); } + tooltip_timer.stop(); } void TimelineWidget::open_sequence_properties() { - QList sequence_items; - QList all_top_level_items; - for (int i=0;iget_all_media_from_table(all_top_level_items, sequence_items, MEDIA_TYPE_SEQUENCE); // find all sequences in project + QVector sequence_items = olive::project_model.GetAllSequences(); + for (int i=0;ito_sequence() == olive::ActiveSequence) { NewSequenceDialog nsd(this, sequence_items.at(i)); @@ -231,6 +224,7 @@ void TimelineWidget::open_sequence_properties() { return; } } + QMessageBox::critical(this, tr("Error"), tr("Couldn't locate media wrapper for sequence.")); } diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h index 0a8f55527..bb61279fc 100644 --- a/ui/timelinewidget.h +++ b/ui/timelinewidget.h @@ -37,18 +37,16 @@ class Timeline; -struct TimelineTrackHeight { - int index; - int height; -}; - bool same_sign(int a, int b); void draw_waveform(ClipPtr clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); class TimelineWidget : public QWidget { Q_OBJECT public: - explicit TimelineWidget(QWidget *parent = 0); + explicit TimelineWidget(QWidget *parent); + + void SetTracks(QVector& tracks); + QScrollBar* scrollBar; bool bottom_align; @@ -92,7 +90,7 @@ private: SequencePtr self_created_sequence; QTimer tooltip_timer; - int tooltip_clip; + Clip* tooltip_clip; int scroll; From 5ddf2726ddabb1354fe9d1714f5440dbdd748d2b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 30 Mar 2019 13:26:00 +1100 Subject: [PATCH 060/133] separation of ui from backend in project --- effects/effect.h | 2 - global/config.cpp | 3 +- global/config.h | 5 + global/global.cpp | 129 ++++++- global/global.h | 79 +++++ olive.pro | 8 +- panels/effectcontrols.cpp | 45 +-- panels/effectcontrols.h | 8 +- panels/grapheditor.cpp | 13 +- panels/grapheditor.h | 3 +- panels/panels.cpp | 41 +-- panels/panels.h | 2 +- panels/project.cpp | 650 +---------------------------------- panels/project.h | 39 +-- panels/timeline.h | 2 +- panels/viewer.cpp | 51 ++- panels/viewer.h | 2 +- project/footage.cpp | 53 +++ project/footage.h | 5 + project/media.cpp | 17 + project/media.h | 2 + project/projectfunctions.cpp | 83 +++++ project/projectfunctions.h | 18 + project/projectmodel.cpp | 355 +++++++++++++++++++ project/projectmodel.h | 56 +++ project/savethread.cpp | 97 ++++++ project/savethread.h | 8 + project/sourcescommon.cpp | 27 +- project/sourcescommon.h | 4 +- ui/mainwindow.cpp | 39 +-- ui/panel.cpp | 8 +- ui/panel.h | 2 + 32 files changed, 1046 insertions(+), 810 deletions(-) create mode 100644 project/projectfunctions.cpp create mode 100644 project/projectfunctions.h create mode 100644 project/savethread.cpp create mode 100644 project/savethread.h diff --git a/effects/effect.h b/effects/effect.h index 6871009a7..47dec5629 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -81,8 +81,6 @@ double log_volume(double linear); enum EffectType { EFFECT_TYPE_INVALID, - EFFECT_TYPE_VIDEO, - EFFECT_TYPE_AUDIO, EFFECT_TYPE_EFFECT, EFFECT_TYPE_TRANSITION }; diff --git a/global/config.cpp b/global/config.cpp index 228d8d9de..799abe529 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -82,7 +82,8 @@ Config::Config() default_sequence_audio_channel_layout(3), playback_bit_depth(olive::PIX_FMT_RGBA16F), export_bit_depth(olive::PIX_FMT_RGBA32F), - dont_use_proxies_on_export(true) + dont_use_proxies_on_export(true), + maximum_recent_projects(10) {} void Config::load(QString path) { diff --git a/global/config.h b/global/config.h index 3d7109e1c..3b7bbf2f6 100644 --- a/global/config.h +++ b/global/config.h @@ -615,6 +615,11 @@ struct Config { */ bool dont_use_proxies_on_export; + /** + * @brief The maximum amount of recent projects stored in the Open Recent list + */ + int maximum_recent_projects; + /** * @brief Load config from file * diff --git a/global/global.cpp b/global/global.cpp index a32015b26..72a0fb173 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -38,9 +38,11 @@ #include "dialogs/aboutdialog.h" #include "dialogs/speeddialog.h" #include "dialogs/actionsearch.h" +#include "dialogs/newsequencedialog.h" #include "dialogs/loaddialog.h" #include "dialogs/autocutsilencedialog.h" #include "project/loadthread.h" +#include "project/savethread.h" #include "timeline/sequence.h" #include "ui/mediaiconservice.h" #include "ui/mainwindow.h" @@ -175,16 +177,66 @@ void OliveGlobal::SetNativeStyling(QWidget *w) w->setStyleSheet(""); w->setPalette(w->style()->standardPalette()); w->setStyle(QStyleFactory::create("windowsvista")); +#else + Q_UNUSED(w); #endif } +void OliveGlobal::add_recent_project(const QString &url) +{ + bool found = false; + for (int i=0;i olive::CurrentConfig.maximum_recent_projects) { + recent_projects.removeLast(); + } + } + save_recent_projects(); +} + +void OliveGlobal::load_recent_projects() +{ + QFile f(get_recent_project_list_file()); + if (f.exists() && f.open(QFile::ReadOnly | QFile::Text)) { + QTextStream text_stream(&f); + while (true) { + QString line = text_stream.readLine(); + if (line.isNull()) { + break; + } else { + recent_projects.append(line); + } + } + f.close(); + } +} + +int OliveGlobal::recent_project_count() +{ + return recent_projects.size(); +} + +const QString &OliveGlobal::recent_project(int index) +{ + return recent_projects.at(index); +} + void OliveGlobal::LoadProject(const QString &fn, bool autorecovery) { // QSortFilterProxyModels are not thread-safe, and as we'll be loading in another thread, leaving it connected // can cause glitches in its presentation. Therefore for the duration of the loading process, we disconnect it, // and reconnect it later once the loading is complete. - panel_project->DisconnectFilterToModel(); + for (int i=0;iDisconnectFilterToModel(); + } LoadDialog ld(olive::MainWindow); @@ -198,7 +250,9 @@ void OliveGlobal::LoadProject(const QString &fn, bool autorecovery) connect(lt, SIGNAL(report_progress(int)), &ld, SLOT(setValue(int))); lt->start(); - panel_project->ConnectFilterToModel(); + for (int i=0;iConnectFilterToModel(); + } } void OliveGlobal::ClearProject() @@ -210,11 +264,17 @@ void OliveGlobal::ClearProject() panel_effect_controls->Clear(true); // clear existing project - olive::Global->set_sequence(nullptr); + set_sequence(nullptr); panel_footage_viewer->set_media(nullptr); + // delete sequences first because it's important to close all the clips before deleting the media + QVector sequences = olive::project_model.GetAllSequences(); + for (int i=0;iset_sequence(nullptr); + } + // clear project contents (footage, sequences, etc.) - panel_project->clear(); + olive::project_model.clear(); // clear undo stack olive::UndoStack.clear(); @@ -226,7 +286,25 @@ void OliveGlobal::ClearProject() update_ui(false); // set to unmodified - olive::Global->set_modified(false); + set_modified(false); +} + +void OliveGlobal::save_recent_projects() +{ + // save to file + QFile f(get_recent_project_list_file()); + if (f.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) { + QTextStream out(&f); + for (int i=0;i 0) { + out << "\n"; + } + out << recent_projects.at(i); + } + f.close(); + } else { + qWarning() << "Could not save recent projects"; + } } void OliveGlobal::ImportProject(const QString &fn) @@ -257,7 +335,7 @@ void OliveGlobal::open_recent(int index) { tr("The project '%1' no longer exists. Would you like to remove it from the recent projects list?").arg(recent_url), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { recent_projects.removeAt(index); - panel_project->save_recent_projects(); + save_recent_projects(); } } else if (can_close_project()) { OpenProjectWorker(recent_url, false); @@ -271,7 +349,7 @@ bool OliveGlobal::save_project_as() { fn += ".ove"; } update_project_filename(fn); - panel_project->save_project(false); + olive::Save(false); return true; } return false; @@ -281,7 +359,7 @@ bool OliveGlobal::save_project() { if (olive::ActiveProjectFilename.isEmpty()) { return save_project_as(); } else { - panel_project->save_project(false); + olive::Save(false); return true; } } @@ -307,6 +385,33 @@ bool OliveGlobal::can_close_project() { return true; } +void OliveGlobal::new_sequence() +{ + NewSequenceDialog nsd(olive::MainWindow); + nsd.set_sequence_name(olive::project_model.GetNextSequenceName()); + nsd.exec(); +} + +void OliveGlobal::open_import_dialog() +{ + QFileDialog fd(olive::MainWindow, tr("Import media..."), "", tr("All Files") + " (*)"); + fd.setFileMode(QFileDialog::ExistingFiles); + + if (fd.exec()) { + QStringList files = fd.selectedFiles(); + + Media* parent = nullptr; + for (int i=0;ifocused()) { + parent = panel_project.at(i)->get_selected_folder(); + break; + } + } + + olive::project_model.process_file_list(files, false, nullptr, parent); + } +} + void OliveGlobal::open_export_dialog() { if (CheckForActiveSequence()) { ExportDialog e(olive::MainWindow); @@ -345,7 +450,7 @@ void OliveGlobal::finished_initialize() { void OliveGlobal::save_autorecovery_file() { if (changed_since_last_autorecovery) { - panel_project->save_project(true); + olive::Save(true); changed_since_last_autorecovery = false; @@ -372,6 +477,12 @@ void OliveGlobal::set_sequence(SequencePtr s) panel_timeline->setFocus(); } +void OliveGlobal::clear_recent_projects() +{ + recent_projects.clear(); + save_recent_projects(); +} + void OliveGlobal::OpenProjectWorker(QString fn, bool autorecovery) { ClearProject(); update_project_filename(fn); diff --git a/global/global.h b/global/global.h index 050a28bc8..8d66c6ee6 100644 --- a/global/global.h +++ b/global/global.h @@ -159,6 +159,51 @@ public: */ static void SetNativeStyling(QWidget* w); + /** + * @brief Adds a project URL to the recent projects list + * + * @param url + * + * The project URL to add + */ + void add_recent_project(const QString& url); + + /** + * @brief Load recent projects from file + * + * Should be called on application startup. + */ + void load_recent_projects(); + + /** + * @brief Total count of recent projects + * + * @return + * + * Number of recent projects in the list + */ + int recent_project_count(); + + /** + * @brief Get the recent project at a given index + * + * @param index + * + * @return + * + * The recent project at index + */ + const QString& recent_project(int index); + + /** + * @brief Retrieves the filename of the autorecovery file to save to during this session + * + * @return + * + * A URL pointing to the autorecovery file + */ + const QString& get_autorecovery_filename(); + public slots: /** * @brief Undo user's last action @@ -265,6 +310,16 @@ public slots: */ bool can_close_project(); + /** + * @brief Opens the NewSequenceDialog to create a new Sequence + */ + void new_sequence(); + + /** + * @brief Open a file dialog for importing files into the project + */ + void open_import_dialog(); + /** * @brief Open the Export dialog to trigger an export of the current sequence. */ @@ -336,6 +391,13 @@ public slots: */ void set_sequence(SequencePtr s); + /** + * @brief Clear the recent projects list + * + * Also saves the cleared recent projects to the config file making it permanent. + */ + void clear_recent_projects(); + private: /** * @brief Internal function to handle loading a project from file @@ -403,6 +465,13 @@ private: */ void ClearProject(); + /** + * @brief Saves current recent project list to the configuration file + * + * This should be called whenever the recent projects change so the changes can be persistent. + */ + void save_recent_projects(); + /** * @brief File filter used for any file dialogs relating to Olive project files. */ @@ -438,6 +507,16 @@ private: */ bool rendering_; + /** + * @brief Internal variable for the filename to the autorecovery project file + */ + QString autorecovery_filename; + + /** + * @brief Internal list of recent projects + */ + QStringList recent_projects; + private slots: }; diff --git a/olive.pro b/olive.pro index cdff79126..77966607a 100644 --- a/olive.pro +++ b/olive.pro @@ -176,7 +176,9 @@ SOURCES += \ rendering/pixelformats.cpp \ timeline/timelinefunctions.cpp \ timeline/track.cpp \ - timeline/tracklist.cpp + timeline/tracklist.cpp \ + project/savethread.cpp \ + project/projectfunctions.cpp HEADERS += \ ui/mainwindow.h \ @@ -312,7 +314,9 @@ HEADERS += \ timeline/ghost.h \ timeline/timelinefunctions.h \ timeline/track.h \ - timeline/tracklist.h + timeline/tracklist.h \ + project/savethread.h \ + project/projectfunctions.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 49a45db7b..08bc48629 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -86,10 +86,6 @@ EffectControls::~EffectControls() Clear(true); } -bool EffectControls::keyframe_focus() { - return headers->hasFocus() || keyframeView->hasFocus(); -} - void EffectControls::set_zoom(bool in) { zoom *= (in) ? 2 : 0.5; update_keyframes(); @@ -100,7 +96,7 @@ void EffectControls::menu_select(QAction* q) { ComboAction* ca = new ComboAction(); for (int i=0;itrack() < 0) == (effect_menu_subtype == EFFECT_TYPE_VIDEO)) { + if (c->type() == effect_menu_subtype) { const EffectMeta* meta = reinterpret_cast(q->data().value()); if (effect_menu_type == EFFECT_TYPE_TRANSITION) { if (c->opening_transition == nullptr) { @@ -194,7 +190,7 @@ void EffectControls::cut() { copy(true); } -void EffectControls::show_effect_menu(int type, int subtype) { +void EffectControls::show_effect_menu(int type, Track::Type subtype) { effect_menu_type = type; effect_menu_subtype = subtype; @@ -598,6 +594,23 @@ void EffectControls::DeleteSelectedEffects() { } } +bool EffectControls::focused() +{ + if (this->hasFocus() + || headers->hasFocus() + || keyframeView->hasFocus()) { + return true; + } + + for (int i=0;iIsFocused()) { + return true; + } + } + + return false; +} + void EffectControls::Reload() { Clear(false); Load(); @@ -708,37 +721,25 @@ void EffectControls::Load() { } void EffectControls::video_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, EFFECT_TYPE_VIDEO); + show_effect_menu(EFFECT_TYPE_EFFECT, Track::kTypeVideo); } void EffectControls::audio_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, EFFECT_TYPE_AUDIO); + show_effect_menu(EFFECT_TYPE_EFFECT, Track::kTypeAudio); } void EffectControls::video_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, EFFECT_TYPE_VIDEO); + show_effect_menu(EFFECT_TYPE_TRANSITION, Track::kTypeVideo); } void EffectControls::audio_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, EFFECT_TYPE_AUDIO); + show_effect_menu(EFFECT_TYPE_TRANSITION, Track::kTypeAudio); } void EffectControls::resizeEvent(QResizeEvent*) { update_scrollbar(); } -bool EffectControls::is_focused() { - if (this->hasFocus()) return true; - - for (int i=0;iIsFocused()) { - return true; - } - } - - return false; -} - EffectsArea::EffectsArea(QWidget* parent) : QWidget(parent) {} diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index 0b6c6ce4b..aefd75ffa 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -32,6 +32,7 @@ #include #include "project/projectelements.h" +#include "timeline/track.h" #include "ui/timelineheader.h" #include "ui/keyframeview.h" #include "ui/resizablescrollbar.h" @@ -67,9 +68,8 @@ public: bool IsEffectSelected(Effect* e); void DeleteSelectedEffects(); - bool is_focused(); + virtual bool focused() override; void set_zoom(bool in); - bool keyframe_focus(); void delete_selected_keyframes(); void scroll_to_frame(long frame); @@ -110,7 +110,7 @@ private: void DeleteEffect(ComboAction* ca, Effect* effect_ref); - void show_effect_menu(int type, int subtype); + void show_effect_menu(int type, Track::Type subtype); void load_keyframes(); void open_effect(QVBoxLayout* hlayout, Effect *e); void UpdateTitle(); @@ -118,7 +118,7 @@ private: void setup_ui(); int effect_menu_type; - int effect_menu_subtype; + Track::Type effect_menu_subtype; QString panel_name; QWidget* video_effect_area; diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index c598cc5d5..dd27a705f 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -161,6 +161,11 @@ void GraphEditor::update_panel() { } } +bool GraphEditor::focused() +{ + return hasFocus() || view->hasFocus() || header->hasFocus(); +} + void GraphEditor::set_row(EffectRow *r) { for (int i=0;ihasFocus() || header->hasFocus(); -} - -bool GraphEditor::view_is_under_mouse() { - return view->underMouse() || header->underMouse(); -} - void GraphEditor::delete_selected_keys() { view->delete_selected_keys(); } diff --git a/panels/grapheditor.h b/panels/grapheditor.h index 577a80b11..e0d7355dc 100644 --- a/panels/grapheditor.h +++ b/panels/grapheditor.h @@ -41,8 +41,7 @@ public: void set_row(EffectRow* r); void update_panel(); - bool view_is_focused(); - bool view_is_under_mouse(); + virtual bool focused() override; void delete_selected_keys(); void select_all(); diff --git a/panels/panels.cpp b/panels/panels.cpp index eef2e83a8..f8ec065cd 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -29,7 +29,7 @@ #include #include -Project* panel_project = nullptr; +QVector panel_project; EffectControls* panel_effect_controls = nullptr; Viewer* panel_sequence_viewer = nullptr; Viewer* panel_footage_viewer = nullptr; @@ -49,33 +49,19 @@ void update_ui(bool modified) { QDockWidget *get_focused_panel(bool force_hover) { QDockWidget* w = nullptr; if (olive::CurrentConfig.hover_focus || force_hover) { - if (panel_project->underMouse()) { - w = panel_project; - } else if (panel_effect_controls->underMouse()) { - w = panel_effect_controls; - } else if (panel_sequence_viewer->underMouse()) { - w = panel_sequence_viewer; - } else if (panel_footage_viewer->underMouse()) { - w = panel_footage_viewer; - } else if (panel_timeline->underMouse()) { - w = panel_timeline; - } else if (panel_graph_editor->view_is_under_mouse()) { - w = panel_graph_editor; + for (int i=0;iunderMouse()) { + w = olive::panels.at(i); + break; + } } } if (w == nullptr) { - if (panel_project->is_focused()) { - w = panel_project; - } else if (panel_effect_controls->keyframe_focus() || panel_effect_controls->is_focused()) { - w = panel_effect_controls; - } else if (panel_sequence_viewer->is_focused()) { - w = panel_sequence_viewer; - } else if (panel_footage_viewer->is_focused()) { - w = panel_footage_viewer; - } else if (panel_timeline->focused()) { - w = panel_timeline; - } else if (panel_graph_editor->view_is_focused()) { - w = panel_graph_editor; + for (int i=0;ifocused()) { + w = olive::panels.at(i); + break; + } } } return w; @@ -87,8 +73,9 @@ void alloc_panels(QWidget* parent) { panel_footage_viewer = new Viewer(parent); panel_footage_viewer->setObjectName("footage_viewer"); panel_footage_viewer->show_videoaudio_buttons(true); - panel_project = new Project(parent); - panel_project->setObjectName("proj_root"); + Project* first_project_panel = new Project(parent); + first_project_panel->setObjectName("proj_root"); + panel_project.append(first_project_panel); panel_effect_controls = new EffectControls(parent); panel_effect_controls->setObjectName("fx_controls"); panel_timeline = new Timeline(parent); diff --git a/panels/panels.h b/panels/panels.h index 52a32da15..a051f84c0 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -27,7 +27,7 @@ #include "grapheditor.h" #include "project.h" -extern Project* panel_project; +extern QVector panel_project; extern EffectControls* panel_effect_controls; extern Viewer* panel_sequence_viewer; extern Viewer* panel_footage_viewer; diff --git a/panels/project.cpp b/panels/project.cpp index c75a1ab35..1c423a0ef 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -51,8 +51,8 @@ extern "C" { #include "rendering/cacher.h" #include "dialogs/replaceclipmediadialog.h" #include "panels/effectcontrols.h" -#include "dialogs/newsequencedialog.h" #include "dialogs/mediapropertiesdialog.h" +#include "dialogs/newsequencedialog.h" #include "dialogs/loaddialog.h" #include "project/clipboard.h" #include "ui/sourcetable.h" @@ -62,14 +62,10 @@ extern "C" { #include "ui/mediaiconservice.h" #include "project/sourcescommon.h" #include "project/projectfilter.h" +#include "project/projectfunctions.h" #include "global/debug.h" #include "ui/menu.h" -#define MAXIMUM_RECENT_PROJECTS 10 // FIXME: should be configurable - -QString autorecovery_filename; -QStringList recent_projects; - Project::Project(QWidget *parent) : Panel(parent), sorter(this), @@ -225,97 +221,6 @@ void Project::Retranslate() { setWindowTitle(tr("Project")); } -QString Project::get_next_sequence_name(QString start) { - if (start.isEmpty()) start = tr("Sequence"); - - int n = 1; - bool found = true; - QString name; - while (found) { - found = false; - name = start + " "; - if (n < 10) { - name += "0"; - } - name += QString::number(n); - for (int i=0;iget_name(), name, Qt::CaseInsensitive) == 0) { - found = true; - n++; - break; - } - } - } - return name; -} - -SequencePtr create_sequence_from_media(QVector& media_list) { - SequencePtr s(new Sequence()); - - s->name = panel_project->get_next_sequence_name(); - - // Retrieve default Sequence settings from Config - s->width = olive::CurrentConfig.default_sequence_width; - s->height = olive::CurrentConfig.default_sequence_height; - s->frame_rate = olive::CurrentConfig.default_sequence_framerate; - s->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; - s->audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout; - - bool got_video_values = false; - bool got_audio_values = false; - for (int i=0;iget_type()) { - case MEDIA_TYPE_FOOTAGE: - { - Footage* m = media->to_footage(); - if (m->ready) { - if (!got_video_values) { - for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = m->video_tracks.at(j); - s->width = ms.video_width; - s->height = ms.video_height; - if (!qFuzzyCompare(ms.video_frame_rate, 0.0)) { - s->frame_rate = ms.video_frame_rate * m->speed; - - if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; - - // only break with a decent frame rate, otherwise there may be a better candidate - got_video_values = true; - break; - } - } - } - if (!got_audio_values && m->audio_tracks.size() > 0) { - const FootageStream& ms = m->audio_tracks.at(0); - s->audio_frequency = ms.audio_frequency; - got_audio_values = true; - } - } - } - break; - case MEDIA_TYPE_SEQUENCE: - { - // Clone all attributes of the original sequence (seq) into the new one (s) - Sequence* seq = media->to_sequence().get(); - - s->width = seq->width; - s->height = seq->height; - s->frame_rate = seq->frame_rate; - s->audio_frequency = seq->audio_frequency; - s->audio_layout = seq->audio_layout; - - got_video_values = true; - got_audio_values = true; - } - break; - } - if (got_video_values && got_audio_values) break; - } - - return s; -} - void Project::duplicate_selected() { QModelIndexList items = get_current_selected(); bool duped = false; @@ -323,7 +228,7 @@ void Project::duplicate_selected() { for (int j=0;jget_type() == MEDIA_TYPE_SEQUENCE) { - create_sequence_internal(ca, i->to_sequence()->copy(), false, item_to_media(items.at(j).parent())); + olive::project_model.CreateSequence(ca, i->to_sequence()->copy(), false, item_to_media(items.at(j).parent())); duped = true; } } @@ -339,25 +244,11 @@ void Project::replace_selected_file() { if (selected_items.size() == 1) { MediaPtr item = item_to_media_ptr(selected_items.at(0)); if (item->get_type() == MEDIA_TYPE_FOOTAGE) { - replace_media(item, nullptr); + sources_common.replace_media(item, nullptr); } } } -void Project::replace_media(MediaPtr item, QString filename) { - if (filename.isEmpty()) { - filename = QFileDialog::getOpenFileName( - this, - tr("Replace '%1'").arg(item->get_name()), - "", - tr("All Files") + " (*)"); - } - if (!filename.isEmpty()) { - ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename); - olive::UndoStack.push(rmc); - } -} - void Project::replace_clip_media() { if (olive::ActiveSequence == nullptr) { QMessageBox::critical(this, @@ -416,7 +307,7 @@ void Project::open_properties() { } void Project::new_folder() { - MediaPtr m = create_folder_internal(nullptr); + MediaPtr m = olive::project::CreateFolder(nullptr); olive::UndoStack.push(new AddMediaCommand(m, get_selected_folder())); QModelIndex index = olive::project_model.create_index(m->row(), 0, m.get()); @@ -430,54 +321,10 @@ void Project::new_folder() { } } -void Project::new_sequence() { - NewSequenceDialog nsd(this); - nsd.set_sequence_name(get_next_sequence_name()); - nsd.exec(); -} - -MediaPtr Project::create_sequence_internal(ComboAction *ca, SequencePtr s, bool open, Media* parent) { - - MediaPtr item = std::make_shared(); - item->set_sequence(s); - - if (ca != nullptr) { - - ca->append(new AddMediaCommand(item, parent)); - - if (open) { - ca->append(new ChangeSequenceAction(s)); - } - - } else { - - olive::project_model.appendChild(parent, item); - - if (open) { - olive::Global->set_sequence(s); - } - - } - - return item; - -} - -QString Project::get_file_name_from_path(const QString& path) { - return path.mid(path.lastIndexOf('/')+1); -} - -bool Project::is_focused() { +bool Project::focused() { return tree_view->hasFocus() || icon_view->hasFocus(); } -MediaPtr Project::create_folder_internal(QString name) { - MediaPtr item = std::make_shared(); - item->set_folder(); - item->set_name(name); - return item; -} - Media* Project::item_to_media(const QModelIndex &index) { return static_cast(sorter.mapToSource(index).internalPointer()); } @@ -665,258 +512,6 @@ void Project::delete_selected_media() { } } -void Project::process_file_list(QStringList& files, bool recursive, MediaPtr replace, Media* parent) { - bool imported = false; - - // retrieve the array of image formats from the user's configuration - QStringList image_sequence_formats = olive::CurrentConfig.img_seq_formats.split("|"); - - // a cache of image sequence formatted URLS to assist the user in importing image sequences - QVector image_sequence_urls; - QVector image_sequence_importassequence; - - if (!recursive) last_imported_media.clear(); - - bool create_undo_action = (!recursive && replace == nullptr); - ComboAction* ca = nullptr; - if (create_undo_action) ca = new ComboAction(); - - // Loop through received files - for (int i=0;iappend(new AddMediaCommand(folder, parent)); - } else { - olive::project_model.appendChild(parent, folder); - } - - process_file_list(subdir_filenames, true, nullptr, folder.get()); - - imported = true; - - } else if (!files.at(i).isEmpty()) { - QString file = files.at(i); - - // Check if the user is importing an Olive project file - if (file.endsWith(".ove", Qt::CaseInsensitive)) { - - // This file is an Olive project file. Ask the user if they really want to import it. - if (QMessageBox::question(this, - tr("Import a Project"), - tr("\"%1\" is an Olive project file. It will merge with this project. " - "Do you wish to continue?").arg(file), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - - // load the project without clearing the current one - olive::Global->ImportProject(file); - - } - - } else { - - // This file is NOT an Olive project file - - // Used later if this file is part of an already processed image sequence - bool skip = false; - - /* Heuristic to determine whether file is part of an image sequence */ - - // Firstly, we run a heuristic on whether this file is an image by checking its file extension - - bool file_is_an_image = false; - - // Get the string position of the extension in the filename - int lastcharindex = file.lastIndexOf("."); - - if (lastcharindex != -1 && lastcharindex > file.lastIndexOf('/')) { - - QString ext = file.mid(lastcharindex+1); - - // If the file extension is part of a predetermined list (from Config::img_seq_formats), we'll treat it - // as an image - if (image_sequence_formats.contains(ext, Qt::CaseInsensitive)) { - file_is_an_image = true; - } - - } else { - - // If we're here, the file has no extension, but we'll still check if its an image sequence just in case - lastcharindex = file.length(); - file_is_an_image = true; - - } - - // Some image sequence's don't start at "0", if it is indeed an image sequence, we'll use this variable - // later to determine where it does start - int start_number = 0; - - // Check if we passed the earlier heuristic to check whether this is a file, and whether the last number in - // the filename (before the extension) is a number - if (file_is_an_image && file[lastcharindex-1].isDigit()) { - - // Check how many digits are at the end of this filename - int digit_count = 0; - int digit_test = lastcharindex-1; - while (file[digit_test].isDigit()) { - digit_count++; - digit_test--; - } - - // Retrieve the integer represented at the end of this filename - digit_test++; - int file_number = file.mid(digit_test, digit_count).toInt(); - - // Check whether a file exists with the same format but one number higher or one number lower - if (QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number-1, digit_count, 10, QChar('0')) + file.mid(lastcharindex))) - || QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number+1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))) { - - // - // If so, it certainly looks like it *could* be an image sequence, but we'll ask the user just in case - // - - // Firstly we should check if this file is part of a sequence the user has already confirmed as either a - // sequence or not a sequence (e.g. if the user happened to select a bunch of images that happen to increase - // consecutively). We format the filename with FFmpeg's '%Nd' (N = digits) formatting for reading image - // sequences - QString new_filename = file.left(digit_test) + "%" + QString::number(digit_count) + "d" + file.mid(lastcharindex); - - int does_url_cache_already_contain_this = image_sequence_urls.indexOf(new_filename); - - if (does_url_cache_already_contain_this > -1) { - - // We've already processed an image with the same formatting - - // Check if the last time we saw this formatting, the user chose to import as a sequence - if (image_sequence_importassequence.at(does_url_cache_already_contain_this)) { - - // If so, no need to import this file too, so we signal to the rest of the function to skip this file - skip = true; - - } - - // If not, we can fall-through to the next step which is importing normally - - } else { - - // If we're here, we've never seen a file with this formatting before, so we'll ask whether to import - // as a sequence or not - - // Add this file formatting file to the URL cache - image_sequence_urls.append(new_filename); - - // This does look like an image sequence, let's ask the user if it'll indeed be an image sequence - if (QMessageBox::question(this, - tr("Image sequence detected"), - tr("The file '%1' appears to be part of an image sequence. " - "Would you like to import it as such?").arg(file), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::Yes) == QMessageBox::Yes) { - - // Proceed to the next step of this with the formatted filename - file = new_filename; - - // Cache the user's answer alongside the image_sequence_urls value - in this case, YES, this will be an - // image sequence - image_sequence_importassequence.append(true); - - - // FFmpeg needs to know what file number to start at in the sequence. In case the image sequence doesn't - // start at a zero, we'll loop decreasing the number until it doesn't exist anymore - QString test_filename_format = QString("%1%2%3").arg(file.left(digit_test), "%1", file.mid(lastcharindex)); - int test_file_number = file_number; - do { - test_file_number--; - } while (QFileInfo::exists(test_filename_format.arg(QString("%1").arg(test_file_number, digit_count, 10, QChar('0'))))); - - // set the image sequence's start number to the last that existed - start_number = test_file_number + 1; - - } else { - - // Cache the user's response to the image sequence question - i.e. none of the files imported with this - // formatting should be imported as an image sequence - image_sequence_importassequence.append(false); - - } - } - - } - - } - - // If we're not skipping this file, let's import it - if (!skip) { - MediaPtr item; - FootagePtr m; - - if (replace != nullptr) { - item = replace; - } else { - item = std::make_shared(); - } - - m = std::make_shared(); - - // Edge case for PNGs that standardized unassociated alpha - if (file.endsWith("png", Qt::CaseInsensitive)) { - m->alpha_is_associated = false; - } - - m->using_inout = false; - m->url = file; - m->name = get_file_name_from_path(files.at(i)); - m->start_number = start_number; - - item->set_footage(m); - - last_imported_media.append(item.get()); - - if (replace == nullptr) { - if (create_undo_action) { - ca->append(new AddMediaCommand(item, parent)); - } else { - olive::project_model.appendChild(parent, item); - } - } - - imported = true; - } - - } - - - } - } - if (create_undo_action) { - if (imported) { - olive::UndoStack.push(ca); - - for (int i=0;iset_row(nullptr); - - // clear effects cache - panel_effect_controls->Clear(true); - - // delete sequences first because it's important to close all the clips before deleting the media - QVector sequences = list_all_project_sequences(); - for (int i=0;iset_sequence(nullptr); - } - - // delete everything else - olive::project_model.clear(); - - // update tree view (sometimes this doesn't seem to update reliably) - tree_view->update(); -} - -void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex& parent) { - for (int i=0;iget_type()) { - if (m->get_type() == MEDIA_TYPE_FOLDER) { - if (set_ids_only) { - m->temp_id = folder_id; // saves a temporary ID for matching in the project file - folder_id++; - } else { - // if we're saving folders, save the folder - stream.writeStartElement("folder"); - stream.writeAttribute("name", m->get_name()); - stream.writeAttribute("id", QString::number(m->temp_id)); - if (!item.parent().isValid()) { - stream.writeAttribute("parent", "0"); - } else { - stream.writeAttribute("parent", QString::number(olive::project_model.getItem(item.parent())->temp_id)); - } - stream.writeEndElement(); - } - // save_folder(stream, item, type, set_ids_only); - } else { - int folder = m->parentItem()->temp_id; - if (type == MEDIA_TYPE_FOOTAGE) { - Footage* f = m->to_footage(); - f->save_id = media_id; - stream.writeStartElement("footage"); - stream.writeAttribute("id", QString::number(media_id)); - stream.writeAttribute("folder", QString::number(folder)); - stream.writeAttribute("name", f->name); - stream.writeAttribute("url", proj_dir.relativeFilePath(f->url)); - stream.writeAttribute("duration", QString::number(f->length)); - stream.writeAttribute("using_inout", QString::number(f->using_inout)); - stream.writeAttribute("in", QString::number(f->in)); - stream.writeAttribute("out", QString::number(f->out)); - stream.writeAttribute("speed", QString::number(f->speed)); - stream.writeAttribute("alphapremul", QString::number(f->alpha_is_associated)); - stream.writeAttribute("startnumber", QString::number(f->start_number)); - stream.writeAttribute("colorspace", f->Colorspace()); - - stream.writeAttribute("proxy", QString::number(f->proxy)); - stream.writeAttribute("proxypath", f->proxy_path); - - // save video stream metadata - for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = f->video_tracks.at(j); - stream.writeStartElement("video"); - stream.writeAttribute("id", QString::number(ms.file_index)); - stream.writeAttribute("width", QString::number(ms.video_width)); - stream.writeAttribute("height", QString::number(ms.video_height)); - stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10)); - stream.writeAttribute("infinite", QString::number(ms.infinite_length)); - stream.writeEndElement(); // video - } - - // save audio stream metadata - for (int j=0;jaudio_tracks.size();j++) { - const FootageStream& ms = f->audio_tracks.at(j); - stream.writeStartElement("audio"); - stream.writeAttribute("id", QString::number(ms.file_index)); - stream.writeAttribute("channels", QString::number(ms.audio_channels)); - stream.writeAttribute("layout", QString::number(ms.audio_layout)); - stream.writeAttribute("frequency", QString::number(ms.audio_frequency)); - stream.writeEndElement(); // audio - } - - // save footage markers - for (int j=0;jmarkers.size();j++) { - f->markers.at(j).Save(stream); - } - - stream.writeEndElement(); // footage - media_id++; - } else if (type == MEDIA_TYPE_SEQUENCE) { - Sequence* s = m->to_sequence().get(); - if (set_ids_only) { - s->save_id = sequence_id; - sequence_id++; - } else { - s->Save(stream); - } - } - } - } - - if (m->get_type() == MEDIA_TYPE_FOLDER) { - save_folder(stream, type, set_ids_only, item); - } - } -} - -void Project::save_project(bool autorecovery) { - folder_id = 1; - media_id = 1; - sequence_id = 1; - - QFile file(autorecovery ? autorecovery_filename : olive::ActiveProjectFilename); - if (!file.open(QIODevice::WriteOnly)) { - qCritical() << "Could not open file"; - return; - } - - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument(); // doc - - stream.writeStartElement("project"); // project - - stream.writeTextElement("version", QString::number(olive::kSaveVersion)); - - stream.writeTextElement("url", olive::ActiveProjectFilename); - proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir(); - - save_folder(stream, MEDIA_TYPE_FOLDER, true); - - stream.writeStartElement("folders"); // folders - save_folder(stream, MEDIA_TYPE_FOLDER, false); - stream.writeEndElement(); // folders - - stream.writeStartElement("media"); // media - save_folder(stream, MEDIA_TYPE_FOOTAGE, false); - stream.writeEndElement(); // media - - save_folder(stream, MEDIA_TYPE_SEQUENCE, true); - - stream.writeStartElement("sequences"); // sequences - save_folder(stream, MEDIA_TYPE_SEQUENCE, false); - stream.writeEndElement();// sequences - - stream.writeEndElement(); // project - - stream.writeEndDocument(); // doc - - file.close(); - - if (!autorecovery) { - add_recent_project(olive::ActiveProjectFilename); - olive::Global->set_modified(false); - } -} - void Project::update_view_type() { tree_view->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE); icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON @@ -1229,28 +651,6 @@ void Project::set_tree_view() { update_view_type(); } -void Project::save_recent_projects() { - // save to file - QFile f(olive::Global->get_recent_project_list_file()); - if (f.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) { - QTextStream out(&f); - for (int i=0;i 0) { - out << "\n"; - } - out << recent_projects.at(i); - } - f.close(); - } else { - qWarning() << "Could not save recent projects"; - } -} - -void Project::clear_recent_projects() { - recent_projects.clear(); - save_recent_projects(); -} - void Project::set_icon_view_size(int s) { if (icon_view->viewMode() == QListView::IconMode) { icon_view->setGridSize(QSize(s, s)); @@ -1275,44 +675,6 @@ void Project::make_new_menu() { new_menu.exec(QCursor::pos()); } -void Project::add_recent_project(QString url) { - bool found = false; - for (int i=0;i MAXIMUM_RECENT_PROJECTS) { - recent_projects.removeLast(); - } - } - save_recent_projects(); -} - -void Project::list_all_sequences_worker(QVector* list, Media* parent) { - for (int i=0;iget_type()) { - case MEDIA_TYPE_SEQUENCE: - list->append(item); - break; - case MEDIA_TYPE_FOLDER: - list_all_sequences_worker(list, item); - break; - } - } -} - -QVector Project::list_all_project_sequences() { - QVector list; - list_all_sequences_worker(&list, nullptr); - return list; -} - QModelIndexList Project::get_current_selected() { if (olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE) { return tree_view->selectionModel()->selectedRows(); diff --git a/panels/project.h b/panels/project.h index 7d959a3e4..2cc8c826c 100644 --- a/panels/project.h +++ b/panels/project.h @@ -37,17 +37,8 @@ #include "ui/sourceiconview.h" #include "timeline/mediaimportdata.h" #include "undo/undo.h" - #include "ui/sourcetable.h" -extern QString autorecovery_filename; -extern QStringList recent_projects; - -SequencePtr create_sequence_from_media(QVector &media_list); - -QString get_channel_layout_name(int channels, uint64_t layout); -QString get_interlacing_name(int interlacing); - class Project : public Panel { Q_OBJECT public: @@ -56,29 +47,14 @@ public: void ConnectFilterToModel(); void DisconnectFilterToModel(); - bool is_focused(); - void clear(); - MediaPtr create_sequence_internal(ComboAction *ca, SequencePtr s, bool open, Media* parent); - QString get_next_sequence_name(QString start = nullptr); - void process_file_list(QStringList& files, bool recursive = false, MediaPtr replace = nullptr, Media *parent = nullptr); - void replace_media(MediaPtr item, QString filename); + virtual bool focused() override; + Media* get_selected_folder(); bool reveal_media(Media *media, QModelIndex parent = QModelIndex()); - void add_recent_project(QString url); - - void save_project(bool autorecovery); - - MediaPtr create_folder_internal(QString name); Media* item_to_media(const QModelIndex& index); MediaPtr item_to_media_ptr(const QModelIndex &index); - void save_recent_projects(); - - QVector list_all_project_sequences(); - - QVector last_imported_media; - QModelIndexList get_current_selected(); bool IsToolbarVisible(); @@ -87,7 +63,6 @@ public: virtual void Retranslate() override; protected: public slots: - void import_dialog(); void delete_selected_media(); void duplicate_selected(); void delete_clips_using_selected_media(); @@ -95,17 +70,8 @@ public slots: void replace_clip_media(); void open_properties(); void new_folder(); - void new_sequence(); - void SetToolbarVisible(bool visible); private: - void save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex &parent = QModelIndex()); - int folder_id; - int media_id; - int sequence_id; - void list_all_sequences_worker(QVector *list, Media* parent); - QString get_file_name_from_path(const QString &path); - QDir proj_dir; QWidget* icon_view_container; QSlider* icon_size_slider; QPushButton* directory_up; @@ -122,7 +88,6 @@ private slots: void set_icon_view(); void set_list_view(); void set_tree_view(); - void clear_recent_projects(); void set_icon_view_size(int); void set_up_dir_enabled(); void go_up_dir(); diff --git a/panels/timeline.h b/panels/timeline.h index f1ce05260..9f15ab10c 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -51,7 +51,7 @@ class Timeline : public Panel public: explicit Timeline(QWidget *parent = nullptr); - bool focused(); + virtual bool focused() override; void multiply_zoom(double m); void copy(bool del); ClipPtr split_clip(ComboAction* ca, bool transitions, int p, long frame); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index fd60fd994..afe282de2 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -34,6 +34,7 @@ extern "C" { #include #include #include +#include #include "rendering/audio.h" #include "timeline.h" @@ -105,7 +106,7 @@ void Viewer::Retranslate() { // update_window_title(); } -bool Viewer::is_focused() { +bool Viewer::focused() { return headers->hasFocus() || viewer_widget_->hasFocus() || go_to_start_button->hasFocus() @@ -324,29 +325,47 @@ void Viewer::pause() { // import audio QStringList file_list; file_list.append(get_recorded_audio_filename()); - panel_project->process_file_list(file_list); + olive::project_model.process_file_list(file_list); // add it to the sequence - ClipPtr c = std::make_shared(seq.get()); - Media* m = panel_project->last_imported_media.at(0); + QVector last_imported_media = olive::project_model.GetLastImportedMedia(); + Media* m = last_imported_media.first(); Footage* f = m->to_footage(); // wait for footage to be completely ready before taking metadata from it f->ready_lock.lock(); - - c->set_media(m, 0); // latest media - c->set_timeline_in(recording_start); - c->set_timeline_out(recording_start + f->get_length_in_frames(seq->frame_rate)); - c->set_clip_in(0); - c->set_track(recording_track); - c->set_color(128, 192, 128); - c->set_name(m->get_name()); - f->ready_lock.unlock(); - QVector add_clips; - add_clips.append(c); - olive::UndoStack.push(new AddClipCommand(seq.get(), add_clips)); // add clip + // Check if we were able to import the audio we just recorded + if (f->invalid) { + + QMessageBox::critical(this, + tr("Failed to import recorded file"), + tr("An error occurred trying to import the recorded audio"), + QMessageBox::Ok); + + } else { + + // Make a clip out of it and add it to the Sequence + + ClipPtr c = std::make_shared(seq.get()); + + c->set_media(m, 0); // latest media + c->set_timeline_in(recording_start); + c->set_timeline_out(recording_start + f->get_length_in_frames(seq->frame_rate)); + c->set_clip_in(0); + c->set_track(recording_track); + c->set_color(128, 192, 128); + c->set_name(m->get_name()); + + QVector add_clips; + add_clips.append(c); + olive::UndoStack.push(new AddClipCommand(seq.get(), add_clips)); // add clip + + } + + + } } } diff --git a/panels/viewer.h b/panels/viewer.h index e488a9696..431c5eb06 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -44,7 +44,7 @@ class Viewer : public Panel public: explicit Viewer(QWidget *parent = nullptr); - bool is_focused(); + virtual bool focused() override; bool is_main_sequence(); void set_main_sequence(); void set_media(Media *m); diff --git a/project/footage.cpp b/project/footage.cpp index 61c37b13c..8048e9a0c 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -29,6 +29,7 @@ namespace OCIO = OCIO_NAMESPACE::v1; #include "project/previewgenerator.h" #include "timeline/clip.h" #include "global/config.h" +#include "global/global.h" Footage::Footage() : ready(false), @@ -48,6 +49,58 @@ Footage::~Footage() { reset(); } +void Footage::Save(QXmlStreamWriter &stream) +{ + QDir proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir(); + + stream.writeStartElement("footage"); + stream.writeAttribute("id", QString::number(media_id)); + stream.writeAttribute("name", name); + stream.writeAttribute("url", proj_dir.relativeFilePath(url)); + stream.writeAttribute("duration", QString::number(length)); + stream.writeAttribute("using_inout", QString::number(using_inout)); + stream.writeAttribute("in", QString::number(in)); + stream.writeAttribute("out", QString::number(out)); + stream.writeAttribute("speed", QString::number(speed)); + stream.writeAttribute("alphapremul", QString::number(alpha_is_associated)); + stream.writeAttribute("startnumber", QString::number(start_number)); + stream.writeAttribute("colorspace", Colorspace()); + + stream.writeAttribute("proxy", QString::number(proxy)); + stream.writeAttribute("proxypath", proxy_path); + + // save video stream metadata + for (int j=0;jvideo_tracks.size();j++) { + const FootageStream& ms = f->video_tracks.at(j); + stream.writeStartElement("video"); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("width", QString::number(ms.video_width)); + stream.writeAttribute("height", QString::number(ms.video_height)); + stream.writeAttribute("framerate", QString::number(ms.video_frame_rate, 'f', 10)); + stream.writeAttribute("infinite", QString::number(ms.infinite_length)); + stream.writeEndElement(); // video + } + + // save audio stream metadata + for (int j=0;jaudio_tracks.size();j++) { + const FootageStream& ms = f->audio_tracks.at(j); + stream.writeStartElement("audio"); + stream.writeAttribute("id", QString::number(ms.file_index)); + stream.writeAttribute("channels", QString::number(ms.audio_channels)); + stream.writeAttribute("layout", QString::number(ms.audio_layout)); + stream.writeAttribute("frequency", QString::number(ms.audio_frequency)); + stream.writeEndElement(); // audio + } + + // save footage markers + for (int j=0;jmarkers.size();j++) { + f->markers.at(j).Save(stream); + } + + stream.writeEndElement(); // footage + media_id++; +} + QString Footage::Colorspace() { if (!colorspace_.isEmpty()) { diff --git a/project/footage.h b/project/footage.h index 2678769d3..e77ee6af1 100644 --- a/project/footage.h +++ b/project/footage.h @@ -70,6 +70,8 @@ public: Footage(); ~Footage(); + void Save(QXmlStreamWriter& stream); + // footage metadata QString url; QString name; @@ -107,6 +109,9 @@ public: long get_length_in_frames(double frame_rate); FootageStream *get_stream_from_file_index(bool video, int index); void reset(); + + static QString get_channel_layout_name(int channels, uint64_t layout); + static QString get_interlacing_name(int interlacing); private: QString colorspace_; }; diff --git a/project/media.cpp b/project/media.cpp index 53914a4c5..a45560436 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -68,6 +68,23 @@ Media::Media() : { } +void Media::Save(QXmlStreamWriter &stream) +{ + switch (type) { + case MEDIA_TYPE_FOLDER: + stream.writeStartElement("folder"); + stream.writeAttribute("name", get_name()); + stream.writeEndElement(); + break; + case MEDIA_TYPE_FOOTAGE: + to_footage()->Save(stream); + break; + case MEDIA_TYPE_SEQUENCE: + to_sequence()->Save(stream); + break; + } +} + Footage* Media::to_footage() { return static_cast(object.get()); } diff --git a/project/media.h b/project/media.h index a604e2180..e9555b310 100644 --- a/project/media.h +++ b/project/media.h @@ -47,6 +47,8 @@ class Media public: Media(); + void Save(QXmlStreamWriter& stream); + Footage *to_footage(); SequencePtr to_sequence(); void set_icon(const QString& str); diff --git a/project/projectfunctions.cpp b/project/projectfunctions.cpp new file mode 100644 index 000000000..5d9b8c486 --- /dev/null +++ b/project/projectfunctions.cpp @@ -0,0 +1,83 @@ +#include "projectfunctions.h" + +#include "projectmodel.h" +#include "global/config.h" + +MediaPtr olive::project::CreateFolder(QString name) { + + MediaPtr item = std::make_shared(); + + item->set_folder(); + item->set_name(name); + + return item; + +} + +SequencePtr olive::project::CreateSequenceFromMedia(QVector &media_list) +{ + SequencePtr s = std::make_shared(); + + s->name = olive::project_model.GetNextSequenceName(); + + // Retrieve default Sequence settings from Config + s->width = olive::CurrentConfig.default_sequence_width; + s->height = olive::CurrentConfig.default_sequence_height; + s->frame_rate = olive::CurrentConfig.default_sequence_framerate; + s->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; + s->audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout; + + bool got_video_values = false; + bool got_audio_values = false; + for (int i=0;iget_type()) { + case MEDIA_TYPE_FOOTAGE: + { + Footage* m = media->to_footage(); + if (m->ready) { + if (!got_video_values) { + for (int j=0;jvideo_tracks.size();j++) { + const FootageStream& ms = m->video_tracks.at(j); + s->width = ms.video_width; + s->height = ms.video_height; + if (!qFuzzyCompare(ms.video_frame_rate, 0.0)) { + s->frame_rate = ms.video_frame_rate * m->speed; + + if (ms.video_interlacing != VIDEO_PROGRESSIVE) s->frame_rate *= 2; + + // only break with a decent frame rate, otherwise there may be a better candidate + got_video_values = true; + break; + } + } + } + if (!got_audio_values && m->audio_tracks.size() > 0) { + const FootageStream& ms = m->audio_tracks.at(0); + s->audio_frequency = ms.audio_frequency; + got_audio_values = true; + } + } + } + break; + case MEDIA_TYPE_SEQUENCE: + { + // Clone all attributes of the original sequence (seq) into the new one (s) + Sequence* seq = media->to_sequence().get(); + + s->width = seq->width; + s->height = seq->height; + s->frame_rate = seq->frame_rate; + s->audio_frequency = seq->audio_frequency; + s->audio_layout = seq->audio_layout; + + got_video_values = true; + got_audio_values = true; + } + break; + } + if (got_video_values && got_audio_values) break; + } + + return s; +} diff --git a/project/projectfunctions.h b/project/projectfunctions.h new file mode 100644 index 000000000..1c62d8f24 --- /dev/null +++ b/project/projectfunctions.h @@ -0,0 +1,18 @@ +#ifndef PROJECTFUNCTIONS_H +#define PROJECTFUNCTIONS_H + +#include "project/media.h" +#include "timeline/sequence.h" +#include "timeline/mediaimportdata.h" + +namespace olive { +namespace project { + +MediaPtr CreateFolder(QString name); + +SequencePtr CreateSequenceFromMedia(QVector &media_list); + +} +} + +#endif // PROJECTFUNCTIONS_H diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index 57099ac35..001a35362 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -36,6 +36,57 @@ ProjectModel::~ProjectModel() { destroy_root(); } +int ProjectModel::PrepareToSave() +{ + int element_count = 0; + + PrepareToSaveInternal(element_count, get_root()); + + return element_count; +} + +void ProjectModel::PrepareToSaveInternal(int& element_count, Media *root) +{ + for (int i=0;ichildCount();i++) { + Media* child = root->child(i); + + switch (child->get_type()) { + case MEDIA_TYPE_FOLDER: + child->temp_id = element_count; + break; + case MEDIA_TYPE_FOOTAGE: + child->to_footage()->save_id = element_count; + break; + case MEDIA_TYPE_SEQUENCE: + child->to_sequence()->save_id = element_count; + break; + } + + element_count++; + + if (child->childCount() > 0) { + PrepareToSaveInternal(element_count, child); + } + } +} + +void ProjectModel::Save(QXmlStreamWriter &stream, Media* root) +{ + if (root == nullptr) { + root = get_root(); + } + + for (int i=0;ichildCount();i++) { + Media* child = root->child(i); + + child->Save(stream); + + if (child->childCount() > 0) { + Save(stream, child); + } + } +} + void ProjectModel::make_root() { root_item_ = std::make_shared(); root_item_->temp_id = 0; @@ -211,6 +262,58 @@ QVector ProjectModel::GetAllFolders() return GetAllMediaOfType(MEDIA_TYPE_FOLDER); } +QString ProjectModel::GetNextSequenceName(QString prepend) +{ + if (prepend.isEmpty()) { + prepend = tr("Sequence %1"); + } + + int sequence_number = 1; + QString test; + QVector all_sequences = GetAllSequences(); + bool found; + + do { + found = false; + test = prepend.arg(sequence_number); + for (int i=0;iget_name() == test) { + found = true; + sequence_number++; + break; + } + } + } while (found); + + return test; +} + +MediaPtr ProjectModel::CreateSequence(ComboAction *ca, SequencePtr s, bool open, Media *parent) +{ + MediaPtr item = std::make_shared(); + item->set_sequence(s); + + if (ca != nullptr) { + + ca->append(new AddMediaCommand(item, parent)); + + if (open) { + ca->append(new ChangeSequenceAction(s)); + } + + } else { + + appendChild(parent, item); + + if (open) { + olive::Global->set_sequence(s); + } + + } + + return item; +} + QVector ProjectModel::GetAllMediaOfType(int search_type) { QVector media_list; @@ -296,3 +399,255 @@ int ProjectModel::childCount(Media *parent) { } return parent->childCount(); } + +void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPtr replace, Media* parent) { + bool imported = false; + + // retrieve the array of image formats from the user's configuration + QStringList image_sequence_formats = olive::CurrentConfig.img_seq_formats.split("|"); + + // a cache of image sequence formatted URLS to assist the user in importing image sequences + QVector image_sequence_urls; + QVector image_sequence_importassequence; + + if (!recursive) last_imported_media.clear(); + + bool create_undo_action = (!recursive && replace == nullptr); + ComboAction* ca = nullptr; + if (create_undo_action) ca = new ComboAction(); + + // Loop through received files + for (int i=0;iappend(new AddMediaCommand(folder, parent)); + } else { + appendChild(parent, folder); + } + + process_file_list(subdir_filenames, true, nullptr, folder.get()); + + imported = true; + + } else if (!files.at(i).isEmpty()) { + QString file = files.at(i); + + // Check if the user is importing an Olive project file + if (file.endsWith(".ove", Qt::CaseInsensitive)) { + + // This file is an Olive project file. Ask the user if they really want to import it. + if (QMessageBox::question(this, + tr("Import a Project"), + tr("\"%1\" is an Olive project file. It will merge with this project. " + "Do you wish to continue?").arg(file), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + + // load the project without clearing the current one + olive::Global->ImportProject(file); + + } + + } else { + + // This file is NOT an Olive project file + + // Used later if this file is part of an already processed image sequence + bool skip = false; + + /* Heuristic to determine whether file is part of an image sequence */ + + // Firstly, we run a heuristic on whether this file is an image by checking its file extension + + bool file_is_an_image = false; + + // Get the string position of the extension in the filename + int lastcharindex = file.lastIndexOf("."); + + if (lastcharindex != -1 && lastcharindex > file.lastIndexOf('/')) { + + QString ext = file.mid(lastcharindex+1); + + // If the file extension is part of a predetermined list (from Config::img_seq_formats), we'll treat it + // as an image + if (image_sequence_formats.contains(ext, Qt::CaseInsensitive)) { + file_is_an_image = true; + } + + } else { + + // If we're here, the file has no extension, but we'll still check if its an image sequence just in case + lastcharindex = file.length(); + file_is_an_image = true; + + } + + // Some image sequence's don't start at "0", if it is indeed an image sequence, we'll use this variable + // later to determine where it does start + int start_number = 0; + + // Check if we passed the earlier heuristic to check whether this is a file, and whether the last number in + // the filename (before the extension) is a number + if (file_is_an_image && file[lastcharindex-1].isDigit()) { + + // Check how many digits are at the end of this filename + int digit_count = 0; + int digit_test = lastcharindex-1; + while (file[digit_test].isDigit()) { + digit_count++; + digit_test--; + } + + // Retrieve the integer represented at the end of this filename + digit_test++; + int file_number = file.mid(digit_test, digit_count).toInt(); + + // Check whether a file exists with the same format but one number higher or one number lower + if (QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number-1, digit_count, 10, QChar('0')) + file.mid(lastcharindex))) + || QFileInfo::exists(QString(file.left(digit_test) + QString("%1").arg(file_number+1, digit_count, 10, QChar('0')) + file.mid(lastcharindex)))) { + + // + // If so, it certainly looks like it *could* be an image sequence, but we'll ask the user just in case + // + + // Firstly we should check if this file is part of a sequence the user has already confirmed as either a + // sequence or not a sequence (e.g. if the user happened to select a bunch of images that happen to increase + // consecutively). We format the filename with FFmpeg's '%Nd' (N = digits) formatting for reading image + // sequences + QString new_filename = file.left(digit_test) + "%" + QString::number(digit_count) + "d" + file.mid(lastcharindex); + + int does_url_cache_already_contain_this = image_sequence_urls.indexOf(new_filename); + + if (does_url_cache_already_contain_this > -1) { + + // We've already processed an image with the same formatting + + // Check if the last time we saw this formatting, the user chose to import as a sequence + if (image_sequence_importassequence.at(does_url_cache_already_contain_this)) { + + // If so, no need to import this file too, so we signal to the rest of the function to skip this file + skip = true; + + } + + // If not, we can fall-through to the next step which is importing normally + + } else { + + // If we're here, we've never seen a file with this formatting before, so we'll ask whether to import + // as a sequence or not + + // Add this file formatting file to the URL cache + image_sequence_urls.append(new_filename); + + // This does look like an image sequence, let's ask the user if it'll indeed be an image sequence + if (QMessageBox::question(this, + tr("Image sequence detected"), + tr("The file '%1' appears to be part of an image sequence. " + "Would you like to import it as such?").arg(file), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes) == QMessageBox::Yes) { + + // Proceed to the next step of this with the formatted filename + file = new_filename; + + // Cache the user's answer alongside the image_sequence_urls value - in this case, YES, this will be an + // image sequence + image_sequence_importassequence.append(true); + + + // FFmpeg needs to know what file number to start at in the sequence. In case the image sequence doesn't + // start at a zero, we'll loop decreasing the number until it doesn't exist anymore + QString test_filename_format = QString("%1%2%3").arg(file.left(digit_test), "%1", file.mid(lastcharindex)); + int test_file_number = file_number; + do { + test_file_number--; + } while (QFileInfo::exists(test_filename_format.arg(QString("%1").arg(test_file_number, digit_count, 10, QChar('0'))))); + + // set the image sequence's start number to the last that existed + start_number = test_file_number + 1; + + } else { + + // Cache the user's response to the image sequence question - i.e. none of the files imported with this + // formatting should be imported as an image sequence + image_sequence_importassequence.append(false); + + } + } + + } + + } + + // If we're not skipping this file, let's import it + if (!skip) { + MediaPtr item; + FootagePtr m; + + if (replace != nullptr) { + item = replace; + } else { + item = std::make_shared(); + } + + m = std::make_shared(); + + // Edge case for PNGs that standardized unassociated alpha + if (file.endsWith("png", Qt::CaseInsensitive)) { + m->alpha_is_associated = false; + } + + m->using_inout = false; + m->url = file; + m->name = QFileInfo(files.at(i)).fileName(); + m->start_number = start_number; + + item->set_footage(m); + + last_imported_media.append(item.get()); + + if (replace == nullptr) { + if (create_undo_action) { + ca->append(new AddMediaCommand(item, parent)); + } else { + appendChild(parent, item); + } + } + + imported = true; + } + + } + + + } + } + if (create_undo_action) { + if (imported) { + olive::UndoStack.push(ca); + + for (int i=0;i #include "project/media.h" +#include "undo/comboaction.h" class ProjectModel : public QAbstractItemModel { @@ -32,6 +33,42 @@ public: ProjectModel(QObject* parent = nullptr); ~ProjectModel() override; + /** + * @brief Makes preparations for saving the project file. + * + * Some items require IDs to link between them (e.g. the Footage or Nested Sequences used by Clips, linked clips, + * etc). This function sets up those IDs before saving. + * + * NOTE: This function should **always** be called before Save(). + * + * @return + * + * A count of the elements in the project to save into the project file. The loading system can later use this value + * to determine the load progress. + */ + int PrepareToSave(); + + /** + * @brief Initiate a save of all the project data + * + * Recursively goes through the entire project tree saving everything to a specified QXmlStreamWriter object. + * + * It's not recommended to use this function directly as it expects an existing QXmlStreamWriter and doesn't write a + * header and footer for the resulting XML document. Instead use olive::Save(). + * + * NOTE: **Always** call PrepareToSave() just before calling this function to set up valid IDs for saving. + * + * @param stream + * + * A QXmlStreamWriter object. + * + * @param root + * + * Used for recursion, set to any child and called again whenever a child is found with children. If this is nullptr, + * this function will loop over the root item. + */ + void Save(QXmlStreamWriter& stream, Media *root = nullptr); + void make_root(); void destroy_root(); void clear(); @@ -58,15 +95,34 @@ public: int childCount(Media* parent = nullptr); void set_icon(Media* m, const QIcon &ico); + void process_file_list(QStringList& files, bool recursive = false, MediaPtr replace = nullptr, Media *parent = nullptr); + + /** + * @brief Get a list of the last imported media + * + * @return + * + * Returns a list of all the Media processed by the last call to process_file_list(). + */ + QVector GetLastImportedMedia(); + QVector GetAllSequences(); QVector GetAllFootage(); QVector GetAllFolders(); + QString GetNextSequenceName(QString prepend = QString()); + + MediaPtr CreateSequence(ComboAction *ca, SequencePtr s, bool open, Media* parent); + private: MediaPtr root_item_; + QVector last_imported_media; + QVector GetAllMediaOfType(int search_type); void RecurseTree(Media* parent, QVector &list, int search_type); + + void PrepareToSaveInternal(int& element_count, Media* root); }; namespace olive { diff --git a/project/savethread.cpp b/project/savethread.cpp new file mode 100644 index 000000000..8fcca1753 --- /dev/null +++ b/project/savethread.cpp @@ -0,0 +1,97 @@ +#include "savethread.h" + +#include +#include +#include +#include + +#include "global/global.h" +#include "global/config.h" +#include "projectmodel.h" + +void RecursiveSave() { + +} + +void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex& parent) { + for (int i=0;iget_type()) { + if (m->get_type() == MEDIA_TYPE_FOLDER) { + if (set_ids_only) { + m->temp_id = folder_id; // saves a temporary ID for matching in the project file + folder_id++; + } else { + // if we're saving folders, save the folder + stream.writeStartElement("folder"); + stream.writeAttribute("name", m->get_name()); + stream.writeAttribute("id", QString::number(m->temp_id)); + if (!item.parent().isValid()) { + stream.writeAttribute("parent", "0"); + } else { + stream.writeAttribute("parent", QString::number(olive::project_model.getItem(item.parent())->temp_id)); + } + stream.writeEndElement(); + } + // save_folder(stream, item, type, set_ids_only); + } else { + int folder = m->parentItem()->temp_id; + if (type == MEDIA_TYPE_FOOTAGE) { + + } else if (type == MEDIA_TYPE_SEQUENCE) { + Sequence* s = m->to_sequence().get(); + if (set_ids_only) { + s->save_id = sequence_id; + sequence_id++; + } else { + s->Save(stream); + } + } + } + } + + if (m->get_type() == MEDIA_TYPE_FOLDER) { + save_folder(stream, type, set_ids_only, item); + } + } +} + +void olive::Save(bool autorecovery) +{ + QFile file(autorecovery ? olive::Global->get_autorecovery_filename() : olive::ActiveProjectFilename); + if (!file.open(QIODevice::WriteOnly)) { + qCritical() << "Could not open file"; + return; + } + + QXmlStreamWriter stream(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument(); // doc + + stream.writeStartElement("project"); // project + + stream.writeTextElement("version", QString::number(olive::kSaveVersion)); + + stream.writeTextElement("url", olive::ActiveProjectFilename); + + // Prepare project for saving and retrieve element count + int element_count = olive::project_model.PrepareToSave(); + + // Write element count to file - used by the loading thread to determine its loading progress + stream.writeTextElement("elements", QString::number(element_count)); + + olive::project_model.Save(stream); + + stream.writeEndElement(); // project + + stream.writeEndDocument(); // doc + + file.close(); + + if (!autorecovery) { + add_recent_project(olive::ActiveProjectFilename); + olive::Global->set_modified(false); + } +} diff --git a/project/savethread.h b/project/savethread.h new file mode 100644 index 000000000..0687defd5 --- /dev/null +++ b/project/savethread.h @@ -0,0 +1,8 @@ +#ifndef SAVETHREAD_H +#define SAVETHREAD_H + +namespace olive { +void Save(bool autorecovery); +} + +#endif // SAVETHREAD_H diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index d9967ecbb..f140cf764 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "ui/menuhelper.h" @@ -43,6 +44,7 @@ #include "dialogs/proxydialog.h" #include "ui/viewerwidget.h" #include "project/proxygenerator.h" +#include "project/projectfunctions.h" #include "ui/mainwindow.h" #include "ui/menu.h" #include "undo/undostack.h" @@ -64,13 +66,13 @@ void SourcesCommon::create_seq_from_selected() { } ComboAction* ca = new ComboAction(); - SequencePtr s = create_sequence_from_media(media_list); + SequencePtr s = olive::project::CreateSequenceFromMedia(media_list); // add clips to it panel_timeline->create_ghosts_from_media(s.get(), 0, media_list); panel_timeline->add_clips_from_ghosts(ca, s.get()); - project_parent->create_sequence_internal(ca, s, true, nullptr); + olive::project_model.CreateSequence(ca, s, true, nullptr); olive::UndoStack.push(ca); } } @@ -239,6 +241,21 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it menu.exec(QCursor::pos()); } +void SourcesCommon::replace_media(MediaPtr item, QString filename) +{ + if (filename.isEmpty()) { + filename = QFileDialog::getOpenFileName( + this, + tr("Replace '%1'").arg(item->get_name()), + "", + tr("All Files") + " (*)"); + } + if (!filename.isEmpty()) { + ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename); + olive::UndoStack.push(rmc); + } +} + void SourcesCommon::mousePressEvent(QMouseEvent *) { stop_rename_timer(); } @@ -255,7 +272,7 @@ void SourcesCommon::item_click(Media *m, const QModelIndex& index) { void SourcesCommon::mouseDoubleClickEvent(const QModelIndexList& selected_items) { stop_rename_timer(); if (selected_items.size() == 0) { - project_parent->import_dialog(); + olive::Global->open_import_dialog(); } else if (selected_items.size() == 1) { Media* media = project_parent->item_to_media(selected_items.at(0)); if (media->get_type() == MEDIA_TYPE_SEQUENCE) { @@ -292,7 +309,7 @@ void SourcesCommon::dropEvent(QWidget* parent, tr("You dropped a file onto '%1'. Would you like to replace it with the dropped file?").arg(m->get_name()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { replace = true; - project_parent->replace_media(m, paths.at(0)); + replace_media(m, paths.at(0)); } if (!replace) { QModelIndex parent; @@ -303,7 +320,7 @@ void SourcesCommon::dropEvent(QWidget* parent, parent = drop_item.parent(); } } - project_parent->process_file_list(paths, false, nullptr, panel_project->item_to_media(parent)); + olive::project_model.process_file_list(paths, false, nullptr, panel_project->item_to_media(parent)); } } event->acceptProposedAction(); diff --git a/project/sourcescommon.h b/project/sourcescommon.h index 4b00071ed..b74b1165b 100644 --- a/project/sourcescommon.h +++ b/project/sourcescommon.h @@ -27,10 +27,10 @@ #include "project/footage.h" #include "project/projectfilter.h" +#include "media.h" class Project; class QMouseEvent; -class Media; class QAbstractItemView; class QDropEvent; @@ -41,6 +41,8 @@ public: QAbstractItemView* view; void show_context_menu(QWidget* parent, const QModelIndexList &items); + void replace_media(MediaPtr item, QString filename); + void mousePressEvent(QMouseEvent* e); void mouseDoubleClickEvent(const QModelIndexList& selected_items); void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items); diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 660633e07..298750e98 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -146,7 +146,7 @@ void MainWindow::setup_layout(bool reset) { removeDockWidget(olive::panels.at(i)); } - addDockWidget(Qt::TopDockWidgetArea, panel_project); + addDockWidget(Qt::TopDockWidgetArea, panel_project.first()); addDockWidget(Qt::TopDockWidgetArea, panel_graph_editor); addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); tabifyDockWidget(panel_footage_viewer, panel_effect_controls); @@ -154,25 +154,25 @@ void MainWindow::setup_layout(bool reset) { addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); - panel_project->show(); + panel_project.first()->show(); panel_effect_controls->show(); panel_footage_viewer->show(); panel_sequence_viewer->show(); panel_timeline->show(); panel_graph_editor->hide(); - panel_project->setFloating(false); + panel_project.first()->setFloating(false); panel_effect_controls->setFloating(false); panel_footage_viewer->setFloating(false); panel_sequence_viewer->setFloating(false); panel_timeline->setFloating(false); panel_graph_editor->setFloating(true); - resizeDocks({panel_project, panel_footage_viewer, panel_sequence_viewer}, + resizeDocks({panel_project.first(), panel_footage_viewer, panel_sequence_viewer}, {width()/3, width()/3, width()/3}, Qt::Horizontal); - resizeDocks({panel_project, panel_timeline}, + resizeDocks({panel_project.first(), panel_timeline}, {height()/2, height()/2}, Qt::Vertical); } @@ -242,19 +242,7 @@ MainWindow::MainWindow(QWidget *parent) : } // search for open recents list - QFile f(olive::Global->get_recent_project_list_file()); - if (f.exists() && f.open(QFile::ReadOnly | QFile::Text)) { - QTextStream text_stream(&f); - while (true) { - QString line = text_stream.readLine(); - if (line.isNull()) { - break; - } else { - recent_projects.append(line); - } - } - f.close(); - } + olive::Global->load_recent_projects(); } } QString config_path = get_config_path(); @@ -518,7 +506,7 @@ void MainWindow::setup_menus() { open_recent = MenuHelper::create_submenu(file_menu); - clear_open_recent_action = MenuHelper::create_menu_action(nullptr, "clearopenrecent", panel_project, SLOT(clear_recent_projects())); + clear_open_recent_action = MenuHelper::create_menu_action(nullptr, "clearopenrecent", olive::Global.get(), SLOT(clear_recent_projects())); save_project = MenuHelper::create_menu_action(file_menu, "saveproj", olive::Global.get(), SLOT(save_project()), QKeySequence("Ctrl+S")); @@ -526,7 +514,7 @@ void MainWindow::setup_menus() { file_menu->addSeparator(); - import_action = MenuHelper::create_menu_action(file_menu, "import", panel_project, SLOT(import_dialog()), QKeySequence("Ctrl+I")); + import_action = MenuHelper::create_menu_action(file_menu, "import", olive::Global.get(), SLOT(open_import_dialog()), QKeySequence("Ctrl+I")); file_menu->addSeparator(); @@ -978,9 +966,12 @@ void MainWindow::closeEvent(QCloseEvent *e) { QString data_dir = get_data_path(); QString config_path = get_config_path(); + + const QString& autorecovery_filename = olive::Global->get_autorecovery_filename(); if (!data_dir.isEmpty() && !autorecovery_filename.isEmpty()) { if (QFile::exists(autorecovery_filename)) { - QFile::rename(autorecovery_filename, autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); + QFile::rename(autorecovery_filename, + autorecovery_filename + "." + QDateTime::currentDateTimeUtc().toString("yyyyMMddHHmmss")); } } if (!config_path.isEmpty()) { @@ -1208,11 +1199,11 @@ void MainWindow::set_panels_locked(bool locked) } void MainWindow::fileMenu_About_To_Be_Shown() { - if (recent_projects.size() > 0) { + if (olive::Global->recent_project_count() > 0) { open_recent->clear(); open_recent->setEnabled(true); - for (int i=0;iaddAction(recent_projects.at(i)); + for (int i=0;irecent_project_count();i++) { + QAction* action = open_recent->addAction(olive::Global->recent_project(i)); action->setProperty("keyignore", true); action->setData(i); connect(action, SIGNAL(triggered()), &olive::MenuHelper, SLOT(open_recent_from_menu())); diff --git a/ui/panel.cpp b/ui/panel.cpp index ce74c1437..27d80afdf 100644 --- a/ui/panel.cpp +++ b/ui/panel.cpp @@ -26,9 +26,6 @@ QVector olive::panels; Panel::Panel(QWidget *parent) : QDockWidget (parent) { -// setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); -// setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - olive::panels.append(this); } @@ -37,6 +34,11 @@ Panel::~Panel() olive::panels.removeAll(this); } +bool Panel::focused() +{ + return hasFocus(); +} + void Panel::LoadLayoutState(const QByteArray &) {} QByteArray Panel::SaveLayoutState() diff --git a/ui/panel.h b/ui/panel.h index b0141cec4..4c97ee178 100644 --- a/ui/panel.h +++ b/ui/panel.h @@ -31,6 +31,8 @@ public: virtual void Retranslate() = 0; + virtual bool focused(); + virtual void LoadLayoutState(const QByteArray& data); virtual QByteArray SaveLayoutState(); protected: From 7f92774dc96d895a4842311ff8f0bb361bb8aabf Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 30 Mar 2019 13:36:44 +1100 Subject: [PATCH 061/133] use track types and clip refs --- panels/timeline.cpp | 4 ++-- timeline/clip.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 390b10553..de6fe4eba 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1870,7 +1870,7 @@ void Timeline::transition_tool_click() { for (int i=0;isetObjectName("v"); a->setData(reinterpret_cast(&em)); @@ -1881,7 +1881,7 @@ void Timeline::transition_tool_click() { for (int i=0;isetObjectName("a"); a->setData(reinterpret_cast(&em)); diff --git a/timeline/clip.h b/timeline/clip.h index eb3fccbf7..69bd75e67 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -128,7 +128,7 @@ public: // other variables (should be deep copied/duplicated in copy()) int IndexOfEffect(Effect* e); QList effects; - QVector linked; + QVector linked; TransitionPtr opening_transition; TransitionPtr closing_transition; From 8e95210b2225d34a5fbde5dfe027b24d9c49327e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 30 Mar 2019 19:43:22 +1100 Subject: [PATCH 062/133] started new timeline paradigm --- olive.pro | 14 +++- panels/timeline.cpp | 6 +- panels/timeline.h | 9 +-- panels/viewer.h | 2 +- timeline/timelinefunctions.h | 6 ++ timeline/timelineshared.cpp | 6 ++ timeline/timelineshared.h | 11 +++ timeline/track.cpp | 35 +++++++- timeline/track.h | 14 ++++ timeline/tracklist.cpp | 5 ++ timeline/tracklist.h | 2 +- ui/mainwindow.cpp | 2 +- ui/timelinearea.cpp | 36 +++++++++ ui/timelinearea.h | 25 ++++++ ui/timelinelabel.cpp | 50 ++++++++++++ ui/timelinelabel.h | 23 ++++++ ui/{timelinewidget.cpp => timelineview.cpp} | 90 +++++++++------------ ui/{timelinewidget.h => timelineview.h} | 6 +- ui/viewerwidget.cpp | 2 +- 19 files changed, 272 insertions(+), 72 deletions(-) create mode 100644 timeline/timelineshared.cpp create mode 100644 timeline/timelineshared.h create mode 100644 ui/timelinearea.cpp create mode 100644 ui/timelinearea.h create mode 100644 ui/timelinelabel.cpp create mode 100644 ui/timelinelabel.h rename ui/{timelinewidget.cpp => timelineview.cpp} (95%) rename ui/{timelinewidget.h => timelineview.h} (91%) diff --git a/olive.pro b/olive.pro index 77966607a..672286233 100644 --- a/olive.pro +++ b/olive.pro @@ -57,7 +57,6 @@ SOURCES += \ panels/timeline.cpp \ ui/sourcetable.cpp \ dialogs/aboutdialog.cpp \ - ui/timelinewidget.cpp \ project/media.cpp \ project/footage.cpp \ timeline/sequence.cpp \ @@ -178,7 +177,11 @@ SOURCES += \ timeline/track.cpp \ timeline/tracklist.cpp \ project/savethread.cpp \ - project/projectfunctions.cpp + project/projectfunctions.cpp \ + ui/timelinearea.cpp \ + timeline/timelineshared.cpp \ + ui/timelineview.cpp \ + ui/timelinelabel.cpp HEADERS += \ ui/mainwindow.h \ @@ -188,7 +191,6 @@ HEADERS += \ panels/timeline.h \ ui/sourcetable.h \ dialogs/aboutdialog.h \ - ui/timelinewidget.h \ project/media.h \ project/footage.h \ timeline/sequence.h \ @@ -316,7 +318,11 @@ HEADERS += \ timeline/track.h \ timeline/tracklist.h \ project/savethread.h \ - project/projectfunctions.h + project/projectfunctions.h \ + ui/timelinearea.h \ + timeline/timelineshared.h \ + ui/timelineview.h \ + ui/timelinelabel.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index de6fe4eba..b0858baee 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -37,7 +37,7 @@ #include "global/global.h" #include "panels/panels.h" #include "project/projectelements.h" -#include "ui/timelinewidget.h" +#include "ui/timelineview.h" #include "ui/icons.h" #include "ui/viewerwidget.h" #include "rendering/audio.h" @@ -2058,7 +2058,7 @@ void Timeline::setup_ui() { videoContainerLayout->setSpacing(0); videoContainerLayout->setContentsMargins(0, 0, 0, 0); - video_area = new TimelineWidget(); + video_area = new TimelineView(); video_area->setFocusPolicy(Qt::ClickFocus); videoContainerLayout->addWidget(video_area); @@ -2075,7 +2075,7 @@ void Timeline::setup_ui() { audioContainerLayout->setSpacing(0); audioContainerLayout->setContentsMargins(0, 0, 0, 0); - audio_area = new TimelineWidget(); + audio_area = new TimelineView(); audio_area->setFocusPolicy(Qt::ClickFocus); audioContainerLayout->addWidget(audio_area); diff --git a/panels/timeline.h b/panels/timeline.h index 9f15ab10c..c6c8915e9 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -25,7 +25,7 @@ #include #include -#include "ui/timelinewidget.h" +#include "ui/timelinearea.h" #include "ui/timelinetools.h" #include "timeline/selection.h" #include "timeline/clip.h" @@ -94,9 +94,6 @@ public: bool showing_all; double old_zoom; - int GetTrackHeight(int track); - void SetTrackHeight(int track, int height); - // snapping bool snapping; bool snapped; @@ -233,8 +230,8 @@ private: long rc_ripple_max; QWidget* timeline_area; - TimelineWidget* video_area; - TimelineWidget* audio_area; + TimelineArea* video_area; + TimelineArea* audio_area; QWidget* editAreas; QScrollBar* videoScrollbar; QScrollBar* audioScrollbar; diff --git a/panels/viewer.h b/panels/viewer.h index 431c5eb06..fb5835737 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -32,7 +32,7 @@ #include "ui/panel.h" #include "ui/viewerwidget.h" -#include "ui/timelinewidget.h" +#include "ui/timelineview.h" #include "ui/timelineheader.h" #include "ui/labelslider.h" #include "ui/resizablescrollbar.h" diff --git a/timeline/timelinefunctions.h b/timeline/timelinefunctions.h index b42ce8b50..6cf5cb75e 100644 --- a/timeline/timelinefunctions.h +++ b/timeline/timelinefunctions.h @@ -19,6 +19,12 @@ enum TrimType { TRIM_OUT }; +enum Alignment { + kAlignmentTop, + kAlignmentBottom, + kAlignmentSingle +}; + } } diff --git a/timeline/timelineshared.cpp b/timeline/timelineshared.cpp new file mode 100644 index 000000000..6f5fd8337 --- /dev/null +++ b/timeline/timelineshared.cpp @@ -0,0 +1,6 @@ +#include "timelineshared.h" + +TimelineShared::TimelineShared() +{ + +} diff --git a/timeline/timelineshared.h b/timeline/timelineshared.h new file mode 100644 index 000000000..0622ac154 --- /dev/null +++ b/timeline/timelineshared.h @@ -0,0 +1,11 @@ +#ifndef TIMELINESHARED_H +#define TIMELINESHARED_H + + +class TimelineShared +{ +public: + TimelineShared(); +}; + +#endif // TIMELINESHARED_H diff --git a/timeline/track.cpp b/timeline/track.cpp index 2b6d4d52f..8a2f3fa31 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -8,7 +8,10 @@ int olive::timeline::kTrackHeightIncrement = 10; Track::Track(TrackList* parent, Type type) : parent_(parent), - type_(type) + type_(type), + muted_(false), + soloed_(false), + locked_(false) { } @@ -172,3 +175,33 @@ long Track::GetEndFrame() return end_frame; } + +bool Track::IsMuted() +{ + return muted_; +} + +void Track::SetMuted(bool muted) +{ + muted_ = muted; +} + +bool Track::IsSoloed() +{ + return soloed_; +} + +void Track::SetSoloed(bool soloed) +{ + soloed_ = soloed; +} + +bool Track::IsLocked() +{ + return locked_; +} + +void Track::SetLocked(bool locked) +{ + locked_ = locked; +} diff --git a/timeline/track.h b/timeline/track.h index 43d90735e..4a947363e 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -65,6 +65,16 @@ public: void ClearSelections(); long GetEndFrame(); + + bool IsMuted(); + bool IsSoloed(); + bool IsLocked(); + +public slots: + void SetMuted(bool muted); + void SetSoloed(bool soloed); + void SetLocked(bool locked); + private: void ResizeClipArray(int new_size); @@ -74,6 +84,10 @@ private: QVector clips_; QVector effects_; QVector selections_; + + bool muted_; + bool soloed_; + bool locked_; }; #endif // TRACK_H diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index 1f4bcf469..45bf5418b 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -39,6 +39,11 @@ Track *TrackList::First() return tracks_.first().get(); } +int TrackList::TrackCount() +{ + return tracks_.size(); +} + QVector TrackList::tracks() { return tracks_; diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 599a76bf3..40975c55c 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -13,11 +13,11 @@ public: void AddTrack(); void RemoveTrack(int i); Track* First(); + int TrackCount(); QVector tracks(); Sequence* GetParent(); - private: void ResizeTrackArray(int i); diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 298750e98..17e55c257 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -684,7 +684,7 @@ void MainWindow::setup_menus() { window_project_action = MenuHelper::create_menu_action(window_menu, "panelproject", this, SLOT(toggle_panel_visibility())); window_project_action->setCheckable(true); - window_project_action->setData(reinterpret_cast(panel_project)); + window_project_action->setData(reinterpret_cast(panel_project.first())); window_effectcontrols_action = MenuHelper::create_menu_action(window_menu, "paneleffectcontrols", this, SLOT(toggle_panel_visibility())); window_effectcontrols_action->setCheckable(true); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp new file mode 100644 index 000000000..ae3ac7c6f --- /dev/null +++ b/ui/timelinearea.cpp @@ -0,0 +1,36 @@ +#include "timelinearea.h" + +TimelineArea::TimelineArea() : + track_list_(nullptr) +{ + +} + +void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) +{ + if (sequence == nullptr) { + + track_list_ = nullptr; + + } else { + + track_list_ = sequence->GetTrackList(track_list); + + } + + +} + +void TimelineArea::RefreshLabels() +{ + if (track_list_ == nullptr) { + labels_.clear(); + } else { + + labels_.resize(track_list_->TrackCount()); + for (int i=0;i labels_; +}; + +#endif // TIMELINEAREA_H diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp new file mode 100644 index 000000000..937042408 --- /dev/null +++ b/ui/timelinelabel.cpp @@ -0,0 +1,50 @@ +#include "timelinelabel.h" + +#include +#include +#include + +TimelineLabel::TimelineLabel() : + track_(nullptr) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + + QLabel* label = new QLabel("Track!"); + layout->addWidget(label); + + mute_button_ = new QPushButton("M"); + mute_button_->setCheckable(true); + mute_button_->setFlat(true); + layout->addWidget(mute_button_); + + solo_button_ = new QPushButton("S"); + solo_button_->setCheckable(true); + solo_button_->setFlat(true); + layout->addWidget(solo_button_); + + lock_button_ = new QPushButton("L"); + lock_button_->setCheckable(true); + lock_button_->setFlat(true); + layout->addWidget(lock_button_); +} + +void TimelineLabel::SetTrack(Track *track) +{ + if (track_ != nullptr) { + disconnect(mute_button_, SIGNAL(toggled(bool)), track_, SLOT(SetMuted(bool))); + disconnect(solo_button_, SIGNAL(toggled(bool)), track_, SLOT(SetSoloed(bool))); + disconnect(lock_button_, SIGNAL(toggled(bool)), track_, SLOT(SetLocked(bool))); + } + + track_ = track; + + if (track != nullptr) { + mute_button_->setChecked(track->IsMuted()); + solo_button_->setChecked(track->IsSoloed()); + lock_button_->setChecked(track->IsLocked()); + + connect(mute_button_, SIGNAL(toggled(bool)), track_, SLOT(SetMuted(bool))); + connect(solo_button_, SIGNAL(toggled(bool)), track_, SLOT(SetSoloed(bool))); + connect(lock_button_, SIGNAL(toggled(bool)), track_, SLOT(SetLocked(bool))); + } +} diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h new file mode 100644 index 000000000..448f5d955 --- /dev/null +++ b/ui/timelinelabel.h @@ -0,0 +1,23 @@ +#ifndef TIMELINELABEL_H +#define TIMELINELABEL_H + +#include + +#include "timeline/track.h" + +class TimelineLabel : public QWidget +{ + Q_OBJECT +public: + TimelineLabel(); + + void SetTrack(Track* track); +private: + QPushButton* mute_button_; + QPushButton* solo_button_; + QPushButton* lock_button_; + + Track* track_; +}; + +#endif // TIMELINELABEL_H diff --git a/ui/timelinewidget.cpp b/ui/timelineview.cpp similarity index 95% rename from ui/timelinewidget.cpp rename to ui/timelineview.cpp index 7e1873ca7..8616461ec 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelineview.cpp @@ -18,7 +18,7 @@ ***/ -#include "timelinewidget.h" +#include "timelineview.h" #include #include @@ -63,7 +63,7 @@ #define MAX_TEXT_WIDTH 20 #define TRANSITION_BETWEEN_RANGE 40 -TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent) { +TimelineView::TimelineView(QWidget *parent) : QWidget(parent) { selection_command = nullptr; self_created_sequence = nullptr; scroll = 0; @@ -81,7 +81,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent) { connect(&tooltip_timer, SIGNAL(timeout()), this, SLOT(tooltip_timer_timeout())); } -void TimelineWidget::show_context_menu(const QPoint& pos) { +void TimelineView::show_context_menu(const QPoint& pos) { if (olive::ActiveSequence != nullptr) { // hack because sometimes right clicking doesn't trigger mouse release event panel_timeline->rect_select_init = false; @@ -146,23 +146,6 @@ void TimelineWidget::show_context_menu(const QPoint& pos) { olive::MenuHelper.make_clip_functions_menu(&menu); - // stabilizer option - /*int video_clip_count = 0; - bool all_video_is_footage = true; - for (int i=0;itrack() < 0) { - video_clip_count++; - if (selected_clips.at(i)->media() == nullptr - || selected_clips.at(i)->media()->get_type() != MEDIA_TYPE_FOOTAGE) { - all_video_is_footage = false; - } - } - } - if (video_clip_count == 1 && all_video_is_footage) { - QAction* stabilizerAction = menu.addAction("S&tabilizer"); - connect(stabilizerAction, SIGNAL(triggered(bool)), this, SLOT(show_stabilizer_diag())); - }*/ - // check if all selected clips have the same media for a "Reveal In Project" bool same_media = true; rc_reveal_media = selected_clips.at(0)->media(); @@ -185,7 +168,7 @@ void TimelineWidget::show_context_menu(const QPoint& pos) { } } -void TimelineWidget::toggle_autoscale() { +void TimelineView::toggle_autoscale() { QVector selected_clips = olive::ActiveSequence->SelectedClips(); if (!selected_clips.isEmpty()) { @@ -200,7 +183,7 @@ void TimelineWidget::toggle_autoscale() { } } -void TimelineWidget::tooltip_timer_timeout() { +void TimelineView::tooltip_timer_timeout() { if (tooltip_clip != nullptr) { QToolTip::showText(QCursor::pos(), tr("%1\nStart: %2\nEnd: %3\nDuration: %4").arg( @@ -214,7 +197,7 @@ void TimelineWidget::tooltip_timer_timeout() { tooltip_timer.stop(); } -void TimelineWidget::open_sequence_properties() { +void TimelineView::open_sequence_properties() { QVector sequence_items = olive::project_model.GetAllSequences(); for (int i=0;i selected_clips = olive::ActiveSequence->SelectedClips(); @@ -244,19 +227,26 @@ bool same_sign(int a, int b) { return (a < 0) == (b < 0); } -void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { +void TimelineView::dragEnterEvent(QDragEnterEvent *event) { bool import_init = false; QVector media_list; panel_timeline->importing_files = false; - if (panel_project->IsProjectWidget(event->source())) { - QModelIndexList items = panel_project->get_current_selected(); - media_list.resize(items.size()); - for (int i=0;iitem_to_media(items.at(i)); + for (int i=0;iIsProjectWidget(event->source())) { + + QModelIndexList items = panel_project.at(i)->get_current_selected(); + + media_list.resize(items.size()); + for (int i=0;iitem_to_media(items.at(i)); + } + import_init = true; + + break; + } - import_init = true; } if (event->source() == panel_footage_viewer) { @@ -326,7 +316,7 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) { } } -void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) { +void TimelineView::dragMoveEvent(QDragMoveEvent *event) { if (panel_timeline->importing) { event->acceptProposedAction(); @@ -340,7 +330,7 @@ void TimelineWidget::dragMoveEvent(QDragMoveEvent *event) { } } -void TimelineWidget::wheelEvent(QWheelEvent *event) { +void TimelineView::wheelEvent(QWheelEvent *event) { // TODO: implement pixel scrolling @@ -403,7 +393,7 @@ void TimelineWidget::wheelEvent(QWheelEvent *event) { } } -void TimelineWidget::dragLeaveEvent(QDragLeaveEvent* event) { +void TimelineView::dragLeaveEvent(QDragLeaveEvent* event) { event->accept(); if (panel_timeline->importing) { if (panel_timeline->importing_files) { @@ -511,7 +501,7 @@ void insert_clips(ComboAction* ca) { } } -void TimelineWidget::dropEvent(QDropEvent* event) { +void TimelineView::dropEvent(QDropEvent* event) { if (panel_timeline->importing && panel_timeline->ghosts.size() > 0) { event->acceptProposedAction(); @@ -540,7 +530,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) { } } -void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *event) { +void TimelineView::mouseDoubleClickEvent(QMouseEvent *event) { if (olive::ActiveSequence != nullptr) { if (panel_timeline->tool == TIMELINE_TOOL_EDIT) { int clip_index = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track); @@ -570,7 +560,7 @@ bool current_tool_shows_cursor() { return (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR || panel_timeline->creating); } -void TimelineWidget::mousePressEvent(QMouseEvent *event) { +void TimelineView::mousePressEvent(QMouseEvent *event) { if (olive::ActiveSequence != nullptr) { int effective_tool = panel_timeline->tool; @@ -980,7 +970,7 @@ void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, lo } } -void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { +void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QToolTip::hideText(); if (olive::ActiveSequence != nullptr) { bool alt = (event->modifiers() & Qt::AltModifier); @@ -1542,7 +1532,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { } } -void TimelineWidget::init_ghosts() { +void TimelineView::init_ghosts() { for (int i=0;ighosts.size();i++) { Ghost& g = panel_timeline->ghosts[i]; ClipPtr c = olive::ActiveSequence->clips.at(g.clip); @@ -1612,7 +1602,7 @@ void validate_transitions(Clip* c, int transition_type, long& frame_diff) { } } -void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { +void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { int effective_tool = panel_timeline->tool; if (panel_timeline->importing || panel_timeline->creating) effective_tool = TIMELINE_TOOL_POINTER; @@ -1997,7 +1987,7 @@ void TimelineWidget::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } -void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { +void TimelineView::mouseMoveEvent(QMouseEvent *event) { // interrupt any potential tooltip about to show tooltip_timer.stop(); @@ -2794,7 +2784,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { } } -void TimelineWidget::leaveEvent(QEvent*) { +void TimelineView::leaveEvent(QEvent*) { tooltip_timer.stop(); } @@ -2906,7 +2896,7 @@ void draw_transition(QPainter& p, ClipPtr c, const QRect& clip_rect, QRect& text } -void TimelineWidget::paintEvent(QPaintEvent*) { +void TimelineView::paintEvent(QPaintEvent*) { // Draw clips if (olive::ActiveSequence != nullptr) { QPainter p(this); @@ -3352,11 +3342,11 @@ void TimelineWidget::paintEvent(QPaintEvent*) { } } -void TimelineWidget::resizeEvent(QResizeEvent *) { +void TimelineView::resizeEvent(QResizeEvent *) { scrollBar->setPageStep(height()); } -bool TimelineWidget::is_track_visible(int track) { +bool TimelineView::is_track_visible(int track) { return (bottom_align == (track < 0)); } @@ -3364,7 +3354,7 @@ bool TimelineWidget::is_track_visible(int track) { // screen point <-> frame/track functions // ************************************** -int TimelineWidget::getTrackFromScreenPoint(int y) { +int TimelineView::getTrackFromScreenPoint(int y) { int track_candidate = 0; y += scroll; @@ -3403,7 +3393,7 @@ int TimelineWidget::getTrackFromScreenPoint(int y) { } } -int TimelineWidget::getScreenPointFromTrack(int track) { +int TimelineView::getScreenPointFromTrack(int track) { int point = 0; int start = (track < 0) ? -1 : 0; @@ -3423,7 +3413,7 @@ int TimelineWidget::getScreenPointFromTrack(int track) { } } -int TimelineWidget::getClipIndexFromCoords(long frame, int track) { +int TimelineView::getClipIndexFromCoords(long frame, int track) { for (int i=0;iclips.size();i++) { ClipPtr c = olive::ActiveSequence->clips.at(i); if (c != nullptr && c->track() == track && frame >= c->timeline_in() && frame < c->timeline_out()) { @@ -3433,11 +3423,11 @@ int TimelineWidget::getClipIndexFromCoords(long frame, int track) { return -1; } -void TimelineWidget::setScroll(int s) { +void TimelineView::setScroll(int s) { scroll = s; update(); } -void TimelineWidget::reveal_media() { +void TimelineView::reveal_media() { panel_project->reveal_media(rc_reveal_media); } diff --git a/ui/timelinewidget.h b/ui/timelineview.h similarity index 91% rename from ui/timelinewidget.h rename to ui/timelineview.h index bb61279fc..539f291a2 100644 --- a/ui/timelinewidget.h +++ b/ui/timelineview.h @@ -40,12 +40,10 @@ class Timeline; bool same_sign(int a, int b); void draw_waveform(ClipPtr clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); -class TimelineWidget : public QWidget { +class TimelineView : public QWidget { Q_OBJECT public: - explicit TimelineWidget(QWidget *parent); - - void SetTracks(QVector& tracks); + explicit TimelineView(QWidget *parent); QScrollBar* scrollBar; bool bottom_align; diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index c78309f6b..a6ce74f59 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -55,7 +55,7 @@ extern "C" { #include "project/media.h" #include "ui/viewercontainer.h" #include "rendering/cacher.h" -#include "ui/timelinewidget.h" +#include "ui/timelineview.h" #include "rendering/renderfunctions.h" #include "rendering/renderthread.h" #include "rendering/shadergenerators.h" From f40b744dd44c61abf8f667b9673a42bdb5b02cae Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 31 Mar 2019 10:36:01 +1100 Subject: [PATCH 063/133] further work on timeline rewrite --- panels/effectcontrols.cpp | 4 +- panels/project.cpp | 15 +- panels/timeline.cpp | 485 ++++++--------------------------- panels/timeline.h | 15 +- panels/viewer.cpp | 52 +++- panels/viewer.h | 3 +- project/footage.cpp | 15 +- timeline/clip.cpp | 34 ++- timeline/ghost.h | 5 +- timeline/sequence.cpp | 289 ++++++++++++++++++-- timeline/sequence.h | 12 +- timeline/timelinefunctions.cpp | 28 +- timeline/timelinefunctions.h | 14 +- timeline/track.cpp | 98 ++++++- timeline/track.h | 11 +- timeline/tracklist.cpp | 45 ++- timeline/tracklist.h | 4 + ui/timelinearea.cpp | 23 +- ui/timelinearea.h | 3 + ui/timelineview.cpp | 2 + ui/timelineview.h | 2 +- undo/undo.cpp | 21 +- undo/undo.h | 13 +- 23 files changed, 660 insertions(+), 533 deletions(-) diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 08bc48629..f386c87f3 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -639,10 +639,10 @@ void EffectControls::Load() { QVBoxLayout* layout; - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { vcontainer->setVisible(true); layout = video_effect_layout; - } else { + } else if (c->type() == Track::kTypeAudio) { acontainer->setVisible(true); layout = audio_effect_layout; } diff --git a/panels/project.cpp b/panels/project.cpp index 1c423a0ef..0e0c614ac 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -449,7 +449,7 @@ void Project::delete_selected_media() { } } if (confirm_delete) { - ca->append(new DeleteClipAction(s, k)); + ca->append(new DeleteClipAction(c)); } } } @@ -589,13 +589,12 @@ void Project::delete_clips_using_selected_media() { QVector sequence_clips = olive::ActiveSequence->GetAllClips(); for (int i=0;imedia() == m) { - ca->append(new DeleteClipAction(olive::ActiveSequence.get(), i)); - deleted = true; - } + + for (int j=0;jmedia() == m) { + ca->append(new DeleteClipAction(c)); + deleted = true; } } } diff --git a/panels/timeline.cpp b/panels/timeline.cpp index b0858baee..df96b89ce 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "global/global.h" #include "panels/panels.h" @@ -90,9 +91,7 @@ Timeline::Timeline(QWidget *parent) : headers->viewer = panel_sequence_viewer; - video_area->bottom_align = true; - video_area->scrollBar = videoScrollbar; - audio_area->scrollBar = audioScrollbar; + video_area->SetAlignment(olive::timeline::kAlignmentBottom); tool_buttons.append(toolArrowButton); tool_buttons.append(toolEditButton); @@ -103,11 +102,21 @@ Timeline::Timeline(QWidget *parent) : tool_buttons.append(toolTransitionButton); tool_buttons.append(toolHandButton); + tool_button_group = new QButtonGroup(this); + tool_button_group->addButton(toolArrowButton); + tool_button_group->addButton(toolEditButton); + tool_button_group->addButton(toolRippleButton); + tool_button_group->addButton(toolRazorButton); + tool_button_group->addButton(toolSlipButton); + tool_button_group->addButton(toolSlideButton); + tool_button_group->addButton(toolTransitionButton); + tool_button_group->addButton(toolHandButton); + toolArrowButton->click(); connect(horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(setScroll(int))); - connect(videoScrollbar, SIGNAL(valueChanged(int)), video_area, SLOT(setScroll(int))); - connect(audioScrollbar, SIGNAL(valueChanged(int)), audio_area, SLOT(setScroll(int))); + //connect(videoScrollbar, SIGNAL(valueChanged(int)), video_area, SLOT(setScroll(int))); + //connect(audioScrollbar, SIGNAL(valueChanged(int)), audio_area, SLOT(setScroll(int))); connect(horizontalScrollBar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); update_sequence(); @@ -133,91 +142,6 @@ void Timeline::Retranslate() { UpdateTitle(); } -void Timeline::split_clip_at_positions(ComboAction* ca, int clip_index, QVector positions) { - - QVector pre_splits; - - // Add the clip and each of its links to the pre_splits array - Clip* clip = olive::ActiveSequence->clips.at(clip_index).get(); - pre_splits.append(clip_index); - for (int i=0;ilinked.size();i++) { - pre_splits.append(clip->linked.at(i)); - } - - std::sort(positions.begin(), positions.end()); - - // Remove any duplicate positions - for (int i=1;i > post_splits(positions.size()); - - for (int i=positions.size()-1;i>=0;i--) { - - post_splits[i].resize(pre_splits.size()); - - for (int j=0;jset_timeline_out(positions.at(i+1)); - } - } - } - - for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i])); - } - -} - -void Timeline::previous_cut() { - if (olive::ActiveSequence != nullptr - && olive::ActiveSequence->playhead > 0) { - long p_cut = 0; - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_out() > p_cut && c->timeline_out() < olive::ActiveSequence->playhead) { - p_cut = c->timeline_out(); - } else if (c->timeline_in() > p_cut && c->timeline_in() < olive::ActiveSequence->playhead) { - p_cut = c->timeline_in(); - } - } - } - panel_sequence_viewer->seek(p_cut); - } -} - -void Timeline::next_cut() { - if (olive::ActiveSequence != nullptr) { - bool seek_enabled = false; - long n_cut = LONG_MAX; - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_in() < n_cut && c->timeline_in() > olive::ActiveSequence->playhead) { - n_cut = c->timeline_in(); - seek_enabled = true; - } else if (c->timeline_out() < n_cut && c->timeline_out() > olive::ActiveSequence->playhead) { - n_cut = c->timeline_out(); - seek_enabled = true; - } - } - } - if (seek_enabled) panel_sequence_viewer->seek(n_cut); - } -} - void ripple_clips(ComboAction* ca, Sequence* s, long point, long length, const QVector& ignore) { ca->append(new RippleAction(s, point, length, ignore)); } @@ -227,7 +151,7 @@ void Timeline::toggle_show_all() { showing_all = !showing_all; if (showing_all) { old_zoom = zoom; - set_zoom_value(double(timeline_area->width() - 200) / double(olive::ActiveSequence->getEndFrame())); + set_zoom_value(double(timeline_area->width() - 200) / double(olive::ActiveSequence->GetEndFrame())); } else { set_zoom_value(old_zoom); } @@ -302,7 +226,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jaudio_tracks.size();j++) { if (m->audio_tracks.at(j).enabled) { - g.track = j; + g.track = seq->GetTrackList(Track::kTypeAudio)->First() + j; g.media_stream = m->audio_tracks.at(j).file_index; ghosts.append(g); audio_ghosts = true; @@ -314,7 +238,7 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jvideo_tracks.size();j++) { if (m->video_tracks.at(j).enabled) { - g.track = -1-j; + g.track = seq->GetTrackList(Track::kTypeVideo)->First() + j; g.media_stream = m->video_tracks.at(j).file_index; ghosts.append(g); video_ghosts = true; @@ -331,13 +255,13 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector if (import_data.type() == olive::timeline::kImportVideoOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = -1; + g.track = seq->GetTrackList(Track::kTypeVideo)->First(); ghosts.append(g); } if (import_data.type() == olive::timeline::kImportAudioOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = 0; + g.track = seq->GetTrackList(Track::kTypeAudio)->First(); ghosts.append(g); } @@ -401,15 +325,15 @@ void Timeline::add_clips_from_ghosts(ComboAction* ca, Sequence* s) { for (int j=0;jmedia() == cc->media()) { - c->linked.append(j); + c->linked.append(cc.get()); } } if (olive::CurrentConfig.add_default_effects_to_clips) { - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { // add default video effects c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); - } else { + } else if (c->type() == Track::kTypeAudio) { // add default audio effects c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); @@ -428,26 +352,30 @@ void Timeline::add_transition() { ComboAction* ca = new ComboAction(); bool adding = false; - for (int i=0;iclips.size();i++) { - Clip* c = olive::ActiveSequence->clips.at(i).get(); - if (c != nullptr && c->IsSelected()) { - int transition_to_add = (c->track() < 0) ? TRANSITION_INTERNAL_CROSSDISSOLVE : TRANSITION_INTERNAL_LINEARFADE; - if (c->opening_transition == nullptr) { - ca->append(new AddTransitionCommand(c, - nullptr, - nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), - olive::CurrentConfig.default_transition_length)); - adding = true; - } - if (c->closing_transition == nullptr) { - ca->append(new AddTransitionCommand(nullptr, - c, - nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), - olive::CurrentConfig.default_transition_length)); - adding = true; - } + QVector selected_clips = olive::ActiveSequence->SelectedClips(); + + for (int i=0;itype() == Track::kTypeVideo) ? TRANSITION_INTERNAL_CROSSDISSOLVE + : TRANSITION_INTERNAL_LINEARFADE; + + if (c->opening_transition == nullptr) { + ca->append(new AddTransitionCommand(c, + nullptr, + nullptr, + Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); + adding = true; + } + + if (c->closing_transition == nullptr) { + ca->append(new AddTransitionCommand(nullptr, + c, + nullptr, + Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + olive::CurrentConfig.default_transition_length)); + adding = true; } } @@ -463,7 +391,7 @@ void Timeline::add_transition() { void Timeline::nest() { if (olive::ActiveSequence != nullptr) { // get selected clips - QVector selected_clips = olive::ActiveSequence->SelectedClipIndexes(); + QVector selected_clips = olive::ActiveSequence->SelectedClips(); // nest them if (!selected_clips.isEmpty()) { @@ -471,7 +399,7 @@ void Timeline::nest() { // get earliest point in selected clips long earliest_point = LONG_MAX; for (int i=0;iclips.at(selected_clips.at(i))->timeline_in(), earliest_point); + earliest_point = qMin(selected_clips.first()->timeline_in(), earliest_point); } ComboAction* ca = new ComboAction(); @@ -479,30 +407,37 @@ void Timeline::nest() { // create "nest" sequence with the same attributes as the current sequence SequencePtr s = std::make_shared(); - s->name = panel_project->get_next_sequence_name(tr("Nested Sequence")); + s->name = olive::project_model.GetNextSequenceName(tr("Nested Sequence")); s->width = olive::ActiveSequence->width; s->height = olive::ActiveSequence->height; s->frame_rate = olive::ActiveSequence->frame_rate; s->audio_frequency = olive::ActiveSequence->audio_frequency; s->audio_layout = olive::ActiveSequence->audio_layout; + QVector new_clips; + // copy all selected clips to the nest for (int i=0;iappend(new DeleteClipAction(olive::ActiveSequence.get(), selected_clips.at(i))); + ca->append(new DeleteClipAction(c)); // copy to new - ClipPtr copy(olive::ActiveSequence->clips.at(selected_clips.at(i))->copy(s.get())); + Track* track = s->GetTrackList(c->type())->TrackAt(c->track()->Index()); + ClipPtr copy = selected_clips.at(i)->copy(track); copy->set_timeline_in(copy->timeline_in() - earliest_point); copy->set_timeline_out(copy->timeline_out() - earliest_point); - s->clips.append(copy); + track->AddClip(copy); + + new_clips.append(copy); } // relink clips in new nested sequences - relink_clips_using_ids(selected_clips, s->clips); + olive::timeline::RelinkClips(selected_clips, new_clips); // add sequence to project - MediaPtr m = panel_project->create_sequence_internal(ca, s, false, nullptr); + MediaPtr m = olive::project_model.CreateSequence(ca, s, false, nullptr); // add nested sequence to active sequence QVector media_list; @@ -510,9 +445,10 @@ void Timeline::nest() { create_ghosts_from_media(olive::ActiveSequence.get(), earliest_point, media_list); // ensure ghosts won't overlap anything - for (int j=0;jclips.size();j++) { - Clip* c = olive::ActiveSequence->clips.at(j).get(); - if (c != nullptr && !selected_clips.contains(j)) { + QVector all_sequence_clips = olive::ActiveSequence->GetAllClips(); + for (int j=0;jtrack() == g.track @@ -520,14 +456,14 @@ void Timeline::nest() { && c->timeline_out() < g.in) || (c->timeline_in() > g.out && c->timeline_out() > g.out))) { - // There's a clip occupied by the space taken up by this ghost. Move up/down a track, and seek again - if (g.track < 0) { - g.track--; - } else { - g.track++; - } + + // There's a clip occupied by the space taken up by this ghost. Move up a track, and seek again. + g.track = g.track->track_list()->TrackAt(g.track->Index() + 1); + + // Restart entire loop again j = -1; break; + } } } @@ -538,7 +474,7 @@ void Timeline::nest() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - olive::ActiveSequence->selections.clear(); + olive::ActiveSequence->ClearSelections(); olive::UndoStack.push(ca); @@ -551,7 +487,7 @@ void Timeline::update_sequence() { bool null_sequence = (olive::ActiveSequence == nullptr); for (int i=0;isetEnabled(!null_sequence); + tool_buttons.at(i)->setEnabled(!null_sequence); } snappingButton->setEnabled(!null_sequence); zoomInButton->setEnabled(!null_sequence); @@ -563,7 +499,7 @@ void Timeline::update_sequence() { UpdateTitle(); } -int Timeline::get_snap_range() { +long Timeline::get_snap_range() { return getFrameFromScreenPoint(zoom, 10); } @@ -583,7 +519,7 @@ void Timeline::repaint_timeline() { // auto scroll if (olive::CurrentConfig.autoscroll == olive::AUTOSCROLL_PAGE_SCROLL) { int playhead_x = getTimelineScreenPointFromFrame(olive::ActiveSequence->playhead); - if (playhead_x < 0 || playhead_x > (editAreas->width() - videoScrollbar->width())) { + if (playhead_x < 0 || playhead_x > (editAreas->width())) { horizontalScrollBar->setValue(getScreenPointFromFrame(zoom, olive::ActiveSequence->playhead)); draw = false; } @@ -611,7 +547,7 @@ void Timeline::repaint_timeline() { void Timeline::select_all() { if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->selections.clear(); + olive::ActiveSequence->ClearSelections(); for (int i=0;iclips.size();i++) { ClipPtr c = olive::ActiveSequence->clips.at(i); if (c != nullptr) { @@ -864,12 +800,6 @@ void Timeline::multiply_zoom(double m) { set_zoom_value(zoom * m); } -void Timeline::decheck_tool_buttons(QObject* sender) { - for (int i=0;isetChecked(tool_buttons.at(i) == sender); - } -} - void Timeline::zoom_in() { multiply_zoom(2.0); } @@ -928,83 +858,7 @@ void Timeline::snapping_clicked(bool checked) { snapping = checked; } -ClipPtr Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame) { - return split_clip(ca, transitions, p, frame, frame); -} - -ClipPtr Timeline::split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in) { - Clip* pre = olive::ActiveSequence->clips.at(p).get(); - if (pre != nullptr) { - - if (pre->timeline_in() < frame && pre->timeline_out() > frame) { - // duplicate clip without duplicating its transitions, we'll restore them later - - ClipPtr post = pre->copy(olive::ActiveSequence.get()); - - long new_clip_length = frame - pre->timeline_in(); - - post->set_timeline_in(post_in); - post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); - - pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); - - if (transitions) { - - // check if this clip has a closing transition - if (pre->closing_transition != nullptr) { - - // if so, move closing transition to the post clip - post->closing_transition = pre->closing_transition; - - // and set the original clip's closing transition to nothing - ca->append(new SetPointer(reinterpret_cast(&pre->closing_transition), nullptr)); - - // and set the transition's reference to the post clip - if (post->closing_transition->parent_clip == pre) { - ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->parent_clip), post.get())); - } - if (post->closing_transition->secondary_clip == pre) { - ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->secondary_clip), post.get())); - } - - // and make sure it's at the correct size to the closing clip - if (post->closing_transition != nullptr && post->closing_transition->get_true_length() > post->length()) { - ca->append(new ModifyTransitionCommand(post->closing_transition, post->length())); - post->closing_transition->set_length(post->length()); - } - - } - - // we're keeping the opening clip, so ensure that's a correct size too - if (pre->opening_transition != nullptr && pre->opening_transition->get_true_length() > new_clip_length) { - ca->append(new ModifyTransitionCommand(pre->opening_transition, new_clip_length)); - } - } - - return post; - - } else if (frame == pre->timeline_in() - && pre->opening_transition != nullptr - && pre->opening_transition->secondary_clip != nullptr) { - // special case for shared transitions to split it into two - - // set transition to single-clip mode - ca->append(new SetPointer(reinterpret_cast(&pre->opening_transition->secondary_clip), nullptr)); - - // clone transition for other clip - ca->append(new AddTransitionCommand(nullptr, - pre->opening_transition->secondary_clip, - pre->opening_transition, - nullptr, - 0) - ); - - } - - } - return nullptr; -} - +/* bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool relink) { Clip* c = olive::ActiveSequence->clips.at(clip).get(); if (c != nullptr) { @@ -1045,121 +899,9 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool } return false; } +*/ -void Timeline::clean_up_selections(QVector& areas) { - for (int i=0;i ss.out) { - // do nothing - } else if (s.in >= ss.in && s.out <= ss.out) { - remove = true; - } else if (s.in <= ss.out && s.out > ss.out) { - ss.out = s.out; - remove = true; - } else if (s.out >= ss.in && s.in < ss.in) { - ss.in = s.in; - remove = true; - } - if (remove) { - areas.removeAt(i); - i--; - break; - } - } - } - } - } -} -bool selection_contains_transition(const Selection& s, Clip* c, int type) { - if (type == kTransitionOpening) { - return c->opening_transition != nullptr - && s.out == c->timeline_in() + c->opening_transition->get_true_length() - && ((c->opening_transition->secondary_clip == nullptr && s.in == c->timeline_in()) - || (c->opening_transition->secondary_clip != nullptr && s.in == c->timeline_in() - c->opening_transition->get_true_length())); - } else { - return c->closing_transition != nullptr - && s.in == c->timeline_out() - c->closing_transition->get_true_length() - && ((c->closing_transition->secondary_clip == nullptr && s.out == c->timeline_out()) - || (c->closing_transition->secondary_clip != nullptr && s.out == c->timeline_out() + c->closing_transition->get_true_length())); - } -} - -void Timeline::delete_areas_and_relink(ComboAction* ca, QVector& areas, bool deselect_areas) { - clean_up_selections(areas); - - panel_graph_editor->set_row(nullptr); - panel_effect_controls->Clear(true); - - QVector pre_clips; - QVector post_clips; - - for (int i=0;iclips.size();j++) { - Clip* c = olive::ActiveSequence->clips.at(j).get(); - if (c != nullptr && c->track() == s.track && !c->undeletable) { - if (selection_contains_transition(s, c, kTransitionOpening)) { - // delete opening transition - ca->append(new DeleteTransitionCommand(c->opening_transition)); - } else if (selection_contains_transition(s, c, kTransitionClosing)) { - // delete closing transition - ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { - // clips falls entirely within deletion area - ca->append(new DeleteClipAction(olive::ActiveSequence.get(), j)); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { - // middle of clip is within deletion area - - // duplicate clip - ClipPtr post = split_clip(ca, true, j, s.in, s.out); - - pre_clips.append(j); - post_clips.append(post); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { - // only out point is in deletion area - c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); - - if (c->closing_transition != nullptr) { - if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { - ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else { - ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); - } - } - } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { - // only in point is in deletion area - c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); - - if (c->opening_transition != nullptr) { - if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { - ca->append(new DeleteTransitionCommand(c->opening_transition)); - } else { - ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); - } - } - } - } - } - } - - // deselect selected clip areas - if (deselect_areas) { - QVector area_copy = areas; - for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); -} void Timeline::copy(bool del) { bool cleared = false; @@ -1220,23 +962,6 @@ void Timeline::copy(bool del) { } } -void Timeline::relink_clips_using_ids(QVector& old_clips, QVector& new_clips) { - // relink pasted clips - for (int i=0;iclips.at(old_clips.at(i)); - for (int j=0;jlinked.size();j++) { - for (int k=0;klinked.at(j) == old_clips.at(k)) { - if (new_clips.at(i) != nullptr) { - new_clips.at(i)->linked.append(k); - } - } - } - } - } -} - void Timeline::paste(bool insert) { if (clipboard.size() > 0) { if (clipboard_type == CLIPBOARD_TYPE_CLIP) { @@ -1536,20 +1261,6 @@ bool Timeline::split_selection(ComboAction* ca) { return false; } -bool Timeline::split_all_clips_at_point(ComboAction* ca, long point) { - bool split = false; - for (int j=0;jclips.size();j++) { - ClipPtr c = olive::ActiveSequence->clips.at(j); - if (c != nullptr) { - // always relinks - if (split_clip_and_relink(ca, j, point, true)) { - split = true; - } - } - } - return split; -} - void Timeline::split_at_playhead() { ComboAction* ca = new ComboAction(); bool split_selected = false; @@ -1904,7 +1615,6 @@ void Timeline::transition_menu_select(QAction* a) { transition_tool_side = 1; } - decheck_tool_buttons(sender()); timeline_area->setCursor(Qt::CrossCursor); tool = TIMELINE_TOOL_TRANSITION; toolTransitionButton->setChecked(true); @@ -2052,41 +1762,11 @@ void Timeline::setup_ui() { splitter->setChildrenCollapsible(false); splitter->setOrientation(Qt::Vertical); - QWidget* videoContainer = new QWidget(); + video_area = new TimelineArea(); + splitter->addWidget(video_area); - QHBoxLayout* videoContainerLayout = new QHBoxLayout(videoContainer); - videoContainerLayout->setSpacing(0); - videoContainerLayout->setContentsMargins(0, 0, 0, 0); - - video_area = new TimelineView(); - video_area->setFocusPolicy(Qt::ClickFocus); - videoContainerLayout->addWidget(video_area); - - videoScrollbar = new QScrollBar(); - videoScrollbar->setMaximum(0); - videoScrollbar->setSingleStep(20); - videoScrollbar->setOrientation(Qt::Vertical); - videoContainerLayout->addWidget(videoScrollbar); - - splitter->addWidget(videoContainer); - - QWidget* audioContainer = new QWidget(); - QHBoxLayout* audioContainerLayout = new QHBoxLayout(audioContainer); - audioContainerLayout->setSpacing(0); - audioContainerLayout->setContentsMargins(0, 0, 0, 0); - - audio_area = new TimelineView(); - audio_area->setFocusPolicy(Qt::ClickFocus); - - audioContainerLayout->addWidget(audio_area); - - audioScrollbar = new QScrollBar(); - audioScrollbar->setMaximum(0); - audioScrollbar->setOrientation(Qt::Vertical); - - audioContainerLayout->addWidget(audioScrollbar); - - splitter->addWidget(audioContainer); + audio_area = new TimelineArea(); + splitter->addWidget(audio_area); editAreaLayout->addWidget(splitter); @@ -2111,7 +1791,6 @@ void Timeline::setup_ui() { void Timeline::set_tool() { QPushButton* button = static_cast(sender()); - decheck_tool_buttons(button); tool = button->property("tool").toInt(); creating = false; switch (tool) { diff --git a/panels/timeline.h b/panels/timeline.h index c6c8915e9..45cc380ad 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -54,16 +54,9 @@ public: virtual bool focused() override; void multiply_zoom(double m); void copy(bool del); - ClipPtr split_clip(ComboAction* ca, bool transitions, int p, long frame); - ClipPtr split_clip(ComboAction* ca, bool transitions, int p, long frame, long post_in); - bool split_selection(ComboAction* ca); - bool split_all_clips_at_point(ComboAction *ca, long point); - bool split_clip_and_relink(ComboAction* ca, int clip, long frame, bool relink); - void split_clip_at_positions(ComboAction* ca, int clip_index, QVector positions); void clean_up_selections(QVector& areas); void deselect_area(long in, long out, int track); void delete_areas_and_relink(ComboAction *ca, QVector& areas, bool deselect_areas); - void relink_clips_using_ids(QVector& old_clips, QVector& new_clips); void update_sequence(); void edit_to_point_internal(bool in, bool ripple); @@ -77,7 +70,7 @@ public: int getDisplayScreenPointFromFrame(long frame); long getDisplayFrameFromScreenPoint(int x); - int get_snap_range(); + long get_snap_range(); bool snap_to_point(long point, long* l); bool snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea); void set_marker(); @@ -152,6 +145,8 @@ public: AudioMonitor* audio_monitor; ResizableScrollBar* horizontalScrollBar; + QVector tool_buttons; + QButtonGroup* tool_button_group; QPushButton* toolArrowButton; QPushButton* toolEditButton; QPushButton* toolRippleButton; @@ -216,8 +211,6 @@ private slots: private: void ChangeTrackHeightUniformly(int diff); void set_zoom_value(double v); - QVector tool_buttons; - void decheck_tool_buttons(QObject* sender); void set_tool(int tool); int scroll; void set_sb_max(); @@ -233,8 +226,6 @@ private: TimelineArea* video_area; TimelineArea* audio_area; QWidget* editAreas; - QScrollBar* videoScrollbar; - QScrollBar* audioScrollbar; QPushButton* zoomInButton; QPushButton* zoomOutButton; QPushButton* recordButton; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index afe282de2..04e9187e8 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -348,13 +348,12 @@ void Viewer::pause() { // Make a clip out of it and add it to the Sequence - ClipPtr c = std::make_shared(seq.get()); + ClipPtr c = std::make_shared(recording_track); c->set_media(m, 0); // latest media c->set_timeline_in(recording_start); c->set_timeline_out(recording_start + f->get_length_in_frames(seq->frame_rate)); c->set_clip_in(0); - c->set_track(recording_track); c->set_color(128, 192, 128); c->set_name(m->get_name()); @@ -382,7 +381,7 @@ void Viewer::update_header_zoom() { if (seq != nullptr) { long sequenceEndFrame = seq->GetEndFrame(); if (cached_end_frame != sequenceEndFrame) { - minimum_zoom = (sequenceEndFrame > 0) ? ((double) headers->width() / (double) sequenceEndFrame) : 1; + minimum_zoom = (sequenceEndFrame > 0) ? (double(headers->width()) / double(sequenceEndFrame)) : 1; headers->update_zoom(qMax(headers->get_zoom(), minimum_zoom)); set_sb_max(); viewer_widget_->waveform_zoom = headers->get_zoom(); @@ -431,6 +430,51 @@ void Viewer::update_viewer() { update_end_timecode(); } +void Viewer::prev_cut() +{ + if (seq != nullptr + && seq->playhead > 0) { + + QVector sequence_clips = olive::ActiveSequence->GetAllClips(); + + long p_cut = 0; + for (int i=0;itimeline_out() > p_cut && c->timeline_out() < seq->playhead) { + p_cut = c->timeline_out(); + } else if (c->timeline_in() > p_cut && c->timeline_in() < seq->playhead) { + p_cut = c->timeline_in(); + } + } + panel_sequence_viewer->seek(p_cut); + } +} + +void Viewer::next_cut() +{ + if (seq != nullptr) { + + QVector sequence_clips = seq->GetAllClips(); + + bool seek_enabled = false; + long n_cut = LONG_MAX; + for (int i=0;itimeline_in() < n_cut && c->timeline_in() > seq->playhead) { + n_cut = c->timeline_in(); + seek_enabled = true; + } else if (c->timeline_out() < n_cut && c->timeline_out() > seq->playhead) { + n_cut = c->timeline_out(); + seek_enabled = true; + } + } + + if (seek_enabled) { + panel_sequence_viewer->seek(n_cut); + } + } +} + void Viewer::initiate_drag(olive::timeline::MediaImportType drag_type) { // FIXME: This should contain actual metadata rather than fake metadata @@ -703,7 +747,7 @@ void Viewer::set_media(Media* m) { new_sequence->frame_rate = video_stream.video_frame_rate * footage->speed; } - ClipPtr c = std::make_shared(new_sequence.get()); + ClipPtr c = std::make_shared(new_sequence->GetTrackList(Track::kTypeVideo)->First()); c->set_media(media, video_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate)); diff --git a/panels/viewer.h b/panels/viewer.h index fb5835737..e5659ea4c 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -115,7 +115,8 @@ public slots: void go_to_end(); void close_media(); void update_viewer(); - + void prev_cut(); + void next_cut(); private slots: diff --git a/project/footage.cpp b/project/footage.cpp index 8048e9a0c..9e4dc17f2 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -54,7 +54,7 @@ void Footage::Save(QXmlStreamWriter &stream) QDir proj_dir = QFileInfo(olive::ActiveProjectFilename).absoluteDir(); stream.writeStartElement("footage"); - stream.writeAttribute("id", QString::number(media_id)); + stream.writeAttribute("id", QString::number(save_id)); stream.writeAttribute("name", name); stream.writeAttribute("url", proj_dir.relativeFilePath(url)); stream.writeAttribute("duration", QString::number(length)); @@ -70,8 +70,8 @@ void Footage::Save(QXmlStreamWriter &stream) stream.writeAttribute("proxypath", proxy_path); // save video stream metadata - for (int j=0;jvideo_tracks.size();j++) { - const FootageStream& ms = f->video_tracks.at(j); + for (int j=0;jaudio_tracks.size();j++) { - const FootageStream& ms = f->audio_tracks.at(j); + for (int j=0;jmarkers.size();j++) { - f->markers.at(j).Save(stream); + for (int j=0;jset_clip_in(clip_in()); copy->set_timeline_in(timeline_in()); copy->set_timeline_out(timeline_out()); - copy->set_track(track()); copy->set_color(color()); copy->set_media(media(), media_stream_index()); copy->set_autoscaled(autoscaled()); @@ -75,7 +74,7 @@ ClipPtr Clip::copy(Track* s) { copy->effects.append(effects.at(i)->copy(copy.get())); } - copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->frame_rate); + copy->set_cached_frame_rate((this->track_ == nullptr) ? cached_frame_rate() : this->track_->sequence()->frame_rate); copy->refresh(); @@ -207,13 +206,13 @@ void Clip::reset_audio() { cacher.ResetAudio(); } if (media() != nullptr && media()->get_type() == MEDIA_TYPE_SEQUENCE) { - Sequence* nested_sequence = media()->to_sequence().get(); - for (int i=0;iclips.size();i++) { - Clip* c = nested_sequence->clips.at(i).get(); - if (c != nullptr) { - c->reset_audio(); - } + + QVector nested_sequence_clips = media()->to_sequence()->GetAllClips(); + + for (int i=0;ireset_audio(); } + } } @@ -266,7 +265,6 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeAttribute("clipin", QString::number(clip_in())); stream.writeAttribute("in", QString::number(timeline_in())); stream.writeAttribute("out", QString::number(timeline_out())); - stream.writeAttribute("track", QString::number(track())); stream.writeAttribute("r", QString::number(color().red())); stream.writeAttribute("g", QString::number(color().green())); @@ -277,9 +275,9 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeAttribute("maintainpitch", QString::number(speed().maintain_audio_pitch)); stream.writeAttribute("reverse", QString::number(reversed())); - if (c->media() != nullptr) { + if (media() != nullptr) { stream.writeAttribute("type", QString::number(media()->get_type())); - switch (c->media()->get_type()) { + switch (media()->get_type()) { case MEDIA_TYPE_FOOTAGE: stream.writeAttribute("media", QString::number(media()->to_footage()->save_id)); stream.writeAttribute("stream", QString::number(media_stream_index())); @@ -302,7 +300,7 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeStartElement("linked"); // linked for (int k=0;kload_id)); stream.writeEndElement(); // link } stream.writeEndElement(); // linked @@ -457,13 +455,13 @@ double Clip::media_frame_rate() { double rate = media_->get_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; } - if (sequence != nullptr) return sequence->frame_rate; + if (track() != nullptr) return track()->sequence()->frame_rate; return qSNaN(); } long Clip::media_length() { - if (this->sequence != nullptr) { - double fr = this->sequence->frame_rate; + if (this->track() != nullptr) { + double fr = this->track()->sequence()->frame_rate; fr /= speed_.value; @@ -474,7 +472,7 @@ long Clip::media_length() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media_->to_footage(); - const FootageStream* ms = m->get_stream_from_file_index(track_ < 0, media_stream_index()); + const FootageStream* ms = m->get_stream_from_file_index(type() == Track::kTypeVideo, media_stream_index()); if (ms != nullptr && ms->infinite_length) { return LONG_MAX; } else { @@ -493,13 +491,13 @@ long Clip::media_length() { } int Clip::media_width() { - if (media_ == nullptr && sequence != nullptr) return sequence->width; + if (media_ == nullptr && track() != nullptr) return track()->sequence()->width; switch (media_->get_type()) { case MEDIA_TYPE_FOOTAGE: { const FootageStream* ms = media_stream(); if (ms != nullptr) return ms->video_width; - if (sequence != nullptr) return sequence->width; + if (track() != nullptr) return track()->sequence()->width; break; } case MEDIA_TYPE_SEQUENCE: diff --git a/timeline/ghost.h b/timeline/ghost.h index 4ee9a3806..cf89b6412 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -3,17 +3,18 @@ #include "effects/transition.h" #include "timelinefunctions.h" +#include "track.h" struct Ghost { int clip; long in; long out; - int track; + Track* track; long clip_in; long old_in; long old_out; - int old_track; + Track* old_track; long old_clip_in; // importing variables diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index ae474e07d..8f31a783e 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -33,10 +33,10 @@ Sequence::Sequence() : wrapper_sequence(false) { // Set up tracks - track_lists.resize(Track::kTypeCount); + track_lists_.resize(Track::kTypeCount); - for (int i=0;i(this, i); + for (int i=0;i(i)); } } @@ -50,8 +50,8 @@ SequencePtr Sequence::copy() { s->audio_layout = audio_layout; // deep copy all of the sequence's clips - for (int i=0;itrack_lists[i] = track_lists.at(i).copy(s.get()); + for (int i=0;itrack_lists_[i] = track_lists_.at(i)->copy(s.get()); } // copy all of the sequence's markers @@ -62,6 +62,12 @@ SequencePtr Sequence::copy() { void Sequence::Save(QXmlStreamWriter &stream) { + // Provide unique IDs for each Clip + QVector all_clips = GetAllClips(); + for (int i=0;iload_id = i; + } + stream.writeStartElement("sequence"); stream.writeAttribute("id", QString::number(save_id)); stream.writeAttribute("name", name); @@ -80,22 +86,28 @@ void Sequence::Save(QXmlStreamWriter &stream) QVector transition_save_cache; QVector transition_clip_save_cache; - for (int j=0;jSave(stream); + for (int j=0;jSave(stream); } - for (int j=0;jGetEndFrame(), end_frame); + for (int j=0;jTrackCount();i++) { + end_frame = qMax(track_list->TrackAt(i)->GetEndFrame(), end_frame); + } + } return end_frame; @@ -105,8 +117,14 @@ QVector Sequence::GetAllClips() { QVector all_clips; - for (int i=0;iGetAllClips()); + for (int j=0;jTrackCount();i++) { + all_clips.append(track_list->TrackAt(i)->GetAllClips()); + } + } return all_clips; @@ -114,7 +132,7 @@ QVector Sequence::GetAllClips() TrackList *Sequence::GetTrackList(Track::Type type) { - return track_lists.at(type).get(); + return track_lists_.at(type); } void Sequence::Close() @@ -143,20 +161,158 @@ void Sequence::RefreshClipsUsingMedia(Media *m) { QVector Sequence::SelectedClips(bool containing) { - QVector all_clips = GetAllClips(); - QVector selected_clips; - for (int i=0;iTrackCount();j++) { + Track* t = tl->TrackAt(j); + + selected_clips.append(t->GetAllClips()); } } return selected_clips; } +void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool deselect_areas) +{ + clean_up_selections(areas); + + panel_graph_editor->set_row(nullptr); + panel_effect_controls->Clear(true); + + QVector pre_clips; + QVector post_clips; + + QVector all_clips = GetAllClips(); + + for (int i=0;itrack() == s.track && !c->undeletable) { + if (selection_contains_transition(s, c, kTransitionOpening)) { + // delete opening transition + ca->append(new DeleteTransitionCommand(c->opening_transition)); + } else if (selection_contains_transition(s, c, kTransitionClosing)) { + // delete closing transition + ca->append(new DeleteTransitionCommand(c->closing_transition)); + } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { + // clips falls entirely within deletion area + ca->append(new DeleteClipAction(c)); + } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { + // middle of clip is within deletion area + + // duplicate clip + ClipPtr post = SplitClip(ca, true, c, s.in, s.out); + + pre_clips.append(j); + post_clips.append(post); + } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { + // only out point is in deletion area + c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); + + if (c->closing_transition != nullptr) { + if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { + ca->append(new DeleteTransitionCommand(c->closing_transition)); + } else { + ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); + } + } + } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { + // only in point is in deletion area + c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); + + if (c->opening_transition != nullptr) { + if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { + ca->append(new DeleteTransitionCommand(c->opening_transition)); + } else { + ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); + } + } + } + } + } + } + + // deselect selected clip areas + if (deselect_areas) { + QVector area_copy = areas; + for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); +} + +bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) +{ + bool split = false; + + QVector all_clips = GetAllClips(); + + for (int j=0;jIsActiveAt(point)) { + SplitClipAtPositions(ca, c, {point}, true); + split = true; + } + } + + return split; +} + +void Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) +{ + // Add the clip and each of its links to the pre_splits array + + QVector pre_splits; + pre_splits.append(clip); + + if (relink) { + for (int i=0;ilinked.size();i++) { + pre_splits.append(clip->linked.at(i)); + } + } + + std::sort(positions.begin(), positions.end()); + + // Remove any duplicate positions + for (int i=1;i > post_splits(positions.size()); + + for (int i=positions.size()-1;i>=0;i--) { + + post_splits[i].resize(pre_splits.size()); + + for (int j=0;jset_timeline_out(positions.at(i+1)); + } + } + } + + for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i])); + } +} + +/* QVector Sequence::SelectedClipIndexes() { QVector selected_clips; @@ -170,15 +326,17 @@ QVector Sequence::SelectedClipIndexes() return selected_clips; } +*/ Effect *Sequence::GetSelectedGizmo() { Effect* gizmo_ptr = nullptr; + QVector clips = GetAllClips(); + for (int i=0;iIsActiveAt(playhead) + Clip* c = clips.at(i); + if (c->IsActiveAt(playhead) && IsClipSelected(c, true)) { // This clip is selected and currently active - we'll use this for gizmos @@ -214,10 +372,93 @@ Effect *Sequence::GetSelectedGizmo() void Sequence::ClearSelections() { - for (int i=0;iClearSelections(); + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->ClearSelections(); + } } } +ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) +{ + return SplitClip(ca, transitions, pre, frame, frame); +} + +ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame, long post_in) +{ + if (pre == nullptr) { + return nullptr; + } + + if (pre->timeline_in() < frame && pre->timeline_out() > frame) { + // duplicate clip without duplicating its transitions, we'll restore them later + + ClipPtr post = pre->copy(pre->track()); + + long new_clip_length = frame - pre->timeline_in(); + + post->set_timeline_in(post_in); + post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); + + pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); + + if (transitions) { + + // check if this clip has a closing transition + if (pre->closing_transition != nullptr) { + + // if so, move closing transition to the post clip + post->closing_transition = pre->closing_transition; + + // and set the original clip's closing transition to nothing + ca->append(new SetPointer(reinterpret_cast(&pre->closing_transition), nullptr)); + + // and set the transition's reference to the post clip + if (post->closing_transition->parent_clip == pre) { + ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->parent_clip), post.get())); + } + if (post->closing_transition->secondary_clip == pre) { + ca->append(new SetPointer(reinterpret_cast(&post->closing_transition->secondary_clip), post.get())); + } + + // and make sure it's at the correct size to the closing clip + if (post->closing_transition != nullptr && post->closing_transition->get_true_length() > post->length()) { + ca->append(new ModifyTransitionCommand(post->closing_transition, post->length())); + post->closing_transition->set_length(post->length()); + } + + } + + // we're keeping the opening clip, so ensure that's a correct size too + if (pre->opening_transition != nullptr && pre->opening_transition->get_true_length() > new_clip_length) { + ca->append(new ModifyTransitionCommand(pre->opening_transition, new_clip_length)); + } + } + + return post; + + } else if (frame == pre->timeline_in() + && pre->opening_transition != nullptr + && pre->opening_transition->secondary_clip != nullptr) { + // special case for shared transitions to split it into two + + // set transition to single-clip mode + ca->append(new SetPointer(reinterpret_cast(&pre->opening_transition->secondary_clip), nullptr)); + + // clone transition for other clip + ca->append(new AddTransitionCommand(nullptr, + pre->opening_transition->secondary_clip, + pre->opening_transition, + nullptr, + 0) + ); + + } + + return nullptr; +} + // static variable for the currently active sequence SequencePtr olive::ActiveSequence = nullptr; diff --git a/timeline/sequence.h b/timeline/sequence.h index 0998858ef..5ae8cce88 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -63,7 +63,12 @@ public: void RefreshClipsUsingMedia(Media* m = nullptr); QVector SelectedClips(bool containing = true); - QVector SelectedClipIndexes(); + //QVector SelectedClipIndexes(); + + void DeleteAreas(); + + bool SplitAllClipsAtPoint(ComboAction *ca, long point); + void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); Effect* GetSelectedGizmo(); @@ -84,7 +89,10 @@ public: QVector markers; private: - QVector track_lists; + QVector track_lists_; + + ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame); + ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame, long post_in); }; using SequencePtr = std::shared_ptr; diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index b3f3b4b86..b70e6c08c 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -1,6 +1,30 @@ #include "timelinefunctions.h" -TimelineFunctions::TimelineFunctions() -{ + +void olive::timeline::RelinkClips(QVector &pre_clips, QVector &post_clips) +{ + // Loop through each "pre" clip + for (int i=0;ilinked.size();j++) { + + // Loop again through the "pre" clips to determine which are linked to each other + for (int l=0;llinked.at(j) == pre_clips.at(l)) { + + // Check if we have an equivalent in the post_clips, and link it if so + if (post_clips.at(l) != nullptr) { + post_clips.at(i)->linked.append(post_clips.at(l).get()); + } + + } + } + } + } } diff --git a/timeline/timelinefunctions.h b/timeline/timelinefunctions.h index 6cf5cb75e..23c6bbbdf 100644 --- a/timeline/timelinefunctions.h +++ b/timeline/timelinefunctions.h @@ -1,6 +1,10 @@ #ifndef TIMELINEFUNCTIONS_H #define TIMELINEFUNCTIONS_H +#include + +#include "timeline/clip.h" + namespace olive { namespace timeline { @@ -25,13 +29,9 @@ enum Alignment { kAlignmentSingle }; -} -} +void RelinkClips(QVector& pre_clips, QVector &post_clips); -class TimelineFunctions -{ -public: - TimelineFunctions(); -}; +} +} #endif // TIMELINEFUNCTIONS_H diff --git a/timeline/track.cpp b/timeline/track.cpp index 8a2f3fa31..088d09832 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -1,6 +1,8 @@ #include "track.h" #include "timeline/clip.h" +#include "timeline/tracklist.h" +#include "timeline/sequence.h" int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; @@ -30,20 +32,29 @@ Track *Track::copy(TrackList *parent) return t; } +Sequence *Track::sequence() +{ + return parent_->GetParent(); +} + +TrackList *Track::track_list() +{ + return parent_; +} + void Track::Save(QXmlStreamWriter &stream) { stream.writeStartElement("track"); for (int j=0;jSave(stream); + stream.writeStartElement("clip"); + stream.writeAttribute("id", QString::number(c->load_id)); - stream.writeEndElement(); // clip - } + c->Save(stream); + + stream.writeEndElement(); // clip } stream.writeEndElement(); // track @@ -93,9 +104,49 @@ void Track::ResizeClipArray(int new_size) clips_.resize(new_size); } -QVector Track::GetAllClips() +QVector Track::GetAllClips() { - return clips_; + QVector clips; + + clips.resize(clips_.size()); + for (int i=0;i Track::GetSelectedClips(bool containing) +{ + QVector selected_clips; + + for (int i=0;iIndexOfTrack(this); } bool Track::IsClipSelected(int clip_index, bool containing) @@ -157,6 +208,37 @@ bool Track::IsTransitionSelected(Transition *t) return false; } +void Track::TidySelections() +{ + for (int i=0;i ss.out) { + // do nothing + } else if (s.in >= ss.in && s.out <= ss.out) { + remove = true; + } else if (s.in <= ss.out && s.out > ss.out) { + ss.out = s.out; + remove = true; + } else if (s.out >= ss.in && s.in < ss.in) { + ss.in = s.in; + remove = true; + } + if (remove) { + areas.removeAt(i); + i--; + break; + } + } + } + } + } +} + void Track::ClearSelections() { selections_.clear(); diff --git a/timeline/track.h b/timeline/track.h index 4a947363e..e3d642e8f 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -43,6 +43,9 @@ public: Track(TrackList* parent, Type type); Track* copy(TrackList* parent); + Sequence* sequence(); + TrackList* track_list(); + void Save(QXmlStreamWriter& stream); Type type(); @@ -55,13 +58,17 @@ public: ClipPtr GetClip(int i); void RemoveClip(int i); void RemoveClip(Clip* c); - QVector GetAllClips(); - QVector GetSelectedClips(); + QVector GetAllClips(); + QVector GetSelectedClips(bool containing); + ClipPtr GetClipObjectFromRawPtr(Clip* c); + + int Index(); bool IsClipSelected(int clip_index, bool containing = true); bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void TidySelections(); void ClearSelections(); long GetEndFrame(); diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index 45bf5418b..8215121a0 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -1,5 +1,7 @@ #include "tracklist.h" +#include "timeline/sequence.h" + TrackList::TrackList(Sequence *parent, Track::Type type) : QObject(parent), type_(type) @@ -8,13 +10,24 @@ TrackList::TrackList(Sequence *parent, Track::Type type) : AddTrack(); } -TrackListPtr TrackList::copy(Sequence *parent) +void TrackList::Save(QXmlStreamWriter &stream) { - TrackListPtr t = std::make_shared(parent, type_); + stream.writeStartElement("Tracks"); + + for (int i=0;iSave(stream); + } + + stream.writeEndElement(); // Tracks +} + +TrackList* TrackList::copy(Sequence *parent) +{ + TrackList* t = new TrackList(parent, type_); t->ResizeTrackArray(tracks_.size()); for (int i=0;itracks_[i] = tracks_.at(i)->copy(t.get()); + t->tracks_[i] = tracks_.at(i)->copy(t); } return t; @@ -22,7 +35,7 @@ TrackListPtr TrackList::copy(Sequence *parent) void TrackList::AddTrack() { - TrackPtr track = std::make_shared(this, type_); + Track* track = new Track(this, type_); tracks_.append(track); } @@ -36,7 +49,7 @@ void TrackList::RemoveTrack(int i) Track *TrackList::First() { - return tracks_.first().get(); + return tracks_.first(); } int TrackList::TrackCount() @@ -44,7 +57,27 @@ int TrackList::TrackCount() return tracks_.size(); } -QVector TrackList::tracks() +int TrackList::IndexOfTrack(Track *track) +{ + for (int i=0;i= tracks_.size()) { + AddTrack(); + } + + return tracks_.at(i); +} + +QVector TrackList::tracks() { return tracks_; } diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 40975c55c..558114e30 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -10,10 +10,14 @@ public: TrackList(Sequence* parent, Track::Type type); TrackList* copy(Sequence* parent); + void Save(QXmlStreamWriter& stream); + void AddTrack(); void RemoveTrack(int i); Track* First(); int TrackCount(); + int IndexOfTrack(Track* track); + Track* TrackAt(int i); QVector tracks(); Sequence* GetParent(); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index ae3ac7c6f..7f290b425 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -1,9 +1,28 @@ #include "timelinearea.h" TimelineArea::TimelineArea() : - track_list_(nullptr) + track_list_(nullptr), + alignment_(olive::timeline::kAlignmentTop) { + QHBoxLayout* layout = new QHBoxLayout(this); + // LABELS + QWidget* label_container = new QWidget(); + QVBoxLayout* label_container_layout = new QVBoxLayout(label_container); + layout->addWidget(label_container); + + // VIEW + view_ = new TimelineView(); + layout->addWidget(view_); + + // SCROLLBAR + QScrollBar* scrollbar = new QScrollBar(Qt::Vertical); + layout->addWidget(scrollbar); +} + +void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) +{ + alignment_ = alignment; } void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) @@ -29,7 +48,7 @@ void TimelineArea::RefreshLabels() labels_.resize(track_list_->TrackCount()); for (int i=0;iTrackAt(i)); } } diff --git a/ui/timelinearea.h b/ui/timelinearea.h index 1e35b46f9..dd8fe0ccc 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -4,6 +4,7 @@ #include "timeline/sequence.h" #include "timeline/track.h" #include "timeline/tracklist.h" +#include "timeline/timelinefunctions.h" #include "ui/timelineview.h" #include "ui/timelinelabel.h" @@ -13,6 +14,7 @@ class TimelineArea : public QWidget public: TimelineArea(); + void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(Sequence* sequence, Track::Type track_list); public slots: void RefreshLabels(); @@ -20,6 +22,7 @@ private: TrackList* track_list_; TimelineView* view_; QVector labels_; + olive::timeline::Alignment alignment_; }; #endif // TIMELINEAREA_H diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 8616461ec..f9e8cd484 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -72,6 +72,8 @@ TimelineView::TimelineView(QWidget *parent) : QWidget(parent) { track_resizing = false; setMouseTracking(true); + setFocusPolicy(Qt::ClickFocus); + setAcceptDrops(true); setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/ui/timelineview.h b/ui/timelineview.h index 539f291a2..6cbf22773 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -43,7 +43,7 @@ void draw_waveform(ClipPtr clip, const FootageStream *ms, long media_length, QPa class TimelineView : public QWidget { Q_OBJECT public: - explicit TimelineView(QWidget *parent); + explicit TimelineView(QWidget *parent = nullptr); QScrollBar* scrollBar; bool bottom_align; diff --git a/undo/undo.cpp b/undo/undo.cpp index a8baada80..e562f7e77 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -90,14 +90,12 @@ void MoveClipAction::doRedo() { } } -DeleteClipAction::DeleteClipAction(Sequence *s, int clip) { - seq = s; - index = clip; - opening_transition = -1; - closing_transition = -1; -} +DeleteClipAction::DeleteClipAction(Clip *clip) +{ + // Get shared_ptr object to take ownership of this Clip -DeleteClipAction::~DeleteClipAction() {} + clip_ = clip->track()->GetClipObjectFromRawPtr(clip); +} void DeleteClipAction::doUndo() { // restore ref to clip @@ -113,13 +111,14 @@ void DeleteClipAction::doUndo() { void DeleteClipAction::doRedo() { // remove ref to clip - ref = seq->clips.at(index); - if (ref->IsOpen()) { - ref->Close(true); + if (clip_->IsOpen()) { + clip_->Close(true); } - seq->clips[index] = nullptr; + + clip_->track()->RemoveClip(clip_.get()); // delete link to this clip + QVector clips = clip_->track()-> linkClipIndex.clear(); linkLinkIndex.clear(); for (int i=0;iclips.size();i++) { diff --git a/undo/undo.h b/undo/undo.h index 73b5c43e1..cb58f304a 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -113,20 +113,13 @@ private: class DeleteClipAction : public OliveAction { public: - DeleteClipAction(Sequence* s, int clip); - virtual ~DeleteClipAction() override; + DeleteClipAction(Clip* clip); virtual void doUndo() override; virtual void doRedo() override; private: - Sequence* seq; - ClipPtr ref; - int index; + ClipPtr clip_; - int opening_transition; - int closing_transition; - - QVector linkClipIndex; - QVector linkLinkIndex; + QVector clips_linked_to_this_one_; }; class ChangeSequenceAction : public OliveAction { From 8989333e990faf63ce573ef5e17e4046666d7e51 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 31 Mar 2019 14:14:37 +1100 Subject: [PATCH 064/133] more refactoring and timeline rewriting --- global/global.cpp | 143 +++++++++++++ global/global.h | 5 + global/math.cpp | 6 + global/math.h | 3 + olive.pro | 3 +- panels/effectcontrols.cpp | 6 +- panels/project.cpp | 20 +- panels/timeline.cpp | 440 +++----------------------------------- panels/timeline.h | 3 - project/clipboard.cpp | 63 +++++- project/clipboard.h | 37 +++- timeline/selection.cpp | 67 ++++++ timeline/selection.h | 27 ++- timeline/sequence.cpp | 180 ++++++++++++++-- timeline/sequence.h | 11 +- timeline/track.cpp | 97 ++++++--- timeline/track.h | 8 + 17 files changed, 621 insertions(+), 498 deletions(-) create mode 100644 timeline/selection.cpp diff --git a/global/global.cpp b/global/global.cpp index 72a0fb173..097549e31 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -30,6 +30,7 @@ #include "panels/panels.h" #include "global/path.h" #include "global/config.h" +#include "project/clipboard.h" #include "rendering/audio.h" #include "dialogs/demonotice.h" #include "dialogs/preferencesdialog.h" @@ -307,6 +308,148 @@ void OliveGlobal::save_recent_projects() } } +void OliveGlobal::PasteInternal(Sequence *s) +{ + if (!olive::clipboard.IsEmpty()) { + if (olive::clipboard.type() == Clipboard::CLIPBOARD_TYPE_CLIP) { + ComboAction* ca = new ComboAction(); + + // create copies and delete areas that we'll be pasting to + QVector delete_areas; + QVector pasted_clips; + long paste_start = LONG_MAX; + long paste_end = LONG_MIN; + + for (int i=0;i(olive::clipboard.at(i)); + + // create copy of clip and offset by playhead + ClipPtr cc = c->copy(olive::ActiveSequence.get()); + + // convert frame rates + cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + + cc->set_timeline_in(cc->timeline_in() + olive::ActiveSequence->playhead); + cc->set_timeline_out(cc->timeline_out() + olive::ActiveSequence->playhead); + cc->set_track(c->track()); + + paste_start = qMin(paste_start, cc->timeline_in()); + paste_end = qMax(paste_end, cc->timeline_out()); + + pasted_clips.append(cc); + + if (!insert) { + delete_areas.append(Selection(cc->timeline_in(), cc->timeline_out(), c->track())); + } + } + if (insert) { + split_all_clips_at_point(ca, olive::ActiveSequence->playhead); + ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); + } else { + delete_areas_and_relink(ca, delete_areas, false); + } + + // correct linked clips + for (int i=0;i(clipboard.at(i)); + + for (int j=0;jlinked.size();j++) { + for (int k=0;k(clipboard.at(k)); + if (comp->load_id == oc->linked.at(j)) { + pasted_clips.at(i)->linked.append(k); + } + } + } + } + + ca->append(new AddClipCommand(olive::ActiveSequence.get(), pasted_clips)); + + olive::UndoStack.push(ca); + + update_ui(true); + + if (olive::CurrentConfig.paste_seeks) { + panel_sequence_viewer->seek(paste_end); + } + + } else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) { + ComboAction* ca = new ComboAction(); + + bool replace = false; + bool skip = false; + bool ask_conflict = true; + + QVector selected_clips = olive::ActiveSequence->SelectedClips(); + + for (int i=0;i(clipboard.at(j)); + if ((c->track() < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) { + int found = -1; + if (ask_conflict) { + replace = false; + skip = false; + } + for (int k=0;keffects.size();k++) { + if (c->effects.at(k)->meta == e->meta) { + found = k; + break; + } + } + if (found >= 0 && ask_conflict) { + QMessageBox box(this); + box.setWindowTitle(tr("Effect already exists")); + box.setText(tr("Clip '%1' already contains a '%2' effect. " + "Would you like to replace it with the pasted one or add it as a separate effect?") + .arg(c->name(), e->meta->name)); + box.setIcon(QMessageBox::Icon::Question); + + box.addButton(tr("Add"), QMessageBox::YesRole); + QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole); + QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole); + + QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"), &box); + box.setCheckBox(future_box); + + box.exec(); + + if (box.clickedButton() == replace_button) { + replace = true; + } else if (box.clickedButton() == skip_button) { + skip = true; + } + ask_conflict = !future_box->isChecked(); + } + + if (found >= 0 && skip) { + // do nothing + } else if (found >= 0 && replace) { + ca->append(new EffectDeleteCommand(c->effects.at(found).get())); + + ca->append(new AddEffectCommand(c, e->copy(c), nullptr, found)); + } else { + ca->append(new AddEffectCommand(c, e->copy(c), nullptr)); + } + } + } + } + if (ca->hasActions()) { + ca->appendPost(new ReloadEffectsCommand()); + olive::UndoStack.push(ca); + } else { + delete ca; + } + update_ui(true); + } + } +} + void OliveGlobal::ImportProject(const QString &fn) { LoadProject(fn, false); diff --git a/global/global.h b/global/global.h index 8d66c6ee6..5543fa0ef 100644 --- a/global/global.h +++ b/global/global.h @@ -472,6 +472,11 @@ private: */ void save_recent_projects(); + /** + * @brief Internal pasting function + */ + void PasteInternal(Sequence* s); + /** * @brief File filter used for any file dialogs relating to Olive project files. */ diff --git a/global/math.cpp b/global/math.cpp index f19a22b02..c11a79d4b 100644 --- a/global/math.cpp +++ b/global/math.cpp @@ -99,3 +99,9 @@ QRect fit_size_into_rect(const QRect &r, int width, int height) return QRect(r.x(), r.y() + (r.height() / 2 - new_height / 2), r.width(), new_height); } } + +template +const T &clamp(const T &val, T &min, T &max) +{ + return qMax(qMin(max, val), min); +} diff --git a/global/math.h b/global/math.h index face5003d..9d0c9a9f5 100644 --- a/global/math.h +++ b/global/math.h @@ -32,6 +32,9 @@ double cubic_from_t(double a, double b, double c, double d, double t); double cubic_t_from_x(double x_target, double a, double b, double c, double d); double solveCubicBezier(double p0, double p1, double p2, double p3, double x); +template +const T& clamp(const T& val, T& min, T& max); + QRect fit_size_into_rect(const QRect& r, int width, int height); // decibel conversion functions diff --git a/olive.pro b/olive.pro index 672286233..40be13dd2 100644 --- a/olive.pro +++ b/olive.pro @@ -181,7 +181,8 @@ SOURCES += \ ui/timelinearea.cpp \ timeline/timelineshared.cpp \ ui/timelineview.cpp \ - ui/timelinelabel.cpp + ui/timelinelabel.cpp \ + timeline/selection.cpp HEADERS += \ ui/mainwindow.h \ diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index f386c87f3..279ddc3c9 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -156,12 +156,12 @@ void EffectControls::copy(bool del) { if (e->meta->type == EFFECT_TYPE_EFFECT) { if (!cleared) { - clear_clipboard(); + olive::clipboard.Clear(); cleared = true; - clipboard_type = CLIPBOARD_TYPE_EFFECT; + olive::clipboard.SetType(Clipboard::CLIPBOARD_TYPE_EFFECT); } - clipboard.append(e->copy(nullptr)); + olive::clipboard.Append(e->copy(nullptr)); if (del) { diff --git a/panels/project.cpp b/panels/project.cpp index 0e0c614ac..e58f6ad74 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -354,20 +354,6 @@ bool Project::IsProjectWidget(QObject *child) return (child == tree_view || child == icon_view); } -bool delete_clips_in_clipboard_with_media(ComboAction* ca, Media* m) { - int delete_count = 0; - if (clipboard_type == CLIPBOARD_TYPE_CLIP) { - for (int i=0;i(clipboard.at(i)); - if (c->media() == m) { - ca->append(new RemoveClipsFromClipboard(i-delete_count)); - delete_count++; - } - } - } - return (delete_count > 0); -} - void Project::delete_selected_media() { ComboAction* ca = new ComboAction(); QModelIndexList selected_items = get_current_selected(); @@ -455,7 +441,7 @@ void Project::delete_selected_media() { } } if (confirm_delete) { - delete_clips_in_clipboard_with_media(ca, item); + olive::clipboard.DeleteClipsWithMedia(ca, item); } } } @@ -600,7 +586,9 @@ void Project::delete_clips_using_selected_media() { } for (int j=0;j& ignore) { - ca->append(new RippleAction(s, point, length, ignore)); -} - void Timeline::toggle_show_all() { if (olive::ActiveSequence != nullptr) { showing_all = !showing_all; @@ -453,9 +449,9 @@ void Timeline::nest() { Ghost& g = ghosts[i]; if (c->track() == g.track && !((c->timeline_in() < g.in - && c->timeline_out() < g.in) - || (c->timeline_in() > g.out - && c->timeline_out() > g.out))) { + && c->timeline_out() < g.in) + || (c->timeline_in() > g.out + && c->timeline_out() > g.out))) { // There's a clip occupied by the space taken up by this ghost. Move up a track, and seek again. g.track = g.track->track_list()->TrackAt(g.track->Index() + 1); @@ -547,17 +543,7 @@ void Timeline::repaint_timeline() { void Timeline::select_all() { if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->ClearSelections(); - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - Selection s; - s.in = c->timeline_in(); - s.out = c->timeline_out(); - s.track = c->track(); - olive::ActiveSequence->selections.append(s); - } - } + olive::ActiveSequence->SelectAll(); repaint_timeline(); } } @@ -567,62 +553,11 @@ void Timeline::scroll_to_frame(long frame) { } void Timeline::select_from_playhead() { - olive::ActiveSequence->selections.clear(); - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr - && c->timeline_in() <= olive::ActiveSequence->playhead - && c->timeline_out() > olive::ActiveSequence->playhead) { - Selection s; - s.in = c->timeline_in(); - s.out = c->timeline_out(); - s.track = c->track(); - olive::ActiveSequence->selections.append(s); - } + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->SelectAtPlayhead(); } } -bool Timeline::can_ripple_empty_space(long frame, int track) { - bool can_ripple_delete = true; - bool at_end_of_sequence = true; - rc_ripple_min = 0; - rc_ripple_max = LONG_MAX; - - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (c->timeline_in() > frame || c->timeline_out() > frame) { - at_end_of_sequence = false; - } - if (c->track() == track) { - if (c->timeline_in() <= frame && c->timeline_out() >= frame) { - can_ripple_delete = false; - break; - } else if (c->timeline_out() < frame) { - rc_ripple_min = qMax(rc_ripple_min, c->timeline_out()); - } else if (c->timeline_in() > frame) { - rc_ripple_max = qMin(rc_ripple_max, c->timeline_in()); - } - } - } - } - - return (can_ripple_delete && !at_end_of_sequence); -} - -void Timeline::ripple_delete_empty_space() { - QVector sels; - - Selection s; - s.in = rc_ripple_min; - s.out = rc_ripple_max; - s.track = cursor_track; - - sels.append(s); - - delete_selection(sels, true); -} - void Timeline::resizeEvent(QResizeEvent *) { // adjust maximum scrollbar if (olive::ActiveSequence != nullptr) set_sb_max(); @@ -654,30 +589,6 @@ void Timeline::resizeEvent(QResizeEvent *) { tool_button_widget->setFixedWidth((tool_button_children.at(0)->sizeHint().width())*cols + horizontal_spacing*(cols-1) + 1); } -void Timeline::delete_in_out_internal(bool ripple) { - if (olive::ActiveSequence != nullptr && olive::ActiveSequence->using_workarea) { - QVector areas; - int video_tracks = 0, audio_tracks = 0; - olive::ActiveSequence->getTrackLimits(&video_tracks, &audio_tracks); - for (int i=video_tracks;i<=audio_tracks;i++) { - Selection s; - s.in = olive::ActiveSequence->workarea_in; - s.out = olive::ActiveSequence->workarea_out; - s.track = i; - areas.append(s); - } - ComboAction* ca = new ComboAction(); - delete_areas_and_relink(ca, areas, true); - if (ripple) ripple_clips(ca, - olive::ActiveSequence.get(), - olive::ActiveSequence->workarea_in, - olive::ActiveSequence->workarea_in - olive::ActiveSequence->workarea_out); - ca->append(new SetTimelineInOutCommand(olive::ActiveSequence.get(), false, 0, 0)); - olive::UndoStack.push(ca); - update_ui(true); - } -} - void Timeline::toggle_enable_on_selected_clips() { if (olive::ActiveSequence != nullptr) { @@ -701,76 +612,6 @@ void Timeline::toggle_enable_on_selected_clips() { } } -void Timeline::delete_selection(QVector& selections, bool ripple_delete) { - if (selections.size() > 0) { - panel_graph_editor->set_row(nullptr); - panel_effect_controls->Clear(true); - - ComboAction* ca = new ComboAction(); - - // delete the areas currently selected by `selections` - // if we're ripple deleting, we don't want to delete the selections since we still need them for the ripple - delete_areas_and_relink(ca, selections, !ripple_delete); - - if (ripple_delete) { - long ripple_point = selections.at(0).in; - long ripple_length = selections.at(0).out - selections.at(0).in; - - // retrieve ripple_point and ripple_length from current selection - for (int i=1;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr && c->timeline_in() < ripple_point && c->timeline_out() > ripple_point) { - // conflict detected, but this clip may be getting deleted so let's check - bool deleted = false; - for (int j=0;jtrack() - && !(c->timeline_in() < s.in && c->timeline_out() < s.in) - && !(c->timeline_in() > s.out && c->timeline_out() > s.out)) { - deleted = true; - break; - } - } - if (!deleted) { - for (int j=0;jclips.size();j++) { - ClipPtr cc = olive::ActiveSequence->clips.at(j); - if (cc != nullptr - && cc->track() == c->track() - && cc->timeline_in() > c->timeline_out() - && cc->timeline_in() < c->timeline_out() + ripple_length) { - ripple_length = cc->timeline_in() - c->timeline_out(); - } - } - } - } - } - - if (can_ripple) { - ripple_clips(ca, olive::ActiveSequence.get(), ripple_point, -ripple_length); - panel_sequence_viewer->seek(ripple_point-1); - } - - // if we're rippling, we can clear the selections here - if we're not rippling, delete_areas_and_relink() will - // clear the selections for us - selections.clear(); - } - - olive::UndoStack.push(ca); - - update_ui(true); - } -} - void Timeline::set_zoom_value(double v) { // set zoom value zoom = v; @@ -808,38 +649,9 @@ void Timeline::zoom_out() { multiply_zoom(0.5); } -int Timeline::GetTrackHeight(int track) { - for (int i=0;igetTrackLimits(&min_track, &max_track); - - // for each active track, set the track to increase/decrease based on `diff` - for (int i=min_track;i<=max_track;i++) { - SetTrackHeight(i, qMax(GetTrackHeight(i) + diff, olive::timeline::kTrackMinHeight)); + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->ChangeTrackHeightsRelatively(diff); } // update the timeline @@ -904,205 +716,13 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool void Timeline::copy(bool del) { - bool cleared = false; - bool copied = false; - - long min_in = 0; - - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - for (int j=0;jselections.size();j++) { - const Selection& s = olive::ActiveSequence->selections.at(j); - if (s.track == c->track() && !((c->timeline_in() <= s.in && c->timeline_out() <= s.in) || (c->timeline_in() >= s.out && c->timeline_out() >= s.out))) { - if (!cleared) { - clear_clipboard(); - cleared = true; - clipboard_type = CLIPBOARD_TYPE_CLIP; - } - - ClipPtr copied_clip = c->copy(nullptr); - - // copy linked IDs (we correct these later in paste()) - copied_clip->linked = c->linked; - - if (copied_clip->timeline_in() < s.in) { - copied_clip->set_clip_in(copied_clip->clip_in() + (s.in - copied_clip->timeline_in())); - copied_clip->set_timeline_in(s.in); - } - - if (copied_clip->timeline_out() > s.out) { - copied_clip->set_timeline_out(s.out); - } - - if (copied) { - min_in = qMin(min_in, s.in); - } else { - min_in = s.in; - copied = true; - } - - copied_clip->load_id = i; - - clipboard.append(copied_clip); - } - } - } - } - - for (int i=0;i(clipboard.at(i)); - c->set_timeline_in(c->timeline_in() - min_in); - c->set_timeline_out(c->timeline_out() - min_in); - } - - if (del && copied) { - delete_selection(olive::ActiveSequence->selections, false); + if (olive::ActiveSequence != nullptr) { + olive::ActiveSequence->AddSelectionsToClipboard(del); } } void Timeline::paste(bool insert) { - if (clipboard.size() > 0) { - if (clipboard_type == CLIPBOARD_TYPE_CLIP) { - ComboAction* ca = new ComboAction(); - // create copies and delete areas that we'll be pasting to - QVector delete_areas; - QVector pasted_clips; - long paste_start = LONG_MAX; - long paste_end = LONG_MIN; - for (int i=0;i(clipboard.at(i)); - - // create copy of clip and offset by playhead - ClipPtr cc = c->copy(olive::ActiveSequence.get()); - - // convert frame rates - cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - - cc->set_timeline_in(cc->timeline_in() + olive::ActiveSequence->playhead); - cc->set_timeline_out(cc->timeline_out() + olive::ActiveSequence->playhead); - cc->set_track(c->track()); - - paste_start = qMin(paste_start, cc->timeline_in()); - paste_end = qMax(paste_end, cc->timeline_out()); - - pasted_clips.append(cc); - - if (!insert) { - Selection s; - s.in = cc->timeline_in(); - s.out = cc->timeline_out(); - s.track = c->track(); - delete_areas.append(s); - } - } - if (insert) { - split_all_clips_at_point(ca, olive::ActiveSequence->playhead); - ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); - } else { - delete_areas_and_relink(ca, delete_areas, false); - } - - // correct linked clips - for (int i=0;i(clipboard.at(i)); - - for (int j=0;jlinked.size();j++) { - for (int k=0;k(clipboard.at(k)); - if (comp->load_id == oc->linked.at(j)) { - pasted_clips.at(i)->linked.append(k); - } - } - } - } - - ca->append(new AddClipCommand(olive::ActiveSequence.get(), pasted_clips)); - - olive::UndoStack.push(ca); - - update_ui(true); - - if (olive::CurrentConfig.paste_seeks) { - panel_sequence_viewer->seek(paste_end); - } - } else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) { - ComboAction* ca = new ComboAction(); - - bool replace = false; - bool skip = false; - bool ask_conflict = true; - - QVector selected_clips = olive::ActiveSequence->SelectedClips(); - - for (int i=0;i(clipboard.at(j)); - if ((c->track() < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) { - int found = -1; - if (ask_conflict) { - replace = false; - skip = false; - } - for (int k=0;keffects.size();k++) { - if (c->effects.at(k)->meta == e->meta) { - found = k; - break; - } - } - if (found >= 0 && ask_conflict) { - QMessageBox box(this); - box.setWindowTitle(tr("Effect already exists")); - box.setText(tr("Clip '%1' already contains a '%2' effect. " - "Would you like to replace it with the pasted one or add it as a separate effect?") - .arg(c->name(), e->meta->name)); - box.setIcon(QMessageBox::Icon::Question); - - box.addButton(tr("Add"), QMessageBox::YesRole); - QPushButton* replace_button = box.addButton(tr("Replace"), QMessageBox::NoRole); - QPushButton* skip_button = box.addButton(tr("Skip"), QMessageBox::RejectRole); - - QCheckBox* future_box = new QCheckBox(tr("Do this for all conflicts found"), &box); - box.setCheckBox(future_box); - - box.exec(); - - if (box.clickedButton() == replace_button) { - replace = true; - } else if (box.clickedButton() == skip_button) { - skip = true; - } - ask_conflict = !future_box->isChecked(); - } - - if (found >= 0 && skip) { - // do nothing - } else if (found >= 0 && replace) { - ca->append(new EffectDeleteCommand(c->effects.at(found).get())); - - ca->append(new AddEffectCommand(c, e->copy(c), nullptr, found)); - } else { - ca->append(new AddEffectCommand(c, e->copy(c), nullptr)); - } - } - } - } - if (ca->hasActions()) { - ca->appendPost(new ReloadEffectsCommand()); - olive::UndoStack.push(ca); - } else { - delete ca; - } - update_ui(true); - } - } } void Timeline::edit_to_point_internal(bool in, bool ripple) { @@ -1379,30 +999,32 @@ bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bo } // snap to clip/transition - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - if (snap_to_point(c->timeline_in(), l)) { - return true; - } else if (snap_to_point(c->timeline_out(), l)) { - return true; - } else if (c->opening_transition != nullptr - && snap_to_point(c->timeline_in() + c->opening_transition->get_true_length(), l)) { - return true; - } else if (c->closing_transition != nullptr - && snap_to_point(c->timeline_out() - c->closing_transition->get_true_length(), l)) { - return true; - } else { - // try to snap to clip markers - for (int j=0;jget_markers().size();j++) { - if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) { - return true; - } + QVector all_clips = olive::ActiveSequence->GetAllClips(); + for (int i=0;itimeline_in(), l)) { + return true; + } else if (snap_to_point(c->timeline_out(), l)) { + return true; + } else if (c->opening_transition != nullptr + && snap_to_point(c->timeline_in() + c->opening_transition->get_true_length(), l)) { + return true; + } else if (c->closing_transition != nullptr + && snap_to_point(c->timeline_out() - c->closing_transition->get_true_length(), l)) { + return true; + } else { + // try to snap to clip markers + for (int j=0;jget_markers().size();j++) { + if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) { + return true; } } } + } } + return false; } diff --git a/panels/timeline.h b/panels/timeline.h index 45cc380ad..82b31f153 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -41,8 +41,6 @@ int getScreenPointFromFrame(double zoom, long frame); long getFrameFromScreenPoint(double zoom, int x); bool selection_contains_transition(const Selection& s, Clip *c, int type); -void ripple_clips(ComboAction *ca, Sequence *s, long point, long length, const QVector& ignore = QVector()); - class Timeline : public Panel @@ -95,7 +93,6 @@ public: // selecting functions bool selecting; int selection_offset; - void delete_selection(QVector &selections, bool ripple); void select_all(); bool rect_select_init; bool rect_select_proc; diff --git a/project/clipboard.cpp b/project/clipboard.cpp index d8ee9de8d..9644dd840 100644 --- a/project/clipboard.cpp +++ b/project/clipboard.cpp @@ -24,11 +24,60 @@ #include "effects/effect.h" #include "effects/transition.h" -int clipboard_type = CLIPBOARD_TYPE_CLIP; -QVector clipboard; -QVector clipboard_transitions; - -void clear_clipboard() { - clipboard.clear(); - clipboard_transitions.clear(); +Clipboard::Clipboard() : + type_(CLIPBOARD_TYPE_CLIP) +{ } + +void Clipboard::Append(VoidPtr obj) +{ + clipboard_.append(obj); +} + +void Clipboard::Clear() +{ + clipboard_.clear(); + clipboard_transitions_.clear(); +} + +int Clipboard::Count() +{ + return clipboard_.size(); +} + +VoidPtr Clipboard::Get(int i) +{ + return clipboard_.at(i); +} + +bool Clipboard::IsEmpty() +{ + return clipboard_.isEmpty(); +} + +Clipboard::Type Clipboard::type() +{ + return type_; +} + +bool Clipboard::DeleteClipsWithMedia(ComboAction *ca, Media *m) +{ + if (type_ != CLIPBOARD_TYPE_CLIP) { + return false; + } + + int delete_count = 0; + + for (int i=0;i(clipboard_.at(i)).get(); + + if (c->media() == m) { + ca->append(new RemoveClipsFromClipboard(i-delete_count)); + delete_count++; + } + } + + return (delete_count > 0); +} + +Clipboard olive::clipboard; diff --git a/project/clipboard.h b/project/clipboard.h index 0de6c8f16..6cbdb0992 100644 --- a/project/clipboard.h +++ b/project/clipboard.h @@ -23,16 +23,37 @@ #include -#include - -#define CLIPBOARD_TYPE_CLIP 0 -#define CLIPBOARD_TYPE_EFFECT 1 +#include "effects/transition.h" using VoidPtr = std::shared_ptr; -extern int clipboard_type; -extern QVector clipboard_transitions; -extern QVector clipboard; -void clear_clipboard(); +class Clipboard { +public: + enum Type { + CLIPBOARD_TYPE_CLIP, + CLIPBOARD_TYPE_EFFECT + }; + + Clipboard(); + void Append(VoidPtr obj); + void Clear(); + int Count(); + VoidPtr Get(int i); + void SetType(Type type); + bool IsEmpty(); + Type type(); + + bool DeleteClipsWithMedia(ComboAction* ca, Media* m); + +private: + Type type_; + + QVector clipboard_transitions_; + QVector clipboard_; +}; + +namespace olive { +extern Clipboard clipboard; +} #endif // CLIPBOARD_H diff --git a/timeline/selection.cpp b/timeline/selection.cpp new file mode 100644 index 000000000..96c059613 --- /dev/null +++ b/timeline/selection.cpp @@ -0,0 +1,67 @@ +#include "selection.h" + +Selection::Selection(long in, long out, Track *track) : + in_(in), + out_(out), + track_(track), + old_in_(in), + old_out_(out), + old_track_(track) +{ +} + +long Selection::in() const +{ + return in_; +} + +long Selection::out() const +{ + return out_; +} + +Track *Selection::track() const +{ + return track_; +} + +void Selection::set_in(long in) +{ + in_ = in; +} + +void Selection::set_out(long out) +{ + out_ = out; +} + +void Selection::Tidy(QVector selections) +{ + for (int i=0;i ss.out()) { + // do nothing + } else if (s.in() >= ss.in() && s.out() <= ss.out()) { + remove = true; + } else if (s.in() <= ss.out() && s.out() > ss.out()) { + ss.out = s.out(); + remove = true; + } else if (s.out() >= ss.in() && s.in() < ss.in()) { + ss.in = s.in(); + remove = true; + } + if (remove) { + selections.removeAt(i); + i--; + break; + } + } + } + } + } +} diff --git a/timeline/selection.h b/timeline/selection.h index 3f66905f8..6b76e311d 100644 --- a/timeline/selection.h +++ b/timeline/selection.h @@ -21,14 +21,29 @@ #ifndef SELECTION_H #define SELECTION_H -struct Selection { - long in; - long out; +#include - long old_in; - long old_out; +class Track; - bool trim_in; +class Selection { +public: + Selection(long in, long out, Track* track); + + long in() const; + long out() const; + Track* track() const; + + void set_in(long in); + void set_out(long out); + + static void Tidy(QVector selections); + +private: + long in_; + long out_; + Track* track_; + + bool trim_in_; }; #endif // SELECTION_H diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 8f31a783e..c79900ddb 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -23,6 +23,7 @@ #include #include "panels/panels.h" +#include "project/clipboard.h" #include "global/debug.h" Sequence::Sequence() : @@ -176,9 +177,54 @@ QVector Sequence::SelectedClips(bool containing) return selected_clips; } -void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool deselect_areas) +void Sequence::DeleteInToOut(bool ripple) { - clean_up_selections(areas); + if (using_workarea) { + + QVector areas_to_delete; + + for (int i=0;iTrackCount();j++) { + areas_to_delete.append(Selection(workarea_in, workarea_out, tl->TrackAt(j))); + } + + } + + ComboAction* ca = new ComboAction(); + DeleteAreas(ca, areas_to_delete, true); + if (ripple) Ripple(ca, + workarea_in, + workarea_in - workarea_out); + ca->append(new SetTimelineInOutCommand(olive::ActiveSequence.get(), false, 0, 0)); + olive::UndoStack.push(ca); + update_ui(true); + } +} + +void Sequence::Ripple(ComboAction *ca, long point, long length, const QVector &ignore) +{ + ca->append(new RippleAction(this, point, length, ignore)); +} + +void Sequence::ChangeTrackHeightsRelatively(int diff) +{ + for (int i=0;iTrackCount();j++) { + Track* t = tl->TrackAt(j); + + t->set_height(t->height() + diff); + } + } +} + +void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas) +{ + Selection::Tidy(areas); panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); @@ -192,41 +238,41 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector& areas, bool dese const Selection& s = areas.at(i); for (int j=0;jtrack() == s.track && !c->undeletable) { + if (c->track() == s.track() && !c->undeletable) { if (selection_contains_transition(s, c, kTransitionOpening)) { // delete opening transition ca->append(new DeleteTransitionCommand(c->opening_transition)); } else if (selection_contains_transition(s, c, kTransitionClosing)) { // delete closing transition ca->append(new DeleteTransitionCommand(c->closing_transition)); - } else if (c->timeline_in() >= s.in && c->timeline_out() <= s.out) { + } else if (c->timeline_in() >= s.in() && c->timeline_out() <= s.out()) { // clips falls entirely within deletion area ca->append(new DeleteClipAction(c)); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.out) { + } else if (c->timeline_in() < s.in() && c->timeline_out() > s.out()) { // middle of clip is within deletion area // duplicate clip - ClipPtr post = SplitClip(ca, true, c, s.in, s.out); + ClipPtr post = SplitClip(ca, true, c, s.in(), s.out()); pre_clips.append(j); post_clips.append(post); - } else if (c->timeline_in() < s.in && c->timeline_out() > s.in) { + } else if (c->timeline_in() < s.in() && c->timeline_out() > s.in()) { // only out point is in deletion area - c->move(ca, c->timeline_in(), s.in, c->clip_in(), c->track()); + c->move(ca, c->timeline_in(), s.in(), c->clip_in(), c->track()); if (c->closing_transition != nullptr) { - if (s.in < c->timeline_out() - c->closing_transition->get_true_length()) { + if (s.in() < c->timeline_out() - c->closing_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->closing_transition)); } else { ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); } } - } else if (c->timeline_in() < s.out && c->timeline_out() > s.out) { + } else if (c->timeline_in() < s.out() && c->timeline_out() > s.out()) { // only in point is in deletion area - c->move(ca, s.out, c->timeline_out(), c->clip_in() + (s.out - c->timeline_in()), c->track()); + c->move(ca, s.out(), c->timeline_out(), c->clip_in() + (s.out() - c->timeline_in()), c->track()); if (c->opening_transition != nullptr) { - if (s.out > c->timeline_in() + c->opening_transition->get_true_length()) { + if (s.out() > c->timeline_in() + c->opening_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->opening_transition)); } else { ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); @@ -370,6 +416,28 @@ Effect *Sequence::GetSelectedGizmo() return gizmo_ptr; } +void Sequence::SelectAll() +{ + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->SelectAll(); + } + } +} + +void Sequence::SelectAtPlayhead() +{ + for (int j=0;jTrackCount();i++) { + tl->TrackAt(i)->SelectAtPoint(playhead); + } + } +} + void Sequence::ClearSelections() { for (int j=0;j original_clips; + QVector copied_clips; + + long min_in = LONG_MAX; + + QVector selections = Selections(); + for (int i=0;i track_clips = s.track()->GetAllClips(); + + for (int j=0;jtimeline_out() < s.in() || c->timeline_in() > s.out())) { + + // If so, we'll be copying this clip + original_clips.append(c); + + ClipPtr copy = c->copy(nullptr); + + // If we only copied part of this clip, adjust the copy so it's only that part of the clip + if (copy->timeline_in() < s.in()) { + copy->set_clip_in(copy->clip_in() + (s.in() - copy->timeline_in())); + copy->set_timeline_in(s.in()); + } + + if (copy->timeline_out() > s.out()) { + copy->set_timeline_out(s.out()); + } + + // Store the minimum in point as all copies will be stored offset from 0 + min_in = qMin(min_in, s.in()); + + copied_clips.append(copy); + olive::clipboard.Append(copy); + + } + } + } + + // Determine whether we actually copied anything + if (min_in < LONG_MAX) { + + // Offset all copied clips to 0 + for (int i=0;iset_timeline_in(copy->timeline_in() - min_in); + copy->set_timeline_out(copy->timeline_out() - min_in); + } + + // Relink the copied clips with each other + olive::timeline::RelinkClips(original_clips, copied_clips); + + // If we're deleting the originals (i.e. cutting), delete them now + if (delete_originals) { + ComboAction* ca = new ComboAction(); + DeleteAreas(ca, selections, true); + olive::UndoStack.push(ca); + } + + } +} + +QVector Sequence::Selections() +{ + QVector selections; + + for (int j=0;jTrackCount();i++) { + selections.append(tl->TrackAt(i)->Selections()); + } + } + + return selections; +} + ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) { return SplitClip(ca, transitions, pre, frame, frame); diff --git a/timeline/sequence.h b/timeline/sequence.h index 5ae8cce88..9ec11fc38 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -65,7 +65,12 @@ public: QVector SelectedClips(bool containing = true); //QVector SelectedClipIndexes(); - void DeleteAreas(); + void DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas); + void DeleteInToOut(bool ripple); + + void Ripple(ComboAction *ca, long point, long length, const QVector& ignore = QVector()); + + void ChangeTrackHeightsRelatively(int diff); bool SplitAllClipsAtPoint(ComboAction *ca, long point); void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); @@ -75,7 +80,11 @@ public: bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void SelectAll(); + void SelectAtPlayhead(); void ClearSelections(); + void AddSelectionsToClipboard(bool delete_originals); + QVector Selections(); long playhead; diff --git a/timeline/track.cpp b/timeline/track.cpp index 088d09832..48d174c15 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -3,6 +3,7 @@ #include "timeline/clip.h" #include "timeline/tracklist.h" #include "timeline/sequence.h" +#include "global/math.h" int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; @@ -72,7 +73,7 @@ int Track::height() void Track::set_height(int h) { - height_ = h; + height_ = qMax(h, olive::timeline::kTrackMinHeight); } void Track::AddClip(ClipPtr clip) @@ -158,9 +159,9 @@ bool Track::IsClipSelected(Clip *clip, bool containing) { for (int i=0;itimeline_in() >= s.in && clip->timeline_out() <= s.out) - || (!containing && !(clip->timeline_in() < s.in && clip->timeline_out() < s.in) - && !(clip->timeline_in() > s.in && clip->timeline_out() > s.in)))) { + if (((clip->timeline_in() >= s.in() && clip->timeline_out() <= s.out()) + || (!containing && !(clip->timeline_in() < s.in() && clip->timeline_out() < s.in()) + && !(clip->timeline_in() > s.in() && clip->timeline_out() > s.in())))) { return true; } } @@ -199,8 +200,8 @@ bool Track::IsTransitionSelected(Transition *t) // See if there's a selection matching this for (int i=0;i= transition_out_point) { + if (s.in() <= transition_in_point + && s.out() >= transition_out_point) { return true; } } @@ -208,33 +209,33 @@ bool Track::IsTransitionSelected(Transition *t) return false; } -void Track::TidySelections() +void Track::SelectClip(Clip* c) { - for (int i=0;i ss.out) { - // do nothing - } else if (s.in >= ss.in && s.out <= ss.out) { - remove = true; - } else if (s.in <= ss.out && s.out > ss.out) { - ss.out = s.out; - remove = true; - } else if (s.out >= ss.in && s.in < ss.in) { - ss.in = s.in; - remove = true; - } - if (remove) { - areas.removeAt(i); - i--; - break; - } - } - } + selections_.append(Selection(c->timeline_in(), c->timeline_out(), this)); +} + +void Track::SelectAll() +{ + ClearSelections(); + + // Select every clip + for (int i=0;itimeline_in() <= point + && c->timeline_out() > point) { + SelectClip(c); } } } @@ -244,6 +245,38 @@ void Track::ClearSelections() selections_.clear(); } +void Track::DeselectArea(long in, long out) +{ + int selection_count = selections_.size(); + for (int i=0;i= in && s.out() <= out) { + // whole selection is in deselect area + selections_.removeAt(i); + i--; + selection_count--; + } else if (s.in() < in && s.out() > out) { + // middle of selection is in deselect area + Selection new_sel(out, s.out(), s.track()); + selections_.append(new_sel); + + s.set_out(in); + } else if (s.in() < in && s.out() > in) { + // only out point is in deselect area + s.set_out(in); + } else if (s.in() < out && s.out() > out) { + // only in point is in deselect area + s.set_in(out); + } + } +} + +QVector Track::Selections() +{ + return selections_; +} + long Track::GetEndFrame() { long end_frame = 0; diff --git a/timeline/track.h b/timeline/track.h index e3d642e8f..168a69a91 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -68,8 +68,16 @@ public: bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); + void DeleteArea(ComboAction *ca, const Selection& s); + void DeleteArea(ComboAction *ca, long in, long out); + + void SelectClip(Clip *c); + void SelectAll(); + void SelectAtPoint(long point); void TidySelections(); void ClearSelections(); + void DeselectArea(long in, long out); + QVector Selections(); long GetEndFrame(); From 2d4714547b94377a1e3ce1d7346d72f59e8d0dc1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 3 Apr 2019 01:38:01 +1100 Subject: [PATCH 065/133] rewrite nearly complete --- dialogs/autocutsilencedialog.cpp | 14 +- dialogs/autocutsilencedialog.h | 4 +- dialogs/clippropertiesdialog.cpp | 6 +- dialogs/exportdialog.cpp | 28 +- dialogs/exportdialog.h | 4 +- dialogs/mediapropertiesdialog.cpp | 10 +- dialogs/newsequencedialog.cpp | 22 +- dialogs/preferencesdialog.cpp | 188 +-- dialogs/replaceclipmediadialog.cpp | 14 +- dialogs/speeddialog.cpp | 57 +- effects/effect.cpp | 20 +- effects/effectfield.cpp | 8 +- effects/effectloaders.cpp | 10 +- effects/effectrow.cpp | 12 +- effects/fields/boolfield.cpp | 2 +- effects/fields/colorfield.cpp | 2 +- effects/fields/combofield.cpp | 2 +- effects/fields/doublefield.cpp | 2 +- effects/fields/filefield.cpp | 2 +- effects/fields/fontfield.cpp | 2 +- effects/fields/stringfield.cpp | 4 +- effects/internal/timecodeeffect.cpp | 10 +- effects/internal/toneeffect.cpp | 2 +- effects/internal/transformeffect.cpp | 14 +- effects/keyframe.cpp | 2 +- effects/transition.cpp | 7 +- {project => global}/clipboard.cpp | 20 + {project => global}/clipboard.h | 2 + global/config.cpp | 15 +- global/config.h | 12 +- global/global.cpp | 125 +- global/global.h | 49 +- global/math.h | 4 + global/timing.cpp | 4 +- main.cpp | 4 +- olive.pro | 14 +- panels/effectcontrols.cpp | 23 +- panels/grapheditor.cpp | 2 +- panels/panels.cpp | 29 +- panels/panels.h | 2 +- panels/project.cpp | 90 +- panels/timeline.cpp | 875 ++++-------- panels/timeline.h | 65 +- panels/viewer.cpp | 94 +- panels/viewer.h | 14 +- project/footage.cpp | 25 +- project/loadthread.cpp | 26 +- project/media.cpp | 34 +- project/previewgenerator.cpp | 13 +- project/projectfunctions.cpp | 10 +- project/projectmodel.cpp | 34 +- project/savethread.cpp | 6 +- project/sourcescommon.cpp | 28 +- rendering/audio.cpp | 13 +- rendering/cacher.cpp | 63 +- rendering/exportthread.cpp | 25 +- rendering/exportthread.h | 22 +- rendering/framebufferobject.cpp | 4 +- rendering/renderfunctions.cpp | 27 +- rendering/renderfunctions.h | 4 +- rendering/renderthread.cpp | 14 +- timeline/clip.cpp | 192 ++- timeline/clip.h | 12 +- timeline/ghost.cpp | 6 + timeline/ghost.h | 17 +- timeline/marker.cpp | 117 +- timeline/marker.h | 9 +- timeline/selection.cpp | 33 +- timeline/selection.h | 6 +- timeline/sequence.cpp | 595 +++++++- timeline/sequence.h | 37 +- timeline/timelinefunctions.cpp | 142 ++ timeline/timelinefunctions.h | 17 +- timeline/timelinetools.cpp | 3 + {ui => timeline}/timelinetools.h | 10 +- timeline/track.cpp | 36 +- timeline/track.h | 2 + timeline/tracklist.cpp | 10 + timeline/tracklist.h | 2 + ui/audiomonitor.cpp | 2 +- ui/focusfilter.cpp | 41 +- ui/graphview.cpp | 10 +- ui/keyframeview.cpp | 26 +- ui/labelslider.cpp | 4 +- ui/mainwindow.cpp | 127 +- ui/mainwindow.h | 1 - ui/menu.cpp | 4 +- ui/menuhelper.cpp | 38 +- ui/scrollarea.cpp | 44 - ui/scrollarea.h | 33 - ui/styling.cpp | 4 +- ui/timelinearea.cpp | 14 +- ui/timelinearea.h | 6 +- ui/timelineheader.cpp | 35 +- ui/timelinelabel.h | 3 + ui/timelineview.cpp | 1870 +++++++++++++------------- ui/timelineview.h | 40 +- ui/viewerwidget.cpp | 35 +- undo/undo.cpp | 228 ++-- undo/undo.h | 51 +- undo/undostack.cpp | 2 +- undo/undostack.h | 2 +- 102 files changed, 3213 insertions(+), 2892 deletions(-) rename {project => global}/clipboard.cpp (86%) rename {project => global}/clipboard.h (95%) create mode 100644 timeline/ghost.cpp create mode 100644 timeline/timelinetools.cpp rename {ui => timeline}/timelinetools.h (89%) delete mode 100644 ui/scrollarea.cpp delete mode 100644 ui/scrollarea.h diff --git a/dialogs/autocutsilencedialog.cpp b/dialogs/autocutsilencedialog.cpp index 66f6110ab..3659cde2c 100644 --- a/dialogs/autocutsilencedialog.cpp +++ b/dialogs/autocutsilencedialog.cpp @@ -31,7 +31,7 @@ #include "panels/panels.h" #include "panels/timeline.h" -AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector clips) : +AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector clips) : QDialog(parent), clips_(clips) { @@ -124,16 +124,16 @@ void AutoCutSilenceDialog::cut_silence() { // Loop over clips provided to this dialog for (int j=0;jclips.at(clips_.at(j)).get(); + Clip* clip = clips_.at(j); // Check if this clip is an audio footage clip - if (clip->track() >= 0 + if (clip->type() == Track::kTypeAudio && clip->media() != nullptr && clip->media_stream()->preview_done) { // TODO provide warning for preview not being done QVector split_positions; - int clip_start = clip->timeline_in(); + long clip_start = clip->timeline_in(); const FootageStream* ms = clip->media_stream(); long media_length = clip->media_length(); @@ -156,7 +156,7 @@ void AutoCutSilenceDialog::cut_silence() { // read the current sample into the circular array qint8 tmp = 0; - for (int k=start; kaudio_preview.at(k))))); } vols[circular_index] = tmp; @@ -202,13 +202,13 @@ void AutoCutSilenceDialog::cut_silence() { } } - panel_timeline->split_clip_at_positions(ca, clips_.at(j), split_positions); + clip->track()->sequence()->SplitClipAtPositions(ca, clips_.at(j), split_positions); } } if (ca->hasActions()) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } diff --git a/dialogs/autocutsilencedialog.h b/dialogs/autocutsilencedialog.h index 3b379f5ae..37fe57a61 100644 --- a/dialogs/autocutsilencedialog.h +++ b/dialogs/autocutsilencedialog.h @@ -31,7 +31,7 @@ class AutoCutSilenceDialog : public QDialog { Q_OBJECT public: - AutoCutSilenceDialog(QWidget* parent, QVector clips); + AutoCutSilenceDialog(QWidget* parent, QVector clips); public slots: virtual int exec() override; private slots: @@ -39,7 +39,7 @@ private slots: private: void cut_silence(); - QVector clips_; + QVector clips_; LabelSlider* attack_threshold; LabelSlider* release_threshold; diff --git a/dialogs/clippropertiesdialog.cpp b/dialogs/clippropertiesdialog.cpp index 9d109b648..9a5f51f43 100644 --- a/dialogs/clippropertiesdialog.cpp +++ b/dialogs/clippropertiesdialog.cpp @@ -92,7 +92,7 @@ ClipPropertiesDialog::ClipPropertiesDialog(QWidget *parent, QVector clip } // it's assumed all the clips come from the same sequence - duration_field_->SetFrameRate(first_clip->sequence->frame_rate); + duration_field_->SetFrameRate(first_clip->track()->sequence()->frame_rate); if (all_clips_have_same_duration) { duration_field_->SetDefault(first_clip->length()); @@ -124,7 +124,7 @@ void ClipPropertiesDialog::accept() long clip_duration_rounded = qRound(clip_duration); if (clip->length() != clip_duration_rounded) { - clip->move(ca, + clip->Move(ca, clip->timeline_in(), clip->timeline_in() + clip_duration_rounded, clip->clip_in(), @@ -135,7 +135,7 @@ void ClipPropertiesDialog::accept() } if (ca->hasActions()) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); } else { delete ca; diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index b667ef3d3..797bcf946 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -68,14 +68,15 @@ enum ExportFormats { FORMAT_SIZE }; -ExportDialog::ExportDialog(QWidget *parent) : - QDialog(parent) +ExportDialog::ExportDialog(QWidget *parent, Sequence* sequence) : + QDialog(parent), + sequence_(sequence) { - setWindowTitle(tr("Export \"%1\"").arg(olive::ActiveSequence->name)); + setWindowTitle(tr("Export \"%1\"").arg(sequence->name)); setup_ui(); rangeCombobox->setCurrentIndex(0); - if (olive::ActiveSequence->using_workarea) { + if (sequence->using_workarea) { rangeCombobox->setEnabled(true); rangeCombobox->setCurrentIndex(1); } @@ -109,10 +110,10 @@ ExportDialog::ExportDialog(QWidget *parent) : formatCombobox->setCurrentIndex(FORMAT_MPEG4); // default to sequence's native dimensions - widthSpinbox->setValue(olive::ActiveSequence->width); - heightSpinbox->setValue(olive::ActiveSequence->height); - samplingRateSpinbox->setValue(olive::ActiveSequence->audio_frequency); - framerateSpinbox->setValue(olive::ActiveSequence->frame_rate); + widthSpinbox->setValue(sequence->width); + heightSpinbox->setValue(sequence->height); + samplingRateSpinbox->setValue(sequence->audio_frequency); + framerateSpinbox->setValue(sequence->frame_rate); // set some advanced defaults vcodec_params.threads = 0; @@ -537,6 +538,7 @@ void ExportDialog::StartExport() { // Set up export parameters to send to the ExportThread ExportParams params; + params.sequence = sequence_; params.filename = filename; params.video_enabled = videoGroupbox->isChecked(); if (params.video_enabled) { @@ -555,10 +557,10 @@ void ExportDialog::StartExport() { } params.start_frame = 0; - params.end_frame = olive::ActiveSequence->getEndFrame(); // entire sequence + params.end_frame = sequence_->GetEndFrame(); // entire sequence if (rangeCombobox->currentIndex() == 1) { - params.start_frame = qMax(olive::ActiveSequence->workarea_in, params.start_frame); - params.end_frame = qMin(olive::ActiveSequence->workarea_out, params.end_frame); + params.start_frame = qMax(sequence_->workarea_in, params.start_frame); + params.end_frame = qMin(sequence_->workarea_out, params.end_frame); } // Create export thread @@ -573,7 +575,7 @@ void ExportDialog::StartExport() { panel_effect_controls->Clear(); // Close all currently open clips - olive::ActiveSequence->Close(); + sequence_->Close(); olive::Global->set_export_state(true); @@ -654,7 +656,7 @@ void ExportDialog::comp_type_changed(int) { case COMPRESSION_TYPE_CBR: case COMPRESSION_TYPE_TARGETBR: videoBitrateLabel->setText(tr("Bitrate (Mbps):")); - videobitrateSpinbox->setValue(qMax(0.5, (double) qRound((0.01528 * olive::ActiveSequence->height) - 4.5))); + videobitrateSpinbox->setValue(qMax(0.5, (double) qRound((0.01528 * sequence_->height) - 4.5))); break; case COMPRESSION_TYPE_CFR: videoBitrateLabel->setText(tr("Quality (CRF):")); diff --git a/dialogs/exportdialog.h b/dialogs/exportdialog.h index 0d92fd4b2..d76d359f3 100644 --- a/dialogs/exportdialog.h +++ b/dialogs/exportdialog.h @@ -50,7 +50,7 @@ public: * * QWidget parent. Usually MainWindow. */ - explicit ExportDialog(QWidget *parent); + explicit ExportDialog(QWidget *parent, Sequence *sequence); private slots: /** @@ -270,6 +270,8 @@ private: * @brief Time value set when exporting begins to determine the total duration of the export */ qint64 total_export_time_start; + + Sequence* sequence_; }; #endif // EXPORTDIALOG_H diff --git a/dialogs/mediapropertiesdialog.cpp b/dialogs/mediapropertiesdialog.cpp index 45832cf79..01154ffb6 100644 --- a/dialogs/mediapropertiesdialog.cpp +++ b/dialogs/mediapropertiesdialog.cpp @@ -114,12 +114,12 @@ MediaPropertiesDialog::MediaPropertiesDialog(QWidget *parent, Media *i) : interlacing_box = new QComboBox(this); interlacing_box->addItem( tr("Auto (%1)").arg( - get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + Footage::get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) ) ); - interlacing_box->addItem(get_interlacing_name(VIDEO_PROGRESSIVE)); - interlacing_box->addItem(get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); - interlacing_box->addItem(get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_PROGRESSIVE)); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); interlacing_box->setCurrentIndex( (f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing) @@ -232,7 +232,7 @@ void MediaPropertiesDialog::accept() { } ca->appendPost(new UpdateViewer()); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); QDialog::accept(); } diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index 6860a77cd..5d23f49af 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -108,8 +108,8 @@ void NewSequenceDialog::accept() { s->audio_layout = AV_CH_LAYOUT_STEREO; ComboAction* ca = new ComboAction(); - panel_project->create_sequence_internal(ca, s, true, nullptr); - olive::UndoStack.push(ca); + olive::project_model.CreateSequence(ca, s, true, nullptr); + olive::undo_stack.push(ca); } else if (existing_item != nullptr) { @@ -128,14 +128,12 @@ void NewSequenceDialog::accept() { esc->audio_layout = AV_CH_LAYOUT_STEREO; ca->append(esc); - for (int i=0;iclips.size();i++) { - ClipPtr c = existing_sequence->clips.at(i); - if (c != nullptr) { - c->refactor_frame_rate(ca, multiplier, true); - } + QVector existing_sequence_clips = existing_sequence->GetAllClips(); + for (int i=0;irefactor_frame_rate(ca, multiplier, true); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else if (existing_sequence != nullptr) { @@ -235,13 +233,13 @@ void NewSequenceDialog::setup_ui() { videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1); width_numeric = new QSpinBox(videoGroupBox); width_numeric->setMaximum(9999); - width_numeric->setValue(olive::CurrentConfig.default_sequence_width); + width_numeric->setValue(olive::config.default_sequence_width); videoLayout->addWidget(width_numeric, 0, 2, 1, 2); videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2); height_numeric = new QSpinBox(videoGroupBox); height_numeric->setMaximum(9999); - height_numeric->setValue(olive::CurrentConfig.default_sequence_height); + height_numeric->setValue(olive::config.default_sequence_height); videoLayout->addWidget(height_numeric, 1, 2, 1, 2); videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1); @@ -258,7 +256,7 @@ void NewSequenceDialog::setup_ui() { frame_rate_combobox->addItem("59.94 FPS", 59.94); frame_rate_combobox->addItem("60 FPS", 60.0); for (int i=0;icount();i++) { - if (qFuzzyCompare(frame_rate_combobox->itemData(i).toDouble(), olive::CurrentConfig.default_sequence_framerate)) { + if (qFuzzyCompare(frame_rate_combobox->itemData(i).toDouble(), olive::config.default_sequence_framerate)) { frame_rate_combobox->setCurrentIndex(i); } } @@ -288,7 +286,7 @@ void NewSequenceDialog::setup_ui() { audio_frequency_combobox = new QComboBox(audioGroupBox); combobox_audio_sample_rates(audio_frequency_combobox); for (int i=0;icount();i++) { - if (audio_frequency_combobox->itemData(i) == olive::CurrentConfig.default_sequence_audio_frequency) { + if (audio_frequency_combobox->itemData(i) == olive::config.default_sequence_audio_frequency) { audio_frequency_combobox->setCurrentIndex(i); } } diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index 03a2ce45a..f70a369b1 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -90,11 +90,11 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) : // set up default sequence default_sequence.name = tr("Default Sequence"); - default_sequence.width = olive::CurrentConfig.default_sequence_width; - default_sequence.height = olive::CurrentConfig.default_sequence_height; - default_sequence.frame_rate = olive::CurrentConfig.default_sequence_framerate; - default_sequence.audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; - default_sequence.audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout; + default_sequence.width = olive::config.default_sequence_width; + default_sequence.height = olive::config.default_sequence_height; + default_sequence.frame_rate = olive::config.default_sequence_framerate; + default_sequence.audio_frequency = olive::config.default_sequence_audio_frequency; + default_sequence.audio_layout = olive::config.default_sequence_audio_channel_layout; } void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent) { @@ -187,13 +187,13 @@ void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) ocio_default_input->addItem(colorspace); - if (colorspace == olive::CurrentConfig.ocio_default_input_colorspace) { + if (colorspace == olive::config.ocio_default_input_colorspace) { ocio_default_input->setCurrentIndex(i); } } // Get current display name (if the config is empty, get the current default display) - QString current_display = olive::CurrentConfig.ocio_display; + QString current_display = olive::config.ocio_display; if (current_display.isEmpty()) { current_display = config->getDefaultDisplay(); } @@ -219,7 +219,7 @@ void PreferencesDialog::populate_ocio_menus(OCIO::ConstConfigRcPtr config) ocio_look->addItem(look, look); - if (look == olive::CurrentConfig.ocio_look) { + if (look == olive::config.ocio_look) { ocio_look->setCurrentIndex(i); } } @@ -249,7 +249,7 @@ void PreferencesDialog::update_ocio_view_menu(OCIO::ConstConfigRcPtr config) QString display = ocio_display->currentText(); // Get current view - QString current_view = olive::CurrentConfig.ocio_view; + QString current_view = olive::config.ocio_view; if (current_view.isEmpty()) { current_view = config->getDefaultView(display.toUtf8()); } @@ -351,7 +351,7 @@ void PreferencesDialog::accept() { ); return; - } else if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { + } else if (olive::config.ocio_config_path != ocio_config_file->text()) { // Check whether OCIO can load it OCIO::ConstConfigRcPtr file_config = TestOCIOConfig(ocio_config_file->text().toUtf8()); @@ -375,10 +375,10 @@ void PreferencesDialog::accept() { // Check if any settings will require a restart of Olive (including the bool options determined above) if (bool_requires_restart - || olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() - || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value() - || olive::CurrentConfig.css_path != custom_css_fn->text() - || olive::CurrentConfig.style != static_cast(ui_style->currentData().toInt())) { + || olive::config.thumbnail_resolution != thumbnail_res_spinbox->value() + || olive::config.waveform_resolution != waveform_res_spinbox->value() + || olive::config.css_path != custom_css_fn->text() + || olive::config.style != static_cast(ui_style->currentData().toInt())) { // any changes to these settings will require a restart - ask the user if we should do one now or later @@ -406,65 +406,65 @@ void PreferencesDialog::accept() { // Everything checks out, start saving settings from the UI to the backend - olive::CurrentConfig.css_path = custom_css_fn->text(); - olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1; - olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text(); - olive::CurrentConfig.upcoming_queue_size = upcoming_queue_spinbox->value(); - olive::CurrentConfig.upcoming_queue_type = upcoming_queue_type->currentIndex(); - olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value(); - olive::CurrentConfig.previous_queue_type = previous_queue_type->currentIndex(); + olive::config.css_path = custom_css_fn->text(); + olive::config.recording_mode = recordingComboBox->currentIndex() + 1; + olive::config.img_seq_formats = imgSeqFormatEdit->text(); + olive::config.upcoming_queue_size = upcoming_queue_spinbox->value(); + olive::config.upcoming_queue_type = upcoming_queue_type->currentIndex(); + olive::config.previous_queue_size = previous_queue_spinbox->value(); + olive::config.previous_queue_type = previous_queue_type->currentIndex(); // Audio settings may require the audio device to be re-initiated. - if (olive::CurrentConfig.preferred_audio_output != audio_output_devices->currentData().toString() - || olive::CurrentConfig.preferred_audio_input != audio_input_devices->currentData().toString() - || olive::CurrentConfig.audio_rate != audio_sample_rate->currentData().toInt()) { + if (olive::config.preferred_audio_output != audio_output_devices->currentData().toString() + || olive::config.preferred_audio_input != audio_input_devices->currentData().toString() + || olive::config.audio_rate != audio_sample_rate->currentData().toInt()) { reinit_audio = true; } - olive::CurrentConfig.preferred_audio_output = audio_output_devices->currentData().toString(); - olive::CurrentConfig.preferred_audio_input = audio_input_devices->currentData().toString(); - olive::CurrentConfig.audio_rate = audio_sample_rate->currentData().toInt(); + olive::config.preferred_audio_output = audio_output_devices->currentData().toString(); + olive::config.preferred_audio_input = audio_input_devices->currentData().toString(); + olive::config.audio_rate = audio_sample_rate->currentData().toInt(); - olive::CurrentConfig.effect_textbox_lines = effect_textbox_lines_field->value(); + olive::config.effect_textbox_lines = effect_textbox_lines_field->value(); // see if the language file should be reloaded (not necessary if the app is restarting anyway) if (!restart_after_saving - && olive::CurrentConfig.language_file != language_combobox->currentData().toString()) { + && olive::config.language_file != language_combobox->currentData().toString()) { reload_language = true; } - olive::CurrentConfig.language_file = language_combobox->currentData().toString(); + olive::config.language_file = language_combobox->currentData().toString(); // Check whether OCIO settings will require a reset of the render threads - if (olive::CurrentConfig.playback_bit_depth != playback_bit_depth->currentIndex() - || olive::CurrentConfig.export_bit_depth != export_bit_depth->currentIndex()) { + if (olive::config.playback_bit_depth != playback_bit_depth->currentIndex() + || olive::config.export_bit_depth != export_bit_depth->currentIndex()) { reset_render_threads = true; } - if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text() - || olive::CurrentConfig.ocio_display != ocio_display->currentText() - || olive::CurrentConfig.ocio_view != ocio_view->currentText() - || olive::CurrentConfig.ocio_look != ocio_look->currentData().toString()) { + if (olive::config.ocio_config_path != ocio_config_file->text() + || olive::config.ocio_display != ocio_display->currentText() + || olive::config.ocio_view != ocio_view->currentText() + || olive::config.ocio_look != ocio_look->currentData().toString()) { reset_ocio_shaders = true; } - if (olive::CurrentConfig.ocio_config_path != ocio_config_file->text()) { + if (olive::config.ocio_config_path != ocio_config_file->text()) { OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8())); - olive::CurrentConfig.ocio_config_path = ocio_config_file->text(); + olive::config.ocio_config_path = ocio_config_file->text(); } - olive::CurrentConfig.enable_color_management = enable_color_management->isChecked(); - olive::CurrentConfig.playback_bit_depth = playback_bit_depth->currentIndex(); - olive::CurrentConfig.export_bit_depth = export_bit_depth->currentIndex(); - olive::CurrentConfig.ocio_display = ocio_display->currentText(); - olive::CurrentConfig.ocio_default_input_colorspace = ocio_default_input->currentText(); - olive::CurrentConfig.ocio_view = ocio_view->currentText(); + olive::config.enable_color_management = enable_color_management->isChecked(); + olive::config.playback_bit_depth = playback_bit_depth->currentIndex(); + olive::config.export_bit_depth = export_bit_depth->currentIndex(); + olive::config.ocio_display = ocio_display->currentText(); + olive::config.ocio_default_input_colorspace = ocio_default_input->currentText(); + olive::config.ocio_view = ocio_view->currentText(); // We use data here instead of text because there's a "(None)" option with an empty string - olive::CurrentConfig.ocio_look = ocio_look->currentData().toString(); + olive::config.ocio_look = ocio_look->currentData().toString(); // Set default sequence options - olive::CurrentConfig.default_sequence_width = default_sequence.width; - olive::CurrentConfig.default_sequence_height = default_sequence.height; - olive::CurrentConfig.default_sequence_framerate = default_sequence.frame_rate; - olive::CurrentConfig.default_sequence_audio_frequency = default_sequence.audio_frequency; - olive::CurrentConfig.default_sequence_audio_channel_layout = default_sequence.audio_layout; + olive::config.default_sequence_width = default_sequence.width; + olive::config.default_sequence_height = default_sequence.height; + olive::config.default_sequence_framerate = default_sequence.frame_rate; + olive::config.default_sequence_audio_frequency = default_sequence.audio_frequency; + olive::config.default_sequence_audio_channel_layout = default_sequence.audio_layout; // Set all bool options for (int i=0;i(ui_style->currentData().toInt()); + olive::config.style = static_cast(ui_style->currentData().toInt()); // Check if the thumbnail or waveform icon fields have changed, we may need to recreate the previews if so - if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value() - || olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) { + if (olive::config.thumbnail_resolution != thumbnail_res_spinbox->value() + || olive::config.waveform_resolution != waveform_res_spinbox->value()) { // we're changing the size of thumbnails and waveforms, so let's delete them and regenerate them next start // delete nothing PreviewDeleteTypes delete_type = DELETE_NONE; - if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()) { + if (olive::config.thumbnail_resolution != thumbnail_res_spinbox->value()) { // delete existing thumbnails - olive::CurrentConfig.thumbnail_resolution = thumbnail_res_spinbox->value(); + olive::config.thumbnail_resolution = thumbnail_res_spinbox->value(); // delete only thumbnails delete_type = DELETE_THUMBNAILS; } - if (olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()) { + if (olive::config.waveform_resolution != waveform_res_spinbox->value()) { // delete existing waveforms - olive::CurrentConfig.waveform_resolution = waveform_res_spinbox->value(); + olive::config.waveform_resolution = waveform_res_spinbox->value(); // if we're already deleting thumbnails if (delete_type == DELETE_THUMBNAILS) { @@ -749,7 +749,7 @@ void PreferencesDialog::setup_ui() { QString locale_str = locale_file_basename.mid(locale_file_basename.lastIndexOf('_')+1); language_combobox->addItem(QLocale(locale_str).nativeLanguageName(), locale_relative_path); - if (olive::CurrentConfig.language_file == locale_relative_path) { + if (olive::config.language_file == locale_relative_path) { language_combobox->setCurrentIndex(language_combobox->count() - 1); } } @@ -764,7 +764,7 @@ void PreferencesDialog::setup_ui() { general_layout->addWidget(new QLabel(tr("Image sequence formats:"), this), row, 0); imgSeqFormatEdit = new QLineEdit(general_tab); - imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats); + imgSeqFormatEdit->setText(olive::config.img_seq_formats); general_layout->addWidget(imgSeqFormatEdit, row, 1, 1, 4); row++; @@ -775,7 +775,7 @@ void PreferencesDialog::setup_ui() { thumbnail_res_spinbox = new QSpinBox(this); thumbnail_res_spinbox->setMinimum(0); thumbnail_res_spinbox->setMaximum(INT_MAX); - thumbnail_res_spinbox->setValue(olive::CurrentConfig.thumbnail_resolution); + thumbnail_res_spinbox->setValue(olive::config.thumbnail_resolution); general_layout->addWidget(thumbnail_res_spinbox, row, 1); general_layout->addWidget(new QLabel(tr("Waveform Resolution:"), this), row, 2); @@ -783,7 +783,7 @@ void PreferencesDialog::setup_ui() { waveform_res_spinbox = new QSpinBox(this); waveform_res_spinbox->setMinimum(0); waveform_res_spinbox->setMaximum(INT_MAX); - waveform_res_spinbox->setValue(olive::CurrentConfig.waveform_resolution); + waveform_res_spinbox->setValue(olive::config.waveform_resolution); general_layout->addWidget(waveform_res_spinbox, row, 3); QPushButton* delete_preview_btn = new QPushButton(tr("Delete Previews")); @@ -796,13 +796,13 @@ void PreferencesDialog::setup_ui() { // General -> Use Software Fallbacks When Possible QCheckBox* use_software_fallbacks_checkbox = new QCheckBox(tr("Use Software Fallbacks When Possible")); - AddBoolPair(use_software_fallbacks_checkbox, &olive::CurrentConfig.use_software_fallback, true); + AddBoolPair(use_software_fallbacks_checkbox, &olive::config.use_software_fallback, true); misc_general->addWidget(use_software_fallbacks_checkbox); // General -> Don't Use Proxies When Exporting QCheckBox* dont_use_proxies_when_exporting = new QCheckBox(tr("Don't Use Proxies When Exporting")); dont_use_proxies_when_exporting->setToolTip(tr("Use originals instead of proxies when exporting")); - AddBoolPair(dont_use_proxies_when_exporting, &olive::CurrentConfig.dont_use_proxies_on_export); + AddBoolPair(dont_use_proxies_when_exporting, &olive::config.dont_use_proxies_on_export); misc_general->addWidget(dont_use_proxies_when_exporting); // General -> Default Sequence Settings @@ -823,68 +823,68 @@ void PreferencesDialog::setup_ui() { ColumnedGridLayout* behavior_tab_layout = new ColumnedGridLayout(behavior_tab, 2); QCheckBox* add_default_effects_to_clips = new QCheckBox(tr("Add Default Effects to New Clips")); - AddBoolPair(add_default_effects_to_clips, &olive::CurrentConfig.add_default_effects_to_clips); + AddBoolPair(add_default_effects_to_clips, &olive::config.add_default_effects_to_clips); behavior_tab_layout->Add(add_default_effects_to_clips); QCheckBox* auto_seek_to_beginning = new QCheckBox(tr("Automatically Seek to the Beginning When Playing at the End of a Sequence")); - AddBoolPair(auto_seek_to_beginning, &olive::CurrentConfig.auto_seek_to_beginning); + AddBoolPair(auto_seek_to_beginning, &olive::config.auto_seek_to_beginning); behavior_tab_layout->Add(auto_seek_to_beginning); QCheckBox* selecting_also_seeks = new QCheckBox(tr("Selecting Also Seeks")); - AddBoolPair(selecting_also_seeks, &olive::CurrentConfig.select_also_seeks); + AddBoolPair(selecting_also_seeks, &olive::config.select_also_seeks); behavior_tab_layout->Add(selecting_also_seeks); QCheckBox* edit_tool_also_seeks = new QCheckBox(tr("Edit Tool Also Seeks")); - AddBoolPair(edit_tool_also_seeks, &olive::CurrentConfig.edit_tool_also_seeks); + AddBoolPair(edit_tool_also_seeks, &olive::config.edit_tool_also_seeks); behavior_tab_layout->Add(edit_tool_also_seeks); QCheckBox* edit_tool_selects_links = new QCheckBox(tr("Edit Tool Selects Links")); - AddBoolPair(edit_tool_selects_links, &olive::CurrentConfig.edit_tool_selects_links); + AddBoolPair(edit_tool_selects_links, &olive::config.edit_tool_selects_links); behavior_tab_layout->Add(edit_tool_selects_links); QCheckBox* seek_also_selects = new QCheckBox(tr("Seek Also Selects")); - AddBoolPair(seek_also_selects, &olive::CurrentConfig.seek_also_selects); + AddBoolPair(seek_also_selects, &olive::config.seek_also_selects); behavior_tab_layout->Add(seek_also_selects); QCheckBox* seek_to_end_of_pastes = new QCheckBox(tr("Seek to the End of Pastes")); - AddBoolPair(seek_to_end_of_pastes, &olive::CurrentConfig.paste_seeks); + AddBoolPair(seek_to_end_of_pastes, &olive::config.paste_seeks); behavior_tab_layout->Add(seek_to_end_of_pastes); QCheckBox* scroll_wheel_zooms = new QCheckBox(tr("Scroll Wheel Zooms")); scroll_wheel_zooms->setToolTip(tr("Hold CTRL to toggle this setting")); - AddBoolPair(scroll_wheel_zooms, &olive::CurrentConfig.scroll_zooms); + AddBoolPair(scroll_wheel_zooms, &olive::config.scroll_zooms); behavior_tab_layout->Add(scroll_wheel_zooms); QCheckBox* invert_timeline_scroll_axes = new QCheckBox(tr("Invert Timeline Scroll Axes")); - AddBoolPair(invert_timeline_scroll_axes, &olive::CurrentConfig.invert_timeline_scroll_axes); + AddBoolPair(invert_timeline_scroll_axes, &olive::config.invert_timeline_scroll_axes); behavior_tab_layout->Add(invert_timeline_scroll_axes); QCheckBox* enable_drag_files_to_timeline = new QCheckBox(tr("Enable Drag Files to Timeline")); - AddBoolPair(enable_drag_files_to_timeline, &olive::CurrentConfig.enable_drag_files_to_timeline); + AddBoolPair(enable_drag_files_to_timeline, &olive::config.enable_drag_files_to_timeline); behavior_tab_layout->Add(enable_drag_files_to_timeline); QCheckBox* autoscale_by_default = new QCheckBox(tr("Auto-Scale By Default")); - AddBoolPair(autoscale_by_default, &olive::CurrentConfig.autoscale_by_default); + AddBoolPair(autoscale_by_default, &olive::config.autoscale_by_default); behavior_tab_layout->Add(autoscale_by_default); QCheckBox* enable_seek_to_import = new QCheckBox(tr("Auto-Seek to Imported Clips")); - AddBoolPair(enable_seek_to_import, &olive::CurrentConfig.enable_seek_to_import); + AddBoolPair(enable_seek_to_import, &olive::config.enable_seek_to_import); behavior_tab_layout->Add(enable_seek_to_import); QCheckBox* enable_audio_scrubbing = new QCheckBox(tr("Audio Scrubbing")); - AddBoolPair(enable_audio_scrubbing, &olive::CurrentConfig.enable_audio_scrubbing); + AddBoolPair(enable_audio_scrubbing, &olive::config.enable_audio_scrubbing); behavior_tab_layout->Add(enable_audio_scrubbing); QCheckBox* enable_drop_on_media_to_replace = new QCheckBox(tr("Drop Files on Media to Replace")); - AddBoolPair(enable_drop_on_media_to_replace, &olive::CurrentConfig.drop_on_media_to_replace); + AddBoolPair(enable_drop_on_media_to_replace, &olive::config.drop_on_media_to_replace); behavior_tab_layout->Add(enable_drop_on_media_to_replace); QCheckBox* enable_hover_focus = new QCheckBox(tr("Enable Hover Focus")); - AddBoolPair(enable_hover_focus, &olive::CurrentConfig.hover_focus); + AddBoolPair(enable_hover_focus, &olive::config.hover_focus); behavior_tab_layout->Add(enable_hover_focus); QCheckBox* set_name_and_marker = new QCheckBox(tr("Ask For Name When Setting Marker")); - AddBoolPair(set_name_and_marker, &olive::CurrentConfig.set_name_with_marker); + AddBoolPair(set_name_and_marker, &olive::config.set_name_with_marker); behavior_tab_layout->Add(set_name_and_marker); // Appearance @@ -903,7 +903,7 @@ void PreferencesDialog::setup_ui() { ui_style->addItem(tr("Olive Light"), olive::styling::kOliveDefaultLight); ui_style->addItem(tr("Native"), olive::styling::kNativeDarkIcons); ui_style->addItem(tr("Native (Light Icons)"), olive::styling::kNativeLightIcons); - ui_style->setCurrentIndex(olive::CurrentConfig.style); + ui_style->setCurrentIndex(olive::config.style); appearance_layout->addWidget(ui_style, row, 1, 1, 2); row++; @@ -922,7 +922,7 @@ void PreferencesDialog::setup_ui() { appearance_layout->addWidget(new QLabel(tr("Custom CSS:"), this), row, 0); custom_css_fn = new QLineEdit(general_tab); - custom_css_fn->setText(olive::CurrentConfig.css_path); + custom_css_fn->setText(olive::config.css_path); appearance_layout->addWidget(custom_css_fn, row, 1); QPushButton* custom_css_browse = new QPushButton(tr("Browse"), general_tab); @@ -936,7 +936,7 @@ void PreferencesDialog::setup_ui() { effect_textbox_lines_field = new QSpinBox(general_tab); effect_textbox_lines_field->setMinimum(1); - effect_textbox_lines_field->setValue(olive::CurrentConfig.effect_textbox_lines); + effect_textbox_lines_field->setValue(olive::config.effect_textbox_lines); appearance_layout->addWidget(effect_textbox_lines_field, row, 1, 1, 2); row++; @@ -951,21 +951,21 @@ void PreferencesDialog::setup_ui() { QGridLayout* memory_usage_layout = new QGridLayout(memory_usage_group); memory_usage_layout->addWidget(new QLabel(tr("Upcoming Frame Queue:"), playback_tab), 0, 0); upcoming_queue_spinbox = new QDoubleSpinBox(playback_tab); - upcoming_queue_spinbox->setValue(olive::CurrentConfig.upcoming_queue_size); + upcoming_queue_spinbox->setValue(olive::config.upcoming_queue_size); memory_usage_layout->addWidget(upcoming_queue_spinbox, 0, 1); upcoming_queue_type = new QComboBox(playback_tab); upcoming_queue_type->addItem(tr("frames")); upcoming_queue_type->addItem(tr("seconds")); - upcoming_queue_type->setCurrentIndex(olive::CurrentConfig.upcoming_queue_type); + upcoming_queue_type->setCurrentIndex(olive::config.upcoming_queue_type); memory_usage_layout->addWidget(upcoming_queue_type, 0, 2); memory_usage_layout->addWidget(new QLabel(tr("Previous Frame Queue:"), playback_tab), 1, 0); previous_queue_spinbox = new QDoubleSpinBox(playback_tab); - previous_queue_spinbox->setValue(olive::CurrentConfig.previous_queue_size); + previous_queue_spinbox->setValue(olive::config.previous_queue_size); memory_usage_layout->addWidget(previous_queue_spinbox, 1, 1); previous_queue_type = new QComboBox(playback_tab); previous_queue_type->addItem(tr("frames")); previous_queue_type->addItem(tr("seconds")); - previous_queue_type->setCurrentIndex(olive::CurrentConfig.previous_queue_type); + previous_queue_type->setCurrentIndex(olive::config.previous_queue_type); memory_usage_layout->addWidget(previous_queue_type, 1, 2); playback_tab_layout->addWidget(memory_usage_group); @@ -991,7 +991,7 @@ void PreferencesDialog::setup_ui() { for (int i=0;iaddItem(devs.at(i).deviceName(), devs.at(i).deviceName()); if (!found_preferred_device - && devs.at(i).deviceName() == olive::CurrentConfig.preferred_audio_output) { + && devs.at(i).deviceName() == olive::config.preferred_audio_output) { audio_output_devices->setCurrentIndex(audio_output_devices->count()-1); found_preferred_device = true; } @@ -1014,7 +1014,7 @@ void PreferencesDialog::setup_ui() { for (int i=0;iaddItem(devs.at(i).deviceName(), devs.at(i).deviceName()); if (!found_preferred_device - && devs.at(i).deviceName() == olive::CurrentConfig.preferred_audio_input) { + && devs.at(i).deviceName() == olive::config.preferred_audio_input) { audio_input_devices->setCurrentIndex(audio_input_devices->count()-1); found_preferred_device = true; } @@ -1031,7 +1031,7 @@ void PreferencesDialog::setup_ui() { audio_sample_rate = new QComboBox(); combobox_audio_sample_rates(audio_sample_rate); for (int i=0;icount();i++) { - if (audio_sample_rate->itemData(i).toInt() == olive::CurrentConfig.audio_rate) { + if (audio_sample_rate->itemData(i).toInt() == olive::config.audio_rate) { audio_sample_rate->setCurrentIndex(i); break; } @@ -1047,7 +1047,7 @@ void PreferencesDialog::setup_ui() { recordingComboBox = new QComboBox(general_tab); recordingComboBox->addItem(tr("Mono")); recordingComboBox->addItem(tr("Stereo")); - recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1); + recordingComboBox->setCurrentIndex(olive::config.recording_mode - 1); audio_tab_layout->addWidget(recordingComboBox, row, 1); row++; @@ -1066,7 +1066,7 @@ void PreferencesDialog::setup_ui() { // COLOR MANAGEMENT -> Enable Color Management enable_color_management = new QCheckBox(tr("Enable Color Management")); - enable_color_management->setChecked(olive::CurrentConfig.enable_color_management); + enable_color_management->setChecked(olive::config.enable_color_management); color_management_layout->addWidget(enable_color_management, row, 0); row++; @@ -1078,7 +1078,7 @@ void PreferencesDialog::setup_ui() { opencolorio_groupbox_layout->addWidget(new QLabel(tr("OpenColorIO Config File:")), 0, 0); ocio_config_file = new QLineEdit(); - ocio_config_file->setText(olive::CurrentConfig.ocio_config_path); + ocio_config_file->setText(olive::config.ocio_config_path); connect(ocio_config_file, SIGNAL(textChanged(const QString &)), this, SLOT(update_ocio_config(const QString&))); opencolorio_groupbox_layout->addWidget(ocio_config_file, 0, 1, 1, 4); @@ -1120,7 +1120,7 @@ void PreferencesDialog::setup_ui() { for (int i=0;iaddItem(olive::pixel_formats.at(i).name, i); } - playback_bit_depth->setCurrentIndex(olive::CurrentConfig.playback_bit_depth); + playback_bit_depth->setCurrentIndex(olive::config.playback_bit_depth); bit_depth_groupbox_layout->addWidget(new QLabel(tr("Playback (Offline):")), 0, 0); bit_depth_groupbox_layout->addWidget(playback_bit_depth, 0, 1); @@ -1129,7 +1129,7 @@ void PreferencesDialog::setup_ui() { for (int i=0;iaddItem(olive::pixel_formats.at(i).name, i); } - export_bit_depth->setCurrentIndex(olive::CurrentConfig.export_bit_depth); + export_bit_depth->setCurrentIndex(olive::config.export_bit_depth); bit_depth_groupbox_layout->addWidget(new QLabel(tr("Export (Online):")), 0, 2); bit_depth_groupbox_layout->addWidget(export_bit_depth, 0, 3); diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp index 67e73e386..d90452a6e 100644 --- a/dialogs/replaceclipmediadialog.cpp +++ b/dialogs/replaceclipmediadialog.cpp @@ -95,7 +95,10 @@ void ReplaceClipMediaDialog::accept() { QMessageBox::Ok ); } else { - if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == new_item->to_sequence()) { + + SequencePtr top_sequence = Timeline::GetTopSequence(); + + if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && top_sequence == new_item->to_sequence()) { QMessageBox::critical( this, tr("Active sequence selected"), @@ -109,14 +112,15 @@ void ReplaceClipMediaDialog::accept() { use_same_media_in_points->isChecked() ); - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr && c->media() == media) { + QVector all_clips = top_sequence->GetAllClips(); + for (int i=0;imedia() == media) { rcmc->clips.append(c); } } - olive::UndoStack.push(rcmc); + olive::undo_stack.push(rcmc); QDialog::accept(); } diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index ff3f9ddd5..0e11063f9 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -61,7 +61,7 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector clips) : QDialog(parent grid->addWidget(new QLabel(tr("Duration:"), this), 2, 0); duration = new LabelSlider(this); duration->SetDisplayType(LabelSlider::FrameNumber); - duration->SetFrameRate(olive::ActiveSequence->frame_rate); + duration->SetFrameRate(clips_.first()->track()->sequence()->frame_rate); grid->addWidget(duration, 2, 1); main_layout->addLayout(grid); @@ -103,7 +103,7 @@ int SpeedDialog::exec() { // get default frame rate/percentage clip_percent = c->speed().value; - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { bool process_video = true; if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { FootageStream* ms = c->media_stream(); @@ -131,7 +131,7 @@ int SpeedDialog::exec() { enable_frame_rate = true; } - } else { + } else if (c->type() == Track::kTypeAudio) { maintain_pitch->setEnabled(true); if (!multiple_audio) { @@ -192,7 +192,7 @@ void SpeedDialog::percent_update() { Clip* c = clips_.at(i); // get frame rate - if (frame_rate->isEnabled() && c->track() < 0) { + if (frame_rate->isEnabled() && c->type() == Track::kTypeVideo) { double clip_fr = c->media_frame_rate() * percent->value(); if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { @@ -236,7 +236,7 @@ void SpeedDialog::duration_update() { } // get frame rate - if (frame_rate->isEnabled() && c->track() < 0) { + if (frame_rate->isEnabled() && c->type() == Track::kTypeVideo) { double clip_fr = c->media_frame_rate() * clip_pc; if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { @@ -276,7 +276,7 @@ void SpeedDialog::frame_rate_update() { old_pc_val = qSNaN(); } - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { // what would the new speed be based on this frame rate double new_clip_speed = frame_rate->value() / c->media_frame_rate(); if (!got_pc_val) { @@ -301,7 +301,7 @@ void SpeedDialog::frame_rate_update() { for (int i=0;itrack() >= 0) { + if (c->type() == Track::kTypeAudio) { long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ? c->length() : qRound((c->length() * c->speed().value) / pc_val); @@ -318,15 +318,17 @@ void SpeedDialog::frame_rate_update() { } void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, long& lr) { - panel_timeline->deselect_area(c->timeline_in(), c->timeline_out(), c->track()); + c->track()->DeselectArea(c->timeline_in(), c->timeline_out()); long proposed_out = c->timeline_out(); double multiplier = (c->speed().value / speed); proposed_out = qRound(c->timeline_in() + (c->length() * multiplier)); ca->append(new SetSpeedAction(c, speed)); if (!ripple && proposed_out > c->timeline_out()) { - for (int i=0;isequence->clips.size();i++) { - ClipPtr compare = c->sequence->clips.at(i); + QVector all_clips = c->track()->sequence()->GetAllClips(); + + for (int i=0;itrack() == c->track() && compare->timeline_in() >= c->timeline_out() && compare->timeline_in() < proposed_out) { @@ -336,15 +338,16 @@ void set_speed(ComboAction* ca, Clip* c, double speed, bool ripple, long& ep, lo } ep = qMin(ep, c->timeline_out()); lr = qMax(lr, proposed_out - c->timeline_out()); - c->move(ca, c->timeline_in(), proposed_out, qRound(c->clip_in() * multiplier), c->track()); + c->track()->sequence()->MoveClip(c, + ca, + c->timeline_in(), + proposed_out, + qRound(c->clip_in() * multiplier), + c->track()); c->refactor_frame_rate(ca, multiplier, false); - Selection sel; - sel.in = c->timeline_in(); - sel.out = proposed_out; - sel.track = c->track(); - olive::ActiveSequence->selections.append(sel); + c->track()->SelectArea(c->timeline_in(), proposed_out); } void SpeedDialog::accept() { @@ -357,8 +360,8 @@ void SpeedDialog::accept() { SetClipProperty* reversed_action = new SetClipProperty(kSetClipPropertyReversed); // undoable action for restoring clip selections - SetSelectionsCommand* sel_command = new SetSelectionsCommand(olive::ActiveSequence.get()); - sel_command->old_data = olive::ActiveSequence->selections; + Sequence* sequence = clips_.first()->track()->sequence(); + QVector old_selections = sequence->Selections(); // variables used to calculate ripples long earliest_point = LONG_MAX; @@ -373,7 +376,7 @@ void SpeedDialog::accept() { } // set maintain audio pitch if the user made a selection - if (c->track() >= 0 + if (c->type() == Track::kTypeAudio && maintain_pitch->checkState() != Qt::PartiallyChecked && c->speed().maintain_audio_pitch != maintain_pitch->isChecked()) { audio_pitch_action->AddSetting(c, maintain_pitch->isChecked()); @@ -382,7 +385,12 @@ void SpeedDialog::accept() { // set reverse setting if the user made a selection if (reverse->checkState() != Qt::PartiallyChecked && c->reversed() != reverse->isChecked()) { long new_clip_in = (c->media_length() - (c->length() + c->clip_in())); - c->move(ca, c->timeline_in(), c->timeline_out(), new_clip_in, c->track()); + c->track()->sequence()->MoveClip(c, + ca, + c->timeline_in(), + c->timeline_out(), + new_clip_in, + c->track()); c->set_clip_in(new_clip_in); reversed_action->AddSetting(c, reverse->isChecked()); } @@ -423,7 +431,7 @@ void SpeedDialog::accept() { // make changes for (int i=0;itrack() < 0) { + if (c->type() == Track::kTypeVideo) { set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple); } else if (can_change_all) { set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple); @@ -438,16 +446,15 @@ void SpeedDialog::accept() { } if (ripple->isChecked()) { - ripple_clips(ca, clips_.at(0)->sequence, earliest_point, longest_ripple); + sequence->Ripple(ca, earliest_point, longest_ripple); } - sel_command->new_data = olive::ActiveSequence->selections; - ca->append(sel_command); + ca->append(new SetSelectionsCommand(sequence, old_selections, sequence->Selections())); ca->append(reversed_action); ca->append(audio_pitch_action); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(true); QDialog::accept(); diff --git a/effects/effect.cpp b/effects/effect.cpp index e142ad890..727bcff50 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -48,7 +48,7 @@ #include "global/path.h" #include "ui/mainwindow.h" #include "global/math.h" -#include "project/clipboard.h" +#include "global/clipboard.h" #include "global/config.h" #include "transition.h" #include "undo/undostack.h" @@ -412,7 +412,7 @@ void Effect::FieldChanged() { } void Effect::delete_self() { - olive::UndoStack.push(new EffectDeleteCommand(this)); + olive::undo_stack.push(new EffectDeleteCommand(this)); update_ui(true); } @@ -426,7 +426,7 @@ void Effect::move_up() { command->clip = parent_clip; command->from = index_of_effect; command->to = command->from - 1; - olive::UndoStack.push(command); + olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); } @@ -441,7 +441,7 @@ void Effect::move_down() { command->clip = parent_clip; command->from = index_of_effect; command->to = command->from + 1; - olive::UndoStack.push(command); + olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); } @@ -488,7 +488,7 @@ void Effect::load_from_file() { QFile file_handle(file); if (file_handle.open(QFile::ReadOnly)) { - olive::UndoStack.push(new SetEffectData(this, file_handle.readAll())); + olive::undo_stack.push(new SetEffectData(this, file_handle.readAll())); file_handle.close(); @@ -743,7 +743,7 @@ void Effect::open() { qWarning() << "Tried to open an effect that was already open"; close(); } - if (olive::CurrentRuntimeConfig.shaders_are_enabled && (Flags() & ShaderFlag)) { + if (olive::runtime_config.shaders_are_enabled && (Flags() & ShaderFlag)) { if (QOpenGLContext::currentContext() == nullptr) { qWarning() << "No current context to create a shader program for - will retry next repaint"; } else { @@ -1023,7 +1023,7 @@ void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, doub ca->append(gizmo_dragging_actions_.at(j)); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); gizmo_dragging_actions_.clear(); } @@ -1044,10 +1044,10 @@ void Effect::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& p projection, QRect(0, 0, - parent_clip->sequence->width, - parent_clip->sequence->height)); + parent_clip->track()->sequence()->width, + parent_clip->track()->sequence()->height)); - g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->sequence->height-screen_pos.y()); + g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->track()->sequence()->height-screen_pos.y()); } } diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 61d6a0431..7446fbc5f 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -235,13 +235,13 @@ void EffectField::SetValueAt(double time, const QVariant &value) double EffectField::Now() { Clip* c = GetParentRow()->GetParentEffect()->parent_clip; - return playhead_to_clip_seconds(c, c->sequence->playhead); + return playhead_to_clip_seconds(c, c->track()->sequence()->playhead); } long EffectField::NowInFrames() { Clip* c = GetParentRow()->GetParentEffect()->parent_clip; - return playhead_to_clip_frame(c, c->sequence->playhead); + return playhead_to_clip_frame(c, c->track()->sequence()->playhead); } void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) @@ -331,11 +331,11 @@ double EffectField::GetValidKeyframeHandlePosition(int key, bool post) { } double EffectField::FrameToSeconds(long frame) { - return (double(frame) / GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate); + return (double(frame) / GetParentRow()->GetParentEffect()->parent_clip->track()->sequence()->frame_rate); } long EffectField::SecondsToFrame(double seconds) { - return qRound(seconds * GetParentRow()->GetParentEffect()->parent_clip->sequence->frame_rate); + return qRound(seconds * GetParentRow()->GetParentEffect()->parent_clip->track()->sequence()->frame_rate); } void EffectField::GetKeyframeData(double timecode, int &before, int &after, double &progress) { diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index 131db86f5..ebb840fbd 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -34,7 +34,7 @@ QMutex olive::effects_loaded; void load_internal_effects() { - if (!olive::CurrentRuntimeConfig.shaders_are_enabled) { + if (!olive::runtime_config.shaders_are_enabled) { qWarning() << "Shaders are disabled, some effects may be nonfunctional"; } @@ -44,7 +44,7 @@ void load_internal_effects() { em.path = ":/internalshaders"; em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_AUDIO; + em.subtype = Track::kTypeAudio; em.name = "Volume"; em.internal = EFFECT_INTERNAL_VOLUME; @@ -70,7 +70,7 @@ void load_internal_effects() { em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; olive::effects.append(em); - em.subtype = EFFECT_TYPE_VIDEO; + em.subtype = Track::kTypeVideo; em.name = "Transform"; em.category = "Distort"; @@ -115,7 +115,7 @@ void load_internal_effects() { em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; olive::effects.append(em); - em.subtype = EFFECT_TYPE_AUDIO; + em.subtype = Track::kTypeAudio; em.name = "Linear Fade"; em.internal = TRANSITION_INTERNAL_LINEARFADE; @@ -181,7 +181,7 @@ void load_shader_effects_worker(const QString& effects_path) { if (!effect_name.isEmpty()) { EffectMeta em; em.type = EFFECT_TYPE_EFFECT; - em.subtype = EFFECT_TYPE_VIDEO; + em.subtype = Track::kTypeVideo; em.name = effect_name; em.category = effect_cat; em.filename = file.fileName(); diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index f78a017ce..02305a7f8 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -93,7 +93,7 @@ void EffectRow::SetKeyframingEnabled(bool enabled) { Field(i)->PrepareDataForKeyframing(true, ca); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); @@ -116,7 +116,7 @@ void EffectRow::SetKeyframingEnabled(bool enabled) { // Disable keyframing setting on this row ca->append(new SetIsKeyframing(this, false)); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); @@ -131,7 +131,7 @@ void EffectRow::SetKeyframingEnabled(bool enabled) { void EffectRow::GoToPreviousKeyframe() { long key = LONG_MIN; Clip* c = GetParentEffect()->parent_clip; - long sequence_playhead = c->sequence->playhead; + long sequence_playhead = c->track()->sequence()->playhead; // Used to convert clip frame number to sequence frame number long time_adjustment = c->timeline_in() - c->clip_in(); @@ -158,7 +158,7 @@ void EffectRow::GoToPreviousKeyframe() { void EffectRow::ToggleKeyframe() { Clip* c = GetParentEffect()->parent_clip; - long sequence_playhead = c->sequence->playhead; + long sequence_playhead = c->track()->sequence()->playhead; // Used to convert clip frame number to sequence frame number long time_adjustment = c->timeline_in() - c->clip_in(); @@ -222,7 +222,7 @@ void EffectRow::ToggleKeyframe() { } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); } @@ -233,7 +233,7 @@ void EffectRow::GoToNextKeyframe() { EffectField* f = Field(i); for (int j=0;jkeyframes.size();j++) { long comp = f->keyframes.at(j).time - c->clip_in() + c->timeline_in(); - if (comp > olive::ActiveSequence->playhead) { + if (comp > c->track()->sequence()->playhead) { key = qMin(comp, key); } } diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index f4db7d85d..13c958912 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -92,5 +92,5 @@ void BoolField::UpdateFromWidget(bool b) SetValueAt(Now(), b); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index 66433db10..b294f9e78 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -67,5 +67,5 @@ void ColorField::UpdateFromWidget(const QColor& c) SetValueAt(Now(), c); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index f5b803679..133e704d3 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -87,5 +87,5 @@ void ComboField::UpdateFromWidget(int index) SetValueAt(Now(), items_.at(index).data); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 9e0527a7d..58603d4b0 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -143,7 +143,7 @@ void DoubleField::UpdateFromWidget(double d) if (!ls->IsDragging() && kdc_ != nullptr) { kdc_->SetNewKeyframes(); - olive::UndoStack.push(kdc_); + olive::undo_stack.push(kdc_); kdc_ = nullptr; } diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index 2756fa8a5..b40eede59 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -62,5 +62,5 @@ void FileField::UpdateFromWidget(const QString &s) SetValueAt(Now(), s); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 9479fb60c..5876bdd78 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -89,5 +89,5 @@ void FontField::UpdateFromWidget(const QString& s) SetValueAt(Now(), s); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 89ea1f74b..57439473a 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -51,7 +51,7 @@ QWidget *StringField::CreateWidget(QWidget *existing) text_edit->setUndoRedoEnabled(true); // the "2" is because the height needs one extra pixel of padding on the top and the bottom - text_edit->setTextHeight(qCeil(text_edit->fontMetrics().lineSpacing()*olive::CurrentConfig.effect_textbox_lines + text_edit->setTextHeight(qCeil(text_edit->fontMetrics().lineSpacing()*olive::config.effect_textbox_lines + text_edit->document()->documentMargin() + text_edit->document()->documentMargin() + 2)); @@ -95,5 +95,5 @@ void StringField::UpdateFromWidget(const QString &s) SetValueAt(Now(), s); kdc->SetNewKeyframes(); - olive::UndoStack.push(kdc); + olive::undo_stack.push(kdc); } diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 84276fc34..613b39383 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -88,14 +88,16 @@ TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) : void TimecodeEffect::redraw(double timecode) { + Sequence* sequence = parent_clip->track()->sequence(); + if (tc_select->GetValueAt(timecode).toBool()) { - display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(olive::ActiveSequence->playhead, - olive::CurrentConfig.timecode_view, - olive::ActiveSequence->frame_rate); + display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(sequence->playhead, + olive::config.timecode_view, + sequence->frame_rate); } else { double media_rate = parent_clip->media_frame_rate(); display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(qRound(timecode * media_rate), - olive::CurrentConfig.timecode_view, + olive::config.timecode_view, media_rate); } img.fill(Qt::transparent); diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index b88cfbedc..f877083b6 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -55,7 +55,7 @@ void ToneEffect::process_audio(double timecode_start, double timecode_end, quint double timecode = timecode_start+(interval*i); qint16 left_tone_sample = qint16(qRound(qSin((2*M_PI*sinX*freq_val->GetDoubleAt(timecode)) - /parent_clip->sequence->audio_frequency) + /parent_clip->track()->sequence()->audio_frequency) *log_volume(amount_val->GetDoubleAt(timecode)*0.01)*INT16_MAX)); qint16 right_tone_sample = left_tone_sample; diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 75d9ccc70..425c00bae 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -152,13 +152,13 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) } void TransformEffect::refresh() { - if (parent_clip != nullptr && parent_clip->sequence != nullptr) { + if (parent_clip != nullptr && parent_clip->track()->sequence() != nullptr) { - position_x->SetDefault(parent_clip->sequence->width/2); - position_y->SetDefault(parent_clip->sequence->height/2); + position_x->SetDefault(parent_clip->track()->sequence()->width/2); + position_y->SetDefault(parent_clip->track()->sequence()->height/2); - double x_percent_multipler = 200.0 / parent_clip->sequence->width; - double y_percent_multipler = 200.0 / parent_clip->sequence->height; + double x_percent_multipler = 200.0 / parent_clip->track()->sequence()->width; + double y_percent_multipler = 200.0 / parent_clip->track()->sequence()->height; top_left_gizmo->x_field_multi1 = -x_percent_multipler; top_left_gizmo->y_field_multi1 = -y_percent_multipler; @@ -190,8 +190,8 @@ void TransformEffect::toggle_uniform_scale(bool enabled) { void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) { // position - coords.matrix.translate(position_x->GetDoubleAt(timecode)-(parent_clip->sequence->width/2), - position_y->GetDoubleAt(timecode)-(parent_clip->sequence->height/2), + coords.matrix.translate(position_x->GetDoubleAt(timecode)-(parent_clip->track()->sequence()->width/2), + position_y->GetDoubleAt(timecode)-(parent_clip->track()->sequence()->height/2), 0); // anchor point diff --git a/effects/keyframe.cpp b/effects/keyframe.cpp index 83f580207..870de3123 100644 --- a/effects/keyframe.cpp +++ b/effects/keyframe.cpp @@ -60,7 +60,7 @@ void delete_keyframes(QVector& selected_key_fields, QVector for (int i=0;iappend(new KeyframeDelete(fields.at(i), key_indices.at(i))); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); selected_keys.clear(); selected_key_fields.clear(); update_ui(false); diff --git a/effects/transition.cpp b/effects/transition.cpp index e7f3585e3..469aa59dd 100644 --- a/effects/transition.cpp +++ b/effects/transition.cpp @@ -24,8 +24,7 @@ #include "timeline/clip.h" #include "timeline/sequence.h" #include "global/debug.h" - -#include "project/clipboard.h" +#include "global/clipboard.h" #include "effects/internal/crossdissolvetransition.h" #include "effects/internal/linearfadetransition.h" @@ -50,8 +49,8 @@ Transition::Transition(Clip *c, Clip *s, const EffectMeta* em) : length_field->SetDefault(30); length_field->SetMinimum(1); length_field->SetDisplayType(LabelSlider::FrameNumber); - length_field->SetFrameRate(parent_clip->sequence == nullptr ? - parent_clip->cached_frame_rate() : parent_clip->sequence->frame_rate); + length_field->SetFrameRate(parent_clip->track()->sequence() == nullptr ? + parent_clip->cached_frame_rate() : parent_clip->track()->sequence()->frame_rate); connect(length_field, SIGNAL(Changed()), this, SLOT(UpdateMaximumLength())); } diff --git a/project/clipboard.cpp b/global/clipboard.cpp similarity index 86% rename from project/clipboard.cpp rename to global/clipboard.cpp index 9644dd840..c2157ba22 100644 --- a/project/clipboard.cpp +++ b/global/clipboard.cpp @@ -34,6 +34,16 @@ void Clipboard::Append(VoidPtr obj) clipboard_.append(obj); } +void Clipboard::Insert(int pos, VoidPtr obj) +{ + clipboard_.insert(pos, obj); +} + +void Clipboard::RemoveAt(int pos) +{ + clipboard_.removeAt(pos); +} + void Clipboard::Clear() { clipboard_.clear(); @@ -50,6 +60,16 @@ VoidPtr Clipboard::Get(int i) return clipboard_.at(i); } +void Clipboard::SetType(Clipboard::Type type) +{ + if (type == type_) { + return; + } + + Clear(); + type_ = type; +} + bool Clipboard::IsEmpty() { return clipboard_.isEmpty(); diff --git a/project/clipboard.h b/global/clipboard.h similarity index 95% rename from project/clipboard.h rename to global/clipboard.h index 6cbdb0992..9a2366256 100644 --- a/project/clipboard.h +++ b/global/clipboard.h @@ -36,6 +36,8 @@ public: Clipboard(); void Append(VoidPtr obj); + void Insert(int pos, VoidPtr obj); + void RemoveAt(int pos); void Clear(); int Count(); VoidPtr Get(int i); diff --git a/global/config.cpp b/global/config.cpp index 799abe529..cd22f52a0 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -29,12 +29,11 @@ #include "debug.h" -Config olive::CurrentConfig; -RuntimeConfig olive::CurrentRuntimeConfig; +Config olive::config; +RuntimeConfig olive::runtime_config; Config::Config() - : show_track_lines(true), - scroll_zooms(false), + : scroll_zooms(false), edit_tool_selects_links(false), edit_tool_also_seeks(false), select_also_seeks(false), @@ -94,10 +93,7 @@ void Config::load(QString path) { while (!stream.atEnd()) { stream.readNext(); if (stream.isStartElement()) { - if (stream.name() == "ShowTrackLines") { - stream.readNext(); - show_track_lines = (stream.text() == "1"); - } else if (stream.name() == "ScrollZooms") { + if (stream.name() == "ScrollZooms") { stream.readNext(); scroll_zooms = (stream.text() == "1"); } else if (stream.name() == "InvertTimelineScrollAxes") { @@ -295,7 +291,6 @@ void Config::save(QString path) { stream.writeStartElement("Configuration"); // configuration stream.writeTextElement("Version", QString::number(olive::kSaveVersion)); - stream.writeTextElement("ShowTrackLines", QString::number(show_track_lines)); stream.writeTextElement("ScrollZooms", QString::number(scroll_zooms)); stream.writeTextElement("InvertTimelineScrollAxes", QString::number(invert_timeline_scroll_axes)); stream.writeTextElement("EditToolSelectsLinks", QString::number(edit_tool_selects_links)); @@ -320,7 +315,7 @@ void Config::save(QString path) { stream.writeTextElement("HoverFocus", QString::number(hover_focus)); stream.writeTextElement("ProjectViewType", QString::number(project_view_type)); stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker)); - stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->IsToolbarVisible())); + stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project.first()->IsToolbarVisible())); stream.writeTextElement("PreviousFrameQueueSize", QString::number(previous_queue_size)); stream.writeTextElement("PreviousFrameQueueType", QString::number(previous_queue_type)); stream.writeTextElement("UpcomingFrameQueueSize", QString::number(upcoming_queue_size)); diff --git a/global/config.h b/global/config.h index 3b7bbf2f6..0291a5e3a 100644 --- a/global/config.h +++ b/global/config.h @@ -24,6 +24,7 @@ #include #include "ui/styling.h" +#include "timeline/timelinetools.h" namespace olive { /** @@ -159,13 +160,6 @@ struct Config { */ Config(); - /** - * @brief Show track lines - * - * **TRUE** if the Timeline should show lines between tracks. - */ - bool show_track_lines; - /** * @brief The scroll wheel zooms rather than scrolls * @@ -674,8 +668,8 @@ struct RuntimeConfig { }; namespace olive { -extern Config CurrentConfig; -extern RuntimeConfig CurrentRuntimeConfig; +extern Config config; +extern RuntimeConfig runtime_config; } #endif // CONFIG_H diff --git a/global/global.cpp b/global/global.cpp index 097549e31..4c0854142 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -30,7 +30,8 @@ #include "panels/panels.h" #include "global/path.h" #include "global/config.h" -#include "project/clipboard.h" +#include "global/timing.h" +#include "global/clipboard.h" #include "rendering/audio.h" #include "dialogs/demonotice.h" #include "dialogs/preferencesdialog.h" @@ -147,12 +148,12 @@ QString OliveGlobal::get_recent_project_list_file() { } void OliveGlobal::load_translation_from_config() { - QString language_file = olive::CurrentRuntimeConfig.external_translation_file.isEmpty() ? - olive::CurrentConfig.language_file : - olive::CurrentRuntimeConfig.external_translation_file; + QString language_file = olive::runtime_config.external_translation_file.isEmpty() ? + olive::config.language_file : + olive::runtime_config.external_translation_file; // clear runtime language file so if the user sets a different language, we won't load it next time - olive::CurrentRuntimeConfig.external_translation_file.clear(); + olive::runtime_config.external_translation_file.clear(); // remove current translation if there is one QApplication::removeTranslator(translator.get()); @@ -195,7 +196,7 @@ void OliveGlobal::add_recent_project(const QString &url) } if (!found) { recent_projects.prepend(url); - if (recent_projects.size() > olive::CurrentConfig.maximum_recent_projects) { + if (recent_projects.size() > olive::config.maximum_recent_projects) { recent_projects.removeLast(); } } @@ -229,6 +230,11 @@ const QString &OliveGlobal::recent_project(int index) return recent_projects.at(index); } +const QString &OliveGlobal::get_autorecovery_filename() +{ + return autorecovery_filename; +} + void OliveGlobal::LoadProject(const QString &fn, bool autorecovery) { // QSortFilterProxyModels are not thread-safe, and as we'll be loading in another thread, leaving it connected @@ -265,7 +271,7 @@ void OliveGlobal::ClearProject() panel_effect_controls->Clear(true); // clear existing project - set_sequence(nullptr); + Timeline::CloseAll(); panel_footage_viewer->set_media(nullptr); // delete sequences first because it's important to close all the clips before deleting the media @@ -278,7 +284,7 @@ void OliveGlobal::ClearProject() olive::project_model.clear(); // clear undo stack - olive::UndoStack.clear(); + olive::undo_stack.clear(); // empty current project filename update_project_filename(""); @@ -308,36 +314,42 @@ void OliveGlobal::save_recent_projects() } } -void OliveGlobal::PasteInternal(Sequence *s) +void OliveGlobal::PasteInternal(Sequence *s, bool insert) { + if (s == nullptr) { + return; + } + if (!olive::clipboard.IsEmpty()) { if (olive::clipboard.type() == Clipboard::CLIPBOARD_TYPE_CLIP) { ComboAction* ca = new ComboAction(); // create copies and delete areas that we'll be pasting to QVector delete_areas; + QVector original_clips; QVector pasted_clips; long paste_start = LONG_MAX; long paste_end = LONG_MIN; - for (int i=0;i(olive::clipboard.at(i)); + for (int i=0;i(olive::clipboard.Get(i)); // create copy of clip and offset by playhead - ClipPtr cc = c->copy(olive::ActiveSequence.get()); + ClipPtr cc = c->copy(s->GetTrackList(c->track()->type())->TrackAt(c->track()->Index())); // convert frame rates - cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); - cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), olive::ActiveSequence->frame_rate)); + cc->set_timeline_in(rescale_frame_number(cc->timeline_in(), c->cached_frame_rate(), s->frame_rate)); + cc->set_timeline_out(rescale_frame_number(cc->timeline_out(), c->cached_frame_rate(), s->frame_rate)); + cc->set_clip_in(rescale_frame_number(cc->clip_in(), c->cached_frame_rate(), s->frame_rate)); - cc->set_timeline_in(cc->timeline_in() + olive::ActiveSequence->playhead); - cc->set_timeline_out(cc->timeline_out() + olive::ActiveSequence->playhead); + cc->set_timeline_in(cc->timeline_in() + s->playhead); + cc->set_timeline_out(cc->timeline_out() + s->playhead); cc->set_track(c->track()); paste_start = qMin(paste_start, cc->timeline_in()); paste_end = qMax(paste_end, cc->timeline_out()); + original_clips.append(c.get()); pasted_clips.append(cc); if (!insert) { @@ -345,52 +357,40 @@ void OliveGlobal::PasteInternal(Sequence *s) } } if (insert) { - split_all_clips_at_point(ca, olive::ActiveSequence->playhead); - ripple_clips(ca, olive::ActiveSequence.get(), paste_start, paste_end - paste_start); + s->SplitAllClipsAtPoint(ca, s->playhead); + s->Ripple(ca, paste_start, paste_end - paste_start); } else { - delete_areas_and_relink(ca, delete_areas, false); + s->DeleteAreas(ca, delete_areas, false); } // correct linked clips - for (int i=0;i(clipboard.at(i)); + olive::timeline::RelinkClips(original_clips, pasted_clips); - for (int j=0;jlinked.size();j++) { - for (int k=0;k(clipboard.at(k)); - if (comp->load_id == oc->linked.at(j)) { - pasted_clips.at(i)->linked.append(k); - } - } - } - } + ca->append(new AddClipCommand(pasted_clips)); - ca->append(new AddClipCommand(olive::ActiveSequence.get(), pasted_clips)); - - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(true); - if (olive::CurrentConfig.paste_seeks) { + if (olive::config.paste_seeks) { panel_sequence_viewer->seek(paste_end); } - } else if (clipboard_type == CLIPBOARD_TYPE_EFFECT) { + } else if (olive::clipboard.type() == Clipboard::CLIPBOARD_TYPE_EFFECT) { ComboAction* ca = new ComboAction(); bool replace = false; bool skip = false; bool ask_conflict = true; - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = s->SelectedClips(); for (int i=0;i(clipboard.at(j)); - if ((c->track() < 0) == (e->meta->subtype == EFFECT_TYPE_VIDEO)) { + for (int j=0;j(olive::clipboard.Get(j)); + if (c->type() == e->meta->subtype) { int found = -1; if (ask_conflict) { replace = false; @@ -403,7 +403,7 @@ void OliveGlobal::PasteInternal(Sequence *s) } } if (found >= 0 && ask_conflict) { - QMessageBox box(this); + QMessageBox box(olive::MainWindow); box.setWindowTitle(tr("Effect already exists")); box.setText(tr("Clip '%1' already contains a '%2' effect. " "Would you like to replace it with the pasted one or add it as a separate effect?") @@ -441,7 +441,7 @@ void OliveGlobal::PasteInternal(Sequence *s) } if (ca->hasActions()) { ca->appendPost(new ReloadEffectsCommand()); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } @@ -528,7 +528,7 @@ bool OliveGlobal::can_close_project() { return true; } -void OliveGlobal::new_sequence() +void OliveGlobal::open_new_sequence_dialog() { NewSequenceDialog nsd(olive::MainWindow); nsd.set_sequence_name(olive::project_model.GetNextSequenceName()); @@ -557,7 +557,7 @@ void OliveGlobal::open_import_dialog() void OliveGlobal::open_export_dialog() { if (CheckForActiveSequence()) { - ExportDialog e(olive::MainWindow); + ExportDialog e(olive::MainWindow, Timeline::GetTopSequence().get()); e.exec(); } } @@ -609,15 +609,10 @@ void OliveGlobal::open_preferences() { pd.exec(); } -void OliveGlobal::set_sequence(SequencePtr s) +void OliveGlobal::PrimarySequenceChanged() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - - olive::ActiveSequence = s; - panel_sequence_viewer->set_main_sequence(); - panel_timeline->update_sequence(); - panel_timeline->setFocus(); } void OliveGlobal::clear_recent_projects() @@ -630,12 +625,12 @@ void OliveGlobal::OpenProjectWorker(QString fn, bool autorecovery) { ClearProject(); update_project_filename(fn); LoadProject(fn, autorecovery); - olive::UndoStack.clear(); + olive::undo_stack.clear(); } bool OliveGlobal::CheckForActiveSequence(bool show_msg) { - if (olive::ActiveSequence == nullptr) { + if (Timeline::GetTopSequence() == nullptr) { if (show_msg) { QMessageBox::information(olive::MainWindow, @@ -651,30 +646,26 @@ bool OliveGlobal::CheckForActiveSequence(bool show_msg) void OliveGlobal::undo() { // workaround to prevent crash (and also users should never need to do this) - if (!panel_timeline->importing) { - olive::UndoStack.undo(); + if (!Timeline::IsImporting()) { + olive::undo_stack.undo(); update_ui(true); } } void OliveGlobal::redo() { // workaround to prevent crash (and also users should never need to do this) - if (!panel_timeline->importing) { - olive::UndoStack.redo(); + if (!Timeline::IsImporting()) { + olive::undo_stack.redo(); update_ui(true); } } void OliveGlobal::paste() { - if (olive::ActiveSequence != nullptr) { - panel_timeline->paste(false); - } + PasteInternal(Timeline::GetTopSequence().get(), false); } void OliveGlobal::paste_insert() { - if (olive::ActiveSequence != nullptr) { - panel_timeline->paste(true); - } + PasteInternal(Timeline::GetTopSequence().get(), true); } void OliveGlobal::open_about_dialog() { @@ -687,9 +678,9 @@ void OliveGlobal::open_debug_log() { } void OliveGlobal::open_speed_dialog() { - if (olive::ActiveSequence != nullptr) { + if (Timeline::GetTopSequence() != nullptr) { - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = Timeline::GetTopSequence()->SelectedClips(); if (!selected_clips.isEmpty()) { SpeedDialog s(olive::MainWindow, selected_clips); @@ -701,7 +692,7 @@ void OliveGlobal::open_speed_dialog() { void OliveGlobal::open_autocut_silence_dialog() { if (CheckForActiveSequence()) { - QVector selected_clips = olive::ActiveSequence->SelectedClipIndexes(); + QVector selected_clips = Timeline::GetTopSequence()->SelectedClips(); if (selected_clips.isEmpty()) { QMessageBox::critical(olive::MainWindow, @@ -717,7 +708,7 @@ void OliveGlobal::open_autocut_silence_dialog() { } void OliveGlobal::clear_undo_stack() { - olive::UndoStack.clear(); + olive::undo_stack.clear(); } void OliveGlobal::open_action_search() { diff --git a/global/global.h b/global/global.h index 5543fa0ef..a8a0ee47f 100644 --- a/global/global.h +++ b/global/global.h @@ -204,6 +204,18 @@ public: */ const QString& get_autorecovery_filename(); + /** + * @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not + * + * Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in + * order to perform said action. + * + * @return + * + * TRUE if there is an active Sequence, FALSE if not. + */ + bool CheckForActiveSequence(bool show_msg = true); + public slots: /** * @brief Undo user's last action @@ -313,7 +325,7 @@ public slots: /** * @brief Opens the NewSequenceDialog to create a new Sequence */ - void new_sequence(); + void open_new_sequence_dialog(); /** * @brief Open a file dialog for importing files into the project @@ -378,19 +390,6 @@ public slots: */ void open_preferences(); - /** - * @brief Set the current active Sequence - * - * Call this to change the active Sequence (e.g. when the user double clicks a Sequence in the Project panel). - * This will affect panel_timeline, panel_sequence_viewer, and panel_effect_controls and can then be retrieved - * using olive::ActiveSequence. - * - * @param s - * - * The Sequence to set as the active Sequence. - */ - void set_sequence(SequencePtr s); - /** * @brief Clear the recent projects list * @@ -398,6 +397,14 @@ public slots: */ void clear_recent_projects(); + /** + * @brief Slot for when the primary sequence has changed. + * + * Usually by opening a sequence or bringing a corresponding + * Timeline widget on top. + */ + void PrimarySequenceChanged(); + private: /** * @brief Internal function to handle loading a project from file @@ -417,18 +424,6 @@ private: */ void OpenProjectWorker(QString fn, bool autorecovery); - /** - * @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not - * - * Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in - * order to perform said action. - * - * @return - * - * TRUE if there is an active Sequence, FALSE if not. - */ - bool CheckForActiveSequence(bool show_msg = true); - /** * @brief Create a LoadDialog and start a LoadThread to load data from a project * @@ -475,7 +470,7 @@ private: /** * @brief Internal pasting function */ - void PasteInternal(Sequence* s); + void PasteInternal(Sequence* s, bool insert); /** * @brief File filter used for any file dialogs relating to Olive project files. diff --git a/global/math.h b/global/math.h index 9d0c9a9f5..721e4453e 100644 --- a/global/math.h +++ b/global/math.h @@ -41,4 +41,8 @@ QRect fit_size_into_rect(const QRect& r, int width, int height); double amplitude_to_db(double amplitude); double db_to_amplitude(double db); +// frame <-> pixel conversion functions +int getScreenPointFromFrame(double zoom, long frame); +long getFrameFromScreenPoint(double zoom, int x); + #endif // MATH_H diff --git a/global/timing.cpp b/global/timing.cpp index b14315734..f87cacb70 100644 --- a/global/timing.cpp +++ b/global/timing.cpp @@ -5,7 +5,7 @@ #include "global/config.h" double get_timecode(Clip* c, long playhead) { - return double(playhead_to_clip_frame(c, playhead))/c->sequence->frame_rate; + return double(playhead_to_clip_frame(c, playhead))/c->track()->sequence()->frame_rate; } long rescale_frame_number(long framenumber, double source_frame_rate, double target_frame_rate) { @@ -24,7 +24,7 @@ double playhead_to_clip_seconds(Clip* c, long playhead) { clip_frame = c->media_length() - clip_frame - 1; } - double secs = (double(clip_frame)/c->sequence->frame_rate)*c->speed().value; + double secs = (double(clip_frame)/c->track()->sequence()->frame_rate)*c->speed().value; if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { secs *= c->media()->to_footage()->speed; } diff --git a/main.cpp b/main.cpp index 395ac1813..e387d92e0 100644 --- a/main.cpp +++ b/main.cpp @@ -70,13 +70,13 @@ int main(int argc, char *argv[]) { } else if (!strcmp(argv[i], "--fullscreen") || !strcmp(argv[i], "-f")) { launch_fullscreen = true; } else if (!strcmp(argv[i], "--disable-shaders")) { - olive::CurrentRuntimeConfig.shaders_are_enabled = false; + olive::runtime_config.shaders_are_enabled = false; } else if (!strcmp(argv[i], "--no-debug")) { use_internal_logger = false; } else if (!strcmp(argv[i], "--translation")) { if (i + 1 < argc && argv[i + 1][0] != '-') { // load translation file - olive::CurrentRuntimeConfig.external_translation_file = argv[i + 1]; + olive::runtime_config.external_translation_file = argv[i + 1]; i++; } else { diff --git a/olive.pro b/olive.pro index 40be13dd2..a58ad5db1 100644 --- a/olive.pro +++ b/olive.pro @@ -75,7 +75,6 @@ SOURCES += \ dialogs/preferencesdialog.cpp \ ui/audiomonitor.cpp \ undo/undo.cpp \ - ui/scrollarea.cpp \ ui/comboboxex.cpp \ ui/colorbutton.cpp \ dialogs/replaceclipmediadialog.cpp \ @@ -108,7 +107,6 @@ SOURCES += \ effects/effect.cpp \ effects/effectrow.cpp \ effects/effectgizmo.cpp \ - project/clipboard.cpp \ ui/resizablescrollbar.cpp \ ui/sourceiconview.cpp \ project/sourcescommon.cpp \ @@ -182,7 +180,10 @@ SOURCES += \ timeline/timelineshared.cpp \ ui/timelineview.cpp \ ui/timelinelabel.cpp \ - timeline/selection.cpp + timeline/selection.cpp \ + global/clipboard.cpp \ + timeline/timelinetools.cpp \ + timeline/ghost.cpp HEADERS += \ ui/mainwindow.h \ @@ -204,14 +205,12 @@ HEADERS += \ ui/collapsiblewidget.h \ panels/panels.h \ rendering/exportthread.h \ - ui/timelinetools.h \ ui/timelineheader.h \ project/previewgenerator.h \ ui/labelslider.h \ dialogs/preferencesdialog.h \ ui/audiomonitor.h \ undo/undo.h \ - ui/scrollarea.h \ ui/comboboxex.h \ ui/colorbutton.h \ dialogs/replaceclipmediadialog.h \ @@ -246,7 +245,6 @@ HEADERS += \ effects/effectrow.h \ effects/internal/cubetransition.h \ effects/effectgizmo.h \ - project/clipboard.h \ ui/resizablescrollbar.h \ ui/sourceiconview.h \ project/sourcescommon.h \ @@ -323,7 +321,9 @@ HEADERS += \ ui/timelinearea.h \ timeline/timelineshared.h \ ui/timelineview.h \ - ui/timelinelabel.h + ui/timelinelabel.h \ + global/clipboard.h \ + timeline/timelinetools.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 279ddc3c9..2321572a2 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -47,7 +47,7 @@ #include "ui/viewerwidget.h" #include "ui/menuhelper.h" #include "ui/icons.h" -#include "project/clipboard.h" +#include "global/clipboard.h" #include "global/config.h" #include "ui/timelineheader.h" #include "ui/keyframeview.h" @@ -89,7 +89,10 @@ EffectControls::~EffectControls() void EffectControls::set_zoom(bool in) { zoom *= (in) ? 2 : 0.5; update_keyframes(); - scroll_to_frame(olive::ActiveSequence->playhead); + + if (!selected_clips_.isEmpty()) { + scroll_to_frame(selected_clips_.first()->track()->sequence()->playhead); + } } void EffectControls::menu_select(QAction* q) { @@ -104,21 +107,21 @@ void EffectControls::menu_select(QAction* q) { nullptr, nullptr, meta, - olive::CurrentConfig.default_transition_length)); + olive::config.default_transition_length)); } if (c->closing_transition == nullptr) { ca->append(new AddTransitionCommand(nullptr, c, nullptr, meta, - olive::CurrentConfig.default_transition_length)); + olive::config.default_transition_length)); } } else { ca->append(new AddEffectCommand(c, nullptr, meta)); } } } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); if (effect_menu_type == EFFECT_TYPE_TRANSITION) { update_ui(true); } else { @@ -175,7 +178,7 @@ void EffectControls::copy(bool del) { if (del) { if (ca->hasActions()) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } @@ -587,7 +590,7 @@ void EffectControls::DeleteSelectedEffects() { } if (ca->hasActions()) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(true); } else { delete ca; @@ -620,11 +623,13 @@ void EffectControls::SetClips() { Clear(true); - if (olive::ActiveSequence == nullptr) { + Sequence* top_sequence = Timeline::GetTopSequence().get(); + + if (top_sequence == nullptr) { selected_clips_.clear(); } else { // replace clip vector - selected_clips_ = olive::ActiveSequence->SelectedClips(false); + selected_clips_ = top_sequence->SelectedClips(false); Load(); } diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index dd27a705f..e664b8aba 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -27,7 +27,7 @@ #include "ui/keyframenavigator.h" #include "ui/timelineheader.h" -#include "ui/timelinetools.h" +#include "timeline/timelinetools.h" #include "ui/labelslider.h" #include "ui/graphview.h" #include "effects/effect.h" diff --git a/panels/panels.cpp b/panels/panels.cpp index f8ec065cd..6fb3455f8 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -25,6 +25,7 @@ #include "effects/transition.h" #include "global/config.h" #include "global/debug.h" +#include "global/math.h" #include #include @@ -33,7 +34,7 @@ QVector panel_project; EffectControls* panel_effect_controls = nullptr; Viewer* panel_sequence_viewer = nullptr; Viewer* panel_footage_viewer = nullptr; -Timeline* panel_timeline = nullptr; +QVector panel_timeline; GraphEditor* panel_graph_editor = nullptr; void update_ui(bool modified) { @@ -41,14 +42,16 @@ void update_ui(bool modified) { panel_effect_controls->SetClips(); } panel_effect_controls->update_keyframes(); - panel_timeline->repaint_timeline(); + for (int i=0;irepaint_timeline(); + } panel_sequence_viewer->update_viewer(); panel_graph_editor->update_panel(); } QDockWidget *get_focused_panel(bool force_hover) { QDockWidget* w = nullptr; - if (olive::CurrentConfig.hover_focus || force_hover) { + if (olive::config.hover_focus || force_hover) { for (int i=0;iunderMouse()) { w = olive::panels.at(i); @@ -78,8 +81,9 @@ void alloc_panels(QWidget* parent) { panel_project.append(first_project_panel); panel_effect_controls = new EffectControls(parent); panel_effect_controls->setObjectName("fx_controls"); - panel_timeline = new Timeline(parent); - panel_timeline->setObjectName("timeline"); + Timeline* first_timeline_panel = new Timeline(parent); + first_timeline_panel->setObjectName("timeline"); + panel_timeline.append(first_timeline_panel); panel_graph_editor = new GraphEditor(parent); panel_graph_editor->setObjectName("graph_editor"); } @@ -89,12 +93,19 @@ void free_panels() { panel_sequence_viewer = nullptr; delete panel_footage_viewer; panel_footage_viewer = nullptr; - delete panel_project; - panel_project = nullptr; + + for (int i=0;i panel_project; extern EffectControls* panel_effect_controls; extern Viewer* panel_sequence_viewer; extern Viewer* panel_footage_viewer; -extern Timeline* panel_timeline; +extern QVector panel_timeline; extern GraphEditor* panel_graph_editor; void update_ui(bool modified); diff --git a/panels/project.cpp b/panels/project.cpp index e58f6ad74..289a1e7be 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -54,7 +54,7 @@ extern "C" { #include "dialogs/mediapropertiesdialog.h" #include "dialogs/newsequencedialog.h" #include "dialogs/loaddialog.h" -#include "project/clipboard.h" +#include "global/clipboard.h" #include "ui/sourcetable.h" #include "ui/sourceiconview.h" #include "ui/icons.h" @@ -83,7 +83,7 @@ Project::Project(QWidget *parent) : // optional toolbar toolbar_widget = new QWidget(); - toolbar_widget->setVisible(olive::CurrentConfig.show_project_toolbar); + toolbar_widget->setVisible(olive::config.show_project_toolbar); toolbar_widget->setObjectName("project_toolbar"); QHBoxLayout* toolbar = new QHBoxLayout(toolbar_widget); @@ -233,7 +233,7 @@ void Project::duplicate_selected() { } } if (duped) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } @@ -250,7 +250,9 @@ void Project::replace_selected_file() { } void Project::replace_clip_media() { - if (olive::ActiveSequence == nullptr) { + Sequence* top_sequence = Timeline::GetTopSequence().get(); + + if (top_sequence == nullptr) { QMessageBox::critical(this, tr("No active sequence"), tr("No sequence is active, please open the sequence you want to replace clips from."), @@ -259,7 +261,7 @@ void Project::replace_clip_media() { QModelIndexList selected_items = get_current_selected(); if (selected_items.size() == 1) { Media* item = item_to_media(selected_items.at(0)); - if (item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == item->to_sequence()) { + if (item->get_type() == MEDIA_TYPE_SEQUENCE && top_sequence == item->to_sequence().get()) { QMessageBox::critical(this, tr("Active sequence selected"), tr("You cannot insert a sequence into itself, so no clips of this media would be in this sequence."), @@ -299,7 +301,7 @@ void Project::open_properties() { item->get_name()); if (!new_name.isEmpty()) { MediaRename* mr = new MediaRename(item, new_name); - olive::UndoStack.push(mr); + olive::undo_stack.push(mr); } } } @@ -308,10 +310,10 @@ void Project::open_properties() { void Project::new_folder() { MediaPtr m = olive::project::CreateFolder(nullptr); - olive::UndoStack.push(new AddMediaCommand(m, get_selected_folder())); + olive::undo_stack.push(new AddMediaCommand(m, get_selected_folder())); QModelIndex index = olive::project_model.create_index(m->row(), 0, m.get()); - switch (olive::CurrentConfig.project_view_type) { + switch (olive::config.project_view_type) { case olive::PROJECT_VIEW_TREE: tree_view->edit(sorter.mapFromSource(index)); break; @@ -451,8 +453,9 @@ void Project::delete_selected_media() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->ClearSelections(); + Sequence* top_sequence = Timeline::GetTopSequence().get(); + if (top_sequence != nullptr) { + top_sequence->ClearSelections(); } // remove media and parents @@ -474,9 +477,7 @@ void Project::delete_selected_media() { Sequence* s = items.at(i)->to_sequence().get(); - if (s == olive::ActiveSequence.get()) { - ca->append(new ChangeSequenceAction(nullptr)); - } + Timeline::CloseSequence(s); if (s == panel_footage_viewer->seq.get()) { panel_footage_viewer->set_media(nullptr); @@ -487,7 +488,7 @@ void Project::delete_selected_media() { } } } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); // redraw clips if (redraw) { @@ -527,7 +528,7 @@ bool Project::reveal_media(Media *media, QModelIndex parent) { // retrieve its parent item QModelIndex hierarchy = sorted_index.parent(); - if (olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE) { + if (olive::config.project_view_type == olive::PROJECT_VIEW_TREE) { // if we're in tree view, expand every folder in the hierarchy containing the media while (hierarchy.isValid()) { @@ -542,7 +543,7 @@ bool Project::reveal_media(Media *media, QModelIndex parent) { ); tree_view->selectionModel()->select(row_select, QItemSelectionModel::Select); - } else if (olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON) { + } else if (olive::config.project_view_type == olive::PROJECT_VIEW_ICON) { // if we're in icon view, we just "browse" to the parent folder icon_view->setRootIndex(hierarchy); @@ -563,55 +564,42 @@ bool Project::reveal_media(Media *media, QModelIndex parent) { } void Project::delete_clips_using_selected_media() { - if (olive::ActiveSequence == nullptr) { + Sequence* top_sequence = Timeline::GetTopSequence().get(); + + if (top_sequence == nullptr) { QMessageBox::critical(this, tr("No active sequence"), tr("No sequence is active, please open the sequence you want to delete clips from."), QMessageBox::Ok); } else { - ComboAction* ca = new ComboAction(); - bool deleted = false; - QModelIndexList items = get_current_selected(); - QVector sequence_clips = olive::ActiveSequence->GetAllClips(); - for (int i=0;imedia() == m) { - ca->append(new DeleteClipAction(c)); - deleted = true; - } - } - } - for (int j=0;j media; + + media.resize(items.size()); + + for (int i=0;iDeleteClipsUsingMedia(media); + } } void Project::update_view_type() { - tree_view->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE); - icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON - || olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_LIST); + tree_view->setVisible(olive::config.project_view_type == olive::PROJECT_VIEW_TREE); + icon_view_container->setVisible(olive::config.project_view_type == olive::PROJECT_VIEW_ICON + || olive::config.project_view_type == olive::PROJECT_VIEW_LIST); - switch (olive::CurrentConfig.project_view_type) { + switch (olive::config.project_view_type) { case olive::PROJECT_VIEW_TREE: sources_common.view = tree_view; break; case olive::PROJECT_VIEW_ICON: case olive::PROJECT_VIEW_LIST: - icon_view->setViewMode(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON ? + icon_view->setViewMode(olive::config.project_view_type == olive::PROJECT_VIEW_ICON ? QListView::IconMode : QListView::ListMode); // update list/grid size since they use this value slightly differently @@ -623,18 +611,18 @@ void Project::update_view_type() { } void Project::set_icon_view() { - olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_ICON; + olive::config.project_view_type = olive::PROJECT_VIEW_ICON; update_view_type(); } void Project::set_list_view() { - olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_LIST; + olive::config.project_view_type = olive::PROJECT_VIEW_LIST; update_view_type(); } void Project::set_tree_view() { - olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_TREE; + olive::config.project_view_type = olive::PROJECT_VIEW_TREE; update_view_type(); } @@ -663,7 +651,7 @@ void Project::make_new_menu() { } QModelIndexList Project::get_current_selected() { - if (olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE) { + if (olive::config.project_view_type == olive::PROJECT_VIEW_TREE) { return tree_view->selectionModel()->selectedRows(); } return icon_view->selectionModel()->selectedIndexes(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 966408348..dbfbf1dc6 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -45,7 +45,8 @@ #include "rendering/cacher.h" #include "rendering/renderfunctions.h" #include "global/config.h" -#include "project/clipboard.h" +#include "global/clipboard.h" +#include "global/math.h" #include "ui/timelineheader.h" #include "ui/resizablescrollbar.h" #include "ui/audiomonitor.h" @@ -60,20 +61,17 @@ Timeline::Timeline(QWidget *parent) : Panel(parent), cursor_frame(0), - cursor_track(0), + cursor_track(nullptr), zoom(1.0), zoom_just_changed(false), showing_all(false), - snapping(true), - snapped(false), - snap_point(0), selecting(false), rect_select_init(false), rect_select_proc(false), moving_init(false), moving_proc(false), move_insert(false), - trim_target(-1), + trim_target(nullptr), trim_type(olive::timeline::TRIM_NONE), splitting(false), importing(false), @@ -81,11 +79,12 @@ Timeline::Timeline(QWidget *parent) : creating(false), transition_tool_init(false), transition_tool_proc(false), - transition_tool_open_clip(-1), - transition_tool_close_clip(-1), + transition_tool_open_clip(nullptr), + transition_tool_close_clip(nullptr), hand_moving(false), block_repaints(false), - scroll(0) + scroll(0), + sequence_(nullptr) { setup_ui(); @@ -124,6 +123,113 @@ Timeline::Timeline(QWidget *parent) : Retranslate(); } +Timeline *Timeline::GetTopTimeline() +{ + for (int i=0;iisVisible()) { + return panel_timeline.at(i); + } + } + + return nullptr; +} + +SequencePtr Timeline::GetTopSequence() +{ + Timeline* top_timeline = GetTopTimeline(); + + if (top_timeline != nullptr) { + return top_timeline->sequence_; + } + + return nullptr; +} + +void Timeline::OpenSequence(SequencePtr s) +{ + Q_ASSERT(s != nullptr); + + for (int i=0;isequence_ == s) { + t->raise(); + return; + } else if (t->sequence_ == nullptr) { + t->SetSequence(s); + t->raise(); + return; + } + } + + Timeline* t = new Timeline(olive::MainWindow); + panel_timeline.append(t); + olive::MainWindow->addDockWidget(Qt::BottomDockWidgetArea, t); + olive::MainWindow->tabifyDockWidget(panel_timeline.last(), t); + t->SetSequence(s); + t->raise(); +} + +void Timeline::CloseSequence(Sequence *s) +{ + Q_ASSERT(s != nullptr); + + // Don't respond to a null sequence + if (s == nullptr) { + return; + } + + // If there's only one Timeline object left, just set it to nullptr without destroying it + if (panel_timeline.size() == 1) { + panel_timeline.first()->SetSequence(nullptr); + return; + } + + // If there are multiple, kill the Timeline object that has the specified sequence + for (int i=0;isequence_.get() == s) { + delete t; + panel_timeline.removeAt(i); + i--; + } + } +} + +void Timeline::CloseAll() +{ + while (panel_timeline.size() > 1) { + delete panel_timeline.last(); + panel_timeline.removeLast(); + } + panel_timeline.first()->SetSequence(nullptr); +} + +bool Timeline::IsImporting() +{ + for (int i=0;iimporting) { + return true; + } + } + return false; +} + +void Timeline::SetSequence(SequencePtr sequence) +{ + if (sequence_ == sequence) { + return; + } + + sequence_ = sequence; + update_sequence(); + video_area->SetTrackList(sequence_.get(), Track::kTypeVideo); + audio_area->SetTrackList(sequence_.get(), Track::kTypeAudio); + repaint_timeline(); + + emit SequenceChanged(sequence_); +} + void Timeline::Retranslate() { toolArrowButton->setToolTip(tr("Pointer Tool") + " (V)"); toolEditButton->setToolTip(tr("Edit Tool") + " (X)"); @@ -143,212 +249,22 @@ void Timeline::Retranslate() { } void Timeline::toggle_show_all() { - if (olive::ActiveSequence != nullptr) { + if (sequence_ != nullptr) { showing_all = !showing_all; if (showing_all) { old_zoom = zoom; - set_zoom_value(double(timeline_area->width() - 200) / double(olive::ActiveSequence->GetEndFrame())); + set_zoom_value(double(timeline_area->width() - 200) / double(sequence_->GetEndFrame())); } else { set_zoom_value(old_zoom); } } } -void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector& media_list) { - video_ghosts = false; - audio_ghosts = false; - - for (int i=0;iget_type()) { - case MEDIA_TYPE_FOOTAGE: - m = medium->to_footage(); - can_import = m->ready; - if (m->using_inout) { - double source_fr = 30; - if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) { - source_fr = m->video_tracks.at(0).video_frame_rate * m->speed; - } - default_clip_in = rescale_frame_number(m->in, source_fr, seq->frame_rate); - default_clip_out = rescale_frame_number(m->out, source_fr, seq->frame_rate); - } - break; - case MEDIA_TYPE_SEQUENCE: - s = medium->to_sequence().get(); - sequence_length = s->GetEndFrame(); - if (seq != nullptr) sequence_length = rescale_frame_number(sequence_length, s->frame_rate, seq->frame_rate); - can_import = (s != seq && sequence_length != 0); - if (s->using_workarea) { - default_clip_in = rescale_frame_number(s->workarea_in, s->frame_rate, seq->frame_rate); - default_clip_out = rescale_frame_number(s->workarea_out, s->frame_rate, seq->frame_rate); - } - break; - default: - can_import = false; - } - - if (can_import) { - Ghost g; - g.clip = -1; - g.trim_type = olive::timeline::TRIM_NONE; - g.old_clip_in = g.clip_in = default_clip_in; - g.media = medium; - g.in = entry_point; - g.transition = nullptr; - - switch (medium->get_type()) { - case MEDIA_TYPE_FOOTAGE: - // is video source a still image? - if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { - g.out = g.in + 100; - } else { - long length = m->get_length_in_frames(seq->frame_rate); - g.out = entry_point + length - default_clip_in; - if (m->using_inout) { - g.out -= (length - default_clip_out); - } - } - - if (import_data.type() == olive::timeline::kImportAudioOnly - || import_data.type() == olive::timeline::kImportBoth) { - for (int j=0;jaudio_tracks.size();j++) { - if (m->audio_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeAudio)->First() + j; - g.media_stream = m->audio_tracks.at(j).file_index; - ghosts.append(g); - audio_ghosts = true; - } - } - } - - if (import_data.type() == olive::timeline::kImportVideoOnly - || import_data.type() == olive::timeline::kImportBoth) { - for (int j=0;jvideo_tracks.size();j++) { - if (m->video_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeVideo)->First() + j; - g.media_stream = m->video_tracks.at(j).file_index; - ghosts.append(g); - video_ghosts = true; - } - } - } - break; - case MEDIA_TYPE_SEQUENCE: - g.out = entry_point + sequence_length - default_clip_in; - - if (s->using_workarea) { - g.out -= (sequence_length - default_clip_out); - } - - if (import_data.type() == olive::timeline::kImportVideoOnly - || import_data.type() == olive::timeline::kImportBoth) { - g.track = seq->GetTrackList(Track::kTypeVideo)->First(); - ghosts.append(g); - } - - if (import_data.type() == olive::timeline::kImportAudioOnly - || import_data.type() == olive::timeline::kImportBoth) { - g.track = seq->GetTrackList(Track::kTypeAudio)->First(); - ghosts.append(g); - } - - video_ghosts = true; - audio_ghosts = true; - break; - } - entry_point = g.out; - } - } - for (int i=0;i added_clips; - for (int i=0;i(s); - c->set_media(g.media, g.media_stream); - c->set_timeline_in(g.in); - c->set_timeline_out(g.out); - c->set_clip_in(g.clip_in); - c->set_track(g.track); - if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - Footage* m = c->media()->to_footage(); - if (m->video_tracks.size() == 0) { - // audio only (greenish) - c->set_color(128, 192, 128); - } else if (m->audio_tracks.size() == 0) { - // video only (orangeish) - c->set_color(192, 160, 128); - } else { - // video and audio (blueish) - c->set_color(128, 128, 192); - } - c->set_name(m->name); - } else if (c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { - // sequence (red?ish?) - c->set_color(192, 128, 128); - - c->set_name(c->media()->to_sequence()->name); - } - c->refresh(); - added_clips.append(c); - } - ca->append(new AddClipCommand(s, added_clips)); - - // link clips from the same media - for (int i=0;imedia() == cc->media()) { - c->linked.append(cc.get()); - } - } - - if (olive::CurrentConfig.add_default_effects_to_clips) { - if (c->type() == Track::kTypeVideo) { - // add default video effects - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); - } else if (c->type() == Track::kTypeAudio) { - // add default audio effects - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); - } - } - } - if (olive::CurrentConfig.enable_seek_to_import) { - panel_sequence_viewer->seek(earliest_point); - } - ghosts.clear(); - importing = false; - snapped = false; -} - void Timeline::add_transition() { ComboAction* ca = new ComboAction(); bool adding = false; - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence_->SelectedClips(); for (int i=0;i selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence_->SelectedClips(); // nest them if (!selected_clips.isEmpty()) { @@ -404,11 +320,11 @@ void Timeline::nest() { SequencePtr s = std::make_shared(); s->name = olive::project_model.GetNextSequenceName(tr("Nested Sequence")); - s->width = olive::ActiveSequence->width; - s->height = olive::ActiveSequence->height; - s->frame_rate = olive::ActiveSequence->frame_rate; - s->audio_frequency = olive::ActiveSequence->audio_frequency; - s->audio_layout = olive::ActiveSequence->audio_layout; + s->width = sequence_->width; + s->height = sequence_->height; + s->frame_rate = sequence_->frame_rate; + s->audio_frequency = sequence_->audio_frequency; + s->audio_layout = sequence_->audio_layout; QVector new_clips; @@ -438,10 +354,10 @@ void Timeline::nest() { // add nested sequence to active sequence QVector media_list; media_list.append(m.get()); - create_ghosts_from_media(olive::ActiveSequence.get(), earliest_point, media_list); + olive::timeline::CreateGhostsFromMedia(sequence_.get(), earliest_point, media_list); // ensure ghosts won't overlap anything - QVector all_sequence_clips = olive::ActiveSequence->GetAllClips(); + QVector all_sequence_clips = sequence_->GetAllClips(); for (int j=0;jAddClipsFromGhosts(ca, ghosts); panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - olive::ActiveSequence->ClearSelections(); + sequence_->ClearSelections(); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(true); } @@ -480,7 +396,7 @@ void Timeline::nest() { } void Timeline::update_sequence() { - bool null_sequence = (olive::ActiveSequence == nullptr); + bool null_sequence = (sequence_ == nullptr); for (int i=0;isetEnabled(!null_sequence); @@ -495,32 +411,28 @@ void Timeline::update_sequence() { UpdateTitle(); } -long Timeline::get_snap_range() { - return getFrameFromScreenPoint(zoom, 10); -} - bool Timeline::focused() { - return (olive::ActiveSequence != nullptr && (headers->hasFocus() || video_area->hasFocus() || audio_area->hasFocus())); + return (sequence_ != nullptr && (headers->hasFocus() || video_area->hasFocus() || audio_area->hasFocus())); } void Timeline::repaint_timeline() { if (!block_repaints) { bool draw = true; - if (olive::ActiveSequence != nullptr + if (sequence_ != nullptr && !horizontalScrollBar->isSliderDown() && !horizontalScrollBar->is_resizing() && panel_sequence_viewer->playing && !zoom_just_changed) { // auto scroll - if (olive::CurrentConfig.autoscroll == olive::AUTOSCROLL_PAGE_SCROLL) { - int playhead_x = getTimelineScreenPointFromFrame(olive::ActiveSequence->playhead); + if (olive::config.autoscroll == olive::AUTOSCROLL_PAGE_SCROLL) { + int playhead_x = getTimelineScreenPointFromFrame(sequence_->playhead); if (playhead_x < 0 || playhead_x > (editAreas->width())) { - horizontalScrollBar->setValue(getScreenPointFromFrame(zoom, olive::ActiveSequence->playhead)); + horizontalScrollBar->setValue(getScreenPointFromFrame(zoom, sequence_->playhead)); draw = false; } - } else if (olive::CurrentConfig.autoscroll == olive::AUTOSCROLL_SMOOTH_SCROLL) { - if (center_scroll_to_playhead(horizontalScrollBar, zoom, olive::ActiveSequence->playhead)) { + } else if (olive::config.autoscroll == olive::AUTOSCROLL_SMOOTH_SCROLL) { + if (center_scroll_to_playhead(horizontalScrollBar, zoom, sequence_->playhead)) { draw = false; } } @@ -531,7 +443,7 @@ void Timeline::repaint_timeline() { video_area->update(); audio_area->update(); - if (olive::ActiveSequence != nullptr + if (sequence_ != nullptr && !zoom_just_changed) { set_sb_max(); } @@ -542,8 +454,8 @@ void Timeline::repaint_timeline() { } void Timeline::select_all() { - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->SelectAll(); + if (sequence_ != nullptr) { + sequence_->SelectAll(); repaint_timeline(); } } @@ -552,15 +464,9 @@ void Timeline::scroll_to_frame(long frame) { scroll_to_frame_internal(horizontalScrollBar, frame, zoom, timeline_area->width()); } -void Timeline::select_from_playhead() { - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->SelectAtPlayhead(); - } -} - void Timeline::resizeEvent(QResizeEvent *) { // adjust maximum scrollbar - if (olive::ActiveSequence != nullptr) set_sb_max(); + if (sequence_ != nullptr) set_sb_max(); // resize tool button widget to its contents @@ -590,10 +496,10 @@ void Timeline::resizeEvent(QResizeEvent *) { } void Timeline::toggle_enable_on_selected_clips() { - if (olive::ActiveSequence != nullptr) { + if (sequence_ != nullptr) { // get currently selected clips - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence_->SelectedClips(); if (!selected_clips.isEmpty()) { // if clips are selected, create an undoable action @@ -606,7 +512,7 @@ void Timeline::toggle_enable_on_selected_clips() { } // push the action - olive::UndoStack.push(set_action); + olive::undo_stack.push(set_action); update_ui(false); } } @@ -623,12 +529,12 @@ void Timeline::set_zoom_value(double v) { zoom_just_changed = true; // set scrollbar to center the playhead - if (olive::ActiveSequence != nullptr) { + if (sequence_ != nullptr) { // update scrollbar maximum value for new zoom set_sb_max(); if (!horizontalScrollBar->is_resizing()) { - center_scroll_to_playhead(horizontalScrollBar, zoom, olive::ActiveSequence->playhead); + center_scroll_to_playhead(horizontalScrollBar, zoom, sequence_->playhead); } } @@ -650,8 +556,8 @@ void Timeline::zoom_out() { } void Timeline::ChangeTrackHeightUniformly(int diff) { - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->ChangeTrackHeightsRelatively(diff); + if (sequence_ != nullptr) { + sequence_->ChangeTrackHeightsRelatively(diff); } // update the timeline @@ -667,12 +573,12 @@ void Timeline::DecreaseTrackHeight() { } void Timeline::snapping_clicked(bool checked) { - snapping = checked; + olive::timeline::snapping = checked; } /* bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool relink) { - Clip* c = olive::ActiveSequence->clips.at(clip).get(); + Clip* c = sequence_->clips.at(clip).get(); if (c != nullptr) { QVector pre_clips; QVector post_clips; @@ -693,7 +599,7 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool // find linked clips of old clip for (int i=0;ilinked.size();i++) { int l = c->linked.at(i); - Clip* link = olive::ActiveSequence->clips.at(l).get(); + Clip* link = sequence_->clips.at(l).get(); if ((original_clip_is_selected && link->IsSelected()) || !original_clip_is_selected) { ClipPtr s = split_clip(ca, true, l, frame); if (s != nullptr) { @@ -705,7 +611,7 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool relink_clips_using_ids(pre_clips, post_clips); } - ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); + ca->append(new AddClipCommand(sequence_.get(), post_clips)); return true; } } @@ -716,403 +622,119 @@ bool Timeline::split_clip_and_relink(ComboAction *ca, int clip, long frame, bool void Timeline::copy(bool del) { - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->AddSelectionsToClipboard(del); - } -} - -void Timeline::paste(bool insert) { - -} - -void Timeline::edit_to_point_internal(bool in, bool ripple) { - if (olive::ActiveSequence != nullptr) { - if (olive::ActiveSequence->clips.size() > 0) { - // get track count - int track_min = INT_MAX; - int track_max = INT_MIN; - long sequence_end = 0; - - bool playhead_falls_on_in = false; - bool playhead_falls_on_out = false; - long next_cut = LONG_MAX; - long prev_cut = 0; - - // find closest in point to playhead - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - track_min = qMin(track_min, c->track()); - track_max = qMax(track_max, c->track()); - - sequence_end = qMax(c->timeline_out(), sequence_end); - - if (c->timeline_in() == olive::ActiveSequence->playhead) - playhead_falls_on_in = true; - if (c->timeline_out() == olive::ActiveSequence->playhead) - playhead_falls_on_out = true; - if (c->timeline_in() > olive::ActiveSequence->playhead) - next_cut = qMin(c->timeline_in(), next_cut); - if (c->timeline_out() > olive::ActiveSequence->playhead) - next_cut = qMin(c->timeline_out(), next_cut); - if (c->timeline_in() < olive::ActiveSequence->playhead) - prev_cut = qMax(c->timeline_in(), prev_cut); - if (c->timeline_out() < olive::ActiveSequence->playhead) - prev_cut = qMax(c->timeline_out(), prev_cut); - } - } - - next_cut = qMin(sequence_end, next_cut); - - QVector areas; - ComboAction* ca = new ComboAction(); - bool push_undo = true; - long seek = olive::ActiveSequence->playhead; - - if ((in && (playhead_falls_on_out || (playhead_falls_on_in && olive::ActiveSequence->playhead == 0))) - || (!in && (playhead_falls_on_in || (playhead_falls_on_out && olive::ActiveSequence->playhead == sequence_end)))) { // one frame mode - if (ripple) { - // set up deletion areas based on track count - long in_point = olive::ActiveSequence->playhead; - if (!in) { - in_point--; - seek--; - } - - if (in_point >= 0) { - Selection s; - s.in = in_point; - s.out = in_point + 1; - for (int i=track_min;i<=track_max;i++) { - s.track = i; - areas.append(s); - } - - // trim and move clips around the in point - delete_areas_and_relink(ca, areas, true); - - if (ripple) ripple_clips(ca, olive::ActiveSequence.get(), in_point, -1); - } else { - push_undo = false; - } - } else { - push_undo = false; - } - } else { - // set up deletion areas based on track count - Selection s; - if (in) seek = prev_cut; - s.in = in ? prev_cut : olive::ActiveSequence->playhead; - s.out = in ? olive::ActiveSequence->playhead : next_cut; - - if (s.in == s.out) { - push_undo = false; - } else { - for (int i=track_min;i<=track_max;i++) { - s.track = i; - areas.append(s); - } - - // trim and move clips around the in point - delete_areas_and_relink(ca, areas, true); - if (ripple) ripple_clips(ca, olive::ActiveSequence.get(), s.in, s.in - s.out); - } - } - - if (push_undo) { - olive::UndoStack.push(ca); - - update_ui(true); - - if (seek != olive::ActiveSequence->playhead && ripple) { - panel_sequence_viewer->seek(seek); - } - } else { - delete ca; - } - } else { - panel_sequence_viewer->seek(0); - } - } -} - -bool Timeline::split_selection(ComboAction* ca) { - bool split = false; - - // temporary relinking vectors - QVector pre_splits; - QVector post_splits; - QVector secondary_post_splits; - - // find clips within selection and split - for (int j=0;jclips.size();j++) { - ClipPtr clip = olive::ActiveSequence->clips.at(j); - if (clip != nullptr) { - for (int i=0;iselections.size();i++) { - const Selection& s = olive::ActiveSequence->selections.at(i); - if (s.track == clip->track()) { - ClipPtr post_b = split_clip(ca, true, j, s.out); - ClipPtr post_a = split_clip(ca, true, j, s.in); - - pre_splits.append(j); - post_splits.append(post_a); - secondary_post_splits.append(post_b); - - if (post_a != nullptr) { - post_a->set_timeline_out(qMin(post_a->timeline_out(), s.out)); - } - - split = true; - } - } - } - } - - if (split) { - // relink after splitting - relink_clips_using_ids(pre_splits, post_splits); - relink_clips_using_ids(pre_splits, secondary_post_splits); - - ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_splits)); - ca->append(new AddClipCommand(olive::ActiveSequence.get(), secondary_post_splits)); - - return true; - } - return false; -} - -void Timeline::split_at_playhead() { - ComboAction* ca = new ComboAction(); - bool split_selected = false; - - if (olive::ActiveSequence->selections.size() > 0) { - // see if whole clips are selected - QVector pre_clips; - QVector post_clips; - for (int j=0;jclips.size();j++) { - Clip* clip = olive::ActiveSequence->clips.at(j).get(); - if (clip != nullptr && clip->IsSelected()) { - ClipPtr s = split_clip(ca, true, j, olive::ActiveSequence->playhead); - if (s != nullptr) { - pre_clips.append(j); - post_clips.append(s); - split_selected = true; - } - } - } - - if (split_selected) { - // relink clips if we split - relink_clips_using_ids(pre_clips, post_clips); - ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); - } else { - // split a selection if not - split_selected = split_selection(ca); - } - } - - // if nothing was selected or no selections fell within playhead, simply split at playhead - if (!split_selected) { - split_selected = split_all_clips_at_point(ca, olive::ActiveSequence->playhead); - } - - if (split_selected) { - olive::UndoStack.push(ca); - update_ui(true); - } else { - delete ca; + if (sequence_ != nullptr) { + sequence_->AddSelectionsToClipboard(del); } } void Timeline::ripple_delete() { - if (olive::ActiveSequence != nullptr) { - if (olive::ActiveSequence->selections.size() > 0) { - panel_timeline->delete_selection(olive::ActiveSequence->selections, true); - } else if (olive::CurrentConfig.hover_focus && get_focused_panel() == panel_timeline) { - if (panel_timeline->can_ripple_empty_space(panel_timeline->cursor_frame, panel_timeline->cursor_track)) { - panel_timeline->ripple_delete_empty_space(); - } + if (sequence_ != nullptr) { + + QVector selections = sequence_->Selections(); + + if (selections.isEmpty()) { + + sequence_->RippleDeleteEmptySpace(cursor_track, cursor_frame); + + } else if (olive::config.hover_focus && get_focused_panel() == this) { + + ComboAction* ca = new ComboAction(); + sequence_->DeleteAreas(ca, selections, true, true); + olive::undo_stack.push(ca); + } } } -void Timeline::deselect_area(long in, long out, int track) { - int len = olive::ActiveSequence->selections.size(); - for (int i=0;iselections[i]; - if (s.track == track) { - if (s.in >= in && s.out <= out) { - // whole selection is in deselect area - olive::ActiveSequence->selections.removeAt(i); - i--; - len--; - } else if (s.in < in && s.out > out) { - // middle of selection is in deselect area - Selection new_sel; - new_sel.in = out; - new_sel.out = s.out; - new_sel.track = s.track; - olive::ActiveSequence->selections.append(new_sel); +void Timeline::ripple_delete_empty_space() +{ + if (sequence_ != nullptr) { - s.out = in; - } else if (s.in < in && s.out > in) { - // only out point is in deselect area - s.out = in; - } else if (s.in < out && s.out > out) { - // only in point is in deselect area - s.in = out; - } - } } } -bool Timeline::snap_to_point(long point, long* l) { - int limit = get_snap_range(); - if (*l > point-limit-1 && *l < point+limit+1) { - snap_point = point; - *l = point; - snapped = true; - return true; - } - return false; -} - -bool Timeline::snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea) { - snapped = false; - if (snapping) { - if (use_playhead && !panel_sequence_viewer->playing) { - // snap to playhead - if (snap_to_point(olive::ActiveSequence->playhead, l)) return true; - } - - // snap to marker - if (use_markers) { - for (int i=0;imarkers.size();i++) { - if (snap_to_point(olive::ActiveSequence->markers.at(i).frame, l)) return true; - } - } - - // snap to in/out - if (use_workarea && olive::ActiveSequence->using_workarea) { - if (snap_to_point(olive::ActiveSequence->workarea_in, l)) return true; - if (snap_to_point(olive::ActiveSequence->workarea_out, l)) return true; - } - - // snap to clip/transition - QVector all_clips = olive::ActiveSequence->GetAllClips(); - for (int i=0;itimeline_in(), l)) { - return true; - } else if (snap_to_point(c->timeline_out(), l)) { - return true; - } else if (c->opening_transition != nullptr - && snap_to_point(c->timeline_in() + c->opening_transition->get_true_length(), l)) { - return true; - } else if (c->closing_transition != nullptr - && snap_to_point(c->timeline_out() - c->closing_transition->get_true_length(), l)) { - return true; - } else { - // try to snap to clip markers - for (int j=0;jget_markers().size();j++) { - if (snap_to_point(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l)) { - return true; - } - } - } - - } - } - - return false; -} - void Timeline::set_marker() { // determine if any clips are selected, and if so add markers to clips rather than the sequence - QVector clips_selected; - bool clip_mode = false; - for (int i=0;iclips.size();i++) { - Clip* c = olive::ActiveSequence->clips.at(i).get(); - if (c != nullptr - && c->IsSelected()) { + QVector selected_clips = sequence_->SelectedClips(); - // only add markers if the playhead is inside the clip - if (olive::ActiveSequence->playhead >= c->timeline_in() - && olive::ActiveSequence->playhead <= c->timeline_out()) { - clips_selected.append(i); + if (selected_clips.isEmpty()) { + + Marker::SetOnSequence(sequence_.get()); + + } else { + + // Remove any clips that don't contain the playhead + for (int i=0;itimeline_out() < sequence_->playhead + || c->timeline_in() > sequence_->playhead) { + selected_clips.removeAt(i); + i--; } - - // we are definitely adding markers to clips though - clip_mode = true; - } + + // Check if we removed them all + if (selected_clips.isEmpty()) { + return; + } + + // If not, let's create markers on them + Marker::SetOnClips(selected_clips); + } - - // if we've selected clips but none of the clips are within the playhead, - // nothing to do here - if (clip_mode && clips_selected.size() == 0) { - return; - } - - // pass off to internal set marker function - set_marker_internal(olive::ActiveSequence.get(), clips_selected); - } void Timeline::delete_inout() { - panel_timeline->delete_in_out_internal(false); + if (sequence_ != nullptr) { + sequence_->DeleteInToOut(false); + } } void Timeline::ripple_delete_inout() { - panel_timeline->delete_in_out_internal(true); + if (sequence_ != nullptr) { + sequence_->DeleteInToOut(true); + } } void Timeline::ripple_to_in_point() { - panel_timeline->edit_to_point_internal(true, true); + if (sequence_ != nullptr) { + sequence_->EditToPoint(true, true); + } } void Timeline::ripple_to_out_point() { - panel_timeline->edit_to_point_internal(false, true); + if (sequence_ != nullptr) { + sequence_->EditToPoint(false, true); + } } void Timeline::edit_to_in_point() { - panel_timeline->edit_to_point_internal(true, false); + if (sequence_ != nullptr) { + sequence_->EditToPoint(true, false); + } } void Timeline::edit_to_out_point() { - panel_timeline->edit_to_point_internal(false, false); -} - -void Timeline::toggle_links() { - LinkCommand* command = new LinkCommand(); - command->s = olive::ActiveSequence.get(); - for (int i=0;iclips.size();i++) { - Clip* c = olive::ActiveSequence->clips.at(i).get(); - if (c != nullptr && c->IsSelected()) { - if (!command->clips.contains(i)) command->clips.append(i); - - if (c->linked.size() > 0) { - command->link = false; // prioritize unlinking - - for (int j=0;jlinked.size();j++) { // add links to the command - if (!command->clips.contains(c->linked.at(j))) command->clips.append(c->linked.at(j)); - } - } - } - } - if (command->clips.size() > 0) { - olive::UndoStack.push(command); - repaint_timeline(); - } else { - delete command; + if (sequence_ != nullptr) { + sequence_->EditToPoint(false, false); } } void Timeline::deselect() { - olive::ActiveSequence->selections.clear(); - repaint_timeline(); + if (sequence_ != nullptr) { + sequence_->ClearSelections(); + repaint_timeline(); + } +} + +void Timeline::split_at_playhead() +{ + if (sequence_ != nullptr) { + sequence_->Split(); + repaint_timeline(); + } } long getFrameFromScreenPoint(double zoom, int x) { @@ -1172,7 +794,7 @@ void Timeline::add_btn_click() { void Timeline::add_menu_item(QAction* action) { creating = true; - creating_object = action->data().toInt(); + creating_object = static_cast(action->data().toInt()); } void Timeline::setScroll(int s) { @@ -1232,13 +854,12 @@ void Timeline::transition_menu_select(QAction* a) { transition_tool_meta = reinterpret_cast(a->data().value()); if (a->objectName() == "v") { - transition_tool_side = -1; + transition_tool_side = Track::kTypeVideo; } else { - transition_tool_side = 1; + transition_tool_side = Track::kTypeAudio; } timeline_area->setCursor(Qt::CrossCursor); - tool = TIMELINE_TOOL_TRANSITION; toolTransitionButton->setChecked(true); } @@ -1247,15 +868,15 @@ void Timeline::resize_move(double z) { } void Timeline::set_sb_max() { - headers->set_scrollbar_max(horizontalScrollBar, olive::ActiveSequence->getEndFrame(), editAreas->width() - getScreenPointFromFrame(zoom, 200)); + headers->set_scrollbar_max(horizontalScrollBar, sequence_->GetEndFrame(), editAreas->width() - getScreenPointFromFrame(zoom, 200)); } void Timeline::UpdateTitle() { QString title = tr("Timeline: "); - if (olive::ActiveSequence == nullptr) { + if (sequence_ == nullptr) { setWindowTitle(title + tr("(none)")); } else { - setWindowTitle(title + olive::ActiveSequence->name); + setWindowTitle(title + sequence_->name); update_ui(false); } } @@ -1280,42 +901,42 @@ void Timeline::setup_ui() { toolArrowButton = new QPushButton(); toolArrowButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/arrow.svg"))); toolArrowButton->setCheckable(true); - toolArrowButton->setProperty("tool", TIMELINE_TOOL_POINTER); + toolArrowButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_POINTER); connect(toolArrowButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolArrowButton); toolEditButton = new QPushButton(); toolEditButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/beam.svg"))); toolEditButton->setCheckable(true); - toolEditButton->setProperty("tool", TIMELINE_TOOL_EDIT); + toolEditButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_EDIT); connect(toolEditButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolEditButton); toolRippleButton = new QPushButton(); toolRippleButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/ripple.svg"))); toolRippleButton->setCheckable(true); - toolRippleButton->setProperty("tool", TIMELINE_TOOL_RIPPLE); + toolRippleButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_RIPPLE); connect(toolRippleButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolRippleButton); toolRazorButton = new QPushButton(); toolRazorButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/razor.svg"))); toolRazorButton->setCheckable(true); - toolRazorButton->setProperty("tool", TIMELINE_TOOL_RAZOR); + toolRazorButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_RAZOR); connect(toolRazorButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolRazorButton); toolSlipButton = new QPushButton(); toolSlipButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/slip.svg"))); toolSlipButton->setCheckable(true); - toolSlipButton->setProperty("tool", TIMELINE_TOOL_SLIP); + toolSlipButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_SLIP); connect(toolSlipButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolSlipButton); toolSlideButton = new QPushButton(); toolSlideButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/slide.svg"))); toolSlideButton->setCheckable(true); - toolSlideButton->setProperty("tool", TIMELINE_TOOL_SLIDE); + toolSlideButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_SLIDE); connect(toolSlideButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolSlideButton); @@ -1323,7 +944,7 @@ void Timeline::setup_ui() { toolHandButton->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/hand.svg"))); toolHandButton->setCheckable(true); - toolHandButton->setProperty("tool", TIMELINE_TOOL_HAND); + toolHandButton->setProperty("tool", olive::timeline::TIMELINE_TOOL_HAND); connect(toolHandButton, SIGNAL(clicked(bool)), this, SLOT(set_tool())); tool_buttons_layout->addWidget(toolHandButton); toolTransitionButton = new QPushButton(); @@ -1384,10 +1005,10 @@ void Timeline::setup_ui() { splitter->setChildrenCollapsible(false); splitter->setOrientation(Qt::Vertical); - video_area = new TimelineArea(); + video_area = new TimelineArea(this); splitter->addWidget(video_area); - audio_area = new TimelineArea(); + audio_area = new TimelineArea(this); splitter->addWidget(audio_area); editAreaLayout->addWidget(splitter); @@ -1413,16 +1034,16 @@ void Timeline::setup_ui() { void Timeline::set_tool() { QPushButton* button = static_cast(sender()); - tool = button->property("tool").toInt(); + olive::timeline::current_tool = static_cast(button->property("tool").toInt()); creating = false; - switch (tool) { - case TIMELINE_TOOL_EDIT: + switch (olive::timeline::current_tool) { + case olive::timeline::TIMELINE_TOOL_EDIT: timeline_area->setCursor(Qt::IBeamCursor); break; - case TIMELINE_TOOL_RAZOR: + case olive::timeline::TIMELINE_TOOL_RAZOR: timeline_area->setCursor(olive::cursor::Razor); break; - case TIMELINE_TOOL_HAND: + case olive::timeline::TIMELINE_TOOL_HAND: timeline_area->setCursor(Qt::OpenHandCursor); break; default: diff --git a/panels/timeline.h b/panels/timeline.h index 82b31f153..72f68feb7 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -26,7 +26,8 @@ #include #include "ui/timelinearea.h" -#include "ui/timelinetools.h" +#include "timeline/timelinetools.h" +#include "timeline/timelinefunctions.h" #include "timeline/selection.h" #include "timeline/clip.h" #include "timeline/mediaimportdata.h" @@ -37,59 +38,44 @@ #include "ui/audiomonitor.h" #include "ui/panel.h" - -int getScreenPointFromFrame(double zoom, long frame); -long getFrameFromScreenPoint(double zoom, int x); -bool selection_contains_transition(const Selection& s, Clip *c, int type); - - class Timeline : public Panel { Q_OBJECT public: explicit Timeline(QWidget *parent = nullptr); + static Timeline* GetTopTimeline(); + static SequencePtr GetTopSequence(); + static void OpenSequence(SequencePtr s); + static void CloseSequence(Sequence* s); + static void CloseAll(); + static bool IsImporting(); + + void SetSequence(SequencePtr sequence); + virtual bool focused() override; void multiply_zoom(double m); void copy(bool del); - void clean_up_selections(QVector& areas); - void deselect_area(long in, long out, int track); - void delete_areas_and_relink(ComboAction *ca, QVector& areas, bool deselect_areas); void update_sequence(); - void edit_to_point_internal(bool in, bool ripple); - void delete_in_out_internal(bool ripple); - - void create_ghosts_from_media(Sequence *seq, long entry_point, QVector &media_list); - void add_clips_from_ghosts(ComboAction *ca, Sequence *s); - int getTimelineScreenPointFromFrame(long frame); long getTimelineFrameFromScreenPoint(int x); int getDisplayScreenPointFromFrame(long frame); long getDisplayFrameFromScreenPoint(int x); - long get_snap_range(); - bool snap_to_point(long point, long* l); - bool snap_to_timeline(long* l, bool use_playhead, bool use_markers, bool use_workarea); void set_marker(); // shared information - int tool; long cursor_frame; - int cursor_track; + Track* cursor_track; double zoom; bool zoom_just_changed; long drag_frame_start; - int drag_track_start; + Track* drag_track_start; void update_effect_controls(); bool showing_all; double old_zoom; - // snapping - bool snapping; - bool snapped; - long snap_point; - // selecting functions bool selecting; int selection_offset; @@ -102,18 +88,16 @@ public: bool moving_init; bool moving_proc; QVector ghosts; - bool video_ghosts; - bool audio_ghosts; bool move_insert; // trimming - int trim_target; + Clip* trim_target; olive::timeline::TrimType trim_type; int transition_select; // splitting bool splitting; - QVector split_tracks; + QVector split_tracks; // importing bool importing; @@ -121,15 +105,15 @@ public: // creating variables bool creating; - int creating_object; + olive::timeline::CreateObjects creating_object; // transition variables bool transition_tool_init; bool transition_tool_proc; - int transition_tool_open_clip; - int transition_tool_close_clip; + Clip* transition_tool_open_clip; + Clip* transition_tool_close_clip; const EffectMeta* transition_tool_meta; - int transition_tool_side; + Track::Type transition_tool_side; // hand tool variables bool hand_moving; @@ -155,7 +139,6 @@ public: QPushButton* snappingButton; void scroll_to_frame(long frame); - void select_from_playhead(); bool can_ripple_empty_space(long frame, int track); @@ -163,11 +146,9 @@ public: protected: virtual void resizeEvent(QResizeEvent *event) override; public slots: - void paste(bool insert = false); void repaint_timeline(); void toggle_show_all(); void deselect(); - void toggle_links(); void split_at_playhead(); void ripple_delete(); void ripple_delete_empty_space(); @@ -184,9 +165,6 @@ public slots: void IncreaseTrackHeight(); void DecreaseTrackHeight(); - void previous_cut(); - void next_cut(); - void add_transition(); void nest(); @@ -194,6 +172,9 @@ public slots: void zoom_in(); void zoom_out(); +signals: + void SequenceChanged(SequencePtr s); + private slots: void snapping_clicked(bool checked); void add_btn_click(); @@ -206,6 +187,8 @@ private slots: void set_tool(); private: + SequencePtr sequence_; + void ChangeTrackHeightUniformly(int diff); void set_zoom_value(double v); void set_tool(int tool); diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 04e9187e8..79ec033bd 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -70,7 +70,8 @@ Viewer::Viewer(QWidget *parent) : created_sequence(false), minimum_zoom(1.0), cue_recording_internal(false), - playback_speed(0) + playback_speed(0), + mode_(kTimelineMode) { setup_ui(); @@ -101,6 +102,16 @@ Viewer::Viewer(QWidget *parent) : update_end_timecode(); } +void Viewer::SetMode(Viewer::Mode mode) +{ + mode_ = mode; +} + +Viewer::Mode Viewer::mode() +{ + return mode_; +} + void Viewer::Retranslate() { /// Viewer panels are retranslated through the MainWindow to differentiate Media and Sequence Viewers // update_window_title(); @@ -116,14 +127,6 @@ bool Viewer::focused() { || go_to_end_frame->hasFocus(); } -bool Viewer::is_main_sequence() { - return main_sequence; -} - -void Viewer::set_main_sequence() { - set_sequence(true, olive::ActiveSequence); -} - void Viewer::reset_all_audio() { // reset all clip audio if (seq != nullptr) { @@ -150,20 +153,24 @@ void Viewer::reset_all_audio() { void Viewer::seek(long p) { pause(); - if (main_sequence) { + + if (mode_ == kTimelineMode) { seq->playhead = p; } else { seq->playhead = qMin(seq->GetEndFrame(), qMax(0L, p)); } + bool update_fx = false; - if (main_sequence) { - panel_timeline->scroll_to_frame(p); + + if (mode_ == kTimelineMode) { + panel_timeline.first()->scroll_to_frame(p); panel_effect_controls->scroll_to_frame(p); - if (olive::CurrentConfig.seek_also_selects) { - panel_timeline->select_from_playhead(); + if (olive::config.seek_also_selects) { + seq->SelectAtPlayhead(); update_fx = true; } } + reset_all_audio(); audio_scrub = true; last_playhead = seq->playhead; @@ -274,11 +281,11 @@ void Viewer::play(bool in_to_out) { uncue_recording(); } - bool seek_to_in = (seq->using_workarea && (olive::CurrentConfig.loop || playing_in_to_out)); + bool seek_to_in = (seq->using_workarea && (olive::config.loop || playing_in_to_out)); if (!is_recording_cued() && playback_speed >= 0 && (playing_in_to_out - || (olive::CurrentConfig.auto_seek_to_beginning && seq->playhead >= sequence_end_frame) + || (olive::config.auto_seek_to_beginning && seq->playhead >= sequence_end_frame) || (seek_to_in && seq->playhead >= seq->workarea_out))) { seek(seek_to_in ? seq->workarea_in : 0); } @@ -359,7 +366,7 @@ void Viewer::pause() { QVector add_clips; add_clips.append(c); - olive::UndoStack.push(new AddClipCommand(seq.get(), add_clips)); // add clip + olive::undo_stack.push(new AddClipCommand(add_clips)); // add clip } @@ -374,7 +381,7 @@ void Viewer::update_playhead_timecode(long p) { } void Viewer::update_end_timecode() { - end_timecode->setText((seq == nullptr) ? frame_to_timecode(0, olive::CurrentConfig.timecode_view, 30) : frame_to_timecode(seq->GetEndFrame(), olive::CurrentConfig.timecode_view, seq->frame_rate)); + end_timecode->setText((seq == nullptr) ? frame_to_timecode(0, olive::config.timecode_view, 30) : frame_to_timecode(seq->GetEndFrame(), olive::config.timecode_view, seq->frame_rate)); } void Viewer::update_header_zoom() { @@ -392,11 +399,10 @@ void Viewer::update_header_zoom() { } void Viewer::update_parents(bool reload_fx) { - if (main_sequence) { + if (mode_ == kTimelineMode) { update_ui(reload_fx); } else { update_viewer(); - panel_timeline->repaint_timeline(); } } @@ -410,7 +416,7 @@ ViewerWidget *Viewer::viewer_widget() } void Viewer::set_marker() { - set_marker_internal(seq.get()); + Marker::SetOnSequence(seq.get()); } void Viewer::resizeEvent(QResizeEvent *e) { @@ -435,7 +441,7 @@ void Viewer::prev_cut() if (seq != nullptr && seq->playhead > 0) { - QVector sequence_clips = olive::ActiveSequence->GetAllClips(); + QVector sequence_clips = seq->GetAllClips(); long p_cut = 0; for (int i=0;iusing_workarea) { - olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, 0, seq->workarea_out)); + olive::undo_stack.push(new SetTimelineInOutCommand(seq.get(), true, 0, seq->workarea_out)); update_parents(); } } @@ -497,7 +503,7 @@ void Viewer::clear_in() { void Viewer::clear_out() { if (seq != nullptr && seq->using_workarea) { - olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, seq->workarea_in, seq->GetEndFrame())); + olive::undo_stack.push(new SetTimelineInOutCommand(seq.get(), true, seq->workarea_in, seq->GetEndFrame())); update_parents(); } } @@ -505,7 +511,7 @@ void Viewer::clear_out() { void Viewer::clear_inout_point() { if (seq != nullptr && seq->using_workarea) { - olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), false, 0, 0)); + olive::undo_stack.push(new SetTimelineInOutCommand(seq.get(), false, 0, 0)); update_parents(); } } @@ -575,13 +581,13 @@ void Viewer::set_playback_speed(int s) { } long Viewer::get_seq_in() { - return ((olive::CurrentConfig.loop || playing_in_to_out) && seq->using_workarea) + return ((olive::config.loop || playing_in_to_out) && seq->using_workarea) ? seq->workarea_in : 0; } long Viewer::get_seq_out() { - return ((olive::CurrentConfig.loop || playing_in_to_out) && seq->using_workarea && previous_playhead < seq->workarea_out) + return ((olive::config.loop || playing_in_to_out) && seq->using_workarea && previous_playhead < seq->workarea_out) ? seq->workarea_out : seq->GetEndFrame(); } @@ -713,7 +719,6 @@ void Viewer::setup_ui() { } void Viewer::set_media(Media* m) { - main_sequence = false; media = m; SequencePtr new_sequence = nullptr; @@ -737,7 +742,7 @@ void Viewer::set_media(Media* m) { new_sequence->workarea_out = footage->out; } - new_sequence->frame_rate = olive::CurrentConfig.default_sequence_framerate; + new_sequence->frame_rate = olive::config.default_sequence_framerate; if (footage->video_tracks.size() > 0) { const FootageStream& video_stream = footage->video_tracks.at(0); @@ -761,20 +766,19 @@ void Viewer::set_media(Media* m) { c->refresh(); track->AddClip(c); } else { - new_sequence->width = olive::CurrentConfig.default_sequence_width; - new_sequence->height = olive::CurrentConfig.default_sequence_height; + new_sequence->width = olive::config.default_sequence_width; + new_sequence->height = olive::config.default_sequence_height; } if (footage->audio_tracks.size() > 0) { const FootageStream& audio_stream = footage->audio_tracks.at(0); new_sequence->audio_frequency = audio_stream.audio_frequency; - ClipPtr c = std::make_shared(new_sequence.get()); + Track* track = new_sequence->GetTrackList(Track::kTypeAudio)->First(); + ClipPtr c = std::make_shared(track); c->set_media(media, audio_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate)); - Track* track = new_sequence->GetTrackList(Track::kTypeAudio)->First(); - c->set_track(track); c->set_clip_in(0); c->refresh(); track->AddClip(c); @@ -786,7 +790,7 @@ void Viewer::set_media(Media* m) { viewer_widget_->frame_update(); } } else { - new_sequence->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; + new_sequence->audio_frequency = olive::config.default_sequence_audio_frequency; } new_sequence->audio_layout = AV_CH_LAYOUT_STEREO; @@ -798,7 +802,7 @@ void Viewer::set_media(Media* m) { } } - set_sequence(false, new_sequence); + set_sequence(new_sequence); } void Viewer::update_playhead() { @@ -810,11 +814,11 @@ void Viewer::timer_update() { seq->playhead = qMax(0, qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed))); - if (olive::CurrentConfig.seek_also_selects) { - panel_timeline->select_from_playhead(); + if (olive::config.seek_also_selects) { + seq->SelectAtPlayhead(); } - update_parents(olive::CurrentConfig.seek_also_selects); + update_parents(olive::config.seek_also_selects); if (playing) { if (playback_speed < 0 && seq->playhead == 0) { @@ -825,11 +829,11 @@ void Viewer::timer_update() { } } else if (playback_speed > 0) { long end_frame = seq->GetEndFrame(); - if ((olive::CurrentConfig.auto_seek_to_beginning || previous_playhead < end_frame) && seq->playhead >= end_frame) { + if ((olive::config.auto_seek_to_beginning || previous_playhead < end_frame) && seq->playhead >= end_frame) { pause(); } if (seq->using_workarea && seq->playhead >= seq->workarea_out) { - if (olive::CurrentConfig.loop) { + if (olive::config.loop) { // loop play(); } else if (playing_in_to_out) { @@ -882,7 +886,7 @@ void Viewer::clean_created_seq() { } } -void Viewer::set_sequence(bool main, SequencePtr s) { +void Viewer::set_sequence(SequencePtr s) { pause(); reset_all_audio(); @@ -896,11 +900,7 @@ void Viewer::set_sequence(bool main, SequencePtr s) { clean_created_seq(); - main_sequence = main; - - - - seq = (main) ? olive::ActiveSequence : s; + seq = s; bool null_sequence = (seq == nullptr); diff --git a/panels/viewer.h b/panels/viewer.h index e5659ea4c..80485d26b 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -44,9 +44,16 @@ class Viewer : public Panel public: explicit Viewer(QWidget *parent = nullptr); + enum Mode { + kFootageMode, + kTimelineMode + }; + + void SetMode(Mode mode); + Mode mode(); + virtual bool focused() override; bool is_main_sequence(); - void set_main_sequence(); void set_media(Media *m); void compose(); void set_playpause_icon(bool play); @@ -131,8 +138,7 @@ private slots: private: void update_window_title(); void clean_created_seq(); - void set_sequence(bool main, SequencePtr s); - bool main_sequence; + void set_sequence(SequencePtr s); bool created_sequence; long cached_end_frame; QString panel_name; @@ -169,6 +175,8 @@ private: long previous_playhead; int playback_speed; + + Mode mode_; }; #endif // VIEWER_H diff --git a/project/footage.cpp b/project/footage.cpp index 9e4dc17f2..e1fd0fc5a 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace OCIO = OCIO_NAMESPACE::v1; @@ -114,7 +115,7 @@ QString Footage::Colorspace() return guess_colorspace; } - return olive::CurrentConfig.ocio_default_input_colorspace; + return olive::config.ocio_default_input_colorspace; } void Footage::SetColorspace(const QString &cs) @@ -154,3 +155,25 @@ FootageStream* Footage::get_stream_from_file_index(bool video, int index) { } return nullptr; } + +QString Footage::get_interlacing_name(int interlacing) { + switch (interlacing) { + case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)"); + case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Top Field First"); + case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Bottom Field First"); + default: return QCoreApplication::translate("InterlacingName", "Invalid"); + } +} + +QString Footage::get_channel_layout_name(int channels, uint64_t layout) { + switch (channels) { + case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid"); + case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono"); + case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo"); + default: { + char buf[50]; + av_get_channel_layout_string(buf, sizeof(buf), channels, layout); + return QString(buf); + } + } +} diff --git a/project/loadthread.cpp b/project/loadthread.cpp index 20ddc66d6..9f3d135ea 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -27,6 +27,7 @@ #include "global/config.h" #include "rendering/renderfunctions.h" #include "project/previewgenerator.h" +#include "project/projectfunctions.h" #include "effects/internal/voideffect.h" #include "global/debug.h" #include "effects/effectloaders.h" @@ -82,8 +83,10 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { // Find the clip with the ID referenced in the transition int clip_id = attr.value().toInt(); - for (int i=0;isequence->clips.size();i++) { - Clip* test_clip = c->sequence->clips.at(i).get(); + + QVector sequence_clips = c->track()->sequence()->GetAllClips(); + for (int i=0;iload_id == clip_id) { sharing_clip = test_clip; break; @@ -262,7 +265,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { switch (type) { case MEDIA_TYPE_FOLDER: { - MediaPtr folder = panel_project->create_folder_internal(nullptr); + MediaPtr folder = olive::project::CreateFolder(nullptr); folder->temp_id2 = 0; for (int j=0;j(s.get()); + Track* t; + ClipPtr c = std::make_shared(t); + //ClipPtr c = std::make_shared(s.get()); QColor clip_color; ClipSpeed speed_info = c->speed(); @@ -459,8 +464,6 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { c->set_timeline_in(attr.value().toLong()); } else if (attr.name() == "out") { c->set_timeline_out(attr.value().toLong()); - } else if (attr.name() == "track") { - c->set_track(attr.value().toInt()); } else if (attr.name() == "r") { clip_color.setRed(attr.value().toInt()); } else if (attr.name() == "g") { @@ -518,7 +521,8 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { for (int k=0;klinked.append(link_attr.value().toInt()); + // FIXME reimplement this + //c->linked.append(link_attr.value().toInt()); break; } } @@ -546,11 +550,12 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } if (cancelled_) return false; - s->clips.append(c); + //s->clips.append(c); } } if (cancelled_) return false; + /* // correct links, clip IDs, transitions for (int i=0;iclips.size();i++) { // correct links @@ -585,6 +590,7 @@ bool LoadThread::load_worker(QFile& f, QXmlStreamReader& stream, int type) { MediaPtr m = panel_project->create_sequence_internal(nullptr, s, false, parent); loaded_sequences.append(m.get()); + */ } break; } @@ -780,11 +786,11 @@ void LoadThread::success_func() { olive::Global->update_project_filename(orig_filename); } else { - panel_project->add_recent_project(filename_); + olive::Global->add_recent_project(filename_); } olive::Global->set_modified(autorecovery_); if (open_seq != nullptr) { - olive::Global->set_sequence(open_seq); + Timeline::OpenSequence(open_seq); } } diff --git a/project/media.cpp b/project/media.cpp index a45560436..a242142c5 100644 --- a/project/media.cpp +++ b/project/media.cpp @@ -39,28 +39,6 @@ extern "C" { #include "global/debug.h" #include "global/timing.h" -QString get_interlacing_name(int interlacing) { - switch (interlacing) { - case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)"); - case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Top Field First"); - case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Bottom Field First"); - default: return QCoreApplication::translate("InterlacingName", "Invalid"); - } -} - -QString get_channel_layout_name(int channels, uint64_t layout) { - switch (channels) { - case 0: return QCoreApplication::translate("ChannelLayoutName", "Invalid"); - case 1: return QCoreApplication::translate("ChannelLayoutName", "Mono"); - case 2: return QCoreApplication::translate("ChannelLayoutName", "Stereo"); - default: { - char buf[50]; - av_get_channel_layout_string(buf, sizeof(buf), channels, layout); - return QString(buf); - } - } -} - Media::Media() : root(false), type(-1), @@ -172,7 +150,7 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += get_interlacing_name(f->video_tracks.at(i).video_interlacing); + tooltip += Footage::get_interlacing_name(f->video_tracks.at(i).video_interlacing); } } @@ -193,7 +171,7 @@ void Media::update_tooltip(const QString& error) { if (i > 0) { tooltip += ", "; } - tooltip += get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout); + tooltip += Footage::get_channel_layout_name(f->audio_tracks.at(i).audio_channels, f->audio_tracks.at(i).audio_layout); } // tooltip += "\n"; } @@ -216,7 +194,7 @@ void Media::update_tooltip(const QString& error) { QString::number(s->height), QString::number(s->frame_rate), QString::number(s->audio_frequency), - get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout) + Footage::get_channel_layout_name(av_get_channel_layout_nb_channels(s->audio_layout), s->audio_layout) ); } break; @@ -288,7 +266,7 @@ bool Media::setData(int col, const QVariant &value) { if (col == 0) { QString n = value.toString(); if (!n.isEmpty() && get_name() != n) { - olive::UndoStack.push(new MediaRename(this, value.toString())); + olive::undo_stack.push(new MediaRename(this, value.toString())); return true; } } @@ -310,7 +288,7 @@ int Media::columnCount() const { QString Media::GetStringDuration() { if (get_type() == MEDIA_TYPE_SEQUENCE) { Sequence* s = to_sequence().get(); - return frame_to_timecode(s->GetEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate); + return frame_to_timecode(s->GetEndFrame(), olive::config.timecode_view, s->frame_rate); } if (get_type() == MEDIA_TYPE_FOOTAGE) { Footage* f = to_footage(); @@ -320,7 +298,7 @@ QString Media::GetStringDuration() { r = f->video_tracks.at(0).video_frame_rate * f->speed; long len = f->get_length_in_frames(r); - if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r); + if (len > 0) return frame_to_timecode(len, olive::config.timecode_view, r); } return QString(); } diff --git a/project/previewgenerator.cpp b/project/previewgenerator.cpp index 035166577..173bc1572 100644 --- a/project/previewgenerator.cpp +++ b/project/previewgenerator.cpp @@ -218,8 +218,9 @@ void PreviewGenerator::finalize_media() { media_->update_tooltip(); } - if (olive::ActiveSequence != nullptr) { - olive::ActiveSequence->RefreshClips(media_); + QVector all_sequences = olive::project_model.GetAllSequences(); + for (int i=0;ito_sequence()->RefreshClipsUsingMedia(media_); } } } @@ -258,8 +259,8 @@ void PreviewGenerator::generate_waveform() { // we only generate previews for video and audio // and only if the thumbnail and waveform sizes are > 0 - if ((fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && olive::CurrentConfig.thumbnail_resolution > 0) - || (fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && olive::CurrentConfig.waveform_resolution > 0)) { + if ((fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && olive::config.thumbnail_resolution > 0) + || (fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && olive::config.waveform_resolution > 0)) { AVCodec* codec = avcodec_find_decoder(fmt_ctx_->streams[i]->codecpar->codec_id); if (codec != nullptr) { @@ -332,7 +333,7 @@ void PreviewGenerator::generate_waveform() { if (s != nullptr) { if (fmt_ctx_->streams[packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (!s->preview_done) { - int dstH = olive::CurrentConfig.thumbnail_resolution; + int dstH = olive::config.thumbnail_resolution; int dstW = qRound(dstH * (float(temp_frame->width)/float(temp_frame->height))); sws_ctx = sws_getContext( @@ -401,7 +402,7 @@ void PreviewGenerator::generate_waveform() { // `config.waveform_resolution` determines how many samples per second are stored in waveform. // `sample_rate` is samples per second, so `interval` is how many samples are averaged in // each "point" of the waveform - int interval = qFloor((temp_frame->sample_rate/olive::CurrentConfig.waveform_resolution)/4)*4; + int interval = qFloor((temp_frame->sample_rate/olive::config.waveform_resolution)/4)*4; // get the amount of bytes in an audio sample int sample_size = av_get_bytes_per_sample(static_cast(swr_frame->format)); diff --git a/project/projectfunctions.cpp b/project/projectfunctions.cpp index 5d9b8c486..9c36836c2 100644 --- a/project/projectfunctions.cpp +++ b/project/projectfunctions.cpp @@ -21,11 +21,11 @@ SequencePtr olive::project::CreateSequenceFromMedia(QVectorname = olive::project_model.GetNextSequenceName(); // Retrieve default Sequence settings from Config - s->width = olive::CurrentConfig.default_sequence_width; - s->height = olive::CurrentConfig.default_sequence_height; - s->frame_rate = olive::CurrentConfig.default_sequence_framerate; - s->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency; - s->audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout; + s->width = olive::config.default_sequence_width; + s->height = olive::config.default_sequence_height; + s->frame_rate = olive::config.default_sequence_framerate; + s->audio_frequency = olive::config.default_sequence_audio_frequency; + s->audio_layout = olive::config.default_sequence_audio_channel_layout; bool got_video_values = false; bool got_audio_values = false; diff --git a/project/projectmodel.cpp b/project/projectmodel.cpp index 001a35362..234abf6be 100644 --- a/project/projectmodel.cpp +++ b/project/projectmodel.cpp @@ -20,11 +20,18 @@ #include "projectmodel.h" +#include + #include "panels/panels.h" #include "panels/viewer.h" #include "ui/viewerwidget.h" +#include "ui/mainwindow.h" #include "project/media.h" #include "global/debug.h" +#include "global/config.h" +#include "global/global.h" +#include "projectfunctions.h" +#include "previewgenerator.h" ProjectModel olive::project_model; @@ -297,18 +304,14 @@ MediaPtr ProjectModel::CreateSequence(ComboAction *ca, SequencePtr s, bool open, ca->append(new AddMediaCommand(item, parent)); - if (open) { - ca->append(new ChangeSequenceAction(s)); - } - } else { appendChild(parent, item); - if (open) { - olive::Global->set_sequence(s); - } + } + if (open) { + Timeline::OpenSequence(s); } return item; @@ -404,7 +407,7 @@ void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPt bool imported = false; // retrieve the array of image formats from the user's configuration - QStringList image_sequence_formats = olive::CurrentConfig.img_seq_formats.split("|"); + QStringList image_sequence_formats = olive::config.img_seq_formats.split("|"); // a cache of image sequence formatted URLS to assist the user in importing image sequences QVector image_sequence_urls; @@ -422,8 +425,8 @@ void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPt // If this file is a directory, we'll recursively call this function again to process the directory's contents if (QFileInfo(files.at(i)).isDir()) { - QString folder_name = get_file_name_from_path(files.at(i)); - MediaPtr folder = CreateFolder(folder_name); + QString folder_name = QFileInfo(files.at(i)).fileName(); + MediaPtr folder = olive::project::CreateFolder(folder_name); QDir directory(files.at(i)); directory.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries); @@ -452,7 +455,7 @@ void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPt if (file.endsWith(".ove", Qt::CaseInsensitive)) { // This file is an Olive project file. Ask the user if they really want to import it. - if (QMessageBox::question(this, + if (QMessageBox::question(olive::MainWindow, tr("Import a Project"), tr("\"%1\" is an Olive project file. It will merge with this project. " "Do you wish to continue?").arg(file), @@ -556,7 +559,7 @@ void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPt image_sequence_urls.append(new_filename); // This does look like an image sequence, let's ask the user if it'll indeed be an image sequence - if (QMessageBox::question(this, + if (QMessageBox::question(olive::MainWindow, tr("Image sequence detected"), tr("The file '%1' appears to be part of an image sequence. " "Would you like to import it as such?").arg(file), @@ -640,7 +643,7 @@ void ProjectModel::process_file_list(QStringList& files, bool recursive, MediaPt } if (create_undo_action) { if (imported) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); for (int i=0;i ProjectModel::GetLastImportedMedia() +{ + return last_imported_media; +} diff --git a/project/savethread.cpp b/project/savethread.cpp index 8fcca1753..380f03be1 100644 --- a/project/savethread.cpp +++ b/project/savethread.cpp @@ -9,11 +9,12 @@ #include "global/config.h" #include "projectmodel.h" +/* void RecursiveSave() { } -void Project::save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex& parent) { +void save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex& parent) { for (int i=0;iadd_recent_project(olive::ActiveProjectFilename); olive::Global->set_modified(false); } } diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index f140cf764..b94daab3d 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -68,12 +68,11 @@ void SourcesCommon::create_seq_from_selected() { ComboAction* ca = new ComboAction(); SequencePtr s = olive::project::CreateSequenceFromMedia(media_list); - // add clips to it - panel_timeline->create_ghosts_from_media(s.get(), 0, media_list); - panel_timeline->add_clips_from_ghosts(ca, s.get()); + // add clips to it + s->AddClipsFromGhosts(ca, olive::timeline::CreateGhostsFromMedia(s.get(), 0, media_list)); olive::project_model.CreateSequence(ca, s, true, nullptr); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } } @@ -245,14 +244,14 @@ void SourcesCommon::replace_media(MediaPtr item, QString filename) { if (filename.isEmpty()) { filename = QFileDialog::getOpenFileName( - this, + olive::MainWindow, tr("Replace '%1'").arg(item->get_name()), "", tr("All Files") + " (*)"); } if (!filename.isEmpty()) { ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename); - olive::UndoStack.push(rmc); + olive::undo_stack.push(rmc); } } @@ -276,7 +275,7 @@ void SourcesCommon::mouseDoubleClickEvent(const QModelIndexList& selected_items) } else if (selected_items.size() == 1) { Media* media = project_parent->item_to_media(selected_items.at(0)); if (media->get_type() == MEDIA_TYPE_SEQUENCE) { - olive::UndoStack.push(new ChangeSequenceAction(media->to_sequence())); + Timeline::OpenSequence(media->to_sequence()); } else { OpenSelectedMediaInMediaViewer(project_parent->item_to_media(selected_items.at(0))); } @@ -302,7 +301,7 @@ void SourcesCommon::dropEvent(QWidget* parent, && drop_item.isValid() && m->get_type() == MEDIA_TYPE_FOOTAGE && !QFileInfo(paths.at(0)).isDir() - && olive::CurrentConfig.drop_on_media_to_replace + && olive::config.drop_on_media_to_replace && QMessageBox::question( parent, tr("Replace Media"), @@ -320,7 +319,7 @@ void SourcesCommon::dropEvent(QWidget* parent, parent = drop_item.parent(); } } - olive::project_model.process_file_list(paths, false, nullptr, panel_project->item_to_media(parent)); + olive::project_model.process_file_list(paths, false, nullptr, project_parent->item_to_media(parent)); } } event->acceptProposedAction(); @@ -359,7 +358,7 @@ void SourcesCommon::dropEvent(QWidget* parent, MediaMove* mm = new MediaMove(); mm->to = m.get(); mm->items = move_items; - olive::UndoStack.push(mm); + olive::undo_stack.push(mm); } } } @@ -403,7 +402,7 @@ void SourcesCommon::rename_interval() { void SourcesCommon::item_renamed(Media* item) { if (editing_item == item) { MediaRename* mr = new MediaRename(item, "idk"); - olive::UndoStack.push(mr); + olive::undo_stack.push(mr); editing_item = nullptr; } } @@ -447,9 +446,10 @@ void SourcesCommon::clear_proxies_from_selected() { f->proxy_path.clear(); } - if (olive::ActiveSequence != nullptr) { + QVector all_sequences = olive::project_model.GetAllSequences(); + for (int i=0;iClose(); + all_sequences.at(i)->to_sequence()->Close(); } // delete proxies requested to be deleted @@ -457,7 +457,7 @@ void SourcesCommon::clear_proxies_from_selected() { QFile::remove(delete_list.at(i)); } - if (olive::ActiveSequence != nullptr) { + if (panel_sequence_viewer->seq != nullptr) { // update viewer (will re-open active clips with original media) panel_sequence_viewer->viewer_widget()->frame_update(); } diff --git a/rendering/audio.cpp b/rendering/audio.cpp index e535d533a..e5a7c0747 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -69,7 +69,7 @@ QAudioDeviceInfo get_audio_device(QAudio::Mode mode) { QList devs = QAudioDeviceInfo::availableDevices(mode); // try to retrieve preferred device from config - QString preferred_device = (mode == QAudio::AudioOutput) ? olive::CurrentConfig.preferred_audio_output : olive::CurrentConfig.preferred_audio_input; + QString preferred_device = (mode == QAudio::AudioOutput) ? olive::config.preferred_audio_output : olive::config.preferred_audio_input; if (!preferred_device.isEmpty()) { for (int i=0;iaudio_monitor->set_value(averages); + panel_timeline.first()->audio_monitor->set_value(averages); } memset(audio_ibuffer+offset, 0, actual_write); @@ -324,8 +324,7 @@ void write_wave_trailer(QFile& f) { } bool start_recording() { - if (olive::ActiveSequence == nullptr) { - qCritical() << "No active sequence to record into"; + if (!olive::Global->CheckForActiveSequence(true)) { return false; } @@ -355,8 +354,8 @@ bool start_recording() { } QAudioFormat audio_format = audio_output->format(); - if (olive::CurrentConfig.recording_mode != audio_format.channelCount()) { - audio_format.setChannelCount(olive::CurrentConfig.recording_mode); + if (olive::config.recording_mode != audio_format.channelCount()) { + audio_format.setChannelCount(olive::config.recording_mode); } QAudioDeviceInfo info = get_audio_device(QAudio::AudioInput); diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index f85cf15a4..8832355c0 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -65,8 +65,8 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int } if (clip->opening_transition != nullptr) { if (clip->media() != nullptr && clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - double transition_start = (clip->clip_in(true) / clip->sequence->frame_rate); - double transition_end = (clip->clip_in(true) + clip->opening_transition->get_length()) / clip->sequence->frame_rate; + double transition_start = (clip->clip_in(true) / clip->track()->sequence()->frame_rate); + double transition_end = (clip->clip_in(true) + clip->opening_transition->get_length()) / clip->track()->sequence()->frame_rate; if (timecode_end < transition_end) { double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; @@ -78,8 +78,8 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int if (clip->closing_transition != nullptr) { if (clip->media() != nullptr && clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { long length_with_transitions = clip->timeline_out(true) - clip->timeline_in(true); - double transition_start = (clip->clip_in(true) + length_with_transitions - clip->closing_transition->get_length()) / clip->sequence->frame_rate; - double transition_end = (clip->clip_in(true) + length_with_transitions) / clip->sequence->frame_rate; + double transition_start = (clip->clip_in(true) + length_with_transitions - clip->closing_transition->get_length()) / clip->track()->sequence()->frame_rate; + double transition_end = (clip->clip_in(true) + length_with_transitions) / clip->track()->sequence()->frame_rate; if (timecode_start > transition_start) { double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; @@ -93,7 +93,7 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int Clip* next_nest = nests.last(); nests.removeLast(); apply_audio_effects(next_nest, - timecode_start + (double(clip->timeline_in(true)-clip->clip_in(true))/clip->sequence->frame_rate), + timecode_start + (double(clip->timeline_in(true)-clip->clip_in(true))/clip->track()->sequence()->frame_rate), frame, nb_bytes, nests); @@ -122,16 +122,16 @@ void Cacher::CacheAudioWorker() { bool reverse_audio = IsReversed(); long frame_skip = 0; - double last_fr = clip->sequence->frame_rate; + double last_fr = clip->track()->sequence()->frame_rate; if (!nests_.isEmpty()) { for (int i=nests_.size()-1;i>=0;i--) { - timeline_in = rescale_frame_number(timeline_in, last_fr, nests_.at(i)->sequence->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); - timeline_out = rescale_frame_number(timeline_out, last_fr, nests_.at(i)->sequence->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); - target_frame = rescale_frame_number(target_frame, last_fr, nests_.at(i)->sequence->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); + timeline_in = rescale_frame_number(timeline_in, last_fr, nests_.at(i)->track()->sequence()->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); + timeline_out = rescale_frame_number(timeline_out, last_fr, nests_.at(i)->track()->sequence()->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); + target_frame = rescale_frame_number(target_frame, last_fr, nests_.at(i)->track()->sequence()->frame_rate) + nests_.at(i)->timeline_in(true) - nests_.at(i)->clip_in(true); timeline_out = qMin(timeline_out, nests_.at(i)->timeline_out(true)); - frame_skip = rescale_frame_number(frame_skip, last_fr, nests_.at(i)->sequence->frame_rate); + frame_skip = rescale_frame_number(frame_skip, last_fr, nests_.at(i)->track()->sequence()->frame_rate); long validator = nests_.at(i)->timeline_in(true) - timeline_in; if (validator > 0) { @@ -139,12 +139,13 @@ void Cacher::CacheAudioWorker() { //timeline_in = nests_.at(i)->timeline_in(true); } - last_fr = nests_.at(i)->sequence->frame_rate; + last_fr = nests_.at(i)->track()->sequence()->frame_rate; } } if (temp_reverse) { - long seq_end = olive::ActiveSequence->GetEndFrame(); + // FIXME breakable? + long seq_end = Timeline::GetTopSequence()->GetEndFrame(); timeline_in = seq_end - timeline_in; timeline_out = seq_end - timeline_out; target_frame = seq_end - target_frame; @@ -394,7 +395,7 @@ void Cacher::CacheAudioWorker() { // apply any audio effects to the data if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; if (new_frame) { - apply_audio_effects(clip, bytes_to_seconds(audio_buffer_write, 2, current_audio_freq()) + audio_ibuffer_timecode + ((double)clip->clip_in(true)/clip->sequence->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests_); + apply_audio_effects(clip, bytes_to_seconds(audio_buffer_write, 2, current_audio_freq()) + audio_ibuffer_timecode + ((double)clip->clip_in(true)/clip->track()->sequence()->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests_); } } @@ -402,7 +403,7 @@ void Cacher::CacheAudioWorker() { if (frame->nb_samples == 0) { break; } else { - qint64 buffer_timeline_out = get_buffer_offset_from_frame(clip->sequence->frame_rate, timeline_out); + qint64 buffer_timeline_out = get_buffer_offset_from_frame(clip->track()->sequence()->frame_rate, timeline_out); audio_write_lock.lock(); @@ -597,15 +598,15 @@ void Cacher::CacheVideoWorker() { // For reversed playback, we flip the queue stats as "upcoming" frames are going to be played before the "previous" // frames now if (reversed) { - previous_queue_type = olive::CurrentConfig.upcoming_queue_type; - previous_queue_size = olive::CurrentConfig.upcoming_queue_size; - upcoming_queue_type = olive::CurrentConfig.previous_queue_type; - upcoming_queue_size = olive::CurrentConfig.previous_queue_size; + previous_queue_type = olive::config.upcoming_queue_type; + previous_queue_size = olive::config.upcoming_queue_size; + upcoming_queue_type = olive::config.previous_queue_type; + upcoming_queue_size = olive::config.previous_queue_size; } else { - previous_queue_type = olive::CurrentConfig.previous_queue_type; - previous_queue_size = olive::CurrentConfig.previous_queue_size; - upcoming_queue_type = olive::CurrentConfig.upcoming_queue_type; - upcoming_queue_size = olive::CurrentConfig.upcoming_queue_size; + previous_queue_type = olive::config.previous_queue_type; + previous_queue_size = olive::config.previous_queue_size; + upcoming_queue_type = olive::config.upcoming_queue_type; + upcoming_queue_size = olive::config.upcoming_queue_size; } // Determine "previous" queue statistics @@ -794,7 +795,7 @@ void Cacher::CacheVideoWorker() { void Cacher::Reset() { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values if (clip->media() == nullptr) { - if (clip->track() >= 0) { + if (clip->type() == Track::kTypeAudio) { // a null-media audio clip is usually an auto-generated sound clip such as Tone or Noise reached_end = false; audio_target_frame = playhead_; @@ -860,7 +861,7 @@ Cacher::Cacher(Clip* c) : void Cacher::OpenWorker() { // set some defaults for the audio cacher - if (clip->track() >= 0) { + if (clip->type() == Track::kTypeAudio) { audio_reset_ = false; frame_sample_index_ = -1; audio_buffer_write = 0; @@ -868,10 +869,10 @@ void Cacher::OpenWorker() { reached_end = false; if (clip->media() == nullptr) { - if (clip->track() >= 0) { + if (clip->type() == Track::kTypeAudio) { frame_ = av_frame_alloc(); frame_->format = kDestSampleFmt; - frame_->channel_layout = clip->sequence->audio_layout; + frame_->channel_layout = clip->track()->sequence()->audio_layout; frame_->channels = av_get_channel_layout_nb_channels(frame_->channel_layout); frame_->sample_rate = current_audio_freq(); frame_->nb_samples = 2048; @@ -889,7 +890,7 @@ void Cacher::OpenWorker() { QByteArray ba; // do we have a proxy? - if ((!olive::Global->is_exporting() || !olive::CurrentConfig.dont_use_proxies_on_export) + if ((!olive::Global->is_exporting() || !olive::config.dont_use_proxies_on_export) && m->proxy && !m->proxy_path.isEmpty() && QFileInfo::exists(m->proxy_path)) { @@ -1029,8 +1030,8 @@ void Cacher::OpenWorker() { reverse_frame->format = kDestSampleFmt; reverse_frame->nb_samples = current_audio_freq()*10; - reverse_frame->channel_layout = clip->sequence->audio_layout; - reverse_frame->channels = av_get_channel_layout_nb_channels(clip->sequence->audio_layout); + reverse_frame->channel_layout = clip->track()->sequence()->audio_layout; + reverse_frame->channels = av_get_channel_layout_nb_channels(clip->track()->sequence()->audio_layout); av_frame_get_buffer(reverse_frame, 0); queue_.append(reverse_frame); @@ -1116,7 +1117,7 @@ void Cacher::OpenWorker() { } void Cacher::CacheWorker() { - if (clip->track() < 0) { + if (clip->type() == Track::kTypeVideo) { // clip is a video track, start caching video CacheVideoWorker(); } else { @@ -1207,7 +1208,7 @@ void Cacher::Open() caching_ = true; queued_ = false; - start((clip->track() < 0) ? QThread::HighPriority : QThread::TimeCriticalPriority); + start((clip->type() == Track::kTypeVideo) ? QThread::HighPriority : QThread::TimeCriticalPriority); } void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int playback_speed) diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp index 30079f737..9bb58ed51 100644 --- a/rendering/exportthread.cpp +++ b/rendering/exportthread.cpp @@ -34,7 +34,6 @@ extern "C" { #include #include "global/global.h" -#include "timeline/sequence.h" #include "panels/panels.h" #include "ui/viewerwidget.h" #include "rendering/renderthread.h" @@ -192,16 +191,16 @@ bool ExportThread::SetupVideo() { video_frame = av_frame_alloc(); av_frame_make_writable(video_frame); video_frame->format = AV_PIX_FMT_RGBA; - video_frame->width = olive::ActiveSequence->width; - video_frame->height = olive::ActiveSequence->height; + video_frame->width = params_.sequence->width; + video_frame->height = params_.sequence->height; av_frame_get_buffer(video_frame, 0); av_init_packet(&video_pkt); // Set up conversion context sws_ctx = sws_getContext( - olive::ActiveSequence->width, - olive::ActiveSequence->height, + params_.sequence->width, + params_.sequence->height, AV_PIX_FMT_RGBA, params_.video_width, params_.video_height, @@ -288,7 +287,7 @@ bool ExportThread::SetupAudio() { acodec_ctx->channel_layout, acodec_ctx->sample_fmt, acodec_ctx->sample_rate, - olive::ActiveSequence->audio_layout, + params_.sequence->audio_layout, AV_SAMPLE_FMT_S16, acodec_ctx->sample_rate, 0, @@ -417,7 +416,7 @@ void ExportThread::Export() mutex.lock(); // Loop from now (set to the beginning frame earlier) to the end of the frame - while (olive::ActiveSequence->playhead <= params_.end_frame && !interrupt_) { + while (params_.sequence->playhead <= params_.end_frame && !interrupt_) { // Start timing how long this frame will take frame_start_time = QDateTime::currentMSecsSinceEpoch(); @@ -426,14 +425,14 @@ void ExportThread::Export() if (params_.audio_enabled) { waiting_for_audio_ = true; SetAudioWakeObject(this); - olive::rendering::compose_audio(nullptr, olive::ActiveSequence.get(), 1, true); + olive::rendering::compose_audio(nullptr, params_.sequence, 1, true); } // If we're exporting video, trigger a render on the RenderThread if (params_.video_enabled) { do { // TODO optimize by rendering the next frame while encoding the last - renderer->start_render(nullptr, olive::ActiveSequence.get(), 1, nullptr, video_frame->data[0], video_frame->linesize[0]/4); + renderer->start_render(nullptr, params_.sequence, 1, nullptr, video_frame->data[0], video_frame->linesize[0]/4); // Wait for RenderThread to return waitCond.wait(&mutex); @@ -452,7 +451,7 @@ void ExportThread::Export() } // Get the current sequence playhead in seconds (used for timestamp calculations later on) - double timecode_secs = double(olive::ActiveSequence->playhead - params_.start_frame) / olive::ActiveSequence->frame_rate; + double timecode_secs = double(params_.sequence->playhead - params_.start_frame) / params_.sequence->frame_rate; // If we're exporting video, construct an AVFrame in the destination codec's pixel format to convert the raw RGBA // OpenGL buffer to @@ -538,15 +537,15 @@ void ExportThread::Export() // Generating encoding statistics (e.g. the time it took to encode this frame/estimated remaining time) frame_time = (QDateTime::currentMSecsSinceEpoch()-frame_start_time); total_time += frame_time; - remaining_frames = (params_.end_frame - olive::ActiveSequence->playhead); + remaining_frames = (params_.end_frame - params_.sequence->playhead); avg_time = (total_time/frame_count); eta = (remaining_frames*avg_time); // Emit a signal for the percent of the sequence that's been encoded so far - emit ProgressChanged(qRound((double(olive::ActiveSequence->playhead - params_.start_frame) / double(params_.end_frame - params_.start_frame)) * 100.0), eta); + emit ProgressChanged(qRound((double(params_.sequence->playhead - params_.start_frame) / double(params_.end_frame - params_.start_frame)) * 100.0), eta); // Increment sequence playhead - olive::ActiveSequence->playhead++; + params_.sequence->playhead++; // Increment frame count (used for generating encoding statistics above) frame_count++; diff --git a/rendering/exportthread.h b/rendering/exportthread.h index 7c52e84f0..6180ec4e6 100644 --- a/rendering/exportthread.h +++ b/rendering/exportthread.h @@ -21,11 +21,17 @@ #ifndef EXPORTTHREAD_H #define EXPORTTHREAD_H +extern "C" { +#include +} + #include #include #include #include +#include "timeline/sequence.h" + struct AVFormatContext; struct AVCodecContext; struct AVFrame; @@ -35,19 +41,19 @@ struct AVCodec; struct SwsContext; struct SwrContext; -extern "C" { -#include -} - -#define COMPRESSION_TYPE_CBR 0 -#define COMPRESSION_TYPE_CFR 1 -#define COMPRESSION_TYPE_TARGETSIZE 2 -#define COMPRESSION_TYPE_TARGETBR 3 +enum CompressionType { + COMPRESSION_TYPE_CBR, + COMPRESSION_TYPE_CFR, + COMPRESSION_TYPE_TARGETSIZE, + COMPRESSION_TYPE_TARGETBR +}; // structs that store parameters passed from the export dialogs to this thread struct ExportParams { + // export parameters + Sequence* sequence; QString filename; bool video_enabled; int video_codec; diff --git a/rendering/framebufferobject.cpp b/rendering/framebufferobject.cpp index 876b01f08..153c6f7eb 100644 --- a/rendering/framebufferobject.cpp +++ b/rendering/framebufferobject.cpp @@ -68,8 +68,8 @@ void FramebufferObject::Create(QOpenGLContext *ctx, int width, int height) // allocate storage for texture const olive::PixelFormatInfo& bit_depth = olive::pixel_formats.at(olive::Global->is_exporting() ? - olive::CurrentConfig.export_bit_depth : - olive::CurrentConfig.playback_bit_depth); + olive::config.export_bit_depth : + olive::config.playback_bit_depth); ctx->functions()->glTexImage2D( GL_TEXTURE_2D, diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 8a8fe5a63..719554479 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -187,7 +187,7 @@ void process_effect(QOpenGLContext* ctx, if (e->Flags() & Effect::CoordsFlag) { e->process_coords(timecode, coords, data); } - bool can_process_shaders = ((e->Flags() & Effect::ShaderFlag) && olive::CurrentRuntimeConfig.shaders_are_enabled); + bool can_process_shaders = ((e->Flags() & Effect::ShaderFlag) && olive::runtime_config.shaders_are_enabled); if (can_process_shaders || (e->Flags() & Effect::SuperimposeFlag)) { if (!e->is_open()) { @@ -227,7 +227,7 @@ void process_effect(QOpenGLContext* ctx, } GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { - GLuint final_fbo = params.video ? params.main_buffer->buffer() : 0; + GLuint final_fbo = params.type == Track::kTypeVideo ? params.main_buffer->buffer() : 0; Sequence* s = params.seq; long playhead = s->playhead; @@ -237,10 +237,10 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { for (int i=0;imedia()->to_sequence().get(); playhead += params.nests.at(i)->clip_in(true) - params.nests.at(i)->timeline_in(true); - playhead = rescale_frame_number(playhead, params.nests.at(i)->sequence->frame_rate, s->frame_rate); + playhead = rescale_frame_number(playhead, params.nests.at(i)->track()->sequence()->frame_rate, s->frame_rate); } - if (params.video && !params.nests.last()->fbo.isEmpty()) { + if (params.type == Track::kTypeVideo && !params.nests.last()->fbo.isEmpty()) { params.nests.last()->fbo.at(0).BindBuffer(); params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); final_fbo = params.nests.last()->fbo.at(0).buffer(); @@ -253,14 +253,15 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { QVector current_clips; // loop through clips, find currently active, and sort by track - for (int i=0;iclips.size();i++) { + QVector sequence_clips = s->GetAllClips(); + for (int i=0;iclips.at(i).get(); + Clip* c = sequence_clips.at(i); if (c != nullptr) { // if clip is video and we're processing video - if ((c->track() < 0) == params.video) { + if (c->type() == params.type) { bool clip_is_active = false; @@ -269,7 +270,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { Footage* m = c->media()->to_footage(); // does the clip have a valid media source? - if (!m->invalid && !(c->track() >= 0 && !is_audio_device_set())) { + if (!m->invalid && !(c->type() == Track::kTypeAudio && !is_audio_device_set())) { // is the media process and ready? if (m->ready) { @@ -286,7 +287,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { clip_is_active = true; // increment audio track count - if (c->track() >= 0) audio_track_count++; + if (c->type() == Track::kTypeAudio) audio_track_count++; } else if (c->IsOpen()) { @@ -320,7 +321,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // track sorting is only necessary for video clips // audio clips are mixed equally, so we skip sorting for those - if (params.video) { + if (params.type == Track::kTypeVideo) { // insertion sort by track for (int j=0;jfunctions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -444,7 +445,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { } else if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { // Convert frame from source to linear colorspace - if (olive::CurrentConfig.enable_color_management) + if (olive::config.enable_color_management) { // Convert texture to sequence's internal format @@ -794,7 +795,7 @@ void olive::rendering::compose_audio(Viewer* viewer, Sequence* seq, int playback params.viewer = viewer; params.ctx = nullptr; params.seq = seq; - params.video = false; + params.type = Track::kTypeAudio; params.gizmos = nullptr; params.wait_for_mutexes = wait_for_mutexes; params.playback_speed = playback_speed; diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index 228608762..e38ee8bb6 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -80,9 +80,9 @@ struct ComposeSequenceParams { /** * @brief Set compose mode to video or audio * - * **TRUE** if this function should render video, **FALSE** if this function should render audio. + * Accepts Track::kTypeVideo to render video, Track::kTypeAudio if this function should render audio. */ - bool video; + Track::Type type; /** * @brief Set to the Effect whose gizmos were chosen to be drawn on screen diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index 352224d36..c92b323bd 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -116,7 +116,7 @@ void RenderThread::run() { } // If there's no OpenColorIO shader or the configuration has changed, (re-)create it now - if (olive::CurrentConfig.enable_color_management && ocio_shader == nullptr) { + if (olive::config.enable_color_management && ocio_shader == nullptr) { destroy_ocio(); set_up_ocio(); @@ -155,12 +155,12 @@ void RenderThread::set_up_ocio() OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); // Get current OCIO display from Config (or defaults if there is no setting) - QString display = olive::CurrentConfig.ocio_display; + QString display = olive::config.ocio_display; if (display.isEmpty()) { display = config->getDefaultDisplay(); } - QString view = olive::CurrentConfig.ocio_view; + QString view = olive::config.ocio_view; if (view.isEmpty()) { view = config->getDefaultView(display.toUtf8()); } @@ -171,8 +171,8 @@ void RenderThread::set_up_ocio() transform->setDisplay(display.toUtf8()); transform->setView(view.toUtf8()); - if (!olive::CurrentConfig.ocio_look.isEmpty()) { - transform->setLooksOverride(olive::CurrentConfig.ocio_look.toUtf8()); + if (!olive::config.ocio_look.isEmpty()) { + transform->setLooksOverride(olive::config.ocio_look.toUtf8()); transform->setLooksOverrideEnabled(true); } @@ -206,7 +206,7 @@ void RenderThread::paint() { params.viewer = nullptr; params.ctx = ctx; params.seq = seq; - params.video = true; + params.type = Track::kTypeVideo; params.texture_failed = false; params.wait_for_mutexes = true; params.playback_speed = playback_speed_; @@ -244,7 +244,7 @@ void RenderThread::paint() { // Blit the composite buffer to one of the front buffers // If we're color managing, conver the linear composited frame to display color space - if (olive::CurrentConfig.enable_color_management && ocio_shader != nullptr) { + if (olive::config.enable_color_management && ocio_shader != nullptr) { olive::rendering::OCIOBlit(ocio_shader.get(), ocio_lut_texture, diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 426502b65..e1d212058 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -32,8 +32,8 @@ #include "timeline/sequence.h" #include "panels/timeline.h" #include "project/media.h" -#include "project/clipboard.h" #include "undo/undo.h" +#include "global/clipboard.h" #include "global/debug.h" #include "global/timing.h" @@ -46,7 +46,7 @@ Clip::Clip(Track *s) : timeline_out_(0), media_(nullptr), reverse_(false), - autoscale_(olive::CurrentConfig.autoscale_by_default), + autoscale_(olive::config.autoscale_by_default), opening_transition(nullptr), closing_transition(nullptr), undeletable(false), @@ -110,6 +110,11 @@ bool Clip::IsTransitionSelected(TransitionType type) } } +Selection Clip::ToSelection() +{ + return Selection(timeline_in(), timeline_out(), track()); +} + Track::Type Clip::type() { return track()->type(); @@ -158,6 +163,11 @@ void Clip::set_media(Media *m, int s) media_stream_ = s; } +void Clip::Move(ComboAction *ca, long iin, long iout, long iclip_in, Track *itrack, bool verify_transitions, bool relative) +{ + track()->sequence()->MoveClip(this, ca, iin, iout, iclip_in, itrack, verify_transitions, relative); +} + bool Clip::enabled() { return enabled_; @@ -168,39 +178,6 @@ void Clip::set_enabled(bool e) enabled_ = e; } -void Clip::move(ComboAction* ca, long iin, long iout, long iclip_in, int itrack, bool verify_transitions, bool relative) -{ - ca->append(new MoveClipAction(this, iin, iout, iclip_in, itrack, relative)); - - if (verify_transitions) { - - // if this is a shared transition, and the corresponding clip will be moved away somehow - if (opening_transition != nullptr - && opening_transition->secondary_clip != nullptr - && opening_transition->secondary_clip->timeline_out() != iin) { - // separate transition - ca->append(new SetPointer(reinterpret_cast(&opening_transition->secondary_clip), nullptr)); - ca->append(new AddTransitionCommand(nullptr, - opening_transition->secondary_clip, - opening_transition, - nullptr, - 0)); - } - - if (closing_transition != nullptr - && closing_transition->secondary_clip != nullptr - && closing_transition->parent_clip->timeline_in() != iout) { - // separate transition - ca->append(new SetPointer(reinterpret_cast(&closing_transition->secondary_clip), nullptr)); - ca->append(new AddTransitionCommand(nullptr, - this, - closing_transition, - nullptr, - 0)); - } - } -} - void Clip::reset_audio() { if (UsesCacher()) { cacher.ResetAudio(); @@ -260,6 +237,9 @@ Clip::~Clip() { void Clip::Save(QXmlStreamWriter &stream) { + stream.writeStartElement("clip"); + stream.writeAttribute("id", QString::number(load_id)); + stream.writeAttribute("enabled", QString::number(enabled())); stream.writeAttribute("name", name()); stream.writeAttribute("clipin", QString::number(clip_in())); @@ -312,21 +292,17 @@ void Clip::Save(QXmlStreamWriter &stream) if (transition != nullptr) { stream.writeStartElement((t == kTransitionOpening) ? "opening" : "closing"); - // check if this is a shared transition and the transition has already been saved - int transition_cache_index = transition_save_cache.indexOf(transition); - - if (transition_cache_index > -1) { + // check if this is a shared transition + if (this == transition->secondary_clip) { // if so, just save a reference to the other clip stream.writeAttribute("shared", - QString::number(transition_clip_save_cache.at(transition_cache_index))); + QString::number(transition->parent_clip->load_id)); } else { // otherwise save the whole transition transition->save(stream); - transition_save_cache.append(transition); - transition_clip_save_cache.append(j); } - stream.writeEndElement(); // opening + stream.writeEndElement(); // opening/closing } } @@ -336,7 +312,7 @@ void Clip::Save(QXmlStreamWriter &stream) stream.writeEndElement(); // effect } - + stream.writeEndElement(); // clip } long Clip::clip_in(bool with_transition) { @@ -441,6 +417,15 @@ Track *Clip::track() void Clip::set_track(Track *t) { + // Ensure this clip has already been added to this track + bool found = false; + for (int i=0;iClipCount();i++) { + if (t->GetClip(i).get() == this) { + found = true; + break; + } + } + track_ = t; } @@ -510,13 +495,13 @@ int Clip::media_width() { } int Clip::media_height() { - if (media_ == nullptr && sequence != nullptr) return sequence->height; + if (media_ == nullptr && track() != nullptr) return track()->sequence()->height; switch (media_->get_type()) { case MEDIA_TYPE_FOOTAGE: { const FootageStream* ms = media_stream(); if (ms != nullptr) return ms->video_height; - if (sequence != nullptr) return sequence->height; + if (track() != nullptr) return track()->sequence()->height; } break; case MEDIA_TYPE_SEQUENCE: @@ -530,11 +515,12 @@ int Clip::media_height() { void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points) { if (change_timeline_points) { - this->move(ca, - qRound(double(timeline_in_) * multiplier), - qRound(double(timeline_out_) * multiplier), - qRound(double(clip_in_) * multiplier), - track_); + track()->sequence()->MoveClip(this, + ca, + qRound(double(timeline_in_) * multiplier), + qRound(double(timeline_out_) * multiplier), + qRound(double(clip_in_) * multiplier), + track_); } // move keyframes @@ -645,79 +631,79 @@ bool Clip::Retrieve() //if (frame->pts != texture_timestamp) { - bool allocate_data = false; + bool allocate_data = false; - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - // check if the opengl texture exists yet, create it if not - if (texture == 0) { + // check if the opengl texture exists yet, create it if not + if (texture == 0) { - // create texture object - f->glGenTextures(1, &texture); + // create texture object + f->glGenTextures(1, &texture); - f->glBindTexture(GL_TEXTURE_2D, texture); + f->glBindTexture(GL_TEXTURE_2D, texture); - // set texture filtering to bilinear - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // set texture filtering to bilinear + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // set texture wrapping to clamp - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // set texture wrapping to clamp + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // queue an allocation ahead - allocate_data = true; + // queue an allocation ahead + allocate_data = true; - } else { + } else { - f->glBindTexture(GL_TEXTURE_2D, texture); + f->glBindTexture(GL_TEXTURE_2D, texture); - } + } - int video_width = cacher.media_width(); - int video_height = cacher.media_height(); + int video_width = cacher.media_width(); + int video_height = cacher.media_height(); - const olive::PixelFormatInfo& pix_fmt_info = olive::pixel_formats.at(cacher.media_pixel_format()); + const olive::PixelFormatInfo& pix_fmt_info = olive::pixel_formats.at(cacher.media_pixel_format()); - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/pix_fmt_info.bytes_per_pixel); + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]/pix_fmt_info.bytes_per_pixel); - if (allocate_data) { + if (allocate_data) { - // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure - // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the - // composition - f->glTexImage2D( - GL_TEXTURE_2D, - 0, - pix_fmt_info.internal_format, - video_width, - video_height, - 0, - pix_fmt_info.pixel_format, - pix_fmt_info.pixel_type, - frame->data[0] - ); + // the raw frame size may differ from the one we're using (e.g. a lower resolution proxy), so we make sure + // the texture is using the correct dimensions, but then treat it as if it's the original resolution in the + // composition + f->glTexImage2D( + GL_TEXTURE_2D, + 0, + pix_fmt_info.internal_format, + video_width, + video_height, + 0, + pix_fmt_info.pixel_format, + pix_fmt_info.pixel_type, + frame->data[0] + ); - } else { + } else { - f->glTexSubImage2D(GL_TEXTURE_2D, - 0, - 0, - 0, - video_width, - video_height, - pix_fmt_info.pixel_format, - pix_fmt_info.pixel_type, - frame->data[0] - ); + f->glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + video_width, + video_height, + pix_fmt_info.pixel_format, + pix_fmt_info.pixel_type, + frame->data[0] + ); - } + } - f->glBindTexture(GL_TEXTURE_2D, 0); + f->glBindTexture(GL_TEXTURE_2D, 0); - f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + f->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - texture_timestamp = frame->pts; + texture_timestamp = frame->pts; //} diff --git a/timeline/clip.h b/timeline/clip.h index 69bd75e67..9df7ec244 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -59,6 +59,8 @@ public: bool IsSelected(bool containing = true); bool IsTransitionSelected(TransitionType type); + Selection ToSelection(); + Track::Type type(); const QColor& color(); @@ -74,17 +76,17 @@ public: long media_length(); void set_media(Media* m, int s); - bool enabled(); - void set_enabled(bool e); - - void move(ComboAction* ca, + void Move(ComboAction* ca, long iin, long iout, long iclip_in, - int itrack, + Track *itrack, bool verify_transitions = true, bool relative = false); + bool enabled(); + void set_enabled(bool e); + long clip_in(bool with_transition = false); void set_clip_in(long c); diff --git a/timeline/ghost.cpp b/timeline/ghost.cpp new file mode 100644 index 000000000..2e9780709 --- /dev/null +++ b/timeline/ghost.cpp @@ -0,0 +1,6 @@ +#include "ghost.h" + +Selection Ghost::ToSelection() const +{ + return Selection(in, out, track); +} diff --git a/timeline/ghost.h b/timeline/ghost.h index cf89b6412..e0da417d5 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -2,11 +2,22 @@ #define GHOST_H #include "effects/transition.h" -#include "timelinefunctions.h" #include "track.h" +namespace olive { +namespace timeline { + +enum TrimType { + TRIM_NONE, + TRIM_IN, + TRIM_OUT +}; + +} +} + struct Ghost { - int clip; + Clip* clip; long in; long out; Track* track; @@ -28,6 +39,8 @@ struct Ghost { // transition trimming TransitionPtr transition; + + Selection ToSelection() const; }; #endif // GHOST_H diff --git a/timeline/marker.cpp b/timeline/marker.cpp index 18fa068b9..f4043da2c 100644 --- a/timeline/marker.cpp +++ b/timeline/marker.cpp @@ -49,22 +49,24 @@ void Marker::Draw(QPainter &p, int x, int y, int bottom, bool selected) { p.drawPolygon(points, 5); } -void set_marker_internal(Sequence* seq, const QVector& clips) { - // if clips is empty, the marker is being added to the sequence +void Marker::SetOnClips(const QVector &clips) +{ + // Don't bother if there are no clips + if (clips.isEmpty()) { + return; + } // add_marker is used to determine whether we're adding a marker, depending on whether the user input a marker name // however if (config.set_name_with_marker) is true, we don't need a marker name so we just add - bool add_marker = !olive::CurrentConfig.set_name_with_marker; + bool add_marker = !olive::config.set_name_with_marker; QString marker_name; - // if (config.set_name_with_marker) is false (set above), ask for a marker name + // if Config::set_name_with_marker is true (set above), ask for a marker name if (!add_marker) { QInputDialog d(olive::MainWindow); d.setWindowTitle(QCoreApplication::translate("Marker", "Set Marker")); - d.setLabelText(clips.size() > 0 - ? QCoreApplication::translate("Marker", "Set clip marker name:") - : QCoreApplication::translate("Marker", "Set sequence marker name:")); + d.setLabelText(QCoreApplication::translate("Marker", "Set clip marker name:")); d.setInputMode(QInputDialog::TextInput); add_marker = (d.exec() == QDialog::Accepted); marker_name = d.textValue(); @@ -75,43 +77,16 @@ void set_marker_internal(Sequence* seq, const QVector& clips) { ComboAction* ca = new ComboAction(); - if (clips.size() > 0) { - - // add a marker action for each clip - foreach (int i, clips) { - ClipPtr c = seq->clips.at(i); - ca->append(new AddMarkerAction(&c->get_markers(), - seq->playhead - c->timeline_in() + c->clip_in(), - marker_name)); - } - - } else { - - // if no clips are selected, we're adding a marker to the sequence - - // kind of hacky, we get the correct marker structure from the viewer panel object that the sequence is attached to - if (seq == panel_footage_viewer->seq.get()) { - - // get correct marker reference from footage viewer - ca->append(new AddMarkerAction(panel_footage_viewer->marker_ref, seq->playhead, marker_name)); - - } else if (seq == panel_sequence_viewer->seq.get()) { - - // get correct marker reference from sequence viewer - ca->append(new AddMarkerAction(panel_sequence_viewer->marker_ref, seq->playhead, marker_name)); - - } else { - - // fallback to using markers from sequence provided - ca->append(new AddMarkerAction(&seq->markers, seq->playhead, marker_name)); - - } - + // add a marker action for each clip + foreach (Clip* c, clips) { + ca->append(new AddMarkerAction(&c->get_markers(), + c->track()->sequence()->playhead - c->timeline_in() + c->clip_in(), + marker_name)); } // push action - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); // redraw UI for new markers update_ui(false); @@ -120,11 +95,65 @@ void set_marker_internal(Sequence* seq, const QVector& clips) { } } -void set_marker_internal(Sequence *seq) { - // create empty clip array - QVector clips; +void Marker::SetOnSequence(Sequence *seq) { + + // Don't bother if there is no sequence + if (seq == nullptr) { + return; + } + + // add_marker is used to determine whether we're adding a marker, depending on whether the user input a marker name + // however if (config.set_name_with_marker) is true, we don't need a marker name so we just add + bool add_marker = !olive::config.set_name_with_marker; + + QString marker_name; + + // if Config::set_name_with_marker is true (set above), ask for a marker name + if (!add_marker) { + QInputDialog d(olive::MainWindow); + d.setWindowTitle(QCoreApplication::translate("Marker", "Set Marker")); + d.setLabelText(QCoreApplication::translate("Marker", "Set sequence marker name:")); + d.setInputMode(QInputDialog::TextInput); + add_marker = (d.exec() == QDialog::Accepted); + marker_name = d.textValue(); + } + + // if we've decided to add a marker + if (add_marker) { + + ComboAction* ca = new ComboAction(); + + // FIXME kind of hacky, we get the correct marker structure from the viewer panel object that the sequence is + // attached to, as the viewers will give us the footage marker set if its footage rather than the sequence marker + // set + + if (seq == panel_footage_viewer->seq.get()) { + + // get correct marker reference from footage viewer + ca->append(new AddMarkerAction(panel_footage_viewer->marker_ref, seq->playhead, marker_name)); + + } else if (seq == panel_sequence_viewer->seq.get()) { + + // get correct marker reference from sequence viewer + ca->append(new AddMarkerAction(panel_sequence_viewer->marker_ref, seq->playhead, marker_name)); + + } else { + + // fallback to using markers from sequence provided + ca->append(new AddMarkerAction(&seq->markers, seq->playhead, marker_name)); + + } + + + // push action + olive::undo_stack.push(ca); + + // redraw UI for new markers + update_ui(false); + panel_footage_viewer->update_viewer(); + + } - set_marker_internal(seq, clips); } void Marker::Save(QXmlStreamWriter &stream) const diff --git a/timeline/marker.h b/timeline/marker.h index 9e88bf598..7d8c9ad7a 100644 --- a/timeline/marker.h +++ b/timeline/marker.h @@ -28,8 +28,8 @@ #include #include +class Clip; class Sequence; -using SequencePtr = std::shared_ptr; struct Marker { long frame; @@ -37,9 +37,10 @@ struct Marker { void Save(QXmlStreamWriter& stream) const; static void Draw(QPainter& p, int x, int y, int bottom, bool selected); + + + static void SetOnClips(const QVector& clips); + static void SetOnSequence(Sequence* seq); }; -void set_marker_internal(Sequence *seq, const QVector& clips); -void set_marker_internal(Sequence* seq); - #endif // MARKER_H diff --git a/timeline/selection.cpp b/timeline/selection.cpp index 96c059613..f7c71649a 100644 --- a/timeline/selection.cpp +++ b/timeline/selection.cpp @@ -1,12 +1,16 @@ #include "selection.h" +#include "effects/transition.h" +#include "timeline/clip.h" + +Selection::Selection() +{ +} + Selection::Selection(long in, long out, Track *track) : in_(in), out_(out), - track_(track), - old_in_(in), - old_out_(out), - old_track_(track) + track_(track) { } @@ -35,7 +39,22 @@ void Selection::set_out(long out) out_ = out; } -void Selection::Tidy(QVector selections) +bool Selection::ContainsTransition(Clip* c, int type) const +{ + if (type == kTransitionOpening) { + return c->opening_transition != nullptr + && out_ == c->timeline_in() + c->opening_transition->get_true_length() + && ((c->opening_transition->secondary_clip == nullptr && in_ == c->timeline_in()) + || (c->opening_transition->secondary_clip != nullptr && in_ == c->timeline_in() - c->opening_transition->get_true_length())); + } else { + return c->closing_transition != nullptr + && in_ == c->timeline_out() - c->closing_transition->get_true_length() + && ((c->closing_transition->secondary_clip == nullptr && out_ == c->timeline_out()) + || (c->closing_transition->secondary_clip != nullptr && out_ == c->timeline_out() + c->closing_transition->get_true_length())); + } +} + +void Selection::Tidy(QVector& selections) { for (int i=0;i selections) } else if (s.in() >= ss.in() && s.out() <= ss.out()) { remove = true; } else if (s.in() <= ss.out() && s.out() > ss.out()) { - ss.out = s.out(); + ss.set_out(s.out()); remove = true; } else if (s.out() >= ss.in() && s.in() < ss.in()) { - ss.in = s.in(); + ss.set_in(s.in()); remove = true; } if (remove) { diff --git a/timeline/selection.h b/timeline/selection.h index 6b76e311d..bdf045eac 100644 --- a/timeline/selection.h +++ b/timeline/selection.h @@ -23,10 +23,12 @@ #include +class Clip; class Track; class Selection { public: + Selection(); Selection(long in, long out, Track* track); long in() const; @@ -36,7 +38,9 @@ public: void set_in(long in); void set_out(long out); - static void Tidy(QVector selections); + bool ContainsTransition(Clip* c, int type) const; + + static void Tidy(QVector &selections); private: long in_; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index c79900ddb..93ab5bf2d 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -22,8 +22,10 @@ #include +#include "timelinefunctions.h" #include "panels/panels.h" -#include "project/clipboard.h" +#include "global/clipboard.h" +#include "global/config.h" #include "global/debug.h" Sequence::Sequence() : @@ -77,7 +79,7 @@ void Sequence::Save(QXmlStreamWriter &stream) stream.writeAttribute("framerate", QString::number(frame_rate, 'f', 10)); stream.writeAttribute("afreq", QString::number(audio_frequency)); stream.writeAttribute("alayout", QString::number(audio_layout)); - if (this == olive::ActiveSequence.get()) { + if (this == Timeline::GetTopSequence().get()) { stream.writeAttribute("open", "1"); } stream.writeAttribute("workarea", QString::number(using_workarea)); @@ -170,13 +172,303 @@ QVector Sequence::SelectedClips(bool containing) for (int j=0;jTrackCount();j++) { Track* t = tl->TrackAt(j); - selected_clips.append(t->GetAllClips()); + selected_clips.append(t->GetSelectedClips(containing)); } } return selected_clips; } +void Sequence::AddClipsFromGhosts(ComboAction* ca, const QVector& ghosts) +{ + // add clips + long earliest_point = LONG_MAX; + QVector added_clips; + for (int i=0;i(g.track); + c->set_media(g.media, g.media_stream); + c->set_timeline_in(g.in); + c->set_timeline_out(g.out); + c->set_clip_in(g.clip_in); + c->set_track(g.track); + if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { + Footage* m = c->media()->to_footage(); + if (m->video_tracks.size() == 0) { + // audio only (greenish) + c->set_color(128, 192, 128); + } else if (m->audio_tracks.size() == 0) { + // video only (orangeish) + c->set_color(192, 160, 128); + } else { + // video and audio (blueish) + c->set_color(128, 128, 192); + } + c->set_name(m->name); + } else if (c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { + // sequence (red?ish?) + c->set_color(192, 128, 128); + + c->set_name(c->media()->to_sequence()->name); + } + c->refresh(); + added_clips.append(c); + + } + ca->append(new AddClipCommand(added_clips)); + + // link clips from the same media + for (int i=0;imedia() == cc->media()) { + c->linked.append(cc.get()); + } + } + + if (olive::config.add_default_effects_to_clips) { + if (c->type() == Track::kTypeVideo) { + // add default video effects + c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); + } else if (c->type() == Track::kTypeAudio) { + // add default audio effects + c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); + c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); + } + } + } + + if (olive::config.enable_seek_to_import) { + panel_sequence_viewer->seek(earliest_point); + } + + olive::timeline::snapped = false; +} + +void Sequence::MoveClip(Clip *c, ComboAction *ca, long iin, long iout, long iclip_in, Track *itrack, bool verify_transitions, bool relative) +{ + ClipPtr clip_ptr = c->track()->GetClipObjectFromRawPtr(c); + + ca->append(new MoveClipAction(clip_ptr, iin, iout, iclip_in, itrack, relative)); + + if (verify_transitions) { + + // if this is a shared transition, and the corresponding clip will be moved away somehow + if (c->opening_transition != nullptr + && c->opening_transition->secondary_clip != nullptr + && c->opening_transition->secondary_clip->timeline_out() != iin) { + // separate transition + ca->append(new SetPointer(reinterpret_cast(&c->opening_transition->secondary_clip), nullptr)); + ca->append(new AddTransitionCommand(nullptr, + c->opening_transition->secondary_clip, + c->opening_transition, + nullptr, + 0)); + } + + if (c->closing_transition != nullptr + && c->closing_transition->secondary_clip != nullptr + && c->closing_transition->parent_clip->timeline_in() != iout) { + // separate transition + ca->append(new SetPointer(reinterpret_cast(&c->closing_transition->secondary_clip), nullptr)); + ca->append(new AddTransitionCommand(nullptr, + c, + c->closing_transition, + nullptr, + 0)); + } + } +} + +void Sequence::EditToPoint(bool in, bool ripple) +{ + QVector all_clips = GetAllClips(); + + if (all_clips.size() > 0) { + long sequence_end = 0; + + bool playhead_falls_on_in = false; + bool playhead_falls_on_out = false; + long next_cut = LONG_MAX; + long prev_cut = 0; + + // find closest in point to playhead + for (int i=0;itimeline_out(), sequence_end); + + if (c->timeline_in() == playhead) + playhead_falls_on_in = true; + + if (c->timeline_out() == playhead) + playhead_falls_on_out = true; + + if (c->timeline_in() > playhead) + next_cut = qMin(c->timeline_in(), next_cut); + + if (c->timeline_out() > playhead) + next_cut = qMin(c->timeline_out(), next_cut); + + if (c->timeline_in() < playhead) + prev_cut = qMax(c->timeline_in(), prev_cut); + + if (c->timeline_out() < playhead) + prev_cut = qMax(c->timeline_out(), prev_cut); + + } + + next_cut = qMin(sequence_end, next_cut); + + QVector areas; + ComboAction* ca = new ComboAction(); + bool push_undo = true; + long seek = playhead; + + if ((in && (playhead_falls_on_out || (playhead_falls_on_in && playhead == 0))) + || (!in && (playhead_falls_on_in || (playhead_falls_on_out && playhead == sequence_end)))) { // one frame mode + if (ripple) { + // set up deletion areas based on track count + long in_point = playhead; + if (!in) { + in_point--; + seek--; + } + + if (in_point >= 0) { + + for (int i=0;iTrackCount();j++) { + areas.append(Selection(in_point, in_point+1, tl->TrackAt(j))); + } + + } + + // trim and move clips around the in point + DeleteAreas(ca, areas, true); + + if (ripple) { + Ripple(ca, in_point, -1); + } + } else { + push_undo = false; + } + } else { + push_undo = false; + } + } else { + // set up deletion areas based on track count + + long area_in, area_out; + + if (in) { + seek = prev_cut; + area_in = prev_cut; + area_out = playhead; + } else { + area_in = playhead; + area_out = next_cut; + } + + if (area_in == area_out) { + + push_undo = false; + + } else { + + for (int i=0;iTrackCount();j++) { + areas.append(Selection(area_in, area_out, tl->TrackAt(j))); + } + + } + + // trim and move clips around the in point + DeleteAreas(ca, areas, true); + if (ripple) { + Ripple(ca, area_in, area_in - area_out); + } + } + } + + if (push_undo) { + olive::undo_stack.push(ca); + + update_ui(true); + + if (seek != playhead && ripple) { + panel_sequence_viewer->seek(seek); + } + } else { + delete ca; + } + } else { + panel_sequence_viewer->seek(0); + } +} + +bool Sequence::SnapPoint(long *l, double zoom, bool use_playhead, bool use_markers, bool use_workarea) +{ + olive::timeline::snapped = false; + if (olive::timeline::snapping) { + if (use_playhead && !panel_sequence_viewer->playing) { + // snap to playhead + if (olive::timeline::SnapToPoint(playhead, l, zoom)) return true; + } + + // snap to marker + if (use_markers) { + for (int i=0;i all_clips = GetAllClips(); + for (int i=0;itimeline_in(), l, zoom)) { + return true; + } else if (olive::timeline::SnapToPoint(c->timeline_out(), l, zoom)) { + return true; + } else if (c->opening_transition != nullptr + && olive::timeline::SnapToPoint(c->timeline_in() + c->opening_transition->get_true_length(), l, zoom)) { + return true; + } else if (c->closing_transition != nullptr + && olive::timeline::SnapToPoint(c->timeline_out() - c->closing_transition->get_true_length(), l, zoom)) { + return true; + } else { + // try to snap to clip markers + for (int j=0;jget_markers().size();j++) { + if (olive::timeline::SnapToPoint(c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(), l, zoom)) { + return true; + } + } + } + + } + } + + return false; +} + void Sequence::DeleteInToOut(bool ripple) { if (using_workarea) { @@ -198,13 +490,45 @@ void Sequence::DeleteInToOut(bool ripple) if (ripple) Ripple(ca, workarea_in, workarea_in - workarea_out); - ca->append(new SetTimelineInOutCommand(olive::ActiveSequence.get(), false, 0, 0)); - olive::UndoStack.push(ca); + ca->append(new SetTimelineInOutCommand(this, false, 0, 0)); + olive::undo_stack.push(ca); update_ui(true); } } -void Sequence::Ripple(ComboAction *ca, long point, long length, const QVector &ignore) +void Sequence::DeleteClipsUsingMedia(const QVector& media) +{ + QVector all_clips = GetAllClips(); + + ComboAction* ca = new ComboAction(); + bool deleted = false; + + for (int j=0;jmedia() == media.at(j)) { + ca->append(new DeleteClipAction(c)); + deleted = true; + } + } + } + + if (deleted) { + olive::undo_stack.push(ca); + update_ui(true); + } else { + delete ca; + } +} + +void Sequence::Ripple(ComboAction *ca, long point, long length, const QVector &ignore) { ca->append(new RippleAction(this, point, length, ignore)); } @@ -222,14 +546,95 @@ void Sequence::ChangeTrackHeightsRelatively(int diff) } } -void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas) +void Sequence::ToggleLinksOnSelected() +{ + QVector selected_clips = SelectedClips(); + + bool link = true; + QVector link_clips; + + for (int i=0;ilinked.size() > 0) { + link = false; // prioritize unlinking + + for (int j=0;jlinked.size();j++) { // add links to the command + if (!link_clips.contains(c->linked.at(j))) { + link_clips.append(c->linked.at(j)); + } + } + } + } + + if (!link_clips.isEmpty()) { + olive::undo_stack.push(new LinkCommand(link_clips, link)); + } +} + +void Sequence::Split() +{ + ComboAction* ca = new ComboAction(); + bool split_selected = false; + + QVector selected_clips = SelectedClips(true); + if (selected_clips.size() > 0) { + // see if whole clips are selected + QVector pre_clips; + QVector post_clips; + + for (int i=0;iappend(new AddClipCommand(post_clips)); + + } else { + + // split a selection if not + // FIXME reimplement split selection + //split_selected = split_selection(ca); + + } + } + + // if nothing was selected or no selections fell within playhead, simply split at playhead + if (!split_selected) { + split_selected = SplitAllClipsAtPoint(ca, playhead); + } + + if (split_selected) { + olive::undo_stack.push(ca); + update_ui(true); + } else { + delete ca; + } +} + +void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas, bool ripple) { Selection::Tidy(areas); panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - QVector pre_clips; + QVector pre_clips; QVector post_clips; QVector all_clips = GetAllClips(); @@ -239,10 +644,10 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool desel for (int j=0;jtrack() == s.track() && !c->undeletable) { - if (selection_contains_transition(s, c, kTransitionOpening)) { + if (s.ContainsTransition(c, kTransitionOpening)) { // delete opening transition ca->append(new DeleteTransitionCommand(c->opening_transition)); - } else if (selection_contains_transition(s, c, kTransitionClosing)) { + } else if (s.ContainsTransition(c, kTransitionClosing)) { // delete closing transition ca->append(new DeleteTransitionCommand(c->closing_transition)); } else if (c->timeline_in() >= s.in() && c->timeline_out() <= s.out()) { @@ -254,28 +659,30 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool desel // duplicate clip ClipPtr post = SplitClip(ca, true, c, s.in(), s.out()); - pre_clips.append(j); + pre_clips.append(c); post_clips.append(post); } else if (c->timeline_in() < s.in() && c->timeline_out() > s.in()) { // only out point is in deletion area - c->move(ca, c->timeline_in(), s.in(), c->clip_in(), c->track()); + MoveClip(c, ca, c->timeline_in(), s.in(), c->clip_in(), c->track()); if (c->closing_transition != nullptr) { if (s.in() < c->timeline_out() - c->closing_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->closing_transition)); } else { - ca->append(new ModifyTransitionCommand(c->closing_transition, c->closing_transition->get_true_length() - (c->timeline_out() - s.in))); + ca->append(new ModifyTransitionCommand(c->closing_transition, + c->closing_transition->get_true_length() - (c->timeline_out() - s.in()))); } } } else if (c->timeline_in() < s.out() && c->timeline_out() > s.out()) { // only in point is in deletion area - c->move(ca, s.out(), c->timeline_out(), c->clip_in() + (s.out() - c->timeline_in()), c->track()); + MoveClip(c, ca, s.out(), c->timeline_out(), c->clip_in() + (s.out() - c->timeline_in()), c->track()); if (c->opening_transition != nullptr) { if (s.out() > c->timeline_in() + c->opening_transition->get_true_length()) { ca->append(new DeleteTransitionCommand(c->opening_transition)); } else { - ca->append(new ModifyTransitionCommand(c->opening_transition, c->opening_transition->get_true_length() - (s.out - c->timeline_in()))); + ca->append(new ModifyTransitionCommand(c->opening_transition, + c->opening_transition->get_true_length() - (s.out() - c->timeline_in()))); } } } @@ -288,12 +695,16 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool desel QVector area_copy = areas; for (int i=0;iDeselectArea(s.in(), s.out()); } } - relink_clips_using_ids(pre_clips, post_clips); - ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_clips)); + if (ripple) { + + } + + olive::timeline::RelinkClips(pre_clips, post_clips); + ca->append(new AddClipCommand(post_clips)); } bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) @@ -314,10 +725,12 @@ bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) return split; } -void Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) +bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) { // Add the clip and each of its links to the pre_splits array + bool split_occurred = false; + QVector pre_splits; pre_splits.append(clip); @@ -346,15 +759,102 @@ void Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector p for (int j=0;jset_timeline_out(positions.at(i+1)); + if (post_splits[i][j] != nullptr) { + split_occurred = true; + + if (i + 1 < positions.size()) { + post_splits[i][j]->set_timeline_out(positions.at(i+1)); + } } } } for (int i=0;iappend(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i])); + ca->append(new AddClipCommand(post_splits[i])); + } + + return split_occurred; +} + +void Sequence::RippleDeleteEmptySpace(Track* track, long point) +{ + QVector track_clips = track->GetAllClips(); + + long ripple_start = LONG_MAX; + long ripple_end = LONG_MAX; + + for (int i=0;itimeline_in() <= point && c->timeline_out() >= point) { + // This point is not actually empty, so there's nothing to do here + return; + } + + if (c->timeline_out() < point) { + + ripple_start = qMin(c->timeline_out(), ripple_start); + + } else if (c->timeline_in() > point) { + + ripple_end = qMin(c->timeline_in(), ripple_end); + + } + } + + // We now know the maximum ripple we could do to clear this empty space, but we need to ensure it won't cause + // overlaps of clips in other tracks + + for (int i=0;iTrackCount();j++) { + Track* t = tl->TrackAt(j); + + // We've already tested `track`, so we don't need to test it again + if (t != track) { + + long first_in_point_after_point = LONG_MAX; + long out_point_just_before_first_in_point = LONG_MIN; + + QVector track_clips = t->GetAllClips(); + + // Find the in point of the clip directly after the point + for (int k=0;ktimeline_in() > point) { + first_in_point_after_point = qMin(first_in_point_after_point, c->timeline_in()); + } + } + + // Ensure we found a valid in point before proceeding + if (first_in_point_after_point != LONG_MAX) { + + // Find the out point of the clip directly before the clip found above + for (int k=0;ktimeline_out() < first_in_point_after_point) { + out_point_just_before_first_in_point = qMax(out_point_just_before_first_in_point, c->timeline_out()); + } + } + + long gap_between_clips = first_in_point_after_point - out_point_just_before_first_in_point; + + if (gap_between_clips > (ripple_end - ripple_start)) { + ripple_end = ripple_start + gap_between_clips; + } + } + } + } + } + + if (ripple_start != ripple_end) { + ComboAction* ca = new ComboAction(); + Ripple(ca, ripple_start, ripple_start - ripple_end); + olive::undo_stack.push(ca); } } @@ -383,7 +883,7 @@ Effect *Sequence::GetSelectedGizmo() for (int i=0;iIsActiveAt(playhead) - && IsClipSelected(c, true)) { + && c->IsSelected()) { // This clip is selected and currently active - we'll use this for gizmos if (!c->effects.isEmpty()) { @@ -516,7 +1016,7 @@ void Sequence::AddSelectionsToClipboard(bool delete_originals) if (delete_originals) { ComboAction* ca = new ComboAction(); DeleteAreas(ca, selections, true); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } } @@ -537,6 +1037,24 @@ QVector Sequence::Selections() return selections; } +void Sequence::SetSelections(const QVector &selections) +{ + ClearSelections(); + + for (int i=0;iSelectArea(s.in(), s.out()); + } +} + +void Sequence::TidySelections() +{ + QVector selections = Selections(); + Selection::Tidy(selections); + SetSelections(selections); +} + ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long frame) { return SplitClip(ca, transitions, pre, frame, frame); @@ -558,7 +1076,7 @@ ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long f post->set_timeline_in(post_in); post->set_clip_in(pre->clip_in() + (post->timeline_in() - pre->timeline_in())); - pre->move(ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); + MoveClip(pre, ca, pre->timeline_in(), frame, pre->clip_in(), pre->track(), false); if (transitions) { @@ -616,5 +1134,28 @@ ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long f return nullptr; } -// static variable for the currently active sequence -SequencePtr olive::ActiveSequence = nullptr; +bool Sequence::SplitSelection(ComboAction *ca, QVector selections) +{ + QVector all_clips = GetAllClips(); + + for (int i=0;i points; + + for (int j=0;jtrack()) { + if (c->timeline_in() < s.in() && c->timeline_out() > s.in()) { + points.append(s.in()); + } + if (c->timeline_in() < s.out() && c->timeline_out() > s.out()) { + points.append(s.out()); + } + } + } + + + } +} diff --git a/timeline/sequence.h b/timeline/sequence.h index 9ec11fc38..3edf773fd 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -28,6 +28,7 @@ #include "marker.h" #include "selection.h" #include "tracklist.h" +#include "ghost.h" class Sequence : public QObject { Q_OBJECT @@ -63,17 +64,37 @@ public: void RefreshClipsUsingMedia(Media* m = nullptr); QVector SelectedClips(bool containing = true); - //QVector SelectedClipIndexes(); - void DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas); + void AddClipsFromGhosts(ComboAction *ca, const QVector &ghosts); + + void MoveClip(Clip* c, + ComboAction* ca, + long iin, + long iout, + long iclip_in, + Track *itrack, + bool verify_transitions = true, + bool relative = false); + + void EditToPoint(bool in, bool ripple); + + bool SnapPoint(long* l, double zoom, bool use_playhead, bool use_markers, bool use_workarea); + + void DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas = false, bool ripple = false); void DeleteInToOut(bool ripple); + void DeleteClipsUsingMedia(const QVector &media); - void Ripple(ComboAction *ca, long point, long length, const QVector& ignore = QVector()); + void Ripple(ComboAction *ca, long point, long length, const QVector &ignore = QVector()); void ChangeTrackHeightsRelatively(int diff); + void ToggleLinksOnSelected(); + + void Split(); bool SplitAllClipsAtPoint(ComboAction *ca, long point); - void SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); + bool SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); + + void RippleDeleteEmptySpace(Track *track, long point); Effect* GetSelectedGizmo(); @@ -85,6 +106,8 @@ public: void ClearSelections(); void AddSelectionsToClipboard(bool delete_originals); QVector Selections(); + void SetSelections(const QVector& selections); + void TidySelections(); long playhead; @@ -102,13 +125,9 @@ private: ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame); ClipPtr SplitClip(ComboAction* ca, bool transitions, Clip *clip, long frame, long post_in); + bool SplitSelection(ComboAction* ca, QVector selections); }; using SequencePtr = std::shared_ptr; -// static variable for the currently active sequence -namespace olive { - extern SequencePtr ActiveSequence; -} - #endif // SEQUENCE_H diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index b70e6c08c..3e776b78b 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -1,6 +1,14 @@ #include "timelinefunctions.h" +#include "global/math.h" +#include "global/config.h" +#include "global/timing.h" +#include "sequence.h" +// snapping +bool olive::timeline::snapping = true; +bool olive::timeline::snapped = false; +long olive::timeline::snap_point = 0; void olive::timeline::RelinkClips(QVector &pre_clips, QVector &post_clips) { @@ -28,3 +36,137 @@ void olive::timeline::RelinkClips(QVector &pre_clips, QVector & } } } + +bool olive::timeline::SnapToPoint(long point, long* l, double zoom) { + long limit = getFrameFromScreenPoint(zoom, 10); // FIXME magic number 10 used for on screen pixel threshold to snap to + if (*l > point-limit-1 && *l < point+limit+1) { + olive::timeline::snap_point = point; + *l = point; + olive::timeline::snapped = true; + return true; + } + return false; +} + + +QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, + long entry_point, + QVector &media_list) +{ + QVector ghosts; + + for (int i=0;iget_type()) { + case MEDIA_TYPE_FOOTAGE: + m = medium->to_footage(); + can_import = m->ready; + if (m->using_inout) { + double source_fr = 30; + if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) { + source_fr = m->video_tracks.at(0).video_frame_rate * m->speed; + } + default_clip_in = rescale_frame_number(m->in, source_fr, seq->frame_rate); + default_clip_out = rescale_frame_number(m->out, source_fr, seq->frame_rate); + } + break; + case MEDIA_TYPE_SEQUENCE: + s = medium->to_sequence().get(); + sequence_length = s->GetEndFrame(); + if (seq != nullptr) sequence_length = rescale_frame_number(sequence_length, s->frame_rate, seq->frame_rate); + can_import = (s != seq && sequence_length != 0); + if (s->using_workarea) { + default_clip_in = rescale_frame_number(s->workarea_in, s->frame_rate, seq->frame_rate); + default_clip_out = rescale_frame_number(s->workarea_out, s->frame_rate, seq->frame_rate); + } + break; + default: + can_import = false; + } + + if (can_import) { + Ghost g; + g.clip = nullptr; + g.trim_type = olive::timeline::TRIM_NONE; + g.old_clip_in = g.clip_in = default_clip_in; + g.media = medium; + g.in = entry_point; + g.transition = nullptr; + + switch (medium->get_type()) { + case MEDIA_TYPE_FOOTAGE: + // is video source a still image? + if (m->video_tracks.size() > 0 && m->video_tracks.at(0).infinite_length && m->audio_tracks.size() == 0) { + g.out = g.in + 100; + } else { + long length = m->get_length_in_frames(seq->frame_rate); + g.out = entry_point + length - default_clip_in; + if (m->using_inout) { + g.out -= (length - default_clip_out); + } + } + + if (import_data.type() == olive::timeline::kImportAudioOnly + || import_data.type() == olive::timeline::kImportBoth) { + for (int j=0;jaudio_tracks.size();j++) { + if (m->audio_tracks.at(j).enabled) { + g.track = seq->GetTrackList(Track::kTypeAudio)->First() + j; + g.media_stream = m->audio_tracks.at(j).file_index; + ghosts.append(g); + } + } + } + + if (import_data.type() == olive::timeline::kImportVideoOnly + || import_data.type() == olive::timeline::kImportBoth) { + for (int j=0;jvideo_tracks.size();j++) { + if (m->video_tracks.at(j).enabled) { + g.track = seq->GetTrackList(Track::kTypeVideo)->First() + j; + g.media_stream = m->video_tracks.at(j).file_index; + ghosts.append(g); + } + } + } + break; + case MEDIA_TYPE_SEQUENCE: + g.out = entry_point + sequence_length - default_clip_in; + + if (s->using_workarea) { + g.out -= (sequence_length - default_clip_out); + } + + if (import_data.type() == olive::timeline::kImportVideoOnly + || import_data.type() == olive::timeline::kImportBoth) { + g.track = seq->GetTrackList(Track::kTypeVideo)->First(); + ghosts.append(g); + } + + if (import_data.type() == olive::timeline::kImportAudioOnly + || import_data.type() == olive::timeline::kImportBoth) { + g.track = seq->GetTrackList(Track::kTypeAudio)->First(); + ghosts.append(g); + } + + break; + } + entry_point = g.out; + } + } + for (int i=0;i #include "timeline/clip.h" +#include "timeline/mediaimportdata.h" +#include "ghost.h" namespace olive { namespace timeline { @@ -17,12 +19,6 @@ enum CreateObjects { ADD_OBJ_AUDIO }; -enum TrimType { - TRIM_NONE, - TRIM_IN, - TRIM_OUT -}; - enum Alignment { kAlignmentTop, kAlignmentBottom, @@ -31,6 +27,15 @@ enum Alignment { void RelinkClips(QVector& pre_clips, QVector &post_clips); +bool SnapToPoint(long point, long* l, double zoom); + +QVector CreateGhostsFromMedia(Sequence *seq, long entry_point, QVector &media_list); + +// snapping +extern bool snapping; +extern bool snapped; +extern long snap_point; + } } diff --git a/timeline/timelinetools.cpp b/timeline/timelinetools.cpp new file mode 100644 index 000000000..fe3eea243 --- /dev/null +++ b/timeline/timelinetools.cpp @@ -0,0 +1,3 @@ +#include "timelinetools.h" + +olive::timeline::Tool olive::timeline::current_tool = olive::timeline::TIMELINE_TOOL_POINTER; diff --git a/ui/timelinetools.h b/timeline/timelinetools.h similarity index 89% rename from ui/timelinetools.h rename to timeline/timelinetools.h index b13f4764d..4fbe6f704 100644 --- a/ui/timelinetools.h +++ b/timeline/timelinetools.h @@ -21,7 +21,10 @@ #ifndef TIMELINETOOLS_H #define TIMELINETOOLS_H -enum TimelineTool { +namespace olive { +namespace timeline { + +enum Tool { TIMELINE_TOOL_POINTER, TIMELINE_TOOL_EDIT, TIMELINE_TOOL_RAZOR, @@ -36,4 +39,9 @@ enum TimelineTool { TIMELINE_TOOL_COUNT }; +extern Tool current_tool; + +} +} + #endif // TIMELINETOOLS_H diff --git a/timeline/track.cpp b/timeline/track.cpp index 48d174c15..4ae915c16 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -50,12 +50,8 @@ void Track::Save(QXmlStreamWriter &stream) for (int j=0;jload_id)); - c->Save(stream); - stream.writeEndElement(); // clip } stream.writeEndElement(); // track @@ -78,6 +74,10 @@ void Track::set_height(int h) void Track::AddClip(ClipPtr clip) { + if (clips_.contains(clip)) { + return; + } + clips_.append(clip); if (clip->track() != nullptr) { clip->track()->RemoveClip(clip.get()); @@ -85,6 +85,16 @@ void Track::AddClip(ClipPtr clip) clip->set_track(this); } +int Track::ClipCount() +{ + return clips_.size(); +} + +ClipPtr Track::GetClip(int i) +{ + return clips_.at(i); +} + void Track::RemoveClip(int i) { clips_.removeAt(i); @@ -145,6 +155,19 @@ ClipPtr Track::GetClipObjectFromRawPtr(Clip *c) Q_ASSERT(false); } +Clip *Track::GetClipFromPoint(long point) +{ + for (int i=0;itimeline_in() <= point && c->timeline_out() > point) { + return c; + } + } + + return nullptr; +} + int Track::Index() { return parent_->IndexOfTrack(this); @@ -209,6 +232,11 @@ bool Track::IsTransitionSelected(Transition *t) return false; } +void Track::SelectArea(long in, long out) +{ + selections_.append(Selection(in, out, this)); +} + void Track::SelectClip(Clip* c) { selections_.append(Selection(c->timeline_in(), c->timeline_out(), this)); diff --git a/timeline/track.h b/timeline/track.h index 168a69a91..b21b67c7d 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -61,6 +61,7 @@ public: QVector GetAllClips(); QVector GetSelectedClips(bool containing); ClipPtr GetClipObjectFromRawPtr(Clip* c); + Clip* GetClipFromPoint(long point); int Index(); @@ -71,6 +72,7 @@ public: void DeleteArea(ComboAction *ca, const Selection& s); void DeleteArea(ComboAction *ca, long in, long out); + void SelectArea(long in, long out); void SelectClip(Clip *c); void SelectAll(); void SelectAtPoint(long point); diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index 8215121a0..b1a76ac84 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -82,7 +82,17 @@ QVector TrackList::tracks() return tracks_; } +Track::Type TrackList::type() +{ + return type_; +} + Sequence *TrackList::GetParent() { return static_cast(parent()); } + +void TrackList::ResizeTrackArray(int i) +{ + tracks_.resize(i); +} diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 558114e30..5b7ca2dec 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -20,6 +20,8 @@ public: Track* TrackAt(int i); QVector tracks(); + Track::Type type(); + Sequence* GetParent(); private: diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp index f40042ba7..d69277587 100644 --- a/ui/audiomonitor.cpp +++ b/ui/audiomonitor.cpp @@ -68,7 +68,7 @@ void AudioMonitor::resizeEvent(QResizeEvent *e) { } void AudioMonitor::paintEvent(QPaintEvent *) { - if (olive::ActiveSequence != nullptr && values.size() > 0) { + if (values.size() > 0) { QPainter p(this); int channel_x = AUDIO_MONITOR_GAP; int channel_count = values.size(); diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index 18b6a1c5e..4092bc6c7 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -100,7 +100,7 @@ void FocusFilter::set_viewer_fullscreen() { } void FocusFilter::set_marker() { - if (olive::ActiveSequence != nullptr) { + if (Timeline::GetTopSequence() != nullptr) { QDockWidget* focused_panel = get_focused_panel(); if (focused_panel == panel_footage_viewer) { @@ -108,7 +108,7 @@ void FocusFilter::set_marker() { } else if (focused_panel == panel_sequence_viewer) { panel_sequence_viewer->set_marker(); } else { - panel_timeline->set_marker(); + panel_timeline.first()->set_marker(); } } } @@ -190,28 +190,31 @@ void FocusFilter::clear_inout() { } void FocusFilter::delete_function() { - if (panel_timeline->headers->hasFocus()) { - panel_timeline->headers->delete_markers(); + if (panel_timeline.first()->headers->hasFocus()) { + panel_timeline.first()->headers->delete_markers(); } else if (panel_footage_viewer->headers->hasFocus()) { panel_footage_viewer->headers->delete_markers(); } else if (panel_sequence_viewer->headers->hasFocus()) { panel_sequence_viewer->headers->delete_markers(); - } else if (panel_effect_controls->is_focused()) { + } else if (panel_effect_controls->focused()) { panel_effect_controls->DeleteSelectedEffects(); - } else if (panel_project->is_focused()) { - panel_project->delete_selected_media(); - } else if (panel_effect_controls->keyframe_focus()) { + } else if (panel_project.first()->focused()) { + panel_project.first()->delete_selected_media(); + } else if (panel_effect_controls->focused()) { panel_effect_controls->delete_selected_keyframes(); - } else if (panel_graph_editor->view_is_focused()) { + } else if (panel_graph_editor->focused()) { panel_graph_editor->delete_selected_keys(); } else { - panel_timeline->delete_selection(olive::ActiveSequence->selections, false); + Sequence* top_sequence = Timeline::GetTopSequence().get(); + ComboAction* ca = new ComboAction(); + top_sequence->DeleteAreas(ca, top_sequence->Selections(), true); + olive::undo_stack.push(ca); } } void FocusFilter::duplicate() { - if (panel_project->is_focused()) { - panel_project->duplicate_selected(); + if (panel_project.first()->focused()) { + panel_project.first()->duplicate_selected(); } } @@ -220,7 +223,7 @@ void FocusFilter::select_all() { if (focused_panel == panel_graph_editor) { panel_graph_editor->select_all(); } else { - panel_timeline->select_all(); + panel_timeline.first()->select_all(); } } @@ -233,7 +236,7 @@ void FocusFilter::zoom_in() { } else if (focused_panel == panel_sequence_viewer) { panel_sequence_viewer->set_zoom(true); } else { - panel_timeline->zoom_in(); + panel_timeline.first()->zoom_in(); } } @@ -246,28 +249,28 @@ void FocusFilter::zoom_out() { } else if (focused_panel == panel_sequence_viewer) { panel_sequence_viewer->set_zoom(false); } else { - panel_timeline->zoom_out(); + panel_timeline.first()->zoom_out(); } } void FocusFilter::cut() { - if (olive::ActiveSequence != nullptr) { + if (Timeline::GetTopSequence() != nullptr) { QDockWidget* focused_panel = get_focused_panel(); if (panel_effect_controls == focused_panel) { panel_effect_controls->copy(true); } else { - panel_timeline->copy(true); + panel_timeline.first()->copy(true); } } } void FocusFilter::copy() { - if (olive::ActiveSequence != nullptr) { + if (Timeline::GetTopSequence() != nullptr) { QDockWidget* focused_panel = get_focused_panel(); if (panel_effect_controls == focused_panel) { panel_effect_controls->copy(false); } else { - panel_timeline->copy(false); + panel_timeline.first()->copy(false); } } } diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 8f77b43ba..5885d7f76 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -470,7 +470,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { void GraphView::mouseMoveEvent(QMouseEvent *event) { if (!mousedown || !click_add) unsetCursor(); if (mousedown) { - if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (event->buttons() & Qt::MiddleButton || olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND) { set_scroll_x(x_scroll + start_x - event->pos().x()); set_scroll_y(y_scroll + event->pos().y() - start_y); start_x = event->pos().x(); @@ -701,7 +701,7 @@ void GraphView::mouseMoveEvent(QMouseEvent *event) { void GraphView::mouseReleaseEvent(QMouseEvent *) { if (click_add_proc) { - olive::UndoStack.push(new KeyframeAdd(click_add_field, click_add_key)); + olive::undo_stack.push(new KeyframeAdd(click_add_field, click_add_key)); } else if (moved_keys && selected_keys.size() > 0) { ComboAction* ca = new ComboAction(); switch (current_handle) { @@ -723,7 +723,7 @@ void GraphView::mouseReleaseEvent(QMouseEvent *) { } break; } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } moved_keys = false; mousedown = false; @@ -757,7 +757,7 @@ void GraphView::wheelEvent(QWheelEvent *event) { double new_x_zoom = x_zoom; double new_y_zoom = y_zoom; - if (ctrl != olive::CurrentConfig.scroll_zooms) { + if (ctrl != olive::config.scroll_zooms) { zooming = true; } @@ -849,7 +849,7 @@ void GraphView::set_selected_keyframe_type(int type) { EffectKeyframe& key = row->Field(selected_keys_fields.at(i))->keyframes[selected_keys.at(i)]; ca->append(new SetInt(&key.type, type)); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); } } diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index 409294548..e1926c394 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -42,6 +42,7 @@ #include "effects/keyframe.h" #include "ui/graphview.h" #include "ui/menu.h" +#include "global/math.h" KeyframeView::KeyframeView(QWidget *parent) : QWidget(parent), @@ -95,7 +96,7 @@ void KeyframeView::menu_set_key_type(QAction* a) { EffectField* f = selected_fields.at(i); ca->append(new SetInt(&f->keyframes[selected_keyframes.at(i)].type, a->data().toInt())); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); update_ui(false); } } @@ -172,8 +173,9 @@ void KeyframeView::paintEvent(QPaintEvent*) { panel_effect_controls->horizontalScrollBar->setMaximum(qMax(max_width - width(), 0)); header->set_visible_in(visible_in); - int playhead_x = getScreenPointFromFrame(panel_effect_controls->zoom, olive::ActiveSequence->playhead-visible_in) - x_scroll; - if (dragging && panel_timeline->snapped) { + int playhead_x = getScreenPointFromFrame(panel_effect_controls->zoom, + open_effects_.first()->GetEffect()->parent_clip->track()->sequence()->playhead-visible_in) - x_scroll; + if (dragging && olive::timeline::snapped) { p.setPen(Qt::white); } else { p.setPen(Qt::red); @@ -234,7 +236,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { rect_select_w = 0; rect_select_h = 0; - if (panel_timeline->tool == TIMELINE_TOOL_HAND || event->buttons() & Qt::MiddleButton) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND || event->buttons() & Qt::MiddleButton) { scroll_drag = true; return; } @@ -323,7 +325,7 @@ void KeyframeView::mousePressEvent(QMouseEvent *event) { } void KeyframeView::mouseMoveEvent(QMouseEvent* event) { - if (panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND) { setCursor(Qt::OpenHandCursor); } else { unsetCursor(); @@ -376,14 +378,14 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { long frame_diff = current_frame - drag_frame_start; // snapping to playhead - panel_timeline->snapped = false; - if (panel_timeline->snapping) { + olive::timeline::snapped = false; + if (olive::timeline::snapping) { for (int i=0;iGetParentRow()->GetParentEffect()->parent_clip; long key_time = old_key_vals.at(i) + frame_diff - c->clip_in() + c->timeline_in(); long key_eval = key_time; - if (panel_timeline->snap_to_point(olive::ActiveSequence->playhead, &key_eval)) { + if (olive::timeline::SnapToPoint(c->track()->sequence()->playhead, &key_eval, header->get_zoom())) { frame_diff += (key_eval - key_time); break; } @@ -398,10 +400,10 @@ void KeyframeView::mouseMoveEvent(QMouseEvent* event) { while (!keyframeIsSelected(field, j) && field->keyframes.at(j).time == eval_key + frame_diff) { if (last_frame_diff > frame_diff) { frame_diff++; - panel_timeline->snapped = false; + olive::timeline::snapped = false; } else { frame_diff--; - panel_timeline->snapped = false; + olive::timeline::snapped = false; } } } @@ -432,13 +434,13 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent*) { selected_fields.at(i)->keyframes.at(selected_keyframes.at(i)).time )); } - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } select_rect = false; dragging = false; mousedown = false; scroll_drag = false; - panel_timeline->snapped = false; + olive::timeline::snapped = false; update_ui(false); } diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index 77043a8b3..9aa622a28 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -94,7 +94,7 @@ QString LabelSlider::ValueToString() { } else { switch (display_type) { case FrameNumber: - return frame_to_timecode(long(v), olive::CurrentConfig.timecode_view, frame_rate); + return frame_to_timecode(long(v), olive::config.timecode_view, frame_rate); case Percent: return QString::number((v*100), 'f', decimal_places).append("%"); case Decibel: @@ -311,7 +311,7 @@ void LabelSlider::ShowDialog() if (s.isEmpty()) return; // parse string timecode to a frame number - d = timecode_to_frame(s, olive::CurrentConfig.timecode_view, frame_rate); + d = timecode_to_frame(s, olive::config.timecode_view, frame_rate); } else { diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 17e55c257..b0fb78497 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -152,27 +152,27 @@ void MainWindow::setup_layout(bool reset) { tabifyDockWidget(panel_footage_viewer, panel_effect_controls); panel_footage_viewer->raise(); addDockWidget(Qt::TopDockWidgetArea, panel_sequence_viewer); - addDockWidget(Qt::BottomDockWidgetArea, panel_timeline); + addDockWidget(Qt::BottomDockWidgetArea, panel_timeline.first()); panel_project.first()->show(); panel_effect_controls->show(); panel_footage_viewer->show(); panel_sequence_viewer->show(); - panel_timeline->show(); + panel_timeline.first()->show(); panel_graph_editor->hide(); panel_project.first()->setFloating(false); panel_effect_controls->setFloating(false); panel_footage_viewer->setFloating(false); panel_sequence_viewer->setFloating(false); - panel_timeline->setFloating(false); + panel_timeline.first()->setFloating(false); panel_graph_editor->setFloating(true); resizeDocks({panel_project.first(), panel_footage_viewer, panel_sequence_viewer}, {width()/3, width()/3, width()/3}, Qt::Horizontal); - resizeDocks({panel_project.first(), panel_timeline}, + resizeDocks({panel_project.first(), panel_timeline.first()}, {height()/2, height()/2}, Qt::Vertical); } @@ -251,7 +251,7 @@ MainWindow::MainWindow(QWidget *parent) : config_dir.mkpath("."); QString config_fn = config_dir.filePath("config.xml"); if (QFileInfo::exists(config_fn)) { - olive::CurrentConfig.load(config_fn); + olive::config.load(config_fn); } } @@ -260,9 +260,9 @@ MainWindow::MainWindow(QWidget *parent) : olive::icon::Initialize(); // Load OpenColorIO configuration if set - if (olive::CurrentConfig.enable_color_management && !olive::CurrentConfig.ocio_config_path.isEmpty()) { + if (olive::config.enable_color_management && !olive::config.ocio_config_path.isEmpty()) { try { - OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(olive::CurrentConfig.ocio_config_path.toUtf8())); + OCIO::SetCurrentConfig(OCIO::Config::CreateFromFile(olive::config.ocio_config_path.toUtf8())); } catch (OCIO::Exception& e) { QMessageBox::critical(this, tr("OpenColorIO Config Error"), @@ -397,8 +397,8 @@ void MainWindow::Restyle() qApp->setStyle(QStyleFactory::create("Fusion")); // Set up whether to load custom CSS or default CSS+palette - if (!olive::CurrentConfig.css_path.isEmpty() - && load_css_from_file(olive::CurrentConfig.css_path)) { + if (!olive::config.css_path.isEmpty() + && load_css_from_file(olive::config.css_path)) { qApp->setPalette(qApp->style()->standardPalette()); @@ -407,7 +407,7 @@ void MainWindow::Restyle() // set default palette QPalette palette; - if (olive::CurrentConfig.style == olive::styling::kOliveDefaultLight) { + if (olive::config.style == olive::styling::kOliveDefaultLight) { palette.setColor(QPalette::Window, QColor(208, 208, 208)); palette.setColor(QPalette::WindowText, Qt::black); @@ -480,14 +480,14 @@ void MainWindow::Restyle() } void MainWindow::editMenu_About_To_Be_Shown() { - undo_action->setEnabled(olive::UndoStack.canUndo()); - redo_action->setEnabled(olive::UndoStack.canRedo()); + undo_action->setEnabled(olive::undo_stack.canUndo()); + redo_action->setEnabled(olive::undo_stack.canRedo()); } void MainWindow::setup_menus() { QMenuBar* menuBar = new QMenuBar(this); - if (olive::CurrentConfig.use_native_menu_styling) { + if (olive::config.use_native_menu_styling) { OliveGlobal::SetNativeStyling(menuBar); } @@ -538,7 +538,7 @@ void MainWindow::setup_menus() { edit_menu->addSeparator(); select_all_action = MenuHelper::create_menu_action(edit_menu, "selectall", &olive::FocusFilter, SLOT(select_all()), QKeySequence("Ctrl+A")); - deselect_all_action = MenuHelper::create_menu_action(edit_menu, "deselectall", panel_timeline, SLOT(deselect()), QKeySequence("Ctrl+Shift+A")); + deselect_all_action = MenuHelper::create_menu_action(edit_menu, "deselectall", panel_timeline.first(), SLOT(deselect()), QKeySequence("Ctrl+Shift+A")); edit_menu->addSeparator(); @@ -546,16 +546,16 @@ void MainWindow::setup_menus() { edit_menu->addSeparator(); - ripple_to_in_point_ = MenuHelper::create_menu_action(edit_menu, "rippletoin", panel_timeline, SLOT(ripple_to_in_point()), QKeySequence("Q")); - ripple_to_out_point_ = MenuHelper::create_menu_action(edit_menu, "rippletoout", panel_timeline, SLOT(ripple_to_out_point()), QKeySequence("W")); - edit_to_in_point_ = MenuHelper::create_menu_action(edit_menu, "edittoin", panel_timeline, SLOT(edit_to_in_point()), QKeySequence("Ctrl+Alt+Q")); - edit_to_out_point_ = MenuHelper::create_menu_action(edit_menu, "edittoout", panel_timeline, SLOT(edit_to_out_point()), QKeySequence("Ctrl+Alt+W")); + ripple_to_in_point_ = MenuHelper::create_menu_action(edit_menu, "rippletoin", panel_timeline.first(), SLOT(ripple_to_in_point()), QKeySequence("Q")); + ripple_to_out_point_ = MenuHelper::create_menu_action(edit_menu, "rippletoout", panel_timeline.first(), SLOT(ripple_to_out_point()), QKeySequence("W")); + edit_to_in_point_ = MenuHelper::create_menu_action(edit_menu, "edittoin", panel_timeline.first(), SLOT(edit_to_in_point()), QKeySequence("Ctrl+Alt+Q")); + edit_to_out_point_ = MenuHelper::create_menu_action(edit_menu, "edittoout", panel_timeline.first(), SLOT(edit_to_out_point()), QKeySequence("Ctrl+Alt+W")); edit_menu->addSeparator(); olive::MenuHelper.make_inout_menu(edit_menu); - delete_inout_point_ = MenuHelper::create_menu_action(edit_menu, "deleteinout", panel_timeline, SLOT(delete_inout()), QKeySequence(";")); - ripple_delete_inout_point_ = MenuHelper::create_menu_action(edit_menu, "rippledeleteinout", panel_timeline, SLOT(ripple_delete_inout()), QKeySequence("'")); + delete_inout_point_ = MenuHelper::create_menu_action(edit_menu, "deleteinout", panel_timeline.first(), SLOT(delete_inout()), QKeySequence(";")); + ripple_delete_inout_point_ = MenuHelper::create_menu_action(edit_menu, "rippledeleteinout", panel_timeline.first(), SLOT(ripple_delete_inout()), QKeySequence("'")); edit_menu->addSeparator(); @@ -567,21 +567,17 @@ void MainWindow::setup_menus() { zoom_in_ = MenuHelper::create_menu_action(view_menu, "zoomin", &olive::FocusFilter, SLOT(zoom_in()), QKeySequence("=")); zoom_out_ = MenuHelper::create_menu_action(view_menu, "zoomout", &olive::FocusFilter, SLOT(zoom_out()), QKeySequence("-")); - increase_track_height_ = MenuHelper::create_menu_action(view_menu, "vzoomin", panel_timeline, SLOT(IncreaseTrackHeight()), QKeySequence("Ctrl+=")); - decrease_track_height_ = MenuHelper::create_menu_action(view_menu, "vzoomout", panel_timeline, SLOT(DecreaseTrackHeight()), QKeySequence("Ctrl+-")); + increase_track_height_ = MenuHelper::create_menu_action(view_menu, "vzoomin", panel_timeline.first(), SLOT(IncreaseTrackHeight()), QKeySequence("Ctrl+=")); + decrease_track_height_ = MenuHelper::create_menu_action(view_menu, "vzoomout", panel_timeline.first(), SLOT(DecreaseTrackHeight()), QKeySequence("Ctrl+-")); - show_all = MenuHelper::create_menu_action(view_menu, "showall", panel_timeline, SLOT(toggle_show_all()), QKeySequence("\\")); + show_all = MenuHelper::create_menu_action(view_menu, "showall", panel_timeline.first(), SLOT(toggle_show_all()), QKeySequence("\\")); show_all->setCheckable(true); view_menu->addSeparator(); - track_lines = MenuHelper::create_menu_action(view_menu, "tracklines", &olive::MenuHelper, SLOT(toggle_bool_action())); - track_lines->setCheckable(true); - track_lines->setData(reinterpret_cast(&olive::CurrentConfig.show_track_lines)); - rectified_waveforms = MenuHelper::create_menu_action(view_menu, "rectifiedwaveforms", &olive::MenuHelper, SLOT(toggle_bool_action())); rectified_waveforms->setCheckable(true); - rectified_waveforms->setData(reinterpret_cast(&olive::CurrentConfig.rectified_waveforms)); + rectified_waveforms->setData(reinterpret_cast(&olive::config.rectified_waveforms)); view_menu->addSeparator(); @@ -658,8 +654,8 @@ void MainWindow::setup_menus() { playback_menu->addSeparator(); - go_to_prev_cut_ = MenuHelper::create_menu_action(playback_menu, "prevcut", panel_timeline, SLOT(previous_cut()), QKeySequence("Up")); - go_to_next_cut_ = MenuHelper::create_menu_action(playback_menu, "nextcut", panel_timeline, SLOT(next_cut()), QKeySequence("Down")); + go_to_prev_cut_ = MenuHelper::create_menu_action(playback_menu, "prevcut", panel_project.first(), SLOT(previous_cut()), QKeySequence("Up")); + go_to_next_cut_ = MenuHelper::create_menu_action(playback_menu, "nextcut", panel_project.first(), SLOT(next_cut()), QKeySequence("Down")); playback_menu->addSeparator(); @@ -676,7 +672,7 @@ void MainWindow::setup_menus() { loop_action_ = MenuHelper::create_menu_action(playback_menu, "loop", &olive::MenuHelper, SLOT(toggle_bool_action())); loop_action_->setCheckable(true); - loop_action_->setData(reinterpret_cast(&olive::CurrentConfig.loop)); + loop_action_->setData(reinterpret_cast(&olive::config.loop)); // INITIALIZE WINDOW MENU @@ -692,7 +688,7 @@ void MainWindow::setup_menus() { window_timeline_action = MenuHelper::create_menu_action(window_menu, "paneltimeline", this, SLOT(toggle_panel_visibility())); window_timeline_action->setCheckable(true); - window_timeline_action->setData(reinterpret_cast(panel_timeline)); + window_timeline_action->setData(reinterpret_cast(panel_timeline.first())); window_graph_editor_action = MenuHelper::create_menu_action(window_menu, "panelgrapheditor", this, SLOT(toggle_panel_visibility())); window_graph_editor_action->setCheckable(true); @@ -726,49 +722,49 @@ void MainWindow::setup_menus() { pointer_tool_action = MenuHelper::create_menu_action(tools_menu, "pointertool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("V")); pointer_tool_action->setCheckable(true); - pointer_tool_action->setData(reinterpret_cast(panel_timeline->toolArrowButton)); + pointer_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolArrowButton)); tools_group->addAction(pointer_tool_action); edit_tool_action = MenuHelper::create_menu_action(tools_menu, "edittool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("X")); edit_tool_action->setCheckable(true); - edit_tool_action->setData(reinterpret_cast(panel_timeline->toolEditButton)); + edit_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolEditButton)); tools_group->addAction(edit_tool_action); ripple_tool_action = MenuHelper::create_menu_action(tools_menu, "rippletool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("B")); ripple_tool_action->setCheckable(true); - ripple_tool_action->setData(reinterpret_cast(panel_timeline->toolRippleButton)); + ripple_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolRippleButton)); tools_group->addAction(ripple_tool_action); razor_tool_action = MenuHelper::create_menu_action(tools_menu, "razortool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("C")); razor_tool_action->setCheckable(true); - razor_tool_action->setData(reinterpret_cast(panel_timeline->toolRazorButton)); + razor_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolRazorButton)); tools_group->addAction(razor_tool_action); slip_tool_action = MenuHelper::create_menu_action(tools_menu, "sliptool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("Y")); slip_tool_action->setCheckable(true); - slip_tool_action->setData(reinterpret_cast(panel_timeline->toolSlipButton)); + slip_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolSlipButton)); tools_group->addAction(slip_tool_action); slide_tool_action = MenuHelper::create_menu_action(tools_menu, "slidetool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("U")); slide_tool_action->setCheckable(true); - slide_tool_action->setData(reinterpret_cast(panel_timeline->toolSlideButton)); + slide_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolSlideButton)); tools_group->addAction(slide_tool_action); hand_tool_action = MenuHelper::create_menu_action(tools_menu, "handtool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("H")); hand_tool_action->setCheckable(true); - hand_tool_action->setData(reinterpret_cast(panel_timeline->toolHandButton)); + hand_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolHandButton)); tools_group->addAction(hand_tool_action); transition_tool_action = MenuHelper::create_menu_action(tools_menu, "transitiontool", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("T")); transition_tool_action->setCheckable(true); - transition_tool_action->setData(reinterpret_cast(panel_timeline->toolTransitionButton)); + transition_tool_action->setData(reinterpret_cast(panel_timeline.first()->toolTransitionButton)); tools_group->addAction(transition_tool_action); tools_menu->addSeparator(); snap_toggle = MenuHelper::create_menu_action(tools_menu, "snapping", &olive::MenuHelper, SLOT(menu_click_button()), QKeySequence("S")); snap_toggle->setCheckable(true); - snap_toggle->setData(reinterpret_cast(panel_timeline->snappingButton)); + snap_toggle->setData(reinterpret_cast(panel_timeline.first()->snappingButton)); tools_menu->addSeparator(); @@ -850,7 +846,6 @@ void MainWindow::Retranslate() increase_track_height_->setText(tr("Increase Track Height")); decrease_track_height_->setText(tr("Decrease Track Height")); show_all->setText(tr("Toggle Show All")); - track_lines->setText(tr("Track Lines")); rectified_waveforms->setText(tr("Rectified Waveforms")); frames_action->setText(tr("Frames")); drop_frame_action->setText(tr("Drop Frame")); @@ -955,14 +950,10 @@ void MainWindow::closeEvent(QCloseEvent *e) { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - olive::Global->set_sequence(nullptr); - panel_footage_viewer->viewer_widget()->close_window(); panel_sequence_viewer->viewer_widget()->close_window(); - panel_footage_viewer->set_main_sequence(); - - olive::UndoStack.clear(); + olive::undo_stack.clear(); QString data_dir = get_data_path(); QString config_path = get_config_path(); @@ -980,7 +971,7 @@ void MainWindow::closeEvent(QCloseEvent *e) { QString config_fn = config_dir.filePath("config.xml"); // save settings - olive::CurrentConfig.save(config_fn); + olive::config.save(config_fn); // save panel layout QFile panel_config(get_config_dir().filePath("layout")); @@ -1123,32 +1114,30 @@ void MainWindow::playbackMenu_About_To_Be_Shown() { } void MainWindow::viewMenu_About_To_Be_Shown() { - olive::MenuHelper.set_bool_action_checked(track_lines); - olive::MenuHelper.set_bool_action_checked(rectified_waveforms); - olive::MenuHelper.set_int_action_checked(frames_action, olive::CurrentConfig.timecode_view); - olive::MenuHelper.set_int_action_checked(drop_frame_action, olive::CurrentConfig.timecode_view); - olive::MenuHelper.set_int_action_checked(nondrop_frame_action, olive::CurrentConfig.timecode_view); - olive::MenuHelper.set_int_action_checked(milliseconds_action, olive::CurrentConfig.timecode_view); + olive::MenuHelper.set_int_action_checked(frames_action, olive::config.timecode_view); + olive::MenuHelper.set_int_action_checked(drop_frame_action, olive::config.timecode_view); + olive::MenuHelper.set_int_action_checked(nondrop_frame_action, olive::config.timecode_view); + olive::MenuHelper.set_int_action_checked(milliseconds_action, olive::config.timecode_view); - title_safe_off->setChecked(!olive::CurrentConfig.show_title_safe_area); - title_safe_default->setChecked(olive::CurrentConfig.show_title_safe_area - && !olive::CurrentConfig.use_custom_title_safe_ratio); - title_safe_43->setChecked(olive::CurrentConfig.show_title_safe_area - && olive::CurrentConfig.use_custom_title_safe_ratio - && qFuzzyCompare(olive::CurrentConfig.custom_title_safe_ratio, title_safe_43->data().toDouble())); - title_safe_169->setChecked(olive::CurrentConfig.show_title_safe_area - && olive::CurrentConfig.use_custom_title_safe_ratio - && qFuzzyCompare(olive::CurrentConfig.custom_title_safe_ratio, title_safe_169->data().toDouble())); - title_safe_custom->setChecked(olive::CurrentConfig.show_title_safe_area - && olive::CurrentConfig.use_custom_title_safe_ratio + title_safe_off->setChecked(!olive::config.show_title_safe_area); + title_safe_default->setChecked(olive::config.show_title_safe_area + && !olive::config.use_custom_title_safe_ratio); + title_safe_43->setChecked(olive::config.show_title_safe_area + && olive::config.use_custom_title_safe_ratio + && qFuzzyCompare(olive::config.custom_title_safe_ratio, title_safe_43->data().toDouble())); + title_safe_169->setChecked(olive::config.show_title_safe_area + && olive::config.use_custom_title_safe_ratio + && qFuzzyCompare(olive::config.custom_title_safe_ratio, title_safe_169->data().toDouble())); + title_safe_custom->setChecked(olive::config.show_title_safe_area + && olive::config.use_custom_title_safe_ratio && !title_safe_43->isChecked() && !title_safe_169->isChecked()); full_screen->setChecked(windowState() == Qt::WindowFullScreen); - show_all->setChecked(panel_timeline->showing_all); + show_all->setChecked(panel_timeline.first()->showing_all); } void MainWindow::toolMenu_About_To_Be_Shown() { @@ -1162,9 +1151,9 @@ void MainWindow::toolMenu_About_To_Be_Shown() { olive::MenuHelper.set_button_action_checked(transition_tool_action); olive::MenuHelper.set_button_action_checked(snap_toggle); - olive::MenuHelper.set_int_action_checked(no_autoscroll, olive::CurrentConfig.autoscroll); - olive::MenuHelper.set_int_action_checked(page_autoscroll, olive::CurrentConfig.autoscroll); - olive::MenuHelper.set_int_action_checked(smooth_autoscroll, olive::CurrentConfig.autoscroll); + olive::MenuHelper.set_int_action_checked(no_autoscroll, olive::config.autoscroll); + olive::MenuHelper.set_int_action_checked(page_autoscroll, olive::config.autoscroll); + olive::MenuHelper.set_int_action_checked(smooth_autoscroll, olive::config.autoscroll); } void MainWindow::toggle_panel_visibility() { diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 7ffd76a32..264e83868 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -263,7 +263,6 @@ private: QAction* zoom_out_; QAction* increase_track_height_; QAction* decrease_track_height_; - QAction* track_lines; QAction* frames_action; QAction* drop_frame_action; QAction* nondrop_frame_action; diff --git a/ui/menu.cpp b/ui/menu.cpp index a5c877a9a..a77652794 100644 --- a/ui/menu.cpp +++ b/ui/menu.cpp @@ -26,7 +26,7 @@ Menu::Menu(QWidget *parent) : QMenu(parent) { - if (olive::CurrentConfig.use_native_menu_styling) { + if (olive::config.use_native_menu_styling) { OliveGlobal::SetNativeStyling(this); } } @@ -34,7 +34,7 @@ Menu::Menu(QWidget *parent) : Menu::Menu(const QString &title, QWidget *parent) : QMenu(title, parent) { - if (olive::CurrentConfig.use_native_menu_styling) { + if (olive::config.use_native_menu_styling) { OliveGlobal::SetNativeStyling(this); } } diff --git a/ui/menuhelper.cpp b/ui/menuhelper.cpp index 0e0f448f0..e4a38c280 100644 --- a/ui/menuhelper.cpp +++ b/ui/menuhelper.cpp @@ -26,7 +26,7 @@ #include #include "global/config.h" -#include "project/clipboard.h" +#include "global/clipboard.h" #include "ui/mainwindow.h" #include "global/global.h" #include "panels/panels.h" @@ -39,10 +39,10 @@ void MenuHelper::InitializeSharedMenus() new_project_ = create_menu_action(nullptr, "newproj", olive::Global.get(), SLOT(new_project()), QKeySequence("Ctrl+N")); new_project_->setParent(this); - new_sequence_ = create_menu_action(nullptr, "newseq", panel_project, SLOT(new_sequence()), QKeySequence("Ctrl+Shift+N")); + new_sequence_ = create_menu_action(nullptr, "newseq", olive::Global.get(), SLOT(open_new_sequence_dialog()), QKeySequence("Ctrl+Shift+N")); new_sequence_->setParent(this); - new_folder_ = create_menu_action(nullptr, "newfolder", panel_project, SLOT(new_folder())); + new_folder_ = create_menu_action(nullptr, "newfolder", panel_project.first(), SLOT(new_folder())); new_folder_->setParent(this); set_in_point_ = create_menu_action(nullptr, "setinpoint", &olive::FocusFilter, SLOT(set_in_point()), QKeySequence("I")); @@ -60,16 +60,16 @@ void MenuHelper::InitializeSharedMenus() clear_inout_point = create_menu_action(nullptr, "clearinout", &olive::FocusFilter, SLOT(clear_inout()), QKeySequence("G")); clear_inout_point->setParent(this); - add_default_transition_ = create_menu_action(nullptr, "deftransition", panel_timeline, SLOT(add_transition()), QKeySequence("Ctrl+Shift+D")); + add_default_transition_ = create_menu_action(nullptr, "deftransition", panel_timeline.first(), SLOT(add_transition()), QKeySequence("Ctrl+Shift+D")); add_default_transition_->setParent(this); - link_unlink_ = create_menu_action(nullptr, "linkunlink", panel_timeline, SLOT(toggle_links()), QKeySequence("Ctrl+L")); + link_unlink_ = create_menu_action(nullptr, "linkunlink", panel_timeline.first(), SLOT(toggle_links()), QKeySequence("Ctrl+L")); link_unlink_->setParent(this); - enable_disable_ = create_menu_action(nullptr, "enabledisable", panel_timeline, SLOT(toggle_enable_on_selected_clips()), QKeySequence("Shift+E")); + enable_disable_ = create_menu_action(nullptr, "enabledisable", panel_timeline.first(), SLOT(toggle_enable_on_selected_clips()), QKeySequence("Shift+E")); enable_disable_->setParent(this); - nest_ = create_menu_action(nullptr, "nest", panel_timeline, SLOT(nest())); + nest_ = create_menu_action(nullptr, "nest", panel_timeline.first(), SLOT(nest())); nest_->setParent(this); cut_ = create_menu_action(nullptr, "cut", &olive::FocusFilter, SLOT(cut()), QKeySequence("Ctrl+X")); @@ -90,10 +90,10 @@ void MenuHelper::InitializeSharedMenus() delete_ = create_menu_action(nullptr, "delete", &olive::FocusFilter, SLOT(delete_function()), QKeySequence("Del")); delete_->setParent(this); - ripple_delete_ = create_menu_action(nullptr, "rippledelete", panel_timeline, SLOT(ripple_delete()), QKeySequence("Shift+Del")); + ripple_delete_ = create_menu_action(nullptr, "rippledelete", panel_timeline.first(), SLOT(ripple_delete()), QKeySequence("Shift+Del")); ripple_delete_->setParent(this); - split_ = create_menu_action(nullptr, "split", panel_timeline, SLOT(split_at_playhead()), QKeySequence("Ctrl+K")); + split_ = create_menu_action(nullptr, "split", panel_timeline.first(), SLOT(split_at_playhead()), QKeySequence("Ctrl+K")); split_->setParent(this); Retranslate(); @@ -193,23 +193,23 @@ void MenuHelper::set_titlesafe_from_menu() { if (qIsNaN(tsa)) { // disable title safe area - olive::CurrentConfig.show_title_safe_area = false; + olive::config.show_title_safe_area = false; } else { // using title safe area - olive::CurrentConfig.show_title_safe_area = true; + olive::config.show_title_safe_area = true; // are we using the default area aspect ratio, or a specific one if (qIsNull(tsa)) { // default title safe area - olive::CurrentConfig.use_custom_title_safe_ratio = false; + olive::config.use_custom_title_safe_ratio = false; } else { // using a specific aspect ratio - olive::CurrentConfig.use_custom_title_safe_ratio = true; + olive::config.use_custom_title_safe_ratio = true; if (tsa < 0.0) { @@ -229,13 +229,13 @@ void MenuHelper::set_titlesafe_from_menu() { if (!input.isEmpty()) { QStringList inputList = input.split(':'); - olive::CurrentConfig.custom_title_safe_ratio = inputList.at(0).toDouble()/inputList.at(1).toDouble(); + olive::config.custom_title_safe_ratio = inputList.at(0).toDouble()/inputList.at(1).toDouble(); } } else { // specified tsa is a specific custom aspect ratio - olive::CurrentConfig.custom_title_safe_ratio = tsa; + olive::config.custom_title_safe_ratio = tsa; } } @@ -247,7 +247,7 @@ void MenuHelper::set_titlesafe_from_menu() { void MenuHelper::set_autoscroll() { QAction* action = static_cast(sender()); - olive::CurrentConfig.autoscroll = action->data().toInt(); + olive::config.autoscroll = action->data().toInt(); } void MenuHelper::menu_click_button() { @@ -256,7 +256,7 @@ void MenuHelper::menu_click_button() { void MenuHelper::set_timecode_view() { QAction* action = static_cast(sender()); - olive::CurrentConfig.timecode_view = action->data().toInt(); + olive::config.timecode_view = action->data().toInt(); update_ui(false); } @@ -267,8 +267,8 @@ void MenuHelper::open_recent_from_menu() { void MenuHelper::create_effect_paste_action(QMenu *menu) { - QAction* paste_action = menu->addAction(tr("&Paste"), panel_timeline, SLOT(paste(bool))); - paste_action->setEnabled(clipboard.size() > 0 && clipboard_type == CLIPBOARD_TYPE_EFFECT); + QAction* paste_action = menu->addAction(tr("&Paste"), olive::Global.get(), SLOT(paste(bool))); + paste_action->setEnabled(olive::clipboard.Count() > 0 && olive::clipboard.type() == Clipboard::CLIPBOARD_TYPE_EFFECT); } Menu* MenuHelper::create_submenu(QMenuBar* parent, diff --git a/ui/scrollarea.cpp b/ui/scrollarea.cpp deleted file mode 100644 index 579f3fb5f..000000000 --- a/ui/scrollarea.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*** - - 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 "scrollarea.h" - -#include -#include - -#include "global/config.h" -#include "panels/panels.h" -#include "panels/timeline.h" - -ScrollArea::ScrollArea(QWidget* parent) : QScrollArea(parent) {} - -void ScrollArea::wheelEvent(QWheelEvent *e) { - if (olive::CurrentConfig.scroll_zooms) { - e->ignore(); - - if (e->angleDelta().y() > 0) { - panel_timeline->zoom_in(); - } else if (e->angleDelta().y() < 0) { - panel_timeline->zoom_out(); - } - } else { - QScrollArea::wheelEvent(e); - } -} diff --git a/ui/scrollarea.h b/ui/scrollarea.h deleted file mode 100644 index 1c2ee5082..000000000 --- a/ui/scrollarea.h +++ /dev/null @@ -1,33 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef SCROLLAREA_H -#define SCROLLAREA_H - -#include - -class ScrollArea : public QScrollArea -{ -public: - ScrollArea(QWidget* parent = 0); - void wheelEvent(QWheelEvent *); -}; - -#endif // SCROLLAREA_H diff --git a/ui/styling.cpp b/ui/styling.cpp index bf1ae3d2b..5d8dfd08f 100644 --- a/ui/styling.cpp +++ b/ui/styling.cpp @@ -24,7 +24,7 @@ bool olive::styling::UseDarkIcons() { - return olive::CurrentConfig.style == kOliveDefaultLight || olive::CurrentConfig.style == kNativeDarkIcons; + return olive::config.style == kOliveDefaultLight || olive::config.style == kNativeDarkIcons; } QColor olive::styling::GetIconColor() @@ -40,5 +40,5 @@ QColor olive::styling::GetIconColor() bool olive::styling::UseNativeUI() { - return olive::CurrentConfig.style == kNativeLightIcons || olive::CurrentConfig.style == kNativeDarkIcons; + return olive::config.style == kNativeLightIcons || olive::config.style == kNativeDarkIcons; } diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 7f290b425..69f5c3bd1 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -1,6 +1,7 @@ #include "timelinearea.h" -TimelineArea::TimelineArea() : +TimelineArea::TimelineArea(Timeline* timeline) : + timeline_(timeline), track_list_(nullptr), alignment_(olive::timeline::kAlignmentTop) { @@ -8,21 +9,24 @@ TimelineArea::TimelineArea() : // LABELS QWidget* label_container = new QWidget(); - QVBoxLayout* label_container_layout = new QVBoxLayout(label_container); + label_container_layout_ = new QVBoxLayout(label_container); layout->addWidget(label_container); // VIEW - view_ = new TimelineView(); + view_ = new TimelineView(timeline_); layout->addWidget(view_); // SCROLLBAR QScrollBar* scrollbar = new QScrollBar(Qt::Vertical); layout->addWidget(scrollbar); + + view_->scrollBar = scrollbar; } void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) { alignment_ = alignment; + view_->SetAlignment(alignment); } void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) @@ -37,7 +41,7 @@ void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) } - + view_->SetTrackList(track_list_); } void TimelineArea::RefreshLabels() @@ -48,7 +52,7 @@ void TimelineArea::RefreshLabels() labels_.resize(track_list_->TrackCount()); for (int i=0;iTrackAt(i)); + labels_[i]->SetTrack(track_list_->TrackAt(i)); } } diff --git a/ui/timelinearea.h b/ui/timelinearea.h index dd8fe0ccc..08f90304c 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -12,17 +12,19 @@ class TimelineArea : public QWidget { Q_OBJECT public: - TimelineArea(); + TimelineArea(Timeline *timeline); void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(Sequence* sequence, Track::Type track_list); public slots: void RefreshLabels(); private: + Timeline* timeline_; TrackList* track_list_; TimelineView* view_; - QVector labels_; + QVector labels_; olive::timeline::Alignment alignment_; + QVBoxLayout* label_container_layout_; }; #endif // TIMELINEAREA_H diff --git a/ui/timelineheader.cpp b/ui/timelineheader.cpp index 59f6d3919..996f6e6a5 100644 --- a/ui/timelineheader.cpp +++ b/ui/timelineheader.cpp @@ -28,6 +28,7 @@ #include "mainwindow.h" #include "panels/panels.h" +#include "global/math.h" #include "timeline/sequence.h" #include "undo/undo.h" #include "project/media.h" @@ -93,7 +94,7 @@ int TimelineHeader::getHeaderScreenPointFromFrame(long frame) { void TimelineHeader::set_playhead(int mouse_x) { long frame = getHeaderFrameFromScreenPoint(mouse_x); - if (snapping) panel_timeline->snap_to_timeline(&frame, false, true, true); + if (snapping) viewer->seq->SnapPoint(&frame, zoom, false, true, true); if (frame != viewer->seq->playhead) { viewer->seek(frame); } @@ -113,10 +114,10 @@ void TimelineHeader::set_in_point(long new_in) { if (new_out == new_in) { new_in--; } else if (new_out < new_in) { - new_out = viewer->seq->getEndFrame(); + new_out = viewer->seq->GetEndFrame(); } - olive::UndoStack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, new_in, new_out)); + olive::undo_stack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, new_in, new_out)); update_parents(); } @@ -128,7 +129,7 @@ void TimelineHeader::set_out_point(long new_out) { new_in = 0; } - olive::UndoStack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, new_in, new_out)); + olive::undo_stack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, new_in, new_out)); update_parents(); } @@ -149,7 +150,7 @@ void TimelineHeader::show_text(bool enable) { void TimelineHeader::mousePressEvent(QMouseEvent* event) { if (viewer->seq != nullptr && event->buttons() & Qt::LeftButton) { if (resizing_workarea) { - sequence_end = viewer->seq->getEndFrame(); + sequence_end = viewer->seq->GetEndFrame(); } else { /*int QPoint start(in_x, height()+2); @@ -215,7 +216,7 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) { if (dragging) { if (resizing_workarea) { long frame = getHeaderFrameFromScreenPoint(event->pos().x()); - if (snapping) panel_timeline->snap_to_timeline(&frame, true, true, false); + if (snapping) viewer->seq->SnapPoint(&frame, zoom, true, true, false); if (resizing_workarea_in) { temp_workarea_in = qMax(qMin(temp_workarea_out-1, frame), 0L); @@ -230,7 +231,7 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) { // snap markers for (int i=0;isnap_to_timeline(&fm, true, false, true)) { + if (snapping && viewer->seq->SnapPoint(&fm, zoom, true, false, true)) { frame_movement = fm - selected_marker_original_times.at(i); break; } @@ -281,7 +282,7 @@ void TimelineHeader::mouseReleaseEvent(QMouseEvent*) { if (viewer->seq != nullptr) { dragging = false; if (resizing_workarea) { - olive::UndoStack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, temp_workarea_in, temp_workarea_out)); + olive::undo_stack.push(new SetTimelineInOutCommand(viewer->seq.get(), true, temp_workarea_in, temp_workarea_out)); } else if (dragging_markers && selected_markers.size() > 0) { bool moved = false; ComboAction* ca = new ComboAction(); @@ -293,7 +294,7 @@ void TimelineHeader::mouseReleaseEvent(QMouseEvent*) { } } if (moved) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } @@ -302,7 +303,7 @@ void TimelineHeader::mouseReleaseEvent(QMouseEvent*) { resizing_workarea = false; dragging = false; dragging_markers = false; - panel_timeline->snapped = false; + olive::timeline::snapped = false; update_parents(); } } @@ -331,7 +332,7 @@ void TimelineHeader::delete_markers() { // Send command to delete selected markers DeleteMarkerAction* dma = new DeleteMarkerAction(viewer->marker_ref); dma->markers.append(selected_markers); - olive::UndoStack.push(dma); + olive::undo_stack.push(dma); // remove any indices for the selected markers that no longer exist for (int i=0;i lastTextBoundary) { - timecode = frame_to_timecode(frame + in_visible, olive::CurrentConfig.timecode_view, viewer->seq->frame_rate); + timecode = frame_to_timecode(frame + in_visible, olive::config.timecode_view, viewer->seq->frame_rate); fullTextWidth = fm.width(timecode); textWidth = fullTextWidth>>1; text_x = lineX; // centers the text to that point on the timeline, LEFT aligns it if not - if (olive::CurrentConfig.center_timeline_timecodes) { + if (olive::config.center_timeline_timecodes) { text_x -= textWidth; } else { text_x += TEXT_PADDING_FROM_LINE; @@ -415,7 +416,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) { // draw line markers p.setPen(Qt::gray); - p.drawLine(lineX, (!olive::CurrentConfig.center_timeline_timecodes && draw_text) ? 0 : yoff, lineX, height()); + p.drawLine(lineX, (!olive::config.center_timeline_timecodes && draw_text) ? 0 : yoff, lineX, height()); // draw sub-line markers for (int j=1;jsetCheckable(true); - center_timecodes->setChecked(olive::CurrentConfig.center_timeline_timecodes); - center_timecodes->setData(reinterpret_cast(&olive::CurrentConfig.center_timeline_timecodes)); + center_timecodes->setChecked(olive::config.center_timeline_timecodes); + center_timecodes->setData(reinterpret_cast(&olive::config.center_timeline_timecodes)); menu.exec(mapToGlobal(pos)); } diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h index 448f5d955..6ca1d983f 100644 --- a/ui/timelinelabel.h +++ b/ui/timelinelabel.h @@ -2,6 +2,7 @@ #define TIMELINELABEL_H #include +#include #include "timeline/track.h" @@ -20,4 +21,6 @@ private: Track* track_; }; +using TimelineLabelPtr = std::shared_ptr; + #endif // TIMELINELABEL_H diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index f9e8cd484..bdf705443 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -59,17 +59,20 @@ #include "effects/effect.h" #include "effects/internal/solideffect.h" #include "timeline/track.h" +#include "global/math.h" +#include "project/projectfunctions.h" #define MAX_TEXT_WIDTH 20 #define TRANSITION_BETWEEN_RANGE 40 -TimelineView::TimelineView(QWidget *parent) : QWidget(parent) { - selection_command = nullptr; - self_created_sequence = nullptr; - scroll = 0; - - bottom_align = false; - track_resizing = false; +TimelineView::TimelineView(Timeline *parent) : + timeline_(parent), + self_created_sequence(nullptr), + track_list_(nullptr), + scroll(0), + alignment_(olive::timeline::kAlignmentTop), + track_resizing(false) +{ setMouseTracking(true); setFocusPolicy(Qt::ClickFocus); @@ -83,11 +86,23 @@ TimelineView::TimelineView(QWidget *parent) : QWidget(parent) { connect(&tooltip_timer, SIGNAL(timeout()), this, SLOT(tooltip_timer_timeout())); } +void TimelineView::SetAlignment(olive::timeline::Alignment alignment) +{ + alignment_ = alignment; +} + +void TimelineView::SetTrackList(TrackList *tl) +{ + track_list_ = tl; + + update(); +} + void TimelineView::show_context_menu(const QPoint& pos) { - if (olive::ActiveSequence != nullptr) { + if (sequence() != nullptr) { // hack because sometimes right clicking doesn't trigger mouse release event - panel_timeline->rect_select_init = false; - panel_timeline->rect_select_proc = false; + ParentTimeline()->rect_select_init = false; + ParentTimeline()->rect_select_proc = false; Menu menu(this); @@ -95,12 +110,12 @@ void TimelineView::show_context_menu(const QPoint& pos) { QAction* redoAction = menu.addAction(tr("&Redo")); connect(undoAction, SIGNAL(triggered(bool)), olive::Global.get(), SLOT(undo())); connect(redoAction, SIGNAL(triggered(bool)), olive::Global.get(), SLOT(redo())); - undoAction->setEnabled(olive::UndoStack.canUndo()); - redoAction->setEnabled(olive::UndoStack.canRedo()); + undoAction->setEnabled(olive::undo_stack.canUndo()); + redoAction->setEnabled(olive::undo_stack.canRedo()); menu.addSeparator(); // collect all the selected clips - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence()->SelectedClips(); olive::MenuHelper.make_edit_functions_menu(&menu, !selected_clips.isEmpty()); @@ -108,12 +123,13 @@ void TimelineView::show_context_menu(const QPoint& pos) { // no clips are selected // determine if we can perform a ripple empty space - panel_timeline->cursor_frame = panel_timeline->getTimelineFrameFromScreenPoint(pos.x()); - panel_timeline->cursor_track = getTrackFromScreenPoint(pos.y()); + ParentTimeline()->cursor_frame = ParentTimeline()->getTimelineFrameFromScreenPoint(pos.x()); + ParentTimeline()->cursor_track = getTrackFromScreenPoint(pos.y()); - if (panel_timeline->can_ripple_empty_space(panel_timeline->cursor_frame, panel_timeline->cursor_track)) { + // check if the space the cursor is currently at is empty + if (ParentTimeline()->cursor_track->GetClipFromPoint(ParentTimeline()->cursor_frame) == nullptr) { QAction* ripple_delete_action = menu.addAction(tr("R&ipple Delete Empty Space")); - connect(ripple_delete_action, SIGNAL(triggered(bool)), panel_timeline, SLOT(ripple_delete_empty_space())); + connect(ripple_delete_action, SIGNAL(triggered(bool)), ParentTimeline(), SLOT(ripple_delete_empty_space())); } QAction* seq_settings = menu.addAction(tr("Sequence Settings")); @@ -171,7 +187,7 @@ void TimelineView::show_context_menu(const QPoint& pos) { } void TimelineView::toggle_autoscale() { - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence()->SelectedClips(); if (!selected_clips.isEmpty()) { SetClipProperty* action = new SetClipProperty(kSetClipPropertyAutoscale); @@ -181,7 +197,7 @@ void TimelineView::toggle_autoscale() { action->AddSetting(c, !c->autoscaled()); } - olive::UndoStack.push(action); + olive::undo_stack.push(action); } } @@ -190,9 +206,9 @@ void TimelineView::tooltip_timer_timeout() { QToolTip::showText(QCursor::pos(), tr("%1\nStart: %2\nEnd: %3\nDuration: %4").arg( tooltip_clip->name(), - frame_to_timecode(tooltip_clip->timeline_in(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), - frame_to_timecode(tooltip_clip->timeline_out(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate), - frame_to_timecode(tooltip_clip->length(), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate) + frame_to_timecode(tooltip_clip->timeline_in(), olive::config.timecode_view, sequence()->frame_rate), + frame_to_timecode(tooltip_clip->timeline_out(), olive::config.timecode_view, sequence()->frame_rate), + frame_to_timecode(tooltip_clip->length(), olive::config.timecode_view, sequence()->frame_rate) )); } @@ -203,7 +219,7 @@ void TimelineView::open_sequence_properties() { QVector sequence_items = olive::project_model.GetAllSequences(); for (int i=0;ito_sequence() == olive::ActiveSequence) { + if (sequence_items.at(i)->to_sequence().get() == sequence()) { NewSequenceDialog nsd(this, sequence_items.at(i)); nsd.exec(); return; @@ -216,7 +232,7 @@ void TimelineView::open_sequence_properties() { void TimelineView::show_clip_properties() { // get list of selected clips - QVector selected_clips = olive::ActiveSequence->SelectedClips(); + QVector selected_clips = sequence()->SelectedClips(); // if clips are selected, open the clip properties dialog if (!selected_clips.isEmpty()) { @@ -225,15 +241,11 @@ void TimelineView::show_clip_properties() } } -bool same_sign(int a, int b) { - return (a < 0) == (b < 0); -} - void TimelineView::dragEnterEvent(QDragEnterEvent *event) { bool import_init = false; QVector media_list; - panel_timeline->importing_files = false; + ParentTimeline()->importing_files = false; for (int i=0;iIsProjectWidget(event->source())) { @@ -252,16 +264,16 @@ void TimelineView::dragEnterEvent(QDragEnterEvent *event) { } if (event->source() == panel_footage_viewer) { - if (panel_footage_viewer->seq != olive::ActiveSequence) { // don't allow nesting the same sequence + if (panel_footage_viewer->seq.get() != sequence()) { // don't allow nesting the same sequence media_list.append(olive::timeline::MediaImportData(panel_footage_viewer->media, - static_cast(event->mimeData()->text().toInt()))); + static_cast(event->mimeData()->text().toInt()))); import_init = true; } } - if (olive::CurrentConfig.enable_drag_files_to_timeline && event->mimeData()->hasUrls()) { + if (olive::config.enable_drag_files_to_timeline && event->mimeData()->hasUrls()) { QList urls = event->mimeData()->urls(); if (!urls.isEmpty()) { QStringList file_list; @@ -270,10 +282,11 @@ void TimelineView::dragEnterEvent(QDragEnterEvent *event) { file_list.append(urls.at(i).toLocalFile()); } - panel_project->process_file_list(file_list); + olive::project_model.process_file_list(file_list); - for (int i=0;ilast_imported_media.size();i++) { - Footage* f = panel_project->last_imported_media.at(i)->to_footage(); + QVector last_imported_media = olive::project_model.GetLastImportedMedia(); + for (int i=0;ito_footage(); // waits for media to have a duration // TODO would be much nicer if this was multithreaded @@ -281,15 +294,15 @@ void TimelineView::dragEnterEvent(QDragEnterEvent *event) { f->ready_lock.unlock(); if (f->ready) { - media_list.append(panel_project->last_imported_media.at(i)); + media_list.append(last_imported_media.at(i)); } } if (media_list.isEmpty()) { - olive::UndoStack.undo(); + olive::undo_stack.undo(); } else { import_init = true; - panel_timeline->importing_files = true; + ParentTimeline()->importing_files = true; } } } @@ -298,35 +311,35 @@ void TimelineView::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); long entry_point; - Sequence* seq = olive::ActiveSequence.get(); + Sequence* seq = sequence(); if (seq == nullptr) { // if no sequence, we're going to create a new one using the clips as a reference entry_point = 0; - self_created_sequence = create_sequence_from_media(media_list); + self_created_sequence = olive::project::CreateSequenceFromMedia(media_list); seq = self_created_sequence.get(); } else { - entry_point = panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x()); - panel_timeline->drag_frame_start = entry_point + getFrameFromScreenPoint(panel_timeline->zoom, 50); - panel_timeline->drag_track_start = (bottom_align) ? -1 : 0; + entry_point = ParentTimeline()->getTimelineFrameFromScreenPoint(event->pos().x()); + ParentTimeline()->drag_frame_start = entry_point + getFrameFromScreenPoint(ParentTimeline()->zoom, 50); + ParentTimeline()->drag_track_start = track_list_->First(); } - panel_timeline->create_ghosts_from_media(seq, entry_point, media_list); + ParentTimeline()->ghosts = olive::timeline::CreateGhostsFromMedia(seq, entry_point, media_list); - panel_timeline->importing = true; + ParentTimeline()->importing = true; } } void TimelineView::dragMoveEvent(QDragMoveEvent *event) { - if (panel_timeline->importing) { + if (ParentTimeline()->importing) { event->acceptProposedAction(); - if (olive::ActiveSequence != nullptr) { + if (sequence() != nullptr) { QPoint pos = event->pos(); - panel_timeline->scroll_to_frame(panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x())); + ParentTimeline()->scroll_to_frame(ParentTimeline()->getTimelineFrameFromScreenPoint(event->pos().x())); update_ghosts(pos, event->keyboardModifiers() & Qt::ShiftModifier); - panel_timeline->move_insert = ((event->keyboardModifiers() & Qt::ControlModifier) && (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->importing)); + ParentTimeline()->move_insert = ((event->keyboardModifiers() & Qt::ControlModifier) && (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER || ParentTimeline()->importing)); update_ui(false); } } @@ -344,11 +357,11 @@ void TimelineView::wheelEvent(QWheelEvent *event) { // "Scroll Zooms" false + Control down: zooming // "Scroll Zooms" true + Control up : zooming // "Scroll Zooms" true + Control down: not zooming - bool zooming = (olive::CurrentConfig.scroll_zooms != ctrl); + bool zooming = (olive::config.scroll_zooms != ctrl); // Allow shift for axis swap, but don't swap on zoom... Unless // we need to override Qt's axis swap via Alt - bool swap_hv = ((shift != olive::CurrentConfig.invert_timeline_scroll_axes) & + bool swap_hv = ((shift != olive::config.invert_timeline_scroll_axes) & !zooming) | (alt & !shift & zooming); int delta_h = swap_hv ? event->angleDelta().y() : event->angleDelta().x(); @@ -371,7 +384,7 @@ void TimelineView::wheelEvent(QWheelEvent *event) { zoom_ratio = 1.0 / zoom_ratio; } - panel_timeline->multiply_zoom(zoom_ratio); + ParentTimeline()->multiply_zoom(zoom_ratio); } } else { @@ -380,7 +393,7 @@ void TimelineView::wheelEvent(QWheelEvent *event) { // widget's scrollbar for vertical scrolling. QScrollBar* bar_v = scrollBar; - QScrollBar* bar_h = panel_timeline->horizontalScrollBar; + QScrollBar* bar_h = ParentTimeline()->horizontalScrollBar; // Match the wheel events to the size of a step as per // https://doc.qt.io/qt-5/qwheelevent.html#angleDelta @@ -397,13 +410,13 @@ void TimelineView::wheelEvent(QWheelEvent *event) { void TimelineView::dragLeaveEvent(QDragLeaveEvent* event) { event->accept(); - if (panel_timeline->importing) { - if (panel_timeline->importing_files) { - olive::UndoStack.undo(); + if (ParentTimeline()->importing) { + if (ParentTimeline()->importing_files) { + olive::undo_stack.undo(); } - panel_timeline->importing_files = false; - panel_timeline->ghosts.clear(); - panel_timeline->importing = false; + ParentTimeline()->importing_files = false; + ParentTimeline()->ghosts.clear(); + ParentTimeline()->importing = false; update_ui(false); } if (self_created_sequence != nullptr) { @@ -412,21 +425,16 @@ void TimelineView::dragLeaveEvent(QDragLeaveEvent* event) { } } -void delete_area_under_ghosts(ComboAction* ca) { +void TimelineView::delete_area_under_ghosts(ComboAction* ca, Sequence* s) { // delete areas before adding QVector delete_areas; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); - Selection sel; - sel.in = g.in; - sel.out = g.out; - sel.track = g.track; - delete_areas.append(sel); + for (int i=0;ighosts.size();i++) { + delete_areas.append(ParentTimeline()->ghosts.at(i).ToSelection()); } - panel_timeline->delete_areas_and_relink(ca, delete_areas, false); + s->DeleteAreas(ca, delete_areas, false); } -void insert_clips(ComboAction* ca) { +void TimelineView::insert_clips(ComboAction* ca, Sequence* s) { bool ripple_old_point = true; long earliest_old_point = LONG_MAX; @@ -435,16 +443,16 @@ void insert_clips(ComboAction* ca) { long earliest_new_point = LONG_MAX; long latest_new_point = LONG_MIN; - QVector ignore_clips; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + QVector ignore_clips; + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); earliest_old_point = qMin(earliest_old_point, g.old_in); latest_old_point = qMax(latest_old_point, g.old_out); earliest_new_point = qMin(earliest_new_point, g.in); latest_new_point = qMax(latest_new_point, g.out); - if (g.clip >= 0) { + if (g.clip != nullptr) { ignore_clips.append(g.clip); } else { // don't try to close old gap if importing @@ -452,79 +460,81 @@ void insert_clips(ComboAction* ca) { } } - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { - // don't split any clips that are moving - bool found = false; - for (int j=0;jghosts.size();j++) { - if (panel_timeline->ghosts.at(j).clip == i) { - found = true; - break; - } + QVector sequence_clips = sequence()->GetAllClips(); + for (int i=0;ighosts.size();j++) { + if (ParentTimeline()->ghosts.at(j).clip == c) { + found = true; + break; + } + } + if (!found) { + if (c->timeline_in() < earliest_new_point && c->timeline_out() > earliest_new_point) { + sequence()->SplitClipAtPositions(ca, c, {earliest_new_point}, true); } - if (!found) { - if (c->timeline_in() < earliest_new_point && c->timeline_out() > earliest_new_point) { - panel_timeline->split_clip_and_relink(ca, i, earliest_new_point, true); - } - // determine if we should close the gap the old clips left behind - if (ripple_old_point - && !((c->timeline_in() < earliest_old_point && c->timeline_out() <= earliest_old_point) || (c->timeline_in() >= latest_old_point && c->timeline_out() > latest_old_point)) - && !ignore_clips.contains(i)) { - ripple_old_point = false; - } + // determine if we should close the gap the old clips left behind + if (ripple_old_point + && !((c->timeline_in() < earliest_old_point && c->timeline_out() <= earliest_old_point) || (c->timeline_in() >= latest_old_point && c->timeline_out() > latest_old_point)) + && !ignore_clips.contains(c)) { + ripple_old_point = false; } } } long ripple_length = (latest_new_point - earliest_new_point); - ripple_clips(ca, olive::ActiveSequence.get(), earliest_new_point, ripple_length, ignore_clips); + sequence()->Ripple(ca, earliest_new_point, ripple_length, ignore_clips); if (ripple_old_point) { // works for moving later clips earlier but not earlier to later long second_ripple_length = (earliest_old_point - latest_old_point); - ripple_clips(ca, olive::ActiveSequence.get(), latest_old_point, second_ripple_length, ignore_clips); + sequence()->Ripple(ca, latest_old_point, second_ripple_length, ignore_clips); if (earliest_old_point < earliest_new_point) { - for (int i=0;ighosts.size();i++) { - Ghost& g = panel_timeline->ghosts[i]; + for (int i=0;ighosts.size();i++) { + Ghost& g = ParentTimeline()->ghosts[i]; g.in += second_ripple_length; g.out += second_ripple_length; } - for (int i=0;iselections.size();i++) { - Selection& s = olive::ActiveSequence->selections[i]; - s.in += second_ripple_length; - s.out += second_ripple_length; + + QVector sequence_selections = sequence()->Selections(); + for (int i=0;iSetSelections(sequence_selections); } } } void TimelineView::dropEvent(QDropEvent* event) { - if (panel_timeline->importing && panel_timeline->ghosts.size() > 0) { + if (ParentTimeline()->importing && ParentTimeline()->ghosts.size() > 0) { event->acceptProposedAction(); ComboAction* ca = new ComboAction(); - Sequence* s = olive::ActiveSequence.get(); + Sequence* s = sequence(); // if we're dropping into nothing, create a new sequences based on the clip being dragged if (s == nullptr) { s = self_created_sequence.get(); - panel_project->create_sequence_internal(ca, self_created_sequence, true, nullptr); + olive::project_model.CreateSequence(ca, self_created_sequence, true, nullptr); self_created_sequence = nullptr; } else if (event->keyboardModifiers() & Qt::ControlModifier) { - insert_clips(ca); + insert_clips(ca, s); } else { - delete_area_under_ghosts(ca); + delete_area_under_ghosts(ca, s); } - panel_timeline->add_clips_from_ghosts(ca, s); + s->AddClipsFromGhosts(ca, ParentTimeline()->ghosts); - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); setFocus(); @@ -533,64 +543,71 @@ void TimelineView::dropEvent(QDropEvent* event) { } void TimelineView::mouseDoubleClickEvent(QMouseEvent *event) { - if (olive::ActiveSequence != nullptr) { - if (panel_timeline->tool == TIMELINE_TOOL_EDIT) { - int clip_index = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track); - if (clip_index >= 0) { - ClipPtr clip = olive::ActiveSequence->clips.at(clip_index); - if (!(event->modifiers() & Qt::ShiftModifier)) olive::ActiveSequence->selections.clear(); - Selection s; - s.in = clip->timeline_in(); - s.out = clip->timeline_out(); - s.track = clip->track(); - olive::ActiveSequence->selections.append(s); + if (sequence() != nullptr) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_EDIT) { + Clip* clip = GetClipAtCursor(); + if (clip != nullptr) { + if (!(event->modifiers() & Qt::ShiftModifier)) { + sequence()->ClearSelections(); + } + clip->track()->SelectClip(clip); update_ui(false); } - } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER) { - int clip_index = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track); - if (clip_index >= 0) { - ClipPtr c = olive::ActiveSequence->clips.at(clip_index); + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER) { + Clip* c = GetClipAtCursor(); + if (c != nullptr) { if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { - olive::Global->set_sequence(c->media()->to_sequence()); + Timeline::OpenSequence(c->media()->to_sequence()); } } } } } -bool current_tool_shows_cursor() { - return (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR || panel_timeline->creating); +bool TimelineView::current_tool_shows_cursor() { + return (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_EDIT + || olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RAZOR + || ParentTimeline()->creating); +} + +Clip *TimelineView::GetClipAtCursor() +{ + if (ParentTimeline()->cursor_track == nullptr) { + return nullptr; + } + + return ParentTimeline()->cursor_track->GetClipFromPoint(ParentTimeline()->cursor_frame); } void TimelineView::mousePressEvent(QMouseEvent *event) { - if (olive::ActiveSequence != nullptr) { + if (sequence() != nullptr) { - int effective_tool = panel_timeline->tool; + int effective_tool = olive::timeline::current_tool; // some user actions will override which tool we'll be using if (event->button() == Qt::MiddleButton) { - effective_tool = TIMELINE_TOOL_HAND; - panel_timeline->creating = false; + effective_tool = olive::timeline::TIMELINE_TOOL_HAND; + ParentTimeline()->creating = false; } else if (event->button() == Qt::RightButton) { - effective_tool = TIMELINE_TOOL_MENU; - panel_timeline->creating = false; + effective_tool = olive::timeline::TIMELINE_TOOL_MENU; + ParentTimeline()->creating = false; } // ensure cursor_frame and cursor_track are up to date mouseMoveEvent(event); // store current cursor positions - panel_timeline->drag_x_start = event->pos().x(); - panel_timeline->drag_y_start = event->pos().y(); + ParentTimeline()->drag_x_start = event->pos().x(); + ParentTimeline()->drag_y_start = event->pos().y(); // store current frame/tracks as the values to start dragging from - panel_timeline->drag_frame_start = panel_timeline->cursor_frame; - panel_timeline->drag_track_start = panel_timeline->cursor_track; + ParentTimeline()->drag_frame_start = ParentTimeline()->cursor_frame; + ParentTimeline()->drag_track_start = ParentTimeline()->cursor_track; // get the clip the user is currently hovering over, priority to trim_target set from mouseMoveEvent - int hovered_clip = panel_timeline->trim_target == -1 ? - getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track) - : panel_timeline->trim_target; + Clip* hovered_clip = ParentTimeline()->trim_target == nullptr ? + GetClipAtCursor() + : ParentTimeline()->trim_target; bool shift = (event->modifiers() & Qt::ShiftModifier); bool alt = (event->modifiers() & Qt::AltModifier); @@ -599,40 +616,39 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // to the existing selections. `selection_offset` is the index to change selections from (and we don't touch // any prior to that) if (shift) { - panel_timeline->selection_offset = olive::ActiveSequence->selections.size(); + ParentTimeline()->selection_offset = sequence()->Selections().size(); } else { - panel_timeline->selection_offset = 0; + ParentTimeline()->selection_offset = 0; } // if the user is creating an object - if (panel_timeline->creating) { - int comp = 0; - switch (panel_timeline->creating_object) { + if (ParentTimeline()->creating) { + Track::Type create_type = Track::kTypeVideo; + switch (ParentTimeline()->creating_object) { case olive::timeline::ADD_OBJ_TITLE: case olive::timeline::ADD_OBJ_SOLID: case olive::timeline::ADD_OBJ_BARS: - comp = -1; break; case olive::timeline::ADD_OBJ_TONE: case olive::timeline::ADD_OBJ_NOISE: case olive::timeline::ADD_OBJ_AUDIO: - comp = 1; + create_type = Track::kTypeAudio; break; } // if the track the user clicked is correct for the type of object we're adding - if ((panel_timeline->drag_track_start < 0) == (comp < 0)) { + if (ParentTimeline()->drag_track_start->type() == create_type) { Ghost g; - g.in = g.old_in = g.out = g.old_out = panel_timeline->drag_frame_start; - g.track = g.old_track = panel_timeline->drag_track_start; + g.in = g.old_in = g.out = g.old_out = ParentTimeline()->drag_frame_start; + g.track = g.old_track = ParentTimeline()->drag_track_start; g.transition = nullptr; - g.clip = -1; + g.clip = nullptr; g.trim_type = olive::timeline::TRIM_OUT; - panel_timeline->ghosts.append(g); + ParentTimeline()->ghosts.append(g); - panel_timeline->moving_init = true; - panel_timeline->moving_proc = true; + ParentTimeline()->moving_init = true; + ParentTimeline()->moving_proc = true; } } else { @@ -640,73 +656,75 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { switch (effective_tool) { // many tools share pointer-esque behavior - case TIMELINE_TOOL_POINTER: - case TIMELINE_TOOL_RIPPLE: - case TIMELINE_TOOL_SLIP: - case TIMELINE_TOOL_ROLLING: - case TIMELINE_TOOL_SLIDE: - case TIMELINE_TOOL_MENU: + case olive::timeline::TIMELINE_TOOL_POINTER: + case olive::timeline::TIMELINE_TOOL_RIPPLE: + case olive::timeline::TIMELINE_TOOL_SLIP: + case olive::timeline::TIMELINE_TOOL_ROLLING: + case olive::timeline::TIMELINE_TOOL_SLIDE: + case olive::timeline::TIMELINE_TOOL_MENU: { - if (track_resizing && effective_tool != TIMELINE_TOOL_MENU) { + if (track_resizing && effective_tool != olive::timeline::TIMELINE_TOOL_MENU) { // if the cursor is currently hovering over a track, init track resizing - panel_timeline->moving_init = true; + ParentTimeline()->moving_init = true; } else { // check if we're currently hovering over a clip or not - if (hovered_clip >= 0) { - Clip* clip = olive::ActiveSequence->clips.at(hovered_clip).get(); + if (hovered_clip != nullptr) { - if (clip->IsSelected()) { + if (hovered_clip->IsSelected()) { if (shift) { // if the user clicks a selected clip while holding shift, deselect the clip - panel_timeline->deselect_area(clip->timeline_in(), clip->timeline_out(), clip->track()); + hovered_clip->track()->DeselectArea(hovered_clip->timeline_in(), hovered_clip->timeline_out()); // if the user isn't holding alt, also deselect all of its links as well if (!alt) { - for (int i=0;ilinked.size();i++) { - ClipPtr link = olive::ActiveSequence->clips.at(clip->linked.at(i)); - panel_timeline->deselect_area(link->timeline_in(), link->timeline_out(), link->track()); + for (int i=0;ilinked.size();i++) { + Clip* link = hovered_clip->linked.at(i); + link->track()->DeselectArea(link->timeline_in(), link->timeline_out()); } } - } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER - && panel_timeline->transition_select != kTransitionNone) { + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER + && ParentTimeline()->transition_select != kTransitionNone) { // if the clip was selected by then the user clicked a transition, de-select the clip and its links // and select the transition only - panel_timeline->deselect_area(clip->timeline_in(), clip->timeline_out(), clip->track()); + hovered_clip->track()->DeselectArea(hovered_clip->timeline_in(), hovered_clip->timeline_out()); - for (int i=0;ilinked.size();i++) { - ClipPtr link = olive::ActiveSequence->clips.at(clip->linked.at(i)); - panel_timeline->deselect_area(link->timeline_in(), link->timeline_out(), link->track()); + for (int i=0;ilinked.size();i++) { + Clip* link = hovered_clip->linked.at(i); + link->track()->DeselectArea(link->timeline_in(), link->timeline_out()); } - Selection s; - s.track = clip->track(); + long s_in, s_out; // select the transition only - if (panel_timeline->transition_select == kTransitionOpening && clip->opening_transition != nullptr) { - s.in = clip->timeline_in(); + if (ParentTimeline()->transition_select == kTransitionOpening + && hovered_clip->opening_transition != nullptr) { + s_in = hovered_clip->timeline_in(); - if (clip->opening_transition->secondary_clip != nullptr) { - s.in -= clip->opening_transition->get_true_length(); + if (hovered_clip->opening_transition->secondary_clip != nullptr) { + s_in -= hovered_clip->opening_transition->get_true_length(); } - s.out = clip->timeline_in() + clip->opening_transition->get_true_length(); - } else if (panel_timeline->transition_select == kTransitionClosing && clip->closing_transition != nullptr) { - s.in = clip->timeline_out() - clip->closing_transition->get_true_length(); - s.out = clip->timeline_out(); + s_out = hovered_clip->timeline_in() + hovered_clip->opening_transition->get_true_length(); - if (clip->closing_transition->secondary_clip != nullptr) { - s.out += clip->closing_transition->get_true_length(); + } else if (ParentTimeline()->transition_select == kTransitionClosing + && hovered_clip->closing_transition != nullptr) { + + s_in = hovered_clip->timeline_out() - hovered_clip->closing_transition->get_true_length(); + s_out = hovered_clip->timeline_out(); + + if (hovered_clip->closing_transition->secondary_clip != nullptr) { + s_out += hovered_clip->closing_transition->get_true_length(); } } - olive::ActiveSequence->selections.append(s); + hovered_clip->track()->SelectArea(s_in, s_out); } } else { @@ -714,59 +732,52 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // if shift is NOT down, we change clear all current selections if (!shift) { - olive::ActiveSequence->selections.clear(); + sequence()->ClearSelections(); } - Selection s; - - s.in = clip->timeline_in(); - s.out = clip->timeline_out(); - s.track = clip->track(); + long s_in = hovered_clip->timeline_in(); + long s_out = hovered_clip->timeline_out(); // if user is using the pointer tool, they may be trying to select a transition // check if the use is hovering over a transition - if (panel_timeline->tool == TIMELINE_TOOL_POINTER) { - if (panel_timeline->transition_select == kTransitionOpening) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER) { + if (ParentTimeline()->transition_select == kTransitionOpening) { // move the selection to only select the transitoin - s.out = clip->timeline_in() + clip->opening_transition->get_true_length(); + s_out = hovered_clip->timeline_in() + hovered_clip->opening_transition->get_true_length(); // if the transition is a "shared" transition, adjust the selection to select both sides - if (clip->opening_transition->secondary_clip != nullptr) { - s.in -= clip->opening_transition->get_true_length(); + if (hovered_clip->opening_transition->secondary_clip != nullptr) { + s_in -= hovered_clip->opening_transition->get_true_length(); } - } else if (panel_timeline->transition_select == kTransitionClosing) { + } else if (ParentTimeline()->transition_select == kTransitionClosing) { // move the selection to only select the transitoin - s.in = clip->timeline_out() - clip->closing_transition->get_true_length(); + s_in = hovered_clip->timeline_out() - hovered_clip->closing_transition->get_true_length(); // if the transition is a "shared" transition, adjust the selection to select both sides - if (clip->closing_transition->secondary_clip != nullptr) { - s.out += clip->closing_transition->get_true_length(); + if (hovered_clip->closing_transition->secondary_clip != nullptr) { + s_out += hovered_clip->closing_transition->get_true_length(); } } } // add the selection to the array - olive::ActiveSequence->selections.append(s); + hovered_clip->track()->SelectArea(s_in, s_out); // if the config is set to also seek with selections, do so now - if (olive::CurrentConfig.select_also_seeks) { - panel_sequence_viewer->seek(clip->timeline_in()); + if (olive::config.select_also_seeks) { + panel_sequence_viewer->seek(hovered_clip->timeline_in()); } // if alt is not down, select links (provided we're not selecting transitions) - if (!alt && panel_timeline->transition_select == kTransitionNone) { + if (!alt && ParentTimeline()->transition_select == kTransitionNone) { - for (int i=0;ilinked.size();i++) { + for (int i=0;ilinked.size();i++) { - Clip* link = olive::ActiveSequence->clips.at(clip->linked.at(i)).get(); + Clip* link = hovered_clip->linked.at(i); // check if the clip is already selected if (!link->IsSelected()) { - Selection ss; - ss.in = link->timeline_in(); - ss.out = link->timeline_out(); - ss.track = link->track(); - olive::ActiveSequence->selections.append(ss); + link->track()->SelectClip(link); } } @@ -775,8 +786,8 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { } // authorize the starting of a move action if the mouse moves after this - if (effective_tool != TIMELINE_TOOL_MENU) { - panel_timeline->moving_init = true; + if (effective_tool != olive::timeline::TIMELINE_TOOL_MENU) { + ParentTimeline()->moving_init = true; } } else { @@ -784,10 +795,10 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // if the user did not click a clip at all, we start a rectangle selection if (!shift) { - olive::ActiveSequence->selections.clear(); + sequence()->ClearSelections(); } - panel_timeline->rect_select_init = true; + ParentTimeline()->rect_select_init = true; } // update everything @@ -795,42 +806,42 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { } } break; - case TIMELINE_TOOL_HAND: + case olive::timeline::TIMELINE_TOOL_HAND: // initiate moving with the hand tool - panel_timeline->hand_moving = true; + ParentTimeline()->hand_moving = true; break; - case TIMELINE_TOOL_EDIT: + case olive::timeline::TIMELINE_TOOL_EDIT: // if the config is set to seek with the edit tool, do so now - if (olive::CurrentConfig.edit_tool_also_seeks) { - panel_sequence_viewer->seek(panel_timeline->drag_frame_start); + if (olive::config.edit_tool_also_seeks) { + panel_sequence_viewer->seek(ParentTimeline()->drag_frame_start); } // initiate selecting - panel_timeline->selecting = true; + ParentTimeline()->selecting = true; break; - case TIMELINE_TOOL_RAZOR: + case olive::timeline::TIMELINE_TOOL_RAZOR: { // initiate razor tool - panel_timeline->splitting = true; + ParentTimeline()->splitting = true; // add this track as a track being split by the razor - panel_timeline->split_tracks.append(panel_timeline->drag_track_start); + ParentTimeline()->split_tracks.append(ParentTimeline()->drag_track_start); update_ui(false); } break; - case TIMELINE_TOOL_TRANSITION: + case olive::timeline::TIMELINE_TOOL_TRANSITION: { // if there is a clip to run the transition tool on, initiate the transition tool - if (panel_timeline->transition_tool_open_clip > -1 - || panel_timeline->transition_tool_close_clip > -1) { - panel_timeline->transition_tool_init = true; + if (ParentTimeline()->transition_tool_open_clip != nullptr + || ParentTimeline()->transition_tool_close_clip != nullptr) { + ParentTimeline()->transition_tool_init = true; } } @@ -882,7 +893,7 @@ void make_room_for_transition(ComboAction* ca, } } -void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, long transition_start, long transition_end) { +void TimelineView::VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, long transition_start, long transition_end) { // in case the user made the transition larger than the clips, we're going to delete everything under // the transition ghost and extend the clips to the transition's coordinates as necessary @@ -894,7 +905,7 @@ void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, lo // determine whether this is a "shared" transition between to clips or not bool shared_transition = (open != nullptr && close != nullptr); - int track = 0; + Track* track = nullptr; // first we set the clips to "undeletable" so they aren't affected by delete_areas_and_relink() if (open != nullptr) { @@ -908,12 +919,8 @@ void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, lo // set the area to delete to the transition's coordinates and clear it QVector areas; - Selection s; - s.in = transition_start; - s.out = transition_end; - s.track = track; - areas.append(s); - panel_timeline->delete_areas_and_relink(ca, areas, false); + areas.append(Selection(transition_start, transition_end, track)); + sequence()->DeleteAreas(ca, areas, false); // set the clips back to undeletable now that we're done if (open != nullptr) { @@ -962,7 +969,7 @@ void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, lo - clip_ref->move(ca, + clip_ref->Move(ca, new_in, new_out, clip_ref->clip_in() - (clip_ref->timeline_in() - new_in), @@ -974,7 +981,7 @@ void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, lo void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QToolTip::hideText(); - if (olive::ActiveSequence != nullptr) { + if (sequence() != nullptr) { bool alt = (event->modifiers() & Qt::AltModifier); bool shift = (event->modifiers() & Qt::ShiftModifier); bool ctrl = (event->modifiers() & Qt::ControlModifier); @@ -983,45 +990,38 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { ComboAction* ca = new ComboAction(); bool push_undo = false; - if (panel_timeline->creating) { - if (panel_timeline->ghosts.size() > 0) { - const Ghost& g = panel_timeline->ghosts.at(0); + if (ParentTimeline()->creating) { + if (ParentTimeline()->ghosts.size() > 0) { + const Ghost& g = ParentTimeline()->ghosts.at(0); - if (panel_timeline->creating_object == olive::timeline::ADD_OBJ_AUDIO) { + if (ParentTimeline()->creating_object == olive::timeline::ADD_OBJ_AUDIO) { olive::MainWindow->statusBar()->clearMessage(); panel_sequence_viewer->cue_recording(qMin(g.in, g.out), qMax(g.in, g.out), g.track); - panel_timeline->creating = false; + ParentTimeline()->creating = false; } else if (g.in != g.out) { - ClipPtr c = std::make_shared(olive::ActiveSequence.get()); + ClipPtr c = std::make_shared(g.track); c->set_media(nullptr, 0); c->set_timeline_in(qMin(g.in, g.out)); c->set_timeline_out(qMax(g.in, g.out)); c->set_clip_in(0); c->set_color(192, 192, 64); - c->set_track(g.track); if (ctrl) { - insert_clips(ca); + insert_clips(ca, sequence()); } else { - Selection s; - s.in = c->timeline_in(); - s.out = c->timeline_out(); - s.track = c->track(); - QVector areas; - areas.append(s); - panel_timeline->delete_areas_and_relink(ca, areas, false); + sequence()->DeleteAreas(ca, {c->ToSelection()}, false); } QVector add; add.append(c); - ca->append(new AddClipCommand(olive::ActiveSequence.get(), add)); + ca->append(new AddClipCommand(add)); - if (c->track() < 0 && olive::CurrentConfig.add_default_effects_to_clips) { + if (c->type() == Track::kTypeVideo && olive::config.add_default_effects_to_clips) { // default video effects (before custom effects) c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); } - switch (panel_timeline->creating_object) { + switch (ParentTimeline()->creating_object) { case olive::timeline::ADD_OBJ_TITLE: c->set_name(tr("Title")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_RICHTEXT, EFFECT_TYPE_EFFECT))); @@ -1052,7 +1052,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { break; } - if (c->track() >= 0 && olive::CurrentConfig.add_default_effects_to_clips) { + if (c->type() == Track::kTypeAudio && olive::config.add_default_effects_to_clips) { // default audio effects (after custom effects) c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); @@ -1061,19 +1061,19 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { push_undo = true; if (!shift) { - panel_timeline->creating = false; + ParentTimeline()->creating = false; } } } - } else if (panel_timeline->moving_proc) { + } else if (ParentTimeline()->moving_proc) { // see if any clips actually moved, otherwise we don't need to do any processing // (perhaps this could be moved further up to cover more actions?) bool process_moving = false; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); if (g.in != g.old_in || g.out != g.old_out || g.clip_in != g.old_clip_in @@ -1084,17 +1084,17 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } if (process_moving) { - const Ghost& first_ghost = panel_timeline->ghosts.at(0); + const Ghost& first_ghost = ParentTimeline()->ghosts.at(0); // start a ripple movement - if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RIPPLE) { // ripple_length becomes the length/number of frames we trimmed // ripple_point is the "axis" around which we move all the clips, any clips after it get moved long ripple_length; long ripple_point = LONG_MAX; - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { // it's assumed that all the ghosts rippled by the same length, so we just take the difference of the // first ghost here @@ -1102,66 +1102,63 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { // for in trimming movements we also move the selections forward (unnecessary for out trimming since // the selected clips more or less stay in the same place) - for (int i=0;iselections.size();i++) { - olive::ActiveSequence->selections[i].in += ripple_length; - olive::ActiveSequence->selections[i].out += ripple_length; + /* + for (int i=0;iselections.size();i++) { + sequence()->selections[i].in += ripple_length; + sequence()->selections[i].out += ripple_length; } + */ } else { // use the out points for length if the user trimmed the out point - ripple_length = first_ghost.old_out - panel_timeline->ghosts.at(0).out; + ripple_length = first_ghost.old_out - ParentTimeline()->ghosts.at(0).out; } // build a list of "ignore clips" that won't get affected by ripple_clips() below - QVector ignore_clips; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + QVector ignore_clips; + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); // for the same reason that we pushed selections forward above, for in trimming, // we push the ghosts forward here - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { ignore_clips.append(g.clip); - panel_timeline->ghosts[i].in += ripple_length; - panel_timeline->ghosts[i].out += ripple_length; + ParentTimeline()->ghosts[i].in += ripple_length; + ParentTimeline()->ghosts[i].out += ripple_length; } // find the earliest ripple point - long comp_point = (panel_timeline->trim_type == olive::timeline::TRIM_IN) ? g.old_in : g.old_out; + long comp_point = (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) ? g.old_in : g.old_out; ripple_point = qMin(ripple_point, comp_point); } // if this was out trimming, flip the direction of the ripple - if (panel_timeline->trim_type == olive::timeline::TRIM_OUT) ripple_length = -ripple_length; + if (ParentTimeline()->trim_type == olive::timeline::TRIM_OUT) ripple_length = -ripple_length; // finally, ripple everything - ripple_clips(ca, olive::ActiveSequence.get(), ripple_point, ripple_length, ignore_clips); + sequence()->Ripple(ca, ripple_point, ripple_length, ignore_clips); } - if (panel_timeline->tool == TIMELINE_TOOL_POINTER + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER && (event->modifiers() & Qt::AltModifier) - && panel_timeline->trim_target == -1) { + && ParentTimeline()->trim_target == nullptr) { // if the user was holding alt (and not trimming), we duplicate clips rather than move them - QVector old_clips; + QVector old_clips; QVector new_clips; QVector delete_areas; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); if (g.old_in != g.in || g.old_out != g.out || g.track != g.old_track || g.clip_in != g.old_clip_in) { // create copy of clip - ClipPtr c = olive::ActiveSequence->clips.at(g.clip)->copy(olive::ActiveSequence.get()); + ClipPtr c = g.clip->copy(g.track); c->set_timeline_in(g.in); c->set_timeline_out(g.out); - c->set_track(g.track); - Selection s; - s.in = g.in; - s.out = g.out; - s.track = g.track; - delete_areas.append(s); + delete_areas.append(g.ToSelection()); old_clips.append(g.clip); new_clips.append(c); @@ -1172,13 +1169,13 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { if (new_clips.size() > 0) { // delete anything under the new clips - panel_timeline->delete_areas_and_relink(ca, delete_areas, false); + sequence()->DeleteAreas(ca, delete_areas, false); // relink duplicated clips - panel_timeline->relink_clips_using_ids(old_clips, new_clips); + olive::timeline::RelinkClips(old_clips, new_clips); // add them - ca->append(new AddClipCommand(olive::ActiveSequence.get(), new_clips)); + ca->append(new AddClipCommand(new_clips)); } @@ -1187,22 +1184,22 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { // if we're not holding alt, this will just be a move // if the user is holding ctrl, perform an insert rather than an overwrite - if (panel_timeline->tool == TIMELINE_TOOL_POINTER && ctrl) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER && ctrl) { - insert_clips(ca); + insert_clips(ca, sequence()); - } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->tool == TIMELINE_TOOL_SLIDE) { + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER || olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIDE) { // if the user is not holding ctrl, we start standard clip movement // delete everything under the new clips QVector delete_areas; - for (int i=0;ighosts.size();i++) { + for (int i=0;ighosts.size();i++) { // step 1 - set clips that are moving to "undeletable" (to avoid step 2 deleting any part of them) - const Ghost& g = panel_timeline->ghosts.at(i); + const Ghost& g = ParentTimeline()->ghosts.at(i); // set clip to undeletable so it's unaffected by delete_areas_and_relink() below - olive::ActiveSequence->clips.at(g.clip)->undeletable = true; + g.clip->undeletable = true; // if the user was moving a transition make sure they're undeletable too if (g.transition != nullptr) { @@ -1213,19 +1210,15 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // set area to delete - Selection s; - s.in = g.in; - s.out = g.out; - s.track = g.track; - delete_areas.append(s); + delete_areas.append(g.ToSelection()); } - panel_timeline->delete_areas_and_relink(ca, delete_areas, false); + sequence()->DeleteAreas(ca, delete_areas, false); // clean up, i.e. make everything not undeletable again - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); - olive::ActiveSequence->clips.at(g.clip)->undeletable = false; + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); + g.clip->undeletable = false; if (g.transition != nullptr) { g.transition->parent_clip->undeletable = false; @@ -1237,16 +1230,22 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // finally, perform actual movement of clips - for (int i=0;ighosts.size();i++) { - Ghost& g = panel_timeline->ghosts[i]; + for (int i=0;ighosts.size();i++) { + Ghost& g = ParentTimeline()->ghosts[i]; - Clip* c = olive::ActiveSequence->clips.at(g.clip).get(); + Clip* c = g.clip; if (g.transition == nullptr) { // if this was a clip rather than a transition - c->move(ca, (g.in - g.old_in), (g.out - g.old_out), (g.clip_in - g.old_clip_in), (g.track - g.old_track), false, true); + c->Move(ca, + (g.in - g.old_in), + (g.out - g.old_out), + (g.clip_in - g.old_clip_in), + g.track, + false, + true); } else { @@ -1257,7 +1256,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { if (g.transition->secondary_clip != nullptr) new_transition_length >>= 1; ca->append( new ModifyTransitionCommand(is_opening_transition ? c->opening_transition : c->closing_transition, - new_transition_length) + new_transition_length) ); long clip_length = c->length(); @@ -1280,8 +1279,8 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { timeline_in_movement = g.in - g.transition->secondary_clip->timeline_in(); } - g.transition->parent_clip->move(ca, movement, timeline_out_movement, movement, 0, false, true); - g.transition->secondary_clip->move(ca, timeline_in_movement, movement, timeline_in_movement, 0, false, true); + g.transition->parent_clip->Move(ca, movement, timeline_out_movement, movement, g.transition->parent_clip->track(), false, true); + g.transition->secondary_clip->Move(ca, timeline_in_movement, movement, timeline_in_movement, g.transition->secondary_clip->track(), false, true); make_room_for_transition(ca, g.transition->parent_clip, kTransitionOpening, g.in, g.out, false); make_room_for_transition(ca, g.transition->secondary_clip, kTransitionClosing, g.in, g.out, false); @@ -1299,7 +1298,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { timeline_out_movement = g.out - g.transition->parent_clip->timeline_out(); } - c->move(ca, (g.in - g.old_in), timeline_out_movement, (g.clip_in - g.old_clip_in), 0, false, true); + c->Move(ca, (g.in - g.old_in), timeline_out_movement, (g.clip_in - g.old_clip_in), 0, false, true); clip_length -= (g.in - g.old_in); } @@ -1316,7 +1315,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // if transition is going to make the clip bigger, make the clip bigger - c->move(ca, timeline_in_movement, (g.out - g.old_out), timeline_in_movement, 0, false, true); + c->Move(ca, timeline_in_movement, (g.out - g.old_out), timeline_in_movement, 0, false, true); clip_length += (g.out - g.old_out); } @@ -1327,12 +1326,12 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // time to verify the transitions of moved clips - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); // only applies to moving clips, transitions are verified above instead if (g.transition == nullptr) { - ClipPtr c = olive::ActiveSequence->clips.at(g.clip); + Clip* c = g.clip; long new_clip_length = g.out - g.in; @@ -1369,12 +1368,12 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { // for a shared transition, the secondary_clip will always be the closing transition side and // the parent_clip will always be the opening transition side Clip* search_clip = (t == kTransitionOpening) - ? transition->secondary_clip : transition->parent_clip; + ? transition->secondary_clip : transition->parent_clip; - for (int j=0;jghosts.size();j++) { - const Ghost& other_clip_ghost = panel_timeline->ghosts.at(j); + for (int j=0;jghosts.size();j++) { + const Ghost& other_clip_ghost = ParentTimeline()->ghosts.at(j); - if (olive::ActiveSequence->clips.at(other_clip_ghost.clip).get() == search_clip) { + if (other_clip_ghost.clip == search_clip) { // we found the other clip in the current ghosts/selections @@ -1442,9 +1441,9 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } push_undo = true; } - } else if (panel_timeline->selecting || panel_timeline->rect_select_proc) { - } else if (panel_timeline->transition_tool_proc) { - const Ghost& g = panel_timeline->ghosts.at(0); + } else if (ParentTimeline()->selecting || ParentTimeline()->rect_select_proc) { + } else if (ParentTimeline()->transition_tool_proc) { + const Ghost& g = ParentTimeline()->ghosts.at(0); // if the transition is greater than 0 length (if it is 0, we make nothing) if (g.in != g.out) { @@ -1454,13 +1453,9 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { long transition_end = qMax(g.in, g.out); // get clip references from tool's cached data - Clip* open = (panel_timeline->transition_tool_open_clip > -1) - ? olive::ActiveSequence->clips.at(panel_timeline->transition_tool_open_clip).get() - : nullptr; + Clip* open = ParentTimeline()->transition_tool_open_clip; - Clip* close = (panel_timeline->transition_tool_close_clip > -1) - ? olive::ActiveSequence->clips.at(panel_timeline->transition_tool_close_clip).get() - : nullptr; + Clip* close = ParentTimeline()->transition_tool_close_clip; @@ -1476,16 +1471,17 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { ca->append(new AddTransitionCommand(open, close, nullptr, - panel_timeline->transition_tool_meta, + ParentTimeline()->transition_tool_meta, transition_length)); push_undo = true; } - } else if (panel_timeline->splitting) { + } else if (ParentTimeline()->splitting) { bool split = false; - for (int i=0;isplit_tracks.size();i++) { - int split_index = getClipIndexFromCoords(panel_timeline->drag_frame_start, panel_timeline->split_tracks.at(i)); - if (split_index > -1 && panel_timeline->split_clip_and_relink(ca, split_index, panel_timeline->drag_frame_start, !alt)) { + for (int i=0;isplit_tracks.size();i++) { + Clip* split_index = ParentTimeline()->split_tracks.at(i)->GetClipFromPoint(ParentTimeline()->drag_frame_start); + if (split_index != nullptr + && sequence()->SplitClipAtPositions(ca, split_index, {ParentTimeline()->drag_frame_start}, !alt)) { split = true; } } @@ -1495,54 +1491,56 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // remove duplicate selections - panel_timeline->clean_up_selections(olive::ActiveSequence->selections); + sequence()->TidySelections(); + /* if (selection_command != nullptr) { - selection_command->new_data = olive::ActiveSequence->selections; + selection_command->new_data = sequence()->selections; ca->append(selection_command); selection_command = nullptr; push_undo = true; } + */ if (push_undo) { - olive::UndoStack.push(ca); + olive::undo_stack.push(ca); } else { delete ca; } // destroy all ghosts - panel_timeline->ghosts.clear(); + ParentTimeline()->ghosts.clear(); // clear split tracks - panel_timeline->split_tracks.clear(); + ParentTimeline()->split_tracks.clear(); - panel_timeline->selecting = false; - panel_timeline->moving_proc = false; - panel_timeline->moving_init = false; - panel_timeline->splitting = false; - panel_timeline->snapped = false; - panel_timeline->rect_select_init = false; - panel_timeline->rect_select_proc = false; - panel_timeline->transition_tool_init = false; - panel_timeline->transition_tool_proc = false; + ParentTimeline()->selecting = false; + ParentTimeline()->moving_proc = false; + ParentTimeline()->moving_init = false; + ParentTimeline()->splitting = false; + olive::timeline::snapped = false; + ParentTimeline()->rect_select_init = false; + ParentTimeline()->rect_select_proc = false; + ParentTimeline()->transition_tool_init = false; + ParentTimeline()->transition_tool_proc = false; pre_clips.clear(); post_clips.clear(); update_ui(true); } - panel_timeline->hand_moving = false; + ParentTimeline()->hand_moving = false; } } void TimelineView::init_ghosts() { - for (int i=0;ighosts.size();i++) { - Ghost& g = panel_timeline->ghosts[i]; - ClipPtr c = olive::ActiveSequence->clips.at(g.clip); + for (int i=0;ighosts.size();i++) { + Ghost& g = ParentTimeline()->ghosts[i]; + Clip* c = g.clip; g.track = g.old_track = c->track(); g.clip_in = g.old_clip_in = c->clip_in(); - if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIP) { g.clip_in = g.old_clip_in = c->clip_in(true); g.in = g.old_in = c->timeline_in(true); g.out = g.old_out = c->timeline_out(true); @@ -1566,12 +1564,14 @@ void TimelineView::init_ghosts() { // used for trim ops g.media_length = c->media_length(); } - for (int i=0;iselections.size();i++) { - Selection& s = olive::ActiveSequence->selections[i]; + /* + for (int i=0;iselections.size();i++) { + Selection& s = sequence()->selections[i]; s.old_in = s.in; s.old_out = s.out; s.old_track = s.track; } + */ } void validate_transitions(Clip* c, int transition_type, long& frame_diff) { @@ -1605,52 +1605,54 @@ void validate_transitions(Clip* c, int transition_type, long& frame_diff) { } void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { - int effective_tool = panel_timeline->tool; - if (panel_timeline->importing || panel_timeline->creating) effective_tool = TIMELINE_TOOL_POINTER; + int effective_tool = olive::timeline::current_tool; + if (ParentTimeline()->importing || ParentTimeline()->creating) effective_tool = olive::timeline::TIMELINE_TOOL_POINTER; - int mouse_track = getTrackFromScreenPoint(mouse_pos.y()); - long frame_diff = (lock_frame) ? 0 : panel_timeline->getTimelineFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start; - int track_diff = ((effective_tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != kTransitionNone) && !panel_timeline->importing) ? 0 : mouse_track - panel_timeline->drag_track_start; + Track* mouse_track = getTrackFromScreenPoint(mouse_pos.y()); + long frame_diff = (lock_frame) ? 0 : ParentTimeline()->getTimelineFrameFromScreenPoint(mouse_pos.x()) - ParentTimeline()->drag_frame_start; + int track_diff = ((effective_tool == olive::timeline::TIMELINE_TOOL_SLIDE || ParentTimeline()->transition_select != kTransitionNone) && !ParentTimeline()->importing) ? 0 : mouse_track - ParentTimeline()->drag_track_start; long validator; long earliest_in_point = LONG_MAX; // first try to snap long fm; - if (effective_tool != TIMELINE_TOOL_SLIP) { + if (effective_tool != olive::timeline::TIMELINE_TOOL_SLIP) { // slipping doesn't move the clips so we don't bother snapping for it - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); // snap ghost's in point - if ((panel_timeline->tool != TIMELINE_TOOL_TRANSITION && panel_timeline->trim_target == -1) + if ((olive::timeline::current_tool != olive::timeline::TIMELINE_TOOL_TRANSITION && ParentTimeline()->trim_target == nullptr) || g.trim_type == olive::timeline::TRIM_IN - || panel_timeline->transition_tool_open_clip > -1) { + || ParentTimeline()->transition_tool_open_clip != nullptr) { fm = g.old_in + frame_diff; - if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { + if (sequence()->SnapPoint(&fm, ParentTimeline()->zoom, true, true, true)) { frame_diff = fm - g.old_in; break; } } // snap ghost's out point - if ((panel_timeline->tool != TIMELINE_TOOL_TRANSITION && panel_timeline->trim_target == -1) + if ((olive::timeline::current_tool != olive::timeline::TIMELINE_TOOL_TRANSITION && ParentTimeline()->trim_target == nullptr) || g.trim_type == olive::timeline::TRIM_OUT - || panel_timeline->transition_tool_close_clip > -1) { + || ParentTimeline()->transition_tool_close_clip != nullptr) { fm = g.old_out + frame_diff; - if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { + if (sequence()->SnapPoint(&fm, ParentTimeline()->zoom, true, true, true)) { frame_diff = fm - g.old_out; break; } } // if the ghost is attached to a clip, snap its markers too - if (panel_timeline->trim_target == -1 && g.clip >= 0 && panel_timeline->tool != TIMELINE_TOOL_TRANSITION) { - ClipPtr c = olive::ActiveSequence->clips.at(g.clip); + if (ParentTimeline()->trim_target == nullptr + && g.clip != nullptr + && olive::timeline::current_tool != olive::timeline::TIMELINE_TOOL_TRANSITION) { + Clip* c = g.clip; for (int j=0;jget_markers().size();j++) { long marker_real_time = c->get_markers().at(j).frame + c->timeline_in() - c->clip_in(); fm = marker_real_time + frame_diff; - if (panel_timeline->snap_to_timeline(&fm, true, true, true)) { + if (sequence()->SnapPoint(&fm, ParentTimeline()->zoom, true, true, true)) { frame_diff = fm - marker_real_time; break; } @@ -1659,26 +1661,26 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } - bool clips_are_movable = (effective_tool == TIMELINE_TOOL_POINTER || effective_tool == TIMELINE_TOOL_SLIDE); + bool clips_are_movable = (effective_tool == olive::timeline::TIMELINE_TOOL_POINTER || effective_tool == olive::timeline::TIMELINE_TOOL_SLIDE); // validate ghosts long temp_frame_diff = frame_diff; // cache to see if we change it (thus cancelling any snap) - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); Clip* c = nullptr; - if (g.clip != -1) { - c = olive::ActiveSequence->clips.at(g.clip).get(); + if (g.clip != nullptr) { + c = g.clip; } const FootageStream* ms = nullptr; - if (g.clip != -1 && c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { + if (g.clip != nullptr && c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { ms = c->media_stream(); } // validate ghosts for trimming - if (panel_timeline->creating) { + if (ParentTimeline()->creating) { // i feel like we might need something here but we haven't so far? - } else if (effective_tool == TIMELINE_TOOL_SLIP) { + } else if (effective_tool == olive::timeline::TIMELINE_TOOL_SLIP) { if ((c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) || (ms != nullptr && !ms->infinite_length)) { // prevent slip moving a clip below 0 clip_in @@ -1696,7 +1698,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (validator < 1) frame_diff -= (1 - validator); // prevent timeline in from going below 0 - if (effective_tool != TIMELINE_TOOL_RIPPLE) { + if (effective_tool != olive::timeline::TIMELINE_TOOL_RIPPLE) { validator = g.old_in + frame_diff; if (validator < 0) frame_diff -= validator; } @@ -1747,21 +1749,21 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // ripple ops - if (effective_tool == TIMELINE_TOOL_RIPPLE) { + if (effective_tool == olive::timeline::TIMELINE_TOOL_RIPPLE) { for (int j=0;jtrim_type == olive::timeline::TRIM_IN) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { validator = post->timeline_in() - frame_diff; if (validator < 0) frame_diff += validator; } // prevent any post-clips colliding with pre-clips for (int k=0;ktrack() == post->track()) { - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { validator = post->timeline_in() - frame_diff - pre->timeline_out(); if (validator < 0) frame_diff += validator; } else { @@ -1810,7 +1812,8 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // prevent clips from crossing tracks - if (same_sign(g.old_track, panel_timeline->drag_track_start)) { + /* + if (same_sign(g.old_track, ParentTimeline()->drag_track_start)) { while (!same_sign(g.old_track, g.old_track + track_diff)) { if (g.old_track < 0) { track_diff--; @@ -1819,16 +1822,17 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } } - } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { - if (panel_timeline->transition_tool_open_clip == -1 - || panel_timeline->transition_tool_close_clip == -1) { + */ + } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { + if (ParentTimeline()->transition_tool_open_clip == nullptr + || ParentTimeline()->transition_tool_close_clip == nullptr) { validate_transitions(c, g.media_stream, frame_diff); } else { // open transition clip - Clip* otc = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_open_clip).get(); + Clip* otc = ParentTimeline()->transition_tool_open_clip; // close transition clip - Clip* ctc = olive::ActiveSequence->clips.at(panel_timeline->transition_tool_close_clip).get(); + Clip* ctc = ParentTimeline()->transition_tool_close_clip; if (g.media_stream == kTransitionClosing) { // swap @@ -1852,21 +1856,21 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // if the above validation changed the frame movement, it's unlikely we're still snapped if (temp_frame_diff != frame_diff) { - panel_timeline->snapped = false; + olive::timeline::snapped = false; } // apply changes to ghosts - for (int i=0;ighosts.size();i++) { - Ghost& g = panel_timeline->ghosts[i]; + for (int i=0;ighosts.size();i++) { + Ghost& g = ParentTimeline()->ghosts[i]; - if (effective_tool == TIMELINE_TOOL_SLIP) { + if (effective_tool == olive::timeline::TIMELINE_TOOL_SLIP) { g.clip_in = g.old_clip_in - frame_diff; } else if (g.trim_type != olive::timeline::TRIM_NONE) { long ghost_diff = frame_diff; // prevent trimming clips from overlapping each other - for (int j=0;jghosts.size();j++) { - const Ghost& comp = panel_timeline->ghosts.at(j); + for (int j=0;jghosts.size();j++) { + const Ghost& comp = ParentTimeline()->ghosts.at(j); if (i != j && g.track == comp.track) { long validator; if (g.trim_type == olive::timeline::TRIM_IN && comp.out < g.out) { @@ -1896,13 +1900,14 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.out = g.old_out + frame_diff; if (g.transition != nullptr - && g.transition == olive::ActiveSequence->clips.at(g.clip)->opening_transition) { + && g.transition == g.clip->opening_transition) { g.clip_in = g.old_clip_in + frame_diff; } - if (panel_timeline->importing) { - if ((panel_timeline->video_ghosts && mouse_track < 0) - || (panel_timeline->audio_ghosts && mouse_track >= 0)) { + if (ParentTimeline()->importing) { + /* + if ((ParentTimeline()->video_ghosts && mouse_track->type() == Track::kTypeVideo) + || (ParentTimeline()->audio_ghosts && mouse_track->type() == Track::kTypeAudio)) { int abs_track_diff = abs(track_diff); if (g.old_track < 0) { // clip is video g.track -= abs_track_diff; @@ -1910,15 +1915,17 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.track += abs_track_diff; } } - } else if (same_sign(g.old_track, panel_timeline->drag_track_start)) { + */ + g.track = track_list_->First(); + } else if (g.old_track->type() == ParentTimeline()->drag_track_start->type()) { g.track += track_diff; } - } else if (effective_tool == TIMELINE_TOOL_TRANSITION) { - if (panel_timeline->transition_tool_open_clip > -1 - && panel_timeline->transition_tool_close_clip > -1) { + } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { + if (ParentTimeline()->transition_tool_open_clip != nullptr + && ParentTimeline()->transition_tool_close_clip != nullptr) { g.in = g.old_in - frame_diff; g.out = g.old_out + frame_diff; - } else if (panel_timeline->transition_tool_open_clip == g.clip) { + } else if (ParentTimeline()->transition_tool_open_clip == g.clip) { g.out = g.old_out + frame_diff; } else { g.in = g.old_in + frame_diff; @@ -1929,23 +1936,24 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // apply changes to selections - if (effective_tool != TIMELINE_TOOL_SLIP && !panel_timeline->importing && !panel_timeline->creating) { - for (int i=0;iselections.size();i++) { - Selection& s = olive::ActiveSequence->selections[i]; - if (panel_timeline->trim_target > -1) { - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + /* + if (effective_tool != olive::timeline::TIMELINE_TOOL_SLIP && !ParentTimeline()->importing && !ParentTimeline()->creating) { + for (int i=0;iselections.size();i++) { + Selection& s = sequence()->selections[i]; + if (ParentTimeline()->trim_target > -1) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { s.in = s.old_in + frame_diff; } else { s.out = s.old_out + frame_diff; } } else if (clips_are_movable) { - for (int i=0;iselections.size();i++) { - Selection& s = olive::ActiveSequence->selections[i]; + for (int i=0;iselections.size();i++) { + Selection& s = sequence()->selections[i]; s.in = s.old_in + frame_diff; s.out = s.old_out + frame_diff; s.track = s.old_track; - if (panel_timeline->importing) { + if (ParentTimeline()->importing) { int abs_track_diff = abs(track_diff); if (s.old_track < 0) { s.track -= abs_track_diff; @@ -1953,23 +1961,25 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { s.track += abs_track_diff; } } else { - if (same_sign(s.track, panel_timeline->drag_track_start)) s.track += track_diff; + if (same_sign(s.track, ParentTimeline()->drag_track_start)) s.track += track_diff; } } } } } + */ - if (panel_timeline->importing) { - QToolTip::showText(mapToGlobal(mouse_pos), frame_to_timecode(earliest_in_point, olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate)); + if (ParentTimeline()->importing) { + QToolTip::showText(mapToGlobal(mouse_pos), frame_to_timecode(earliest_in_point, olive::config.timecode_view, sequence()->frame_rate)); } else { - QString tip = ((frame_diff < 0) ? "-" : "+") + frame_to_timecode(qAbs(frame_diff), olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate); - if (panel_timeline->trim_target > -1) { + QString tip = ((frame_diff < 0) ? "-" : "+") + frame_to_timecode(qAbs(frame_diff), olive::config.timecode_view, sequence()->frame_rate); + + if (ParentTimeline()->trim_target != nullptr) { // find which clip is being moved const Ghost* g = nullptr; - for (int i=0;ighosts.size();i++) { - if (panel_timeline->ghosts.at(i).clip == panel_timeline->trim_target) { - g = &panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + if (ParentTimeline()->ghosts.at(i).clip == ParentTimeline()->trim_target) { + g = &ParentTimeline()->ghosts.at(i); break; } } @@ -1977,14 +1987,15 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (g != nullptr) { tip += " " + tr("Duration:") + " "; long len = (g->old_out-g->old_in); - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { len -= frame_diff; } else { len += frame_diff; } - tip += frame_to_timecode(len, olive::CurrentConfig.timecode_view, olive::ActiveSequence->frame_rate); + tip += frame_to_timecode(len, olive::config.timecode_view, sequence()->frame_rate); } } + QToolTip::showText(mapToGlobal(mouse_pos), tip); } } @@ -1993,85 +2004,87 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // interrupt any potential tooltip about to show tooltip_timer.stop(); - if (olive::ActiveSequence != nullptr) { + if (sequence() != nullptr) { bool alt = (event->modifiers() & Qt::AltModifier); // store current frame/track corresponding to the cursor - panel_timeline->cursor_frame = panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x()); - panel_timeline->cursor_track = getTrackFromScreenPoint(event->pos().y()); + ParentTimeline()->cursor_frame = ParentTimeline()->getTimelineFrameFromScreenPoint(event->pos().x()); + ParentTimeline()->cursor_track = getTrackFromScreenPoint(event->pos().y()); // if holding the mouse button down, let's scroll to that location - if (event->buttons() != 0 && panel_timeline->tool != TIMELINE_TOOL_HAND) { - panel_timeline->scroll_to_frame(panel_timeline->cursor_frame); + if (event->buttons() != 0 && olive::timeline::current_tool != olive::timeline::TIMELINE_TOOL_HAND) { + ParentTimeline()->scroll_to_frame(ParentTimeline()->cursor_frame); } // determine if the action should be "inserting" rather than "overwriting" // Default behavior is to replace/overwrite clips under any clips we're dropping over them. Inserting will // split and move existing clips at the drop point to make space for the drop - panel_timeline->move_insert = ((event->modifiers() & Qt::ControlModifier) - && (panel_timeline->tool == TIMELINE_TOOL_POINTER - || panel_timeline->importing - || panel_timeline->creating)); + ParentTimeline()->move_insert = ((event->modifiers() & Qt::ControlModifier) + && (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER + || ParentTimeline()->importing + || ParentTimeline()->creating)); // if we're not currently resizing already, default track resizing to false (we'll set it to true later if // the user is still hovering over a track line) - if (!panel_timeline->moving_init) { + if (!ParentTimeline()->moving_init) { track_resizing = false; } // if the current tool uses an on-screen visible cursor, we snap the cursor to the timeline if (current_tool_shows_cursor()) { - panel_timeline->snap_to_timeline(&panel_timeline->cursor_frame, + sequence()->SnapPoint(&ParentTimeline()->cursor_frame, + ParentTimeline()->zoom, - // only snap to the playhead if the edit tool doesn't force the playhead to - // follow it (or if we're not selecting since that means the playhead is - // static at the moment) - !olive::CurrentConfig.edit_tool_also_seeks || !panel_timeline->selecting, + // only snap to the playhead if the edit tool doesn't force the playhead to + // follow it (or if we're not selecting since that means the playhead is + // static at the moment) + !olive::config.edit_tool_also_seeks || !ParentTimeline()->selecting, - true, - true); + true, + true); } - if (panel_timeline->selecting) { + if (ParentTimeline()->selecting) { + /* // get number of selections based on tracks in selection area - int selection_tool_count = 1 + qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start) - qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start); + int selection_tool_count = 1 + qMax(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start) - qMin(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start); // add count to selection offset for the total number of selection objects // (offset is usually 0, unless the user is holding shift in which case we add to existing selections) - int selection_count = selection_tool_count + panel_timeline->selection_offset; + int selection_count = selection_tool_count + ParentTimeline()->selection_offset; // resize selection object array to new count - if (olive::ActiveSequence->selections.size() != selection_count) { - olive::ActiveSequence->selections.resize(selection_count); + if (sequence()->selections.size() != selection_count) { + sequence()->selections.resize(selection_count); } // loop through tracks in selection area and adjust them accordingly - int minimum_selection_track = qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start); - int maximum_selection_track = qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start); - long selection_in = qMin(panel_timeline->drag_frame_start, panel_timeline->cursor_frame); - long selection_out = qMax(panel_timeline->drag_frame_start, panel_timeline->cursor_frame); - for (int i=panel_timeline->selection_offset;iselections[i]; - s.track = minimum_selection_track + i - panel_timeline->selection_offset; + int minimum_selection_track = qMin(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start); + int maximum_selection_track = qMax(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start); + long selection_in = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + long selection_out = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + for (int i=ParentTimeline()->selection_offset;iselections[i]; + s.track = minimum_selection_track + i - ParentTimeline()->selection_offset; s.in = selection_in; s.out = selection_out; } // If the config is set to select links as well with the edit tool - if (olive::CurrentConfig.edit_tool_selects_links) { + if (olive::config.edit_tool_selects_links) { // find which clips are selected - for (int j=0;jclips.size();j++) { + for (int j=0;jclips.size();j++) { - Clip* c = olive::ActiveSequence->clips.at(j).get(); + Clip* c = sequence()->clips.at(j).get(); if (c != nullptr && c->IsSelected(false)) { // loop through linked clips for (int k=0;klinked.size();k++) { - ClipPtr link = olive::ActiveSequence->clips.at(c->linked.at(k)); + ClipPtr link = sequence()->clips.at(c->linked.at(k)); // see if one of the selections is already covering this track if (!(link->track() >= minimum_selection_track @@ -2082,7 +2095,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { link_sel.in = selection_in; link_sel.out = selection_out; link_sel.track = link->track(); - olive::ActiveSequence->selections.append(link_sel); + sequence()->selections.append(link_sel); } @@ -2093,40 +2106,41 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } // if the config is set to seek with the edit too, do so now - if (olive::CurrentConfig.edit_tool_also_seeks) { - panel_sequence_viewer->seek(qMin(panel_timeline->drag_frame_start, panel_timeline->cursor_frame)); + if (olive::config.edit_tool_also_seeks) { + panel_sequence_viewer->seek(qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame)); } else { // if not, repaint (seeking will trigger a repaint) - panel_timeline->repaint_timeline(); + ParentTimeline()->repaint_timeline(); } + */ - } else if (panel_timeline->hand_moving) { + } else if (ParentTimeline()->hand_moving) { // if we're hand moving, we'll be adding values directly to the scrollbars // the scrollbars trigger repaints when they scroll, which is unnecessary here so we block them - panel_timeline->block_repaints = true; - panel_timeline->horizontalScrollBar->setValue(panel_timeline->horizontalScrollBar->value() + panel_timeline->drag_x_start - event->pos().x()); - scrollBar->setValue(scrollBar->value() + panel_timeline->drag_y_start - event->pos().y()); - panel_timeline->block_repaints = false; + ParentTimeline()->block_repaints = true; + ParentTimeline()->horizontalScrollBar->setValue(ParentTimeline()->horizontalScrollBar->value() + ParentTimeline()->drag_x_start - event->pos().x()); + scrollBar->setValue(scrollBar->value() + ParentTimeline()->drag_y_start - event->pos().y()); + ParentTimeline()->block_repaints = false; // finally repaint - panel_timeline->repaint_timeline(); + ParentTimeline()->repaint_timeline(); // store current cursor position for next hand move event - panel_timeline->drag_x_start = event->pos().x(); - panel_timeline->drag_y_start = event->pos().y(); + ParentTimeline()->drag_x_start = event->pos().x(); + ParentTimeline()->drag_y_start = event->pos().y(); - } else if (panel_timeline->moving_init) { + } else if (ParentTimeline()->moving_init) { if (track_resizing) { // get cursor movement - int diff = (event->pos().y() - panel_timeline->drag_y_start); + int diff = (event->pos().y() - ParentTimeline()->drag_y_start); // add it to the current track height - int new_height = panel_timeline->GetTrackHeight(track_target); - if (bottom_align) { + int new_height = track_target->height(); + if (alignment_ == olive::timeline::kAlignmentBottom) { new_height -= diff; } else { new_height += diff; @@ -2136,13 +2150,13 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { new_height = qMax(new_height, olive::timeline::kTrackMinHeight); // set the track height - panel_timeline->SetTrackHeight(track_target, new_height); + track_target->set_height(new_height); // store current cursor position for next track resize event - panel_timeline->drag_y_start = event->pos().y(); + ParentTimeline()->drag_y_start = event->pos().y(); update(); - } else if (panel_timeline->moving_proc) { + } else if (ParentTimeline()->moving_proc) { // we're currently dragging ghosts update_ghosts(event->pos(), event->modifiers() & Qt::ShiftModifier); @@ -2153,9 +2167,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // actually apply it to the clips (in mouseReleaseEvent) // loop through clips for any currently selected - for (int i=0;iclips.size();i++) { + QVector partially_selected_clips = sequence()->SelectedClips(false); + for (int i=0;iclips.at(i).get(); + Clip* c = partially_selected_clips.at(i); if (c != nullptr) { Ghost g; @@ -2166,30 +2181,16 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // check if a transition is selected (prioritize transition selection) // (only the pointer tool supports moving transitions) - if (panel_timeline->tool == TIMELINE_TOOL_POINTER + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER && (c->opening_transition != nullptr || c->closing_transition != nullptr)) { // check if any selections contain a whole transition - for (int j=0;jselections.size();j++) { - - const Selection& s = olive::ActiveSequence->selections.at(j); - - if (s.track == c->track()) { - if (selection_contains_transition(s, c, kTransitionOpening)) { - - g.transition = c->opening_transition; - add = true; - break; - - } else if (selection_contains_transition(s, c, kTransitionClosing)) { - - g.transition = c->closing_transition; - add = true; - break; - - } - } - + if (c->IsTransitionSelected(kTransitionOpening)) { + g.transition = c->opening_transition; + add = true; + } else if (c->IsTransitionSelected(kTransitionClosing)) { + g.transition = c->closing_transition; + add = true; } } @@ -2204,8 +2205,8 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (g.transition != nullptr) { // transition may be a dual transition, check if it's already been added elsewhere - for (int j=0;jghosts.size();j++) { - if (panel_timeline->ghosts.at(j).transition == g.transition) { + for (int j=0;jghosts.size();j++) { + if (ParentTimeline()->ghosts.at(j).transition == g.transition) { add = false; break; } @@ -2214,61 +2215,52 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } if (add) { - g.clip = i; - g.trim_type = panel_timeline->trim_type; - panel_timeline->ghosts.append(g); + g.clip = c; + g.trim_type = ParentTimeline()->trim_type; + ParentTimeline()->ghosts.append(g); } } } } - if (panel_timeline->tool == TIMELINE_TOOL_SLIDE) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIDE) { // for the slide tool, we add the surrounding clips as ghosts that are getting trimmed the opposite way // store original array size since we'll be adding to it - int ghost_arr_size = panel_timeline->ghosts.size(); + int ghost_arr_size = ParentTimeline()->ghosts.size(); // loop through clips for any that are "touching" the selected clips - for (int j=0;jclips.size();j++) { + for (int i=0;ighosts.at(i).clip; - ClipPtr c = olive::ActiveSequence->clips.at(j); - if (c != nullptr) { + Clip* pre_clip = ghost_clip->track()->GetClipFromPoint(ghost_clip->timeline_in() - 1); + Clip* post_clip = ghost_clip->track()->GetClipFromPoint(ghost_clip->timeline_out() + 1); - for (int i=0;ighosts[i]; - g.trim_type = olive::timeline::TRIM_NONE; // the selected clips will be moving, not trimming - - ClipPtr ghost_clip = olive::ActiveSequence->clips.at(g.clip); - - if (c->track() == ghost_clip->track()) { - - // see if this clip is currently selected, if so we won't add it as a "touching" clip - bool found = false; - for (int k=0;kghosts.at(k).clip == j) { - found = true; - break; - } - } - - if (!found) { // the clip is not currently selected - - // check if this clip is indeed touching - bool is_in = (c->timeline_in() == ghost_clip->timeline_out()); - if (is_in || c->timeline_out() == ghost_clip->timeline_in()) { - Ghost gh; - gh.transition = nullptr; - gh.clip = j; - gh.trim_type = is_in ? olive::timeline::TRIM_IN : olive::timeline::TRIM_OUT; - panel_timeline->ghosts.append(gh); - } - } - } + // Check if this clip is already in the ghosts, in which case don't add it + for (int j=0;jghosts.at(j).clip == pre_clip) { + pre_clip = nullptr; + } else if (ParentTimeline()->ghosts.at(j).clip == post_clip) { + post_clip = nullptr; } } + + Ghost gh; + gh.transition = nullptr; + + if (pre_clip != nullptr) { + gh.clip = pre_clip; + gh.trim_type = olive::timeline::TRIM_OUT; + ParentTimeline()->ghosts.append(gh); + } + + if (post_clip != nullptr) { + gh.clip = post_clip; + gh.trim_type = olive::timeline::TRIM_IN; + ParentTimeline()->ghosts.append(gh); + } } } @@ -2276,18 +2268,18 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { init_ghosts(); // if the ripple tool is selected, prepare to ripple - if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RIPPLE) { long axis = LONG_MAX; // find the earliest point within the selected clips which is the point we'll ripple around // also store the currently selected clips so we don't have to do it later - QVector ghost_clips; - ghost_clips.resize(panel_timeline->ghosts.size()); + QVector ghost_clips; + ghost_clips.resize(ParentTimeline()->ghosts.size()); - for (int i=0;ighosts.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(panel_timeline->ghosts.at(i).clip); - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { + for (int i=0;ighosts.size();i++) { + Clip* c = ParentTimeline()->ghosts.at(i).clip; + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { axis = qMin(axis, c->timeline_in()); } else { axis = qMin(axis, c->timeline_out()); @@ -2298,19 +2290,20 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } // loop through clips and cache which are earlier than the axis and which after after - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr && !ghost_clips.contains(c)) { + QVector sequence_clips = sequence()->GetAllClips(); + for (int i=0;itimeline_in() >= axis); // construct the list of pre and post clips - QVector& clip_list = (clip_is_post) ? post_clips : pre_clips; + QVector& clip_list = (clip_is_post) ? post_clips : pre_clips; // check if there's already a clip in this list on this track, and if this clip is closer or not bool found = false; for (int j=0;jtrack() == c->track()) { @@ -2335,26 +2328,28 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } // store selections - selection_command = new SetSelectionsCommand(olive::ActiveSequence.get()); - selection_command->old_data = olive::ActiveSequence->selections; + /* + selection_command = new SetSelectionsCommand(sequence().get()); + selection_command->old_data = sequence()->selections; + */ // ready to start moving clips - panel_timeline->moving_proc = true; + ParentTimeline()->moving_proc = true; } update_ui(false); - } else if (panel_timeline->splitting) { + } else if (ParentTimeline()->splitting) { // get the range of tracks currently dragged - int track_start = qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start); - int track_end = qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start); + int track_start = qMin(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()); + int track_end = qMax(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()); int track_size = 1 + track_end - track_start; // set tracks to be split - panel_timeline->split_tracks.resize(track_size); + ParentTimeline()->split_tracks.resize(track_size); for (int i=0;isplit_tracks[i] = track_start + i; + ParentTimeline()->split_tracks[i] = ParentTimeline()->cursor_track->track_list()->TrackAt(track_start + i); } // if alt isn't being held, also add the tracks of the clip's links @@ -2362,17 +2357,16 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { for (int i=0;idrag_frame_start, panel_timeline->split_tracks[i]); + Clip* clip = ParentTimeline()->split_tracks[i]->GetClipFromPoint(ParentTimeline()->drag_frame_start); - if (clip_index > -1) { - ClipPtr clip = olive::ActiveSequence->clips.at(clip_index); + if (clip != nullptr) { for (int j=0;jlinked.size();j++) { - ClipPtr link = olive::ActiveSequence->clips.at(clip->linked.at(j)); + Clip* link = clip->linked.at(j); // if this clip isn't already in the list of tracks to split - if (link->track() < track_start || link->track() > track_end) { - panel_timeline->split_tracks.append(link->track()); + if (link->track()->Index() < track_start || link->track()->Index() > track_end) { + ParentTimeline()->split_tracks.append(link->track()); } } @@ -2382,108 +2376,95 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { update_ui(false); - } else if (panel_timeline->rect_select_init) { + } else if (ParentTimeline()->rect_select_init) { // set if the user started dragging at point where there was no clip - if (panel_timeline->rect_select_proc) { + if (ParentTimeline()->rect_select_proc) { // we're currently rectangle selecting // set the right/bottom coords to the current mouse position // (left/top were set to the starting drag position earlier) - panel_timeline->rect_select_rect.setRight(event->pos().x()); + ParentTimeline()->rect_select_rect.setRight(event->pos().x()); - if (bottom_align) { - panel_timeline->rect_select_rect.setBottom(event->pos().y() - height()); + if (alignment_ == olive::timeline::kAlignmentBottom) { + ParentTimeline()->rect_select_rect.setBottom(event->pos().y() - height()); } else { - panel_timeline->rect_select_rect.setBottom(event->pos().y()); + ParentTimeline()->rect_select_rect.setBottom(event->pos().y()); } - long frame_min = qMin(panel_timeline->drag_frame_start, panel_timeline->cursor_frame); - long frame_max = qMax(panel_timeline->drag_frame_start, panel_timeline->cursor_frame); + long frame_min = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + long frame_max = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); - int track_min = qMin(panel_timeline->drag_track_start, panel_timeline->cursor_track); - int track_max = qMax(panel_timeline->drag_track_start, panel_timeline->cursor_track); + int track_min = qMin(ParentTimeline()->drag_track_start->Index(), ParentTimeline()->cursor_track->Index()); + int track_max = qMax(ParentTimeline()->drag_track_start->Index(), ParentTimeline()->cursor_track->Index()); // determine which clips are in this rectangular selection - QVector selected_clips; - for (int i=0;iclips.size();i++) { - ClipPtr clip = olive::ActiveSequence->clips.at(i); - if (clip != nullptr && - clip->track() >= track_min && - clip->track() <= track_max && - !(clip->timeline_in() < frame_min && clip->timeline_out() < frame_min) && - !(clip->timeline_in() > frame_max && clip->timeline_out() > frame_max)) { + QVector selected_clips; + for (int j=0;jTrackCount();j++) { + Track* track = track_list_->TrackAt(j); - // create a group of the clip (and its links if alt is not pressed) - QVector session_clips; - session_clips.append(clip); + for (int i=0;iClipCount();i++) { + Clip* clip = track->GetClip(i).get(); + if (clip->track()->Index() >= track_min && + clip->track()->Index() <= track_max && + !(clip->timeline_in() < frame_min && clip->timeline_out() < frame_min) && + !(clip->timeline_in() > frame_max && clip->timeline_out() > frame_max)) { - if (!alt) { - for (int j=0;jlinked.size();j++) { - session_clips.append(olive::ActiveSequence->clips.at(clip->linked.at(j))); + // create a group of the clip (and its links if alt is not pressed) + QVector session_clips; + session_clips.append(clip); + + if (!alt) { + session_clips.append(clip->linked); } - } - // for each of these clips, see if clip has already been added - - // this can easily happen due to adding linked clips - for (int j=0;jselections.resize(selected_clips.size() + panel_timeline->selection_offset); for (int i=0;iselections[i+panel_timeline->selection_offset]; - ClipPtr clip = selected_clips.at(i); - s.old_in = s.in = clip->timeline_in(); - s.old_out = s.out = clip->timeline_out(); - s.old_track = s.track = clip->track(); + selected_clips.at(i)->track()->SelectClip(selected_clips.at(i)); } - panel_timeline->repaint_timeline(); + ParentTimeline()->repaint_timeline(); } else { // set up rectangle selecting - panel_timeline->rect_select_rect.setX(event->pos().x()); + ParentTimeline()->rect_select_rect.setX(event->pos().x()); - if (bottom_align) { + if (alignment_ == olive::timeline::kAlignmentBottom) { // bottom aligned widgets start with 0 at the bottom and go down to a negative number - panel_timeline->rect_select_rect.setY(event->pos().y() - height()); + ParentTimeline()->rect_select_rect.setY(event->pos().y() - height()); } else { - panel_timeline->rect_select_rect.setY(event->pos().y()); + ParentTimeline()->rect_select_rect.setY(event->pos().y()); } - panel_timeline->rect_select_rect.setWidth(0); - panel_timeline->rect_select_rect.setHeight(0); + ParentTimeline()->rect_select_rect.setWidth(0); + ParentTimeline()->rect_select_rect.setHeight(0); - panel_timeline->rect_select_proc = true; + ParentTimeline()->rect_select_proc = true; } } else if (current_tool_shows_cursor()) { // we're not currently performing an action (click is not pressed), but redraw because we have an on-screen cursor - panel_timeline->repaint_timeline(); + ParentTimeline()->repaint_timeline(); - } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || - panel_timeline->tool == TIMELINE_TOOL_RIPPLE || - panel_timeline->tool == TIMELINE_TOOL_ROLLING) { + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER || + olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RIPPLE || + olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_ROLLING) { // hide any tooltip that may be currently showing QToolTip::hideText(); @@ -2497,8 +2478,8 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // threshold around a trim point that the cursor can be within and still considered "trimming" int lim = 5; - long mouse_frame_lower = panel_timeline->getTimelineFrameFromScreenPoint(pos.x()-lim)-1; - long mouse_frame_upper = panel_timeline->getTimelineFrameFromScreenPoint(pos.x()+lim)+1; + long mouse_frame_lower = ParentTimeline()->getTimelineFrameFromScreenPoint(pos.x()-lim)-1; + long mouse_frame_upper = ParentTimeline()->getTimelineFrameFromScreenPoint(pos.x()+lim)+1; // used to determine whether we the cursor found a trim point or not bool found = false; @@ -2510,135 +2491,126 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // (and more specifically, whether another point is closer or not) int closeness = INT_MAX; - // while we loop through the clips, we cache the maximum/minimum tracks in this sequence - int min_track = INT_MAX; - int max_track = INT_MIN; - // we default to selecting no transition, but set this accordingly if the cursor is on a transition - panel_timeline->transition_select = kTransitionNone; + ParentTimeline()->transition_select = kTransitionNone; // we also default to no trimming which may be changed later in this function - panel_timeline->trim_type = olive::timeline::TRIM_NONE; + ParentTimeline()->trim_type = olive::timeline::TRIM_NONE; // set currently trimming clip to -1 (aka null) - panel_timeline->trim_target = -1; + ParentTimeline()->trim_target = nullptr; // loop through current clips in the sequence - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr) { + QVector sequence_clips = sequence()->GetAllClips(); + for (int i=0;itrack()); - max_track = qMax(max_track, c->track()); + // if this clip is on the same track the mouse is + if (c->track() == ParentTimeline()->cursor_track) { - // if this clip is on the same track the mouse is - if (c->track() == panel_timeline->cursor_track) { + // if this cursor is inside the boundaries of this clip (hovering over the clip) + if (ParentTimeline()->cursor_frame >= c->timeline_in() && + ParentTimeline()->cursor_frame <= c->timeline_out()) { - // if this cursor is inside the boundaries of this clip (hovering over the clip) - if (panel_timeline->cursor_frame >= c->timeline_in() && - panel_timeline->cursor_frame <= c->timeline_out()) { + // acknowledge that we are hovering over a clip + cursor_contains_clip = true; - // acknowledge that we are hovering over a clip - cursor_contains_clip = true; + // start a timer to show a tooltip about this clip + tooltip_timer.start(); + tooltip_clip = c; - // start a timer to show a tooltip about this clip - tooltip_timer.start(); - tooltip_clip = i; + // check if the cursor is specifically hovering over one of the clip's transitions + if (c->opening_transition != nullptr + && ParentTimeline()->cursor_frame <= c->timeline_in() + c->opening_transition->get_true_length()) { - // check if the cursor is specifically hovering over one of the clip's transitions - if (c->opening_transition != nullptr - && panel_timeline->cursor_frame <= c->timeline_in() + c->opening_transition->get_true_length()) { + ParentTimeline()->transition_select = kTransitionOpening; - panel_timeline->transition_select = kTransitionOpening; + } else if (c->closing_transition != nullptr + && ParentTimeline()->cursor_frame >= c->timeline_out() - c->closing_transition->get_true_length()) { - } else if (c->closing_transition != nullptr - && panel_timeline->cursor_frame >= c->timeline_out() - c->closing_transition->get_true_length()) { + ParentTimeline()->transition_select = kTransitionClosing; - panel_timeline->transition_select = kTransitionClosing; - - } } + } - // is the cursor hovering around the clip's IN point? - if (c->timeline_in() > mouse_frame_lower && c->timeline_in() < mouse_frame_upper) { + // is the cursor hovering around the clip's IN point? + if (c->timeline_in() > mouse_frame_lower && c->timeline_in() < mouse_frame_upper) { - // test how close this IN point is to the cursor - int nc = qAbs(c->timeline_in() + 1 - panel_timeline->cursor_frame); + // test how close this IN point is to the cursor + int nc = qAbs(c->timeline_in() + 1 - ParentTimeline()->cursor_frame); - // and test whether it's closer than the last in/out point we found - if (nc < closeness) { + // and test whether it's closer than the last in/out point we found + if (nc < closeness) { - // if so, this is the point we'll make active for now (unless we find a closer one later) - panel_timeline->trim_target = i; - panel_timeline->trim_type = olive::timeline::TRIM_IN; - closeness = nc; - found = true; + // if so, this is the point we'll make active for now (unless we find a closer one later) + ParentTimeline()->trim_target = c; + ParentTimeline()->trim_type = olive::timeline::TRIM_IN; + closeness = nc; + found = true; - } } + } - // is the cursor hovering around the clip's OUT point? - if (c->timeline_out() > mouse_frame_lower && c->timeline_out() < mouse_frame_upper) { + // is the cursor hovering around the clip's OUT point? + if (c->timeline_out() > mouse_frame_lower && c->timeline_out() < mouse_frame_upper) { - // test how close this OUT point is to the cursor - int nc = qAbs(c->timeline_out() - 1 - panel_timeline->cursor_frame); + // test how close this OUT point is to the cursor + int nc = qAbs(c->timeline_out() - 1 - ParentTimeline()->cursor_frame); - // and test whether it's closer than the last in/out point we found - if (nc < closeness) { + // and test whether it's closer than the last in/out point we found + if (nc < closeness) { - // if so, this is the point we'll make active for now (unless we find a closer one later) - panel_timeline->trim_target = i; - panel_timeline->trim_type = olive::timeline::TRIM_OUT; - closeness = nc; - found = true; + // if so, this is the point we'll make active for now (unless we find a closer one later) + ParentTimeline()->trim_target = c; + ParentTimeline()->trim_type = olive::timeline::TRIM_OUT; + closeness = nc; + found = true; - } } + } - // the pointer can be used to resize/trim transitions, here we test if the - // cursor is within the trim point of one of the clip's transitions - if (panel_timeline->tool == TIMELINE_TOOL_POINTER) { + // the pointer can be used to resize/trim transitions, here we test if the + // cursor is within the trim point of one of the clip's transitions + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER) { - // if the clip has an opening transition - if (c->opening_transition != nullptr) { + // if the clip has an opening transition + if (c->opening_transition != nullptr) { - // cache the timeline frame where the transition ends - long transition_point = c->timeline_in() + c->opening_transition->get_true_length(); + // cache the timeline frame where the transition ends + long transition_point = c->timeline_in() + c->opening_transition->get_true_length(); - // check if the cursor is hovering around it (within the threshold) - if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { + // check if the cursor is hovering around it (within the threshold) + if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { - // similar to above, test how close it is and if it's closer, make this active - int nc = qAbs(transition_point - 1 - panel_timeline->cursor_frame); - if (nc < closeness) { - panel_timeline->trim_target = i; - panel_timeline->trim_type = olive::timeline::TRIM_OUT; - panel_timeline->transition_select = kTransitionOpening; - closeness = nc; - found = true; - } + // similar to above, test how close it is and if it's closer, make this active + int nc = qAbs(transition_point - 1 - ParentTimeline()->cursor_frame); + if (nc < closeness) { + ParentTimeline()->trim_target = c; + ParentTimeline()->trim_type = olive::timeline::TRIM_OUT; + ParentTimeline()->transition_select = kTransitionOpening; + closeness = nc; + found = true; } } + } - // if the clip has a closing transition - if (c->closing_transition != nullptr) { + // if the clip has a closing transition + if (c->closing_transition != nullptr) { - // cache the timeline frame where the transition starts - long transition_point = c->timeline_out() - c->closing_transition->get_true_length(); + // cache the timeline frame where the transition starts + long transition_point = c->timeline_out() - c->closing_transition->get_true_length(); - // check if the cursor is hovering around it (within the threshold) - if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { + // check if the cursor is hovering around it (within the threshold) + if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { - // similar to above, test how close it is and if it's closer, make this active - int nc = qAbs(transition_point + 1 - panel_timeline->cursor_frame); - if (nc < closeness) { - panel_timeline->trim_target = i; - panel_timeline->trim_type = olive::timeline::TRIM_IN; - panel_timeline->transition_select = kTransitionClosing; - closeness = nc; - found = true; - } + // similar to above, test how close it is and if it's closer, make this active + int nc = qAbs(transition_point + 1 - ParentTimeline()->cursor_frame); + if (nc < closeness) { + ParentTimeline()->trim_target = c; + ParentTimeline()->trim_type = olive::timeline::TRIM_IN; + ParentTimeline()->transition_select = kTransitionClosing; + closeness = nc; + found = true; } } } @@ -2649,10 +2621,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // if the cursor is indeed on a clip edge, we set the cursor accordingly if (found) { - if (panel_timeline->trim_type == olive::timeline::TRIM_IN) { // if we're trimming an IN point - setCursor(panel_timeline->tool == TIMELINE_TOOL_RIPPLE ? olive::cursor::LeftRipple : olive::cursor::LeftTrim); + if (ParentTimeline()->trim_type == olive::timeline::TRIM_IN) { // if we're trimming an IN point + setCursor(olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RIPPLE ? olive::cursor::LeftRipple : olive::cursor::LeftTrim); } else { // if we're trimming an OUT point - setCursor(panel_timeline->tool == TIMELINE_TOOL_RIPPLE ? olive::cursor::RightRipple : olive::cursor::RightTrim); + setCursor(olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_RIPPLE ? olive::cursor::RightRipple : olive::cursor::RightTrim); } } else { @@ -2662,44 +2634,44 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { unsetCursor(); // check to see if we're resizing a track height - int test_range = 5; int mouse_pos = event->pos().y(); - int hover_track = getTrackFromScreenPoint(mouse_pos); - int track_y_edge = getScreenPointFromTrack(hover_track); + Track* hover_track = getTrackFromScreenPoint(mouse_pos); - if (!bottom_align) { - track_y_edge += panel_timeline->GetTrackHeight(hover_track); - } + if (hover_track != nullptr) { + int test_range = 5; // FIXME magic number - if (mouse_pos > track_y_edge - test_range - && mouse_pos < track_y_edge + test_range) { - if (cursor_contains_clip - || (olive::CurrentConfig.show_track_lines - && panel_timeline->cursor_track >= min_track - && panel_timeline->cursor_track <= max_track)) { + int track_y_edge = getScreenPointFromTrack(hover_track); + + if (alignment_ == olive::timeline::kAlignmentTop) { + track_y_edge += hover_track->height(); + } + + if (mouse_pos > track_y_edge - test_range + && mouse_pos < track_y_edge + test_range) { track_resizing = true; track_target = hover_track; setCursor(Qt::SizeVerCursor); } } + } - } else if (panel_timeline->tool == TIMELINE_TOOL_SLIP) { + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIP) { // we're not currently performing any slipping, all we do here is set the cursor if mouse is hovering over a // cursor - if (getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track) > -1) { + if (GetClipAtCursor() != nullptr) { setCursor(olive::cursor::Slip); } else { unsetCursor(); } - } else if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) { + } else if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { - if (panel_timeline->transition_tool_init) { + if (ParentTimeline()->transition_tool_init) { // the transition tool has started - if (panel_timeline->transition_tool_proc) { + if (ParentTimeline()->transition_tool_proc) { // ghosts have been set up, so just run update update_ghosts(event->pos(), event->modifiers() & Qt::ShiftModifier); @@ -2707,29 +2679,27 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else { // transition tool is being used but ghosts haven't been set up yet, set them up now - int primary_type = kTransitionOpening; - int primary = panel_timeline->transition_tool_open_clip; - if (primary == -1) { + TransitionType primary_type = kTransitionOpening; + Clip* primary = ParentTimeline()->transition_tool_open_clip; + if (primary == nullptr) { primary_type = kTransitionClosing; - primary = panel_timeline->transition_tool_close_clip; + primary = ParentTimeline()->transition_tool_close_clip; } - ClipPtr c = olive::ActiveSequence->clips.at(primary); - Ghost g; g.in = g.old_in = g.out = g.old_out = (primary_type == kTransitionOpening) ? - c->timeline_in() - : c->timeline_out(); + primary->timeline_in() + : primary->timeline_out(); - g.track = c->track(); + g.track = primary->track(); g.clip = primary; g.media_stream = primary_type; g.trim_type = olive::timeline::TRIM_NONE; - panel_timeline->ghosts.append(g); + ParentTimeline()->ghosts.append(g); - panel_timeline->transition_tool_proc = true; + ParentTimeline()->transition_tool_proc = true; } @@ -2738,42 +2708,40 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // transition tool has been selected but is not yet active, so we show screen feedback to the user on // possible transitions - int mouse_clip = getClipIndexFromCoords(panel_timeline->cursor_frame, panel_timeline->cursor_track); + Clip* mouse_clip = GetClipAtCursor(); // set default transition tool references to no clip - panel_timeline->transition_tool_open_clip = -1; - panel_timeline->transition_tool_close_clip = -1; + ParentTimeline()->transition_tool_open_clip = nullptr; + ParentTimeline()->transition_tool_close_clip = nullptr; - if (mouse_clip > -1) { + if (mouse_clip != nullptr) { // cursor is hovering over a clip - ClipPtr c = olive::ActiveSequence->clips.at(mouse_clip); - // check if the clip and transition are both the same sign (meaning video/audio are the same) - if (same_sign(c->track(), panel_timeline->transition_tool_side)) { + if (mouse_clip->track()->type() == ParentTimeline()->transition_tool_side) { // the range within which the transition tool will assume the user wants to make a shared transition // between two clips rather than just one transition on one clip - long between_range = getFrameFromScreenPoint(panel_timeline->zoom, TRANSITION_BETWEEN_RANGE) + 1; + long between_range = getFrameFromScreenPoint(ParentTimeline()->zoom, TRANSITION_BETWEEN_RANGE) + 1; // set whether the transition is opening or closing based on whether the cursor is on the left half // or right half of the clip - if (panel_timeline->cursor_frame > (c->timeline_in() + (c->length()/2))) { - panel_timeline->transition_tool_close_clip = mouse_clip; + if (ParentTimeline()->cursor_frame > (mouse_clip->timeline_in() + (mouse_clip->length()/2))) { + ParentTimeline()->transition_tool_close_clip = mouse_clip; // if the cursor is within this range, set the post_clip to be the next clip touching // // getClipIndexFromCoords() will automatically set to -1 if there's no clip there which means the // end result will be the same as not setting a clip here at all - if (panel_timeline->cursor_frame > c->timeline_out() - between_range) { - panel_timeline->transition_tool_open_clip = getClipIndexFromCoords(c->timeline_out()+1, c->track()); + if (ParentTimeline()->cursor_frame > mouse_clip->timeline_out() - between_range) { + ParentTimeline()->transition_tool_open_clip = mouse_clip->track()->GetClipFromPoint(mouse_clip->timeline_out()+1); } } else { - panel_timeline->transition_tool_open_clip = mouse_clip; + ParentTimeline()->transition_tool_open_clip = mouse_clip; - if (panel_timeline->cursor_frame < c->timeline_in() + between_range) { - panel_timeline->transition_tool_close_clip = getClipIndexFromCoords(c->timeline_in()-1, c->track()); + if (ParentTimeline()->cursor_frame < mouse_clip->timeline_in() + between_range) { + ParentTimeline()->transition_tool_close_clip = mouse_clip->track()->GetClipFromPoint(mouse_clip->timeline_in()-1); } } @@ -2781,7 +2749,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } } - panel_timeline->repaint_timeline(); + ParentTimeline()->repaint_timeline(); } } } @@ -2790,7 +2758,7 @@ void TimelineView::leaveEvent(QEvent*) { tooltip_timer.stop(); } -void draw_waveform(ClipPtr clip, const FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) { +void draw_waveform(Clip* clip, const FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) { // audio channels multiplied by the number of bytes in a 16-bit audio sample int divider = ms->audio_channels*2; @@ -2808,7 +2776,7 @@ void draw_waveform(ClipPtr clip, const FootageStream* ms, long media_length, QPa if (last_waveform_index < 0) last_waveform_index = waveform_index; for (int j=0;jaudio_channels;j++) { - int mid = (olive::CurrentConfig.rectified_waveforms) ? clip_rect.top()+channel_height*(j+1) : clip_rect.top()+channel_height*j+(channel_height/2); + int mid = (olive::config.rectified_waveforms) ? clip_rect.top()+channel_height*(j+1) : clip_rect.top()+channel_height*j+(channel_height/2); int offset_range_start = last_waveform_index+(j*2); int offset_range_end = waveform_index+(j*2); @@ -2832,7 +2800,7 @@ void draw_waveform(ClipPtr clip, const FootageStream* ms, long media_length, QPa } // draw waveforms - if (olive::CurrentConfig.rectified_waveforms) { + if (olive::config.rectified_waveforms) { // rectified waveforms start from the bottom and draw upwards p->drawLine(clip_rect.left()+i, mid, clip_rect.left()+i, mid - (max - min)); @@ -2848,11 +2816,11 @@ void draw_waveform(ClipPtr clip, const FootageStream* ms, long media_length, QPa } } -void draw_transition(QPainter& p, ClipPtr c, const QRect& clip_rect, QRect& text_rect, int transition_type) { +void TimelineView::draw_transition(QPainter& p, Clip* c, const QRect& clip_rect, QRect& text_rect, int transition_type) { TransitionPtr t = (transition_type == kTransitionOpening) ? c->opening_transition : c->closing_transition; if (t != nullptr) { QColor transition_color(255, 0, 0, 16); - int transition_width = getScreenPointFromFrame(panel_timeline->zoom, t->get_true_length()); + int transition_width = getScreenPointFromFrame(ParentTimeline()->zoom, t->get_true_length()); int transition_height = clip_rect.height(); int tr_y = clip_rect.y(); int tr_x = 0; @@ -2900,44 +2868,40 @@ void draw_transition(QPainter& p, ClipPtr c, const QRect& clip_rect, QRect& text void TimelineView::paintEvent(QPaintEvent*) { // Draw clips - if (olive::ActiveSequence != nullptr) { + if (track_list_ != nullptr) { QPainter p(this); // get widget width and height - int video_track_limit = 0; - int audio_track_limit = 0; - for (int i=0;iclips.size();i++) { - ClipPtr clip = olive::ActiveSequence->clips.at(i); - if (clip != nullptr) { - video_track_limit = qMin(video_track_limit, clip->track()); - audio_track_limit = qMax(audio_track_limit, clip->track()); - } - } // start by adding a track height worth of padding int panel_height = olive::timeline::kTrackDefaultHeight; - // loop through tracks for maximum panel height - if (bottom_align) { - for (int i=-1;i>=video_track_limit;i--) { - panel_height += panel_timeline->GetTrackHeight(i); - } - } else { - for (int i=0;i<=audio_track_limit;i++) { - panel_height += panel_timeline->GetTrackHeight(i); - } + for (int i=0;iTrackCount();i++) { + panel_height += track_list_->TrackAt(i)->height() + 1; } - if (bottom_align) { + if (alignment_ == olive::timeline::kAlignmentBottom) { scrollBar->setMinimum(qMin(0, - panel_height + height())); } else { scrollBar->setMaximum(qMax(0, panel_height - height())); } - for (int i=0;iclips.size();i++) { - ClipPtr clip = olive::ActiveSequence->clips.at(i); - if (clip != nullptr && is_track_visible(clip->track())) { - QRect clip_rect(panel_timeline->getTimelineScreenPointFromFrame(clip->timeline_in()), getScreenPointFromTrack(clip->track()), getScreenPointFromFrame(panel_timeline->zoom, clip->length()), panel_timeline->GetTrackHeight(clip->track())); - QRect text_rect(clip_rect.left() + olive::timeline::kClipTextPadding, clip_rect.top() + olive::timeline::kClipTextPadding, clip_rect.width() - olive::timeline::kClipTextPadding - 1, clip_rect.height() - olive::timeline::kClipTextPadding - 1); + int track_line = 0; + + for (int i=0;iTrackCount();i++) { + + Track* track = track_list_->TrackAt(i); + + for (int j=0;jClipCount();j++) { + Clip* clip = track->GetClip(j).get(); + + QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), + getScreenPointFromTrack(clip->track()), + getScreenPointFromFrame(ParentTimeline()->zoom, clip->length()), + clip->track()->height()); + QRect text_rect(clip_rect.left() + olive::timeline::kClipTextPadding, + clip_rect.top() + olive::timeline::kClipTextPadding, + clip_rect.width() - olive::timeline::kClipTextPadding - 1, + clip_rect.height() - olive::timeline::kClipTextPadding - 1); if (clip_rect.left() < width() && clip_rect.right() >= 0 && clip_rect.top() < height() && clip_rect.bottom() >= 0) { QRect actual_clip_rect = clip_rect; if (actual_clip_rect.x() < 0) actual_clip_rect.setX(0); @@ -2993,18 +2957,18 @@ void TimelineView::paintEvent(QPaintEvent*) { // draw thumbnail/waveform long media_length = clip->media_length(); - if (clip->track() < 0) { + if (clip->type() == Track::kTypeVideo) { // draw thumbnail int thumb_y = p.fontMetrics().height()+olive::timeline::kClipTextPadding+olive::timeline::kClipTextPadding; if (thumb_x < width() && thumb_y < height()) { int space_for_thumb = clip_rect.width()-1; if (clip->opening_transition != nullptr) { - int ot_width = getScreenPointFromFrame(panel_timeline->zoom, clip->opening_transition->get_true_length()); + int ot_width = getScreenPointFromFrame(ParentTimeline()->zoom, clip->opening_transition->get_true_length()); thumb_x += ot_width; space_for_thumb -= ot_width; } if (clip->closing_transition != nullptr) { - space_for_thumb -= getScreenPointFromFrame(panel_timeline->zoom, clip->closing_transition->get_true_length()); + space_for_thumb -= getScreenPointFromFrame(ParentTimeline()->zoom, clip->closing_transition->get_true_length()); } int thumb_height = clip_rect.height()-thumb_y; int thumb_width = qRound(thumb_height*(double(ms->video_preview.width())/double(ms->video_preview.height()))); @@ -3028,14 +2992,14 @@ void TimelineView::paintEvent(QPaintEvent*) { } if (clip->timeline_out() - clip->timeline_in() + clip->clip_in() > clip->media_length()) { draw_checkerboard = true; - checkerboard_rect.setLeft(panel_timeline->getTimelineScreenPointFromFrame(clip->media_length() + clip->timeline_in() - clip->clip_in())); + checkerboard_rect.setLeft(ParentTimeline()->getTimelineScreenPointFromFrame(clip->media_length() + clip->timeline_in() - clip->clip_in())); } } else if (clip_rect.height() > olive::timeline::kTrackMinHeight) { // draw waveform p.setPen(QColor(80, 80, 80)); int waveform_start = -qMin(clip_rect.x(), 0); - int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(panel_timeline->zoom, media_length - clip->clip_in())); + int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(ParentTimeline()->zoom, media_length - clip->clip_in())); if ((clip_rect.x() + waveform_limit) > width()) { waveform_limit -= (clip_rect.x() + waveform_limit - width()); @@ -3044,7 +3008,7 @@ void TimelineView::paintEvent(QPaintEvent*) { if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit); } - draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, panel_timeline->zoom); + draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, ParentTimeline()->zoom); } } if (draw_checkerboard) { @@ -3086,9 +3050,9 @@ void TimelineView::paintEvent(QPaintEvent*) { // convert marker time (in clip time) to sequence time long marker_time = m.frame + clip->timeline_in() - clip->clip_in(); - int marker_x = panel_timeline->getTimelineScreenPointFromFrame(marker_time); + int marker_x = ParentTimeline()->getTimelineScreenPointFromFrame(marker_time); if (marker_x > clip_rect.x() && marker_x < clip_rect.right()) { - draw_marker(p, marker_x, clip_rect.bottom()-p.fontMetrics().height(), clip_rect.bottom(), false); + Marker::Draw(p, marker_x, clip_rect.bottom()-p.fontMetrics().height(), clip_rect.bottom(), false); } } p.setBrush(Qt::NoBrush); @@ -3112,7 +3076,7 @@ void TimelineView::paintEvent(QPaintEvent*) { } if (clip->linked.size() > 0) { int underline_y = olive::timeline::kClipTextPadding + p.fontMetrics().height() + clip_rect.top(); - int underline_width = qMin(text_rect.width() - 1, p.fontMetrics().width(clip->name())); + int underline_width = qMin(text_rect.width() - 1, p.fontMetrics().width(clip->name())); p.drawLine(text_rect.x(), underline_y, text_rect.x() + underline_width, underline_y); } QString name = clip->name(); @@ -3130,22 +3094,22 @@ void TimelineView::paintEvent(QPaintEvent*) { if (clip_rect.bottom() >= 0 && clip_rect.bottom() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.bottom()), QPoint(qMin(width(), clip_rect.right()), clip_rect.bottom())); // draw transition tool - if (panel_timeline->tool == TIMELINE_TOOL_TRANSITION) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { - bool shared_transition = (panel_timeline->transition_tool_open_clip > -1 - && panel_timeline->transition_tool_close_clip > -1); + bool shared_transition = (ParentTimeline()->transition_tool_open_clip != nullptr + && ParentTimeline()->transition_tool_close_clip != nullptr); QRect transition_tool_rect = clip_rect; bool draw_transition_tool_rect = false; - if (panel_timeline->transition_tool_open_clip == i) { + if (ParentTimeline()->transition_tool_open_clip == clip) { if (shared_transition) { transition_tool_rect.setWidth(TRANSITION_BETWEEN_RANGE); } else { transition_tool_rect.setWidth(transition_tool_rect.width()>>2); } draw_transition_tool_rect = true; - } else if (panel_timeline->transition_tool_close_clip == i) { + } else if (ParentTimeline()->transition_tool_close_clip == clip) { if (shared_transition) { transition_tool_rect.setLeft(transition_tool_rect.right() - TRANSITION_BETWEEN_RANGE); } else { @@ -3168,87 +3132,101 @@ void TimelineView::paintEvent(QPaintEvent*) { } } } - } - // Draw recording clip if recording if valid - if (panel_sequence_viewer->is_recording_cued() && is_track_visible(panel_sequence_viewer->recording_track)) { - int rec_track_x = panel_timeline->getTimelineScreenPointFromFrame(panel_sequence_viewer->recording_start); - int rec_track_y = getScreenPointFromTrack(panel_sequence_viewer->recording_track); - int rec_track_height = panel_timeline->GetTrackHeight(panel_sequence_viewer->recording_track); - if (panel_sequence_viewer->recording_start != panel_sequence_viewer->recording_end) { - QRect rec_rect( + // Draw recording clip if recording if valid + if (panel_sequence_viewer->is_recording_cued() && panel_sequence_viewer->recording_track == track) { + int rec_track_x = ParentTimeline()->getTimelineScreenPointFromFrame(panel_sequence_viewer->recording_start); + int rec_track_y = getScreenPointFromTrack(panel_sequence_viewer->recording_track); + int rec_track_height = track->height(); + if (panel_sequence_viewer->recording_start != panel_sequence_viewer->recording_end) { + QRect rec_rect( + rec_track_x, + rec_track_y, + getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->recording_end - panel_sequence_viewer->recording_start), + rec_track_height + ); + p.setPen(QPen(QColor(96, 96, 96), 2)); + p.fillRect(rec_rect, QColor(192, 192, 192)); + p.drawRect(rec_rect); + } + QRect active_rec_rect( rec_track_x, rec_track_y, - getScreenPointFromFrame(panel_timeline->zoom, panel_sequence_viewer->recording_end - panel_sequence_viewer->recording_start), + getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->seq->playhead - panel_sequence_viewer->recording_start), rec_track_height ); - p.setPen(QPen(QColor(96, 96, 96), 2)); - p.fillRect(rec_rect, QColor(192, 192, 192)); - p.drawRect(rec_rect); - } - QRect active_rec_rect( - rec_track_x, - rec_track_y, - getScreenPointFromFrame(panel_timeline->zoom, panel_sequence_viewer->seq->playhead - panel_sequence_viewer->recording_start), - rec_track_height - ); - p.setPen(QPen(QColor(192, 0, 0), 2)); - p.fillRect(active_rec_rect, QColor(255, 96, 96)); - p.drawRect(active_rec_rect); + p.setPen(QPen(QColor(192, 0, 0), 2)); + p.fillRect(active_rec_rect, QColor(255, 96, 96)); + p.drawRect(active_rec_rect); - p.setPen(Qt::NoPen); + p.setPen(Qt::NoPen); - if (!panel_sequence_viewer->playing) { - int rec_marker_size = 6; - int rec_track_midY = rec_track_y + (rec_track_height >> 1); - p.setBrush(Qt::white); - QPoint cue_marker[3] = { - QPoint(rec_track_x, rec_track_midY - rec_marker_size), - QPoint(rec_track_x + rec_marker_size, rec_track_midY), - QPoint(rec_track_x, rec_track_midY + rec_marker_size) - }; - p.drawPolygon(cue_marker, 3); - } - } - - // Draw track lines - if (olive::CurrentConfig.show_track_lines) { - p.setPen(QColor(0, 0, 0, 96)); - audio_track_limit++; - if (video_track_limit == 0) video_track_limit--; - - if (bottom_align) { - // only draw lines for video tracks - for (int i=video_track_limit;i<0;i++) { - int line_y = getScreenPointFromTrack(i) - 1; - p.drawLine(0, line_y, rect().width(), line_y); - } - } else { - // only draw lines for audio tracks - for (int i=0;iGetTrackHeight(i); - p.drawLine(0, line_y, rect().width(), line_y); + if (!panel_sequence_viewer->playing) { + int rec_marker_size = 6; + int rec_track_midY = rec_track_y + (rec_track_height >> 1); + p.setBrush(Qt::white); + QPoint cue_marker[3] = { + QPoint(rec_track_x, rec_track_midY - rec_marker_size), + QPoint(rec_track_x + rec_marker_size, rec_track_midY), + QPoint(rec_track_x, rec_track_midY + rec_marker_size) + }; + p.drawPolygon(cue_marker, 3); } } - } - // Draw selections - for (int i=0;iselections.size();i++) { - const Selection& s = olive::ActiveSequence->selections.at(i); - if (is_track_visible(s.track)) { - int selection_y = getScreenPointFromTrack(s.track); - int selection_x = panel_timeline->getTimelineScreenPointFromFrame(s.in); + // Draw selections + QVector selections = track->Selections(); + for (int j=0;jgetTimelineScreenPointFromFrame(s.in()); p.setPen(Qt::NoPen); p.setBrush(Qt::NoBrush); - p.fillRect(selection_x, selection_y, panel_timeline->getTimelineScreenPointFromFrame(s.out) - selection_x, panel_timeline->GetTrackHeight(s.track), QColor(0, 0, 0, 64)); + p.fillRect(selection_x, + track_line, + ParentTimeline()->getTimelineScreenPointFromFrame(s.out()) - selection_x, + s.track()->height(), + QColor(0, 0, 0, 64)); } + + // Draw splitting cursor + if (ParentTimeline()->splitting && ParentTimeline()->split_tracks.contains(track)) { + int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->drag_frame_start); + + p.setPen(QColor(64, 64, 64)); + p.drawLine(cursor_x, + track_line, + cursor_x, + track_line + track->height()); + } + + // Draw edit cursor + if (current_tool_shows_cursor() && ParentTimeline()->cursor_track == track) { + int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->cursor_frame); + + p.setPen(Qt::gray); + p.drawLine(cursor_x, + track_line, + cursor_x, + track_line + track->height()); + } + + // Draw track's line + track_line += track->height(); + if (track_line >= 0 && track_line < height()) { + p.setPen(QColor(0, 0, 0, 96)); + p.drawLine(0, track_line, rect().width(), track_line); + } + track_line++; + + } // draw rectangle select - if (panel_timeline->rect_select_proc) { - QRect rect_select = panel_timeline->rect_select_rect; + if (ParentTimeline()->rect_select_proc) { + QRect rect_select = ParentTimeline()->rect_select_rect; - if (bottom_align) { + if (alignment_ == olive::timeline::kAlignmentBottom) { rect_select.translate(0, height()); } @@ -3256,17 +3234,17 @@ void TimelineView::paintEvent(QPaintEvent*) { } // Draw ghosts - if (!panel_timeline->ghosts.isEmpty()) { + if (!ParentTimeline()->ghosts.isEmpty()) { QVector insert_points; long first_ghost = LONG_MAX; - for (int i=0;ighosts.size();i++) { - const Ghost& g = panel_timeline->ghosts.at(i); + for (int i=0;ighosts.size();i++) { + const Ghost& g = ParentTimeline()->ghosts.at(i); first_ghost = qMin(first_ghost, g.in); - if (is_track_visible(g.track)) { - int ghost_x = panel_timeline->getTimelineScreenPointFromFrame(g.in); + if (g.track->type() == track_list_->type()) { + int ghost_x = ParentTimeline()->getTimelineScreenPointFromFrame(g.in); int ghost_y = getScreenPointFromTrack(g.track); - int ghost_width = panel_timeline->getTimelineScreenPointFromFrame(g.out) - ghost_x - 1; - int ghost_height = panel_timeline->GetTrackHeight(g.track) - 1; + int ghost_width = ParentTimeline()->getTimelineScreenPointFromFrame(g.out) - ghost_x - 1; + int ghost_height = g.track->height() - 1; insert_points.append(ghost_y + (ghost_height>>1)); @@ -3278,10 +3256,10 @@ void TimelineView::paintEvent(QPaintEvent*) { } // draw insert indicator - if (panel_timeline->move_insert && !insert_points.isEmpty()) { + if (ParentTimeline()->move_insert && !insert_points.isEmpty()) { p.setBrush(Qt::white); p.setPen(Qt::NoPen); - int insert_x = panel_timeline->getTimelineScreenPointFromFrame(first_ghost); + int insert_x = ParentTimeline()->getTimelineScreenPointFromFrame(first_ghost); int tri_size = olive::timeline::kTrackMinHeight>>2; for (int i=0;isplitting) { - for (int i=0;isplit_tracks.size();i++) { - if (is_track_visible(panel_timeline->split_tracks.at(i))) { - int cursor_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->drag_frame_start); - int cursor_y = getScreenPointFromTrack(panel_timeline->split_tracks.at(i)); - - p.setPen(QColor(64, 64, 64)); - p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->GetTrackHeight(panel_timeline->split_tracks.at(i))); - } - } - } - // Draw playhead p.setPen(Qt::red); - int playhead_x = panel_timeline->getTimelineScreenPointFromFrame(olive::ActiveSequence->playhead); + int playhead_x = ParentTimeline()->getTimelineScreenPointFromFrame(sequence()->playhead); p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom()); // Draw single frame highlight - int playhead_frame_width = panel_timeline->getTimelineScreenPointFromFrame(olive::ActiveSequence->playhead+1) - playhead_x; + int playhead_frame_width = ParentTimeline()->getTimelineScreenPointFromFrame(sequence()->playhead+1) - playhead_x; if (playhead_frame_width > 5){ //hardcoded for now, maybe better way to do this? - QRectF singleFrameRect(playhead_x, rect().top(), playhead_frame_width, rect().bottom()); - p.fillRect(singleFrameRect, QColor(255,255,255,15)); + QRectF singleFrameRect(playhead_x, rect().top(), playhead_frame_width, rect().bottom()); + p.fillRect(singleFrameRect, QColor(255,255,255,15)); } // draw border p.setPen(QColor(0, 0, 0, 64)); - int edge_y = (bottom_align) ? rect().height()-1 : 0; + int edge_y = 0; + p.drawLine(0, edge_y, rect().width(), edge_y); + edge_y = rect().height()-1; p.drawLine(0, edge_y, rect().width(), edge_y); // draw snap point - if (panel_timeline->snapped) { + if (olive::timeline::snapped) { p.setPen(Qt::white); - int snap_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->snap_point); + int snap_x = ParentTimeline()->getTimelineScreenPointFromFrame(olive::timeline::snap_point); p.drawLine(snap_x, 0, snap_x, height()); } - - // Draw edit cursor - if (current_tool_shows_cursor() && 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->GetTrackHeight(panel_timeline->cursor_track)); - } } } @@ -3348,81 +3305,50 @@ void TimelineView::resizeEvent(QResizeEvent *) { scrollBar->setPageStep(height()); } -bool TimelineView::is_track_visible(int track) { - return (bottom_align == (track < 0)); -} - // ************************************** // screen point <-> frame/track functions // ************************************** -int TimelineView::getTrackFromScreenPoint(int y) { - int track_candidate = 0; - +Track *TimelineView::getTrackFromScreenPoint(int y) { y += scroll; - if (bottom_align) { - y -= height(); - } + int heights = 0; + for (int i=0;iTrackCount();i++) { + int new_heights = heights + track_list_->TrackAt(i)->height() + 1; - if (y < 0) { - track_candidate--; - } - - int compounded_heights = 0; - - while (true) { - int track_height = panel_timeline->GetTrackHeight(track_candidate); - if (olive::CurrentConfig.show_track_lines) track_height++; - if (y < 0) { - track_height = -track_height; + if (y >= heights && y < new_heights) { + return track_list_->TrackAt(i); } - int next_compounded_height = compounded_heights + track_height; - - - if (y >= qMin(next_compounded_height, compounded_heights) && y < qMax(next_compounded_height, compounded_heights)) { - return track_candidate; - } - - compounded_heights = next_compounded_height; - - if (y < 0) { - track_candidate--; - } else { - track_candidate++; - } + heights = new_heights; } + + return nullptr; } -int TimelineView::getScreenPointFromTrack(int track) { +int TimelineView::getScreenPointFromTrack(Track *track) { int point = 0; - - int start = (track < 0) ? -1 : 0; - int interval = (track < 0) ? -1 : 1; - - if (track < 0) track--; - - for (int i=start;i!=track;i+=interval) { - point += panel_timeline->GetTrackHeight(i); - if (olive::CurrentConfig.show_track_lines) point++; - } - - if (bottom_align) { - return height() - point - scroll; - } else { - return point - scroll; + for (int i=0;iTrackCount();i++) { + if (track == track_list_->TrackAt(i)) { + return point; + } + point += track_list_->TrackAt(i)->height() + 1; } + return point - scroll; } -int TimelineView::getClipIndexFromCoords(long frame, int track) { - for (int i=0;iclips.size();i++) { - ClipPtr c = olive::ActiveSequence->clips.at(i); - if (c != nullptr && c->track() == track && frame >= c->timeline_in() && frame < c->timeline_out()) { - return i; - } +Timeline *TimelineView::ParentTimeline() +{ + return timeline_; +} + +Sequence *TimelineView::sequence() +{ + if (track_list_ == nullptr) { + return nullptr; } - return -1; + + return track_list_->GetParent(); } void TimelineView::setScroll(int s) { @@ -3431,5 +3357,5 @@ void TimelineView::setScroll(int s) { } void TimelineView::reveal_media() { - panel_project->reveal_media(rc_reveal_media); + panel_project.first()->reveal_media(rc_reveal_media); } diff --git a/ui/timelineview.h b/ui/timelineview.h index 6cbf22773..3e743b92b 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -30,23 +30,25 @@ #include "timeline/sequence.h" #include "timeline/clip.h" +#include "timeline/timelinetools.h" +#include "timeline/timelinefunctions.h" #include "project/footage.h" #include "project/media.h" #include "undo/undo.h" -#include "timelinetools.h" class Timeline; -bool same_sign(int a, int b); -void draw_waveform(ClipPtr clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); +void draw_waveform(Clip* clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); class TimelineView : public QWidget { Q_OBJECT public: - explicit TimelineView(QWidget *parent = nullptr); + explicit TimelineView(Timeline *parent); + + void SetAlignment(olive::timeline::Alignment alignment); + void SetTrackList(TrackList* tl); QScrollBar* scrollBar; - bool bottom_align; public slots: @@ -70,18 +72,29 @@ protected: private: void init_ghosts(); void update_ghosts(const QPoint& mouse_pos, bool lock_frame); - bool is_track_visible(int track); - int getTrackFromScreenPoint(int y); - int getScreenPointFromTrack(int track); - int getClipIndexFromCoords(long frame, int track); + Track* getTrackFromScreenPoint(int y); + int getScreenPointFromTrack(Track* track); + Timeline* ParentTimeline(); + Sequence* sequence(); + void delete_area_under_ghosts(ComboAction* ca, Sequence *s); + void insert_clips(ComboAction* ca, Sequence *s); + bool current_tool_shows_cursor(); + void draw_transition(QPainter& p, Clip *c, const QRect& clip_rect, QRect& text_rect, int transition_type); + Clip* GetClipAtCursor(); + void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, long transition_start, long transition_end); void VerifyTransitionHelper(); - bool track_resizing; - int track_target; + Timeline* timeline_; - QVector pre_clips; - QVector post_clips; + olive::timeline::Alignment alignment_; + TrackList* track_list_; + + bool track_resizing; + Track* track_target; + + QVector pre_clips; + QVector post_clips; Media* rc_reveal_media; @@ -92,7 +105,6 @@ private: int scroll; - SetSelectionsCommand* selection_command; signals: public slots: diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index a6ce74f59..b773de2ec 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -146,7 +146,7 @@ void ViewerWidget::show_context_menu() { connect(&zoom_menu, SIGNAL(triggered(QAction*)), this, SLOT(set_menu_zoom(QAction*))); menu.addMenu(&zoom_menu); - if (!viewer->is_main_sequence()) { + if (viewer->mode() != Viewer::kTimelineMode) { menu.addAction(tr("Close Media"), viewer, SLOT(close_media())); } @@ -338,7 +338,12 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) { int x_movement = qRound((event->pos().x() - drag_start_x)*multiplier); int y_movement = qRound((event->pos().y() - drag_start_y)*multiplier); - gizmos->gizmo_move(selected_gizmo, x_movement, y_movement, get_timecode(gizmos->parent_clip, gizmos->parent_clip->sequence->playhead), done); + gizmos->gizmo_move(selected_gizmo, + x_movement, + y_movement, + get_timecode(gizmos->parent_clip, + gizmos->parent_clip->track()->sequence()->playhead), + done); gizmo_x_mvmt += x_movement; gizmo_y_mvmt += y_movement; @@ -351,7 +356,7 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) { void ViewerWidget::mousePressEvent(QMouseEvent* event) { if (waveform) { seek_from_click(event->x()); - } else if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + } else if (event->buttons() & Qt::MiddleButton || olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND) { container->dragScrollPress(event->pos()*container->zoom); } else if (event->buttons() & Qt::LeftButton) { drag_start_x = event->pos().x(); @@ -367,13 +372,13 @@ void ViewerWidget::mousePressEvent(QMouseEvent* event) { void ViewerWidget::mouseMoveEvent(QMouseEvent* event) { unsetCursor(); - if (panel_timeline->tool == TIMELINE_TOOL_HAND) { + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND) { setCursor(Qt::OpenHandCursor); } if (dragging) { if (waveform) { seek_from_click(event->x()); - } else if (event->buttons() & Qt::MiddleButton || panel_timeline->tool == TIMELINE_TOOL_HAND) { + } else if (event->buttons() & Qt::MiddleButton || olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_HAND) { container->dragScrollMove(event->pos()*container->zoom); } else if (event->buttons() & Qt::LeftButton) { if (gizmos == nullptr) { @@ -397,7 +402,7 @@ void ViewerWidget::mouseReleaseEvent(QMouseEvent *event) { if (dragging && gizmos != nullptr && event->button() == Qt::LeftButton - && panel_timeline->tool != TIMELINE_TOOL_HAND) { + && olive::timeline::current_tool != olive::timeline::TIMELINE_TOOL_HAND) { move_gizmos(event, true); } dragging = false; @@ -431,7 +436,7 @@ void ViewerWidget::draw_waveform_func() { wr.setX(wr.x() - waveform_scroll); p.setPen(Qt::green); - draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out(), &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom); + draw_waveform(waveform_clip.get(), waveform_ms, waveform_clip->timeline_out(), &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom); p.setPen(Qt::red); int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll; p.drawLine(playhead_x, 0, playhead_x, height()); @@ -452,16 +457,16 @@ void ViewerWidget::draw_title_safe_area() { matrix.ortho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f); // adjust the horizontal center cross by the aspect ratio to appear "square" - if (olive::CurrentConfig.use_custom_title_safe_ratio && olive::CurrentConfig.custom_title_safe_ratio > 0) { - if (ar > olive::CurrentConfig.custom_title_safe_ratio) { - matrix.translate(((ar - olive::CurrentConfig.custom_title_safe_ratio) / 2.0) / ar, 0.0f); - matrix.scale(olive::CurrentConfig.custom_title_safe_ratio / ar, 1.0f); + if (olive::config.use_custom_title_safe_ratio && olive::config.custom_title_safe_ratio > 0) { + if (ar > olive::config.custom_title_safe_ratio) { + matrix.translate(((ar - olive::config.custom_title_safe_ratio) / 2.0) / ar, 0.0f); + matrix.scale(olive::config.custom_title_safe_ratio / ar, 1.0f); } else { - matrix.translate(0.0f, (((olive::CurrentConfig.custom_title_safe_ratio - ar) / 2.0) / olive::CurrentConfig.custom_title_safe_ratio)); - matrix.scale(1.0f, ar / olive::CurrentConfig.custom_title_safe_ratio); + matrix.translate(0.0f, (((olive::config.custom_title_safe_ratio - ar) / 2.0) / olive::config.custom_title_safe_ratio)); + matrix.scale(1.0f, ar / olive::config.custom_title_safe_ratio); } - horizontal_cross_size *= ar/olive::CurrentConfig.custom_title_safe_ratio; + horizontal_cross_size *= ar/olive::config.custom_title_safe_ratio; } float adjusted_cross_x1 = 0.5f - horizontal_cross_size; @@ -745,7 +750,7 @@ void ViewerWidget::paintGL() { f->glBindTexture(GL_TEXTURE_2D, 0); // draw title/action safe area - if (olive::CurrentConfig.show_title_safe_area) { + if (olive::config.show_title_safe_area) { draw_title_safe_area(); } diff --git a/undo/undo.cpp b/undo/undo.cpp index e562f7e77..dd603d652 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -38,11 +38,11 @@ #include "ui/labelslider.h" #include "ui/viewerwidget.h" #include "project/media.h" -#include "project/clipboard.h" +#include "global/clipboard.h" #include "project/previewgenerator.h" #include "ui/mainwindow.h" -MoveClipAction::MoveClipAction(Clip *c, long iin, long iout, long iclip_in, int itrack, bool irelative) : +MoveClipAction::MoveClipAction(ClipPtr c, long iin, long iout, long iclip_in, Track* itrack, bool irelative) : clip(c), old_in(c->timeline_in()), old_out(c->timeline_out()), @@ -63,13 +63,12 @@ void MoveClipAction::doUndo() { clip->set_timeline_in (clip->timeline_in() - new_in); clip->set_timeline_out (clip->timeline_out() - new_out); clip->set_clip_in (clip->clip_in() - new_clip_in); - clip->set_track (clip->track() - new_track); } else { clip->set_timeline_in(old_in); clip->set_timeline_out(old_out); clip->set_clip_in(old_clip_in); - clip->set_track(old_track); } + clip->set_track(old_track); done = false; } @@ -79,13 +78,12 @@ void MoveClipAction::doRedo() { clip->set_timeline_in(clip->timeline_in() + new_in); clip->set_timeline_out(clip->timeline_out() + new_out); clip->set_clip_in(clip->clip_in() + new_clip_in); - clip->set_track(clip->track() + new_track); } else { clip->set_timeline_in(new_in); clip->set_timeline_out(new_out); clip->set_clip_in(new_clip_in); - clip->set_track(new_track); } + new_track->AddClip(clip); done = true; } } @@ -98,15 +96,13 @@ DeleteClipAction::DeleteClipAction(Clip *clip) } void DeleteClipAction::doUndo() { - // restore ref to clip - seq->clips[index] = ref; + // restore clip to this track + clip_->track()->AddClip(clip_); // restore links to this clip - for (int i=linkClipIndex.size()-1;i>=0;i--) { - seq->clips.at(linkClipIndex.at(i))->linked.insert(linkLinkIndex.at(i), index); + for (int i=0;itrack()->RemoveClip(clip_.get()); // delete link to this clip - QVector clips = clip_->track()-> - linkClipIndex.clear(); - linkLinkIndex.clear(); - for (int i=0;iclips.size();i++) { - ClipPtr c = seq->clips.at(i); - if (c != nullptr) { - for (int j=0;jlinked.size();j++) { - if (c->linked.at(j) == index) { - linkClipIndex.append(i); - linkLinkIndex.append(j); - c->linked.removeAt(j); - } + QVector clips = clip_->track()->sequence()->GetAllClips(); + for (int i=0;ilinked.size();j++) { + if (c->linked.at(j) == clip_.get()) { + c->linked.removeAt(j); + clips_linked_to_this_one_.append(c); + break; } } } } -ChangeSequenceAction::ChangeSequenceAction(SequencePtr s) { - new_sequence = s; -} - -void ChangeSequenceAction::doUndo() { - olive::Global->set_sequence(old_sequence); -} - -void ChangeSequenceAction::doRedo() { - old_sequence = olive::ActiveSequence; - olive::Global->set_sequence(new_sequence); -} - SetTimelineInOutCommand::SetTimelineInOutCommand(Sequence* s, bool enabled, long in, long out) { seq = s; new_enabled = enabled; @@ -162,7 +142,7 @@ void SetTimelineInOutCommand::doUndo() { // footage viewer functions if (seq->wrapper_sequence) { - Footage* m = seq->clips.at(0)->media()->to_footage(); + Footage* m = seq->GetAllClips().first()->media()->to_footage(); m->using_inout = old_enabled; m->in = old_in; m->out = old_out; @@ -180,7 +160,7 @@ void SetTimelineInOutCommand::doRedo() { // footage viewer functions if (seq->wrapper_sequence) { - Footage* m = seq->clips.at(0)->media()->to_footage(); + Footage* m = seq->GetAllClips().first()->media()->to_footage(); m->using_inout = new_enabled; m->in = new_in; m->out = new_out; @@ -351,10 +331,8 @@ void DeleteMediaCommand::doRedo() { olive::project_model.removeChild(parent, item.get()); } -AddClipCommand::AddClipCommand(Sequence *s, QVector& add) : - link_offset_(0), - seq(s), - clips(add), +AddClipCommand::AddClipCommand(const QVector &add) : + clips_(add), done_(false) { doRedo(); @@ -365,26 +343,21 @@ void AddClipCommand::doUndo() { panel_graph_editor->set_row(nullptr); panel_effect_controls->Clear(true); - for (int i=0;iclips.last(); + for (int i=0;ilinked.size();j++) { - c->linked[j] -= link_offset_; - } + + c->track()->RemoveClip(c.get()); // deselect the area occupied by this clip - panel_timeline->deselect_area(c->timeline_in(), c->timeline_out(), c->track()); + c->track()->DeselectArea(c->timeline_in(), c->timeline_out()); // if the clip is open, close it if (c->IsOpen()) { c->Close(true); } } - - // remove it from the sequence - seq->clips.removeLast(); } done_ = false; @@ -392,54 +365,52 @@ void AddClipCommand::doUndo() { void AddClipCommand::doRedo() { if (!done_) { - link_offset_ = seq->clips.size(); - for (int i=0;ilinked.size();j++) { - original->linked[j] += link_offset_; - } - - } - - seq->clips.append(original); + original->track()->AddClip(original); + } } done_ = true; } } -LinkCommand::LinkCommand() { - link = true; +LinkCommand::LinkCommand(const QVector& clips, bool link) : + clips_(clips), + link_(link) +{ } void LinkCommand::doUndo() { - for (int i=0;iclips.at(clips.at(i)); - if (link) { + for (int i=0;ilinked.clear(); } else { - c->linked = old_links.at(i); + c->linked = old_links_.at(i); } } } void LinkCommand::doRedo() { - old_links.clear(); - for (int i=0;iclips.at(clips.at(i)); - if (link) { - for (int j=0;jlinked.append(clips.at(j)); + c->linked.append(clips_.at(j)); } } + } else { - old_links.append(c->linked); + + old_links_.append(c->linked); c->linked.clear(); + } } } @@ -472,12 +443,14 @@ ReplaceMediaCommand::ReplaceMediaCommand(MediaPtr i, QString s) { void ReplaceMediaCommand::replace(QString& filename) { // close any clips currently using this media - QVector all_sequences = panel_project->list_all_project_sequences(); + QVector all_sequences = olive::project_model.GetAllSequences(); for (int i=0;ito_sequence().get(); - for (int j=0;jclips.size();j++) { - ClipPtr c = s->clips.at(j); - if (c != nullptr && c->media() == item.get() && c->IsOpen()) { + + QVector sequence_clips = all_sequences.at(i)->to_sequence()->GetAllClips(); + + for (int j=0;jmedia() == item.get() && c->IsOpen()) { c->Close(true); c->replaced = true; } @@ -487,7 +460,7 @@ void ReplaceMediaCommand::replace(QString& filename) { // replace media QStringList files; files.append(filename); - panel_project->process_file_list(files, false, item, nullptr); + olive::project_model.process_file_list(files, false, item, nullptr); PreviewGenerator::AnalyzeMedia(item.get()); } @@ -513,7 +486,7 @@ void ReplaceClipMediaCommand::replace(bool undo) { } for (int i=0;iIsOpen()) { c->Close(true); } @@ -806,20 +779,25 @@ void SetBool::doRedo() { *boolean = new_setting; } -SetSelectionsCommand::SetSelectionsCommand(Sequence *s) { - seq = s; - done = true; +SetSelectionsCommand::SetSelectionsCommand(Sequence *s, + const QVector &old_data, + const QVector &new_data) : + old_data_(old_data), + new_data_(new_data), + done_(true) +{ + } void SetSelectionsCommand::doUndo() { - seq->selections = old_data; - done = false; + seq_->SetSelections(old_data_); + done_ = false; } void SetSelectionsCommand::doRedo() { - if (!done) { - seq->selections = new_data; - done = true; + if (!done_) { + seq_->SetSelections(new_data_); + done_ = true; } } @@ -858,15 +836,12 @@ void EditSequenceCommand::update() { // Update sequence's tooltip item->update_tooltip(); - for (int i=0;iclips.size();i++) { - if (seq->clips.at(i) != nullptr) { - seq->clips.at(i)->refresh(); + QVector all_clips = seq->GetAllClips(); + for (int i=0;irefresh(); } } - - if (olive::ActiveSequence == seq) { - olive::Global->set_sequence(seq); - } } SetInt::SetInt(int* pointer, int new_value) { @@ -904,7 +879,11 @@ void CloseAllClipsCommand::doUndo() { } void CloseAllClipsCommand::doRedo() { - olive::ActiveSequence->Close(); + QVector sequences = olive::project_model.GetAllSequences(); + + for (int i=0;ito_sequence()->Close(); + } } UpdateFootageTooltip::UpdateFootageTooltip(Media *i) { @@ -938,13 +917,13 @@ RemoveClipsFromClipboard::RemoveClipsFromClipboard(int index) { RemoveClipsFromClipboard::~RemoveClipsFromClipboard() {} void RemoveClipsFromClipboard::doUndo() { - clipboard.insert(pos, clip); + olive::clipboard.Insert(pos, clip); done = false; } void RemoveClipsFromClipboard::doRedo() { - clip = std::static_pointer_cast(clipboard.at(pos)); - clipboard.removeAt(pos); + clip = std::static_pointer_cast(olive::clipboard.Get(pos)); + olive::clipboard.RemoveAt(pos); done = true; } @@ -985,7 +964,7 @@ void ReloadEffectsCommand::doRedo() { panel_effect_controls->Reload(); } -RippleAction::RippleAction(Sequence *is, long ipoint, long ilength, const QVector &iignore) : +RippleAction::RippleAction(Sequence *is, long ipoint, long ilength, const QVector &iignore) : s(is), point(ipoint), length(ilength), @@ -1000,13 +979,21 @@ void RippleAction::doUndo() { void RippleAction::doRedo() { ca = new ComboAction(); - for (int i=0;iclips.size();i++) { - if (!ignore.contains(i)) { - ClipPtr c = s->clips.at(i); - if (c != nullptr) { - if (c->timeline_in() >= point) { - c->move(ca, length, length, 0, 0, true, true); - } + + QVector all_clips = s->GetAllClips(); + + for (int i=0;itimeline_in() >= point) { + s->MoveClip(c, + ca, + length, + length, + 0, + c->track(), + true, + true); } } } @@ -1092,8 +1079,9 @@ void SetIsKeyframing::doRedo() { row->SetKeyframingInternal(b); } -RefreshClips::RefreshClips(Media *m) { - media = m; +RefreshClips::RefreshClips(Media *m) : + media(m) +{ } void RefreshClips::doUndo() { @@ -1102,12 +1090,14 @@ void RefreshClips::doUndo() { void RefreshClips::doRedo() { // close any clips currently using this media - QVector all_sequences = panel_project->list_all_project_sequences(); + QVector all_sequences = olive::project_model.GetAllSequences(); for (int i=0;ito_sequence().get(); - for (int j=0;jclips.size();j++) { - Clip* c = s->clips.at(j).get(); - if (c != nullptr && c->media() == media) { + + QVector sequence_clips = all_sequences.at(i)->to_sequence().get()->GetAllClips(); + + for (int j=0;jmedia() == media || media == nullptr) { c->replaced = true; c->refresh(); } diff --git a/undo/undo.h b/undo/undo.h index cb58f304a..21a50113e 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -77,21 +77,21 @@ private: class MoveClipAction : public OliveAction { public: - MoveClipAction(Clip* c, long iin, long iout, long iclip_in, int itrack, bool irelative); + MoveClipAction(ClipPtr c, long iin, long iout, long iclip_in, Track* itrack, bool irelative); virtual void doUndo() override; virtual void doRedo() override; private: - Clip* clip; + ClipPtr clip; long old_in; long old_out; long old_clip_in; - int old_track; + Track* old_track; long new_in; long new_out; long new_clip_in; - int new_track; + Track* new_track; bool relative; @@ -100,14 +100,14 @@ private: class RippleAction : public OliveAction { public: - RippleAction(Sequence* is, long ipoint, long ilength, const QVector& iignore); + RippleAction(Sequence* is, long ipoint, long ilength, const QVector &iignore); virtual void doUndo() override; virtual void doRedo() override; private: Sequence* s; long point; long length; - QVector ignore; + QVector ignore; ComboAction* ca; }; @@ -118,20 +118,9 @@ public: virtual void doRedo() override; private: ClipPtr clip_; - QVector clips_linked_to_this_one_; }; -class ChangeSequenceAction : public OliveAction { -public: - ChangeSequenceAction(SequencePtr s); - virtual void doUndo() override; - virtual void doRedo() override; -private: - SequencePtr old_sequence; - SequencePtr new_sequence; -}; - class AddEffectCommand : public OliveAction { public: AddEffectCommand(Clip* c, EffectPtr e, const EffectMeta* m, int insert_pos = -1); @@ -223,26 +212,24 @@ private: class AddClipCommand : public OliveAction { public: - AddClipCommand(Sequence* s, QVector& add); + AddClipCommand(const QVector& add); virtual void doUndo() override; virtual void doRedo() override; private: - Sequence* seq; - QVector clips; - int link_offset_; + Track* track_; + QVector clips_; bool done_; }; class LinkCommand : public OliveAction { public: - LinkCommand(); + LinkCommand(const QVector &clips, bool link); virtual void doUndo() override; virtual void doRedo() override; - Sequence* s; - QVector clips; - bool link; private: - QVector< QVector > old_links; + QVector clips_; + bool link_; + QVector< QVector > old_links_; }; class CheckboxCommand : public OliveAction { @@ -274,7 +261,7 @@ public: ReplaceClipMediaCommand(Media *, Media *, bool); virtual void doUndo() override; virtual void doRedo() override; - QVector clips; + QVector clips; private: Media* old_media; Media* new_media; @@ -423,14 +410,14 @@ private: class SetSelectionsCommand : public OliveAction { public: - SetSelectionsCommand(Sequence* s); + SetSelectionsCommand(Sequence* s, const QVector& old_data, const QVector& new_data); virtual void doUndo() override; virtual void doRedo() override; - QVector old_data; - QVector new_data; private: - Sequence* seq; - bool done; + QVector old_data_; + QVector new_data_; + Sequence* seq_; + bool done_; }; class EditSequenceCommand : public OliveAction { diff --git a/undo/undostack.cpp b/undo/undostack.cpp index 5eb605390..7fa229777 100644 --- a/undo/undostack.cpp +++ b/undo/undostack.cpp @@ -20,4 +20,4 @@ #include "undostack.h" -QUndoStack olive::UndoStack; +QUndoStack olive::undo_stack; diff --git a/undo/undostack.h b/undo/undostack.h index f876dd8b8..35bc92584 100644 --- a/undo/undostack.h +++ b/undo/undostack.h @@ -27,7 +27,7 @@ namespace olive { /** * @brief Global undo stack object */ -extern QUndoStack UndoStack; +extern QUndoStack undo_stack; } #endif // UNDOSTACK_H From 365d96f83e4809d6e6c599e43ff1e1480e6a37b0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 3 Apr 2019 02:05:15 +1100 Subject: [PATCH 066/133] fixed clip adding --- panels/timeline.cpp | 4 ++-- project/sourcescommon.cpp | 2 +- timeline/track.cpp | 5 +++-- ui/timelinearea.cpp | 14 ++++++++++++++ ui/timelinelabel.cpp | 12 +++++++++--- ui/timelineview.cpp | 5 +++++ undo/undo.cpp | 1 + 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index dbfbf1dc6..c1f0d9327 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -162,11 +162,11 @@ void Timeline::OpenSequence(SequencePtr s) } Timeline* t = new Timeline(olive::MainWindow); - panel_timeline.append(t); - olive::MainWindow->addDockWidget(Qt::BottomDockWidgetArea, t); olive::MainWindow->tabifyDockWidget(panel_timeline.last(), t); t->SetSequence(s); + t->show(); t->raise(); + panel_timeline.append(t); } void Timeline::CloseSequence(Sequence *s) diff --git a/project/sourcescommon.cpp b/project/sourcescommon.cpp index b94daab3d..cff3d855f 100644 --- a/project/sourcescommon.cpp +++ b/project/sourcescommon.cpp @@ -82,7 +82,7 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it selected_items = items; QAction* import_action = menu.addAction(tr("Import...")); - QObject::connect(import_action, SIGNAL(triggered(bool)), project_parent, SLOT(import_dialog())); + QObject::connect(import_action, SIGNAL(triggered(bool)), olive::Global.get(), SLOT(open_import_dialog())); Menu* new_menu = new Menu(tr("New")); menu.addMenu(new_menu); diff --git a/timeline/track.cpp b/timeline/track.cpp index 4ae915c16..3e35a1e54 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -14,7 +14,8 @@ Track::Track(TrackList* parent, Type type) : type_(type), muted_(false), soloed_(false), - locked_(false) + locked_(false), + height_(olive::timeline::kTrackDefaultHeight) { } @@ -79,7 +80,7 @@ void Track::AddClip(ClipPtr clip) } clips_.append(clip); - if (clip->track() != nullptr) { + if (clip->track() != nullptr && clip->track() != this) { clip->track()->RemoveClip(clip.get()); } clip->set_track(this); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 69f5c3bd1..b3dee106d 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -6,10 +6,15 @@ TimelineArea::TimelineArea(Timeline* timeline) : alignment_(olive::timeline::kAlignmentTop) { QHBoxLayout* layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); // LABELS QWidget* label_container = new QWidget(); + label_container->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); label_container_layout_ = new QVBoxLayout(label_container); + label_container_layout_->setMargin(0); + label_container_layout_->setSpacing(0); layout->addWidget(label_container); // VIEW @@ -35,13 +40,22 @@ void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) track_list_ = nullptr; + labels_.clear(); + } else { track_list_ = sequence->GetTrackList(track_list); + labels_.resize(track_list_->TrackCount()); + for (int i=0;i(); + label_container_layout_->addWidget(labels_[i].get()); + } + } view_->SetTrackList(track_list_); + } void TimelineArea::RefreshLabels() diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp index 937042408..9148ca41e 100644 --- a/ui/timelinelabel.cpp +++ b/ui/timelinelabel.cpp @@ -12,19 +12,25 @@ TimelineLabel::TimelineLabel() : QLabel* label = new QLabel("Track!"); layout->addWidget(label); + mute_button_ = new QPushButton("M"); + QSize fixed_size(mute_button_->sizeHint().height(), mute_button_->sizeHint().height()); + fixed_size *= 0.75; + mute_button_->setFixedSize(fixed_size); + mute_button_->setStyleSheet("QPushButton::checked { background: red; }"); mute_button_->setCheckable(true); - mute_button_->setFlat(true); layout->addWidget(mute_button_); solo_button_ = new QPushButton("S"); + solo_button_->setFixedSize(fixed_size); + solo_button_->setStyleSheet("QPushButton::checked { background: yellow; }"); solo_button_->setCheckable(true); - solo_button_->setFlat(true); layout->addWidget(solo_button_); lock_button_ = new QPushButton("L"); + lock_button_->setFixedSize(fixed_size); + lock_button_->setStyleSheet("QPushButton::checked { background: gray; }"); lock_button_->setCheckable(true); - lock_button_->setFlat(true); layout->addWidget(lock_button_); } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index bdf705443..38403af41 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2891,7 +2891,12 @@ void TimelineView::paintEvent(QPaintEvent*) { Track* track = track_list_->TrackAt(i); + qDebug() << "track clip cound was" << track->ClipCount(); + for (int j=0;jClipCount();j++) { + + qDebug() << "going to draw a clip!!!"; + Clip* clip = track->GetClip(j).get(); QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), diff --git a/undo/undo.cpp b/undo/undo.cpp index dd603d652..98056807b 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -369,6 +369,7 @@ void AddClipCommand::doRedo() { ClipPtr original = clips_.at(i); if (original != nullptr) { + qDebug() << "h"; original->track()->AddClip(original); } } From d504ee6302ccda942899a8c17787cc36fdac56da Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 3 Apr 2019 12:05:54 +1100 Subject: [PATCH 067/133] connect timelines to sequence viewer --- panels/timeline.cpp | 11 +++++++-- panels/timeline.h | 1 + panels/viewer.h | 4 ++-- rendering/audio.cpp | 4 ++++ rendering/exportthread.cpp | 3 ++- timeline/timelinefunctions.cpp | 4 ++-- timeline/track.cpp | 25 +++++++++++++++++++++ timeline/track.h | 5 +++++ ui/clickablelabel.cpp | 5 +++++ ui/clickablelabel.h | 8 ++++--- ui/timelinearea.cpp | 1 + ui/timelinelabel.cpp | 41 +++++++++++++++++++++++++++------- ui/timelinelabel.h | 6 +++++ ui/timelineview.cpp | 31 +++++++++++++------------ 14 files changed, 115 insertions(+), 34 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index c1f0d9327..8483aaaeb 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -114,9 +114,9 @@ Timeline::Timeline(QWidget *parent) : toolArrowButton->click(); connect(horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(setScroll(int))); - //connect(videoScrollbar, SIGNAL(valueChanged(int)), video_area, SLOT(setScroll(int))); - //connect(audioScrollbar, SIGNAL(valueChanged(int)), audio_area, SLOT(setScroll(int))); connect(horizontalScrollBar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); + connect(this, SIGNAL(SequenceChanged(SequencePtr)), panel_sequence_viewer, SLOT(set_sequence(SequencePtr))); + connect(this, SIGNAL(visibilityChanged(bool)), this, SLOT(visibility_changed_slot(bool))); update_sequence(); @@ -1051,6 +1051,13 @@ void Timeline::set_tool() { } } +void Timeline::visibility_changed_slot(bool visibility) +{ + if (visibility) { + emit SequenceChanged(sequence_); + } +} + void olive::timeline::MultiplyTrackSizesByDPI() { kTrackDefaultHeight *= QApplication::desktop()->devicePixelRatio(); diff --git a/panels/timeline.h b/panels/timeline.h index 72f68feb7..b3dc3517b 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -185,6 +185,7 @@ private slots: void transition_menu_select(QAction*); void resize_move(double d); void set_tool(); + void visibility_changed_slot(bool visibility); private: SequencePtr sequence_; diff --git a/panels/viewer.h b/panels/viewer.h index 80485d26b..9ce04ee39 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -53,7 +53,6 @@ public: Mode mode(); virtual bool focused() override; - bool is_main_sequence(); void set_media(Media *m); void compose(); void set_playpause_icon(bool play); @@ -110,6 +109,8 @@ protected: virtual void resizeEvent(QResizeEvent *event) override; public slots: + void set_sequence(SequencePtr s); + void play_wake(); void go_to_start(); void go_to_in(); @@ -138,7 +139,6 @@ private slots: private: void update_window_title(); void clean_created_seq(); - void set_sequence(SequencePtr s); bool created_sequence; long cached_end_frame; QString panel_name; diff --git a/rendering/audio.cpp b/rendering/audio.cpp index e5a7c0747..780d32107 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -209,6 +209,8 @@ void AudioSenderThread::run() { int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { // send audio to device + audio_write_lock.lock(); + qint64 actual_write = audio_io_device->write(reinterpret_cast(audio_ibuffer)+offset, max); qint64 audio_ibuffer_limit = audio_ibuffer_read + actual_write; @@ -239,6 +241,8 @@ int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { audio_ibuffer_read = audio_ibuffer_limit; + audio_write_lock.unlock(); + return actual_write; } diff --git a/rendering/exportthread.cpp b/rendering/exportthread.cpp index 9bb58ed51..fc4580ebb 100644 --- a/rendering/exportthread.cpp +++ b/rendering/exportthread.cpp @@ -160,7 +160,8 @@ bool ExportThread::SetupVideo() { break; } break; - + default: + break; } // Set export to be multithreaded diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index 3e776b78b..e37a90f89 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -119,7 +119,7 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jaudio_tracks.size();j++) { if (m->audio_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeAudio)->First() + j; + g.track = seq->GetTrackList(Track::kTypeAudio)->TrackAt(j); g.media_stream = m->audio_tracks.at(j).file_index; ghosts.append(g); } @@ -130,7 +130,7 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jvideo_tracks.size();j++) { if (m->video_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeVideo)->First() + j; + g.track = seq->GetTrackList(Track::kTypeVideo)->TrackAt(j); g.media_stream = m->video_tracks.at(j).file_index; ghosts.append(g); } diff --git a/timeline/track.cpp b/timeline/track.cpp index 3e35a1e54..079f3f236 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -73,6 +73,31 @@ void Track::set_height(int h) height_ = qMax(h, olive::timeline::kTrackMinHeight); } +QString Track::name() +{ + if (name_.isEmpty()) { + int display_index = Index() + 1; + + switch (type_) { + case kTypeVideo: + return tr("Video %1").arg(display_index); + case kTypeAudio: + return tr("Audio %1").arg(display_index); + case kTypeSubtitle: + return tr("Subtitle %1").arg(display_index); + default: + return tr("Unknown %1").arg(display_index); + } + } + + return name_; +} + +void Track::SetName(const QString &s) +{ + name_ = s; +} + void Track::AddClip(ClipPtr clip) { if (clips_.contains(clip)) { diff --git a/timeline/track.h b/timeline/track.h index b21b67c7d..3614064a0 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -53,6 +53,9 @@ public: int height(); void set_height(int h); + QString name(); + void SetName(const QString& s); + void AddClip(ClipPtr clip); int ClipCount(); ClipPtr GetClip(int i); @@ -105,6 +108,8 @@ private: bool muted_; bool soloed_; bool locked_; + + QString name_; }; #endif // TRACK_H diff --git a/ui/clickablelabel.cpp b/ui/clickablelabel.cpp index 1b8352fa9..dcd2c1c72 100644 --- a/ui/clickablelabel.cpp +++ b/ui/clickablelabel.cpp @@ -31,3 +31,8 @@ ClickableLabel::ClickableLabel(const QString &text, QWidget *parent, Qt::WindowF void ClickableLabel::mousePressEvent(QMouseEvent *) { emit clicked(); } + +void ClickableLabel::mouseDoubleClickEvent(QMouseEvent *ev) +{ + emit double_clicked(); +} diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h index aba75c6ec..2cf9d6848 100644 --- a/ui/clickablelabel.h +++ b/ui/clickablelabel.h @@ -31,11 +31,13 @@ class ClickableLabel : public QLabel { Q_OBJECT public: - ClickableLabel(QWidget * parent = 0, Qt::WindowFlags f = 0); - ClickableLabel(const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0); - void mousePressEvent(QMouseEvent *ev); + ClickableLabel(QWidget * parent = nullptr, Qt::WindowFlags f = nullptr); + ClickableLabel(const QString & text, QWidget * parent = nullptr, Qt::WindowFlags f = nullptr); + virtual void mousePressEvent(QMouseEvent *ev) override; + virtual void mouseDoubleClickEvent(QMouseEvent *ev) override; signals: void clicked(); + void double_clicked(); }; #endif // CLICKABLELABEL_H diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index b3dee106d..739180d20 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -49,6 +49,7 @@ void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) labels_.resize(track_list_->TrackCount()); for (int i=0;i(); + labels_[i]->SetTrack(track_list_->TrackAt(i)); label_container_layout_->addWidget(labels_[i].get()); } diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp index 9148ca41e..5216f442a 100644 --- a/ui/timelinelabel.cpp +++ b/ui/timelinelabel.cpp @@ -1,17 +1,19 @@ #include "timelinelabel.h" #include -#include #include +#include TimelineLabel::TimelineLabel() : track_(nullptr) { QHBoxLayout* layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(layout->spacing()/2); - QLabel* label = new QLabel("Track!"); - layout->addWidget(label); - + label_ = new ClickableLabel(); + layout->addWidget(label_); + connect(label_, SIGNAL(double_clicked()), this, SLOT(RenameTrack())); mute_button_ = new QPushButton("M"); QSize fixed_size(mute_button_->sizeHint().height(), mute_button_->sizeHint().height()); @@ -44,13 +46,36 @@ void TimelineLabel::SetTrack(Track *track) track_ = track; - if (track != nullptr) { - mute_button_->setChecked(track->IsMuted()); - solo_button_->setChecked(track->IsSoloed()); - lock_button_->setChecked(track->IsLocked()); + if (track_ != nullptr) { + UpdateState(); connect(mute_button_, SIGNAL(toggled(bool)), track_, SLOT(SetMuted(bool))); connect(solo_button_, SIGNAL(toggled(bool)), track_, SLOT(SetSoloed(bool))); connect(lock_button_, SIGNAL(toggled(bool)), track_, SLOT(SetLocked(bool))); } } + +void TimelineLabel::UpdateState() +{ + label_->setText(track_->name()); + + mute_button_->setChecked(track_->IsMuted()); + solo_button_->setChecked(track_->IsSoloed()); + lock_button_->setChecked(track_->IsLocked()); +} + +void TimelineLabel::RenameTrack() +{ + bool ok; + QString new_name = QInputDialog::getText(this, + tr("Rename Track"), + tr("Enter the new name for this track"), + QLineEdit::Normal, + track_->name(), + &ok); + if (ok) { + track_->SetName(new_name); + + UpdateState(); + } +} diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h index 6ca1d983f..b4fcdb1da 100644 --- a/ui/timelinelabel.h +++ b/ui/timelinelabel.h @@ -4,6 +4,7 @@ #include #include +#include "ui/clickablelabel.h" #include "timeline/track.h" class TimelineLabel : public QWidget @@ -13,12 +14,17 @@ public: TimelineLabel(); void SetTrack(Track* track); + void UpdateState(); private: QPushButton* mute_button_; QPushButton* solo_button_; QPushButton* lock_button_; + ClickableLabel* label_; + Track* track_; +private slots: + void RenameTrack(); }; using TimelineLabelPtr = std::shared_ptr; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 38403af41..c147c30c9 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -534,6 +534,8 @@ void TimelineView::dropEvent(QDropEvent* event) { s->AddClipsFromGhosts(ca, ParentTimeline()->ghosts); + ParentTimeline()->ghosts.clear(); + olive::undo_stack.push(ca); setFocus(); @@ -1905,20 +1907,15 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } if (ParentTimeline()->importing) { - /* - if ((ParentTimeline()->video_ghosts && mouse_track->type() == Track::kTypeVideo) - || (ParentTimeline()->audio_ghosts && mouse_track->type() == Track::kTypeAudio)) { - int abs_track_diff = abs(track_diff); - if (g.old_track < 0) { // clip is video - g.track -= abs_track_diff; - } else { // clip is audio - g.track += abs_track_diff; - } + + if (mouse_track != nullptr) { + g.track = g.track->track_list()->TrackAt(mouse_track->Index()); } - */ - g.track = track_list_->First(); + } else if (g.old_track->type() == ParentTimeline()->drag_track_start->type()) { + g.track += track_diff; + } } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { if (ParentTimeline()->transition_tool_open_clip != nullptr @@ -2891,12 +2888,8 @@ void TimelineView::paintEvent(QPaintEvent*) { Track* track = track_list_->TrackAt(i); - qDebug() << "track clip cound was" << track->ClipCount(); - for (int j=0;jClipCount();j++) { - qDebug() << "going to draw a clip!!!"; - Clip* clip = track->GetClip(j).get(); QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), @@ -3291,11 +3284,13 @@ void TimelineView::paintEvent(QPaintEvent*) { } // draw border + /* p.setPen(QColor(0, 0, 0, 64)); int edge_y = 0; p.drawLine(0, edge_y, rect().width(), edge_y); edge_y = rect().height()-1; p.drawLine(0, edge_y, rect().width(), edge_y); + */ // draw snap point if (olive::timeline::snapped) { @@ -3318,8 +3313,11 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { y += scroll; int heights = 0; + for (int i=0;iTrackCount();i++) { - int new_heights = heights + track_list_->TrackAt(i)->height() + 1; + int new_heights = heights + 1; + + new_heights += track_list_->TrackAt(i)->height(); if (y >= heights && y < new_heights) { return track_list_->TrackAt(i); @@ -3333,6 +3331,7 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { int TimelineView::getScreenPointFromTrack(Track *track) { int point = 0; + for (int i=0;iTrackCount();i++) { if (track == track_list_->TrackAt(i)) { return point; From f3178ff071a61cb5761a5761e8fdf376b837beaa Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 3 Apr 2019 12:15:03 +1100 Subject: [PATCH 068/133] solo and mute buttons are now implemented --- timeline/clip.cpp | 3 ++- timeline/track.cpp | 21 +++++++++++++++++++++ timeline/track.h | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/timeline/clip.cpp b/timeline/clip.cpp index e1d212058..cac020813 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -86,7 +86,8 @@ bool Clip::IsActiveAt(long timecode) return enabled() && timeline_in(true) < timecode && timeline_out(true) > timecode - && timecode - timeline_in(true) + clip_in(true) < media_length(); + && timecode - timeline_in(true) + clip_in(true) < media_length() + && !track()->IsEffectivelyMuted(); } bool Clip::IsSelected(bool containing) diff --git a/timeline/track.cpp b/timeline/track.cpp index 079f3f236..7778d10b7 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -345,6 +345,27 @@ long Track::GetEndFrame() return end_frame; } +bool Track::IsEffectivelyMuted() +{ + // Check if this track is muted + if (muted_) { + return true; + } + + // Check if any tracks are soloed + bool a_track_is_soloed = false; + + for (int i=0;iTrackCount();i++) { + if (track_list()->TrackAt(i)->IsSoloed()) { + a_track_is_soloed = true; + break; + } + } + + // Return if a track is soloed and this track is not soloed + return (a_track_is_soloed && !soloed_); +} + bool Track::IsMuted() { return muted_; diff --git a/timeline/track.h b/timeline/track.h index 3614064a0..52f98a7a5 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -86,6 +86,7 @@ public: long GetEndFrame(); + bool IsEffectivelyMuted(); bool IsMuted(); bool IsSoloed(); bool IsLocked(); From 673bcc0bf70179e1395138b7e3c69cd56ef8325b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 3 Apr 2019 23:59:36 +1100 Subject: [PATCH 069/133] fixed last references to track as an integer --- dialogs/speeddialog.cpp | 2 +- panels/timeline.cpp | 8 ++++++-- rendering/renderfunctions.cpp | 4 ++-- timeline/clip.cpp | 2 +- timeline/track.h | 2 ++ ui/timelinearea.cpp | 4 +++- ui/timelineview.cpp | 20 ++++++++++++++++---- 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index 0e11063f9..b1482e2eb 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -418,7 +418,7 @@ void SpeedDialog::accept() { if (i > 0 && !qFuzzyCompare(cached_speed, c->speed().value)) { can_change_all = false; } - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { if (qIsNaN(cached_fr)) { cached_fr = c->media_frame_rate(); } else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) { diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 8483aaaeb..7308b6c72 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -90,7 +90,7 @@ Timeline::Timeline(QWidget *parent) : headers->viewer = panel_sequence_viewer; - video_area->SetAlignment(olive::timeline::kAlignmentBottom); + //video_area->SetAlignment(olive::timeline::kAlignmentBottom); tool_buttons.append(toolArrowButton); tool_buttons.append(toolEditButton); @@ -993,8 +993,11 @@ void Timeline::setup_ui() { timeline_area_layout->setSpacing(0); timeline_area_layout->setContentsMargins(0, 0, 0, 0); + QHBoxLayout* timeline_header_layout = new QHBoxLayout(); + timeline_header_layout->addSpacing(olive::timeline::kTimelineLabelFixedWidth); headers = new TimelineHeader(); - timeline_area_layout->addWidget(headers); + timeline_header_layout->addWidget(headers); + timeline_area_layout->addLayout(timeline_header_layout); editAreas = new QWidget(); QHBoxLayout* editAreaLayout = new QHBoxLayout(editAreas); @@ -1063,4 +1066,5 @@ void olive::timeline::MultiplyTrackSizesByDPI() kTrackDefaultHeight *= QApplication::desktop()->devicePixelRatio(); kTrackMinHeight *= QApplication::desktop()->devicePixelRatio(); kTrackHeightIncrement *= QApplication::desktop()->devicePixelRatio(); + kTimelineLabelFixedWidth *= QApplication::desktop()->devicePixelRatio(); } diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 719554479..40e22cc0d 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -370,7 +370,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { if (got_mutex && c->IsOpen()) { // if clip is a video clip - if (c->track() < 0) { + if (c->type() == Track::kTypeVideo) { // textureID variable contains texture to be drawn on screen at the end GLuint textureID = 0; @@ -750,7 +750,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // == END FINAL DRAW ON SEQUENCE BUFFER == } } - } else { + } else if (c->type() == Track::kTypeAudio) { if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { params.nests.append(c); compose_sequence(params); diff --git a/timeline/clip.cpp b/timeline/clip.cpp index cac020813..2d15bc18b 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -721,7 +721,7 @@ bool Clip::Retrieve() bool Clip::UsesCacher() { - return track() >= 0 || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE); + return type() == Track::kTypeAudio || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE); } ClipSpeed::ClipSpeed() : diff --git a/timeline/track.h b/timeline/track.h index 52f98a7a5..c684014f5 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -24,6 +24,8 @@ namespace olive { extern int kTrackDefaultHeight; extern int kTrackMinHeight; extern int kTrackHeightIncrement; + + extern int kTimelineLabelFixedWidth; } } diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 739180d20..982d447b6 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -1,5 +1,7 @@ #include "timelinearea.h" +int olive::timeline::kTimelineLabelFixedWidth = 200; + TimelineArea::TimelineArea(Timeline* timeline) : timeline_(timeline), track_list_(nullptr), @@ -11,7 +13,7 @@ TimelineArea::TimelineArea(Timeline* timeline) : // LABELS QWidget* label_container = new QWidget(); - label_container->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + label_container->setFixedWidth(olive::timeline::kTimelineLabelFixedWidth); label_container_layout_ = new QVBoxLayout(label_container); label_container_layout_->setMargin(0); label_container_layout_->setSpacing(0); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index c147c30c9..f78c02fda 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -536,6 +536,8 @@ void TimelineView::dropEvent(QDropEvent* event) { ParentTimeline()->ghosts.clear(); + ParentTimeline()->importing = false; + olive::undo_stack.push(ca); setFocus(); @@ -1914,7 +1916,10 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } else if (g.old_track->type() == ParentTimeline()->drag_track_start->type()) { - g.track += track_diff; + if (mouse_track != nullptr) { + g.track = g.track->track_list()->TrackAt(mouse_track->Index()); + } + //g.track += track_diff; } } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { @@ -3310,11 +3315,18 @@ void TimelineView::resizeEvent(QResizeEvent *) { // ************************************** Track *TimelineView::getTrackFromScreenPoint(int y) { + if (y < 0 || y > height()) { + return nullptr; + } + y += scroll; int heights = 0; - for (int i=0;iTrackCount();i++) { + //for (int i=0;iTrackCount();i++) { + int i = 0; + while (true) { + int new_heights = heights + 1; new_heights += track_list_->TrackAt(i)->height(); @@ -3324,9 +3336,9 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { } heights = new_heights; - } - return nullptr; + i++; + } } int TimelineView::getScreenPointFromTrack(Track *track) { From e708791e90d3d68bbe5d19130038eadff659ee94 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 4 Apr 2019 03:07:04 +1100 Subject: [PATCH 070/133] reimplemented rectangle selection --- timeline/clip.cpp | 2 -- timeline/track.cpp | 3 ++- ui/timelineview.cpp | 58 ++++++++++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 2d15bc18b..78ad4f74a 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -706,8 +706,6 @@ bool Clip::Retrieve() texture_timestamp = frame->pts; - //} - ret = true; } else { qCritical() << "Failed to retrieve frame for clip" << name(); diff --git a/timeline/track.cpp b/timeline/track.cpp index 7778d10b7..740f7a1e0 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -261,11 +261,12 @@ bool Track::IsTransitionSelected(Transition *t) void Track::SelectArea(long in, long out) { selections_.append(Selection(in, out, this)); + Selection::Tidy(selections_); } void Track::SelectClip(Clip* c) { - selections_.append(Selection(c->timeline_in(), c->timeline_out(), this)); + SelectArea(c->timeline_in(), c->timeline_out()); } void Track::SelectAll() diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index f78c02fda..73a2ba77c 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2399,39 +2399,45 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { long frame_min = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); long frame_max = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); - int track_min = qMin(ParentTimeline()->drag_track_start->Index(), ParentTimeline()->cursor_track->Index()); - int track_max = qMax(ParentTimeline()->drag_track_start->Index(), ParentTimeline()->cursor_track->Index()); - // determine which clips are in this rectangular selection QVector selected_clips; for (int j=0;jTrackCount();j++) { Track* track = track_list_->TrackAt(j); - for (int i=0;iClipCount();i++) { - Clip* clip = track->GetClip(i).get(); - if (clip->track()->Index() >= track_min && - clip->track()->Index() <= track_max && - !(clip->timeline_in() < frame_min && clip->timeline_out() < frame_min) && - !(clip->timeline_in() > frame_max && clip->timeline_out() > frame_max)) { + int track_top = getScreenPointFromTrack(track); + int track_bottom = track_top + track->height(); + int rect_top = qMin(ParentTimeline()->rect_select_rect.top(), ParentTimeline()->rect_select_rect.bottom()); + int rect_bottom = qMax(ParentTimeline()->rect_select_rect.top(), ParentTimeline()->rect_select_rect.bottom()); - // create a group of the clip (and its links if alt is not pressed) - QVector session_clips; - session_clips.append(clip); + // See if this track touches this rectangle at all + if (!(track_bottom < rect_top + || track_top > rect_bottom)) { - if (!alt) { - session_clips.append(clip->linked); - } + // Loop through track's clips for clips touching this rectangle + for (int i=0;iClipCount();i++) { + Clip* clip = track->GetClip(i).get(); + if (!(clip->timeline_out() < frame_min || clip->timeline_in() > frame_max) ) { - // for each of these clips, see if clip has already been added - - // this can easily happen due to adding linked clips - for (int j=0;j session_clips; + session_clips.append(clip); - if (!selected_clips.contains(c)) { - selected_clips.append(c); + if (!alt) { + session_clips.append(clip->linked); + } + + // for each of these clips, see if clip has already been added - + // this can easily happen due to adding linked clips + for (int j=0;jTrackCount();i++) { - int i = 0; - while (true) { + for (int i=0;iTrackCount();i++) { +// int i = 0; +// while (true) { int new_heights = heights + 1; @@ -3337,8 +3343,10 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { heights = new_heights; - i++; +// i++; } + + return nullptr; } int TimelineView::getScreenPointFromTrack(Track *track) { From d7091c2b10f425486efe848acff7c357f4fb7858 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 4 Apr 2019 19:35:52 +1100 Subject: [PATCH 071/133] better attachment between labels and tracks --- timeline/track.cpp | 1 + timeline/track.h | 3 +++ timeline/tracklist.cpp | 9 +++++++++ timeline/tracklist.h | 4 ++++ ui/timelinearea.cpp | 33 ++++++++++++++++----------------- ui/timelinearea.h | 3 +-- ui/timelinelabel.cpp | 9 +++++++++ ui/timelinelabel.h | 1 + ui/timelineview.cpp | 17 +++++++++-------- 9 files changed, 53 insertions(+), 27 deletions(-) diff --git a/timeline/track.cpp b/timeline/track.cpp index 740f7a1e0..469ff66b6 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -71,6 +71,7 @@ int Track::height() void Track::set_height(int h) { height_ = qMax(h, olive::timeline::kTrackMinHeight); + emit HeightChanged(h); } QString Track::name() diff --git a/timeline/track.h b/timeline/track.h index c684014f5..b0e079370 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -98,6 +98,9 @@ public slots: void SetSoloed(bool soloed); void SetLocked(bool locked); +signals: + void HeightChanged(int height); + private: void ResizeClipArray(int new_size); diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index b1a76ac84..5dbb3ac52 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -37,6 +37,8 @@ void TrackList::AddTrack() { Track* track = new Track(this, type_); tracks_.append(track); + + emit TrackCountChanged(); } void TrackList::RemoveTrack(int i) @@ -45,6 +47,8 @@ void TrackList::RemoveTrack(int i) return; } tracks_.removeAt(i); + + emit TrackCountChanged(); } Track *TrackList::First() @@ -52,6 +56,11 @@ Track *TrackList::First() return tracks_.first(); } +Track *TrackList::Last() +{ + return tracks_.last(); +} + int TrackList::TrackCount() { return tracks_.size(); diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 5b7ca2dec..4186cf84b 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -15,6 +15,7 @@ public: void AddTrack(); void RemoveTrack(int i); Track* First(); + Track* Last(); int TrackCount(); int IndexOfTrack(Track* track); Track* TrackAt(int i); @@ -24,6 +25,9 @@ public: Sequence* GetParent(); +signals: + void TrackCountChanged(); + private: void ResizeTrackArray(int i); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 982d447b6..4e4558450 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -2,10 +2,10 @@ int olive::timeline::kTimelineLabelFixedWidth = 200; -TimelineArea::TimelineArea(Timeline* timeline) : +TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignment) : timeline_(timeline), track_list_(nullptr), - alignment_(olive::timeline::kAlignmentTop) + alignment_(alignment) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); @@ -17,10 +17,12 @@ TimelineArea::TimelineArea(Timeline* timeline) : label_container_layout_ = new QVBoxLayout(label_container); label_container_layout_->setMargin(0); label_container_layout_->setSpacing(0); + label_container_layout_->addStretch(); layout->addWidget(label_container); // VIEW view_ = new TimelineView(timeline_); + view_->SetAlignment(alignment_); layout->addWidget(view_); // SCROLLBAR @@ -30,33 +32,25 @@ TimelineArea::TimelineArea(Timeline* timeline) : view_->scrollBar = scrollbar; } -void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) -{ - alignment_ = alignment; - view_->SetAlignment(alignment); -} - void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) { + if (track_list_ != nullptr) { + disconnect(track_list_, SIGNAL(TrackCountChanged()), this, SLOT(RefreshLabels())); + } + if (sequence == nullptr) { track_list_ = nullptr; - labels_.clear(); - } else { track_list_ = sequence->GetTrackList(track_list); - - labels_.resize(track_list_->TrackCount()); - for (int i=0;i(); - labels_[i]->SetTrack(track_list_->TrackAt(i)); - label_container_layout_->addWidget(labels_[i].get()); - } + connect(track_list_, SIGNAL(TrackCountChanged()), this, SLOT(RefreshLabels())); } + RefreshLabels(); + view_->SetTrackList(track_list_); } @@ -64,12 +58,17 @@ void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) void TimelineArea::RefreshLabels() { if (track_list_ == nullptr) { + labels_.clear(); + } else { labels_.resize(track_list_->TrackCount()); for (int i=0;i(); labels_[i]->SetTrack(track_list_->TrackAt(i)); + + label_container_layout_->insertWidget(label_container_layout_->count()-1, labels_[i].get()); } } diff --git a/ui/timelinearea.h b/ui/timelinearea.h index 08f90304c..511152534 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -12,9 +12,8 @@ class TimelineArea : public QWidget { Q_OBJECT public: - TimelineArea(Timeline *timeline); + TimelineArea(Timeline *timeline, olive::timeline::Alignment alignment = olive::timeline::kAlignmentTop); - void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(Sequence* sequence, Track::Type track_list); public slots: void RefreshLabels(); diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp index 5216f442a..9163b6286 100644 --- a/ui/timelinelabel.cpp +++ b/ui/timelinelabel.cpp @@ -52,6 +52,8 @@ void TimelineLabel::SetTrack(Track *track) connect(mute_button_, SIGNAL(toggled(bool)), track_, SLOT(SetMuted(bool))); connect(solo_button_, SIGNAL(toggled(bool)), track_, SLOT(SetSoloed(bool))); connect(lock_button_, SIGNAL(toggled(bool)), track_, SLOT(SetLocked(bool))); + + connect(track_, SIGNAL(HeightChanged(int)), this, SLOT(UpdateHeight(int))); } } @@ -59,6 +61,8 @@ void TimelineLabel::UpdateState() { label_->setText(track_->name()); + UpdateHeight(track_->height()); + mute_button_->setChecked(track_->IsMuted()); solo_button_->setChecked(track_->IsSoloed()); lock_button_->setChecked(track_->IsLocked()); @@ -79,3 +83,8 @@ void TimelineLabel::RenameTrack() UpdateState(); } } + +void TimelineLabel::UpdateHeight(int h) +{ + setFixedHeight(h); +} diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h index b4fcdb1da..0a5a65c3c 100644 --- a/ui/timelinelabel.h +++ b/ui/timelinelabel.h @@ -25,6 +25,7 @@ private: Track* track_; private slots: void RenameTrack(); + void UpdateHeight(int h); }; using TimelineLabelPtr = std::shared_ptr; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 73a2ba77c..777bb5fde 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -1614,7 +1614,6 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { Track* mouse_track = getTrackFromScreenPoint(mouse_pos.y()); long frame_diff = (lock_frame) ? 0 : ParentTimeline()->getTimelineFrameFromScreenPoint(mouse_pos.x()) - ParentTimeline()->drag_frame_start; - int track_diff = ((effective_tool == olive::timeline::TIMELINE_TOOL_SLIDE || ParentTimeline()->transition_select != kTransitionNone) && !ParentTimeline()->importing) ? 0 : mouse_track - ParentTimeline()->drag_track_start; long validator; long earliest_in_point = LONG_MAX; @@ -3321,17 +3320,19 @@ void TimelineView::resizeEvent(QResizeEvent *) { // ************************************** Track *TimelineView::getTrackFromScreenPoint(int y) { - if (y < 0 || y > height()) { - return nullptr; + if (y < 0) { + return track_list_->First(); + } else if (y > height()) { + return track_list_->Last(); } y += scroll; int heights = 0; - for (int i=0;iTrackCount();i++) { -// int i = 0; -// while (true) { +// for (int i=0;iTrackCount();i++) { + int i = 0; + while (true) { int new_heights = heights + 1; @@ -3343,10 +3344,10 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { heights = new_heights; -// i++; + i++; } - return nullptr; +// return nullptr; } int TimelineView::getScreenPointFromTrack(Track *track) { From 9f029bedfaf25a04f1f68d3ffc7ae2a4c5fa83de Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 5 Apr 2019 02:16:12 +1100 Subject: [PATCH 072/133] ghosts can traverse tracks again --- panels/timeline.cpp | 2 +- timeline/ghost.cpp | 2 +- timeline/ghost.h | 6 +- timeline/sequence.cpp | 3 +- timeline/timelinefunctions.cpp | 2 +- timeline/track.cpp | 25 ++++++++ timeline/track.h | 4 ++ ui/timelineview.cpp | 102 ++++++++++++++++++++------------- ui/timelineview.h | 5 ++ 9 files changed, 103 insertions(+), 48 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 7308b6c72..88af64d1e 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -370,7 +370,7 @@ void Timeline::nest() { && c->timeline_out() > g.out))) { // There's a clip occupied by the space taken up by this ghost. Move up a track, and seek again. - g.track = g.track->track_list()->TrackAt(g.track->Index() + 1); + g.track = g.track->Next(); // Restart entire loop again j = -1; diff --git a/timeline/ghost.cpp b/timeline/ghost.cpp index 2e9780709..dccd5ecfd 100644 --- a/timeline/ghost.cpp +++ b/timeline/ghost.cpp @@ -2,5 +2,5 @@ Selection Ghost::ToSelection() const { - return Selection(in, out, track); + return Selection(in, out, track->Sibling(track_movement)); } diff --git a/timeline/ghost.h b/timeline/ghost.h index e0da417d5..cd5dafd5b 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -18,16 +18,18 @@ enum TrimType { struct Ghost { Clip* clip; + long in; long out; - Track* track; long clip_in; long old_in; long old_out; - Track* old_track; long old_clip_in; + Track* track; + int track_movement; + // importing variables Media* media; int media_stream; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 93ab5bf2d..19fef2e98 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -189,12 +189,11 @@ void Sequence::AddClipsFromGhosts(ComboAction* ca, const QVector& ghosts) earliest_point = qMin(earliest_point, g.in); - ClipPtr c = std::make_shared(g.track); + ClipPtr c = std::make_shared(g.track->Sibling(g.track_movement)); c->set_media(g.media, g.media_stream); c->set_timeline_in(g.in); c->set_timeline_out(g.out); c->set_clip_in(g.clip_in); - c->set_track(g.track); if (c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { Footage* m = c->media()->to_footage(); if (m->video_tracks.size() == 0) { diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index e37a90f89..beaf69929 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -165,7 +165,7 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, Ghost& g = ghosts[i]; g.old_in = g.in; g.old_out = g.out; - g.old_track = g.track; + g.track_movement = 0; } return ghosts; diff --git a/timeline/track.cpp b/timeline/track.cpp index 469ff66b6..8d74b9999 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -195,6 +195,31 @@ Clip *Track::GetClipFromPoint(long point) return nullptr; } +Track *Track::Previous() +{ + int index = Index(); + + if (index == 0) { + return nullptr; + } + + return parent_->TrackAt(index - 1); +} + +Track *Track::Next() +{ + return parent_->TrackAt(Index() + 1); +} + +Track *Track::Sibling(int diff) +{ + if (diff == 0) { + return this; + } + + return track_list()->TrackAt(qMax(0, Index() + diff)); +} + int Track::Index() { return parent_->IndexOfTrack(this); diff --git a/timeline/track.h b/timeline/track.h index b0e079370..23ca28cf4 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -68,6 +68,10 @@ public: ClipPtr GetClipObjectFromRawPtr(Clip* c); Clip* GetClipFromPoint(long point); + Track* Previous(); + Track* Next(); + Track* Sibling(int diff); + int Index(); bool IsClipSelected(int clip_index, bool containing = true); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 777bb5fde..63eed4044 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -645,7 +645,8 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { if (ParentTimeline()->drag_track_start->type() == create_type) { Ghost g; g.in = g.old_in = g.out = g.old_out = ParentTimeline()->drag_frame_start; - g.track = g.old_track = ParentTimeline()->drag_track_start; + g.track = ParentTimeline()->drag_track_start; + g.track_movement = 0; g.transition = nullptr; g.clip = nullptr; g.trim_type = olive::timeline::TRIM_OUT; @@ -1054,6 +1055,8 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { c->set_name(tr("Noise")); c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_NOISE, EFFECT_TYPE_EFFECT))); break; + default: + break; } if (c->type() == Track::kTypeAudio && olive::config.add_default_effects_to_clips) { @@ -1081,7 +1084,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { if (g.in != g.old_in || g.out != g.old_out || g.clip_in != g.old_clip_in - || g.track != g.old_track) { + || g.track_movement != 0) { process_moving = true; break; } @@ -1154,10 +1157,10 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QVector delete_areas; for (int i=0;ighosts.size();i++) { const Ghost& g = ParentTimeline()->ghosts.at(i); - if (g.old_in != g.in || g.old_out != g.out || g.track != g.old_track || g.clip_in != g.old_clip_in) { + if (g.old_in != g.in || g.old_out != g.out || g.track_movement != 0 || g.clip_in != g.old_clip_in) { // create copy of clip - ClipPtr c = g.clip->copy(g.track); + ClipPtr c = g.clip->copy(g.track->Sibling(g.track_movement)); c->set_timeline_in(g.in); c->set_timeline_out(g.out); @@ -1247,7 +1250,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { (g.in - g.old_in), (g.out - g.old_out), (g.clip_in - g.old_clip_in), - g.track, + g.track->Sibling(g.track_movement), false, true); @@ -1302,7 +1305,13 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { timeline_out_movement = g.out - g.transition->parent_clip->timeline_out(); } - c->Move(ca, (g.in - g.old_in), timeline_out_movement, (g.clip_in - g.old_clip_in), 0, false, true); + c->Move(ca, + (g.in - g.old_in), + timeline_out_movement, + (g.clip_in - g.old_clip_in), + g.track, + false, + true); clip_length -= (g.in - g.old_in); } @@ -1319,7 +1328,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } // if transition is going to make the clip bigger, make the clip bigger - c->Move(ca, timeline_in_movement, (g.out - g.old_out), timeline_in_movement, 0, false, true); + c->Move(ca, timeline_in_movement, (g.out - g.old_out), timeline_in_movement, c->track(), false, true); clip_length += (g.out - g.old_out); } @@ -1541,7 +1550,8 @@ void TimelineView::init_ghosts() { Ghost& g = ParentTimeline()->ghosts[i]; Clip* c = g.clip; - g.track = g.old_track = c->track(); + g.track = c->track(); + g.track_movement = 0; g.clip_in = g.old_clip_in = c->clip_in(); if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIP) { @@ -1616,6 +1626,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { long frame_diff = (lock_frame) ? 0 : ParentTimeline()->getTimelineFrameFromScreenPoint(mouse_pos.x()) - ParentTimeline()->drag_frame_start; long validator; long earliest_in_point = LONG_MAX; + int track_diff = getTrackIndexFromScreenPoint(mouse_pos.y()) - ParentTimeline()->drag_track_start->Index(); // first try to snap long fm; @@ -1814,18 +1825,12 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } } - // prevent clips from crossing tracks - /* - if (same_sign(g.old_track, ParentTimeline()->drag_track_start)) { - while (!same_sign(g.old_track, g.old_track + track_diff)) { - if (g.old_track < 0) { - track_diff--; - } else { - track_diff++; - } - } + // Prevent any clips from going below the "zeroeth" track + int track_validator = g.track->Index() + track_diff; + if (track_validator < 0) { + track_diff -= track_validator; } - */ + } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { if (ParentTimeline()->transition_tool_open_clip == nullptr || ParentTimeline()->transition_tool_close_clip == nullptr) { @@ -1898,7 +1903,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.out = g.old_out + ghost_diff; } } else if (clips_are_movable) { - g.track = g.old_track; + g.track_movement = 0; g.in = g.old_in + frame_diff; g.out = g.old_out + frame_diff; @@ -1909,16 +1914,11 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { if (ParentTimeline()->importing) { - if (mouse_track != nullptr) { - g.track = g.track->track_list()->TrackAt(mouse_track->Index()); - } + g.track_movement = getTrackIndexFromScreenPoint(mouse_pos.y()); - } else if (g.old_track->type() == ParentTimeline()->drag_track_start->type()) { + } else if (g.track->type() == ParentTimeline()->drag_track_start->type()) { - if (mouse_track != nullptr) { - g.track = g.track->track_list()->TrackAt(mouse_track->Index()); - } - //g.track += track_diff; + g.track_movement = track_diff; } } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { @@ -3250,7 +3250,7 @@ void TimelineView::paintEvent(QPaintEvent*) { first_ghost = qMin(first_ghost, g.in); if (g.track->type() == track_list_->type()) { int ghost_x = ParentTimeline()->getTimelineScreenPointFromFrame(g.in); - int ghost_y = getScreenPointFromTrack(g.track); + int ghost_y = getScreenPointFromTrackIndex(g.track->Index() + g.track_movement); int ghost_width = ParentTimeline()->getTimelineScreenPointFromFrame(g.out) - ghost_x - 1; int ghost_height = g.track->height() - 1; @@ -3320,26 +3320,44 @@ void TimelineView::resizeEvent(QResizeEvent *) { // ************************************** Track *TimelineView::getTrackFromScreenPoint(int y) { + + int index = getTrackIndexFromScreenPoint(y); + + if (index < track_list_->TrackCount()) { + return track_list_->TrackAt(index); + } + + return nullptr; + +} + +int TimelineView::getScreenPointFromTrack(Track *track) { + return getScreenPointFromTrackIndex(track_list_->IndexOfTrack(track)); +} + +int TimelineView::getTrackIndexFromScreenPoint(int y) +{ if (y < 0) { - return track_list_->First(); - } else if (y > height()) { - return track_list_->Last(); + return 0; } y += scroll; int heights = 0; -// for (int i=0;iTrackCount();i++) { int i = 0; while (true) { int new_heights = heights + 1; - new_heights += track_list_->TrackAt(i)->height(); + if (i < track_list_->TrackCount()) { + new_heights += track_list_->TrackAt(i)->height(); + } else { + new_heights += olive::timeline::kTrackDefaultHeight; + } if (y >= heights && y < new_heights) { - return track_list_->TrackAt(i); + return i; } heights = new_heights; @@ -3347,18 +3365,20 @@ Track *TimelineView::getTrackFromScreenPoint(int y) { i++; } -// return nullptr; } -int TimelineView::getScreenPointFromTrack(Track *track) { +int TimelineView::getScreenPointFromTrackIndex(int track) +{ int point = 0; - for (int i=0;iTrackCount();i++) { - if (track == track_list_->TrackAt(i)) { - return point; + for (int i=0;iTrackCount()) { + point += track_list_->TrackAt(i)->height() + 1; + } else { + point += olive::timeline::kTrackDefaultHeight + 1; } - point += track_list_->TrackAt(i)->height() + 1; } + return point - scroll; } diff --git a/ui/timelineview.h b/ui/timelineview.h index 3e743b92b..13be5613f 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -72,8 +72,13 @@ protected: private: void init_ghosts(); void update_ghosts(const QPoint& mouse_pos, bool lock_frame); + Track* getTrackFromScreenPoint(int y); int getScreenPointFromTrack(Track* track); + + int getTrackIndexFromScreenPoint(int y); + int getScreenPointFromTrackIndex(int track); + Timeline* ParentTimeline(); Sequence* sequence(); void delete_area_under_ghosts(ComboAction* ca, Sequence *s); From ce725ae32a79a60e69fab574300cec31053a6379 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 5 Apr 2019 02:20:37 +1100 Subject: [PATCH 073/133] added condition to track validator --- ui/timelineview.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 63eed4044..c55eda974 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -1826,9 +1826,14 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } // Prevent any clips from going below the "zeroeth" track - int track_validator = g.track->Index() + track_diff; - if (track_validator < 0) { - track_diff -= track_validator; + + if (ParentTimeline()->importing || g.track->type() == ParentTimeline()->drag_track_start->type()) { + + int track_validator = g.track->Index() + track_diff; + if (track_validator < 0) { + track_diff -= track_validator; + } + } } else if (effective_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { From 6e9a8e76130d89f44066cd456c5dbf63016b781b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 5 Apr 2019 04:40:45 +1100 Subject: [PATCH 074/133] possible fix for #691 --- ui/audiomonitor.cpp | 12 ++++++++++-- ui/audiomonitor.h | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp index d69277587..f5f2ba02e 100644 --- a/ui/audiomonitor.cpp +++ b/ui/audiomonitor.cpp @@ -46,9 +46,13 @@ AudioMonitor::AudioMonitor(QWidget *parent) : } void AudioMonitor::set_value(const QVector &ivalues) { - values = ivalues; - update(); + qDebug() << "am set"; + value_lock.lock(); + values = ivalues; + value_lock.unlock(); + + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); QMetaObject::invokeMethod(&clear_timer, "start", Qt::QueuedConnection); } @@ -68,7 +72,10 @@ void AudioMonitor::resizeEvent(QResizeEvent *e) { } void AudioMonitor::paintEvent(QPaintEvent *) { + value_lock.lock(); + qDebug() << "am paint"; if (values.size() > 0) { + qDebug() << "am paint 2"; QPainter p(this); int channel_x = AUDIO_MONITOR_GAP; int channel_count = values.size(); @@ -95,4 +102,5 @@ void AudioMonitor::paintEvent(QPaintEvent *) { channel_x += channel_width + AUDIO_MONITOR_GAP; } } + value_lock.unlock(); } diff --git a/ui/audiomonitor.h b/ui/audiomonitor.h index 8ce9da55f..70898ce60 100644 --- a/ui/audiomonitor.h +++ b/ui/audiomonitor.h @@ -23,6 +23,7 @@ #include #include +#include /** * @brief The AudioMonitor class @@ -84,6 +85,13 @@ private: */ QVector values; + /** + * @brief Value mutex + * + * Audio is often processed and sent to this object from other threads. To keep them synchronized, we lock them here. + */ + QMutex value_lock; + /** * @brief Internal timer to clear the audio monitor after a certain amount of time * From 3fb51f8c0a935dd2d8dd89b0d421953f6b37a15b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 00:12:00 +1100 Subject: [PATCH 075/133] removed debug messages from audio monitor --- ui/audiomonitor.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp index f5f2ba02e..b3628ad74 100644 --- a/ui/audiomonitor.cpp +++ b/ui/audiomonitor.cpp @@ -29,8 +29,6 @@ #include #include -#include - #define AUDIO_MONITOR_PEAK_HEIGHT 15 #define AUDIO_MONITOR_GAP 3 @@ -46,8 +44,6 @@ AudioMonitor::AudioMonitor(QWidget *parent) : } void AudioMonitor::set_value(const QVector &ivalues) { - qDebug() << "am set"; - value_lock.lock(); values = ivalues; value_lock.unlock(); @@ -73,9 +69,7 @@ void AudioMonitor::resizeEvent(QResizeEvent *e) { void AudioMonitor::paintEvent(QPaintEvent *) { value_lock.lock(); - qDebug() << "am paint"; if (values.size() > 0) { - qDebug() << "am paint 2"; QPainter p(this); int channel_x = AUDIO_MONITOR_GAP; int channel_count = values.size(); From 5f5535bb111349b51bd76e73f57f9163246e918a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 00:12:16 +1100 Subject: [PATCH 076/133] fixed clips not showing their first frames --- timeline/clip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 78ad4f74a..2fe81cc38 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -84,7 +84,7 @@ ClipPtr Clip::copy(Track* s) { bool Clip::IsActiveAt(long timecode) { return enabled() - && timeline_in(true) < timecode + && timeline_in(true) <= timecode && timeline_out(true) > timecode && timecode - timeline_in(true) + clip_in(true) < media_length() && !track()->IsEffectivelyMuted(); From 0247e7b992fa9ec69cf5311afd5841d18b310595 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 00:15:06 +1100 Subject: [PATCH 077/133] fixed broken nav to previous and next cuts --- ui/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index b0fb78497..927aa26c9 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -654,8 +654,8 @@ void MainWindow::setup_menus() { playback_menu->addSeparator(); - go_to_prev_cut_ = MenuHelper::create_menu_action(playback_menu, "prevcut", panel_project.first(), SLOT(previous_cut()), QKeySequence("Up")); - go_to_next_cut_ = MenuHelper::create_menu_action(playback_menu, "nextcut", panel_project.first(), SLOT(next_cut()), QKeySequence("Down")); + go_to_prev_cut_ = MenuHelper::create_menu_action(playback_menu, "prevcut", panel_sequence_viewer, SLOT(prev_cut()), QKeySequence("Up")); + go_to_next_cut_ = MenuHelper::create_menu_action(playback_menu, "nextcut", panel_sequence_viewer, SLOT(next_cut()), QKeySequence("Down")); playback_menu->addSeparator(); From eb08a794bc351254741e563de00448db8cb45474 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 00:22:36 +1100 Subject: [PATCH 078/133] update timeline after delete function --- timeline/sequence.h | 2 ++ ui/focusfilter.cpp | 9 ++++++--- undo/undo.cpp | 5 +++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/timeline/sequence.h b/timeline/sequence.h index 3edf773fd..0bf2dad49 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -120,6 +120,8 @@ public: int save_id; QVector markers; +signals: + void Changed(); private: QVector track_lists_; diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index 4092bc6c7..b3e6de286 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -206,9 +206,12 @@ void FocusFilter::delete_function() { panel_graph_editor->delete_selected_keys(); } else { Sequence* top_sequence = Timeline::GetTopSequence().get(); - ComboAction* ca = new ComboAction(); - top_sequence->DeleteAreas(ca, top_sequence->Selections(), true); - olive::undo_stack.push(ca); + if (top_sequence != nullptr) { + ComboAction* ca = new ComboAction(); + top_sequence->DeleteAreas(ca, top_sequence->Selections(), true); + olive::undo_stack.push(ca); + Timeline::GetTopTimeline()->repaint_timeline(); + } } } diff --git a/undo/undo.cpp b/undo/undo.cpp index 98056807b..366fc8a3a 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -270,8 +270,9 @@ void ModifyTransitionCommand::doRedo() { transition_ref_->set_length(new_length_); } -DeleteTransitionCommand::DeleteTransitionCommand(TransitionPtr t) { - transition_ref_ = t; +DeleteTransitionCommand::DeleteTransitionCommand(TransitionPtr t) : + transition_ref_(t) +{ } void DeleteTransitionCommand::doUndo() { From 2b1400032561e66cb1d098c2620289cfef1ca220 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 01:10:33 +1100 Subject: [PATCH 079/133] break shared transitions if a clip's track changes --- panels/timeline.cpp | 1 + ui/timelineview.cpp | 30 ++++++++++++++++++++---------- undo/undo.cpp | 3 +-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 88af64d1e..ed665ba1c 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -860,6 +860,7 @@ void Timeline::transition_menu_select(QAction* a) { } timeline_area->setCursor(Qt::CrossCursor); + olive::timeline::current_tool = olive::timeline::TIMELINE_TOOL_TRANSITION; toolTransitionButton->setChecked(true); } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index c55eda974..12af5e67c 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -642,11 +642,17 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // if the track the user clicked is correct for the type of object we're adding - if (ParentTimeline()->drag_track_start->type() == create_type) { + if (track_list_->type() == create_type) { Ghost g; g.in = g.old_in = g.out = g.old_out = ParentTimeline()->drag_frame_start; + g.track = ParentTimeline()->drag_track_start; g.track_movement = 0; + if (g.track == nullptr) { + g.track = track_list_->Last(); + g.track_movement = getTrackIndexFromScreenPoint(event->pos().x()) - g.track->Index(); + } + g.transition = nullptr; g.clip = nullptr; g.trim_type = olive::timeline::TRIM_OUT; @@ -1366,7 +1372,8 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { // check if the transition's "edge" is going to move if ((t == kTransitionOpening && g.in != g.old_in) - || (t == kTransitionClosing && g.out != g.old_out)) { + || (t == kTransitionClosing && g.out != g.old_out) + || (g.track_movement != 0)) { // if we're here, this clip shares its opening transition as the closing transition of another // clip (or vice versa), and the in point is moving, so we may have to account for this @@ -1396,11 +1403,14 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { // also only do this if j is less than i, because it only needs to happen once and chances are // the other clip already - bool edges_still_touch; - if (t == kTransitionOpening) { - edges_still_touch = (other_clip_ghost.out == g.in); - } else { - edges_still_touch = (other_clip_ghost.in == g.out); + bool edges_still_touch = (other_clip_ghost.track_movement == g.track_movement); + + if (edges_still_touch) { + if (t == kTransitionOpening) { + edges_still_touch = (other_clip_ghost.out == g.in); + } else { + edges_still_touch = (other_clip_ghost.in == g.out); + } } if (edges_still_touch || j < i) { @@ -1827,7 +1837,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { // Prevent any clips from going below the "zeroeth" track - if (ParentTimeline()->importing || g.track->type() == ParentTimeline()->drag_track_start->type()) { + if (ParentTimeline()->importing || g.track->type() == track_list_->type()) { int track_validator = g.track->Index() + track_diff; if (track_validator < 0) { @@ -1921,7 +1931,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.track_movement = getTrackIndexFromScreenPoint(mouse_pos.y()); - } else if (g.track->type() == ParentTimeline()->drag_track_start->type()) { + } else if (g.track->type() == track_list_->type()) { g.track_movement = track_diff; @@ -2731,7 +2741,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // cursor is hovering over a clip // check if the clip and transition are both the same sign (meaning video/audio are the same) - if (mouse_clip->track()->type() == ParentTimeline()->transition_tool_side) { + if (track_list_->type() == ParentTimeline()->transition_tool_side) { // the range within which the transition tool will assume the user wants to make a shared transition // between two clips rather than just one transition on one clip diff --git a/undo/undo.cpp b/undo/undo.cpp index 366fc8a3a..ae8c80f11 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -68,7 +68,7 @@ void MoveClipAction::doUndo() { clip->set_timeline_out(old_out); clip->set_clip_in(old_clip_in); } - clip->set_track(old_track); + old_track->AddClip(clip); done = false; } @@ -370,7 +370,6 @@ void AddClipCommand::doRedo() { ClipPtr original = clips_.at(i); if (original != nullptr) { - qDebug() << "h"; original->track()->AddClip(original); } } From 2b23b069fa799b0d517d426e1d62a938d0b49d36 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 01:21:08 +1100 Subject: [PATCH 080/133] rectangle select looks correct --- ui/timelineview.cpp | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 12af5e67c..c7c471158 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2402,17 +2402,17 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // set the right/bottom coords to the current mouse position // (left/top were set to the starting drag position earlier) - ParentTimeline()->rect_select_rect.setRight(event->pos().x()); - - if (alignment_ == olive::timeline::kAlignmentBottom) { - ParentTimeline()->rect_select_rect.setBottom(event->pos().y() - height()); - } else { - ParentTimeline()->rect_select_rect.setBottom(event->pos().y()); - } + ParentTimeline()->rect_select_rect.setBottomRight(mapToGlobal(event->pos())); long frame_min = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); long frame_max = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + QPoint relative_tl = mapFromGlobal(ParentTimeline()->rect_select_rect.topLeft()); + QPoint relative_br = mapFromGlobal(ParentTimeline()->rect_select_rect.bottomRight()); + + int rect_top = qMin(relative_tl.y(), relative_br.y()); + int rect_bottom = qMax(relative_tl.y(), relative_br.y()); + // determine which clips are in this rectangular selection QVector selected_clips; for (int j=0;jTrackCount();j++) { @@ -2420,8 +2420,6 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { int track_top = getScreenPointFromTrack(track); int track_bottom = track_top + track->height(); - int rect_top = qMin(ParentTimeline()->rect_select_rect.top(), ParentTimeline()->rect_select_rect.bottom()); - int rect_bottom = qMax(ParentTimeline()->rect_select_rect.top(), ParentTimeline()->rect_select_rect.bottom()); // See if this track touches this rectangle at all if (!(track_bottom < rect_top @@ -2464,17 +2462,8 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else { // set up rectangle selecting - ParentTimeline()->rect_select_rect.setX(event->pos().x()); - - if (alignment_ == olive::timeline::kAlignmentBottom) { - // bottom aligned widgets start with 0 at the bottom and go down to a negative number - ParentTimeline()->rect_select_rect.setY(event->pos().y() - height()); - } else { - ParentTimeline()->rect_select_rect.setY(event->pos().y()); - } - - ParentTimeline()->rect_select_rect.setWidth(0); - ParentTimeline()->rect_select_rect.setHeight(0); + ParentTimeline()->rect_select_rect.setTopLeft(mapToGlobal(event->pos())); + ParentTimeline()->rect_select_rect.setSize(QSize(0, 0)); ParentTimeline()->rect_select_proc = true; @@ -3247,13 +3236,10 @@ void TimelineView::paintEvent(QPaintEvent*) { // draw rectangle select if (ParentTimeline()->rect_select_proc) { - QRect rect_select = ParentTimeline()->rect_select_rect; + QRect relative_rect = QRect(mapFromGlobal(ParentTimeline()->rect_select_rect.topLeft()), + mapFromGlobal(ParentTimeline()->rect_select_rect.bottomRight())); - if (alignment_ == olive::timeline::kAlignmentBottom) { - rect_select.translate(0, height()); - } - - olive::ui::DrawSelectionRectangle(p, rect_select); + olive::ui::DrawSelectionRectangle(p, relative_rect); } // Draw ghosts From 6c826a7e160c18b148153c63ce01064f37d862c3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 01:55:27 +1100 Subject: [PATCH 081/133] use selection cache for more intuitive selections --- panels/timeline.h | 2 +- ui/timelineview.cpp | 80 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/panels/timeline.h b/panels/timeline.h index b3dc3517b..ff8c1c836 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -78,7 +78,7 @@ public: // selecting functions bool selecting; - int selection_offset; + QVector selection_cache; void select_all(); bool rect_select_init; bool rect_select_proc; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index c7c471158..23bc328ee 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -620,9 +620,9 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // to the existing selections. `selection_offset` is the index to change selections from (and we don't touch // any prior to that) if (shift) { - ParentTimeline()->selection_offset = sequence()->Selections().size(); + ParentTimeline()->selection_cache = sequence()->Selections(); } else { - ParentTimeline()->selection_offset = 0; + ParentTimeline()->selection_cache.clear(); } // if the user is creating an object @@ -2017,6 +2017,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { } void TimelineView::mouseMoveEvent(QMouseEvent *event) { + // interrupt any potential tooltip about to show tooltip_timer.stop(); @@ -2062,9 +2063,72 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (ParentTimeline()->selecting) { + QVector selections = ParentTimeline()->selection_cache; + + if (ParentTimeline()->drag_track_start != nullptr || ParentTimeline()->cursor_track != nullptr) { + + long selection_in = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + long selection_out = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + + int start_track = (ParentTimeline()->drag_track_start == nullptr) + ? track_list_->TrackCount() - 1 + : ParentTimeline()->drag_track_start->Index(); + + int end_track = (ParentTimeline()->cursor_track == nullptr) + ? track_list_->TrackCount() - 1 + : ParentTimeline()->cursor_track->Index(); + + int min_track = qMin(start_track, end_track); + int max_track = qMax(start_track, end_track); + + for (int i=min_track;i<=max_track;i++) { + + Track* track = track_list_->TrackAt(i); + + selections.append(Selection(selection_in, selection_out, track)); + + // If the config is set to select links as well with the edit tool + if (olive::config.edit_tool_selects_links) { + + for (int j=0;jClipCount();j++) { + + Clip* c = track->GetClip(j).get(); + + // See if this selection contains this clip + if (!(c->timeline_in() > selection_out || c->timeline_out() < selection_in)) { + + // If so, select its links as well + for (int k=0;klinked.size();k++) { + Clip* link = c->linked.at(k); + + // Make sure there isn't already a selection for this link + bool found = false; + for (int l=0;ltrack()) { + found = true; + break; + } + } + // If not, make one now + if (!found) { + selections.append(Selection(selection_in, selection_out, link->track())); + } + } + } + } + } + } + } + + sequence()->SetSelections(selections); + /* // get number of selections based on tracks in selection area - int selection_tool_count = 1 + qMax(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start) - qMin(ParentTimeline()->cursor_track, ParentTimeline()->drag_track_start); + int selection_tool_count = 1 + + qMax(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()) + - qMin(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()); // add count to selection offset for the total number of selection objects // (offset is usually 0, unless the user is holding shift in which case we add to existing selections) @@ -2120,6 +2184,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } } } + */ // if the config is set to seek with the edit too, do so now if (olive::config.edit_tool_also_seeks) { @@ -2127,8 +2192,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else { // if not, repaint (seeking will trigger a repaint) ParentTimeline()->repaint_timeline(); - } - */ + } } else if (ParentTimeline()->hand_moving) { @@ -2400,6 +2464,8 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // we're currently rectangle selecting + QVector selections = ParentTimeline()->selection_cache; + // set the right/bottom coords to the current mouse position // (left/top were set to the starting drag position earlier) ParentTimeline()->rect_select_rect.setBottomRight(mapToGlobal(event->pos())); @@ -2455,9 +2521,11 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // add each of the selected clips to the main sequence's selections for (int i=0;itrack()->SelectClip(selected_clips.at(i)); + selections.append(selected_clips.at(i)->ToSelection()); } + sequence()->SetSelections(selections); + ParentTimeline()->repaint_timeline(); } else { From 98336348155f93d6b78cb19e00bea5c75f796131 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 11:08:39 +1100 Subject: [PATCH 082/133] fixed shared cross dissolve frozen at start frame --- rendering/renderfunctions.cpp | 2 +- timeline/clip.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 40e22cc0d..054a41f2e 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -397,7 +397,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { // retrieve video frame from cache and store it in c->texture - c->Cache(qMax(playhead, c->timeline_in()), false, params.nests, params.playback_speed); + c->Cache(qMax(playhead, c->timeline_in(true)), false, params.nests, params.playback_speed); if (!c->Retrieve()) { params.texture_failed = true; } else { diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 2fe81cc38..822847e0e 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -318,7 +318,7 @@ void Clip::Save(QXmlStreamWriter &stream) long Clip::clip_in(bool with_transition) { if (with_transition && opening_transition != nullptr && opening_transition->secondary_clip != nullptr) { - // we must be the secondary clip, so return (timeline in - length) + // we must be the secondary clip, so return (clip in - length) return clip_in_ - opening_transition->get_true_length(); } return clip_in_; @@ -344,7 +344,7 @@ void Clip::set_timeline_in(long t) long Clip::timeline_out(bool with_transitions) { if (with_transitions && closing_transition != nullptr && closing_transition->secondary_clip != nullptr) { - // we must be the primary clip, so return (timeline out + length2) + // we must be the primary clip, so return (timeline out + length) return timeline_out_ + closing_transition->get_true_length(); } else { return timeline_out_; From 8ed09cdccf66dce3ef358f79b4c4dca7e8cd4b04 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 16:12:07 +1100 Subject: [PATCH 083/133] enable ripple deleting --- effects/internal/crossdissolvetransition.cpp | 5 +- panels/timeline.cpp | 15 ++-- timeline/ghost.cpp | 7 ++ timeline/ghost.h | 2 + timeline/sequence.cpp | 83 +++++++++--------- timeline/sequence.h | 3 +- ui/focusfilter.cpp | 2 +- ui/timelineview.cpp | 90 ++++++++++---------- undo/undo.cpp | 52 ++++++----- undo/undo.h | 2 +- 10 files changed, 137 insertions(+), 124 deletions(-) diff --git a/effects/internal/crossdissolvetransition.cpp b/effects/internal/crossdissolvetransition.cpp index 9fa94e2e4..c5c35f12c 100644 --- a/effects/internal/crossdissolvetransition.cpp +++ b/effects/internal/crossdissolvetransition.cpp @@ -27,8 +27,9 @@ CrossDissolveTransition::CrossDissolveTransition(Clip* c, Clip* s, const EffectM } void CrossDissolveTransition::process_coords(double progress, GLTextureCoords& coords, int data) { - if (!(data == kTransitionClosing && secondary_clip != nullptr)) { - if (data == kTransitionClosing) progress = 1.0 - progress; + if (data == kTransitionClosing) { + coords.opacity *= (1.0 - progress); + } else { coords.opacity *= progress; } } diff --git a/panels/timeline.cpp b/panels/timeline.cpp index ed665ba1c..23109ebff 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -628,22 +628,27 @@ void Timeline::copy(bool del) { } void Timeline::ripple_delete() { + if (sequence_ != nullptr) { QVector selections = sequence_->Selections(); - if (selections.isEmpty()) { - - sequence_->RippleDeleteEmptySpace(cursor_track, cursor_frame); - - } else if (olive::config.hover_focus && get_focused_panel() == this) { + if (!selections.isEmpty()) { ComboAction* ca = new ComboAction(); sequence_->DeleteAreas(ca, selections, true, true); olive::undo_stack.push(ca); + } else if (olive::config.hover_focus && get_focused_panel() == this) { + + ComboAction* ca = new ComboAction(); + sequence_->RippleDeleteEmptySpace(ca, cursor_track, cursor_frame); + olive::undo_stack.push(ca); + } } + + repaint_timeline(); } void Timeline::ripple_delete_empty_space() diff --git a/timeline/ghost.cpp b/timeline/ghost.cpp index dccd5ecfd..ec48003c5 100644 --- a/timeline/ghost.cpp +++ b/timeline/ghost.cpp @@ -1,5 +1,12 @@ #include "ghost.h" +Ghost::Ghost() : + transition(nullptr), + track_movement(0), + clip(nullptr) +{ +} + Selection Ghost::ToSelection() const { return Selection(in, out, track->Sibling(track_movement)); diff --git a/timeline/ghost.h b/timeline/ghost.h index cd5dafd5b..f95314fe3 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -17,6 +17,8 @@ enum TrimType { } struct Ghost { + Ghost(); + Clip* clip; long in; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 19fef2e98..c47e2fb6b 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -628,6 +628,10 @@ void Sequence::Split() void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool deselect_areas, bool ripple) { + if (areas.isEmpty()) { + return; + } + Selection::Tidy(areas); panel_graph_editor->set_row(nullptr); @@ -690,16 +694,22 @@ void Sequence::DeleteAreas(ComboAction* ca, QVector areas, bool desel } // deselect selected clip areas + long minimum_in = LONG_MAX; + long minimum_length = LONG_MAX; if (deselect_areas) { QVector area_copy = areas; for (int i=0;iDeselectArea(s.in(), s.out()); + + // Get ripple point and ripple length + minimum_in = qMin(minimum_in, s.in()); + minimum_length = qMin(minimum_length, s.in() - s.out()); } } if (ripple) { - + RippleDeleteArea(ca, minimum_in, minimum_length); } olive::timeline::RelinkClips(pre_clips, post_clips); @@ -776,7 +786,7 @@ bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector p return split_occurred; } -void Sequence::RippleDeleteEmptySpace(Track* track, long point) +void Sequence::RippleDeleteEmptySpace(ComboAction* ca, Track* track, long point) { QVector track_clips = track->GetAllClips(); @@ -805,6 +815,11 @@ void Sequence::RippleDeleteEmptySpace(Track* track, long point) // We now know the maximum ripple we could do to clear this empty space, but we need to ensure it won't cause // overlaps of clips in other tracks + RippleDeleteArea(ca, point, ripple_end - ripple_start); +} + +void Sequence::RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length) { + for (int i=0;iTrackAt(j); // We've already tested `track`, so we don't need to test it again - if (t != track) { + long first_in_point_after_point = LONG_MAX; + long out_point_just_before_first_in_point = LONG_MIN; - long first_in_point_after_point = LONG_MAX; - long out_point_just_before_first_in_point = LONG_MIN; + QVector track_clips = t->GetAllClips(); - QVector track_clips = t->GetAllClips(); + // Find the in point of the clip directly after the point + for (int k=0;ktimeline_in() >= ripple_point) { + first_in_point_after_point = qMin(first_in_point_after_point, c->timeline_in()); + } + } + + // Ensure we found a valid in point before proceeding + if (first_in_point_after_point != LONG_MAX) { + + // Find the out point of the clip directly before the clip found above for (int k=0;ktimeline_in() > point) { - first_in_point_after_point = qMin(first_in_point_after_point, c->timeline_in()); + if (c->timeline_out() <= first_in_point_after_point) { + out_point_just_before_first_in_point = qMax(out_point_just_before_first_in_point, c->timeline_out()); } } - // Ensure we found a valid in point before proceeding - if (first_in_point_after_point != LONG_MAX) { + long ripple_test = first_in_point_after_point - out_point_just_before_first_in_point + ripple_length; - // Find the out point of the clip directly before the clip found above - for (int k=0;ktimeline_out() < first_in_point_after_point) { - out_point_just_before_first_in_point = qMax(out_point_just_before_first_in_point, c->timeline_out()); - } - } - - long gap_between_clips = first_in_point_after_point - out_point_just_before_first_in_point; - - if (gap_between_clips > (ripple_end - ripple_start)) { - ripple_end = ripple_start + gap_between_clips; - } + if (ripple_test < 0) { + ripple_length -= ripple_test; } } } } - if (ripple_start != ripple_end) { - ComboAction* ca = new ComboAction(); - Ripple(ca, ripple_start, ripple_start - ripple_end); - olive::undo_stack.push(ca); - } -} - -/* -QVector Sequence::SelectedClipIndexes() -{ - QVector selected_clips; - - for (int i=0;i positions, bool relink = true); - void RippleDeleteEmptySpace(Track *track, long point); + void RippleDeleteEmptySpace(ComboAction *ca, Track *track, long point); + void RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length); Effect* GetSelectedGizmo(); diff --git a/ui/focusfilter.cpp b/ui/focusfilter.cpp index b3e6de286..be1736a48 100644 --- a/ui/focusfilter.cpp +++ b/ui/focusfilter.cpp @@ -210,7 +210,7 @@ void FocusFilter::delete_function() { ComboAction* ca = new ComboAction(); top_sequence->DeleteAreas(ca, top_sequence->Selections(), true); olive::undo_stack.push(ca); - Timeline::GetTopTimeline()->repaint_timeline(); + update_ui(false); } } } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 23bc328ee..143a85b47 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -647,14 +647,12 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { g.in = g.old_in = g.out = g.old_out = ParentTimeline()->drag_frame_start; g.track = ParentTimeline()->drag_track_start; - g.track_movement = 0; if (g.track == nullptr) { g.track = track_list_->Last(); - g.track_movement = getTrackIndexFromScreenPoint(event->pos().x()) - g.track->Index(); + ParentTimeline()->drag_track_start = track_list_->Last(); + g.track_movement = getTrackIndexFromScreenPoint(event->pos().y()) - g.track->Index(); } - g.transition = nullptr; - g.clip = nullptr; g.trim_type = olive::timeline::TRIM_OUT; ParentTimeline()->ghosts.append(g); @@ -1010,7 +1008,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { panel_sequence_viewer->cue_recording(qMin(g.in, g.out), qMax(g.in, g.out), g.track); ParentTimeline()->creating = false; } else if (g.in != g.out) { - ClipPtr c = std::make_shared(g.track); + ClipPtr c = std::make_shared(g.track->Sibling(g.track_movement)); c->set_media(nullptr, 0); c->set_timeline_in(qMin(g.in, g.out)); c->set_timeline_out(qMax(g.in, g.out)); @@ -1097,6 +1095,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } if (process_moving) { + const Ghost& first_ghost = ParentTimeline()->ghosts.at(0); // start a ripple movement @@ -1462,7 +1461,16 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { } } } + + // move selections to match new ghosts + QVector new_selections; + for (int i=0;ighosts.size();i++) { + new_selections.append(ParentTimeline()->ghosts.at(i).ToSelection()); + } + ca->append(new SetSelectionsCommand(sequence(), sequence()->Selections(), new_selections)); + push_undo = true; + } } else if (ParentTimeline()->selecting || ParentTimeline()->rect_select_proc) { } else if (ParentTimeline()->transition_tool_proc) { @@ -1561,7 +1569,6 @@ void TimelineView::init_ghosts() { Clip* c = g.clip; g.track = c->track(); - g.track_movement = 0; g.clip_in = g.old_clip_in = c->clip_in(); if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_SLIP) { @@ -1632,7 +1639,6 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { int effective_tool = olive::timeline::current_tool; if (ParentTimeline()->importing || ParentTimeline()->creating) effective_tool = olive::timeline::TIMELINE_TOOL_POINTER; - Track* mouse_track = getTrackFromScreenPoint(mouse_pos.y()); long frame_diff = (lock_frame) ? 0 : ParentTimeline()->getTimelineFrameFromScreenPoint(mouse_pos.x()) - ParentTimeline()->drag_frame_start; long validator; long earliest_in_point = LONG_MAX; @@ -1918,7 +1924,6 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.out = g.old_out + ghost_diff; } } else if (clips_are_movable) { - g.track_movement = 0; g.in = g.old_in + frame_diff; g.out = g.old_out + frame_diff; @@ -1931,7 +1936,7 @@ void TimelineView::update_ghosts(const QPoint& mouse_pos, bool lock_frame) { g.track_movement = getTrackIndexFromScreenPoint(mouse_pos.y()); - } else if (g.track->type() == track_list_->type()) { + } else if (g.track->type() == track_list_->type() && g.transition == nullptr) { g.track_movement = track_diff; @@ -2192,7 +2197,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else { // if not, repaint (seeking will trigger a repaint) ParentTimeline()->repaint_timeline(); - } + } } else if (ParentTimeline()->hand_moving) { @@ -2254,52 +2259,43 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c != nullptr) { Ghost g; - g.transition = nullptr; - // check if whole clip is added - bool add = false; + // check if whole clip is selected + bool add = c->IsSelected(); - // check if a transition is selected (prioritize transition selection) - // (only the pointer tool supports moving transitions) - if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER - && (c->opening_transition != nullptr || c->closing_transition != nullptr)) { - - // check if any selections contain a whole transition - if (c->IsTransitionSelected(kTransitionOpening)) { - g.transition = c->opening_transition; - add = true; - } else if (c->IsTransitionSelected(kTransitionClosing)) { - g.transition = c->closing_transition; - add = true; - } - - } - - // if a transition isn't selected, check if the whole clip is if (!add) { - add = c->IsSelected(); - } + // check if a transition is selected + // (only the pointer tool supports moving transitions) + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_POINTER + && (c->opening_transition != nullptr || c->closing_transition != nullptr)) { - if (add) { - - if (g.transition != nullptr) { - - // transition may be a dual transition, check if it's already been added elsewhere - for (int j=0;jghosts.size();j++) { - if (ParentTimeline()->ghosts.at(j).transition == g.transition) { - add = false; - break; - } + // check if any selections contain a whole transition + if (c->IsTransitionSelected(kTransitionOpening)) { + g.transition = c->opening_transition; + add = true; + } else if (c->IsTransitionSelected(kTransitionClosing)) { + g.transition = c->closing_transition; + add = true; } } + } - if (add) { - g.clip = c; - g.trim_type = ParentTimeline()->trim_type; - ParentTimeline()->ghosts.append(g); + if (add && g.transition != nullptr) { + + // transition may be a shared transition, check if it's already been added elsewhere + for (int j=0;jghosts.size();j++) { + if (ParentTimeline()->ghosts.at(j).transition == g.transition) { + add = false; + break; + } } + } + if (add) { + g.clip = c; + g.trim_type = ParentTimeline()->trim_type; + ParentTimeline()->ghosts.append(g); } } } @@ -2489,7 +2485,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // See if this track touches this rectangle at all if (!(track_bottom < rect_top - || track_top > rect_bottom)) { + || track_top > rect_bottom)) { // Loop through track's clips for clips touching this rectangle for (int i=0;iClipCount();i++) { diff --git a/undo/undo.cpp b/undo/undo.cpp index ae8c80f11..1a7ba7e80 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -88,11 +88,13 @@ void MoveClipAction::doRedo() { } } -DeleteClipAction::DeleteClipAction(Clip *clip) +DeleteClipAction::DeleteClipAction(Clip *clip) : + done_(false) { // Get shared_ptr object to take ownership of this Clip clip_ = clip->track()->GetClipObjectFromRawPtr(clip); + doRedo(); } void DeleteClipAction::doUndo() { @@ -101,30 +103,38 @@ void DeleteClipAction::doUndo() { // restore links to this clip for (int i=0;ilinked.append(clip_.get()); } + + done_ = false; } void DeleteClipAction::doRedo() { - // remove ref to clip - if (clip_->IsOpen()) { - clip_->Close(true); - } + if (!done_) { - clip_->track()->RemoveClip(clip_.get()); + // remove ref to clip + if (clip_->IsOpen()) { + clip_->Close(true); + } - // delete link to this clip - QVector clips = clip_->track()->sequence()->GetAllClips(); - for (int i=0;itrack()->RemoveClip(clip_.get()); - for (int j=0;jlinked.size();j++) { - if (c->linked.at(j) == clip_.get()) { - c->linked.removeAt(j); - clips_linked_to_this_one_.append(c); - break; + // delete link to this clip + QVector clips = clip_->track()->sequence()->GetAllClips(); + for (int i=0;ilinked.size();j++) { + if (c->linked.at(j) == clip_.get()) { + c->linked.removeAt(j); + clips_linked_to_this_one_.append(c); + break; + } } } + + done_ = true; + } } @@ -783,23 +793,19 @@ void SetBool::doRedo() { SetSelectionsCommand::SetSelectionsCommand(Sequence *s, const QVector &old_data, const QVector &new_data) : + seq_(s), old_data_(old_data), - new_data_(new_data), - done_(true) + new_data_(new_data) { } void SetSelectionsCommand::doUndo() { seq_->SetSelections(old_data_); - done_ = false; } void SetSelectionsCommand::doRedo() { - if (!done_) { - seq_->SetSelections(new_data_); - done_ = true; - } + seq_->SetSelections(new_data_); } EditSequenceCommand::EditSequenceCommand(Media* i, SequencePtr s) { diff --git a/undo/undo.h b/undo/undo.h index 21a50113e..fd6bd0669 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -119,6 +119,7 @@ public: private: ClipPtr clip_; QVector clips_linked_to_this_one_; + bool done_; }; class AddEffectCommand : public OliveAction { @@ -417,7 +418,6 @@ private: QVector old_data_; QVector new_data_; Sequence* seq_; - bool done_; }; class EditSequenceCommand : public OliveAction { From 2674d284e2703f33dc4370eb65efd2f79421e09d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 16:36:40 +1100 Subject: [PATCH 084/133] fixed ripple deleting empty space --- panels/timeline.cpp | 11 +++++++---- timeline/sequence.cpp | 10 +++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 23109ebff..fab6aac7f 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -638,23 +638,26 @@ void Timeline::ripple_delete() { ComboAction* ca = new ComboAction(); sequence_->DeleteAreas(ca, selections, true, true); olive::undo_stack.push(ca); + repaint_timeline(); } else if (olive::config.hover_focus && get_focused_panel() == this) { - ComboAction* ca = new ComboAction(); - sequence_->RippleDeleteEmptySpace(ca, cursor_track, cursor_frame); - olive::undo_stack.push(ca); + ripple_delete_empty_space(); } } - repaint_timeline(); + } void Timeline::ripple_delete_empty_space() { if (sequence_ != nullptr) { + ComboAction* ca = new ComboAction(); + sequence_->RippleDeleteEmptySpace(ca, cursor_track, cursor_frame); + olive::undo_stack.push(ca); + repaint_timeline(); } } diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index c47e2fb6b..5ac286673 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -801,11 +801,11 @@ void Sequence::RippleDeleteEmptySpace(ComboAction* ca, Track* track, long point) return; } - if (c->timeline_out() < point) { + if (c->timeline_out() <= point) { ripple_start = qMin(c->timeline_out(), ripple_start); - } else if (c->timeline_in() > point) { + } else if (c->timeline_in() >= point) { ripple_end = qMin(c->timeline_in(), ripple_end); @@ -815,7 +815,11 @@ void Sequence::RippleDeleteEmptySpace(ComboAction* ca, Track* track, long point) // We now know the maximum ripple we could do to clear this empty space, but we need to ensure it won't cause // overlaps of clips in other tracks - RippleDeleteArea(ca, point, ripple_end - ripple_start); + if (ripple_start == ripple_end) { + return; + } + + RippleDeleteArea(ca, point, ripple_start - ripple_end); } void Sequence::RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length) { From 8199601048d4214175f6c61999da637cf7df74e0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 19:14:45 +1100 Subject: [PATCH 085/133] labels scroll with the timeline views --- panels/timeline.cpp | 2 +- ui/timelinearea.cpp | 126 +++++++- ui/timelinearea.h | 9 + ui/timelinelabel.cpp | 10 +- ui/timelinelabel.h | 2 + ui/timelineview.cpp | 687 ++++++++++++++++++++----------------------- ui/timelineview.h | 8 +- 7 files changed, 456 insertions(+), 388 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index fab6aac7f..26bce0857 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -90,7 +90,7 @@ Timeline::Timeline(QWidget *parent) : headers->viewer = panel_sequence_viewer; - //video_area->SetAlignment(olive::timeline::kAlignmentBottom); +// video_area->SetAlignment(olive::timeline::kAlignmentBottom); tool_buttons.append(toolArrowButton); tool_buttons.append(toolEditButton); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 4e4558450..d2a3000ed 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -1,5 +1,10 @@ #include "timelinearea.h" +#include + +#include "panels/timeline.h" +#include "global/config.h" + int olive::timeline::kTimelineLabelFixedWidth = 200; TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignment) : @@ -12,13 +17,22 @@ TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignm layout->setSpacing(0); // LABELS - QWidget* label_container = new QWidget(); - label_container->setFixedWidth(olive::timeline::kTimelineLabelFixedWidth); - label_container_layout_ = new QVBoxLayout(label_container); + label_container_ = new QWidget(); + label_container_layout_ = new QVBoxLayout(label_container_); label_container_layout_->setMargin(0); label_container_layout_->setSpacing(0); label_container_layout_->addStretch(); - layout->addWidget(label_container); + + // LABEL SCROLLAREA + QScrollArea* label_area = new QScrollArea(); + label_area->setFrameShape(QFrame::NoFrame); + label_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + label_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + label_area->verticalScrollBar()->setMaximum(INT_MAX); + label_area->setWidgetResizable(true); + label_area->setFixedWidth(olive::timeline::kTimelineLabelFixedWidth); + label_area->setWidget(label_container_); + layout->addWidget(label_area); // VIEW view_ = new TimelineView(timeline_); @@ -26,10 +40,13 @@ TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignm layout->addWidget(view_); // SCROLLBAR - QScrollBar* scrollbar = new QScrollBar(Qt::Vertical); - layout->addWidget(scrollbar); + scrollbar_ = new QScrollBar(Qt::Vertical); + layout->addWidget(scrollbar_); - view_->scrollBar = scrollbar; + connect(scrollbar_, SIGNAL(valueChanged(int)), view_, SLOT(setScroll(int))); + connect(scrollbar_, SIGNAL(valueChanged(int)), label_area->verticalScrollBar(), SLOT(setValue(int))); + connect(view_, SIGNAL(setScrollMaximum(int)), this, SLOT(setScrollMaximum(int))); + connect(view_, SIGNAL(requestScrollChange(int)), scrollbar_, SLOT(setValue(int))); } void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) @@ -55,6 +72,78 @@ void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) } +void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) +{ + view_->SetAlignment(alignment); + alignment_ = alignment; + + RefreshLabels(); +} + +void TimelineArea::wheelEvent(QWheelEvent *event) +{ + + // TODO: implement pixel scrolling + + bool shift = (event->modifiers() & Qt::ShiftModifier); + bool ctrl = (event->modifiers() & Qt::ControlModifier); + bool alt = (event->modifiers() & Qt::AltModifier); + + // "Scroll Zooms" false + Control up : not zooming + // "Scroll Zooms" false + Control down: zooming + // "Scroll Zooms" true + Control up : zooming + // "Scroll Zooms" true + Control down: not zooming + bool zooming = (olive::config.scroll_zooms != ctrl); + + // Allow shift for axis swap, but don't swap on zoom... Unless + // we need to override Qt's axis swap via Alt + bool swap_hv = ((shift != olive::config.invert_timeline_scroll_axes) & + !zooming) | (alt & !shift & zooming); + + int delta_h = swap_hv ? event->angleDelta().y() : event->angleDelta().x(); + int delta_v = swap_hv ? event->angleDelta().x() : event->angleDelta().y(); + + if (zooming) { + + // Zoom only uses vertical scrolling, to avoid glitches on touchpads. + // Don't do anything if not scrolling vertically. + + if (delta_v != 0) { + + // delta_v == 120 for one click of a mousewheel. Less or more for a + // touchpad gesture. Calculate speed to compensate. + // 120 = ratio of 4/3 (1.33), -120 = ratio of 3/4 (.75) + + double zoom_ratio = 1.0 + (abs(delta_v) * 0.33 / 120); + + if (delta_v < 0) { + zoom_ratio = 1.0 / zoom_ratio; + } + + timeline_->multiply_zoom(zoom_ratio); + } + + } else { + + // Use the Timeline's main scrollbar for horizontal scrolling, and this + // widget's scrollbar for vertical scrolling. + + QScrollBar* bar_v = scrollbar_; + QScrollBar* bar_h = timeline_->horizontalScrollBar; + + // Match the wheel events to the size of a step as per + // https://doc.qt.io/qt-5/qwheelevent.html#angleDelta + + int step_h = bar_h->singleStep() * delta_h / -120; + int step_v = bar_v->singleStep() * delta_v / -120; + + // Apply to appropriate scrollbars + + bar_h->setValue(bar_h->value() + step_h); + bar_v->setValue(bar_v->value() + step_v); + } +} + void TimelineArea::RefreshLabels() { if (track_list_ == nullptr) { @@ -68,8 +157,29 @@ void TimelineArea::RefreshLabels() labels_[i] = std::make_shared(); labels_[i]->SetTrack(track_list_->TrackAt(i)); - label_container_layout_->insertWidget(label_container_layout_->count()-1, labels_[i].get()); + switch (alignment_) { + case olive::timeline::kAlignmentTop: + label_container_layout_->insertWidget(label_container_layout_->count()-1, labels_[i].get()); + break; + case olive::timeline::kAlignmentBottom: + label_container_layout_->insertWidget(1, labels_[i].get()); + break; + case olive::timeline::kAlignmentSingle: + break; + } } } } + +void TimelineArea::resizeEvent(QResizeEvent *) +{ + scrollbar_->setPageStep(view_->height()); +} + +void TimelineArea::setScrollMaximum(int maximum) +{ + scrollbar_->setMaximum(qMax(0, maximum - view_->height())); + label_container_->setMinimumHeight(maximum); +// label_container_->setFixedHeight(maximum); +} diff --git a/ui/timelinearea.h b/ui/timelinearea.h index 511152534..d3778a3a0 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -15,8 +15,13 @@ public: TimelineArea(Timeline *timeline, olive::timeline::Alignment alignment = olive::timeline::kAlignmentTop); void SetTrackList(Sequence* sequence, Track::Type track_list); + void SetAlignment(olive::timeline::Alignment alignment); + + virtual void wheelEvent(QWheelEvent *event) override; public slots: void RefreshLabels(); +protected: + virtual void resizeEvent(QResizeEvent *event) override; private: Timeline* timeline_; TrackList* track_list_; @@ -24,6 +29,10 @@ private: QVector labels_; olive::timeline::Alignment alignment_; QVBoxLayout* label_container_layout_; + QScrollBar* scrollbar_; + QWidget* label_container_; +private slots: + void setScrollMaximum(int); }; #endif // TIMELINEAREA_H diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp index 9163b6286..7f7cf583e 100644 --- a/ui/timelinelabel.cpp +++ b/ui/timelinelabel.cpp @@ -68,6 +68,14 @@ void TimelineLabel::UpdateState() lock_button_->setChecked(track_->IsLocked()); } +void TimelineLabel::paintEvent(QPaintEvent *) +{ + QPainter p(this); + + p.setPen(QColor(0, 0, 0, 96)); + p.drawLine(0, height() - 1, width(), height() - 1); +} + void TimelineLabel::RenameTrack() { bool ok; @@ -86,5 +94,5 @@ void TimelineLabel::RenameTrack() void TimelineLabel::UpdateHeight(int h) { - setFixedHeight(h); + setFixedHeight(h+1); } diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h index 0a5a65c3c..56368e62c 100644 --- a/ui/timelinelabel.h +++ b/ui/timelinelabel.h @@ -15,6 +15,8 @@ public: void SetTrack(Track* track); void UpdateState(); +protected: + virtual void paintEvent(QPaintEvent *event) override; private: QPushButton* mute_button_; QPushButton* solo_button_; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 143a85b47..09e937ffd 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -346,66 +346,7 @@ void TimelineView::dragMoveEvent(QDragMoveEvent *event) { } void TimelineView::wheelEvent(QWheelEvent *event) { - - // TODO: implement pixel scrolling - - bool shift = (event->modifiers() & Qt::ShiftModifier); - bool ctrl = (event->modifiers() & Qt::ControlModifier); - bool alt = (event->modifiers() & Qt::AltModifier); - - // "Scroll Zooms" false + Control up : not zooming - // "Scroll Zooms" false + Control down: zooming - // "Scroll Zooms" true + Control up : zooming - // "Scroll Zooms" true + Control down: not zooming - bool zooming = (olive::config.scroll_zooms != ctrl); - - // Allow shift for axis swap, but don't swap on zoom... Unless - // we need to override Qt's axis swap via Alt - bool swap_hv = ((shift != olive::config.invert_timeline_scroll_axes) & - !zooming) | (alt & !shift & zooming); - - int delta_h = swap_hv ? event->angleDelta().y() : event->angleDelta().x(); - int delta_v = swap_hv ? event->angleDelta().x() : event->angleDelta().y(); - - if (zooming) { - - // Zoom only uses vertical scrolling, to avoid glitches on touchpads. - // Don't do anything if not scrolling vertically. - - if (delta_v != 0) { - - // delta_v == 120 for one click of a mousewheel. Less or more for a - // touchpad gesture. Calculate speed to compensate. - // 120 = ratio of 4/3 (1.33), -120 = ratio of 3/4 (.75) - - double zoom_ratio = 1.0 + (abs(delta_v) * 0.33 / 120); - - if (delta_v < 0) { - zoom_ratio = 1.0 / zoom_ratio; - } - - ParentTimeline()->multiply_zoom(zoom_ratio); - } - - } else { - - // Use the Timeline's main scrollbar for horizontal scrolling, and this - // widget's scrollbar for vertical scrolling. - - QScrollBar* bar_v = scrollBar; - QScrollBar* bar_h = ParentTimeline()->horizontalScrollBar; - - // Match the wheel events to the size of a step as per - // https://doc.qt.io/qt-5/qwheelevent.html#angleDelta - - int step_h = bar_h->singleStep() * delta_h / -120; - int step_v = bar_v->singleStep() * delta_v / -120; - - // Apply to appropriate scrollbars - - bar_h->setValue(bar_h->value() + step_h); - bar_v->setValue(bar_v->value() + step_v); - } + static_cast(parent())->wheelEvent(event); } void TimelineView::dragLeaveEvent(QDragLeaveEvent* event) { @@ -2206,7 +2147,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // the scrollbars trigger repaints when they scroll, which is unnecessary here so we block them ParentTimeline()->block_repaints = true; ParentTimeline()->horizontalScrollBar->setValue(ParentTimeline()->horizontalScrollBar->value() + ParentTimeline()->drag_x_start - event->pos().x()); - scrollBar->setValue(scrollBar->value() + ParentTimeline()->drag_y_start - event->pos().y()); + emit requestScrollChange(scroll + ParentTimeline()->drag_y_start - event->pos().y()); ParentTimeline()->block_repaints = false; // finally repaint @@ -2224,12 +2165,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { int diff = (event->pos().y() - ParentTimeline()->drag_y_start); // add it to the current track height - int new_height = track_target->height(); - if (alignment_ == olive::timeline::kAlignmentBottom) { - new_height -= diff; - } else { - new_height += diff; - } + int new_height = track_target->height() + diff; // limit track height to track minimum height constant new_height = qMax(new_height, olive::timeline::kTrackMinHeight); @@ -2713,14 +2649,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { Track* hover_track = getTrackFromScreenPoint(mouse_pos); if (hover_track != nullptr) { - int test_range = 5; // FIXME magic number + int test_range = 10; // FIXME magic number int track_y_edge = getScreenPointFromTrack(hover_track); - if (alignment_ == olive::timeline::kAlignmentTop) { - track_y_edge += hover_track->height(); - } - if (mouse_pos > track_y_edge - test_range && mouse_pos < track_y_edge + test_range) { track_resizing = true; @@ -2952,350 +2884,344 @@ void TimelineView::paintEvent(QPaintEvent*) { int panel_height = olive::timeline::kTrackDefaultHeight; for (int i=0;iTrackCount();i++) { - panel_height += track_list_->TrackAt(i)->height() + 1; - } - if (alignment_ == olive::timeline::kAlignmentBottom) { - scrollBar->setMinimum(qMin(0, - panel_height + height())); - } else { - scrollBar->setMaximum(qMax(0, panel_height - height())); + panel_height += track_list_->TrackAt(i)->height(); } - int track_line = 0; + emit setScrollMaximum(panel_height); for (int i=0;iTrackCount();i++) { Track* track = track_list_->TrackAt(i); - for (int j=0;jClipCount();j++) { + int track_top = getScreenPointFromTrack(track); + int track_bottom = track_top + track->height(); - Clip* clip = track->GetClip(j).get(); + if (track_bottom > 0 && track_top < height()) { + for (int j=0;jClipCount();j++) { - QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), - getScreenPointFromTrack(clip->track()), - getScreenPointFromFrame(ParentTimeline()->zoom, clip->length()), - clip->track()->height()); - QRect text_rect(clip_rect.left() + olive::timeline::kClipTextPadding, - clip_rect.top() + olive::timeline::kClipTextPadding, - clip_rect.width() - olive::timeline::kClipTextPadding - 1, - clip_rect.height() - olive::timeline::kClipTextPadding - 1); - if (clip_rect.left() < width() && clip_rect.right() >= 0 && clip_rect.top() < height() && clip_rect.bottom() >= 0) { - QRect actual_clip_rect = clip_rect; - if (actual_clip_rect.x() < 0) actual_clip_rect.setX(0); - if (actual_clip_rect.right() > width()) actual_clip_rect.setRight(width()); - if (actual_clip_rect.y() < 0) actual_clip_rect.setY(0); - if (actual_clip_rect.bottom() > height()) actual_clip_rect.setBottom(height()); - p.fillRect(actual_clip_rect, (clip->enabled()) ? clip->color() : QColor(96, 96, 96)); + Clip* clip = track->GetClip(j).get(); - int thumb_x = clip_rect.x() + 1; + QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), + track_top, + getScreenPointFromFrame(ParentTimeline()->zoom, clip->length()), + track->height() - 1); + QRect text_rect(clip_rect.left() + olive::timeline::kClipTextPadding, + clip_rect.top() + olive::timeline::kClipTextPadding, + clip_rect.width() - olive::timeline::kClipTextPadding - 1, + clip_rect.height() - olive::timeline::kClipTextPadding - 1); + if (clip_rect.left() < width() && clip_rect.right() >= 0 && clip_rect.top() < height() && clip_rect.bottom() >= 0) { + QRect actual_clip_rect = clip_rect; + if (actual_clip_rect.x() < 0) actual_clip_rect.setX(0); + if (actual_clip_rect.right() > width()) actual_clip_rect.setRight(width()); + if (actual_clip_rect.y() < 0) actual_clip_rect.setY(0); + if (actual_clip_rect.bottom() > height()) actual_clip_rect.setBottom(height()); + p.fillRect(actual_clip_rect, (clip->enabled()) ? clip->color() : QColor(96, 96, 96)); - if (clip->media() != nullptr && clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { - bool draw_checkerboard = false; - QRect checkerboard_rect(clip_rect); - FootageStream* ms = clip->media_stream(); - if (ms == nullptr) { - draw_checkerboard = true; - } else if (ms->preview_done) { - // draw top and tail triangles - int triangle_size = olive::timeline::kTrackMinHeight >> 2; - if (!ms->infinite_length && clip_rect.width() > triangle_size) { - p.setPen(Qt::NoPen); - p.setBrush(QColor(80, 80, 80)); - if (clip->clip_in() == 0 - && clip_rect.x() + triangle_size > 0 - && clip_rect.y() + triangle_size > 0 - && clip_rect.x() < width() - && clip_rect.y() < height()) { - const QPoint points[3] = { - QPoint(clip_rect.x(), clip_rect.y()), - QPoint(clip_rect.x() + triangle_size, clip_rect.y()), - QPoint(clip_rect.x(), clip_rect.y() + triangle_size) - }; - p.drawPolygon(points, 3); - text_rect.setLeft(text_rect.left() + (triangle_size >> 2)); - } - if (clip->timeline_out() - clip->timeline_in() + clip->clip_in() == clip->media_length() - && clip_rect.right() - triangle_size < width() - && clip_rect.y() + triangle_size > 0 - && clip_rect.right() > 0 - && clip_rect.y() < height()) { - const QPoint points[3] = { - QPoint(clip_rect.right(), clip_rect.y()), - QPoint(clip_rect.right() - triangle_size, clip_rect.y()), - QPoint(clip_rect.right(), clip_rect.y() + triangle_size) - }; - p.drawPolygon(points, 3); - text_rect.setRight(text_rect.right() - (triangle_size >> 2)); - } - } + int thumb_x = clip_rect.x() + 1; - p.setBrush(Qt::NoBrush); - - // draw thumbnail/waveform - long media_length = clip->media_length(); - - if (clip->type() == Track::kTypeVideo) { - // draw thumbnail - int thumb_y = p.fontMetrics().height()+olive::timeline::kClipTextPadding+olive::timeline::kClipTextPadding; - if (thumb_x < width() && thumb_y < height()) { - int space_for_thumb = clip_rect.width()-1; - if (clip->opening_transition != nullptr) { - int ot_width = getScreenPointFromFrame(ParentTimeline()->zoom, clip->opening_transition->get_true_length()); - thumb_x += ot_width; - space_for_thumb -= ot_width; + if (clip->media() != nullptr && clip->media()->get_type() == MEDIA_TYPE_FOOTAGE) { + bool draw_checkerboard = false; + QRect checkerboard_rect(clip_rect); + FootageStream* ms = clip->media_stream(); + if (ms == nullptr) { + draw_checkerboard = true; + } else if (ms->preview_done) { + // draw top and tail triangles + int triangle_size = olive::timeline::kTrackMinHeight >> 2; + if (!ms->infinite_length && clip_rect.width() > triangle_size) { + p.setPen(Qt::NoPen); + p.setBrush(QColor(80, 80, 80)); + if (clip->clip_in() == 0 + && clip_rect.x() + triangle_size > 0 + && clip_rect.y() + triangle_size > 0 + && clip_rect.x() < width() + && clip_rect.y() < height()) { + const QPoint points[3] = { + QPoint(clip_rect.x(), clip_rect.y()), + QPoint(clip_rect.x() + triangle_size, clip_rect.y()), + QPoint(clip_rect.x(), clip_rect.y() + triangle_size) + }; + p.drawPolygon(points, 3); + text_rect.setLeft(text_rect.left() + (triangle_size >> 2)); } - if (clip->closing_transition != nullptr) { - space_for_thumb -= getScreenPointFromFrame(ParentTimeline()->zoom, clip->closing_transition->get_true_length()); - } - int thumb_height = clip_rect.height()-thumb_y; - int thumb_width = qRound(thumb_height*(double(ms->video_preview.width())/double(ms->video_preview.height()))); - if (thumb_x + thumb_width >= 0 - && thumb_height > thumb_y - && thumb_y + thumb_height >= 0 - && space_for_thumb > MAX_TEXT_WIDTH) { - int thumb_clip_width = qMin(thumb_width, space_for_thumb); - p.drawImage(QRect(thumb_x, - clip_rect.y()+thumb_y, - thumb_clip_width, - thumb_height), - ms->video_preview, - QRect(0, - 0, - qRound(thumb_clip_width*(double(ms->video_preview.width())/double(thumb_width))), - ms->video_preview.height() - ) - ); + if (clip->timeline_out() - clip->timeline_in() + clip->clip_in() == clip->media_length() + && clip_rect.right() - triangle_size < width() + && clip_rect.y() + triangle_size > 0 + && clip_rect.right() > 0 + && clip_rect.y() < height()) { + const QPoint points[3] = { + QPoint(clip_rect.right(), clip_rect.y()), + QPoint(clip_rect.right() - triangle_size, clip_rect.y()), + QPoint(clip_rect.right(), clip_rect.y() + triangle_size) + }; + p.drawPolygon(points, 3); + text_rect.setRight(text_rect.right() - (triangle_size >> 2)); } } - if (clip->timeline_out() - clip->timeline_in() + clip->clip_in() > clip->media_length()) { - draw_checkerboard = true; - checkerboard_rect.setLeft(ParentTimeline()->getTimelineScreenPointFromFrame(clip->media_length() + clip->timeline_in() - clip->clip_in())); - } - } else if (clip_rect.height() > olive::timeline::kTrackMinHeight) { - // draw waveform - p.setPen(QColor(80, 80, 80)); - int waveform_start = -qMin(clip_rect.x(), 0); - int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(ParentTimeline()->zoom, media_length - clip->clip_in())); + p.setBrush(Qt::NoBrush); - if ((clip_rect.x() + waveform_limit) > width()) { - waveform_limit -= (clip_rect.x() + waveform_limit - width()); - } else if (waveform_limit < clip_rect.width()) { - draw_checkerboard = true; - if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit); - } + // draw thumbnail/waveform + long media_length = clip->media_length(); - draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, ParentTimeline()->zoom); - } - } - if (draw_checkerboard) { - checkerboard_rect.setLeft(qMax(checkerboard_rect.left(), 0)); - checkerboard_rect.setRight(qMin(checkerboard_rect.right(), width())); - checkerboard_rect.setTop(qMax(checkerboard_rect.top(), 0)); - checkerboard_rect.setBottom(qMin(checkerboard_rect.bottom(), height())); - - if (checkerboard_rect.left() < width() - && checkerboard_rect.right() >= 0 - && checkerboard_rect.top() < height() - && checkerboard_rect.bottom() >= 0) { - // draw "error lines" if media stream is missing - p.setPen(QPen(QColor(64, 64, 64), 2)); - int limit = checkerboard_rect.width(); - int clip_height = checkerboard_rect.height(); - for (int j=-clip_height;jtype() == Track::kTypeVideo) { + // draw thumbnail + int thumb_y = p.fontMetrics().height()+olive::timeline::kClipTextPadding+olive::timeline::kClipTextPadding; + if (thumb_x < width() && thumb_y < height()) { + int space_for_thumb = clip_rect.width()-1; + if (clip->opening_transition != nullptr) { + int ot_width = getScreenPointFromFrame(ParentTimeline()->zoom, clip->opening_transition->get_true_length()); + thumb_x += ot_width; + space_for_thumb -= ot_width; + } + if (clip->closing_transition != nullptr) { + space_for_thumb -= getScreenPointFromFrame(ParentTimeline()->zoom, clip->closing_transition->get_true_length()); + } + int thumb_height = clip_rect.height()-thumb_y; + int thumb_width = qRound(thumb_height*(double(ms->video_preview.width())/double(ms->video_preview.height()))); + if (thumb_x + thumb_width >= 0 + && thumb_height > thumb_y + && thumb_y + thumb_height >= 0 + && space_for_thumb > MAX_TEXT_WIDTH) { + int thumb_clip_width = qMin(thumb_width, space_for_thumb); + p.drawImage(QRect(thumb_x, + clip_rect.y()+thumb_y, + thumb_clip_width, + thumb_height), + ms->video_preview, + QRect(0, + 0, + qRound(thumb_clip_width*(double(ms->video_preview.width())/double(thumb_width))), + ms->video_preview.height() + ) + ); + } } - if (lines_end_x > checkerboard_rect.right()) { - lines_end_y -= (checkerboard_rect.right() - lines_end_x); - lines_end_x = checkerboard_rect.right(); + if (clip->timeline_out() - clip->timeline_in() + clip->clip_in() > clip->media_length()) { + draw_checkerboard = true; + checkerboard_rect.setLeft(ParentTimeline()->getTimelineScreenPointFromFrame(clip->media_length() + clip->timeline_in() - clip->clip_in())); + } + } else if (clip_rect.height() > olive::timeline::kTrackMinHeight) { + // draw waveform + p.setPen(QColor(80, 80, 80)); + + int waveform_start = -qMin(clip_rect.x(), 0); + int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(ParentTimeline()->zoom, media_length - clip->clip_in())); + + if ((clip_rect.x() + waveform_limit) > width()) { + waveform_limit -= (clip_rect.x() + waveform_limit - width()); + } else if (waveform_limit < clip_rect.width()) { + draw_checkerboard = true; + if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit); + } + + draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, ParentTimeline()->zoom); + } + } + if (draw_checkerboard) { + checkerboard_rect.setLeft(qMax(checkerboard_rect.left(), 0)); + checkerboard_rect.setRight(qMin(checkerboard_rect.right(), width())); + checkerboard_rect.setTop(qMax(checkerboard_rect.top(), 0)); + checkerboard_rect.setBottom(qMin(checkerboard_rect.bottom(), height())); + + if (checkerboard_rect.left() < width() + && checkerboard_rect.right() >= 0 + && checkerboard_rect.top() < height() + && checkerboard_rect.bottom() >= 0) { + // draw "error lines" if media stream is missing + p.setPen(QPen(QColor(64, 64, 64), 2)); + int limit = checkerboard_rect.width(); + int clip_height = checkerboard_rect.height(); + for (int j=-clip_height;j checkerboard_rect.right()) { + lines_end_y -= (checkerboard_rect.right() - lines_end_x); + lines_end_x = checkerboard_rect.right(); + } + p.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y); } - p.drawLine(lines_start_x, lines_start_y, lines_end_x, lines_end_y); } } } - } - // draw clip markers - for (int j=0;jget_markers().size();j++) { - const Marker& m = clip->get_markers().at(j); + // draw clip markers + for (int j=0;jget_markers().size();j++) { + const Marker& m = clip->get_markers().at(j); - // convert marker time (in clip time) to sequence time - long marker_time = m.frame + clip->timeline_in() - clip->clip_in(); - int marker_x = ParentTimeline()->getTimelineScreenPointFromFrame(marker_time); - if (marker_x > clip_rect.x() && marker_x < clip_rect.right()) { - Marker::Draw(p, marker_x, clip_rect.bottom()-p.fontMetrics().height(), clip_rect.bottom(), false); - } - } - p.setBrush(Qt::NoBrush); - - // draw clip transitions - draw_transition(p, clip, clip_rect, text_rect, kTransitionOpening); - draw_transition(p, clip, clip_rect, text_rect, kTransitionClosing); - - // top left bevel - p.setPen(Qt::white); - if (clip_rect.x() >= 0 && clip_rect.x() < width()) p.drawLine(clip_rect.bottomLeft(), clip_rect.topLeft()); - if (clip_rect.y() >= 0 && clip_rect.y() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.top()), QPoint(qMin(width(), clip_rect.right()), clip_rect.top())); - - // draw text - if (text_rect.width() > MAX_TEXT_WIDTH && text_rect.right() > 0 && text_rect.left() < width()) { - if (!clip->enabled()) { - p.setPen(Qt::gray); - } else if (clip->color().lightness() > 160) { - // set to black if color is bright - p.setPen(Qt::black); - } - if (clip->linked.size() > 0) { - int underline_y = olive::timeline::kClipTextPadding + p.fontMetrics().height() + clip_rect.top(); - int underline_width = qMin(text_rect.width() - 1, p.fontMetrics().width(clip->name())); - p.drawLine(text_rect.x(), underline_y, text_rect.x() + underline_width, underline_y); - } - QString name = clip->name(); - if (clip->speed().value != 1.0 || clip->reversed()) { - name += " ("; - if (clip->reversed()) name += "-"; - name += QString::number(clip->speed().value*100) + "%)"; - } - p.drawText(text_rect, 0, name, &text_rect); - } - - // bottom right gray - p.setPen(QColor(0, 0, 0, 128)); - if (clip_rect.right() >= 0 && clip_rect.right() < width()) p.drawLine(clip_rect.bottomRight(), clip_rect.topRight()); - if (clip_rect.bottom() >= 0 && clip_rect.bottom() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.bottom()), QPoint(qMin(width(), clip_rect.right()), clip_rect.bottom())); - - // draw transition tool - if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { - - bool shared_transition = (ParentTimeline()->transition_tool_open_clip != nullptr - && ParentTimeline()->transition_tool_close_clip != nullptr); - - QRect transition_tool_rect = clip_rect; - bool draw_transition_tool_rect = false; - - if (ParentTimeline()->transition_tool_open_clip == clip) { - if (shared_transition) { - transition_tool_rect.setWidth(TRANSITION_BETWEEN_RANGE); - } else { - transition_tool_rect.setWidth(transition_tool_rect.width()>>2); + // convert marker time (in clip time) to sequence time + long marker_time = m.frame + clip->timeline_in() - clip->clip_in(); + int marker_x = ParentTimeline()->getTimelineScreenPointFromFrame(marker_time); + if (marker_x > clip_rect.x() && marker_x < clip_rect.right()) { + Marker::Draw(p, marker_x, clip_rect.bottom()-p.fontMetrics().height(), clip_rect.bottom(), false); } - draw_transition_tool_rect = true; - } else if (ParentTimeline()->transition_tool_close_clip == clip) { - if (shared_transition) { - transition_tool_rect.setLeft(transition_tool_rect.right() - TRANSITION_BETWEEN_RANGE); - } else { - transition_tool_rect.setLeft(transition_tool_rect.left() + (3*(transition_tool_rect.width()>>2))); + } + p.setBrush(Qt::NoBrush); + + // draw clip transitions + draw_transition(p, clip, clip_rect, text_rect, kTransitionOpening); + draw_transition(p, clip, clip_rect, text_rect, kTransitionClosing); + + // top left bevel + p.setPen(Qt::white); + if (clip_rect.x() >= 0 && clip_rect.x() < width()) p.drawLine(clip_rect.bottomLeft(), clip_rect.topLeft()); + if (clip_rect.y() >= 0 && clip_rect.y() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.top()), QPoint(qMin(width(), clip_rect.right()), clip_rect.top())); + + // draw text + if (text_rect.width() > MAX_TEXT_WIDTH && text_rect.right() > 0 && text_rect.left() < width()) { + if (!clip->enabled()) { + p.setPen(Qt::gray); + } else if (clip->color().lightness() > 160) { + // set to black if color is bright + p.setPen(Qt::black); } - draw_transition_tool_rect = true; + if (clip->linked.size() > 0) { + int underline_y = olive::timeline::kClipTextPadding + p.fontMetrics().height() + clip_rect.top(); + int underline_width = qMin(text_rect.width() - 1, p.fontMetrics().width(clip->name())); + p.drawLine(text_rect.x(), underline_y, text_rect.x() + underline_width, underline_y); + } + QString name = clip->name(); + if (clip->speed().value != 1.0 || clip->reversed()) { + name += " ("; + if (clip->reversed()) name += "-"; + name += QString::number(clip->speed().value*100) + "%)"; + } + p.drawText(text_rect, 0, name, &text_rect); } - if (draw_transition_tool_rect - && transition_tool_rect.left() < width() - && transition_tool_rect.right() > 0) { - if (transition_tool_rect.left() < 0) { - transition_tool_rect.setLeft(0); + // bottom right gray + p.setPen(QColor(0, 0, 0, 128)); + if (clip_rect.right() >= 0 && clip_rect.right() < width()) p.drawLine(clip_rect.bottomRight(), clip_rect.topRight()); + if (clip_rect.bottom() >= 0 && clip_rect.bottom() < height()) p.drawLine(QPoint(qMax(0, clip_rect.left()), clip_rect.bottom()), QPoint(qMin(width(), clip_rect.right()), clip_rect.bottom())); + + // draw transition tool + if (olive::timeline::current_tool == olive::timeline::TIMELINE_TOOL_TRANSITION) { + + bool shared_transition = (ParentTimeline()->transition_tool_open_clip != nullptr + && ParentTimeline()->transition_tool_close_clip != nullptr); + + QRect transition_tool_rect = clip_rect; + bool draw_transition_tool_rect = false; + + if (ParentTimeline()->transition_tool_open_clip == clip) { + if (shared_transition) { + transition_tool_rect.setWidth(TRANSITION_BETWEEN_RANGE); + } else { + transition_tool_rect.setWidth(transition_tool_rect.width()>>2); + } + draw_transition_tool_rect = true; + } else if (ParentTimeline()->transition_tool_close_clip == clip) { + if (shared_transition) { + transition_tool_rect.setLeft(transition_tool_rect.right() - TRANSITION_BETWEEN_RANGE); + } else { + transition_tool_rect.setLeft(transition_tool_rect.left() + (3*(transition_tool_rect.width()>>2))); + } + draw_transition_tool_rect = true; } - if (transition_tool_rect.right() > width()) { - transition_tool_rect.setRight(width()); + + if (draw_transition_tool_rect + && transition_tool_rect.left() < width() + && transition_tool_rect.right() > 0) { + if (transition_tool_rect.left() < 0) { + transition_tool_rect.setLeft(0); + } + if (transition_tool_rect.right() > width()) { + transition_tool_rect.setRight(width()); + } + p.fillRect(transition_tool_rect, QColor(0, 0, 0, 128)); } - p.fillRect(transition_tool_rect, QColor(0, 0, 0, 128)); } } } - } - // Draw recording clip if recording if valid - if (panel_sequence_viewer->is_recording_cued() && panel_sequence_viewer->recording_track == track) { - int rec_track_x = ParentTimeline()->getTimelineScreenPointFromFrame(panel_sequence_viewer->recording_start); - int rec_track_y = getScreenPointFromTrack(panel_sequence_viewer->recording_track); - int rec_track_height = track->height(); - if (panel_sequence_viewer->recording_start != panel_sequence_viewer->recording_end) { - QRect rec_rect( + // Draw recording clip if recording if valid + if (panel_sequence_viewer->is_recording_cued() && panel_sequence_viewer->recording_track == track) { + int rec_track_x = ParentTimeline()->getTimelineScreenPointFromFrame(panel_sequence_viewer->recording_start); + int rec_track_y = getScreenPointFromTrack(panel_sequence_viewer->recording_track); + int rec_track_height = track->height(); + if (panel_sequence_viewer->recording_start != panel_sequence_viewer->recording_end) { + QRect rec_rect( + rec_track_x, + rec_track_y, + getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->recording_end - panel_sequence_viewer->recording_start), + rec_track_height + ); + p.setPen(QPen(QColor(96, 96, 96), 2)); + p.fillRect(rec_rect, QColor(192, 192, 192)); + p.drawRect(rec_rect); + } + QRect active_rec_rect( rec_track_x, rec_track_y, - getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->recording_end - panel_sequence_viewer->recording_start), + getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->seq->playhead - panel_sequence_viewer->recording_start), rec_track_height ); - p.setPen(QPen(QColor(96, 96, 96), 2)); - p.fillRect(rec_rect, QColor(192, 192, 192)); - p.drawRect(rec_rect); + p.setPen(QPen(QColor(192, 0, 0), 2)); + p.fillRect(active_rec_rect, QColor(255, 96, 96)); + p.drawRect(active_rec_rect); + + p.setPen(Qt::NoPen); + + if (!panel_sequence_viewer->playing) { + int rec_marker_size = 6; + int rec_track_midY = rec_track_y + (rec_track_height >> 1); + p.setBrush(Qt::white); + QPoint cue_marker[3] = { + QPoint(rec_track_x, rec_track_midY - rec_marker_size), + QPoint(rec_track_x + rec_marker_size, rec_track_midY), + QPoint(rec_track_x, rec_track_midY + rec_marker_size) + }; + p.drawPolygon(cue_marker, 3); + } } - QRect active_rec_rect( - rec_track_x, - rec_track_y, - getScreenPointFromFrame(ParentTimeline()->zoom, panel_sequence_viewer->seq->playhead - panel_sequence_viewer->recording_start), - rec_track_height - ); - p.setPen(QPen(QColor(192, 0, 0), 2)); - p.fillRect(active_rec_rect, QColor(255, 96, 96)); - p.drawRect(active_rec_rect); - p.setPen(Qt::NoPen); + // Draw selections + QVector selections = track->Selections(); + for (int j=0;jplaying) { - int rec_marker_size = 6; - int rec_track_midY = rec_track_y + (rec_track_height >> 1); - p.setBrush(Qt::white); - QPoint cue_marker[3] = { - QPoint(rec_track_x, rec_track_midY - rec_marker_size), - QPoint(rec_track_x + rec_marker_size, rec_track_midY), - QPoint(rec_track_x, rec_track_midY + rec_marker_size) - }; - p.drawPolygon(cue_marker, 3); + int selection_x = ParentTimeline()->getTimelineScreenPointFromFrame(s.in()); + p.setPen(Qt::NoPen); + p.setBrush(Qt::NoBrush); + p.fillRect(selection_x, + track_top, + ParentTimeline()->getTimelineScreenPointFromFrame(s.out()) - selection_x, + track->height(), + QColor(0, 0, 0, 64)); } - } - // Draw selections - QVector selections = track->Selections(); - for (int j=0;jsplitting && ParentTimeline()->split_tracks.contains(track)) { + int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->drag_frame_start); - int selection_x = ParentTimeline()->getTimelineScreenPointFromFrame(s.in()); - p.setPen(Qt::NoPen); - p.setBrush(Qt::NoBrush); - p.fillRect(selection_x, - track_line, - ParentTimeline()->getTimelineScreenPointFromFrame(s.out()) - selection_x, - s.track()->height(), - QColor(0, 0, 0, 64)); - } + p.setPen(QColor(64, 64, 64)); + p.drawLine(cursor_x, + track_top, + cursor_x, + track_top + track->height()); + } - // Draw splitting cursor - if (ParentTimeline()->splitting && ParentTimeline()->split_tracks.contains(track)) { - int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->drag_frame_start); + // Draw edit cursor + if (current_tool_shows_cursor() && ParentTimeline()->cursor_track == track) { + int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->cursor_frame); - p.setPen(QColor(64, 64, 64)); - p.drawLine(cursor_x, - track_line, - cursor_x, - track_line + track->height()); - } + p.setPen(Qt::gray); + p.drawLine(cursor_x, + track_top, + cursor_x, + track_top + track->height()); + } - // Draw edit cursor - if (current_tool_shows_cursor() && ParentTimeline()->cursor_track == track) { - int cursor_x = ParentTimeline()->getTimelineScreenPointFromFrame(ParentTimeline()->cursor_frame); - - p.setPen(Qt::gray); - p.drawLine(cursor_x, - track_line, - cursor_x, - track_line + track->height()); - } - - // Draw track's line - track_line += track->height(); - if (track_line >= 0 && track_line < height()) { + // Draw track line p.setPen(QColor(0, 0, 0, 96)); - p.drawLine(0, track_line, rect().width(), track_line); + p.drawLine(0, track_bottom, rect().width(), track_bottom); } - track_line++; - - } // draw rectangle select @@ -3376,10 +3302,6 @@ void TimelineView::paintEvent(QPaintEvent*) { } } -void TimelineView::resizeEvent(QResizeEvent *) { - scrollBar->setPageStep(height()); -} - // ************************************** // screen point <-> frame/track functions // ************************************** @@ -3402,18 +3324,28 @@ int TimelineView::getScreenPointFromTrack(Track *track) { int TimelineView::getTrackIndexFromScreenPoint(int y) { + if (alignment_ == olive::timeline::kAlignmentSingle) { + return 0; + } + if (y < 0) { return 0; } y += scroll; + /* + if (alignment_ == olive::timeline::kAlignmentBottom) { + y = height() - y; + } + */ + int heights = 0; int i = 0; while (true) { - int new_heights = heights + 1; + int new_heights = heights; if (i < track_list_->TrackCount()) { new_heights += track_list_->TrackAt(i)->height(); @@ -3434,6 +3366,10 @@ int TimelineView::getTrackIndexFromScreenPoint(int y) int TimelineView::getScreenPointFromTrackIndex(int track) { + if (alignment_ == olive::timeline::kAlignmentSingle) { + return 0; + } + int point = 0; for (int i=0;iFirst()->height(); + } + */ + return screen_point; } Timeline *TimelineView::ParentTimeline() diff --git a/ui/timelineview.h b/ui/timelineview.h index 13be5613f..21b7a0acd 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -48,15 +48,9 @@ public: void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(TrackList* tl); - QScrollBar* scrollBar; - -public slots: - protected: void paintEvent(QPaintEvent*); - void resizeEvent(QResizeEvent *event); - void mouseDoubleClickEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); @@ -111,6 +105,8 @@ private: int scroll; signals: + void setScrollMaximum(int); + void requestScrollChange(int); public slots: void setScroll(int); From c922bbfa9d52a9db77d7dfaab2f462b86f661178 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 19:44:30 +1100 Subject: [PATCH 086/133] fixed rectangle selection --- panels/timeline.cpp | 61 +++++++++++++++++++++++++++++ panels/timeline.h | 3 ++ ui/timelinearea.cpp | 10 +++++ ui/timelinearea.h | 3 ++ ui/timelineview.cpp | 94 ++++++++++++++------------------------------- ui/timelineview.h | 14 ++++--- 6 files changed, 113 insertions(+), 72 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 26bce0857..bb09bb5b0 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -761,6 +761,65 @@ long Timeline::getTimelineFrameFromScreenPoint(int x) { return getFrameFromScreenPoint(zoom, x + scroll); } +QVector Timeline::GetClipsInRectangleSelection(bool autoselect_links) +{ + QVector selected_clips; + + TimelineArea* area; + + long frame_min = qMin(drag_frame_start, cursor_frame); + long frame_max = qMax(drag_frame_start, cursor_frame); + + foreach (area, areas) { + QPoint relative_tl = area->mapFromGlobal(rect_select_rect.topLeft()); + QPoint relative_br = area->mapFromGlobal(rect_select_rect.bottomRight()); + + int rect_top = qMin(relative_tl.y(), relative_br.y()); + int rect_bottom = qMax(relative_tl.y(), relative_br.y()); + + // determine which clips are in this rectangular selection + TrackList* track_list = area->track_list(); + for (int j=0;jTrackCount();j++) { + Track* track = track_list->TrackAt(j); + + int track_top = area->view()->getScreenPointFromTrack(track); + int track_bottom = track_top + track->height(); + + // See if this track touches this rectangle at all + if (!(track_bottom < rect_top + || track_top > rect_bottom)) { + + // Loop through track's clips for clips touching this rectangle + for (int i=0;iClipCount();i++) { + Clip* clip = track->GetClip(i).get(); + if (!(clip->timeline_out() < frame_min || clip->timeline_in() > frame_max) ) { + + // create a group of the clip (and its links if alt is not pressed) + QVector session_clips; + session_clips.append(clip); + + if (autoselect_links) { + session_clips.append(clip->linked); + } + + // for each of these clips, see if clip has already been added - + // this can easily happen due to adding linked clips + for (int j=0;jsetOrientation(Qt::Vertical); video_area = new TimelineArea(this); + areas.append(video_area); splitter->addWidget(video_area); audio_area = new TimelineArea(this); + areas.append(audio_area); splitter->addWidget(audio_area); editAreaLayout->addWidget(splitter); diff --git a/panels/timeline.h b/panels/timeline.h index ff8c1c836..db6c45930 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -63,6 +63,8 @@ public: int getDisplayScreenPointFromFrame(long frame); long getDisplayFrameFromScreenPoint(int x); + QVector GetClipsInRectangleSelection(bool autoselect_links); + void set_marker(); // shared information @@ -206,6 +208,7 @@ private: QWidget* timeline_area; TimelineArea* video_area; TimelineArea* audio_area; + QVector areas; QWidget* editAreas; QPushButton* zoomInButton; QPushButton* zoomOutButton; diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index d2a3000ed..86d12a205 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -80,6 +80,16 @@ void TimelineArea::SetAlignment(olive::timeline::Alignment alignment) RefreshLabels(); } +TrackList *TimelineArea::track_list() +{ + return track_list_; +} + +TimelineView *TimelineArea::view() +{ + return view_; +} + void TimelineArea::wheelEvent(QWheelEvent *event) { diff --git a/ui/timelinearea.h b/ui/timelinearea.h index d3778a3a0..50e499e71 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -17,6 +17,9 @@ public: void SetTrackList(Sequence* sequence, Track::Type track_list); void SetAlignment(olive::timeline::Alignment alignment); + TrackList* track_list(); + TimelineView* view(); + virtual void wheelEvent(QWheelEvent *event) override; public slots: void RefreshLabels(); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 09e937ffd..b545c4e9a 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2402,54 +2402,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // (left/top were set to the starting drag position earlier) ParentTimeline()->rect_select_rect.setBottomRight(mapToGlobal(event->pos())); - long frame_min = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); - long frame_max = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); - - QPoint relative_tl = mapFromGlobal(ParentTimeline()->rect_select_rect.topLeft()); - QPoint relative_br = mapFromGlobal(ParentTimeline()->rect_select_rect.bottomRight()); - - int rect_top = qMin(relative_tl.y(), relative_br.y()); - int rect_bottom = qMax(relative_tl.y(), relative_br.y()); - - // determine which clips are in this rectangular selection - QVector selected_clips; - for (int j=0;jTrackCount();j++) { - Track* track = track_list_->TrackAt(j); - - int track_top = getScreenPointFromTrack(track); - int track_bottom = track_top + track->height(); - - // See if this track touches this rectangle at all - if (!(track_bottom < rect_top - || track_top > rect_bottom)) { - - // Loop through track's clips for clips touching this rectangle - for (int i=0;iClipCount();i++) { - Clip* clip = track->GetClip(i).get(); - if (!(clip->timeline_out() < frame_min || clip->timeline_in() > frame_max) ) { - - // create a group of the clip (and its links if alt is not pressed) - QVector session_clips; - session_clips.append(clip); - - if (!alt) { - session_clips.append(clip->linked); - } - - // for each of these clips, see if clip has already been added - - // this can easily happen due to adding linked clips - for (int j=0;j selected_clips = ParentTimeline()->GetClipsInRectangleSelection(!alt); // add each of the selected clips to the main sequence's selections for (int i=0;itransition_select = kTransitionNone; @@ -2548,7 +2501,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c->timeline_in() > mouse_frame_lower && c->timeline_in() < mouse_frame_upper) { // test how close this IN point is to the cursor - int nc = qAbs(c->timeline_in() + 1 - ParentTimeline()->cursor_frame); + long nc = qAbs(c->timeline_in() + 1 - ParentTimeline()->cursor_frame); // and test whether it's closer than the last in/out point we found if (nc < closeness) { @@ -2566,7 +2519,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c->timeline_out() > mouse_frame_lower && c->timeline_out() < mouse_frame_upper) { // test how close this OUT point is to the cursor - int nc = qAbs(c->timeline_out() - 1 - ParentTimeline()->cursor_frame); + long nc = qAbs(c->timeline_out() - 1 - ParentTimeline()->cursor_frame); // and test whether it's closer than the last in/out point we found if (nc < closeness) { @@ -2594,7 +2547,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { // similar to above, test how close it is and if it's closer, make this active - int nc = qAbs(transition_point - 1 - ParentTimeline()->cursor_frame); + long nc = qAbs(transition_point - 1 - ParentTimeline()->cursor_frame); if (nc < closeness) { ParentTimeline()->trim_target = c; ParentTimeline()->trim_type = olive::timeline::TRIM_OUT; @@ -2615,7 +2568,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { // similar to above, test how close it is and if it's closer, make this active - int nc = qAbs(transition_point + 1 - ParentTimeline()->cursor_frame); + long nc = qAbs(transition_point + 1 - ParentTimeline()->cursor_frame); if (nc < closeness) { ParentTimeline()->trim_target = c; ParentTimeline()->trim_type = olive::timeline::TRIM_IN; @@ -2646,18 +2599,21 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // check to see if we're resizing a track height int mouse_pos = event->pos().y(); - Track* hover_track = getTrackFromScreenPoint(mouse_pos); - if (hover_track != nullptr) { - int test_range = 10; // FIXME magic number + // cursor range for resizing a track + int test_range = 10; // FIXME magic number - int track_y_edge = getScreenPointFromTrack(hover_track); + for (int i=0;iTrackCount();i++) { + Track* track = track_list_->TrackAt(i); - if (mouse_pos > track_y_edge - test_range - && mouse_pos < track_y_edge + test_range) { + int resize_point = getScreenPointFromTrackIndex(i + 1); + + if (mouse_pos > resize_point - test_range + && mouse_pos < resize_point + test_range) { track_resizing = true; - track_target = hover_track; + track_target = track; setCursor(Qt::SizeVerCursor); + break; } } @@ -3240,10 +3196,11 @@ void TimelineView::paintEvent(QPaintEvent*) { const Ghost& g = ParentTimeline()->ghosts.at(i); first_ghost = qMin(first_ghost, g.in); if (g.track->type() == track_list_->type()) { + int ghost_x = ParentTimeline()->getTimelineScreenPointFromFrame(g.in); int ghost_y = getScreenPointFromTrackIndex(g.track->Index() + g.track_movement); int ghost_width = ParentTimeline()->getTimelineScreenPointFromFrame(g.out) - ghost_x - 1; - int ghost_height = g.track->height() - 1; + int ghost_height = getTrackHeightFromTrackIndex(g.track->Index() + g.track_movement) - 1; insert_points.append(ghost_y + (ghost_height>>1)); @@ -3373,11 +3330,7 @@ int TimelineView::getScreenPointFromTrackIndex(int track) int point = 0; for (int i=0;iTrackCount()) { - point += track_list_->TrackAt(i)->height() + 1; - } else { - point += olive::timeline::kTrackDefaultHeight + 1; - } + point += getTrackHeightFromTrackIndex(i) + 1; } int screen_point = point - scroll; @@ -3390,6 +3343,15 @@ int TimelineView::getScreenPointFromTrackIndex(int track) return screen_point; } +int TimelineView::getTrackHeightFromTrackIndex(int track) +{ + if (track < track_list_->TrackCount()) { + return track_list_->TrackAt(track)->height(); + } else { + return olive::timeline::kTrackDefaultHeight; + } +} + Timeline *TimelineView::ParentTimeline() { return timeline_; diff --git a/ui/timelineview.h b/ui/timelineview.h index 21b7a0acd..364ea42ee 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -48,6 +48,14 @@ public: void SetAlignment(olive::timeline::Alignment alignment); void SetTrackList(TrackList* tl); + Track* getTrackFromScreenPoint(int y); + int getScreenPointFromTrack(Track* track); + + int getTrackIndexFromScreenPoint(int y); + int getScreenPointFromTrackIndex(int track); + + int getTrackHeightFromTrackIndex(int track); + protected: void paintEvent(QPaintEvent*); @@ -67,12 +75,6 @@ private: void init_ghosts(); void update_ghosts(const QPoint& mouse_pos, bool lock_frame); - Track* getTrackFromScreenPoint(int y); - int getScreenPointFromTrack(Track* track); - - int getTrackIndexFromScreenPoint(int y); - int getScreenPointFromTrackIndex(int track); - Timeline* ParentTimeline(); Sequence* sequence(); void delete_area_under_ghosts(ComboAction* ca, Sequence *s); From dd9a9ba4fd10fe95168bf6c3f63128dcfb101c12 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 19:59:49 +1100 Subject: [PATCH 087/133] fixed edit tool --- panels/timeline.cpp | 38 ++++++------------------------- panels/timeline.h | 2 +- ui/timelineview.cpp | 54 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index bb09bb5b0..70efae94f 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -761,18 +761,15 @@ long Timeline::getTimelineFrameFromScreenPoint(int x) { return getFrameFromScreenPoint(zoom, x + scroll); } -QVector Timeline::GetClipsInRectangleSelection(bool autoselect_links) +QVector Timeline::GetTracksInRectangle(int global_top, int global_bottom) { - QVector selected_clips; + QVector tracks; TimelineArea* area; - long frame_min = qMin(drag_frame_start, cursor_frame); - long frame_max = qMax(drag_frame_start, cursor_frame); - foreach (area, areas) { - QPoint relative_tl = area->mapFromGlobal(rect_select_rect.topLeft()); - QPoint relative_br = area->mapFromGlobal(rect_select_rect.bottomRight()); + QPoint relative_tl = area->mapFromGlobal(QPoint(0, global_top)); + QPoint relative_br = area->mapFromGlobal(QPoint(0, global_bottom)); int rect_top = qMin(relative_tl.y(), relative_br.y()); int rect_bottom = qMax(relative_tl.y(), relative_br.y()); @@ -789,35 +786,14 @@ QVector Timeline::GetClipsInRectangleSelection(bool autoselect_links) if (!(track_bottom < rect_top || track_top > rect_bottom)) { - // Loop through track's clips for clips touching this rectangle - for (int i=0;iClipCount();i++) { - Clip* clip = track->GetClip(i).get(); - if (!(clip->timeline_out() < frame_min || clip->timeline_in() > frame_max) ) { + // It does, so we add it to the list + tracks.append(track); - // create a group of the clip (and its links if alt is not pressed) - QVector session_clips; - session_clips.append(clip); - - if (autoselect_links) { - session_clips.append(clip->linked); - } - - // for each of these clips, see if clip has already been added - - // this can easily happen due to adding linked clips - for (int j=0;j GetClipsInRectangleSelection(bool autoselect_links); + QVector GetTracksInRectangle(int global_top, int global_bottom); void set_marker(); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index b545c4e9a..956a57b67 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2016,20 +2016,15 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { long selection_in = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); long selection_out = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); - int start_track = (ParentTimeline()->drag_track_start == nullptr) - ? track_list_->TrackCount() - 1 - : ParentTimeline()->drag_track_start->Index(); + int selection_top = mapToGlobal(QPoint(0, ParentTimeline()->drag_y_start)).y(); + int selection_bottom = mapToGlobal(event->pos()).y(); - int end_track = (ParentTimeline()->cursor_track == nullptr) - ? track_list_->TrackCount() - 1 - : ParentTimeline()->cursor_track->Index(); + QVector selected_tracks = ParentTimeline()->GetTracksInRectangle(selection_top, + selection_bottom); - int min_track = qMin(start_track, end_track); - int max_track = qMax(start_track, end_track); + Track* track; - for (int i=min_track;i<=max_track;i++) { - - Track* track = track_list_->TrackAt(i); + foreach (track, selected_tracks) { selections.append(Selection(selection_in, selection_out, track)); @@ -2402,7 +2397,42 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // (left/top were set to the starting drag position earlier) ParentTimeline()->rect_select_rect.setBottomRight(mapToGlobal(event->pos())); - QVector selected_clips = ParentTimeline()->GetClipsInRectangleSelection(!alt); + QVector selected_clips; + + QVector selected_tracks = ParentTimeline()->GetTracksInRectangle(ParentTimeline()->rect_select_rect.top(), + ParentTimeline()->rect_select_rect.bottom()); + + long frame_min = qMin(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + long frame_max = qMax(ParentTimeline()->drag_frame_start, ParentTimeline()->cursor_frame); + + Track* track; + + foreach (track, selected_tracks) { + // Loop through track's clips for clips touching this rectangle + for (int i=0;iClipCount();i++) { + Clip* clip = track->GetClip(i).get(); + if (!(clip->timeline_out() < frame_min || clip->timeline_in() > frame_max) ) { + + // create a group of the clip (and its links if alt is not pressed) + QVector session_clips; + session_clips.append(clip); + + if (!alt) { + session_clips.append(clip->linked); + } + + // for each of these clips, see if clip has already been added - + // this can easily happen due to adding linked clips + for (int j=0;j Date: Sat, 6 Apr 2019 20:56:08 +1100 Subject: [PATCH 088/133] timeline alignment works now --- panels/timeline.cpp | 2 +- ui/timelinearea.cpp | 1 + ui/timelinelabel.cpp | 18 +++++++++++-- ui/timelinelabel.h | 4 +++ ui/timelineview.cpp | 60 ++++++++++++++++++++++++++------------------ ui/timelineview.h | 2 ++ 6 files changed, 59 insertions(+), 28 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 70efae94f..c0b3f7a0a 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -90,7 +90,7 @@ Timeline::Timeline(QWidget *parent) : headers->viewer = panel_sequence_viewer; -// video_area->SetAlignment(olive::timeline::kAlignmentBottom); + video_area->SetAlignment(olive::timeline::kAlignmentBottom); tool_buttons.append(toolArrowButton); tool_buttons.append(toolEditButton); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 86d12a205..5fe9d29ff 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -166,6 +166,7 @@ void TimelineArea::RefreshLabels() for (int i=0;i(); labels_[i]->SetTrack(track_list_->TrackAt(i)); + labels_[i]->SetAlignment(alignment_); switch (alignment_) { case olive::timeline::kAlignmentTop: diff --git a/ui/timelinelabel.cpp b/ui/timelinelabel.cpp index 7f7cf583e..37d7fd0b8 100644 --- a/ui/timelinelabel.cpp +++ b/ui/timelinelabel.cpp @@ -5,7 +5,8 @@ #include TimelineLabel::TimelineLabel() : - track_(nullptr) + track_(nullptr), + alignment_(olive::timeline::kAlignmentTop) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); @@ -68,12 +69,25 @@ void TimelineLabel::UpdateState() lock_button_->setChecked(track_->IsLocked()); } +void TimelineLabel::SetAlignment(olive::timeline::Alignment alignment) +{ + alignment_ = alignment; + update(); +} + void TimelineLabel::paintEvent(QPaintEvent *) { + if (alignment_ != olive::timeline::kAlignmentTop && alignment_ != olive::timeline::kAlignmentBottom) { + return; + } + QPainter p(this); p.setPen(QColor(0, 0, 0, 96)); - p.drawLine(0, height() - 1, width(), height() - 1); + + int line_y = (alignment_ == olive::timeline::kAlignmentTop) ? height() - 1 : 0; + + p.drawLine(0, line_y, width(), line_y); } void TimelineLabel::RenameTrack() diff --git a/ui/timelinelabel.h b/ui/timelinelabel.h index 56368e62c..348c59ceb 100644 --- a/ui/timelinelabel.h +++ b/ui/timelinelabel.h @@ -6,6 +6,7 @@ #include "ui/clickablelabel.h" #include "timeline/track.h" +#include "timeline/timelinefunctions.h" class TimelineLabel : public QWidget { @@ -15,6 +16,7 @@ public: void SetTrack(Track* track); void UpdateState(); + void SetAlignment(olive::timeline::Alignment alignment); protected: virtual void paintEvent(QPaintEvent *event) override; private: @@ -25,6 +27,8 @@ private: ClickableLabel* label_; Track* track_; + + olive::timeline::Alignment alignment_; private slots: void RenameTrack(); void UpdateHeight(int h); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 956a57b67..47b1a1d87 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -929,6 +929,18 @@ void TimelineView::VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, C } } +int TimelineView::GetTotalAreaHeight() +{ + // start by adding a track height worth of padding + int panel_height = olive::timeline::kTrackDefaultHeight; + + for (int i=0;iTrackCount();i++) { + panel_height += track_list_->TrackAt(i)->height(); + } + + return panel_height; +} + void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QToolTip::hideText(); if (sequence() != nullptr) { @@ -2865,15 +2877,7 @@ void TimelineView::paintEvent(QPaintEvent*) { QPainter p(this); // get widget width and height - - // start by adding a track height worth of padding - int panel_height = olive::timeline::kTrackDefaultHeight; - - for (int i=0;iTrackCount();i++) { - panel_height += track_list_->TrackAt(i)->height(); - } - - emit setScrollMaximum(panel_height); + emit setScrollMaximum(GetTotalAreaHeight()); for (int i=0;iTrackCount();i++) { @@ -2890,7 +2894,14 @@ void TimelineView::paintEvent(QPaintEvent*) { QRect clip_rect(ParentTimeline()->getTimelineScreenPointFromFrame(clip->timeline_in()), track_top, getScreenPointFromFrame(ParentTimeline()->zoom, clip->length()), - track->height() - 1); + track->height()); + + if (alignment_ == olive::timeline::kAlignmentTop) { + clip_rect.setHeight(track->height() - 1); + } else if (alignment_ == olive::timeline::kAlignmentBottom) { + clip_rect.setTop(track_top + 1); + } + QRect text_rect(clip_rect.left() + olive::timeline::kClipTextPadding, clip_rect.top() + olive::timeline::kClipTextPadding, clip_rect.width() - olive::timeline::kClipTextPadding - 1, @@ -3206,7 +3217,11 @@ void TimelineView::paintEvent(QPaintEvent*) { // Draw track line p.setPen(QColor(0, 0, 0, 96)); - p.drawLine(0, track_bottom, rect().width(), track_bottom); + if (alignment_ == olive::timeline::kAlignmentTop) { + p.drawLine(0, track_bottom, rect().width(), track_bottom); + } else if (alignment_ == olive::timeline::kAlignmentBottom) { + p.drawLine(0, track_top, rect().width(), track_top); + } } } @@ -3315,18 +3330,16 @@ int TimelineView::getTrackIndexFromScreenPoint(int y) return 0; } + if (alignment_ == olive::timeline::kAlignmentBottom) { + y = -(y + 1 + scroll - qMax(height(), GetTotalAreaHeight())); + } else { + y += scroll; + } + if (y < 0) { return 0; } - y += scroll; - - /* - if (alignment_ == olive::timeline::kAlignmentBottom) { - y = height() - y; - } - */ - int heights = 0; int i = 0; @@ -3363,14 +3376,11 @@ int TimelineView::getScreenPointFromTrackIndex(int track) point += getTrackHeightFromTrackIndex(i) + 1; } - int screen_point = point - scroll; - - /* if (alignment_ == olive::timeline::kAlignmentBottom) { - return height() - screen_point - track_list_->First()->height(); + return qMax(height(), GetTotalAreaHeight()) - point - scroll - track_list_->First()->height() - 1; } - */ - return screen_point; + + return point - scroll; } int TimelineView::getTrackHeightFromTrackIndex(int track) diff --git a/ui/timelineview.h b/ui/timelineview.h index 364ea42ee..0cd5ca22c 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -86,6 +86,8 @@ private: void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, long transition_start, long transition_end); void VerifyTransitionHelper(); + int GetTotalAreaHeight(); + Timeline* timeline_; olive::timeline::Alignment alignment_; From 4f246dba24056618f150a5c25ff35624a30ca06f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 21:13:27 +1100 Subject: [PATCH 089/133] fixed bug where ripples re-added deleted clips --- timeline/track.cpp | 11 +++++++++++ timeline/track.h | 1 + undo/undo.cpp | 16 ++++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/timeline/track.cpp b/timeline/track.cpp index 8d74b9999..37cbc7a91 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -195,6 +195,17 @@ Clip *Track::GetClipFromPoint(long point) return nullptr; } +bool Track::ContainsClip(Clip *c) +{ + ClipPtr clip; + foreach (clip, clips_) { + if (clip.get() == c) { + return true; + } + } + return false; +} + Track *Track::Previous() { int index = Index(); diff --git a/timeline/track.h b/timeline/track.h index 23ca28cf4..847f27415 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -67,6 +67,7 @@ public: QVector GetSelectedClips(bool containing); ClipPtr GetClipObjectFromRawPtr(Clip* c); Clip* GetClipFromPoint(long point); + bool ContainsClip(Clip* c); Track* Previous(); Track* Next(); diff --git a/undo/undo.cpp b/undo/undo.cpp index 1a7ba7e80..82738c21e 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -68,7 +68,13 @@ void MoveClipAction::doUndo() { clip->set_timeline_out(old_out); clip->set_clip_in(old_clip_in); } - old_track->AddClip(clip); + + // Move clip to the new track ONLY IF the old track currently contains this clip - a workaround to ensure this + // action doesn't accidentaly add a clip that it's not supposed to + if (new_track->ContainsClip(clip.get())) { + old_track->AddClip(clip); + } + done = false; } @@ -83,7 +89,13 @@ void MoveClipAction::doRedo() { clip->set_timeline_out(new_out); clip->set_clip_in(new_clip_in); } - new_track->AddClip(clip); + + // Move clip to the new track ONLY IF the old track currently contains this clip - a workaround to ensure this + // action doesn't accidentaly add a clip that it's not supposed to + if (old_track->ContainsClip(clip.get())) { + new_track->AddClip(clip); + } + done = true; } } From aa13816fefd659691797cbd34873a9d879af5972 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 6 Apr 2019 21:32:33 +1100 Subject: [PATCH 090/133] split selection works again --- timeline/sequence.cpp | 59 +++++++++++++++++++++++++++++++++---------- timeline/sequence.h | 2 +- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 5ac286673..bb72dc772 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -578,8 +578,9 @@ void Sequence::ToggleLinksOnSelected() void Sequence::Split() { ComboAction* ca = new ComboAction(); - bool split_selected = false; + bool split_occurred = false; + // See if there are any selected clips at the current playhead to split QVector selected_clips = SelectedClips(true); if (selected_clips.size() > 0) { // see if whole clips are selected @@ -594,31 +595,62 @@ void Sequence::Split() if (s != nullptr) { pre_clips.append(c); post_clips.append(s); - split_selected = true; + split_occurred = true; } } - if (split_selected) { + if (split_occurred) { // relink clips if we split olive::timeline::RelinkClips(pre_clips, post_clips); ca->append(new AddClipCommand(post_clips)); - } else { + } + } - // split a selection if not - // FIXME reimplement split selection - //split_selected = split_selection(ca); + // If we weren't able to split any selected clips above, see if there are arbitrary selections to split + if (!split_occurred) { + TrackList* track_list; + Track* track; + + foreach (track_list, track_lists_) { + + QVector tracks = track_list->tracks(); + foreach (track, tracks) { + + QVector track_selections = track->Selections(); + QVector split_positions; + + for (int j=0;jClipCount();i++) { + Clip* c = track->GetClip(i).get(); + + if (SplitClipAtPositions(ca, c, split_positions, false)) { + split_occurred = true; + } + } + } } } // if nothing was selected or no selections fell within playhead, simply split at playhead - if (!split_selected) { - split_selected = SplitAllClipsAtPoint(ca, playhead); + if (!split_occurred) { + split_occurred = SplitAllClipsAtPoint(ca, playhead); } - if (split_selected) { + if (split_occurred) { olive::undo_stack.push(ca); update_ui(true); } else { @@ -734,7 +766,7 @@ bool Sequence::SplitAllClipsAtPoint(ComboAction *ca, long point) return split; } -bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool relink) +bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector positions, bool also_split_links) { // Add the clip and each of its links to the pre_splits array @@ -743,7 +775,7 @@ bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector p QVector pre_splits; pre_splits.append(clip); - if (relink) { + if (also_split_links) { for (int i=0;ilinked.size();i++) { pre_splits.append(clip->linked.at(i)); } @@ -772,7 +804,8 @@ bool Sequence::SplitClipAtPositions(ComboAction *ca, Clip* clip, QVector p split_occurred = true; if (i + 1 < positions.size()) { - post_splits[i][j]->set_timeline_out(positions.at(i+1)); + post_splits[i][j]->set_timeline_out(qMin(post_splits[i][j]->timeline_out(), + positions.at(i+1))); } } } diff --git a/timeline/sequence.h b/timeline/sequence.h index 95c7ec65a..41c2cfbb7 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -92,7 +92,7 @@ public: void Split(); bool SplitAllClipsAtPoint(ComboAction *ca, long point); - bool SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool relink = true); + bool SplitClipAtPositions(ComboAction* ca, Clip *clip, QVector positions, bool also_split_links = true); void RippleDeleteEmptySpace(ComboAction *ca, Track *track, long point); void RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length); From ba824d197ca20836e24673cc68b19bfe8fa9fb3b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 7 Apr 2019 01:59:41 +1100 Subject: [PATCH 091/133] revamped razor tool --- ui/timelineview.cpp | 110 ++++++++++++++++++++++++-------------------- ui/timelineview.h | 2 + 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 47b1a1d87..164f65ec6 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -524,6 +524,48 @@ Clip *TimelineView::GetClipAtCursor() return ParentTimeline()->cursor_track->GetClipFromPoint(ParentTimeline()->cursor_frame); } +QVector TimelineView::GetSplitTracksFromMouseCoords(bool also_split_links, long frame, int top, int bottom) +{ + // Convert top and bottom coords to global coordinates used by GetTracksInRectangle() + int global_top = mapToGlobal(QPoint(0, top)).y(); + int global_bottom = mapToGlobal(QPoint(0, bottom)).y(); + + // Get the current tracks in this mouse range + QVector split_tracks = ParentTimeline()->GetTracksInRectangle(global_top, global_bottom); + + // If we're also splitting links, loop through each track and search for clips that will be split at this point + if (also_split_links) { + + // Cache array size because we'll be adding to it and don't want to cause an infinite loop + int split_track_size = split_tracks.size(); + for (int i=0;iClipCount();j++) { + Clip* c = track->GetClip(j).get(); + + // Check if this clip is going to be split at this frame + if (c->timeline_in() < frame && c->timeline_out() > frame) { + + // Loop through clip's links for more tracks to split + for (int k=0;klinked.size();k++) { + Track* link_track = c->linked.at(k)->track(); + if (!split_tracks.contains(link_track)) { + split_tracks.append(link_track); + } + } + + // Break because there will only be one clip active at this frame per track + break; + } + } + } + } + + return split_tracks; +} + void TimelineView::mousePressEvent(QMouseEvent *event) { if (sequence() != nullptr) { @@ -779,8 +821,10 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // initiate razor tool ParentTimeline()->splitting = true; - // add this track as a track being split by the razor - ParentTimeline()->split_tracks.append(ParentTimeline()->drag_track_start); + ParentTimeline()->split_tracks = GetSplitTracksFromMouseCoords(!alt, + ParentTimeline()->drag_frame_start, + event->pos().y(), + event->pos().y()); update_ui(false); } @@ -1461,31 +1505,24 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { push_undo = true; } } else if (ParentTimeline()->splitting) { - bool split = false; - for (int i=0;isplit_tracks.size();i++) { - Clip* split_index = ParentTimeline()->split_tracks.at(i)->GetClipFromPoint(ParentTimeline()->drag_frame_start); + + QVector split_tracks = GetSplitTracksFromMouseCoords(false, + ParentTimeline()->drag_frame_start, + ParentTimeline()->drag_y_start, + event->pos().y()); + + for (int i=0;iGetClipFromPoint(ParentTimeline()->drag_frame_start); if (split_index != nullptr && sequence()->SplitClipAtPositions(ca, split_index, {ParentTimeline()->drag_frame_start}, !alt)) { - split = true; + push_undo = true; } } - if (split) { - push_undo = true; - } } // remove duplicate selections sequence()->TidySelections(); - /* - if (selection_command != nullptr) { - selection_command->new_data = sequence()->selections; - ca->append(selection_command); - selection_command = nullptr; - push_undo = true; - } - */ - if (push_undo) { olive::undo_stack.push(ca); } else { @@ -2360,39 +2397,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } else if (ParentTimeline()->splitting) { - // get the range of tracks currently dragged - int track_start = qMin(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()); - int track_end = qMax(ParentTimeline()->cursor_track->Index(), ParentTimeline()->drag_track_start->Index()); - int track_size = 1 + track_end - track_start; - - // set tracks to be split - ParentTimeline()->split_tracks.resize(track_size); - for (int i=0;isplit_tracks[i] = ParentTimeline()->cursor_track->track_list()->TrackAt(track_start + i); - } - - // if alt isn't being held, also add the tracks of the clip's links - if (!alt) { - for (int i=0;isplit_tracks[i]->GetClipFromPoint(ParentTimeline()->drag_frame_start); - - if (clip != nullptr) { - for (int j=0;jlinked.size();j++) { - - Clip* link = clip->linked.at(j); - - // if this clip isn't already in the list of tracks to split - if (link->track()->Index() < track_start || link->track()->Index() > track_end) { - ParentTimeline()->split_tracks.append(link->track()); - } - - } - } - } - } - + ParentTimeline()->split_tracks = GetSplitTracksFromMouseCoords(!alt, + ParentTimeline()->drag_frame_start, + ParentTimeline()->drag_y_start, + event->pos().y()); update_ui(false); } else if (ParentTimeline()->rect_select_init) { diff --git a/ui/timelineview.h b/ui/timelineview.h index 0cd5ca22c..50be9c63a 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -83,6 +83,8 @@ private: void draw_transition(QPainter& p, Clip *c, const QRect& clip_rect, QRect& text_rect, int transition_type); Clip* GetClipAtCursor(); + QVector GetSplitTracksFromMouseCoords(bool also_split_links, long frame, int top, int bottom); + void VerifyTransitionsAfterCreating(ComboAction* ca, Clip* open, Clip* close, long transition_start, long transition_end); void VerifyTransitionHelper(); From 62cdd490804acf7ace3e4e6123d862480a272c2f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 7 Apr 2019 10:10:37 +1000 Subject: [PATCH 092/133] corrected resizing bottom aligned tracks --- ui/timelineview.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 164f65ec6..6b9685da3 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2208,6 +2208,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // get cursor movement int diff = (event->pos().y() - ParentTimeline()->drag_y_start); + if (alignment_ == olive::timeline::kAlignmentBottom) { + diff = -diff; + } + // add it to the current track height int new_height = track_target->height() + diff; @@ -2221,6 +2225,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { ParentTimeline()->drag_y_start = event->pos().y(); update(); + } else if (ParentTimeline()->moving_proc) { // we're currently dragging ghosts @@ -2656,7 +2661,15 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { for (int i=0;iTrackCount();i++) { Track* track = track_list_->TrackAt(i); - int resize_point = getScreenPointFromTrackIndex(i + 1); + int effective_track_index = i; + + // If alignment is top, use the next track's screen point as the resize anchor so we resize the bottom + // of the track + if (alignment_ == olive::timeline::kAlignmentTop) { + effective_track_index++; + } + + int resize_point = getScreenPointFromTrackIndex(effective_track_index); if (mouse_pos > resize_point - test_range && mouse_pos < resize_point + test_range) { From a2d70257760389bcc1c2fa1afbd85414b9a63d35 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 7 Apr 2019 11:31:37 +1000 Subject: [PATCH 093/133] optimized waveform generation --- olive.pro | 6 ++-- project/footage.h | 2 +- project/previewgenerator.cpp | 33 +++++++++-------- ui/timelineview.cpp | 61 ++----------------------------- ui/timelineview.h | 2 -- ui/viewerwidget.cpp | 3 +- ui/waveform.cpp | 70 ++++++++++++++++++++++++++++++++++++ ui/waveform.h | 25 +++++++++++++ 8 files changed, 120 insertions(+), 82 deletions(-) create mode 100644 ui/waveform.cpp create mode 100644 ui/waveform.h diff --git a/olive.pro b/olive.pro index a58ad5db1..a3db45409 100644 --- a/olive.pro +++ b/olive.pro @@ -183,7 +183,8 @@ SOURCES += \ timeline/selection.cpp \ global/clipboard.cpp \ timeline/timelinetools.cpp \ - timeline/ghost.cpp + timeline/ghost.cpp \ + ui/waveform.cpp HEADERS += \ ui/mainwindow.h \ @@ -323,7 +324,8 @@ HEADERS += \ ui/timelineview.h \ ui/timelinelabel.h \ global/clipboard.h \ - timeline/timelinetools.h + timeline/timelinetools.h \ + ui/waveform.h FORMS += diff --git a/project/footage.h b/project/footage.h index e77ee6af1..8b250b9e4 100644 --- a/project/footage.h +++ b/project/footage.h @@ -62,7 +62,7 @@ struct FootageStream { // preview thumbnail/waveform bool preview_done; QImage video_preview; - QVector audio_preview; + QVector audio_preview; }; class Footage { diff --git a/project/previewgenerator.cpp b/project/previewgenerator.cpp index 173bc1572..36895599b 100644 --- a/project/previewgenerator.cpp +++ b/project/previewgenerator.cpp @@ -168,10 +168,7 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) { f.open(QFile::ReadOnly); QByteArray data = f.readAll(); ms.audio_preview.resize(data.size()); - for (int j=0;jnb_streams]{0}; // stores samples while scanning before they get sent to preview file - qint16*** waveform_cache_data = new qint16** [fmt_ctx_->nb_streams]; + qint8*** waveform_cache_data = new qint8** [fmt_ctx_->nb_streams]; int waveform_cache_count = 0; // defaults to false, sets to true if we find a valid stream to make a preview of @@ -275,11 +272,11 @@ void PreviewGenerator::generate_waveform() { if (fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { // allocate sample cache for this stream - waveform_cache_data[i] = new qint16* [fmt_ctx_->streams[i]->codecpar->channels]; + waveform_cache_data[i] = new qint8* [fmt_ctx_->streams[i]->codecpar->channels]; // each channel gets a min and a max value so we allocate two ints for each one for (int j=0;jstreams[i]->codecpar->channels;j++) { - waveform_cache_data[i][j] = new qint16[2]; + waveform_cache_data[i][j] = new qint8[2]; } // if codec context has no defined channel layout, guess it from the channel count @@ -381,7 +378,7 @@ void PreviewGenerator::generate_waveform() { AVFrame* swr_frame = av_frame_alloc(); swr_frame->channel_layout = temp_frame->channel_layout; swr_frame->sample_rate = temp_frame->sample_rate; - swr_frame->format = AV_SAMPLE_FMT_S16P; + swr_frame->format = AV_SAMPLE_FMT_U8P; swr_ctx = swr_alloc_set_opts( nullptr, @@ -419,11 +416,11 @@ void PreviewGenerator::generate_waveform() { // if so, we dump our cached values into the preview and reset them // for the next interval for (int j=0;jchannels;j++) { - qint16& min = waveform_cache_data[packet->stream_index][j][0]; - qint16& max = waveform_cache_data[packet->stream_index][j][1]; + qint8& min = waveform_cache_data[packet->stream_index][j][0]; + qint8& max = waveform_cache_data[packet->stream_index][j][1]; - s->audio_preview.append(min >> 8); - s->audio_preview.append(max >> 8); + s->audio_preview.append(min); + s->audio_preview.append(max); } waveform_cache_count = 0; @@ -431,8 +428,8 @@ void PreviewGenerator::generate_waveform() { // standard processing for each channel of information for (int j=0;jchannels;j++) { - qint16& min = waveform_cache_data[packet->stream_index][j][0]; - qint16& max = waveform_cache_data[packet->stream_index][j][1]; + qint8& min = waveform_cache_data[packet->stream_index][j][0]; + qint8& max = waveform_cache_data[packet->stream_index][j][1]; // if we're starting over, reset cache to zero if (waveform_cache_count == 0) { @@ -440,8 +437,10 @@ void PreviewGenerator::generate_waveform() { max = 0; } - // store most minimum and most maximum samples of this interval - qint16 sample = qint16((swr_frame->data[j][i+1] << 8) | swr_frame->data[j][i]); + // Convert unsigned 8-bit PCM sample to signed + qint8 sample = qint8(int(swr_frame->data[j][i]-128)); + + // Store most minimum and most maximum samples of this interval min = qMin(min, sample); max = qMax(max, sample); } @@ -590,7 +589,7 @@ void PreviewGenerator::run() { FootageStream& ms = footage_->audio_tracks[i]; QFile f(get_waveform_path(hash, ms)); f.open(QFile::WriteOnly); - f.write(ms.audio_preview.constData(), ms.audio_preview.size()); + f.write(reinterpret_cast(ms.audio_preview.constData()), ms.audio_preview.size()); f.close(); //dout << "saved" << ms->file_index << "waveform to" << get_waveform_path(hash, ms); } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 6b9685da3..c18d30af5 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -61,6 +61,7 @@ #include "timeline/track.h" #include "global/math.h" #include "project/projectfunctions.h" +#include "ui/waveform.h" #define MAX_TEXT_WIDTH 20 #define TRANSITION_BETWEEN_RANGE 40 @@ -2784,64 +2785,6 @@ void TimelineView::leaveEvent(QEvent*) { tooltip_timer.stop(); } -void draw_waveform(Clip* clip, const FootageStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) { - // audio channels multiplied by the number of bytes in a 16-bit audio sample - int divider = ms->audio_channels*2; - - int channel_height = clip_rect.height()/ms->audio_channels; - - int last_waveform_index = -1; - - for (int i=waveform_start;iclip_in() + (double(i)/zoom))/media_length) * ms->audio_preview.size())/divider)*divider; - - if (clip->reversed()) { - waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2); - } - - if (last_waveform_index < 0) last_waveform_index = waveform_index; - - for (int j=0;jaudio_channels;j++) { - int mid = (olive::config.rectified_waveforms) ? clip_rect.top()+channel_height*(j+1) : clip_rect.top()+channel_height*j+(channel_height/2); - - int offset_range_start = last_waveform_index+(j*2); - int offset_range_end = waveform_index+(j*2); - int offset_range_min = qMin(offset_range_start, offset_range_end); - int offset_range_max = qMax(offset_range_start, offset_range_end); - - // Break if we're about to draw from an index that doesn't exist - if (offset_range_min+1 >= ms->audio_preview.size()) { - break; - } - - qint8 min = qint8(qRound(double(ms->audio_preview.at(offset_range_min)) / 128.0 * (channel_height/2))); - qint8 max = qint8(qRound(double(ms->audio_preview.at(offset_range_min+1)) / 128.0 * (channel_height/2))); - - if ((offset_range_max + 1) < ms->audio_preview.size()) { - - // for waveform drawings, we get the maximum below 0 and maximum above 0 for this waveform range - for (int k=offset_range_min+2;k<=offset_range_max;k+=2) { - min = qMin(min, qint8(qRound(double(ms->audio_preview.at(k)) / 128.0 * (channel_height/2)))); - max = qMax(max, qint8(qRound(double(ms->audio_preview.at(k+1)) / 128.0 * (channel_height/2)))); - } - - // draw waveforms - if (olive::config.rectified_waveforms) { - - // rectified waveforms start from the bottom and draw upwards - p->drawLine(clip_rect.left()+i, mid, clip_rect.left()+i, mid - (max - min)); - } else { - - // non-rectified waveforms start from the center and draw outwards - p->drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max); - - } - } - } - last_waveform_index = waveform_index; - } -} - void TimelineView::draw_transition(QPainter& p, Clip* c, const QRect& clip_rect, QRect& text_rect, int transition_type) { TransitionPtr t = (transition_type == kTransitionOpening) ? c->opening_transition : c->closing_transition; if (t != nullptr) { @@ -3033,7 +2976,7 @@ void TimelineView::paintEvent(QPaintEvent*) { if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit); } - draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, ParentTimeline()->zoom); + olive::ui::DrawWaveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, ParentTimeline()->zoom); } } if (draw_checkerboard) { diff --git a/ui/timelineview.h b/ui/timelineview.h index 50be9c63a..6a9ab16c8 100644 --- a/ui/timelineview.h +++ b/ui/timelineview.h @@ -38,8 +38,6 @@ class Timeline; -void draw_waveform(Clip* clip, const FootageStream *ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom); - class TimelineView : public QWidget { Q_OBJECT public: diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index b773de2ec..186fcc02a 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -61,6 +61,7 @@ extern "C" { #include "rendering/shadergenerators.h" #include "ui/viewerwindow.h" #include "ui/menu.h" +#include "ui/waveform.h" #include "mainwindow.h" const int kTitleActionSafeVertexSize = 84; @@ -436,7 +437,7 @@ void ViewerWidget::draw_waveform_func() { wr.setX(wr.x() - waveform_scroll); p.setPen(Qt::green); - draw_waveform(waveform_clip.get(), waveform_ms, waveform_clip->timeline_out(), &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom); + olive::ui::DrawWaveform(waveform_clip.get(), waveform_ms, waveform_clip->timeline_out(), &p, wr, waveform_scroll, width()+waveform_scroll, waveform_zoom); p.setPen(Qt::red); int playhead_x = getScreenPointFromFrame(waveform_zoom, viewer->seq->playhead) - waveform_scroll; p.drawLine(playhead_x, 0, playhead_x, height()); diff --git a/ui/waveform.cpp b/ui/waveform.cpp new file mode 100644 index 000000000..40933453b --- /dev/null +++ b/ui/waveform.cpp @@ -0,0 +1,70 @@ +#include "waveform.h" + +#include + +#include "global/config.h" + +int ConvertSampleToHeight(qint8 signed_sample, int channel_height) { + int half_channel_height = channel_height >> 1; + return (half_channel_height) + qRound(((double(signed_sample) / 128.0)) * half_channel_height); +} + +void olive::ui::DrawWaveform(Clip* clip, + const FootageStream* ms, + long media_length, + QPainter *p, + const QRect& clip_rect, + int waveform_start, + int waveform_limit, + double zoom) { + + int divider = ms->audio_channels; + + int channel_height = clip_rect.height()/ms->audio_channels; + + int last_waveform_index = -1; + + for (int i=waveform_start;iclip_in() + (double(i)/zoom))/media_length) * ms->audio_preview.size())/divider)*divider; + + if (clip->reversed()) { + waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2); + } + + if (last_waveform_index < 0) last_waveform_index = waveform_index; + + for (int j=0;jaudio_channels;j++) { + int bottom = clip_rect.top()+channel_height*(j+1); + + int offset_range_start = last_waveform_index+(j*2); + int offset_range_end = waveform_index+(j*2); + int offset_range_min = qMin(offset_range_start, offset_range_end); + int offset_range_max = qMax(offset_range_start, offset_range_end); + + // Break if we're about to draw from an index that doesn't exist + if (offset_range_min+1 >= ms->audio_preview.size()) { + break; + } + + int min = ConvertSampleToHeight(ms->audio_preview.at(offset_range_min), channel_height); + int max = ConvertSampleToHeight(ms->audio_preview.at(offset_range_min+1), channel_height); + + if ((offset_range_max + 1) < ms->audio_preview.size()) { + + // for waveform drawings, we get the maximum below 0 and maximum above 0 for this waveform range + for (int k=offset_range_min+2;k<=offset_range_max;k+=2) { + min = ConvertSampleToHeight(ms->audio_preview.at(k), channel_height); + max = ConvertSampleToHeight(ms->audio_preview.at(k+1), channel_height); + } + + // draw waveforms + if (olive::config.rectified_waveforms) { + p->drawLine(clip_rect.left()+i, bottom, clip_rect.left()+i, bottom - (max - min)); + } else { + p->drawLine(clip_rect.left()+i, bottom - min, clip_rect.left()+i, bottom - max); + } + } + } + last_waveform_index = waveform_index; + } +} diff --git a/ui/waveform.h b/ui/waveform.h new file mode 100644 index 000000000..d7dc92040 --- /dev/null +++ b/ui/waveform.h @@ -0,0 +1,25 @@ +#ifndef WAVEFORM_H +#define WAVEFORM_H + +#include +#include + +#include "timeline/clip.h" +#include "project/footage.h" + +namespace olive { +namespace ui { + +void DrawWaveform(Clip* clip, + const FootageStream *ms, + long media_length, + QPainter* p, + const QRect& clip_rect, + int waveform_start, + int waveform_limit, + double zoom); + +} +} + +#endif // WAVEFORM_H From 644d096f3cda41d050928879f370947740f751c5 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 7 Apr 2019 13:37:28 +1000 Subject: [PATCH 094/133] converted internal system to float --- effects/effect.cpp | 12 +- effects/effect.h | 16 ++- effects/internal/audionoiseeffect.cpp | 39 +++--- effects/internal/audionoiseeffect.h | 7 +- effects/internal/cornerpineffect.h | 2 +- effects/internal/crossdissolvetransition.h | 4 +- effects/internal/cubetransition.h | 4 +- .../internal/exponentialfadetransition.cpp | 43 +++--- effects/internal/exponentialfadetransition.h | 9 +- effects/internal/fillleftrighteffect.cpp | 11 +- effects/internal/fillleftrighteffect.h | 7 +- effects/internal/linearfadetransition.cpp | 34 +++-- effects/internal/linearfadetransition.h | 9 +- .../internal/logarithmicfadetransition.cpp | 33 +++-- effects/internal/logarithmicfadetransition.h | 9 +- effects/internal/paneffect.cpp | 30 +++-- effects/internal/paneffect.h | 7 +- effects/internal/richtexteffect.h | 2 +- effects/internal/shakeeffect.h | 4 +- effects/internal/solideffect.h | 2 +- effects/internal/texteffect.h | 2 +- effects/internal/timecodeeffect.h | 2 +- effects/internal/toneeffect.cpp | 44 ++++--- effects/internal/toneeffect.h | 7 +- effects/internal/transformeffect.h | 6 +- effects/internal/volumeeffect.cpp | 34 ++--- effects/internal/volumeeffect.h | 7 +- effects/internal/vsthost.cpp | 122 +++++++++++------- effects/internal/vsthost.h | 32 ++++- project/loadthread.cpp | 2 +- rendering/cacher.cpp | 80 +++++++----- 31 files changed, 365 insertions(+), 257 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 727bcff50..a7476f1dc 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -678,7 +678,7 @@ void Effect::load_from_string(const QByteArray &s) { for (int i=0;i -1) { @@ -1157,9 +1157,3 @@ const EffectMeta* get_meta_from_name(const QString& input) { } return nullptr; } - -qint16 mix_audio_sample(qint16 a, qint16 b) { - qint32 mixed_sample = static_cast(a) + static_cast(b); - mixed_sample = qMax(qMin(mixed_sample, static_cast(INT16_MAX)), static_cast(INT16_MIN)); - return static_cast(mixed_sample); -} diff --git a/effects/effect.h b/effects/effect.h index 47dec5629..95ec3500a 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -126,10 +126,6 @@ struct GLTextureCoords { float opacity; }; -const EffectMeta* get_meta_from_name(const QString& input); - -qint16 mix_audio_sample(qint16 a, qint16 b); - class Effect : public QObject { Q_OBJECT public: @@ -189,7 +185,7 @@ public: virtual void process_shader(double timecode, GLTextureCoords&, int iteration); virtual void process_coords(double timecode, GLTextureCoords& coords, int data); virtual GLuint process_superimpose(QOpenGLContext *ctx, double timecode); - virtual void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, double timecode_end, float **samples, int nb_samples, int nb_channels, int type); virtual void gizmo_draw(double timecode, GLTextureCoords& coords); void gizmo_move(EffectGizmo* sender, int x_movement, int y_movement, double timecode, bool done); @@ -205,8 +201,18 @@ public: return distribution(generator); } + template + T randomFloat() + { + static std::random_device device; + static std::mt19937 generator(device()); + static std::uniform_int_distribution<> distribution(-1.0, 1.0); + return distribution(generator); + } + static EffectPtr Create(Clip *c, const EffectMeta *em); static const EffectMeta* GetInternalMeta(int internal_id, int type); + static const EffectMeta* GetMetaFromName(const QString& input); public slots: void FieldChanged(); void SetEnabled(bool b); diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index 0e59d04b0..91194f1c0 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -35,30 +35,29 @@ AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em mix_val->SetValueAt(0, true); } -void AudioNoiseEffect::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;irandomNumber(); - qint16 right_noise_sample = this->randomNumber(); - // set noise volume - double vol = log_volume( amount_val->GetDoubleAt(timecode)*0.01 ); - left_noise_sample *= vol; - right_noise_sample *= vol; + float vol = log_volume( amount_val->GetDoubleAt(timecode)*0.01 ); - // mix with source audio - if (mix_val->GetBoolAt(timecode)) { - qint16 left_sample = static_cast (((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); - qint16 right_sample = static_cast (((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF)); - left_noise_sample = mix_audio_sample(left_noise_sample, left_sample); - right_noise_sample = mix_audio_sample(right_noise_sample, right_sample); + for (int j=0;jrandomFloat() * vol; + + // mix with source audio + if (mix_val->GetBoolAt(timecode)) { + samples[j][i] += noise_sample; + } else { + samples[j][i] = noise_sample; + } } - - samples[i+3] = static_cast (right_noise_sample >> 8); - samples[i+2] = static_cast (right_noise_sample); - samples[i+1] = static_cast (left_noise_sample >> 8); - samples[i] = static_cast (left_noise_sample); } } diff --git a/effects/internal/audionoiseeffect.h b/effects/internal/audionoiseeffect.h index 74c8ad9cd..819a02999 100644 --- a/effects/internal/audionoiseeffect.h +++ b/effects/internal/audionoiseeffect.h @@ -27,7 +27,12 @@ class AudioNoiseEffect : public Effect { Q_OBJECT public: AudioNoiseEffect(Clip* c, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; DoubleField* amount_val; BoolField* mix_val; diff --git a/effects/internal/cornerpineffect.h b/effects/internal/cornerpineffect.h index 3a2acc525..50ea31a95 100644 --- a/effects/internal/cornerpineffect.h +++ b/effects/internal/cornerpineffect.h @@ -26,7 +26,7 @@ class CornerPinEffect : public Effect { Q_OBJECT public: - CornerPinEffect(Clip* c, const EffectMeta* em); + CornerPinEffect(Clip* c, const EffectMeta* em); void process_coords(double timecode, GLTextureCoords& coords, int data); void process_shader(double timecode, GLTextureCoords& coords, int iterations); void gizmo_draw(double timecode, GLTextureCoords& coords); diff --git a/effects/internal/crossdissolvetransition.h b/effects/internal/crossdissolvetransition.h index 4d1e8f2c4..42afc7b93 100644 --- a/effects/internal/crossdissolvetransition.h +++ b/effects/internal/crossdissolvetransition.h @@ -25,8 +25,8 @@ class CrossDissolveTransition : public Transition { public: - CrossDissolveTransition(Clip *c, Clip *s, const EffectMeta* em); - void process_coords(double timecode, GLTextureCoords &, int data); + CrossDissolveTransition(Clip *c, Clip *s, const EffectMeta* em); + void process_coords(double timecode, GLTextureCoords &, int data); }; #endif // CROSSDISSOLVETRANSITION_H diff --git a/effects/internal/cubetransition.h b/effects/internal/cubetransition.h index f79280dec..a2c4da552 100644 --- a/effects/internal/cubetransition.h +++ b/effects/internal/cubetransition.h @@ -25,8 +25,8 @@ class CubeTransition : public Transition { public: - CubeTransition(Clip* c, Clip* s, const EffectMeta* em); - void process_coords(double timecode, GLTextureCoords &, int data); + CubeTransition(Clip* c, Clip* s, const EffectMeta* em); + void process_coords(double timecode, GLTextureCoords &, int data); }; #endif // CUBETRANSITION_H diff --git a/effects/internal/exponentialfadetransition.cpp b/effects/internal/exponentialfadetransition.cpp index 470964f86..292384b4d 100644 --- a/effects/internal/exponentialfadetransition.cpp +++ b/effects/internal/exponentialfadetransition.cpp @@ -24,32 +24,27 @@ ExponentialFadeTransition::ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} -void ExponentialFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; +void ExponentialFadeTransition::process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) { + double interval = (timecode_end-timecode_start)/nb_samples; - for (int i=0;i> 8); - samples[i] = (quint8) samp; } } diff --git a/effects/internal/exponentialfadetransition.h b/effects/internal/exponentialfadetransition.h index d2fadd73a..a9201f5dc 100644 --- a/effects/internal/exponentialfadetransition.h +++ b/effects/internal/exponentialfadetransition.h @@ -25,8 +25,13 @@ class ExponentialFadeTransition : public Transition { public: - ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; }; #endif // LINEARFADETRANSITION_H diff --git a/effects/internal/fillleftrighteffect.cpp b/effects/internal/fillleftrighteffect.cpp index 40e934c57..03fb4933c 100644 --- a/effects/internal/fillleftrighteffect.cpp +++ b/effects/internal/fillleftrighteffect.cpp @@ -30,9 +30,14 @@ FillLeftRightEffect::FillLeftRightEffect(Clip* c, const EffectMeta *em) : Effect fill_type->AddItem(tr("Fill Right with Left"), FILL_TYPE_RIGHT); } -void FillLeftRightEffect::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;iGetValueAt(timecode_start+(interval*i)) == FILL_TYPE_LEFT) { samples[i+1] = samples[i+3]; samples[i] = samples[i+2]; diff --git a/effects/internal/fillleftrighteffect.h b/effects/internal/fillleftrighteffect.h index 1eba500d3..64b126caa 100644 --- a/effects/internal/fillleftrighteffect.h +++ b/effects/internal/fillleftrighteffect.h @@ -27,7 +27,12 @@ class FillLeftRightEffect : public Effect { Q_OBJECT public: FillLeftRightEffect(Clip* c, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; private: ComboField* fill_type; }; diff --git a/effects/internal/linearfadetransition.cpp b/effects/internal/linearfadetransition.cpp index 0693874b6..ddb89d0af 100644 --- a/effects/internal/linearfadetransition.cpp +++ b/effects/internal/linearfadetransition.cpp @@ -22,22 +22,28 @@ LinearFadeTransition::LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} -void LinearFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; +void LinearFadeTransition::process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) { + double interval = (timecode_end-timecode_start)/nb_samples; - for (int i=0;i> 8); - samples[i] = (quint8) samp; } } diff --git a/effects/internal/linearfadetransition.h b/effects/internal/linearfadetransition.h index ed3fabd2f..abc5b7383 100644 --- a/effects/internal/linearfadetransition.h +++ b/effects/internal/linearfadetransition.h @@ -25,8 +25,13 @@ class LinearFadeTransition : public Transition { public: - LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; }; #endif // LINEARFADETRANSITION_H diff --git a/effects/internal/logarithmicfadetransition.cpp b/effects/internal/logarithmicfadetransition.cpp index 5012330fc..fe2f444ff 100644 --- a/effects/internal/logarithmicfadetransition.cpp +++ b/effects/internal/logarithmicfadetransition.cpp @@ -24,22 +24,27 @@ LogarithmicFadeTransition::LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} -void LogarithmicFadeTransition::process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int type) { - double interval = (timecode_end-timecode_start)/nb_bytes; +void LogarithmicFadeTransition::process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) { + double interval = (timecode_end-timecode_start)/nb_samples; - for (int i=0;i> 8); - samples[i] = (quint8) samp; } } diff --git a/effects/internal/logarithmicfadetransition.h b/effects/internal/logarithmicfadetransition.h index 6983bcaf2..5cd03f57a 100644 --- a/effects/internal/logarithmicfadetransition.h +++ b/effects/internal/logarithmicfadetransition.h @@ -25,8 +25,13 @@ class LogarithmicFadeTransition : public Transition { public: - LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; }; #endif // LOGARITHMICFADETRANSITION_H diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 0d85e8c9c..3902bef29 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -36,26 +36,30 @@ PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { pan_val->SetMaximum(100); } -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;iGetDoubleAt(timecode_start+(interval*i)); double pval = log_volume(qAbs(pan_field_val)*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)); - if (pan_field_val < 0) { // affect right channel - right_sample *= (1.0-pval); + samples[1][i] *= (1.0-pval); } else { // affect left channel - left_sample *= (1.0-pval); + samples[0][i] *= (1.0-pval); } - - samples[i+3] = quint8(right_sample >> 8); - samples[i+2] = quint8(right_sample); - samples[i+1] = quint8(left_sample >> 8); - samples[i] = quint8(left_sample); } } diff --git a/effects/internal/paneffect.h b/effects/internal/paneffect.h index aac24dd6d..ac29d9e45 100644 --- a/effects/internal/paneffect.h +++ b/effects/internal/paneffect.h @@ -27,7 +27,12 @@ class PanEffect : public Effect { Q_OBJECT public: PanEffect(Clip* c, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; DoubleField* pan_val; }; diff --git a/effects/internal/richtexteffect.h b/effects/internal/richtexteffect.h index bdf484270..62c1ecb1b 100644 --- a/effects/internal/richtexteffect.h +++ b/effects/internal/richtexteffect.h @@ -27,7 +27,7 @@ class RichTextEffect : public Effect { Q_OBJECT public: RichTextEffect(Clip* c, const EffectMeta *em); - void redraw(double timecode); + virtual void redraw(double timecode) override; protected: virtual bool AlwaysUpdate() override; private: diff --git a/effects/internal/shakeeffect.h b/effects/internal/shakeeffect.h index fb0c56586..bed1ff55a 100644 --- a/effects/internal/shakeeffect.h +++ b/effects/internal/shakeeffect.h @@ -28,8 +28,8 @@ class ShakeEffect : public Effect { Q_OBJECT public: - ShakeEffect(Clip* c, const EffectMeta* em); - void process_coords(double timecode, GLTextureCoords& coords, int data); + ShakeEffect(Clip* c, const EffectMeta* em); + virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; DoubleField* intensity_val; DoubleField* rotation_val; diff --git a/effects/internal/solideffect.h b/effects/internal/solideffect.h index d7220583a..a52977da9 100644 --- a/effects/internal/solideffect.h +++ b/effects/internal/solideffect.h @@ -35,7 +35,7 @@ public: }; SolidEffect(Clip* c, const EffectMeta *em); - virtual void redraw(double timecode); + virtual void redraw(double timecode) override; void SetType(SolidType type); private slots: diff --git a/effects/internal/texteffect.h b/effects/internal/texteffect.h index 2a0a136a6..857ea7ec9 100644 --- a/effects/internal/texteffect.h +++ b/effects/internal/texteffect.h @@ -30,7 +30,7 @@ class TextEffect : public Effect { Q_OBJECT public: TextEffect(Clip* c, const EffectMeta *em); - void redraw(double timecode); + virtual void redraw(double timecode) override; private slots: void outline_enable(bool); void shadow_enable(bool); diff --git a/effects/internal/timecodeeffect.h b/effects/internal/timecodeeffect.h index 4b5756fa0..743796199 100644 --- a/effects/internal/timecodeeffect.h +++ b/effects/internal/timecodeeffect.h @@ -30,7 +30,7 @@ class TimecodeEffect : public Effect { Q_OBJECT public: TimecodeEffect(Clip* c, const EffectMeta *em); - void redraw(double timecode); + virtual void redraw(double timecode) override; DoubleField* scale_val; ColorField* color_val; ColorField* color_bg_val; diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index f877083b6..c7b23e69c 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -49,29 +49,37 @@ ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_ mix_val->SetValueAt(0, true); } -void ToneEffect::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;iGetDoubleAt(timecode)) + /parent_clip->track()->sequence()->audio_frequency) + *log_volume(amount_val->GetDoubleAt(timecode)*0.01); - qint16 left_tone_sample = qint16(qRound(qSin((2*M_PI*sinX*freq_val->GetDoubleAt(timecode)) - /parent_clip->track()->sequence()->audio_frequency) - *log_volume(amount_val->GetDoubleAt(timecode)*0.01)*INT16_MAX)); - qint16 right_tone_sample = left_tone_sample; + for (int j=0;jGetBoolAt(timecode)) { + + // mix with source audio + samples[j][i] += tone_sample; + + } else { + + // replace source audio + samples[j][i] = tone_sample; + + } - // mix with source audio - if (mix_val->GetBoolAt(timecode)) { - 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)); - left_tone_sample = mix_audio_sample(left_tone_sample, left_sample); - right_tone_sample = mix_audio_sample(right_tone_sample, right_sample); } - samples[i+3] = quint8(right_tone_sample >> 8); - samples[i+2] = quint8(right_tone_sample); - samples[i+1] = quint8(left_tone_sample >> 8); - samples[i] = quint8(left_tone_sample); - sinX++; } } diff --git a/effects/internal/toneeffect.h b/effects/internal/toneeffect.h index 12e118201..396bb5806 100644 --- a/effects/internal/toneeffect.h +++ b/effects/internal/toneeffect.h @@ -27,7 +27,12 @@ class ToneEffect : public Effect { Q_OBJECT public: ToneEffect(Clip* c, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; ComboField* type_val; DoubleField* freq_val; diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index ba05c6614..42c840559 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -27,10 +27,10 @@ class TransformEffect : public Effect { Q_OBJECT public: TransformEffect(Clip* c, const EffectMeta* em); - void refresh(); - void process_coords(double timecode, GLTextureCoords& coords, int data); + virtual void refresh() override; + virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; - void gizmo_draw(double timecode, GLTextureCoords& coords); + virtual void gizmo_draw(double timecode, GLTextureCoords& coords) override; public slots: void toggle_uniform_scale(bool enabled); private: diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index bccb4c496..d54a4d116 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -37,32 +37,22 @@ VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { volume_val->SetDisplayType(LabelSlider::Decibel); } -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;iGetDoubleAt(timecode_start+(interval*i)); - qint32 right_samp = qint16(((samples[i+3] & 0xFF) << 8) | (samples[i+2] & 0xFF)); - qint32 left_samp = qint16(((samples[i+1] & 0xFF) << 8) | (samples[i] & 0xFF)); + for (int j=0;j INT16_MAX) { - left_samp = INT16_MAX; - } else if (left_samp < INT16_MIN) { - left_samp = INT16_MIN; } - - if (right_samp > INT16_MAX) { - right_samp = INT16_MAX; - } else if (right_samp < INT16_MIN) { - right_samp = INT16_MIN; - } - - samples[i+3] = (quint8) (right_samp >> 8); - samples[i+2] = (quint8) right_samp; - samples[i+1] = (quint8) (left_samp >> 8); - samples[i] = (quint8) left_samp; } } diff --git a/effects/internal/volumeeffect.h b/effects/internal/volumeeffect.h index 2922d041e..c4a7a6180 100644 --- a/effects/internal/volumeeffect.h +++ b/effects/internal/volumeeffect.h @@ -27,7 +27,12 @@ class VolumeEffect : public Effect { Q_OBJECT public: VolumeEffect(Clip* c, const EffectMeta* em); - void process_audio(double timecode_start, double timecode_end, quint8* samples, int nb_bytes, int channel_count); + virtual void process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) override; DoubleField* volume_val; }; diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp index a9f53e478..bbde8503a 100644 --- a/effects/internal/vsthost.cpp +++ b/effects/internal/vsthost.cpp @@ -46,7 +46,6 @@ class NSWindow; #endif #define BLOCK_SIZE 512 -#define CHANNEL_COUNT 2 struct VSTRect { int16_t top; @@ -212,15 +211,6 @@ bool VSTHost::canPluginDo(char *canDoString) { return (dispatcher(plugin, effCanDo, 0, 0, static_cast(canDoString), 0.0f) > 0); } -void VSTHost::processAudio(long numFrames) { - // Always reset the output array before processing. - for (int i=0;iprocessReplacing(plugin, inputs, outputs, numFrames); -} - void VSTHost::CreateDialogIfNull() { if (dialog == nullptr) { @@ -240,17 +230,12 @@ void VSTHost::send_data_cache_to_plugin() VSTHost::VSTHost(Clip* c, const EffectMeta *em) : Effect(c, em), plugin(nullptr), - dialog(nullptr) + dialog(nullptr), + input_cache(BLOCK_SIZE), + output_cache(BLOCK_SIZE) { plugin = nullptr; - inputs = new float* [CHANNEL_COUNT]; - outputs = new float* [CHANNEL_COUNT]; - for(int channel = 0; channel < CHANNEL_COUNT; channel++) { - inputs[channel] = new float[BLOCK_SIZE]; - outputs[channel] = new float[BLOCK_SIZE]; - } - EffectRow* file_row = new EffectRow(this, tr("Plugin"), true, false); file_field = new FileField(file_row, "filename"); connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()), Qt::QueuedConnection); @@ -264,47 +249,35 @@ VSTHost::VSTHost(Clip* c, const EffectMeta *em) : } VSTHost::~VSTHost() { - for(int channel = 0; channel < CHANNEL_COUNT; channel++) { - delete [] inputs[channel]; - delete [] outputs[channel]; - } - delete [] outputs; - delete [] inputs; - freePlugin(); } -void VSTHost::process_audio(double, double, quint8* samples, int nb_bytes, int) { +void VSTHost::process_audio(double timecode_start, + double timecode_end, + float **samples, + int nb_samples, + int channel_count, + int type) { if (plugin != nullptr) { - int interval = BLOCK_SIZE*4; - for (int i=0;i>2; - inputs[0][index] = float(left_sample) / float(INT16_MAX); - inputs[1][index] = float(right_sample) / float(INT16_MAX); + for (int i=0;i>2); + plugin->processReplacing(plugin, input_cache.data(), output_cache.data(), sample_size); - // convert back to int16 - for (int j=i;j>2; - - qint16 left_sample = qint16(qRound(outputs[0][index] * INT16_MAX)); - qint16 right_sample = qint16(qRound(outputs[1][index] * INT16_MAX)); - - samples[j+3] = quint8(right_sample >> 8); - samples[j+2] = quint8(right_sample); - samples[j+1] = quint8(left_sample >> 8); - samples[j] = quint8(left_sample); + // Copy output cache back to samples + for (int j=0;jSetEnabled(plugin != nullptr); } + +SampleCache::SampleCache(int block_size) : + block_size_(block_size), + channel_count_(0), + array_(nullptr) +{ +} + +SampleCache::~SampleCache() +{ + destroy(); +} + +void SampleCache::Create(int channels) +{ + if (channel_count_ != channels) { + + if (channel_count_ > 0) { + destroy(); + } + + channel_count_ = channels; + + array_ = new float* [channel_count_]; + for (int i=0;i> 1) / nb_channels / sample_rate); +double samples_to_seconds(int nb_samples, int nb_channels, int sample_rate) { + return (double(nb_samples) / double(nb_channels) / double(sample_rate)); } -void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int nb_bytes, QVector nests) { +int samples_to_bytes(int nb_samples, int nb_channels) { + return nb_samples * nb_channels * sizeof(float); +} + +void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int nb_samples, int nb_channels, QVector nests) { // perform all audio effects double timecode_end; - timecode_end = timecode_start + bytes_to_seconds(nb_bytes, frame->channels, frame->sample_rate); + timecode_end = timecode_start + samples_to_seconds(nb_samples, frame->channels, frame->sample_rate); for (int j=0;jeffects.size();j++) { Effect* e = clip->effects.at(j).get(); if (e->IsEnabled()) { - e->process_audio(timecode_start, timecode_end, frame->data[0], nb_bytes, 2); + e->process_audio(timecode_start, timecode_end, reinterpret_cast(frame->data), nb_samples, nb_channels, kTransitionNone); } } if (clip->opening_transition != nullptr) { @@ -71,7 +75,7 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; double adjusted_range_end = (timecode_end - transition_start) / adjustment; - clip->opening_transition->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, kTransitionOpening); + clip->opening_transition->process_audio(adjusted_range_start, adjusted_range_end, reinterpret_cast(frame->data), nb_samples, nb_channels, kTransitionOpening); } } } @@ -84,7 +88,7 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int double adjustment = transition_end - transition_start; double adjusted_range_start = (timecode_start - transition_start) / adjustment; double adjusted_range_end = (timecode_end - transition_start) / adjustment; - clip->closing_transition->process_audio(adjusted_range_start, adjusted_range_end, frame->data[0], nb_bytes, kTransitionClosing); + clip->closing_transition->process_audio(adjusted_range_start, adjusted_range_end, reinterpret_cast(frame->data), nb_samples, nb_channels, kTransitionClosing); } } } @@ -95,7 +99,8 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int apply_audio_effects(next_nest, timecode_start + (double(clip->timeline_in(true)-clip->clip_in(true))/clip->track()->sequence()->frame_rate), frame, - nb_bytes, + nb_samples, + nb_channels, nests); } } @@ -157,16 +162,16 @@ void Cacher::CacheAudioWorker() { while (true) { AVFrame* frame; - int nb_bytes = INT_MAX; + int nb_samples = INT_MAX; if (clip->media() == nullptr) { frame = frame_; - nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; - while ((frame_sample_index_ == -1 || frame_sample_index_ >= nb_bytes) && nb_bytes > 0) { + nb_samples = frame->nb_samples; + while ((frame_sample_index_ == -1 || frame_sample_index_ >= nb_samples) && nb_samples > 0) { // create "new frame" - memset(frame_->data[0], 0, nb_bytes); - apply_audio_effects(clip, bytes_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_bytes, nests_); - frame_->pts += nb_bytes; + memset(frame_->data[0], 0, nb_samples); + apply_audio_effects(clip, samples_to_seconds(frame->pts, frame->channels, frame->sample_rate), frame, nb_samples, frame->channels, nests_); + frame_->pts += nb_samples; frame_sample_index_ = 0; if (audio_buffer_write == 0) { audio_buffer_write = get_buffer_offset_from_frame(last_fr, qMax(timeline_in, target_frame)); @@ -184,7 +189,7 @@ void Cacher::CacheAudioWorker() { // retrieve frame bool new_frame = false; - while ((frame_sample_index_ == -1 || frame_sample_index_ >= nb_bytes) && nb_bytes > 0) { + while ((frame_sample_index_ == -1 || frame_sample_index_ >= nb_samples) && nb_samples > 0) { // no more audio left in frame, get a new one if (!reached_end) { @@ -338,10 +343,10 @@ void Cacher::CacheAudioWorker() { if (frame_sample_index_ < 0) { frame_sample_index_ = 0; } else { - frame_sample_index_ -= nb_bytes; + frame_sample_index_ -= nb_samples; } - nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + nb_samples = frame->nb_samples; if (audio_just_reset) { // get precise sample offset for the elected clip_in from this audio frame @@ -356,7 +361,7 @@ void Cacher::CacheAudioWorker() { dout << "fsts:" << frame_sts << "tsts:" << target_sts << "nbs:" << nb_samples << "nbb:" << nb_bytes << "rev_targetToSec:" << (reverse_target * timebase); dout << "fsi-calc:" << frame_sample_index; #endif - if (reverse_audio) frame_sample_index_ = nb_bytes - frame_sample_index_; + if (reverse_audio) frame_sample_index_ = nb_samples - frame_sample_index_; audio_just_reset = false; } @@ -393,9 +398,19 @@ void Cacher::CacheAudioWorker() { #endif // apply any audio effects to the data - if (nb_bytes == INT_MAX) nb_bytes = frame->nb_samples * av_get_bytes_per_sample(static_cast(frame->format)) * frame->channels; + if (nb_samples == INT_MAX) { + nb_samples = frame->nb_samples; + } if (new_frame) { - apply_audio_effects(clip, bytes_to_seconds(audio_buffer_write, 2, current_audio_freq()) + audio_ibuffer_timecode + ((double)clip->clip_in(true)/clip->track()->sequence()->frame_rate) - ((double)timeline_in/last_fr), frame, nb_bytes, nests_); + apply_audio_effects(clip, + samples_to_seconds(audio_buffer_write, 2, current_audio_freq()) + + audio_ibuffer_timecode + + (double(clip->clip_in(true))/clip->track()->sequence()->frame_rate) + - (double(timeline_in)/last_fr), + frame, + nb_samples, + frame->channels, + nests_); } } @@ -408,23 +423,20 @@ void Cacher::CacheAudioWorker() { audio_write_lock.lock(); int sample_skip = 4*qMax(0, qAbs(playback_speed_)-1); - int sample_byte_size = av_get_bytes_per_sample(static_cast(frame->format)); - while (frame_sample_index_ < nb_bytes + while (frame_sample_index_ < nb_samples && audio_buffer_write < audio_ibuffer_read+(audio_ibuffer_size>>1) && audio_buffer_write < buffer_timeline_out) { for (int i=0;ichannels;i++) { - int upper_byte_index = (audio_buffer_write+1)%audio_ibuffer_size; - int lower_byte_index = (audio_buffer_write)%audio_ibuffer_size; - qint16 old_sample = static_cast((audio_ibuffer[upper_byte_index] & 0xFF) << 8 | (audio_ibuffer[lower_byte_index] & 0xFF)); - qint16 new_sample = static_cast((frame->data[0][frame_sample_index_+1] & 0xFF) << 8 | (frame->data[0][frame_sample_index_] & 0xFF)); - qint16 mixed_sample = mix_audio_sample(old_sample, new_sample); + int buffer_index = audio_buffer_write%audio_ibuffer_size; + int frame_index = frame_sample_index_ * sizeof(float); - audio_ibuffer[upper_byte_index] = quint8((mixed_sample >> 8) & 0xFF); - audio_ibuffer[lower_byte_index] = quint8(mixed_sample & 0xFF); - - audio_buffer_write+=sample_byte_size; - frame_sample_index_+=sample_byte_size; + audio_ibuffer[buffer_index] += static_cast((frame->data[0][frame_index+3] & 0xFF) << 24 + | (frame->data[0][frame_index+2] & 0xFF) << 16 + | (frame->data[0][frame_index+1] & 0xFF) << 8 + | (frame->data[0][frame_index] & 0xFF)); + audio_buffer_write++; + frame_sample_index_++; } frame_sample_index_ += sample_skip; @@ -444,7 +456,7 @@ void Cacher::CacheAudioWorker() { if (audio_thread != nullptr) audio_thread->notifyReceiver(); } - if (frame_sample_index_ >= nb_bytes) { + if (frame_sample_index_ >= nb_samples) { frame_sample_index_ = -1; } else { // assume we have no more data to send From 8e3d6640612b73cb6831756e11bd5e39dbbcdc21 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 7 Apr 2019 14:09:33 +1000 Subject: [PATCH 095/133] reworked audio output to 32-bit float --- rendering/audio.cpp | 26 ++++++++++++-------------- rendering/audio.h | 2 +- rendering/cacher.cpp | 12 +++++------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/rendering/audio.cpp b/rendering/audio.cpp index 780d32107..1faaf4bf1 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -54,7 +54,7 @@ bool recording = false; int audio_rendering_rate = 0; -qint8 audio_ibuffer[audio_ibuffer_size]; +float audio_ibuffer[audio_ibuffer_size]; qint64 audio_ibuffer_read = 0; long audio_ibuffer_frame = 0; double audio_ibuffer_timecode = 0; @@ -100,10 +100,10 @@ void init_audio() { QAudioFormat audio_format; audio_format.setSampleRate(olive::config.audio_rate); audio_format.setChannelCount(2); - audio_format.setSampleSize(16); + audio_format.setSampleSize(32); audio_format.setCodec("audio/pcm"); audio_format.setByteOrder(QAudioFormat::LittleEndian); - audio_format.setSampleType(QAudioFormat::SignedInt); + audio_format.setSampleType(QAudioFormat::Float); QAudioDeviceInfo info = get_audio_device(QAudio::AudioOutput); @@ -146,7 +146,7 @@ void stop_audio() { void clear_audio_ibuffer() { if (audio_thread != nullptr) audio_thread->lock.lock(); audio_write_lock.lock(); - memset(audio_ibuffer, 0, audio_ibuffer_size); + memset(audio_ibuffer, 0, audio_ibuffer_size * sizeof(float)); audio_ibuffer_read = 0; audio_write_lock.unlock(); if (audio_thread != nullptr) audio_thread->lock.unlock(); @@ -158,7 +158,7 @@ int current_audio_freq() { qint64 get_buffer_offset_from_frame(double framerate, long frame) { if (frame >= audio_ibuffer_frame) { - int multiplier = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16)*av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); + int multiplier = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); return qFloor((double(frame - audio_ibuffer_frame)/framerate)*current_audio_freq())*multiplier; } else { qWarning() << "Invalid values passed to get_buffer_offset_from_frame" << frame << "<" << audio_ibuffer_frame; @@ -190,15 +190,13 @@ void AudioSenderThread::run() { if (close) { break; } else if (panel_sequence_viewer->playing || panel_footage_viewer->playing || audio_scrub) { - int written_bytes = 0; - int adjusted_read_index = audio_ibuffer_read%audio_ibuffer_size; - int max_write = audio_ibuffer_size - adjusted_read_index; + int adjusted_read_index = (audio_ibuffer_read%audio_ibuffer_size); + int max_write = (audio_ibuffer_size - adjusted_read_index) * sizeof(float); int actual_write = send_audio_to_output(adjusted_read_index, max_write); - written_bytes += actual_write; if (actual_write == max_write) { // got all the bytes, write again - written_bytes += send_audio_to_output(0, audio_ibuffer_size); + send_audio_to_output(0, audio_ibuffer_size); } audio_scrub = false; @@ -211,10 +209,9 @@ int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { // send audio to device audio_write_lock.lock(); - qint64 actual_write = audio_io_device->write(reinterpret_cast(audio_ibuffer)+offset, max); - - qint64 audio_ibuffer_limit = audio_ibuffer_read + actual_write; + qint64 actual_write = audio_io_device->write(reinterpret_cast(audio_ibuffer+offset), max); + /* if (actual_write > 0) { // average values and send to audio monitor int channels = audio_output->format().channelCount(); @@ -236,10 +233,11 @@ int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { panel_timeline.first()->audio_monitor->set_value(averages); } + */ memset(audio_ibuffer+offset, 0, actual_write); - audio_ibuffer_read = audio_ibuffer_limit; + audio_ibuffer_read += (actual_write / sizeof(float)); audio_write_lock.unlock(); diff --git a/rendering/audio.h b/rendering/audio.h index facca657e..19b768b26 100644 --- a/rendering/audio.h +++ b/rendering/audio.h @@ -55,7 +55,7 @@ extern AudioSenderThread* audio_thread; extern QMutex audio_write_lock; #define audio_ibuffer_size 192000 -extern qint8 audio_ibuffer[audio_ibuffer_size]; +extern float audio_ibuffer[audio_ibuffer_size]; extern qint64 audio_ibuffer_read; extern long audio_ibuffer_frame; extern double audio_ibuffer_timecode; diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index 161156fd5..ed1c770cc 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -422,23 +422,21 @@ void Cacher::CacheAudioWorker() { audio_write_lock.lock(); - int sample_skip = 4*qMax(0, qAbs(playback_speed_)-1); + int sample_skip = qMax(0, qAbs(playback_speed_)-1); while (frame_sample_index_ < nb_samples && audio_buffer_write < audio_ibuffer_read+(audio_ibuffer_size>>1) && audio_buffer_write < buffer_timeline_out) { for (int i=0;ichannels;i++) { int buffer_index = audio_buffer_write%audio_ibuffer_size; - int frame_index = frame_sample_index_ * sizeof(float); - audio_ibuffer[buffer_index] += static_cast((frame->data[0][frame_index+3] & 0xFF) << 24 - | (frame->data[0][frame_index+2] & 0xFF) << 16 - | (frame->data[0][frame_index+1] & 0xFF) << 8 - | (frame->data[0][frame_index] & 0xFF)); + audio_ibuffer[buffer_index] += reinterpret_cast(frame->data[i])[frame_sample_index_]; + audio_buffer_write++; - frame_sample_index_++; } + frame_sample_index_++; + frame_sample_index_ += sample_skip; if (audio_reset_) break; From 3e1c857de4d380cd59bbc81e88b162aeff82250d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 8 Apr 2019 14:13:08 +1000 Subject: [PATCH 096/133] began node work --- nodes/medianode.cpp | 6 ++++++ nodes/medianode.h | 12 ++++++++++++ nodes/node.cpp | 7 +++++++ nodes/node.h | 31 +++++++++++++++++++++++++++++++ olive.pro | 12 ++++++++++-- panels/nodeeditor.cpp | 29 +++++++++++++++++++++++++++++ panels/nodeeditor.h | 22 ++++++++++++++++++++++ panels/panels.cpp | 3 +++ panels/panels.h | 2 ++ ui/mainwindow.cpp | 8 ++++++++ ui/mainwindow.h | 1 + ui/nodeview.cpp | 35 +++++++++++++++++++++++++++++++++++ ui/nodeview.h | 23 +++++++++++++++++++++++ 13 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 nodes/medianode.cpp create mode 100644 nodes/medianode.h create mode 100644 nodes/node.cpp create mode 100644 nodes/node.h create mode 100644 panels/nodeeditor.cpp create mode 100644 panels/nodeeditor.h create mode 100644 ui/nodeview.cpp create mode 100644 ui/nodeview.h diff --git a/nodes/medianode.cpp b/nodes/medianode.cpp new file mode 100644 index 000000000..c844a1c59 --- /dev/null +++ b/nodes/medianode.cpp @@ -0,0 +1,6 @@ +#include "medianode.h" + +MediaNode::MediaNode() +{ + +} diff --git a/nodes/medianode.h b/nodes/medianode.h new file mode 100644 index 000000000..96dd94ecc --- /dev/null +++ b/nodes/medianode.h @@ -0,0 +1,12 @@ +#ifndef MEDIANODE_H +#define MEDIANODE_H + +#include "node.h" + +class MediaNode : public Node +{ +public: + MediaNode(); +}; + +#endif // MEDIANODE_H diff --git a/nodes/node.cpp b/nodes/node.cpp new file mode 100644 index 000000000..c74e03749 --- /dev/null +++ b/nodes/node.cpp @@ -0,0 +1,7 @@ +#include "node.h" + +Node::Node() : + max_inputs_(INT_MAX), + max_outputs_(INT_MAX) +{ +} diff --git a/nodes/node.h b/nodes/node.h new file mode 100644 index 000000000..752a55b34 --- /dev/null +++ b/nodes/node.h @@ -0,0 +1,31 @@ +#ifndef NODE_H +#define NODE_H + +#include +#include + +class NodeInput { +public: + NodeInput(); + +private: + QString name_; + +}; + +class Node : public QObject +{ + Q_OBJECT +public: + Node(); + +private: + int max_inputs_; + QVector inputs_; + + int max_outputs_; + QVector outputs_; + +}; + +#endif // NODE_H diff --git a/olive.pro b/olive.pro index a3db45409..c55c3bb6c 100644 --- a/olive.pro +++ b/olive.pro @@ -184,7 +184,11 @@ SOURCES += \ global/clipboard.cpp \ timeline/timelinetools.cpp \ timeline/ghost.cpp \ - ui/waveform.cpp + ui/waveform.cpp \ + panels/nodeeditor.cpp \ + ui/nodeview.cpp \ + nodes/medianode.cpp \ + nodes/node.cpp HEADERS += \ ui/mainwindow.h \ @@ -325,7 +329,11 @@ HEADERS += \ ui/timelinelabel.h \ global/clipboard.h \ timeline/timelinetools.h \ - ui/waveform.h + ui/waveform.h \ + panels/nodeeditor.h \ + ui/nodeview.h \ + nodes/medianode.h \ + nodes/node.h FORMS += diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp new file mode 100644 index 000000000..1eb5906d8 --- /dev/null +++ b/panels/nodeeditor.cpp @@ -0,0 +1,29 @@ +#include "nodeeditor.h" + +#include +#include +#include + +NodeEditor::NodeEditor(QWidget *parent) : + Panel(parent), + view_(&scene_) +{ + resize(720, 480); + + QWidget* central_widget = new QWidget(); + setWidget(central_widget); + + QVBoxLayout* layout = new QVBoxLayout(central_widget); + layout->addWidget(&view_); + + view_.setInteractive(true); + view_.setDragMode(QGraphicsView::RubberBandDrag); + + QPushButton* push_button = new QPushButton("heck!!!"); + scene_.addWidget(push_button); +} + +void NodeEditor::Retranslate() +{ + +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h new file mode 100644 index 000000000..eaf77c586 --- /dev/null +++ b/panels/nodeeditor.h @@ -0,0 +1,22 @@ +#ifndef NODEEDITOR_H +#define NODEEDITOR_H + +#include + +#include "ui/panel.h" +#include "ui/nodeview.h" + +class NodeEditor : public Panel { + Q_OBJECT +public: + NodeEditor(QWidget* parent = nullptr); + + virtual void Retranslate() override; + +private: + QGraphicsScene scene_; + NodeView view_; + +}; + +#endif // NODEEDITOR_H diff --git a/panels/panels.cpp b/panels/panels.cpp index 6fb3455f8..60ec67816 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -36,6 +36,7 @@ Viewer* panel_sequence_viewer = nullptr; Viewer* panel_footage_viewer = nullptr; QVector panel_timeline; GraphEditor* panel_graph_editor = nullptr; +NodeEditor* panel_node_editor = nullptr; void update_ui(bool modified) { if (modified) { @@ -86,6 +87,8 @@ void alloc_panels(QWidget* parent) { panel_timeline.append(first_timeline_panel); panel_graph_editor = new GraphEditor(parent); panel_graph_editor->setObjectName("graph_editor"); + panel_node_editor = new NodeEditor(parent); + panel_node_editor->setObjectName("node_editor"); } void free_panels() { diff --git a/panels/panels.h b/panels/panels.h index 3cf3be584..70af99b75 100644 --- a/panels/panels.h +++ b/panels/panels.h @@ -26,6 +26,7 @@ #include "viewer.h" #include "grapheditor.h" #include "project.h" +#include "nodeeditor.h" extern QVector panel_project; extern EffectControls* panel_effect_controls; @@ -33,6 +34,7 @@ extern Viewer* panel_sequence_viewer; extern Viewer* panel_footage_viewer; extern QVector panel_timeline; extern GraphEditor* panel_graph_editor; +extern NodeEditor* panel_node_editor; void update_ui(bool modified); QDockWidget* get_focused_panel(bool force_hover = false); diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 927aa26c9..153080f88 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -148,6 +148,7 @@ void MainWindow::setup_layout(bool reset) { addDockWidget(Qt::TopDockWidgetArea, panel_project.first()); addDockWidget(Qt::TopDockWidgetArea, panel_graph_editor); + addDockWidget(Qt::TopDockWidgetArea, panel_node_editor); addDockWidget(Qt::TopDockWidgetArea, panel_footage_viewer); tabifyDockWidget(panel_footage_viewer, panel_effect_controls); panel_footage_viewer->raise(); @@ -160,6 +161,7 @@ void MainWindow::setup_layout(bool reset) { panel_sequence_viewer->show(); panel_timeline.first()->show(); panel_graph_editor->hide(); + panel_node_editor->hide(); panel_project.first()->setFloating(false); panel_effect_controls->setFloating(false); @@ -167,6 +169,7 @@ void MainWindow::setup_layout(bool reset) { panel_sequence_viewer->setFloating(false); panel_timeline.first()->setFloating(false); panel_graph_editor->setFloating(true); + panel_node_editor->setFloating(true); resizeDocks({panel_project.first(), panel_footage_viewer, panel_sequence_viewer}, {width()/3, width()/3, width()/3}, @@ -694,6 +697,10 @@ void MainWindow::setup_menus() { window_graph_editor_action->setCheckable(true); window_graph_editor_action->setData(reinterpret_cast(panel_graph_editor)); + window_node_editor_action = MenuHelper::create_menu_action(window_menu, "panelnodeeditor", this, SLOT(toggle_panel_visibility())); + window_node_editor_action->setCheckable(true); + window_node_editor_action->setData(reinterpret_cast(panel_node_editor)); + window_footageviewer_action = MenuHelper::create_menu_action(window_menu, "panelfootageviewer", this, SLOT(toggle_panel_visibility())); window_footageviewer_action->setCheckable(true); window_footageviewer_action->setData(reinterpret_cast(panel_footage_viewer)); @@ -887,6 +894,7 @@ void MainWindow::Retranslate() window_effectcontrols_action->setText(tr("Effect Controls")); window_timeline_action->setText(tr("Timeline")); window_graph_editor_action->setText(tr("Graph Editor")); + window_node_editor_action->setText(tr("Node Editor")); window_footageviewer_action->setText(tr("Media Viewer")); window_sequenceviewer_action->setText(tr("Sequence Viewer")); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 264e83868..722e575e5 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -308,6 +308,7 @@ private: QAction* window_effectcontrols_action; QAction* window_timeline_action; QAction* window_graph_editor_action; + QAction* window_node_editor_action; QAction* window_footageviewer_action; QAction* window_sequenceviewer_action; diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp new file mode 100644 index 000000000..16593f328 --- /dev/null +++ b/ui/nodeview.cpp @@ -0,0 +1,35 @@ +#include "nodeview.h" + +#include +#include + +NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : + QGraphicsView(scene, parent), + hand_moving_(false) +{ + setMouseTracking(true); + setWindowTitle(tr("Node Editor")); +} + +void NodeView::mousePressEvent(QMouseEvent *event) +{ + QGraphicsView::mousePressEvent(event); +} + +void NodeView::mouseMoveEvent(QMouseEvent *event) +{ + QGraphicsView::mouseMoveEvent(event); +} + +void NodeView::mouseReleaseEvent(QMouseEvent *event) +{ +} + +void NodeView::wheelEvent(QWheelEvent *event) +{ + if (event->angleDelta().y() > 0) { + scale(0.9, 0.9); + } else { + scale(1.1, 1.1); + } +} diff --git a/ui/nodeview.h b/ui/nodeview.h new file mode 100644 index 000000000..bd751aa4c --- /dev/null +++ b/ui/nodeview.h @@ -0,0 +1,23 @@ +#ifndef NODEVIEW_H +#define NODEVIEW_H + +#include + +class NodeView : public QGraphicsView { + Q_OBJECT +public: + NodeView(QGraphicsScene *scene, QWidget* parent = nullptr); + +protected: + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void mouseMoveEvent(QMouseEvent *event) override; + virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void wheelEvent(QWheelEvent *event) override; + +private: + bool hand_moving_; + QPoint drag_start_; + +}; + +#endif // NODEVIEW_H From 3837d7c461074f3bcffeb1f15977444bd86591b0 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 8 Apr 2019 22:16:47 +1000 Subject: [PATCH 097/133] foundational node UI work --- olive.pro | 10 +- panels/effectcontrols.cpp | 328 ++++++-------------------------------- panels/effectcontrols.h | 27 +--- panels/effectspanel.cpp | 269 +++++++++++++++++++++++++++++++ panels/effectspanel.h | 41 +++++ panels/nodeeditor.cpp | 23 ++- panels/nodeeditor.h | 12 +- panels/panels.cpp | 1 + ui/nodeui.cpp | 55 +++++++ ui/nodeui.h | 21 +++ ui/nodeview.cpp | 20 ++- ui/nodeview.h | 3 + ui/nodewidget.cpp | 46 ++++++ ui/nodewidget.h | 20 +++ 14 files changed, 565 insertions(+), 311 deletions(-) create mode 100644 panels/effectspanel.cpp create mode 100644 panels/effectspanel.h create mode 100644 ui/nodeui.cpp create mode 100644 ui/nodeui.h create mode 100644 ui/nodewidget.cpp create mode 100644 ui/nodewidget.h diff --git a/olive.pro b/olive.pro index c55c3bb6c..107c4e3d8 100644 --- a/olive.pro +++ b/olive.pro @@ -188,7 +188,10 @@ SOURCES += \ panels/nodeeditor.cpp \ ui/nodeview.cpp \ nodes/medianode.cpp \ - nodes/node.cpp + nodes/node.cpp \ + ui/nodeui.cpp \ + ui/nodewidget.cpp \ + panels/effectspanel.cpp HEADERS += \ ui/mainwindow.h \ @@ -333,7 +336,10 @@ HEADERS += \ panels/nodeeditor.h \ ui/nodeview.h \ nodes/medianode.h \ - nodes/node.h + nodes/node.h \ + ui/nodeui.h \ + ui/nodewidget.h \ + panels/effectspanel.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 2321572a2..da37c1ef0 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -56,7 +56,7 @@ #include "ui/menu.h" EffectControls::EffectControls(QWidget *parent) : - Panel(parent), + EffectsPanel(parent), zoom(1) { setup_ui(); @@ -81,11 +81,6 @@ EffectControls::EffectControls(QWidget *parent) : connect(scrollArea->verticalScrollBar(), SIGNAL(valueChanged(int)), verticalScrollBar, SLOT(setValue(int))); } -EffectControls::~EffectControls() -{ - Clear(true); -} - void EffectControls::set_zoom(bool in) { zoom *= (in) ? 2 : 0.5; update_keyframes(); @@ -144,55 +139,10 @@ void EffectControls::delete_selected_keyframes() { keyframeView->delete_selected_keyframes(); } -void EffectControls::copy(bool del) { - bool cleared = false; - - ComboAction* ca = nullptr; - if (del) { - ca = new ComboAction(); - } - - for (int i=0;iIsSelected()) { - Effect* e = open_effects_.at(i)->GetEffect(); - - if (e->meta->type == EFFECT_TYPE_EFFECT) { - - if (!cleared) { - olive::clipboard.Clear(); - cleared = true; - olive::clipboard.SetType(Clipboard::CLIPBOARD_TYPE_EFFECT); - } - - olive::clipboard.Append(e->copy(nullptr)); - - if (del) { - - DeleteEffect(ca, e); - - } - - } - } - } - - if (del) { - if (ca->hasActions()) { - olive::undo_stack.push(ca); - } else { - delete ca; - } - } -} - void EffectControls::scroll_to_frame(long frame) { scroll_to_frame_internal(horizontalScrollBar, frame - keyframeView->visible_in, zoom, keyframeView->width()); } -void EffectControls::cut() { - copy(true); -} - void EffectControls::show_effect_menu(int type, Track::Type subtype) { effect_menu_type = type; effect_menu_subtype = subtype; @@ -263,63 +213,6 @@ void EffectControls::show_effect_menu(int type, Track::Type subtype) { effects_menu.exec(QCursor::pos()); } -void EffectControls::Clear(bool clear_cache) { - // clear existing clips - deselect_all_effects(nullptr); - - for (int i=0;iSetEffects(open_effects_); - - vcontainer->setVisible(false); - acontainer->setVisible(false); - headers->setVisible(false); - keyframeView->setEnabled(false); - - if (clear_cache) { - selected_clips_.clear(); - } - - UpdateTitle(); -} - -bool EffectControls::IsEffectSelected(Effect *e) -{ - for (int i=0;iGetEffect() == e && open_effects_.at(i)->IsSelected()) { - return true; - } - } - return false; -} - -void EffectControls::deselect_all_effects(QWidget* sender) { - - for (int i=0;iheader_click(false, false); - } - } - - if (panel_sequence_viewer != nullptr) { - panel_sequence_viewer->viewer_widget()->update(); - } -} - -void EffectControls::open_effect(QVBoxLayout* layout, Effect* e) { - EffectUI* container = new EffectUI(e); - - connect(container, SIGNAL(CutRequested()), this, SLOT(cut())); - connect(container, SIGNAL(CopyRequested()), this, SLOT(copy())); - connect(container, SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*))); - - open_effects_.append(container); - - layout->addWidget(container); -} - void EffectControls::UpdateTitle() { if (selected_clips_.isEmpty()) { setWindowTitle(panel_name + tr("(none)")); @@ -544,59 +437,6 @@ void EffectControls::effects_area_context_menu() { menu.exec(QCursor::pos()); } -void EffectControls::DeleteEffect(ComboAction* ca, Effect* effect_ref) { - if (effect_ref->meta->type == EFFECT_TYPE_EFFECT) { - - ca->append(new EffectDeleteCommand(effect_ref)); - - } else if (effect_ref->meta->type == EFFECT_TYPE_TRANSITION) { - - // Retrieve shared ptr for this transition - - Clip* attached_clip = effect_ref->parent_clip; - - TransitionPtr t = nullptr; - - if (attached_clip->opening_transition.get() == effect_ref) { - - t = attached_clip->opening_transition; - - } else if (attached_clip->closing_transition.get() == effect_ref) { - - t = attached_clip->closing_transition; - - } - - if (t == nullptr) { - - qWarning() << "Failed to delete transition, couldn't find clip link."; - - } else { - - ca->append(new DeleteTransitionCommand(t)); - - } - - } -} - -void EffectControls::DeleteSelectedEffects() { - ComboAction* ca = new ComboAction(); - - for (int i=0;iIsSelected()) { - DeleteEffect(ca, open_effects_.at(i)->GetEffect()); - } - } - - if (ca->hasActions()) { - olive::undo_stack.push(ca); - update_ui(true); - } else { - delete ca; - } -} - bool EffectControls::focused() { if (this->hasFocus() @@ -605,124 +445,7 @@ bool EffectControls::focused() return true; } - for (int i=0;iIsFocused()) { - return true; - } - } - - return false; -} - -void EffectControls::Reload() { - Clear(false); - Load(); -} - -void EffectControls::SetClips() -{ - Clear(true); - - Sequence* top_sequence = Timeline::GetTopSequence().get(); - - if (top_sequence == nullptr) { - selected_clips_.clear(); - } else { - // replace clip vector - selected_clips_ = top_sequence->SelectedClips(false); - - Load(); - } -} - -void EffectControls::Load() { - bool graph_editor_row_is_still_active = false; - - // load in new clips - for (int i=0;itype() == Track::kTypeVideo) { - vcontainer->setVisible(true); - layout = video_effect_layout; - } else if (c->type() == Track::kTypeAudio) { - acontainer->setVisible(true); - layout = audio_effect_layout; - } - - // Create a list of the effects we'll open - QVector effects_to_open; - - // Determine based on the current selections whether to load all effects or just the transitions - bool whole_clip_is_selected = c->IsSelected(); - - if (whole_clip_is_selected) { - for (int j=0;jeffects.size();j++) { - effects_to_open.append(c->effects.at(j).get()); - } - } - if (c->opening_transition != nullptr - && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->opening_transition.get()))) { - effects_to_open.append(c->opening_transition.get()); - } - if (c->closing_transition != nullptr - && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->closing_transition.get()))) { - effects_to_open.append(c->closing_transition.get()); - } - - for (int j=0;jGetEffect()->meta == effects_to_open.at(j)->meta - && !open_effects_.at(k)->IsAttachedToClip(c)) { - - open_effects_.at(k)->AddAdditionalEffect(effects_to_open.at(j)); - - already_opened = true; - - break; - } - } - - if (!already_opened) { - open_effect(layout, effects_to_open.at(j)); - } - - // Check if one of the open effects contains the row currently active in the graph editor. If not, we'll have - // to clear the graph editor later. - if (!graph_editor_row_is_still_active) { - for (int k=0;krow_count();k++) { - EffectRow* row = effects_to_open.at(j)->row(k); - if (row == panel_graph_editor->get_row()) { - graph_editor_row_is_still_active = true; - break; - } - } - } - } - } - - keyframeView->SetEffects(open_effects_); - - if (selected_clips_.size() > 0) { - keyframeView->setEnabled(true); - - headers->setVisible(true); - - QTimer::singleShot(50, this, SLOT(queue_post_update())); - } - - // If the graph editor's currently active row is not part of the current effects, clear it - if (!graph_editor_row_is_still_active) { - panel_graph_editor->set_row(nullptr); - } - - UpdateTitle(); - update_keyframes(); + return EffectsPanel::focused(); } void EffectControls::video_effect_click() { @@ -745,6 +468,53 @@ void EffectControls::resizeEvent(QResizeEvent*) { update_scrollbar(); } +void EffectControls::ClearEvent() +{ + keyframeView->SetEffects(open_effects_); + + vcontainer->setVisible(false); + acontainer->setVisible(false); + headers->setVisible(false); + keyframeView->setEnabled(false); + + UpdateTitle(); +} + +void EffectControls::LoadEvent() +{ + keyframeView->SetEffects(open_effects_); + + if (selected_clips_.size() > 0) { + keyframeView->setEnabled(true); + + headers->setVisible(true); + + QTimer::singleShot(50, this, SLOT(queue_post_update())); + } + + for (int i=0;iGetEffect(); + + if (e->meta->subtype == Track::kTypeVideo) { + vcontainer->setVisible(true); + layout = video_effect_layout; + } else if (e->meta->subtype == Track::kTypeAudio) { + acontainer->setVisible(true); + layout = audio_effect_layout; + } + + if (layout != nullptr) { + layout->addWidget(container); + } + } + + UpdateTitle(); + update_keyframes(); +} + EffectsArea::EffectsArea(QWidget* parent) : QWidget(parent) {} diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index aefd75ffa..fabe93116 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -37,7 +37,7 @@ #include "ui/keyframeview.h" #include "ui/resizablescrollbar.h" #include "ui/keyframeview.h" -#include "ui/panel.h" +#include "effectspanel.h" #include "ui/effectui.h" class EffectsArea : public QWidget { @@ -53,21 +53,12 @@ public slots: void receive_wheel_event(QWheelEvent* e); }; -class EffectControls : public Panel +class EffectControls : public EffectsPanel { Q_OBJECT public: explicit EffectControls(QWidget *parent = nullptr); - virtual ~EffectControls() override; - - void Reload(); - void SetClips(); - void Clear(bool clear_cache = true); - - bool IsEffectSelected(Effect* e); - - void DeleteSelectedEffects(); virtual bool focused() override; void set_zoom(bool in); void delete_selected_keyframes(); @@ -83,8 +74,6 @@ public: virtual void LoadLayoutState(const QByteArray& data) override; virtual QByteArray SaveLayoutState() override; public slots: - void cut(); - void copy(bool del = false); void update_keyframes(); private slots: void menu_select(QAction* q); @@ -94,7 +83,7 @@ private slots: void video_transition_click(); void audio_transition_click(); - void deselect_all_effects(QWidget*); + void update_scrollbar(); void queue_post_update(); @@ -102,17 +91,11 @@ private slots: void effects_area_context_menu(); protected: virtual void resizeEvent(QResizeEvent *event) override; + virtual void ClearEvent() override; + virtual void LoadEvent() override; private: - QVector selected_clips_; - QVector open_effects_; - - void Load(); - - void DeleteEffect(ComboAction* ca, Effect* effect_ref); - void show_effect_menu(int type, Track::Type subtype); void load_keyframes(); - void open_effect(QVBoxLayout* hlayout, Effect *e); void UpdateTitle(); void setup_ui(); diff --git a/panels/effectspanel.cpp b/panels/effectspanel.cpp new file mode 100644 index 000000000..a901deb83 --- /dev/null +++ b/panels/effectspanel.cpp @@ -0,0 +1,269 @@ +#include "effectspanel.h" + +#include "timeline.h" +#include "global/clipboard.h" +#include "panels.h" + +EffectsPanel::EffectsPanel(QWidget *parent) : + Panel(parent) +{ + +} + +EffectsPanel::~EffectsPanel() +{ + Clear(true); +} + +void EffectsPanel::Clear(bool clear_cache) { + // clear existing clips + deselect_all_effects(nullptr); + + for (int i=0;iIsFocused()) { + return true; + } + } + + return Panel::focused(); +} + +void EffectsPanel::ClearEvent() {} + +void EffectsPanel::LoadEvent() {} + +void EffectsPanel::Reload() { + Clear(false); + Load(); +} + +void EffectsPanel::SetClips() +{ + Clear(true); + + Sequence* top_sequence = Timeline::GetTopSequence().get(); + + if (top_sequence == nullptr) { + selected_clips_.clear(); + } else { + // replace clip vector + selected_clips_ = top_sequence->SelectedClips(false); + + Load(); + } +} + +void EffectsPanel::Load() { + bool graph_editor_row_is_still_active = false; + + // load in new clips + for (int i=0;i effects_to_open; + + // Determine based on the current selections whether to load all effects or just the transitions + bool whole_clip_is_selected = c->IsSelected(); + + if (whole_clip_is_selected) { + for (int j=0;jeffects.size();j++) { + effects_to_open.append(c->effects.at(j).get()); + } + } + if (c->opening_transition != nullptr + && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->opening_transition.get()))) { + effects_to_open.append(c->opening_transition.get()); + } + if (c->closing_transition != nullptr + && (whole_clip_is_selected || c->track()->IsTransitionSelected(c->closing_transition.get()))) { + effects_to_open.append(c->closing_transition.get()); + } + + for (int j=0;jGetEffect()->meta == effects_to_open.at(j)->meta + && !open_effects_.at(k)->IsAttachedToClip(c)) { + + open_effects_.at(k)->AddAdditionalEffect(effects_to_open.at(j)); + + already_opened = true; + + break; + } + } + + if (!already_opened) { + open_effect(effects_to_open.at(j)); + } + + // Check if one of the open effects contains the row currently active in the graph editor. If not, we'll have + // to clear the graph editor later. + if (!graph_editor_row_is_still_active) { + for (int k=0;krow_count();k++) { + EffectRow* row = effects_to_open.at(j)->row(k); + if (row == panel_graph_editor->get_row()) { + graph_editor_row_is_still_active = true; + break; + } + } + } + } + } + + // If the graph editor's currently active row is not part of the current effects, clear it + if (!graph_editor_row_is_still_active) { + panel_graph_editor->set_row(nullptr); + } + + LoadEvent(); +} + +bool EffectsPanel::IsEffectSelected(Effect *e) +{ + for (int i=0;iGetEffect() == e && open_effects_.at(i)->IsSelected()) { + return true; + } + } + return false; +} + +void EffectsPanel::deselect_all_effects(QWidget* sender) { + + for (int i=0;iheader_click(false, false); + } + } + + if (panel_sequence_viewer != nullptr) { + panel_sequence_viewer->viewer_widget()->update(); + } +} + +void EffectsPanel::cut() { + copy(true); +} + +void EffectsPanel::copy(bool del) { + bool cleared = false; + + ComboAction* ca = nullptr; + if (del) { + ca = new ComboAction(); + } + + for (int i=0;iIsSelected()) { + Effect* e = open_effects_.at(i)->GetEffect(); + + if (e->meta->type == EFFECT_TYPE_EFFECT) { + + if (!cleared) { + olive::clipboard.Clear(); + cleared = true; + olive::clipboard.SetType(Clipboard::CLIPBOARD_TYPE_EFFECT); + } + + olive::clipboard.Append(e->copy(nullptr)); + + if (del) { + + DeleteEffect(ca, e); + + } + + } + } + } + + if (del) { + if (ca->hasActions()) { + olive::undo_stack.push(ca); + } else { + delete ca; + } + } +} + +void EffectsPanel::DeleteEffect(ComboAction* ca, Effect* effect_ref) { + if (effect_ref->meta->type == EFFECT_TYPE_EFFECT) { + + ca->append(new EffectDeleteCommand(effect_ref)); + + } else if (effect_ref->meta->type == EFFECT_TYPE_TRANSITION) { + + // Retrieve shared ptr for this transition + + Clip* attached_clip = effect_ref->parent_clip; + + TransitionPtr t = nullptr; + + if (attached_clip->opening_transition.get() == effect_ref) { + + t = attached_clip->opening_transition; + + } else if (attached_clip->closing_transition.get() == effect_ref) { + + t = attached_clip->closing_transition; + + } + + if (t == nullptr) { + + qWarning() << "Failed to delete transition, couldn't find clip link."; + + } else { + + ca->append(new DeleteTransitionCommand(t)); + + } + + } +} + +void EffectsPanel::DeleteSelectedEffects() { + ComboAction* ca = new ComboAction(); + + for (int i=0;iIsSelected()) { + DeleteEffect(ca, open_effects_.at(i)->GetEffect()); + } + } + + if (ca->hasActions()) { + olive::undo_stack.push(ca); + update_ui(true); + } else { + delete ca; + } +} + +void EffectsPanel::open_effect(Effect* e) { + EffectUI* container = new EffectUI(e); + + connect(container, SIGNAL(CutRequested()), this, SLOT(cut())); + connect(container, SIGNAL(CopyRequested()), this, SLOT(copy())); + connect(container, SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*))); + + open_effects_.append(container); +} diff --git a/panels/effectspanel.h b/panels/effectspanel.h new file mode 100644 index 000000000..cb1617b7a --- /dev/null +++ b/panels/effectspanel.h @@ -0,0 +1,41 @@ +#ifndef EFFECTSPANEL_H +#define EFFECTSPANEL_H + +#include "ui/panel.h" +#include "ui/effectui.h" +#include "timeline/clip.h" + +class EffectsPanel : public Panel +{ + Q_OBJECT +public: + explicit EffectsPanel(QWidget *parent = nullptr); + virtual ~EffectsPanel() override; + + void Reload(); + void SetClips(); + void Clear(bool clear_cache = true); + + virtual bool focused() override; + + bool IsEffectSelected(Effect* e); + void DeleteSelectedEffects(); + +public slots: + void cut(); + void copy(bool del = false); +protected: + virtual void ClearEvent(); + virtual void LoadEvent(); + + QVector selected_clips_; + QVector open_effects_; +private: + void Load(); + void open_effect(Effect *e); + void DeleteEffect(ComboAction* ca, Effect* effect_ref); +private slots: + void deselect_all_effects(QWidget*); +}; + +#endif // EFFECTSPANEL_H diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 1eb5906d8..3ea682bea 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -3,9 +3,11 @@ #include #include #include +#include +#include NodeEditor::NodeEditor(QWidget *parent) : - Panel(parent), + EffectsPanel(parent), view_(&scene_) { resize(720, 480); @@ -18,12 +20,25 @@ NodeEditor::NodeEditor(QWidget *parent) : view_.setInteractive(true); view_.setDragMode(QGraphicsView::RubberBandDrag); - - QPushButton* push_button = new QPushButton("heck!!!"); - scene_.addWidget(push_button); + connect(&view_, SIGNAL(ScrollChanged(qreal, qreal)), this, SLOT(Scroll(qreal, qreal))); } void NodeEditor::Retranslate() { } + +void NodeEditor::LoadEvent() +{ + for (int i=0;imoveBy(x, y); + } +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index eaf77c586..b395fbc0b 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -3,19 +3,27 @@ #include -#include "ui/panel.h" +#include "effectspanel.h" #include "ui/nodeview.h" +#include "ui/nodeui.h" -class NodeEditor : public Panel { +class NodeEditor : public EffectsPanel { Q_OBJECT public: NodeEditor(QWidget* parent = nullptr); virtual void Retranslate() override; +protected: + virtual void LoadEvent() override; + private: QGraphicsScene scene_; NodeView view_; + QVector nodes_; + +private slots: + void Scroll(qreal x, qreal y); }; diff --git a/panels/panels.cpp b/panels/panels.cpp index 60ec67816..2145e7478 100644 --- a/panels/panels.cpp +++ b/panels/panels.cpp @@ -41,6 +41,7 @@ NodeEditor* panel_node_editor = nullptr; void update_ui(bool modified) { if (modified) { panel_effect_controls->SetClips(); + panel_node_editor->SetClips(); } panel_effect_controls->update_keyframes(); for (int i=0;i +#include +#include +#include +#include +#include +#include +#include +#include + +NodeUI::NodeUI() : + central_widget_(this) +{ + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); +} + +void NodeUI::AddToScene(QGraphicsScene *scene) +{ + scene->addItem(this); + + QGraphicsProxyWidget* proxy = scene->addWidget(¢ral_widget_); + proxy->setPos(pos()); + proxy->setParentItem(this); +} + +void NodeUI::Resize(const QSize &s) +{ + QRectF rectangle = rect(); + + rectangle.setSize(s); + + setRect(rectangle); +} + +void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + QPalette palette = qApp->palette(); + + if (option->state & QStyle::State_Selected) { + painter->setPen(palette.highlight().color()); + } else { + painter->setPen(palette.base().color()); + } + painter->setBrush(palette.window()); + + QRectF r = rect(); + r.setX(r.x() - 1); + r.setY(r.y() - 1); + r.setRight(r.right() + 1); + r.setBottom(r.bottom() + 1); + painter->drawRect(r); +} diff --git a/ui/nodeui.h b/ui/nodeui.h new file mode 100644 index 000000000..47e410125 --- /dev/null +++ b/ui/nodeui.h @@ -0,0 +1,21 @@ +#ifndef NODEUI_H +#define NODEUI_H + +#include +#include + +#include "ui/nodewidget.h" + +class NodeUI : public QGraphicsRectItem { +public: + NodeUI(); + void AddToScene(QGraphicsScene* scene); + void Resize(const QSize& s); +protected: + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + +private: + NodeWidget central_widget_; +}; + +#endif // NODEUI_H diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 16593f328..db20fb80a 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -9,20 +9,36 @@ NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : { setMouseTracking(true); setWindowTitle(tr("Node Editor")); + + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } void NodeView::mousePressEvent(QMouseEvent *event) { - QGraphicsView::mousePressEvent(event); + if (event->button() == Qt::RightButton) { + hand_moving_ = true; + drag_start_ = event->pos(); + } else { + QGraphicsView::mousePressEvent(event); + } } void NodeView::mouseMoveEvent(QMouseEvent *event) { - QGraphicsView::mouseMoveEvent(event); + if (hand_moving_) { + QPointF scene_delta = mapToScene(event->pos() - drag_start_) - mapToScene(0.0, 0.0); + emit ScrollChanged(scene_delta.x(), scene_delta.y()); + drag_start_ = event->pos(); + } else { + QGraphicsView::mouseMoveEvent(event); + } } void NodeView::mouseReleaseEvent(QMouseEvent *event) { + hand_moving_ = false; + QGraphicsView::mouseReleaseEvent(event); } void NodeView::wheelEvent(QWheelEvent *event) diff --git a/ui/nodeview.h b/ui/nodeview.h index bd751aa4c..61ef0ccdb 100644 --- a/ui/nodeview.h +++ b/ui/nodeview.h @@ -8,6 +8,9 @@ class NodeView : public QGraphicsView { public: NodeView(QGraphicsScene *scene, QWidget* parent = nullptr); +signals: + void ScrollChanged(qreal x, qreal y); + protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/ui/nodewidget.cpp b/ui/nodewidget.cpp new file mode 100644 index 000000000..6a4c3fa07 --- /dev/null +++ b/ui/nodewidget.cpp @@ -0,0 +1,46 @@ +#include "nodewidget.h" + +#include +#include +#include +#include +#include +#include + +#include "ui/nodeui.h" + +NodeWidget::NodeWidget(NodeUI *parent) : + parent_(parent) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + QLabel* title = new QLabel("Node"); + layout->addWidget(title); + + QGridLayout* grid_layout = new QGridLayout(); + + grid_layout->addWidget(new QLabel("Option 1"), 0, 0); + grid_layout->addWidget(new QLineEdit("Value 1"), 0, 1); + + grid_layout->addWidget(new QLabel("Option 2"), 1, 0); + grid_layout->addWidget(new QLineEdit("Value 2"), 1, 1); + + layout->addLayout(grid_layout); +} + +void NodeWidget::resizeEvent(QResizeEvent *event) +{ + parent_->Resize(event->size()); +} + +bool NodeWidget::event(QEvent *event) +{ + if ((event->type() == QEvent::MouseButtonPress + || event->type() == QEvent::MouseButtonRelease + || event->type() == QEvent::MouseMove + || event->type() == QEvent::MouseButtonDblClick) + && parent_->scene()->sendEvent(parent_, event)) { + return true; + } + return QWidget::event(event); +} diff --git a/ui/nodewidget.h b/ui/nodewidget.h new file mode 100644 index 000000000..16271563f --- /dev/null +++ b/ui/nodewidget.h @@ -0,0 +1,20 @@ +#ifndef NODEWIDGET_H +#define NODEWIDGET_H + +#include + +class NodeUI; + +class NodeWidget : public QWidget +{ + Q_OBJECT +public: + NodeWidget(NodeUI *parent); +protected: + virtual void resizeEvent(QResizeEvent *event) override; + virtual bool event(QEvent *event) override; +private: + NodeUI* parent_; +}; + +#endif // NODEWIDGET_H From 81da3228bb3708a8dc857c7aa175fbde2d3e1fad Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 8 Apr 2019 23:47:05 +1000 Subject: [PATCH 098/133] effect UIs ported to nodes --- olive.pro | 2 - panels/effectspanel.cpp | 2 +- panels/nodeeditor.cpp | 25 +++++++++- panels/nodeeditor.h | 1 + ui/collapsiblewidget.cpp | 98 ++++++++++++++++++++++++++++++---------- ui/collapsiblewidget.h | 22 ++++++--- ui/effectui.cpp | 30 +++++++++++- ui/effectui.h | 12 +++++ ui/nodeui.cpp | 40 +++++++++++----- ui/nodeui.h | 10 ++-- ui/nodeview.cpp | 4 +- ui/nodewidget.cpp | 46 ------------------- ui/nodewidget.h | 20 -------- 13 files changed, 192 insertions(+), 120 deletions(-) delete mode 100644 ui/nodewidget.cpp delete mode 100644 ui/nodewidget.h diff --git a/olive.pro b/olive.pro index 107c4e3d8..1238fe7a6 100644 --- a/olive.pro +++ b/olive.pro @@ -190,7 +190,6 @@ SOURCES += \ nodes/medianode.cpp \ nodes/node.cpp \ ui/nodeui.cpp \ - ui/nodewidget.cpp \ panels/effectspanel.cpp HEADERS += \ @@ -338,7 +337,6 @@ HEADERS += \ nodes/medianode.h \ nodes/node.h \ ui/nodeui.h \ - ui/nodewidget.h \ panels/effectspanel.h FORMS += diff --git a/panels/effectspanel.cpp b/panels/effectspanel.cpp index a901deb83..cd99fc6d9 100644 --- a/panels/effectspanel.cpp +++ b/panels/effectspanel.cpp @@ -151,7 +151,7 @@ void EffectsPanel::deselect_all_effects(QWidget* sender) { for (int i=0;iheader_click(false, false); + open_effects_.at(i)->Deselect(); } } diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 3ea682bea..facf925e4 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -30,14 +30,37 @@ void NodeEditor::Retranslate() void NodeEditor::LoadEvent() { + nodes_.resize(open_effects_.size()); for (int i=0;iSetNodeParent(node_ui); + effect_ui->SetSelectable(false); + + node_ui->SetWidget(effect_ui); + node_ui->AddToScene(&scene_); + + nodes_[i] = node_ui; } } +void NodeEditor::ClearEvent() +{ + NodeUI* node; + + foreach (node, nodes_) { + delete node; + } + + nodes_.clear(); +} + void NodeEditor::Scroll(qreal x, qreal y) { NodeUI* node; + foreach (node, nodes_) { node->moveBy(x, y); } diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index b395fbc0b..2da7260e2 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -16,6 +16,7 @@ public: protected: virtual void LoadEvent() override; + virtual void ClearEvent() override; private: QGraphicsScene scene_; diff --git a/ui/collapsiblewidget.cpp b/ui/collapsiblewidget.cpp index 4132078b4..d8136ce91 100644 --- a/ui/collapsiblewidget.cpp +++ b/ui/collapsiblewidget.cpp @@ -29,13 +29,16 @@ #include #include #include +#include +#include #include "ui/icons.h" #include "global/debug.h" -CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) { - selected = false; - +CollapsibleWidget::CollapsibleWidget(QWidget* parent) : + QWidget(parent), + contents(nullptr) +{ layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); @@ -58,24 +61,13 @@ CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) { title_bar_layout->addStretch(); layout->addWidget(title_bar); - connect(title_bar, SIGNAL(select(bool, bool)), this, SLOT(header_click(bool, bool))); + connect(title_bar, SIGNAL(select()), this, SLOT(Selected())); set_button_icon(true); - - contents = nullptr; } -void CollapsibleWidget::header_click(bool s, bool deselect) { - selected = s; - title_bar->selected = s; - if (s) { - QPalette p = title_bar->palette(); - p.setColor(QPalette::Background, QColor(255, 255, 255, 64)); - title_bar->setPalette(p); - } else { - title_bar->setPalette(palette()); - } - if (deselect) emit deselect_others(this); +void CollapsibleWidget::Selected() { + emit deselect_others(this); } bool CollapsibleWidget::IsFocused() { @@ -96,7 +88,17 @@ void CollapsibleWidget::SetExpanded(bool s) bool CollapsibleWidget::IsSelected() { - return selected; + return title_bar->IsSelected(); +} + +void CollapsibleWidget::Deselect() +{ + title_bar->SetSelected(false); +} + +void CollapsibleWidget::SetSelectable(bool s) +{ + title_bar->SetSelectable(s); } void CollapsibleWidget::set_button_icon(bool open) { @@ -125,19 +127,65 @@ void CollapsibleWidget::on_visible_change() { SetExpanded(!IsExpanded()); } -CollapsibleWidgetHeader::CollapsibleWidgetHeader(QWidget* parent) : QWidget(parent), selected(false) { +CollapsibleWidgetHeader::CollapsibleWidgetHeader(QWidget* parent) : + QWidget(parent), + selected_(false), + selectable_(true) +{ setContextMenuPolicy(Qt::CustomContextMenu); } +bool CollapsibleWidgetHeader::IsSelected() +{ + return selected_; +} + +void CollapsibleWidgetHeader::SetSelected(bool s, bool deselect_others) +{ + selected_ = s; + + if (s) { + QPalette p = palette(); + p.setColor(QPalette::Background, QColor(255, 255, 255, 64)); + setPalette(p); + } else { + setPalette(qApp->palette()); + } + + if (deselect_others) { + emit select(); + } +} + +void CollapsibleWidgetHeader::SetSelectable(bool s) +{ + selectable_ = s; + + if (!selectable_ && selected_) { + SetSelected(false); + } +} + +bool CollapsibleWidgetHeader::event(QEvent *event) +{ + if (!selectable_ + && (event->type() == QEvent::MouseButtonPress + || event->type() == QEvent::MouseButtonRelease + || event->type() == QEvent::MouseMove + || event->type() == QEvent::MouseButtonDblClick) + && QApplication::sendEvent(parent(), event)) { + return true; + } + return QWidget::event(event); +} + void CollapsibleWidgetHeader::mousePressEvent(QMouseEvent* event) { - if (selected) { - if ((event->modifiers() & Qt::ShiftModifier)) { - selected = false; - emit select(selected, false); + if (selected_) { + if (event->modifiers() & Qt::ShiftModifier) { + SetSelected(false); } } else { - selected = true; - emit select(selected, !(event->modifiers() & Qt::ShiftModifier)); + SetSelected(true, !(event->modifiers() & Qt::ShiftModifier)); } } diff --git a/ui/collapsiblewidget.h b/ui/collapsiblewidget.h index 3ef0657ad..eca9536d1 100644 --- a/ui/collapsiblewidget.h +++ b/ui/collapsiblewidget.h @@ -34,12 +34,20 @@ class CollapsibleWidgetHeader : public QWidget { Q_OBJECT public: CollapsibleWidgetHeader(QWidget* parent = nullptr); - bool selected; + + bool IsSelected(); + void SetSelected(bool s, bool deselect_others = false); + + void SetSelectable(bool s); protected: - void mousePressEvent(QMouseEvent* event); - void paintEvent(QPaintEvent *event); + virtual bool event(QEvent *event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void paintEvent(QPaintEvent *event) override; signals: - void select(bool, bool); + void select(); +private: + bool selected_; + bool selectable_; }; class CollapsibleWidget : public QWidget @@ -53,13 +61,15 @@ public: bool IsFocused(); bool IsExpanded(); void SetExpanded(bool s); + bool IsSelected(); + void Deselect(); + void SetSelectable(bool s); protected: QCheckBox* enabled_check; CollapsibleWidgetHeader* title_bar; QWidget* contents; private: - bool selected; QLabel* header; QVBoxLayout* layout; QPushButton* collapse_button; @@ -75,7 +85,7 @@ private slots: void on_visible_change(); public slots: - void header_click(bool s, bool deselect); + void Selected(); }; #endif // COLLAPSIBLEWIDGET_H diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 676defaea..d86658584 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -30,7 +30,8 @@ #include "panels/panels.h" EffectUI::EffectUI(Effect* e) : - effect_(e) + effect_(e), + node_parent_(nullptr) { Q_ASSERT(e != nullptr); @@ -89,6 +90,8 @@ EffectUI::EffectUI(Effect* e) : ui->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); SetContents(ui); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + SetExpanded(e->IsExpanded()); connect(this, SIGNAL(visibleChanged(bool)), e, SLOT(SetExpanded(bool))); @@ -276,6 +279,31 @@ bool EffectUI::IsAttachedToClip(Clip *c) return false; } +void EffectUI::SetNodeParent(NodeUI *parent) +{ + node_parent_ = parent; +} + +void EffectUI::resizeEvent(QResizeEvent *event) +{ + if (node_parent_ != nullptr) { + node_parent_->Resize(event->size()); + } +} + +bool EffectUI::event(QEvent *event) +{ + if (node_parent_ != nullptr + && (event->type() == QEvent::MouseButtonPress + || event->type() == QEvent::MouseButtonRelease + || event->type() == QEvent::MouseMove + || event->type() == QEvent::MouseButtonDblClick) + && node_parent_->scene()->sendEvent(node_parent_, event)) { + return true; + } + return CollapsibleWidget::event(event); +} + QWidget *EffectUI::Widget(int row, int field) { return widgets_.at(row).at(field); diff --git a/ui/effectui.h b/ui/effectui.h index a1871061b..86627e96e 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -23,6 +23,7 @@ #include "collapsiblewidget.h" #include "effects/effect.h" +#include "ui/nodeui.h" /** * @brief The EffectUI class @@ -131,6 +132,12 @@ public: */ bool IsAttachedToClip(Clip* c); + void SetNodeParent(NodeUI* parent); + +protected: + virtual void resizeEvent(QResizeEvent* event) override; + virtual bool event(QEvent* event) override; + signals: /** * @brief Cut signal @@ -195,6 +202,11 @@ private: */ QVector keyframe_navigators_; + /** + * @brief Internal reference to node parent + */ + NodeUI* node_parent_; + /** * @brief Attach a KeyframeNavigator object to an EffectRow. * diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 10af09f0d..718365c46 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -10,33 +10,55 @@ #include #include +const int kRoundedRectRadius = 5; + NodeUI::NodeUI() : - central_widget_(this) + central_widget_(nullptr) { setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); } +NodeUI::~NodeUI() +{ + if (scene() != nullptr) { + scene()->removeItem(proxy_); + scene()->removeItem(this); + } +} + void NodeUI::AddToScene(QGraphicsScene *scene) { scene->addItem(this); - QGraphicsProxyWidget* proxy = scene->addWidget(¢ral_widget_); - proxy->setPos(pos()); - proxy->setParentItem(this); + if (central_widget_ != nullptr) { + proxy_ = scene->addWidget(central_widget_); + proxy_->setPos(pos() + QPoint(kRoundedRectRadius, kRoundedRectRadius)); + proxy_->setParentItem(this); + } } void NodeUI::Resize(const QSize &s) { QRectF rectangle = rect(); - rectangle.setSize(s); + rectangle.setSize(s + 2 * QSize(kRoundedRectRadius, kRoundedRectRadius)); + + path_ = QPainterPath(); + path_.addRoundedRect(rectangle, kRoundedRectRadius, kRoundedRectRadius); setRect(rectangle); } +void NodeUI::SetWidget(QWidget *widget) +{ + central_widget_ = widget; +} + void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + Q_UNUSED(widget) + QPalette palette = qApp->palette(); if (option->state & QStyle::State_Selected) { @@ -45,11 +67,5 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW painter->setPen(palette.base().color()); } painter->setBrush(palette.window()); - - QRectF r = rect(); - r.setX(r.x() - 1); - r.setY(r.y() - 1); - r.setRight(r.right() + 1); - r.setBottom(r.bottom() + 1); - painter->drawRect(r); + painter->drawPath(path_); } diff --git a/ui/nodeui.h b/ui/nodeui.h index 47e410125..1bde2f392 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -4,18 +4,20 @@ #include #include -#include "ui/nodewidget.h" - class NodeUI : public QGraphicsRectItem { public: NodeUI(); + virtual ~NodeUI() override; + void AddToScene(QGraphicsScene* scene); void Resize(const QSize& s); + void SetWidget(QWidget* widget); protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - private: - NodeWidget central_widget_; + QWidget* central_widget_; + QGraphicsProxyWidget* proxy_; + QPainterPath path_; }; #endif // NODEUI_H diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index db20fb80a..d4b780ce0 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -10,8 +10,8 @@ NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : setMouseTracking(true); setWindowTitle(tr("Node Editor")); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } void NodeView::mousePressEvent(QMouseEvent *event) diff --git a/ui/nodewidget.cpp b/ui/nodewidget.cpp deleted file mode 100644 index 6a4c3fa07..000000000 --- a/ui/nodewidget.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "nodewidget.h" - -#include -#include -#include -#include -#include -#include - -#include "ui/nodeui.h" - -NodeWidget::NodeWidget(NodeUI *parent) : - parent_(parent) -{ - QVBoxLayout* layout = new QVBoxLayout(this); - - QLabel* title = new QLabel("Node"); - layout->addWidget(title); - - QGridLayout* grid_layout = new QGridLayout(); - - grid_layout->addWidget(new QLabel("Option 1"), 0, 0); - grid_layout->addWidget(new QLineEdit("Value 1"), 0, 1); - - grid_layout->addWidget(new QLabel("Option 2"), 1, 0); - grid_layout->addWidget(new QLineEdit("Value 2"), 1, 1); - - layout->addLayout(grid_layout); -} - -void NodeWidget::resizeEvent(QResizeEvent *event) -{ - parent_->Resize(event->size()); -} - -bool NodeWidget::event(QEvent *event) -{ - if ((event->type() == QEvent::MouseButtonPress - || event->type() == QEvent::MouseButtonRelease - || event->type() == QEvent::MouseMove - || event->type() == QEvent::MouseButtonDblClick) - && parent_->scene()->sendEvent(parent_, event)) { - return true; - } - return QWidget::event(event); -} diff --git a/ui/nodewidget.h b/ui/nodewidget.h deleted file mode 100644 index 16271563f..000000000 --- a/ui/nodewidget.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NODEWIDGET_H -#define NODEWIDGET_H - -#include - -class NodeUI; - -class NodeWidget : public QWidget -{ - Q_OBJECT -public: - NodeWidget(NodeUI *parent); -protected: - virtual void resizeEvent(QResizeEvent *event) override; - virtual bool event(QEvent *event) override; -private: - NodeUI* parent_; -}; - -#endif // NODEWIDGET_H From b1bbcca88bce6dd5c3cc82e17e0e6617525211ec Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Apr 2019 00:17:52 +1000 Subject: [PATCH 099/133] added node editor title --- panels/nodeeditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index facf925e4..1d6ba9cfd 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -10,6 +10,7 @@ NodeEditor::NodeEditor(QWidget *parent) : EffectsPanel(parent), view_(&scene_) { + setWindowTitle(tr("Node Editor")); resize(720, 480); QWidget* central_widget = new QWidget(); From 433ae494adbdb2779bd0089fe0f596c7b25ef3bd Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Apr 2019 13:15:15 +1000 Subject: [PATCH 100/133] some cleanup --- dialogs/preferencesdialog.cpp | 2 +- effects/internal/audionoiseeffect.cpp | 3 +++ effects/internal/fillleftrighteffect.cpp | 24 +++++++++++------- effects/internal/paneffect.cpp | 2 ++ effects/internal/toneeffect.cpp | 3 +++ effects/internal/volumeeffect.cpp | 3 +++ global/global.cpp | 2 +- nodes/node.cpp | 7 ------ nodes/node.h | 31 ------------------------ olive.pro | 4 +-- panels/effectcontrols.cpp | 7 +----- timeline/clip.cpp | 9 ------- timeline/sequence.cpp | 7 +++++- ui/timelineview.cpp | 21 ++++++---------- 14 files changed, 45 insertions(+), 80 deletions(-) delete mode 100644 nodes/node.cpp delete mode 100644 nodes/node.h diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index f70a369b1..6fd5d609c 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -232,7 +232,7 @@ OCIO::ConstConfigRcPtr PreferencesDialog::TestOCIOConfig(const QString &url) // Check whether OCIO can load it OCIO::ConstConfigRcPtr config; try { - config = OCIO::Config::CreateFromFile(ocio_config_file->text().toUtf8()); + config = OCIO::Config::CreateFromFile(url.toUtf8()); } catch (OCIO::Exception& e) { QMessageBox::critical(this, tr("OpenColorIO Config Error"), diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index 91194f1c0..2ee515a22 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -41,6 +41,9 @@ void AudioNoiseEffect::process_audio(double timecode_start, int nb_samples, int channel_count, int type) { + + Q_UNUSED(type) + double interval = (timecode_end - timecode_start)/nb_samples; for (int i=0;iGetValueAt(timecode_start+(interval*i)) == FILL_TYPE_LEFT) { - samples[i+1] = samples[i+3]; - samples[i] = samples[i+2]; - } else { - samples[i+3] = samples[i+1]; - samples[i+2] = samples[i]; + + if (channel_count == 2) { + for (int i=0;iGetValueAt(timecode_start+(interval*i)) == FILL_TYPE_LEFT) { + samples[0][i] = samples[1][i]; + } else { + samples[1][i] = samples[0][i]; + } } } } diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 3902bef29..72a988f5c 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -43,6 +43,8 @@ void PanEffect::process_audio(double timecode_start, int channel_count, int type) { + Q_UNUSED(type) + // This has no effect on mono sources if (channel_count < 2) { return; diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index c7b23e69c..914d8ef78 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -55,6 +55,9 @@ void ToneEffect::process_audio(double timecode_start, int nb_samples, int channel_count, int type) { + + Q_UNUSED(type) + double interval = (timecode_end - timecode_start)/nb_samples; for (int i=0;isetPalette(w->style()->standardPalette()); w->setStyle(QStyleFactory::create("windowsvista")); #else - Q_UNUSED(w); + Q_UNUSED(w) #endif } diff --git a/nodes/node.cpp b/nodes/node.cpp deleted file mode 100644 index c74e03749..000000000 --- a/nodes/node.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "node.h" - -Node::Node() : - max_inputs_(INT_MAX), - max_outputs_(INT_MAX) -{ -} diff --git a/nodes/node.h b/nodes/node.h deleted file mode 100644 index 752a55b34..000000000 --- a/nodes/node.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NODE_H -#define NODE_H - -#include -#include - -class NodeInput { -public: - NodeInput(); - -private: - QString name_; - -}; - -class Node : public QObject -{ - Q_OBJECT -public: - Node(); - -private: - int max_inputs_; - QVector inputs_; - - int max_outputs_; - QVector outputs_; - -}; - -#endif // NODE_H diff --git a/olive.pro b/olive.pro index 1238fe7a6..fe3afc66e 100644 --- a/olive.pro +++ b/olive.pro @@ -48,6 +48,8 @@ system("which git") { CONFIG += c++11 +QMAKE_CXXFLAGS += -Wno-reorder + SOURCES += \ main.cpp \ ui/mainwindow.cpp \ @@ -188,7 +190,6 @@ SOURCES += \ panels/nodeeditor.cpp \ ui/nodeview.cpp \ nodes/medianode.cpp \ - nodes/node.cpp \ ui/nodeui.cpp \ panels/effectspanel.cpp @@ -335,7 +336,6 @@ HEADERS += \ panels/nodeeditor.h \ ui/nodeview.h \ nodes/medianode.h \ - nodes/node.h \ ui/nodeui.h \ panels/effectspanel.h diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index da37c1ef0..1c92954f8 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -117,12 +117,7 @@ void EffectControls::menu_select(QAction* q) { } } olive::undo_stack.push(ca); - if (effect_menu_type == EFFECT_TYPE_TRANSITION) { - update_ui(true); - } else { - Reload(); - panel_sequence_viewer->viewer_widget()->frame_update(); - } + update_ui(true); } void EffectControls::update_keyframes() { diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 822847e0e..2dcdd9f76 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -418,15 +418,6 @@ Track *Clip::track() void Clip::set_track(Track *t) { - // Ensure this clip has already been added to this track - bool found = false; - for (int i=0;iClipCount();i++) { - if (t->GetClip(i).get() == this) { - found = true; - break; - } - } - track_ = t; } diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index bb72dc772..1a1d17fe3 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -1167,6 +1167,7 @@ ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long f bool Sequence::SplitSelection(ComboAction *ca, QVector selections) { + bool ret = false; QVector all_clips = GetAllClips(); for (int i=0;i selections) } } - + if (SplitClipAtPositions(ca, c, points, false)) { + ret = true; + } } + + return ret; } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index c18d30af5..91356b676 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -402,7 +402,7 @@ void TimelineView::insert_clips(ComboAction* ca, Sequence* s) { } } - QVector sequence_clips = sequence()->GetAllClips(); + QVector sequence_clips = s->GetAllClips(); for (int i=0;itimeline_in() < earliest_new_point && c->timeline_out() > earliest_new_point) { - sequence()->SplitClipAtPositions(ca, c, {earliest_new_point}, true); + s->SplitClipAtPositions(ca, c, {earliest_new_point}, true); } // determine if we should close the gap the old clips left behind @@ -429,13 +429,13 @@ void TimelineView::insert_clips(ComboAction* ca, Sequence* s) { long ripple_length = (latest_new_point - earliest_new_point); - sequence()->Ripple(ca, earliest_new_point, ripple_length, ignore_clips); + s->Ripple(ca, earliest_new_point, ripple_length, ignore_clips); if (ripple_old_point) { // works for moving later clips earlier but not earlier to later long second_ripple_length = (earliest_old_point - latest_old_point); - sequence()->Ripple(ca, latest_old_point, second_ripple_length, ignore_clips); + s->Ripple(ca, latest_old_point, second_ripple_length, ignore_clips); if (earliest_old_point < earliest_new_point) { for (int i=0;ighosts.size();i++) { @@ -444,13 +444,13 @@ void TimelineView::insert_clips(ComboAction* ca, Sequence* s) { g.out += second_ripple_length; } - QVector sequence_selections = sequence()->Selections(); + QVector sequence_selections = s->Selections(); for (int i=0;iSetSelections(sequence_selections); + s->SetSelections(sequence_selections); } } } @@ -694,7 +694,8 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { link->track()->DeselectArea(link->timeline_in(), link->timeline_out()); } - long s_in, s_out; + long s_in = 0; + long s_out = 0; // select the transition only if (ParentTimeline()->transition_select == kTransitionOpening @@ -2504,9 +2505,6 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // used to determine whether we the cursor found a trim point or not bool found = false; - // used to determine whether the cursor is within the rect of a clip - bool cursor_contains_clip = false; - // used to determine how close the cursor is to a trim point // (and more specifically, whether another point is closer or not) long closeness = LONG_MAX; @@ -2532,9 +2530,6 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (ParentTimeline()->cursor_frame >= c->timeline_in() && ParentTimeline()->cursor_frame <= c->timeline_out()) { - // acknowledge that we are hovering over a clip - cursor_contains_clip = true; - // start a timer to show a tooltip about this clip tooltip_timer.start(); tooltip_clip = c; From 7cbcb20543fc2ac021d5b476d84a4d400d4d04e4 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Apr 2019 20:55:25 +1000 Subject: [PATCH 101/133] created enum for more node data types --- effects/effect.cpp | 48 ++++++---------- effects/effectfield.cpp | 22 +++---- effects/effectfield.h | 42 ++------------ effects/fields/boolfield.cpp | 2 +- effects/fields/buttonfield.cpp | 2 +- effects/fields/colorfield.cpp | 2 +- effects/fields/combofield.cpp | 2 +- effects/fields/doublefield.cpp | 2 +- effects/fields/filefield.cpp | 2 +- effects/fields/fontfield.cpp | 2 +- effects/fields/labelfield.cpp | 2 +- effects/fields/stringfield.cpp | 2 +- nodes/medianode.h | 4 +- nodes/nodedatatypes.cpp | 74 ++++++++++++++++++++++++ nodes/nodedatatypes.h | 102 +++++++++++++++++++++++++++++++++ olive.pro | 6 +- panels/grapheditor.cpp | 4 +- panels/nodeeditor.cpp | 10 ---- panels/nodeeditor.h | 3 - ui/graphview.cpp | 4 +- ui/nodeview.cpp | 11 +++- ui/nodeview.h | 3 - 22 files changed, 237 insertions(+), 114 deletions(-) create mode 100644 nodes/nodedatatypes.cpp create mode 100644 nodes/nodedatatypes.h diff --git a/effects/effect.cpp b/effects/effect.cpp index a7476f1dc..a94e9bb6a 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -159,21 +159,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : const QXmlStreamAttribute& attr = attributes.at(i); if (attr.name() == "type") { QString comp = attr.value().toString().toUpper(); - if (comp == "DOUBLE") { - type = EffectField::EFFECT_FIELD_DOUBLE; - } else if (comp == "BOOL") { - type = EffectField::EFFECT_FIELD_BOOL; - } else if (comp == "COLOR") { - type = EffectField::EFFECT_FIELD_COLOR; - } else if (comp == "COMBO") { - type = EffectField::EFFECT_FIELD_COMBO; - } else if (comp == "FONT") { - type = EffectField::EFFECT_FIELD_FONT; - } else if (comp == "STRING") { - type = EffectField::EFFECT_FIELD_STRING; - } else if (comp == "FILE") { - type = EffectField::EFFECT_FIELD_FILE; - } + type = olive::nodes::StringToDataType(comp); } else if (attr.name() == "id") { id = attr.value().toString(); } @@ -181,11 +167,13 @@ Effect::Effect(Clip* c, const EffectMeta *em) : if (id.isEmpty()) { qCritical() << "Couldn't load field from" << em->filename << "- ID cannot be empty."; + } else if (type == olive::nodes::kInvalid) { + qWarning() << "Invalid field type found"; } else { EffectField* field = nullptr; switch (type) { - case EffectField::EFFECT_FIELD_DOUBLE: + case olive::nodes::kFloat: { DoubleField* double_field = new DoubleField(row, id); @@ -204,7 +192,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : field = double_field; } break; - case EffectField::EFFECT_FIELD_COLOR: + case olive::nodes::kColor: { QColor color; @@ -232,7 +220,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : field->SetValueAt(0, color); } break; - case EffectField::EFFECT_FIELD_STRING: + case olive::nodes::kString: field = new StringField(row, id); for (int i=0;iField(j); if (!field->id().isEmpty()) { switch (field->type()) { - case EffectField::EFFECT_FIELD_DOUBLE: + case olive::nodes::kFloat: { DoubleField* double_field = static_cast(field); shader_program_->setUniformValue(double_field->id().toUtf8().constData(), GLfloat(double_field->GetDoubleAt(timecode))); } break; - case EffectField::EFFECT_FIELD_COLOR: + case olive::nodes::kColor: { ColorField* color_field = static_cast(field); shader_program_->setUniformValue( @@ -882,18 +870,18 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { ); } break; - case EffectField::EFFECT_FIELD_BOOL: + case olive::nodes::kBoolean: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool()); break; - case EffectField::EFFECT_FIELD_COMBO: + case olive::nodes::kCombo: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt()); break; // can you even send a string to a uniform value? - case EffectField::EFFECT_FIELD_STRING: - case EffectField::EFFECT_FIELD_FONT: - case EffectField::EFFECT_FIELD_FILE: - case EffectField::EFFECT_FIELD_UI: + case olive::nodes::kString: + case olive::nodes::kFont: + case olive::nodes::kFile: + case olive::nodes::kUI: break; } } diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 7446fbc5f..a9abd46c6 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -34,7 +34,7 @@ #include "global/math.h" #include "global/debug.h" -EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) : +EffectField::EffectField(EffectRow* parent, const QString &i, olive::nodes::DataType t) : QObject(parent), type_(t), id_(i), @@ -43,7 +43,7 @@ EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) { // EffectField MUST be created with a parent. Q_ASSERT(parent != nullptr); - Q_ASSERT(!i.isEmpty() || t == EFFECT_FIELD_UI); + Q_ASSERT(!i.isEmpty() || t == olive::nodes::kUI); // Add this field to the parent row specified parent->AddField(this); @@ -93,7 +93,7 @@ QVariant EffectField::GetValueAt(double timecode) const QVariant& before_data = keyframes.at(before_keyframe).data; switch (type_) { - case EFFECT_FIELD_DOUBLE: + case olive::nodes::kFloat: { double value; if (before_keyframe == after_keyframe) { @@ -163,7 +163,7 @@ QVariant EffectField::GetValueAt(double timecode) persistent_data_ = value; break; } - case EFFECT_FIELD_COLOR: + case olive::nodes::kColor: { QColor value; if (before_keyframe == after_keyframe) { @@ -178,11 +178,11 @@ QVariant EffectField::GetValueAt(double timecode) persistent_data_ = value; break; } - case EFFECT_FIELD_STRING: - case EFFECT_FIELD_BOOL: - case EFFECT_FIELD_COMBO: - case EFFECT_FIELD_FONT: - case EFFECT_FIELD_FILE: + case olive::nodes::kString: + case olive::nodes::kBoolean: + case olive::nodes::kCombo: + case olive::nodes::kFont: + case olive::nodes::kFile: persistent_data_ = before_data; break; default: @@ -274,7 +274,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) } } -const EffectField::EffectFieldType &EffectField::type() +const olive::nodes::DataType &EffectField::type() { return type_; } @@ -360,7 +360,7 @@ void EffectField::GetKeyframeData(double timecode, int &before, int &after, doub } } - if ((type_ == EFFECT_FIELD_DOUBLE || type_ == EFFECT_FIELD_COLOR) + if ((type_ == olive::nodes::kFloat || type_ == olive::nodes::kColor) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { // interpolate before = before_keyframe_index; diff --git a/effects/effectfield.h b/effects/effectfield.h index 30b74e53e..1e97a074e 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -28,6 +28,7 @@ #include "effects/keyframe.h" #include "undo/undo.h" #include "undo/undostack.h" +#include "nodes/nodedatatypes.h" class EffectRow; class ComboAction; @@ -56,39 +57,6 @@ class ComboAction; class EffectField : public QObject { Q_OBJECT public: - /** - * @brief The EffectFieldType enum - * - * Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt(). - * - * This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g. - * EFFECT_FIELD_DOUBLE matches to DoubleField). - */ - enum EffectFieldType { - /** Values are doubles. Also corresponds to DoubleField. */ - EFFECT_FIELD_DOUBLE, - - /** Values are colors. Also corresponds to ColorField. */ - EFFECT_FIELD_COLOR, - - /** Values are strings. Also corresponds to StringField. */ - EFFECT_FIELD_STRING, - - /** Values are booleans. Also corresponds to BoolField. */ - EFFECT_FIELD_BOOL, - - /** Values are arbitrary data. Also corresponds to ComboField. */ - EFFECT_FIELD_COMBO, - - /** Values are font family names (in string). Also corresponds to FontField. */ - EFFECT_FIELD_FONT, - - /** Values are filenames (in string). Also corresponds to FileField. */ - EFFECT_FIELD_FILE, - - /** Values is a UI object with no data. Corresponds to nothing. */ - EFFECT_FIELD_UI - }; /** * @brief EffectField Constructor @@ -112,7 +80,7 @@ public: * * The type of data contained within this field. This is expected to be filled by a derived class. */ - EffectField(EffectRow* parent, const QString& i, EffectFieldType t); + EffectField(EffectRow* parent, const QString& i, olive::nodes::DataType t); /** * @brief Get the EffectRow that this field is a member of. @@ -130,9 +98,9 @@ public: * * @return * - * A member of the EffectFieldType enum. + * A member of the olive::nodes::DataType enum. */ - const EffectFieldType& type(); + const olive::nodes::DataType& type(); /** * @brief Get the unique identifier of this field set in the constructor @@ -430,7 +398,7 @@ private: /** * @brief Internal type variable set in the constructor. Access with type(). */ - EffectFieldType type_; + olive::nodes::DataType type_; /** * @brief Internal unique identifier for this field set in the constructor. Access with id(). diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index 13c958912..1cd44a96b 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -23,7 +23,7 @@ #include BoolField::BoolField(EffectRow *parent, const QString &id) : - EffectField(parent, id, EFFECT_FIELD_BOOL) + EffectField(parent, id, olive::nodes::kBoolean) {} bool BoolField::GetBoolAt(double timecode) diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index 77025b17a..3cb5f1e23 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -23,7 +23,7 @@ #include ButtonField::ButtonField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, EFFECT_FIELD_UI), + EffectField(parent, nullptr, olive::nodes::kUI), button_text_(string) {} diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index b294f9e78..109a4b4c1 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -25,7 +25,7 @@ #include "ui/colorbutton.h" ColorField::ColorField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EFFECT_FIELD_COLOR) + EffectField(parent, id, olive::nodes::kColor) {} QColor ColorField::GetColorAt(double timecode) diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 133e704d3..08c6a52a5 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -25,7 +25,7 @@ #include "ui/comboboxex.h" ComboField::ComboField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EFFECT_FIELD_COMBO) + EffectField(parent, id, olive::nodes::kCombo) {} void ComboField::AddItem(const QString &text, const QVariant &data) diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 58603d4b0..537b2bebb 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -23,7 +23,7 @@ #include "effects/effectrow.h" DoubleField::DoubleField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EFFECT_FIELD_DOUBLE), + EffectField(parent, id, olive::nodes::kFloat), min_(qSNaN()), max_(qSNaN()), default_(0), diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index b40eede59..89e74d119 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -25,7 +25,7 @@ #include "ui/embeddedfilechooser.h" FileField::FileField(EffectRow* parent, const QString &id) : - EffectField(parent, id, EFFECT_FIELD_FILE) + EffectField(parent, id, olive::nodes::kFile) { // Set default value to an empty string SetValueAt(0, ""); diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 5876bdd78..6618c683d 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -28,7 +28,7 @@ // NOTE/TODO: This shares a lot of similarity with ComboField, and could probably be a derived class of it FontField::FontField(EffectRow* parent, const QString &id) : - EffectField(parent, id, EFFECT_FIELD_FONT) + EffectField(parent, id, olive::nodes::kFont) { font_list = QFontDatabase().families(); diff --git a/effects/fields/labelfield.cpp b/effects/fields/labelfield.cpp index 231c1ee08..459ec10dc 100644 --- a/effects/fields/labelfield.cpp +++ b/effects/fields/labelfield.cpp @@ -23,7 +23,7 @@ #include LabelField::LabelField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, EFFECT_FIELD_UI), + EffectField(parent, nullptr, olive::nodes::kUI), label_text_(string) {} diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 57439473a..fcdc65cf4 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -27,7 +27,7 @@ #include "global/config.h" StringField::StringField(EffectRow* parent, const QString& id, bool rich_text) : - EffectField(parent, id, EFFECT_FIELD_STRING), + EffectField(parent, id, olive::nodes::kString), rich_text_(rich_text) { // Set default value to an empty string diff --git a/nodes/medianode.h b/nodes/medianode.h index 96dd94ecc..7fb634375 100644 --- a/nodes/medianode.h +++ b/nodes/medianode.h @@ -1,9 +1,7 @@ #ifndef MEDIANODE_H #define MEDIANODE_H -#include "node.h" - -class MediaNode : public Node +class MediaNode { public: MediaNode(); diff --git a/nodes/nodedatatypes.cpp b/nodes/nodedatatypes.cpp new file mode 100644 index 000000000..53636cc7d --- /dev/null +++ b/nodes/nodedatatypes.cpp @@ -0,0 +1,74 @@ +#include "nodedatatypes.h" + +QString olive::nodes::DataTypeToString(DataType type) { + switch (type) { + case kFloat: + return "DOUBLE"; + case kVec2: + return "VEC2"; + case kVec3: + return "VEC3"; + case kVec4: + return "VEC4"; + case kArray: + return "ARRAY"; + case kColor: + return "COLOR"; + case kString: + return "STRING"; + case kBoolean: + return "BOOL"; + case kCombo: + return "COMBO"; + case kFont: + return "FONT"; + case kFile: + return "FILE"; + case kInteger: + return "INTEGER"; + case kTexture: + return "TEXTURE"; + case kMatrix: + return "MATRIX"; + case kUI: + return "UI"; + default: + return QString(); + } +} + +olive::nodes::DataType olive::nodes::StringToDataType(const QString &s) +{ + if (s == "DOUBLE") { + return kFloat; + } else if (s == "VEC2") { + return kVec2; + } else if (s == "VEC3") { + return kVec3; + } else if (s == "VEC4") { + return kVec4; + } else if (s == "ARRAY") { + return kArray; + } else if (s == "COLOR") { + return kColor; + } else if (s == "STRING") { + return kString; + } else if (s == "BOOL") { + return kBoolean; + } else if (s == "COMBO") { + return kCombo; + } else if (s == "FONT") { + return kFont; + } else if (s == "FILE") { + return kFile; + } else if (s == "INTEGER") { + return kInteger; + } else if (s == "TEXTURE") { + return kTexture; + } else if (s == "MATRIX") { + return kMatrix; + } else if (s == "UI") { + return kUI; + } + return kInvalid; +} diff --git a/nodes/nodedatatypes.h b/nodes/nodedatatypes.h new file mode 100644 index 000000000..ebf20eadf --- /dev/null +++ b/nodes/nodedatatypes.h @@ -0,0 +1,102 @@ +#ifndef NODEDATATYPES_H +#define NODEDATATYPES_H + +#include + +namespace olive { +namespace nodes { + +/** + * @brief The EffectFieldType enum + * + * Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt(). + * + * This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g. + * EFFECT_FIELD_DOUBLE matches to DoubleField). + */ +enum DataType { + /** Invalid data type. Used only for error handling. */ + kInvalid, + + /** Values are doubles. Also corresponds to DoubleField. */ + kFloat, + + /** Value is an 2-component vector of floats. */ + kVec2, + + /** Value is an 3-component vector of floats. */ + kVec3, + + /** Value is an 4-component vector of floats. */ + kVec4, + + /** Value is an array of floats. This cannot be an input field, and can only be passed between nodes. */ + kArray, + + /** Values are colors. Equivalent to kVec4 but represents as a color. Corresponds to ColorField. */ + kColor, + + /** Values are strings. Also corresponds to StringField. */ + kString, + + /** Values are booleans. Also corresponds to BoolField. */ + kBoolean, + + /** Values are arbitrary data. Also corresponds to ComboField. */ + kCombo, + + /** Values are font family names (in string). Also corresponds to FontField. */ + kFont, + + /** Values are filenames (in string). Also corresponds to FileField. */ + kFile, + + /** Values are integers. */ + kInteger, + + /** Value is a texture. This cannot be an input field, and can only be passed between nodes. */ + kTexture, + + /** Value is a 4x4 matrix. This cannot be an input field, and can only be passed between nodes. */ + kMatrix, + + /** Values is a UI object with no data. Corresponds to nothing. */ + kUI, + + /** Total count of valid node data types. Never use this as an actual data type. */ + kDataTypeCount +}; + +/** + * @brief Convert a node data type to a unique string that can be saved to an XML file + * + * @param type + * + * The data type to convert to string + * + * @return + * + * The unique string identifier for this data type. + */ +QString DataTypeToString(DataType type); + +/** + * @brief Convert a string to a node data type. + * + * Generally this string should be a string that was received from DataTypeToString() to ensure compatibility and + * correctness. + * + * @param s + * + * The string to convert to a data type + * + * @return + * + * The data type that this string represents + */ +DataType StringToDataType(const QString& s); + +} +} + +#endif // NODEDATATYPES_H diff --git a/olive.pro b/olive.pro index fe3afc66e..cde79eb5e 100644 --- a/olive.pro +++ b/olive.pro @@ -191,7 +191,8 @@ SOURCES += \ ui/nodeview.cpp \ nodes/medianode.cpp \ ui/nodeui.cpp \ - panels/effectspanel.cpp + panels/effectspanel.cpp \ + nodes/nodedatatypes.cpp HEADERS += \ ui/mainwindow.h \ @@ -337,7 +338,8 @@ HEADERS += \ ui/nodeview.h \ nodes/medianode.h \ ui/nodeui.h \ - panels/effectspanel.h + panels/effectspanel.h \ + nodes/nodedatatypes.h FORMS += diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index e664b8aba..6430a80fd 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -149,7 +149,7 @@ void GraphEditor::update_panel() { int slider_index = 0; for (int i=0;iFieldCount();i++) { EffectField* field = row->Field(i); - if (field->type() == EffectField::EFFECT_FIELD_DOUBLE) { + if (field->type() == olive::nodes::kFloat) { field->UpdateWidgetValue(field_sliders_.at(slider_index), field->Now()); slider_index++; } @@ -186,7 +186,7 @@ void GraphEditor::set_row(EffectRow *r) { if (r != nullptr && r->IsKeyframing()) { for (int i=0;iFieldCount();i++) { EffectField* field = r->Field(i); - if (field->type() == EffectField::EFFECT_FIELD_DOUBLE) { + if (field->type() == olive::nodes::kFloat) { QPushButton* slider_button = new QPushButton(); slider_button->setCheckable(true); slider_button->setChecked(field->IsEnabled()); diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 1d6ba9cfd..a4e8823d3 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -21,7 +21,6 @@ NodeEditor::NodeEditor(QWidget *parent) : view_.setInteractive(true); view_.setDragMode(QGraphicsView::RubberBandDrag); - connect(&view_, SIGNAL(ScrollChanged(qreal, qreal)), this, SLOT(Scroll(qreal, qreal))); } void NodeEditor::Retranslate() @@ -57,12 +56,3 @@ void NodeEditor::ClearEvent() nodes_.clear(); } - -void NodeEditor::Scroll(qreal x, qreal y) -{ - NodeUI* node; - - foreach (node, nodes_) { - node->moveBy(x, y); - } -} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index 2da7260e2..a404629c3 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -23,9 +23,6 @@ private: NodeView view_; QVector nodes_; -private slots: - void Scroll(qreal x, qreal y); - }; #endif // NODEEDITOR_H diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 5885d7f76..72b91848e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -244,7 +244,7 @@ void GraphView::paintEvent(QPaintEvent *) { for (int i=row->FieldCount()-1;i>=0;i--) { EffectField* field = row->Field(i); - if (field->type() == EffectField::EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + if (field->type() == olive::nodes::kFloat && field_visibility.at(i)) { // sort keyframes by time QVector sorted_keys = sort_keys_from_field(field); @@ -386,7 +386,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } else { for (int i=0;iFieldCount();i++) { EffectField* field = row->Field(i); - if (field->type() == EffectField::EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { + if (field->type() == olive::nodes::kFloat && field_visibility.at(i)) { for (int j=0;jkeyframes.size();j++) { const EffectKeyframe& key = field->keyframes.at(j); int key_x = get_screen_x(key.time); diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index d4b780ce0..25b40ef36 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -1,6 +1,7 @@ #include "nodeview.h" #include +#include #include NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : @@ -27,8 +28,14 @@ void NodeView::mousePressEvent(QMouseEvent *event) void NodeView::mouseMoveEvent(QMouseEvent *event) { if (hand_moving_) { - QPointF scene_delta = mapToScene(event->pos() - drag_start_) - mapToScene(0.0, 0.0); - emit ScrollChanged(scene_delta.x(), scene_delta.y()); + //QPointF scene_delta = mapToScene(event->pos() - drag_start_) - mapToScene(0.0, 0.0); + //emit ScrollChanged(scene_delta.x(), scene_delta.y()); + + QPoint delta = event->pos() - drag_start_; + + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + delta.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() + delta.y()); + drag_start_ = event->pos(); } else { QGraphicsView::mouseMoveEvent(event); diff --git a/ui/nodeview.h b/ui/nodeview.h index 61ef0ccdb..bd751aa4c 100644 --- a/ui/nodeview.h +++ b/ui/nodeview.h @@ -8,9 +8,6 @@ class NodeView : public QGraphicsView { public: NodeView(QGraphicsScene *scene, QWidget* parent = nullptr); -signals: - void ScrollChanged(qreal x, qreal y); - protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; From a4ff6f3b41394bcc14b0f15aa639488104cc6596 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Apr 2019 21:32:21 +1000 Subject: [PATCH 102/133] adjust scrollbars for hand moving --- ui/nodeview.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 25b40ef36..3a3883e5e 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -17,7 +17,7 @@ NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : void NodeView::mousePressEvent(QMouseEvent *event) { - if (event->button() == Qt::RightButton) { + if (event->button() == Qt::MidButton) { hand_moving_ = true; drag_start_ = event->pos(); } else { @@ -28,13 +28,10 @@ void NodeView::mousePressEvent(QMouseEvent *event) void NodeView::mouseMoveEvent(QMouseEvent *event) { if (hand_moving_) { - //QPointF scene_delta = mapToScene(event->pos() - drag_start_) - mapToScene(0.0, 0.0); - //emit ScrollChanged(scene_delta.x(), scene_delta.y()); - QPoint delta = event->pos() - drag_start_; - horizontalScrollBar()->setValue(horizontalScrollBar()->value() + delta.x()); - verticalScrollBar()->setValue(verticalScrollBar()->value() + delta.y()); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y()); drag_start_ = event->pos(); } else { From a03879a12baa9df13d8ec07052209f4c04bdc4aa Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 9 Apr 2019 23:06:26 +1000 Subject: [PATCH 103/133] restored effect field types --- effects/effectfield.cpp | 6 ++--- effects/effectfield.h | 42 ++++++++++++++++++++++++++++++---- effects/fields/boolfield.cpp | 2 +- effects/fields/buttonfield.cpp | 2 +- effects/fields/colorfield.cpp | 2 +- effects/fields/combofield.cpp | 2 +- effects/fields/doublefield.cpp | 2 +- effects/fields/filefield.cpp | 2 +- effects/fields/fontfield.cpp | 2 +- effects/fields/labelfield.cpp | 2 +- effects/fields/stringfield.cpp | 2 +- nodes/nodeplug.cpp | 11 +++++++++ nodes/nodeplug.h | 13 +++++++++++ olive.pro | 6 +++-- ui/nodeview.cpp | 6 +++-- 15 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 nodes/nodeplug.cpp create mode 100644 nodes/nodeplug.h diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index a9abd46c6..997296628 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -34,7 +34,7 @@ #include "global/math.h" #include "global/debug.h" -EffectField::EffectField(EffectRow* parent, const QString &i, olive::nodes::DataType t) : +EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) : QObject(parent), type_(t), id_(i), @@ -43,7 +43,7 @@ EffectField::EffectField(EffectRow* parent, const QString &i, olive::nodes::Data { // EffectField MUST be created with a parent. Q_ASSERT(parent != nullptr); - Q_ASSERT(!i.isEmpty() || t == olive::nodes::kUI); + Q_ASSERT(!i.isEmpty() || t == EFFECT_FIELD_UI); // Add this field to the parent row specified parent->AddField(this); @@ -274,7 +274,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) } } -const olive::nodes::DataType &EffectField::type() +const EffectField::EffectFieldType &EffectField::type() { return type_; } diff --git a/effects/effectfield.h b/effects/effectfield.h index 1e97a074e..2b94ed99e 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -58,6 +58,40 @@ class EffectField : public QObject { Q_OBJECT public: + /** + * @brief The EffectFieldType enum + * + * Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt(). + * + * This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g. + * EFFECT_FIELD_DOUBLE matches to DoubleField). + */ + enum EffectFieldType { + /** Values are doubles. Also corresponds to DoubleField. */ + EFFECT_FIELD_DOUBLE, + + /** Values are colors. Also corresponds to ColorField. */ + EFFECT_FIELD_COLOR, + + /** Values are strings. Also corresponds to StringField. */ + EFFECT_FIELD_STRING, + + /** Values are booleans. Also corresponds to BoolField. */ + EFFECT_FIELD_BOOL, + + /** Values are arbitrary data. Also corresponds to ComboField. */ + EFFECT_FIELD_COMBO, + + /** Values are font family names (in string). Also corresponds to FontField. */ + EFFECT_FIELD_FONT, + + /** Values are filenames (in string). Also corresponds to FileField. */ + EFFECT_FIELD_FILE, + + /** Values is a UI object with no data. Corresponds to nothing. */ + EFFECT_FIELD_UI + }; + /** * @brief EffectField Constructor * @@ -80,7 +114,7 @@ public: * * The type of data contained within this field. This is expected to be filled by a derived class. */ - EffectField(EffectRow* parent, const QString& i, olive::nodes::DataType t); + EffectField(EffectRow* parent, const QString& i, EffectFieldType t); /** * @brief Get the EffectRow that this field is a member of. @@ -98,9 +132,9 @@ public: * * @return * - * A member of the olive::nodes::DataType enum. + * A member of the EffectFieldType enum. */ - const olive::nodes::DataType& type(); + const EffectFieldType& type(); /** * @brief Get the unique identifier of this field set in the constructor @@ -398,7 +432,7 @@ private: /** * @brief Internal type variable set in the constructor. Access with type(). */ - olive::nodes::DataType type_; + EffectFieldType type_; /** * @brief Internal unique identifier for this field set in the constructor. Access with id(). diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index 1cd44a96b..0dfffd4cf 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -23,7 +23,7 @@ #include BoolField::BoolField(EffectRow *parent, const QString &id) : - EffectField(parent, id, olive::nodes::kBoolean) + EffectField(parent, id, EffectField::EFFECT_FIELD_BOOL) {} bool BoolField::GetBoolAt(double timecode) diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index 3cb5f1e23..c03c0e1ae 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -23,7 +23,7 @@ #include ButtonField::ButtonField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, olive::nodes::kUI), + EffectField(parent, nullptr, EffectField::EFFECT_FIELD_UI), button_text_(string) {} diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index 109a4b4c1..001bb7d77 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -25,7 +25,7 @@ #include "ui/colorbutton.h" ColorField::ColorField(EffectRow* parent, const QString& id) : - EffectField(parent, id, olive::nodes::kColor) + EffectField(parent, id, EffectField::EFFECT_FIELD_COLOR) {} QColor ColorField::GetColorAt(double timecode) diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 08c6a52a5..992c46f14 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -25,7 +25,7 @@ #include "ui/comboboxex.h" ComboField::ComboField(EffectRow* parent, const QString& id) : - EffectField(parent, id, olive::nodes::kCombo) + EffectField(parent, id, EffectField::EFFECT_FIELD_COMBO) {} void ComboField::AddItem(const QString &text, const QVariant &data) diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 537b2bebb..4f273157d 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -23,7 +23,7 @@ #include "effects/effectrow.h" DoubleField::DoubleField(EffectRow* parent, const QString& id) : - EffectField(parent, id, olive::nodes::kFloat), + EffectField(parent, id, EffectField::EFFECT_FIELD_DOUBLE), min_(qSNaN()), max_(qSNaN()), default_(0), diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index 89e74d119..b9a21bbef 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -25,7 +25,7 @@ #include "ui/embeddedfilechooser.h" FileField::FileField(EffectRow* parent, const QString &id) : - EffectField(parent, id, olive::nodes::kFile) + EffectField(parent, id, EffectField::EFFECT_FIELD_FILE) { // Set default value to an empty string SetValueAt(0, ""); diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 6618c683d..21c741389 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -28,7 +28,7 @@ // NOTE/TODO: This shares a lot of similarity with ComboField, and could probably be a derived class of it FontField::FontField(EffectRow* parent, const QString &id) : - EffectField(parent, id, olive::nodes::kFont) + EffectField(parent, id, EffectField::EFFECT_FIELD_FONT) { font_list = QFontDatabase().families(); diff --git a/effects/fields/labelfield.cpp b/effects/fields/labelfield.cpp index 459ec10dc..117683337 100644 --- a/effects/fields/labelfield.cpp +++ b/effects/fields/labelfield.cpp @@ -23,7 +23,7 @@ #include LabelField::LabelField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, olive::nodes::kUI), + EffectField(parent, nullptr, EffectField::EFFECT_FIELD_UI), label_text_(string) {} diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index fcdc65cf4..e228ea729 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -27,7 +27,7 @@ #include "global/config.h" StringField::StringField(EffectRow* parent, const QString& id, bool rich_text) : - EffectField(parent, id, olive::nodes::kString), + EffectField(parent, id, EffectField::EFFECT_FIELD_STRING), rich_text_(rich_text) { // Set default value to an empty string diff --git a/nodes/nodeplug.cpp b/nodes/nodeplug.cpp new file mode 100644 index 000000000..bb60f8397 --- /dev/null +++ b/nodes/nodeplug.cpp @@ -0,0 +1,11 @@ +#include "nodeplug.h" + +NodePlug::NodePlug() +{ + +} + +bool NodePlug::IsConnected() +{ + +} diff --git a/nodes/nodeplug.h b/nodes/nodeplug.h new file mode 100644 index 000000000..764afc716 --- /dev/null +++ b/nodes/nodeplug.h @@ -0,0 +1,13 @@ +#ifndef NODEPLUG_H +#define NODEPLUG_H + + +class NodePlug +{ +public: + NodePlug(); + + bool IsConnected(); +}; + +#endif // NODEPLUG_H diff --git a/olive.pro b/olive.pro index cde79eb5e..e9195da77 100644 --- a/olive.pro +++ b/olive.pro @@ -192,7 +192,8 @@ SOURCES += \ nodes/medianode.cpp \ ui/nodeui.cpp \ panels/effectspanel.cpp \ - nodes/nodedatatypes.cpp + nodes/nodedatatypes.cpp \ + nodes/nodeplug.cpp HEADERS += \ ui/mainwindow.h \ @@ -339,7 +340,8 @@ HEADERS += \ nodes/medianode.h \ ui/nodeui.h \ panels/effectspanel.h \ - nodes/nodedatatypes.h + nodes/nodedatatypes.h \ + nodes/nodeplug.h FORMS += diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 3a3883e5e..f3c8a9ea7 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -41,8 +41,10 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) void NodeView::mouseReleaseEvent(QMouseEvent *event) { - hand_moving_ = false; - QGraphicsView::mouseReleaseEvent(event); + if (!hand_moving_) { + hand_moving_ = false; + QGraphicsView::mouseReleaseEvent(event); + } } void NodeView::wheelEvent(QWheelEvent *event) From c281e5a6ffac1d53297ec1c1d26c9dc84ef56a3a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 10 Apr 2019 09:00:25 +1000 Subject: [PATCH 104/133] fixed node type issues --- effects/effect.cpp | 16 ++++++++-------- effects/effectfield.cpp | 16 ++++++++-------- panels/nodeeditor.cpp | 1 - ui/graphview.cpp | 4 ++-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index a94e9bb6a..27608986c 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -852,14 +852,14 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { EffectField* field = row->Field(j); if (!field->id().isEmpty()) { switch (field->type()) { - case olive::nodes::kFloat: + case EFFECT_FIELD_DOUBLE: { DoubleField* double_field = static_cast(field); shader_program_->setUniformValue(double_field->id().toUtf8().constData(), GLfloat(double_field->GetDoubleAt(timecode))); } break; - case olive::nodes::kColor: + case EFFECT_FIELD_COLOR: { ColorField* color_field = static_cast(field); shader_program_->setUniformValue( @@ -870,18 +870,18 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { ); } break; - case olive::nodes::kBoolean: + case EFFECT_FIELD_BOOL: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool()); break; - case olive::nodes::kCombo: + case EFFECT_FIELD_COMBO: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt()); break; // can you even send a string to a uniform value? - case olive::nodes::kString: - case olive::nodes::kFont: - case olive::nodes::kFile: - case olive::nodes::kUI: + case EFFECT_FIELD_STRING: + case EFFECT_FIELD_FONT: + case EFFECT_FIELD_FILE: + case EFFECT_FIELD_UI: break; } } diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 997296628..7446fbc5f 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -93,7 +93,7 @@ QVariant EffectField::GetValueAt(double timecode) const QVariant& before_data = keyframes.at(before_keyframe).data; switch (type_) { - case olive::nodes::kFloat: + case EFFECT_FIELD_DOUBLE: { double value; if (before_keyframe == after_keyframe) { @@ -163,7 +163,7 @@ QVariant EffectField::GetValueAt(double timecode) persistent_data_ = value; break; } - case olive::nodes::kColor: + case EFFECT_FIELD_COLOR: { QColor value; if (before_keyframe == after_keyframe) { @@ -178,11 +178,11 @@ QVariant EffectField::GetValueAt(double timecode) persistent_data_ = value; break; } - case olive::nodes::kString: - case olive::nodes::kBoolean: - case olive::nodes::kCombo: - case olive::nodes::kFont: - case olive::nodes::kFile: + 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: @@ -360,7 +360,7 @@ void EffectField::GetKeyframeData(double timecode, int &before, int &after, doub } } - if ((type_ == olive::nodes::kFloat || type_ == olive::nodes::kColor) + if ((type_ == EFFECT_FIELD_DOUBLE || type_ == EFFECT_FIELD_COLOR) && (before_keyframe_index > -1 && after_keyframe_index > -1)) { // interpolate before = before_keyframe_index; diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index a4e8823d3..55e67157b 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -25,7 +25,6 @@ NodeEditor::NodeEditor(QWidget *parent) : void NodeEditor::Retranslate() { - } void NodeEditor::LoadEvent() diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 72b91848e..5885d7f76 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -244,7 +244,7 @@ void GraphView::paintEvent(QPaintEvent *) { for (int i=row->FieldCount()-1;i>=0;i--) { EffectField* field = row->Field(i); - if (field->type() == olive::nodes::kFloat && field_visibility.at(i)) { + if (field->type() == EffectField::EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { // sort keyframes by time QVector sorted_keys = sort_keys_from_field(field); @@ -386,7 +386,7 @@ void GraphView::mousePressEvent(QMouseEvent *event) { } else { for (int i=0;iFieldCount();i++) { EffectField* field = row->Field(i); - if (field->type() == olive::nodes::kFloat && field_visibility.at(i)) { + if (field->type() == EffectField::EFFECT_FIELD_DOUBLE && field_visibility.at(i)) { for (int j=0;jkeyframes.size();j++) { const EffectKeyframe& key = field->keyframes.at(j); int key_x = get_screen_x(key.time); From 14ae9030003292fa366d5ca100cf72c61e445657 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 10 Apr 2019 10:36:31 +1000 Subject: [PATCH 105/133] moved ID from field to row --- effects/effectfield.cpp | 2 +- effects/effectfield.h | 14 +----------- effects/effectrow.cpp | 5 +++-- effects/effectrow.h | 40 ++++++++++++++++++++++------------ effects/fields/boolfield.cpp | 4 ++-- effects/fields/boolfield.h | 2 +- effects/fields/buttonfield.cpp | 4 ++-- effects/fields/buttonfield.h | 2 +- effects/fields/colorfield.cpp | 4 ++-- effects/fields/colorfield.h | 2 +- effects/fields/combofield.cpp | 4 ++-- effects/fields/combofield.h | 2 +- effects/fields/doublefield.cpp | 4 ++-- effects/fields/doublefield.h | 2 +- effects/fields/stringfield.cpp | 4 ++-- nodes/inputs/doubleinput.cpp | 6 +++++ nodes/inputs/doubleinput.h | 12 ++++++++++ olive.pro | 6 +++-- 18 files changed, 70 insertions(+), 49 deletions(-) create mode 100644 nodes/inputs/doubleinput.cpp create mode 100644 nodes/inputs/doubleinput.h diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 7446fbc5f..46883e9b9 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -34,7 +34,7 @@ #include "global/math.h" #include "global/debug.h" -EffectField::EffectField(EffectRow* parent, const QString &i, EffectFieldType t) : +EffectField::EffectField(EffectRow* parent, EffectFieldType t) : QObject(parent), type_(t), id_(i), diff --git a/effects/effectfield.h b/effects/effectfield.h index 2b94ed99e..9fd1b07b4 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -103,18 +103,11 @@ public: * using the QObject parent/child system to automate memory management. EffectFields are never expected * to change parent during their lifetime. * - * @param i - * - * Field ID. Must be non-empty. Must also be unique within this Effect. Used for saving/loading values into project - * files so that if ordering of fields are changed, or fields are added/removed from Effects later in development, - * saved values in project files will still link with the correct field. Also used as the uniform variable name - * in GLSL shaders. - * * @param t * * The type of data contained within this field. This is expected to be filled by a derived class. */ - EffectField(EffectRow* parent, const QString& i, EffectFieldType t); + EffectField(EffectRow* parent, EffectFieldType t); /** * @brief Get the EffectRow that this field is a member of. @@ -434,11 +427,6 @@ private: */ EffectFieldType type_; - /** - * @brief Internal unique identifier for this field set in the constructor. Access with id(). - */ - QString id_; - /** * @brief Used by GetValueAt() to determine whether to use keyframe data or persistent data * @return diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index 02305a7f8..8a4ab9fcb 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -37,9 +37,10 @@ #include "ui/keyframenavigator.h" #include "ui/clickablelabel.h" -EffectRow::EffectRow(Effect *parent, const QString &n, bool savable, bool keyframable) : +EffectRow::EffectRow(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : QObject(parent), - name_(n), + id_(id), + name_(name), keyframable_(keyframable), keyframing_(false), savable_(savable) diff --git a/effects/effectrow.h b/effects/effectrow.h index 5248a997c..779b437e6 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -60,7 +60,14 @@ public: * EffectRow and automatically frees it through the QObject parent/child system. EffectRows are never intended to * change parents throughout their lifetimes. * - * @param n + * @param id + * + * Field ID. Must be non-empty. Must also be unique within this Effect. Used for saving/loading values into project + * files so that if ordering of fields are changed, or fields are added/removed from Effects later in development, + * saved values in project files will still link with the correct field. Also used as the uniform variable name + * in GLSL shaders. + * + * @param name * * Row name. This is not used as an internal identifier, it's just for the user interface, so it can be translated * with no issue. @@ -75,19 +82,7 @@ public: * Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent * the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false. */ - EffectRow(Effect* parent, const QString& n, bool savable = true, bool keyframable = true); - - /** - * @brief Add a field to this row - * - * Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this - * will add the field to an additional column. - * - * @param Field - * - * The field to add to this row. - */ - void AddField(EffectField* Field); + EffectRow(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Retrieve the EffectField at this index. Must be less than FieldCount(). @@ -222,6 +217,23 @@ private slots: */ void SetKeyframingEnabled(bool); private: + /** + * @brief Add a field to this row + * + * Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this + * will add the field to an additional column. + * + * @param Field + * + * The field to add to this row. + */ + void AddField(EffectField* Field); + + /** + * @brief Internal unique identifier for this field set in the constructor. Access with id(). + */ + QString id_; + /** * @brief Internal variable for the row's name * diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index 0dfffd4cf..873b5f26e 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -22,8 +22,8 @@ #include -BoolField::BoolField(EffectRow *parent, const QString &id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_BOOL) +BoolField::BoolField(EffectRow *parent) : + EffectField(parent, EffectField::EFFECT_FIELD_BOOL) {} bool BoolField::GetBoolAt(double timecode) diff --git a/effects/fields/boolfield.h b/effects/fields/boolfield.h index 4e73ee364..1e8e891ae 100644 --- a/effects/fields/boolfield.h +++ b/effects/fields/boolfield.h @@ -35,7 +35,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - BoolField(EffectRow* parent, const QString& id); + BoolField(EffectRow* parent); /** * @brief Get the boolean value at a given timecode diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index c03c0e1ae..15cb436a0 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -22,8 +22,8 @@ #include -ButtonField::ButtonField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, EffectField::EFFECT_FIELD_UI), +ButtonField::ButtonField(EffectRow *parent) : + EffectField(parent, EffectField::EFFECT_FIELD_UI), button_text_(string) {} diff --git a/effects/fields/buttonfield.h b/effects/fields/buttonfield.h index 0cea15459..a59def70d 100644 --- a/effects/fields/buttonfield.h +++ b/effects/fields/buttonfield.h @@ -42,7 +42,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - ButtonField(EffectRow* parent, const QString& string); + ButtonField(EffectRow* parent); /** * @brief Set whether this pushbutton is checkable diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index 001bb7d77..10b2e1d0d 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -24,8 +24,8 @@ #include "ui/colorbutton.h" -ColorField::ColorField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_COLOR) +ColorField::ColorField(EffectRow* parent) : + EffectField(parent, EffectField::EFFECT_FIELD_COLOR) {} QColor ColorField::GetColorAt(double timecode) diff --git a/effects/fields/colorfield.h b/effects/fields/colorfield.h index 6ca9e8294..235e831dc 100644 --- a/effects/fields/colorfield.h +++ b/effects/fields/colorfield.h @@ -35,7 +35,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - ColorField(EffectRow* parent, const QString& id); + ColorField(EffectRow* parent); /** * @brief Get the color value at a given timecode diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 992c46f14..173daba45 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -24,8 +24,8 @@ #include "ui/comboboxex.h" -ComboField::ComboField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_COMBO) +ComboField::ComboField(EffectRow* parent) : + EffectField(parent, EffectField::EFFECT_FIELD_COMBO) {} void ComboField::AddItem(const QString &text, const QVariant &data) diff --git a/effects/fields/combofield.h b/effects/fields/combofield.h index 915c9e58c..f7bedb54d 100644 --- a/effects/fields/combofield.h +++ b/effects/fields/combofield.h @@ -48,7 +48,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - ComboField(EffectRow* parent, const QString& id); + ComboField(EffectRow* parent); /** * @brief Add an item to this ComboField diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 4f273157d..09fda4fcb 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -22,8 +22,8 @@ #include "effects/effectrow.h" -DoubleField::DoubleField(EffectRow* parent, const QString& id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_DOUBLE), +DoubleField::DoubleField(EffectRow* parent) : + EffectField(parent, EffectField::EFFECT_FIELD_DOUBLE), min_(qSNaN()), max_(qSNaN()), default_(0), diff --git a/effects/fields/doublefield.h b/effects/fields/doublefield.h index 6aac63a9a..7c1b962e2 100644 --- a/effects/fields/doublefield.h +++ b/effects/fields/doublefield.h @@ -37,7 +37,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - DoubleField(EffectRow* parent, const QString& id); + DoubleField(EffectRow* parent); /** * @brief Get double value at timecode diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index e228ea729..8b2acdb62 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -26,8 +26,8 @@ #include "ui/texteditex.h" #include "global/config.h" -StringField::StringField(EffectRow* parent, const QString& id, bool rich_text) : - EffectField(parent, id, EffectField::EFFECT_FIELD_STRING), +StringField::StringField(EffectRow* parent, bool rich_text) : + EffectField(parent, EffectField::EFFECT_FIELD_STRING), rich_text_(rich_text) { // Set default value to an empty string diff --git a/nodes/inputs/doubleinput.cpp b/nodes/inputs/doubleinput.cpp new file mode 100644 index 000000000..dded84c1f --- /dev/null +++ b/nodes/inputs/doubleinput.cpp @@ -0,0 +1,6 @@ +#include "doubleinput.h" + +DoubleInput::DoubleInput() +{ + +} diff --git a/nodes/inputs/doubleinput.h b/nodes/inputs/doubleinput.h new file mode 100644 index 000000000..58e468496 --- /dev/null +++ b/nodes/inputs/doubleinput.h @@ -0,0 +1,12 @@ +#ifndef DOUBLEINPUT_H +#define DOUBLEINPUT_H + +#include "effects/effectrow.h" + +class DoubleInput : public EffectRow +{ +public: + DoubleInput(); +}; + +#endif // DOUBLEINPUT_H diff --git a/olive.pro b/olive.pro index e9195da77..d9d2f8f17 100644 --- a/olive.pro +++ b/olive.pro @@ -193,7 +193,8 @@ SOURCES += \ ui/nodeui.cpp \ panels/effectspanel.cpp \ nodes/nodedatatypes.cpp \ - nodes/nodeplug.cpp + nodes/nodeplug.cpp \ + nodes/inputs/doubleinput.cpp HEADERS += \ ui/mainwindow.h \ @@ -341,7 +342,8 @@ HEADERS += \ ui/nodeui.h \ panels/effectspanel.h \ nodes/nodedatatypes.h \ - nodes/nodeplug.h + nodes/nodeplug.h \ + nodes/inputs/doubleinput.h FORMS += From d3bc1d4f0684bfcd6d60a995776cfd711d7c0542 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 11 Apr 2019 00:02:50 +1000 Subject: [PATCH 106/133] finished moving field data retrieving to row --- dialogs/newsequencedialog.cpp | 2 - effects/effect.cpp | 307 ++++++++++++----------- effects/effect.h | 25 ++ effects/effectfield.cpp | 40 +-- effects/effectfield.h | 62 ----- effects/effectrow.cpp | 27 +- effects/effectrow.h | 93 ++++++- effects/fields/boolfield.cpp | 4 +- effects/fields/buttonfield.cpp | 2 +- effects/fields/buttonfield.h | 2 +- effects/fields/colorfield.cpp | 3 +- effects/fields/combofield.cpp | 3 +- effects/fields/doublefield.cpp | 4 +- effects/fields/filefield.cpp | 7 +- effects/fields/filefield.h | 4 +- effects/fields/fontfield.cpp | 9 +- effects/fields/fontfield.h | 2 +- effects/fields/labelfield.cpp | 2 +- effects/fields/stringfield.cpp | 3 +- effects/fields/stringfield.h | 2 +- effects/internal/audionoiseeffect.cpp | 6 +- effects/internal/audionoiseeffect.h | 4 +- effects/internal/cornerpineffect.cpp | 46 ++-- effects/internal/cornerpineffect.h | 15 +- effects/internal/fillleftrighteffect.cpp | 3 +- effects/internal/fillleftrighteffect.h | 2 +- effects/internal/paneffect.cpp | 3 +- effects/internal/paneffect.h | 2 +- effects/internal/richtexteffect.cpp | 62 ++--- effects/internal/richtexteffect.h | 23 +- effects/internal/shakeeffect.cpp | 9 +- effects/internal/shakeeffect.h | 6 +- effects/internal/solideffect.cpp | 12 +- effects/internal/solideffect.h | 8 +- effects/internal/texteffect.cpp | 71 ++---- effects/internal/texteffect.h | 37 ++- effects/internal/timecodeeffect.cpp | 37 +-- effects/internal/timecodeeffect.h | 15 +- effects/internal/toneeffect.cpp | 12 +- effects/internal/toneeffect.h | 8 +- effects/internal/transformeffect.cpp | 123 ++++----- effects/internal/transformeffect.h | 18 +- effects/internal/voideffect.cpp | 3 +- effects/internal/volumeeffect.cpp | 3 +- effects/internal/volumeeffect.h | 2 +- effects/internal/vsthost.cpp | 7 +- effects/internal/vsthost.h | 4 +- effects/shaders/boxblur.xml | 12 +- effects/transition.cpp | 3 +- effects/transition.h | 2 +- nodes/inputs.h | 15 ++ nodes/inputs/boolinput.cpp | 14 ++ nodes/inputs/boolinput.h | 36 +++ nodes/inputs/colorinput.cpp | 12 + nodes/inputs/colorinput.h | 28 +++ nodes/inputs/comboinput.cpp | 14 ++ nodes/inputs/comboinput.h | 36 +++ nodes/inputs/doubleinput.cpp | 6 - nodes/inputs/doubleinput.h | 12 - nodes/inputs/fileinput.cpp | 12 + nodes/inputs/fileinput.h | 28 +++ nodes/inputs/fontinput.cpp | 12 + nodes/inputs/fontinput.h | 28 +++ nodes/inputs/stringinput.cpp | 12 + nodes/inputs/stringinput.h | 33 +++ nodes/inputs/vecinput.cpp | 186 ++++++++++++++ nodes/inputs/vecinput.h | 78 ++++++ nodes/nodedatatypes.h | 16 +- nodes/widgets/buttonwidget.cpp | 23 ++ nodes/widgets/buttonwidget.h | 35 +++ nodes/widgets/labelwidget.cpp | 7 + nodes/widgets/labelwidget.h | 12 + olive.pro | 21 +- panels/grapheditor.cpp | 6 +- panels/timeline.cpp | 7 + panels/timeline.h | 1 + project/footage.cpp | 4 +- ui/effectui.cpp | 10 +- 78 files changed, 1189 insertions(+), 676 deletions(-) create mode 100644 nodes/inputs.h create mode 100644 nodes/inputs/boolinput.cpp create mode 100644 nodes/inputs/boolinput.h create mode 100644 nodes/inputs/colorinput.cpp create mode 100644 nodes/inputs/colorinput.h create mode 100644 nodes/inputs/comboinput.cpp create mode 100644 nodes/inputs/comboinput.h delete mode 100644 nodes/inputs/doubleinput.cpp delete mode 100644 nodes/inputs/doubleinput.h create mode 100644 nodes/inputs/fileinput.cpp create mode 100644 nodes/inputs/fileinput.h create mode 100644 nodes/inputs/fontinput.cpp create mode 100644 nodes/inputs/fontinput.h create mode 100644 nodes/inputs/stringinput.cpp create mode 100644 nodes/inputs/stringinput.h create mode 100644 nodes/inputs/vecinput.cpp create mode 100644 nodes/inputs/vecinput.h create mode 100644 nodes/widgets/buttonwidget.cpp create mode 100644 nodes/widgets/buttonwidget.h create mode 100644 nodes/widgets/labelwidget.cpp create mode 100644 nodes/widgets/labelwidget.h diff --git a/dialogs/newsequencedialog.cpp b/dialogs/newsequencedialog.cpp index 5d23f49af..940c046cb 100644 --- a/dialogs/newsequencedialog.cpp +++ b/dialogs/newsequencedialog.cpp @@ -270,8 +270,6 @@ void NewSequenceDialog::setup_ui() { videoLayout->addWidget(new QLabel(tr("Interlacing:"), this), 6, 0, 1, 1); interlacing_combobox = new QComboBox(videoGroupBox); interlacing_combobox->addItem(tr("None (Progressive)")); - // interlacing_combobox->addItem("Upper Field First"); - // interlacing_combobox->addItem("Lower Field First"); videoLayout->addWidget(interlacing_combobox, 6, 2, 1, 2); verticalLayout->addWidget(videoGroupBox); diff --git a/effects/effect.cpp b/effects/effect.cpp index 27608986c..478c7c2f3 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -53,6 +53,7 @@ #include "transition.h" #include "undo/undostack.h" #include "rendering/shadergenerators.h" +#include "global/timing.h" #include "effects/internal/transformeffect.h" #include "effects/internal/texteffect.h" @@ -136,153 +137,138 @@ Effect::Effect(Clip* c, const EffectMeta *em) : QXmlStreamReader reader(&effect_file); while (!reader.atEnd()) { - if (reader.name() == "row" && reader.isStartElement()) { - QString row_name; + if (reader.name() == "field" && reader.isStartElement()) { + int type = olive::nodes::kInvalid; + QString id; + QString name; + + // get field type const QXmlStreamAttributes& attributes = reader.attributes(); for (int i=0;ifilename << "- ID, type, and name cannot be empty."; + } else { + EffectRow* field = nullptr; - if (id.isEmpty()) { - qCritical() << "Couldn't load field from" << em->filename << "- ID cannot be empty."; - } else if (type == olive::nodes::kInvalid) { - qWarning() << "Invalid field type found"; - } else { - EffectField* field = nullptr; + switch (type) { + case olive::nodes::kFloat: + { - switch (type) { - case olive::nodes::kFloat: - { + DoubleInput* double_field = new DoubleInput(this, id, name); - DoubleField* double_field = new DoubleField(row, id); - - for (int i=0;iSetDefault(attr.value().toDouble()); - } else if (attr.name() == "min") { - double_field->SetMinimum(attr.value().toDouble()); - } else if (attr.name() == "max") { - double_field->SetMaximum(attr.value().toDouble()); - } - } - - field = double_field; - } - break; - case olive::nodes::kColor: - { - QColor color; - - field = new ColorField(row, id); - - for (int i=0;iSetValueAt(0, color); - } - break; - case olive::nodes::kString: - field = new StringField(row, id); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - case olive::nodes::kBoolean: - field = new BoolField(row, id); - for (int i=0;iSetValueAt(0, attr.value() == "1"); - } - } - break; - case olive::nodes::kCombo: - { - ComboField* combo_field = new ComboField(row, id); - int combo_default_index = 0; - for (int i=0;iAddItem(reader.text().toString(), combo_item_count); - combo_item_count++; - } - } - combo_field->SetValueAt(0, combo_default_index); - field = combo_field; - } - break; - case olive::nodes::kFont: - field = new FontField(row, id); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - case olive::nodes::kFile: - field = new FileField(row, id); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - } + for (int i=0;iSetDefault(attr.value().toDouble()); + } else if (attr.name() == "min") { + double_field->SetMinimum(attr.value().toDouble()); + } else if (attr.name() == "max") { + double_field->SetMaximum(attr.value().toDouble()); } } + + field = double_field; + } + break; + case olive::nodes::kColor: + { + QColor color; + + field = new ColorInput(this, id, name); + + for (int i=0;iSetValueAt(0, color); + } + break; + case olive::nodes::kString: + field = new StringInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; + case olive::nodes::kBoolean: + field = new BoolInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value() == "1"); + } + } + break; + case olive::nodes::kCombo: + { + ComboInput* combo_field = new ComboInput(this, id, name); + int combo_default_index = 0; + for (int i=0;iAddItem(reader.text().toString(), combo_item_count); + combo_item_count++; + } + } + combo_field->SetValueAt(0, combo_default_index); + field = combo_field; + } + break; + case olive::nodes::kFont: + field = new FontInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; + case olive::nodes::kFile: + field = new FileInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; } } } else if (reader.name() == "shader" && reader.isStartElement()) { @@ -515,6 +501,7 @@ void Effect::SetEnabled(bool b) { } void Effect::load(QXmlStreamReader& stream) { + /* int row_count = 0; QString tag = stream.name().toString(); @@ -602,11 +589,13 @@ void Effect::load(QXmlStreamReader& stream) { custom_load(stream); } } + */ } void Effect::custom_load(QXmlStreamReader &) {} void Effect::save(QXmlStreamWriter& stream) { + /* stream.writeAttribute("name", meta->category + "/" + meta->name); stream.writeAttribute("enabled", QString::number(IsEnabled())); @@ -639,6 +628,7 @@ void Effect::save(QXmlStreamWriter& stream) { stream.writeEndElement(); // row } } + */ } void Effect::load_from_string(const QByteArray &s) { @@ -840,6 +830,7 @@ EffectPtr Effect::copy(Clip *c) { } void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { + /* shader_program_->bind(); shader_program_->setUniformValue("resolution", parent_clip->media_width(), parent_clip->media_height()); @@ -848,20 +839,21 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { for (int i=0;iFieldCount();j++) { EffectField* field = row->Field(j); if (!field->id().isEmpty()) { switch (field->type()) { - case EFFECT_FIELD_DOUBLE: + case EffectField::EFFECT_FIELD_DOUBLE: { DoubleField* double_field = static_cast(field); shader_program_->setUniformValue(double_field->id().toUtf8().constData(), GLfloat(double_field->GetDoubleAt(timecode))); } break; - case EFFECT_FIELD_COLOR: + case EffectField::EFFECT_FIELD_COLOR: { - ColorField* color_field = static_cast(field); + ColorField* color_field = static_cast(field); shader_program_->setUniformValue( color_field->id().toUtf8().constData(), GLfloat(color_field->GetColorAt(timecode).redF()), @@ -870,18 +862,18 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { ); } break; - case EFFECT_FIELD_BOOL: + case EffectField::EFFECT_FIELD_BOOL: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toBool()); break; - case EFFECT_FIELD_COMBO: + case EffectField::EFFECT_FIELD_COMBO: shader_program_->setUniformValue(field->id().toUtf8().constData(), field->GetValueAt(timecode).toInt()); break; // can you even send a string to a uniform value? - case EFFECT_FIELD_STRING: - case EFFECT_FIELD_FONT: - case EFFECT_FIELD_FILE: - case EFFECT_FIELD_UI: + case EffectField::EFFECT_FIELD_STRING: + case EffectField::EFFECT_FIELD_FONT: + case EffectField::EFFECT_FIELD_FILE: + case EffectField::EFFECT_FIELD_UI: break; } } @@ -889,6 +881,7 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { } shader_program_->release(); + */ } void Effect::process_coords(double, GLTextureCoords&, int) {} @@ -929,7 +922,7 @@ GLuint Effect::process_superimpose(QOpenGLContext* ctx, double timecode) { f->glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, tex_width_, tex_height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits() - ); + ); f->glBindTexture(GL_TEXTURE_2D, 0); @@ -1045,6 +1038,16 @@ bool Effect::are_gizmos_enabled() { return (gizmos.size() > 0); } +double Effect::Now() +{ + return playhead_to_clip_seconds(parent_clip, parent_clip->track()->sequence()->playhead); +} + +long Effect::NowInFrames() +{ + return playhead_to_clip_frame(parent_clip, parent_clip->track()->sequence()->playhead); +} + void Effect::redraw(double) { /* // run javascript @@ -1062,22 +1065,22 @@ void Effect::redraw(double) { EffectField* field = row->field(j); if (!field->id.isEmpty()) { switch (field->type) { - case EFFECT_FIELD_DOUBLE: + case EffectField::EFFECT_FIELD_DOUBLE: jsEngine.globalObject().setProperty(field->id, field->get_double_value(timecode)); break; - case EFFECT_FIELD_COLOR: + case EffectField::EFFECT_FIELD_COLOR: jsEngine.globalObject().setProperty(field->id, field->get_color_value(timecode).name()); break; - case EFFECT_FIELD_STRING: + case EffectField::EFFECT_FIELD_STRING: jsEngine.globalObject().setProperty(field->id, field->get_string_value(timecode)); break; - case EFFECT_FIELD_BOOL: + case EffectField::EFFECT_FIELD_BOOL: jsEngine.globalObject().setProperty(field->id, field->get_bool_value(timecode)); break; - case EFFECT_FIELD_COMBO: + case EffectField::EFFECT_FIELD_COMBO: jsEngine.globalObject().setProperty(field->id, field->get_combo_index(timecode)); break; - case EFFECT_FIELD_FONT: + case EffectField::EFFECT_FIELD_FONT: jsEngine.globalObject().setProperty(field->id, field->get_font_name(timecode)); break; } diff --git a/effects/effect.h b/effects/effect.h index 95ec3500a..00aacb01d 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -44,6 +44,7 @@ #include "effectrow.h" #include "effectgizmo.h" #include "rendering/qopenglshaderprogramptr.h" +#include "nodes/inputs.h" class Clip; @@ -192,6 +193,30 @@ public: void gizmo_world_to_screen(const QMatrix4x4 &matrix, const QMatrix4x4 &projection); bool are_gizmos_enabled(); + /** + * @brief Get the current clip/media time + * + * A convenience function that can be plugged into GetValueAt() to get the value wherever the appropriate Sequence's + * playhead it. + * + * @return + * + * Current clip/media time in seconds. + */ + double Now(); + + /** + * @brief Retrieve the current clip as a frame number + * + * Same as Now() but retrieves the value as a frame number (in the appropriate Sequence's frame rate) instead of + * seconds. + * + * @return + * + * The current clip time in frames + */ + long NowInFrames(); + template T randomNumber() { diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 46883e9b9..c636e3a61 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -37,16 +37,10 @@ EffectField::EffectField(EffectRow* parent, EffectFieldType t) : QObject(parent), type_(t), - id_(i), - enabled_(true), - colspan_(1) + enabled_(true) { // 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); @@ -60,17 +54,6 @@ 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; @@ -232,18 +215,6 @@ void EffectField::SetValueAt(double time, const QVariant &value) emit Changed(); } -double EffectField::Now() -{ - Clip* c = GetParentRow()->GetParentEffect()->parent_clip; - return playhead_to_clip_seconds(c, c->track()->sequence()->playhead); -} - -long EffectField::NowInFrames() -{ - Clip* c = GetParentRow()->GetParentEffect()->parent_clip; - return playhead_to_clip_frame(c, c->track()->sequence()->playhead); -} - void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) { if (enabled) { @@ -251,7 +222,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) // Create keyframe from perpetual data EffectKeyframe key; - key.time = NowInFrames(); + key.time = GetParentRow()->GetParentEffect()->NowInFrames(); key.data = persistent_data_; key.type = EFFECT_KEYFRAME_LINEAR; @@ -264,7 +235,7 @@ void EffectField::PrepareDataForKeyframing(bool enabled, ComboAction *ca) // 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()))); + ca->append(new SetQVariant(&persistent_data_, persistent_data_, GetValueAt(GetParentRow()->GetParentEffect()->Now()))); // Delete all keyframes for (int i=0;isetParent(this); + + connect(field, SIGNAL(Clicked()), this, SIGNAL(Clicked())); + connect(field, SIGNAL(Changed()), this, SIGNAL(Changed())); + fields_.append(field); } @@ -77,6 +81,27 @@ bool EffectRow::IsKeyframable() return keyframable_; } +QVariant EffectRow::GetValueAt(double timecode) +{ + Q_ASSERT(FieldCount() == 1); + + return Field(0)->GetValueAt(timecode); +} + +void EffectRow::SetValueAt(double timecode, const QVariant &value) +{ + Q_ASSERT(FieldCount() == 1); + + Field(0)->SetValueAt(timecode, value); +} + +void EffectRow::SetEnabled(bool enabled) +{ + for (int i=0;iSetEnabled(enabled); + } +} + void EffectRow::SetKeyframingEnabled(bool enabled) { if (enabled == keyframing_) { return; @@ -252,7 +277,7 @@ void EffectRow::SetKeyframeOnAllFields(ComboAction* ca) { KeyframeDataChange* kdc = new KeyframeDataChange(field); - field->SetValueAt(field->Now(), field->GetValueAt(field->Now())); + field->SetValueAt(GetParentEffect()->Now(), field->GetValueAt(GetParentEffect()->Now())); kdc->SetNewKeyframes(); ca->append(kdc); diff --git a/effects/effectrow.h b/effects/effectrow.h index 779b437e6..7bfb4379f 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -130,6 +130,15 @@ public: */ const QString& name(); + /** + * @brief Get the unique identifier of this field set in the constructor + * + * Mostly used for saving/loading or interacting with GLSL-based shader effects (see EffectField() for more details). + * + * @return + */ + const QString& id(); + /** * @brief Get whether this row is keyframing or not * @@ -158,6 +167,63 @@ public: * @return True if this row can be keyframed. This value is set in the constructor. */ bool IsKeyframable(); + + /** + * @brief Get value at a given timecode + * + * Functions as a wrapper for EffectField::GetValueAt(). + * + * The default function is to return the result of the first EffectField on this EffectRow which should be sufficient + * for most input types. If this EffectRow has more or less than one EffectField, you must override this function in a + * derived class to provide the correct field <-> row coordination or else it will trigger an abort. + * + * @param timecode + * + * Timecode to get the value at + * + * @return + * + * The value of the first EffectField at the given timecode + */ + virtual QVariant GetValueAt(double timecode); + + /** + * @brief SetValueAt + * + * Functions as a wrapper for EffectField::SetValueAt(). + * + * The default function is to call the function of the first EffectField on this EffectRow which should be sufficient + * for most input types. If this EffectRow has more or less than one EffectField, you must override this function in a + * derived class to provide the correct field <-> row coordination or else it will trigger an abort. + * + * @param timecode + * + * Timecode to set the value at + * + * @param value + * + * Value to set at this timecode + */ + virtual void SetValueAt(double timecode, const QVariant& value); + + /** + * @brief Sets the enabled state on all EffectField objects on this row to enabled + */ + void SetEnabled(bool enabled); + +protected: + /** + * @brief Add a field to this row + * + * Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this + * will add the field to an additional column. + * + * @param Field + * + * The field to add to this row. + */ + void AddField(EffectField* Field); + public slots: /** * @brief Go to previous keyframe @@ -194,6 +260,7 @@ public slots: */ void FocusRow(); signals: + /** * @brief Keyframing setting changed signal * @@ -204,6 +271,21 @@ signals: * True if keyframing was enabled, false if keyframing was disabled. */ void KeyframingSetChanged(bool); + + /** + * @brief Changed signal + * + * Wrapper for EffectField::Changed(). + */ + void Changed(); + + /** + * @brief Clicked signal + * + * Wrapper for EffectField::Clicked(). + */ + void Clicked(); + private slots: /** * @brief Set keyframing enabled state @@ -217,17 +299,6 @@ private slots: */ void SetKeyframingEnabled(bool); private: - /** - * @brief Add a field to this row - * - * Ownership of the EffectField is transferred to this row and the row will free its memory. In the Effect's UI, this - * will add the field to an additional column. - * - * @param Field - * - * The field to add to this row. - */ - void AddField(EffectField* Field); /** * @brief Internal unique identifier for this field set in the constructor. Access with id(). diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index 873b5f26e..f706bc08a 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -22,6 +22,8 @@ #include +#include "effects/effect.h" + BoolField::BoolField(EffectRow *parent) : EffectField(parent, EffectField::EFFECT_FIELD_BOOL) {} @@ -89,7 +91,7 @@ void BoolField::UpdateFromWidget(bool b) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), b); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), b); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index 15cb436a0..2023105f3 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -22,7 +22,7 @@ #include -ButtonField::ButtonField(EffectRow *parent) : +ButtonField::ButtonField(EffectRow *parent, const QString &string) : EffectField(parent, EffectField::EFFECT_FIELD_UI), button_text_(string) {} diff --git a/effects/fields/buttonfield.h b/effects/fields/buttonfield.h index a59def70d..0cea15459 100644 --- a/effects/fields/buttonfield.h +++ b/effects/fields/buttonfield.h @@ -42,7 +42,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - ButtonField(EffectRow* parent); + ButtonField(EffectRow* parent, const QString& string); /** * @brief Set whether this pushbutton is checkable diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index 10b2e1d0d..1797681a4 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -23,6 +23,7 @@ #include #include "ui/colorbutton.h" +#include "effects/effect.h" ColorField::ColorField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_COLOR) @@ -64,7 +65,7 @@ void ColorField::UpdateFromWidget(const QColor& c) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), c); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), c); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 173daba45..7471f7e16 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -22,6 +22,7 @@ #include +#include "effects/effect.h" #include "ui/comboboxex.h" ComboField::ComboField(EffectRow* parent) : @@ -84,7 +85,7 @@ void ComboField::UpdateFromWidget(int index) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), items_.at(index).data); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), items_.at(index).data); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 09fda4fcb..9f70bf033 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -20,7 +20,7 @@ #include "doublefield.h" -#include "effects/effectrow.h" +#include "effects/effect.h" DoubleField::DoubleField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_DOUBLE), @@ -138,7 +138,7 @@ void DoubleField::UpdateFromWidget(double d) kdc_ = new KeyframeDataChange(this); } - SetValueAt(Now(), d); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), d); if (!ls->IsDragging() && kdc_ != nullptr) { kdc_->SetNewKeyframes(); diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index b9a21bbef..b38a035c4 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -23,9 +23,10 @@ #include #include "ui/embeddedfilechooser.h" +#include "effects/effect.h" -FileField::FileField(EffectRow* parent, const QString &id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_FILE) +FileField::FileField(EffectRow* parent) : + EffectField(parent, EffectField::EFFECT_FIELD_FILE) { // Set default value to an empty string SetValueAt(0, ""); @@ -59,7 +60,7 @@ void FileField::UpdateFromWidget(const QString &s) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), s); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), s); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/filefield.h b/effects/fields/filefield.h index 3512cd555..3752fd3f7 100644 --- a/effects/fields/filefield.h +++ b/effects/fields/filefield.h @@ -24,7 +24,7 @@ #include "../effectfield.h" /** - * @brief The FileField class + * @brief The FileInput class * * An EffectField derivative that produces filenames in string and uses an EmbeddedFileChooser * as its visual representation. @@ -36,7 +36,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - FileField(EffectRow* parent, const QString& id); + FileField(EffectRow* parent); /** * @brief Get the filename at the given timecode diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 21c741389..7738f9bc5 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -24,11 +24,12 @@ #include #include "ui/comboboxex.h" +#include "effects/effect.h" -// NOTE/TODO: This shares a lot of similarity with ComboField, and could probably be a derived class of it +// NOTE/TODO: This shares a lot of similarity with ComboInput, and could probably be a derived class of it -FontField::FontField(EffectRow* parent, const QString &id) : - EffectField(parent, id, EffectField::EFFECT_FIELD_FONT) +FontField::FontField(EffectRow* parent) : + EffectField(parent, EffectField::EFFECT_FIELD_FONT) { font_list = QFontDatabase().families(); @@ -86,7 +87,7 @@ void FontField::UpdateFromWidget(const QString& s) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), s); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), s); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/fontfield.h b/effects/fields/fontfield.h index 9fa1a9305..6abd7c6c2 100644 --- a/effects/fields/fontfield.h +++ b/effects/fields/fontfield.h @@ -37,7 +37,7 @@ public: /** * @brief Reimplementation of EffectField::EffectField(). */ - FontField(EffectRow* parent, const QString& id); + FontField(EffectRow* parent); /** * @brief Get the font family name at the given timecode diff --git a/effects/fields/labelfield.cpp b/effects/fields/labelfield.cpp index 117683337..45306699a 100644 --- a/effects/fields/labelfield.cpp +++ b/effects/fields/labelfield.cpp @@ -23,7 +23,7 @@ #include LabelField::LabelField(EffectRow *parent, const QString &string) : - EffectField(parent, nullptr, EffectField::EFFECT_FIELD_UI), + EffectField(parent, EffectField::EFFECT_FIELD_UI), label_text_(string) {} diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 8b2acdb62..6c2720113 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -23,6 +23,7 @@ #include #include +#include "effects/effect.h" #include "ui/texteditex.h" #include "global/config.h" @@ -92,7 +93,7 @@ void StringField::UpdateFromWidget(const QString &s) { KeyframeDataChange* kdc = new KeyframeDataChange(this); - SetValueAt(Now(), s); + SetValueAt(GetParentRow()->GetParentEffect()->Now(), s); kdc->SetNewKeyframes(); olive::undo_stack.push(kdc); diff --git a/effects/fields/stringfield.h b/effects/fields/stringfield.h index 083021c90..4124285bb 100644 --- a/effects/fields/stringfield.h +++ b/effects/fields/stringfield.h @@ -39,7 +39,7 @@ public: * Provides a setting for whether this StringField - and its attached TextEditEx objects - should operate in rich * text or plain text mode, defaulting to rich text mode. */ - StringField(EffectRow* parent, const QString& id, bool rich_text = true); + StringField(EffectRow* parent, bool rich_text = true); /** * @brief Get the string at the given timecode diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index 2ee515a22..a1323c0f6 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -24,14 +24,12 @@ #include AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* amount_row = new EffectRow(this, tr("Amount")); - amount_val = new DoubleField(amount_row, "amount"); + amount_val = new DoubleInput(this, "amount", tr("Amount")); amount_val->SetMinimum(0); amount_val->SetDefault(20); amount_val->SetMaximum(100); - EffectRow* mix_row = new EffectRow(this, tr("Mix")); - mix_val = new BoolField(mix_row, "mix"); + mix_val = new BoolInput(this, "mix", tr("Mix")); mix_val->SetValueAt(0, true); } diff --git a/effects/internal/audionoiseeffect.h b/effects/internal/audionoiseeffect.h index 819a02999..4e1c173ad 100644 --- a/effects/internal/audionoiseeffect.h +++ b/effects/internal/audionoiseeffect.h @@ -34,8 +34,8 @@ public: int channel_count, int type) override; - DoubleField* amount_val; - BoolField* mix_val; + DoubleInput* amount_val; + BoolInput* mix_val; }; #endif // AUDIONOISEEFFECT_H diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 814a45891..14bd4f4ba 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -27,54 +27,42 @@ CornerPinEffect::CornerPinEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { SetFlags(Effect::CoordsFlag | Effect::ShaderFlag); - EffectRow* top_left = new EffectRow(this, tr("Top Left")); - top_left_x = new DoubleField(top_left, "topleftx"); - top_left_y = new DoubleField(top_left, "toplefty"); + top_left = new Vec2Input(this, "topleft", tr("Top Left")); - EffectRow* top_right = new EffectRow(this, tr("Top Right")); - top_right_x = new DoubleField(top_right, "toprightx"); - top_right_y = new DoubleField(top_right, "toprighty"); + top_right = new Vec2Input(this, "topright", tr("Top Right")); - EffectRow* bottom_left = new EffectRow(this, tr("Bottom Left")); - bottom_left_x = new DoubleField(bottom_left, "bottomleftx"); - bottom_left_y = new DoubleField(bottom_left, "bottomlefty"); + bottom_left = new Vec2Input(this, "bottomleft", tr("Bottom Left")); - EffectRow* bottom_right = new EffectRow(this, tr("Bottom Right")); - bottom_right_x = new DoubleField(bottom_right, "bottomrightx"); - bottom_right_y = new DoubleField(bottom_right, "bottomrighty"); + bottom_right = new Vec2Input(this, "bottomright", tr("Bottom Right")); - EffectRow* perspective_row = new EffectRow(this, tr("Perspective")); - perspective = new BoolField(perspective_row, "perspective"); + perspective = new BoolInput(this, "perspective", tr("Perspective")); perspective->SetValueAt(0, true); top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_left_gizmo->x_field1 = top_left_x; - top_left_gizmo->y_field1 = top_left_y; + top_left_gizmo->x_field1 = static_cast(top_left->Field(0)); + top_left_gizmo->y_field1 = static_cast(top_left->Field(1)); top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - top_right_gizmo->x_field1 = top_right_x; - top_right_gizmo->y_field1 = top_right_y; + top_right_gizmo->x_field1 = static_cast(top_right->Field(0)); + top_right_gizmo->y_field1 = static_cast(top_right->Field(1)); bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_left_gizmo->x_field1 = bottom_left_x; - bottom_left_gizmo->y_field1 = bottom_left_y; + bottom_left_gizmo->x_field1 = static_cast(bottom_left->Field(0)); + bottom_left_gizmo->y_field1 = static_cast(bottom_left->Field(1)); bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); - bottom_right_gizmo->x_field1 = bottom_right_x; - bottom_right_gizmo->y_field1 = bottom_right_y; + bottom_right_gizmo->x_field1 = static_cast(bottom_right->Field(0)); + bottom_right_gizmo->y_field1 = static_cast(bottom_right->Field(1)); shader_vert_path_ = "cornerpin.vert"; shader_frag_path_ = "cornerpin.frag"; } void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int) { - coords.vertex_top_left += QVector3D(top_left_x->GetDoubleAt(timecode), top_left_y->GetDoubleAt(timecode), 0.0f); - - coords.vertex_top_right += QVector3D(top_right_x->GetDoubleAt(timecode), top_right_y->GetDoubleAt(timecode), 0.0f); - - coords.vertex_bottom_left += QVector3D(bottom_left_x->GetDoubleAt(timecode), bottom_left_y->GetDoubleAt(timecode), 0.0f); - - coords.vertex_bottom_right += QVector3D(bottom_right_x->GetDoubleAt(timecode), bottom_right_y->GetDoubleAt(timecode), 0.0f); + coords.vertex_top_left += top_left->GetVector2DAt(timecode); + coords.vertex_top_right += top_right->GetVector2DAt(timecode); + coords.vertex_bottom_left += bottom_left->GetVector2DAt(timecode); + coords.vertex_bottom_right += bottom_right->GetVector2DAt(timecode); } void CornerPinEffect::process_shader(double timecode, GLTextureCoords &coords, int) { diff --git a/effects/internal/cornerpineffect.h b/effects/internal/cornerpineffect.h index 50ea31a95..573164254 100644 --- a/effects/internal/cornerpineffect.h +++ b/effects/internal/cornerpineffect.h @@ -31,15 +31,12 @@ public: void process_shader(double timecode, GLTextureCoords& coords, int iterations); void gizmo_draw(double timecode, GLTextureCoords& coords); private: - DoubleField* top_left_x; - DoubleField* top_left_y; - DoubleField* top_right_x; - DoubleField* top_right_y; - DoubleField* bottom_left_x; - DoubleField* bottom_left_y; - DoubleField* bottom_right_x; - DoubleField* bottom_right_y; - BoolField* perspective; + Vec2Input* top_left; + Vec2Input* top_right; + Vec2Input* bottom_left; + Vec2Input* bottom_right; + + BoolInput* perspective; EffectGizmo* top_left_gizmo; EffectGizmo* top_right_gizmo; diff --git a/effects/internal/fillleftrighteffect.cpp b/effects/internal/fillleftrighteffect.cpp index 34709fe79..bbc04285e 100644 --- a/effects/internal/fillleftrighteffect.cpp +++ b/effects/internal/fillleftrighteffect.cpp @@ -26,8 +26,7 @@ enum FillType { }; FillLeftRightEffect::FillLeftRightEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* type_row = new EffectRow(this, tr("Type")); - fill_type = new ComboField(type_row, "type"); + fill_type = new ComboInput(this, "type", tr("Type")); fill_type->AddItem(tr("Fill Left with Right"), FILL_TYPE_LEFT); fill_type->AddItem(tr("Fill Right with Left"), FILL_TYPE_RIGHT); } diff --git a/effects/internal/fillleftrighteffect.h b/effects/internal/fillleftrighteffect.h index 64b126caa..a80ae0227 100644 --- a/effects/internal/fillleftrighteffect.h +++ b/effects/internal/fillleftrighteffect.h @@ -34,7 +34,7 @@ public: int channel_count, int type) override; private: - ComboField* fill_type; + ComboInput* fill_type; }; #endif // FILLLEFTRIGHTEFFECT_H diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 72a988f5c..57b55074b 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -29,8 +29,7 @@ #include "ui/collapsiblewidget.h" PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* pan_row = new EffectRow(this, tr("Pan")); - pan_val = new DoubleField(pan_row, "pan"); + pan_val = new DoubleInput(this, "pan", tr("Pan")); pan_val->SetMinimum(-100); pan_val->SetDefault(0); pan_val->SetMaximum(100); diff --git a/effects/internal/paneffect.h b/effects/internal/paneffect.h index ac29d9e45..f0765ba24 100644 --- a/effects/internal/paneffect.h +++ b/effects/internal/paneffect.h @@ -34,7 +34,7 @@ public: int channel_count, int type) override; - DoubleField* pan_val; + DoubleInput* pan_val; }; #endif // PANEFFECT_H diff --git a/effects/internal/richtexteffect.cpp b/effects/internal/richtexteffect.cpp index cc8b822d3..b773048a8 100644 --- a/effects/internal/richtexteffect.cpp +++ b/effects/internal/richtexteffect.cpp @@ -39,60 +39,38 @@ RichTextEffect::RichTextEffect(Clip *c, const EffectMeta *em) : { SetFlags(Effect::SuperimposeFlag); - EffectRow* text_row = new EffectRow(this, tr("Text")); - text_val = new StringField(text_row, "text"); - text_val->SetColumnSpan(2); + text_val = new StringInput(this, "text", tr("Text")); - EffectRow* padding_row = new EffectRow(this, tr("Padding")); - padding_field = new DoubleField(padding_row, "padding"); - padding_field->SetColumnSpan(2); + padding_field = new DoubleInput(this, "padding", tr("Padding")); - EffectRow* position_row = new EffectRow(this, tr("Position")); - position_x = new DoubleField(position_row, "posx"); - position_y = new DoubleField(position_row, "posy"); + position = new Vec2Input(this, "pos", tr("Position")); - EffectRow* vertical_align_row = new EffectRow(this, tr("Vertical Align:")); - vertical_align = new ComboField(vertical_align_row, "valign"); + vertical_align = new ComboInput(this, "valign", tr("Vertical Align:")); vertical_align->AddItem(tr("Top"), Qt::AlignTop); vertical_align->AddItem(tr("Center"), Qt::AlignCenter); vertical_align->AddItem(tr("Bottom"), Qt::AlignBottom); vertical_align->SetValueAt(0, Qt::AlignCenter); - vertical_align->SetColumnSpan(2); - EffectRow* autoscroll_row = new EffectRow(this, tr("Auto-Scroll")); - autoscroll = new ComboField(autoscroll_row, "autoscroll"); + autoscroll = new ComboInput(this, "autoscroll", tr("Auto-Scroll")); autoscroll->AddItem(tr("Off"), SCROLL_OFF); autoscroll->AddItem(tr("Up"), SCROLL_UP); autoscroll->AddItem(tr("Down"), SCROLL_DOWN); autoscroll->AddItem(tr("Left"), SCROLL_LEFT); autoscroll->AddItem(tr("Right"), SCROLL_RIGHT); - autoscroll->SetColumnSpan(2); - EffectRow* shadow_row = new EffectRow(this, tr("Shadow")); - shadow_bool = new BoolField(shadow_row, "shadow"); - shadow_bool->SetColumnSpan(2); + shadow_bool = new BoolInput(this, "shadow", tr("Shadow")); - EffectRow* shadow_color_row = new EffectRow(this, tr("Shadow Color")); - shadow_color = new ColorField(shadow_color_row, "shadowcolor"); - shadow_color->SetColumnSpan(2); + shadow_color = new ColorInput(this, "shadowcolor", tr("Shadow Color")); - EffectRow* shadow_angle_row = new EffectRow(this, tr("Shadow Angle")); - shadow_angle = new DoubleField(shadow_angle_row, "shadowangle"); - shadow_angle->SetColumnSpan(2); + shadow_angle = new DoubleInput(this, "shadowangle", tr("Shadow Angle")); - EffectRow* shadow_distance_row = new EffectRow(this, tr("Shadow Distance")); - shadow_distance = new DoubleField(shadow_distance_row, "shadowdistance"); - shadow_distance->SetColumnSpan(2); + shadow_distance = new DoubleInput(this, "shadowdistance", tr("Shadow Distance")); shadow_distance->SetMinimum(0); - EffectRow* shadow_softness_row = new EffectRow(this, tr("Shadow Softness")); - shadow_softness = new DoubleField(shadow_softness_row, "shadowsoftness"); - shadow_softness->SetColumnSpan(2); + shadow_softness = new DoubleInput(this, "shadowsoftness", tr("Shadow Softness")); shadow_softness->SetMinimum(0); - EffectRow* shadow_opacity_row = new EffectRow(this, tr("Shadow Opacity")); - shadow_opacity = new DoubleField(shadow_opacity_row, "shadowopacity"); - shadow_opacity->SetColumnSpan(2); + shadow_opacity = new DoubleInput(this, "shadowopacity", tr("Shadow Opacity")); shadow_opacity->SetMinimum(0); shadow_opacity->SetMaximum(100); @@ -120,8 +98,8 @@ void RichTextEffect::redraw(double timecode) td.setHtml(text_val->GetStringAt(timecode)); td.setTextWidth(width); - int translate_x = qRound(position_x->GetDoubleAt(timecode) + padding); - int translate_y = qRound(position_y->GetDoubleAt(timecode) + padding); + QPoint translation = position->GetVector2DAt(timecode).toPoint(); + translation += {padding, padding}; int doc_height = qRound(td.size().height()); @@ -138,9 +116,9 @@ void RichTextEffect::redraw(double timecode) // If we're not auto-scrolling the vertical direction, respect the vertical alignment if (vertical_align->GetValueAt(timecode).toInt() == Qt::AlignCenter) { - translate_y += height / 2 - doc_height / 2; + translation.setY(translation.y() + height / 2 - doc_height / 2); } else if (vertical_align->GetValueAt(timecode).toInt() == Qt::AlignBottom) { - translate_y += height - doc_height; + translation.setY(translation.y() + height - doc_height); } // Check if we are autoscrolling @@ -151,7 +129,7 @@ void RichTextEffect::redraw(double timecode) } int doc_width = qRound(td.size().width()); - translate_x += qRound(-doc_width + (img.width() + doc_width) * scroll_progress); + translation.setX(translation.x() + qRound(-doc_width + (img.width() + doc_width) * scroll_progress)); } } else if (auto_scroll_dir == SCROLL_UP || auto_scroll_dir == SCROLL_DOWN) { @@ -162,13 +140,13 @@ void RichTextEffect::redraw(double timecode) scroll_progress = 1.0 - scroll_progress; } - translate_y += qRound(-doc_height + (img.height() + doc_height)*scroll_progress); + translation.setY(translation.y() + qRound(-doc_height + (img.height() + doc_height)*scroll_progress)); } QRect clip_rect = img.rect(); - clip_rect.translate(-translate_x, -translate_y); - p.translate(translate_x, translate_y); + clip_rect.translate(-translation); + p.translate(translation); img.fill(Qt::transparent); @@ -208,5 +186,5 @@ void RichTextEffect::redraw(double timecode) bool RichTextEffect::AlwaysUpdate() { - return autoscroll->GetValueAt(autoscroll->Now()).toInt() != SCROLL_OFF; + return autoscroll->GetValueAt(Now()).toInt() != SCROLL_OFF; } diff --git a/effects/internal/richtexteffect.h b/effects/internal/richtexteffect.h index 62c1ecb1b..3853d07ac 100644 --- a/effects/internal/richtexteffect.h +++ b/effects/internal/richtexteffect.h @@ -31,19 +31,18 @@ public: protected: virtual bool AlwaysUpdate() override; private: - StringField* text_val; - DoubleField* padding_field; - DoubleField* position_x; - DoubleField* position_y; - ComboField* vertical_align; - ComboField* autoscroll; + StringInput* text_val; + DoubleInput* padding_field; + Vec2Input* position; + ComboInput* vertical_align; + ComboInput* autoscroll; - BoolField* shadow_bool; - DoubleField* shadow_angle; - DoubleField* shadow_distance; - ColorField* shadow_color; - DoubleField* shadow_softness; - DoubleField* shadow_opacity; + BoolInput* shadow_bool; + DoubleInput* shadow_angle; + DoubleInput* shadow_distance; + ColorInput* shadow_color; + DoubleInput* shadow_softness; + DoubleInput* shadow_opacity; }; #endif // RICHTEXTEFFECT_H diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 32b103969..786812e9d 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -35,18 +35,15 @@ ShakeEffect::ShakeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { SetFlags(Effect::CoordsFlag); - EffectRow* intensity_row = new EffectRow(this, tr("Intensity")); - intensity_val = new DoubleField(intensity_row, "intensity"); + intensity_val = new DoubleInput(this, "intensity", tr("Intensity")); intensity_val->SetMinimum(0); intensity_val->SetDefault(25); - EffectRow* rotation_row = new EffectRow(this, tr("Rotation")); - rotation_val = new DoubleField(rotation_row, "rotation"); + rotation_val = new DoubleInput(this, "rotation", tr("Rotation")); rotation_val->SetMinimum(0); rotation_val->SetDefault(10); - EffectRow* frequency_row = new EffectRow(this, tr("Frequency")); - frequency_val = new DoubleField(frequency_row, "frequency"); + frequency_val = new DoubleInput(this, "frequency", tr("Frequency")); frequency_val->SetMinimum(0); frequency_val->SetDefault(5); diff --git a/effects/internal/shakeeffect.h b/effects/internal/shakeeffect.h index bed1ff55a..e0bea64fc 100644 --- a/effects/internal/shakeeffect.h +++ b/effects/internal/shakeeffect.h @@ -31,9 +31,9 @@ public: ShakeEffect(Clip* c, const EffectMeta* em); virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; - DoubleField* intensity_val; - DoubleField* rotation_val; - DoubleField* frequency_val; + DoubleInput* intensity_val; + DoubleInput* rotation_val; + DoubleInput* frequency_val; private: double random_vals[RANDOM_VAL_SIZE]; }; diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index c8359fb7b..1fb51f51b 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -40,24 +40,20 @@ SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : SetFlags(Effect::SuperimposeFlag); // Field for solid type - EffectRow* type_row = new EffectRow(this, tr("Type")); - solid_type = new ComboField(type_row, "type"); + solid_type = new ComboInput(this, "type", tr("Type")); solid_type->AddItem(tr("Solid Color"), SOLID_TYPE_COLOR); solid_type->AddItem(tr("SMPTE Bars"), SOLID_TYPE_BARS); solid_type->AddItem(tr("Checkerboard"), SOLID_TYPE_CHECKERBOARD); - EffectRow* opacity_row = new EffectRow(this, tr("Opacity")); - opacity_field = new DoubleField(opacity_row, "opacity"); + opacity_field = new DoubleInput(this, "opacity", tr("Opacity")); opacity_field->SetMinimum(0); opacity_field->SetDefault(100); opacity_field->SetMaximum(100); - EffectRow* solid_color_row = new EffectRow(this, tr("Color")); - solid_color_field = new ColorField(solid_color_row, "color"); + solid_color_field = new ColorInput(this, "color", tr("Color")); solid_color_field->SetValueAt(0, QColor(Qt::red)); - EffectRow* checkerboard_size = new EffectRow(this, tr("Checkerboard Size")); - checkerboard_size_field = new DoubleField(checkerboard_size, "checker_size"); + checkerboard_size_field = new DoubleInput(this, "checker_size", tr("Checkerboard Size")); checkerboard_size_field->SetMinimum(1); checkerboard_size_field->SetDefault(10); diff --git a/effects/internal/solideffect.h b/effects/internal/solideffect.h index a52977da9..26ca04351 100644 --- a/effects/internal/solideffect.h +++ b/effects/internal/solideffect.h @@ -41,10 +41,10 @@ public: private slots: void ui_update(const QVariant &d); private: - ComboField* solid_type; - ColorField* solid_color_field; - DoubleField* opacity_field; - DoubleField* checkerboard_size_field; + ComboInput* solid_type; + ColorInput* solid_color_field; + DoubleInput* opacity_field; + DoubleInput* checkerboard_size_field; }; #endif // SOLIDEFFECT_H diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index d957dfd5b..458ee4d4a 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -47,85 +47,52 @@ TextEffect::TextEffect(Clip* c, const EffectMeta* em) : { SetFlags(Effect::SuperimposeFlag); - EffectRow* text_field = new EffectRow(this, tr("Text")); - text_val = new StringField(text_field, "text", false); - text_val->SetColumnSpan(2); + text_val = new StringInput(this, "text", tr("Text"), false); - EffectRow* font_row = new EffectRow(this, tr("Font")); - set_font_combobox = new FontField(font_row, "font"); - set_font_combobox->SetColumnSpan(2); + set_font_combobox = new FontInput(this, "font", tr("Font")); - EffectRow* size_row = new EffectRow(this, tr("Size")); - size_val = new DoubleField(size_row, "size"); + size_val = new DoubleInput(this, "size", tr("Size")); size_val->SetMinimum(0); - size_val->SetColumnSpan(2); - EffectRow* color_row = new EffectRow(this, tr("Color")); - set_color_button = new ColorField(color_row, "color"); - set_color_button->SetColumnSpan(2); + set_color_button = new ColorInput(this, "color", tr("Color")); - EffectRow* alignment_row = new EffectRow(this, tr("Alignment")); - halign_field = new ComboField(alignment_row, "halign"); + halign_field = new ComboInput(this, "halign", tr("Horizontal Alignment")); halign_field->AddItem(tr("Left"), Qt::AlignLeft); halign_field->AddItem(tr("Center"), Qt::AlignHCenter); halign_field->AddItem(tr("Right"), Qt::AlignRight); halign_field->AddItem(tr("Justify"), Qt::AlignJustify); - valign_field = new ComboField(alignment_row, "valign"); + valign_field = new ComboInput(this, "valign", tr("Vertical Alignment")); valign_field->AddItem(tr("Top"), Qt::AlignTop); valign_field->AddItem(tr("Center"), Qt::AlignVCenter); valign_field->AddItem(tr("Bottom"), Qt::AlignBottom); - EffectRow* word_wrap_row = new EffectRow(this, tr("Word Wrap")); - word_wrap_field = new BoolField(word_wrap_row, "wordwrap"); - word_wrap_field->SetColumnSpan(2); + word_wrap_field = new BoolInput(this, "wordwrap", tr("Word Wrap")); - EffectRow* padding_row = new EffectRow(this, tr("Padding")); - padding_field = new DoubleField(padding_row, "padding"); - padding_field->SetColumnSpan(2); + padding_field = new DoubleInput(this, "padding", tr("Padding")); - EffectRow* position_row = new EffectRow(this, tr("Position")); - position_x = new DoubleField(position_row, "posx"); - position_y = new DoubleField(position_row, "posy"); + position = new Vec2Input(this, "pos", tr("Position")); - EffectRow* outline_row = new EffectRow(this, tr("Outline")); - outline_bool = new BoolField(outline_row, "outline"); - outline_bool->SetColumnSpan(2); + outline_bool = new BoolInput(this, "outline", tr("Outline")); - EffectRow* outline_color_row = new EffectRow(this, tr("Outline Color")); - outline_color = new ColorField(outline_color_row, "outlinecolor"); - outline_color->SetColumnSpan(2); + outline_color = new ColorInput(this, "outlinecolor", tr("Outline Color")); - EffectRow* outline_width_row = new EffectRow(this, tr("Outline Width")); - outline_width = new DoubleField(outline_width_row, "outlinewidth"); - outline_width->SetColumnSpan(2); + outline_width = new DoubleInput(this, "outlinewidth", tr("Outline Width")); outline_width->SetMinimum(0); - EffectRow* shadow_row = new EffectRow(this, tr("Shadow")); - shadow_bool = new BoolField(shadow_row, "shadow"); - shadow_bool->SetColumnSpan(2); + shadow_bool = new BoolInput(this, "shadow", tr("Shadow")); - EffectRow* shadow_color_row = new EffectRow(this, tr("Shadow Color")); - shadow_color = new ColorField(shadow_color_row, "shadowcolor"); - shadow_color->SetColumnSpan(2); + shadow_color = new ColorInput(this, "shadowcolor", tr("Shadow Color")); - EffectRow* shadow_angle_row = new EffectRow(this, tr("Shadow Angle")); - shadow_angle = new DoubleField(shadow_angle_row, "shadowangle"); - shadow_angle->SetColumnSpan(2); + shadow_angle = new DoubleInput(this, "shadowangle", tr("Shadow Angle")); - EffectRow* shadow_distance_row = new EffectRow(this, tr("Shadow Distance")); - shadow_distance = new DoubleField(shadow_distance_row, "shadowdistance"); - shadow_distance->SetColumnSpan(2); + shadow_distance = new DoubleInput(this, "shadowdistance", tr("Shadow Distance")); shadow_distance->SetMinimum(0); - EffectRow* shadow_softness_row = new EffectRow(this, tr("Shadow Softness")); - shadow_softness = new DoubleField(shadow_softness_row, "shadowsoftness"); - shadow_softness->SetColumnSpan(2); + shadow_softness = new DoubleInput(this, "shadowsoftness", tr("Shadow Softness")); shadow_softness->SetMinimum(0); - EffectRow* shadow_opacity_row = new EffectRow(this, tr("Shadow Opacity")); - shadow_opacity = new DoubleField(shadow_opacity_row, "shadowopacity"); - shadow_opacity->SetColumnSpan(2); + shadow_opacity = new DoubleInput(this, "shadowopacity", tr("Shadow Opacity")); shadow_opacity->SetMinimum(0); shadow_opacity->SetMaximum(100); @@ -252,7 +219,7 @@ void TextEffect::redraw(double timecode) { path.addText(text_x, text_y, font, lines.at(i)); } - path.translate(position_x->GetDoubleAt(timecode) + padding, position_y->GetDoubleAt(timecode) + padding); + path.translate(position->GetVector2DAt(timecode).toPointF() + QPointF(padding, padding)); // draw software shadow if (shadow_bool->GetBoolAt(timecode)) { diff --git a/effects/internal/texteffect.h b/effects/internal/texteffect.h index 857ea7ec9..37a71f7bb 100644 --- a/effects/internal/texteffect.h +++ b/effects/internal/texteffect.h @@ -37,27 +37,26 @@ private slots: private: QFont font; - StringField* text_val; - DoubleField* size_val; - ColorField* set_color_button; - FontField* set_font_combobox; - ComboField* halign_field; - ComboField* valign_field; - BoolField* word_wrap_field; - DoubleField* padding_field; - DoubleField* position_x; - DoubleField* position_y; + StringInput* text_val; + DoubleInput* size_val; + ColorInput* set_color_button; + FontInput* set_font_combobox; + ComboInput* halign_field; + ComboInput* valign_field; + BoolInput* word_wrap_field; + DoubleInput* padding_field; + Vec2Input* position; - BoolField* outline_bool; - DoubleField* outline_width; - ColorField* outline_color; + BoolInput* outline_bool; + DoubleInput* outline_width; + ColorInput* outline_color; - BoolField* shadow_bool; - DoubleField* shadow_angle; - DoubleField* shadow_distance; - ColorField* shadow_color; - DoubleField* shadow_softness; - DoubleField* shadow_opacity; + BoolInput* shadow_bool; + DoubleInput* shadow_angle; + DoubleInput* shadow_distance; + ColorInput* shadow_color; + DoubleInput* shadow_softness; + DoubleInput* shadow_opacity; }; diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 613b39383..3353057e3 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -47,43 +47,31 @@ TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) : { SetFlags(Effect::SuperimposeFlag); - EffectRow* tc_row = new EffectRow(this, tr("Timecode")); - tc_select = new ComboField(tc_row, "tc_selector"); + tc_select = new ComboInput(this, "tc_selector", tr("Timecode")); tc_select->AddItem(tr("Sequence"), true); tc_select->AddItem(tr("Media"), false); tc_select->SetValueAt(0, true); - EffectRow* scale_row = new EffectRow(this, tr("Scale")); - scale_val = new DoubleField(scale_row, "scale"); - scale_val->SetColumnSpan(2); + scale_val = new DoubleInput(this, "scale", tr("Scale")); scale_val->SetMinimum(1); scale_val->SetDefault(100); scale_val->SetMaximum(1000); - EffectRow* color_row = new EffectRow(this, tr("Color")); - color_val = new ColorField(color_row, "color"); - color_val->SetColumnSpan(2); + color_val = new ColorInput(this, "color", tr("Color")); color_val->SetValueAt(0, QColor(Qt::white)); - EffectRow* color_bg_row = new EffectRow(this, tr("Background Color")); - color_bg_val = new ColorField(color_bg_row, "bgcolor"); - color_bg_val->SetColumnSpan(2); + color_bg_val = new ColorInput(this, "bgcolor", tr("Background Color")); color_bg_val->SetValueAt(0, QColor(Qt::black)); - EffectRow* bg_alpha_row = new EffectRow(this, tr("Background Opacity")); - bg_alpha = new DoubleField(bg_alpha_row, "bgalpha"); - bg_alpha->SetColumnSpan(2); + bg_alpha = new DoubleInput(this, "bgalpha", tr("Background Opacity")); bg_alpha->SetMinimum(0); bg_alpha->SetDefault(50); bg_alpha->SetMaximum(100); - EffectRow* offset_row = new EffectRow(this, tr("Offset")); - offset_x_val = new DoubleField(offset_row, "offsetx"); - offset_y_val = new DoubleField(offset_row, "offsety"); + offset_val = new Vec2Input(this, "offset", tr("Offset")); + offset_val->SetDefault(0); - EffectRow* prepent_text_row = new EffectRow(this, tr("Prepend")); - prepend_text = new StringField(prepent_text_row, "prepend", false); - prepend_text->SetColumnSpan(2); + prepend_text = new StringInput(this, "prepend", tr("Prepend"), false); } @@ -116,18 +104,17 @@ void TimecodeEffect::redraw(double timecode) { QPainterPath path; - int text_x, text_y, rect_y, offset_x, offset_y; + int text_x, text_y, rect_y; int text_height = fm.height(); int text_width = fm.width(display_timecode); QColor background_color = color_bg_val->GetColorAt(timecode); int alpha_val = qCeil(bg_alpha->GetDoubleAt(timecode)*2.55); background_color.setAlpha(alpha_val); - offset_x = int(offset_x_val->GetDoubleAt(timecode)); - offset_y = int(offset_y_val->GetDoubleAt(timecode)); + QVector2D offset = offset_val->GetVector2DAt(timecode); - text_x = offset_x + (width/2) - (text_width/2); - text_y = offset_y + height - height/10; + text_x = offset.x() + (width/2) - (text_width/2); + text_y = offset.y() + height - height/10; rect_y = text_y + fm.descent() - text_height; path.addText(text_x, text_y, font, display_timecode); diff --git a/effects/internal/timecodeeffect.h b/effects/internal/timecodeeffect.h index 743796199..693452d06 100644 --- a/effects/internal/timecodeeffect.h +++ b/effects/internal/timecodeeffect.h @@ -31,14 +31,13 @@ class TimecodeEffect : public Effect { public: TimecodeEffect(Clip* c, const EffectMeta *em); virtual void redraw(double timecode) override; - DoubleField* scale_val; - ColorField* color_val; - ColorField* color_bg_val; - DoubleField* bg_alpha; - DoubleField* offset_x_val; - DoubleField* offset_y_val; - StringField* prepend_text; - ComboField* tc_select; + DoubleInput* scale_val; + ColorInput* color_val; + ColorInput* color_bg_val; + DoubleInput* bg_alpha; + Vec2Input* offset_val; + StringInput* prepend_text; + ComboInput* tc_select; protected: virtual bool AlwaysUpdate() override; diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index 914d8ef78..c207f76bb 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -28,24 +28,20 @@ #include "timeline/sequence.h" ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) { - EffectRow* type_row = new EffectRow(this, tr("Type")); - type_val = new ComboField(type_row, "type"); + type_val = new ComboInput(this, "type", tr("Type")); type_val->AddItem(tr("Sine"), TONE_TYPE_SINE); - EffectRow* frequency_row = new EffectRow(this, tr("Frequency")); - freq_val = new DoubleField(frequency_row, "frequency"); + freq_val = new DoubleInput(this, "frequency", tr("Frequency")); freq_val->SetMinimum(20); freq_val->SetMaximum(20000); freq_val->SetDefault(1000); - EffectRow* amount_row = new EffectRow(this, tr("Amount")); - amount_val = new DoubleField(amount_row, "amount"); + amount_val = new DoubleInput(this, "amount", tr("Amount")); amount_val->SetMinimum(0); amount_val->SetMaximum(100); amount_val->SetDefault(25); - EffectRow* mix_row = new EffectRow(this, tr("Mix")); - mix_val = new BoolField(mix_row, "mix"); + mix_val = new BoolInput(this, "mix", tr("Mix")); mix_val->SetValueAt(0, true); } diff --git a/effects/internal/toneeffect.h b/effects/internal/toneeffect.h index 396bb5806..4420fbbb1 100644 --- a/effects/internal/toneeffect.h +++ b/effects/internal/toneeffect.h @@ -34,10 +34,10 @@ public: int channel_count, int type) override; - ComboField* type_val; - DoubleField* freq_val; - DoubleField* amount_val; - BoolField* mix_val; + ComboInput* type_val; + DoubleInput* freq_val; + DoubleInput* amount_val; + BoolInput* mix_val; private: int sinX; }; diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index 425c00bae..c48ec341d 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -46,116 +46,86 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { SetFlags(Effect::CoordsFlag); - EffectRow* position_row = new EffectRow(this, tr("Position")); + position = new Vec2Input(this, "pos", tr("Position")); - position_x = new DoubleField(position_row, "posx"); // position X - position_y = new DoubleField(position_row, "posy"); // position Y + scale = new Vec2Input(this, "scale", tr("Scale")); + scale->SetMinimum(0); + scale->SetDefault(100); - EffectRow* scale_row = new EffectRow(this, tr("Scale")); + uniform_scale_field = new BoolInput(this, "uniformscale", tr("Uniform Scale")); + connect(uniform_scale_field, SIGNAL(Toggled(bool)), scale, SLOT(SetSingleValueMode(bool))); + uniform_scale_field->SetValueAt(0, true); - // scale X (and Y is uniform scale is selected) - scale_x = new DoubleField(scale_row, "scalex"); - scale_x->SetMinimum(0); + rotation = new DoubleInput(this, "rotation", tr("Rotation")); - // scale Y (disabled if uniform scale is selected) - scale_y = new DoubleField(scale_row, "scaley"); - scale_y->SetMinimum(0); - - EffectRow* uniform_scale_row = new EffectRow(this, tr("Uniform Scale")); - - uniform_scale_field = new BoolField(uniform_scale_row, "uniformscale"); // uniform scale option - - EffectRow* rotation_row = new EffectRow(this, tr("Rotation")); - - rotation = new DoubleField(rotation_row, "rotation"); - - EffectRow* anchor_point_row = new EffectRow(this, tr("Anchor Point")); - - anchor_x_box = new DoubleField(anchor_point_row, "anchorx"); // anchor point X - anchor_y_box = new DoubleField(anchor_point_row, "anchory"); // anchor point Y - - EffectRow* opacity_row = new EffectRow(this, tr("Opacity")); + anchor_point = new Vec2Input(this, "anchor", tr("Anchor Point")); + anchor_point->SetDefault(0); // opacity - opacity = new DoubleField(opacity_row, "opacity"); + opacity = new DoubleInput(this, "opacity", tr("Opacity")); opacity->SetMinimum(0); opacity->SetMaximum(100); - - EffectRow* blend_mode_row = new EffectRow(this, tr("Blend Mode")); - - // blend mode - blend_mode_box = new ComboField(blend_mode_row, "blendmode"); - blend_mode_box->SetColumnSpan(2); - blend_mode_box->AddItem(tr("Normal"), -1); + opacity->SetDefault(100); // set up gizmos top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); - top_left_gizmo->x_field1 = scale_x; + top_left_gizmo->x_field1 = static_cast(scale->Field(0)); top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_center_gizmo->set_cursor(Qt::SizeVerCursor); - top_center_gizmo->y_field1 = scale_x; + top_center_gizmo->y_field1 = static_cast(scale->Field(0)); top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_right_gizmo->set_cursor(Qt::SizeBDiagCursor); - top_right_gizmo->x_field1 = scale_x; + top_right_gizmo->x_field1 = static_cast(scale->Field(0)); bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor); - bottom_left_gizmo->x_field1 = scale_x; + bottom_left_gizmo->x_field1 = static_cast(scale->Field(0)); bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_center_gizmo->set_cursor(Qt::SizeVerCursor); - bottom_center_gizmo->y_field1 = scale_x; + bottom_center_gizmo->y_field1 = static_cast(scale->Field(0)); bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT); bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor); - bottom_right_gizmo->x_field1 = scale_x; + bottom_right_gizmo->x_field1 = static_cast(scale->Field(0)); left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); left_center_gizmo->set_cursor(Qt::SizeHorCursor); - left_center_gizmo->x_field1 = scale_x; + left_center_gizmo->x_field1 = static_cast(scale->Field(0)); right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT); right_center_gizmo->set_cursor(Qt::SizeHorCursor); - right_center_gizmo->x_field1 = scale_x; + right_center_gizmo->x_field1 = static_cast(scale->Field(0)); anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET); anchor_gizmo->set_cursor(Qt::SizeAllCursor); - anchor_gizmo->x_field1 = anchor_x_box; - anchor_gizmo->y_field1 = anchor_y_box; - anchor_gizmo->x_field2 = position_x; - anchor_gizmo->y_field2 = position_y; + anchor_gizmo->x_field1 = static_cast(anchor_point->Field(0)); + anchor_gizmo->y_field1 = static_cast(anchor_point->Field(1)); + anchor_gizmo->x_field2 = static_cast(position->Field(0)); + anchor_gizmo->y_field2 = static_cast(position->Field(1)); rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT); rotate_gizmo->color = Qt::green; rotate_gizmo->set_cursor(Qt::SizeAllCursor); - rotate_gizmo->x_field1 = rotation; + rotate_gizmo->x_field1 = static_cast(rotation->Field(0)); rect_gizmo = add_gizmo(GIZMO_TYPE_POLY); - rect_gizmo->x_field1 = position_x; - rect_gizmo->y_field1 = position_y; + rect_gizmo->x_field1 = static_cast(position->Field(0)); + rect_gizmo->y_field1 = static_cast(position->Field(1)); connect(uniform_scale_field, SIGNAL(Toggled(bool)), this, SLOT(toggle_uniform_scale(bool))); - // set defaults - uniform_scale_field->SetValueAt(0, true); - blend_mode_box->SetValueAt(0, -1); - anchor_x_box->SetDefault(0); - anchor_y_box->SetDefault(0); - opacity->SetDefault(100); - scale_x->SetDefault(100); - scale_y->SetDefault(100); - refresh(); } void TransformEffect::refresh() { if (parent_clip != nullptr && parent_clip->track()->sequence() != nullptr) { - position_x->SetDefault(parent_clip->track()->sequence()->width/2); - position_y->SetDefault(parent_clip->track()->sequence()->height/2); + position->SetDefault({float(parent_clip->track()->sequence()->width)*0.5f, + float(parent_clip->track()->sequence()->height)*0.5f}); double x_percent_multipler = 200.0 / parent_clip->track()->sequence()->width; double y_percent_multipler = 200.0 / parent_clip->track()->sequence()->height; @@ -178,7 +148,10 @@ void TransformEffect::refresh() { } void TransformEffect::toggle_uniform_scale(bool enabled) { - scale_y->SetEnabled(!enabled); + scale->SetSingleValueMode(enabled); + + DoubleField* scale_x = static_cast(scale->Field(0)); + DoubleField* scale_y = static_cast(scale->Field(1)); top_center_gizmo->y_field1 = enabled ? scale_x : scale_y; bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y; @@ -190,29 +163,23 @@ void TransformEffect::toggle_uniform_scale(bool enabled) { void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) { // position - coords.matrix.translate(position_x->GetDoubleAt(timecode)-(parent_clip->track()->sequence()->width/2), - position_y->GetDoubleAt(timecode)-(parent_clip->track()->sequence()->height/2), - 0); + coords.matrix.translate(position->GetVector2DAt(timecode) + - QVector2D(parent_clip->track()->sequence()->width*0.5f, + parent_clip->track()->sequence()->height*0.5f)); // anchor point - int anchor_x_offset = qRound(anchor_x_box->GetDoubleAt(timecode)); - int anchor_y_offset = qRound(anchor_y_box->GetDoubleAt(timecode)); + QVector2D anchor_val = anchor_point->GetVector2DAt(timecode); - coords.vertex_top_left -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); - coords.vertex_top_right -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); - coords.vertex_bottom_left -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); - coords.vertex_bottom_right -= QVector3D(anchor_x_offset, anchor_y_offset, 0.0f); + coords.vertex_top_left -= anchor_val; + coords.vertex_top_right -= anchor_val; + coords.vertex_bottom_left -= anchor_val; + coords.vertex_bottom_right -= anchor_val; // rotation coords.matrix.rotate(QQuaternion::fromEulerAngles(0, 0, rotation->GetDoubleAt(timecode))); // scale - double sx = scale_x->GetDoubleAt(timecode)*0.01; - double sy = (uniform_scale_field->GetBoolAt(timecode)) ? sx : scale_y->GetDoubleAt(timecode)*0.01; - coords.matrix.scale(sx, sy); - - // blend mode - coords.blendmode = blend_mode_box->GetValueAt(timecode).toInt(); + coords.matrix.scale(scale->GetVector2DAt(timecode)*0.01); // opacity coords.opacity *= float(opacity->GetDoubleAt(timecode)*0.01); @@ -239,9 +206,9 @@ void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) { rotate_gizmo->world_pos[0] = QVector3D( float_lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), 0.5f), - float_lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1f), - 0.0f - ); + float_lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1f), + 0.0f + ); rect_gizmo->world_pos[0] = coords.vertex_top_left; rect_gizmo->world_pos[1] = coords.vertex_top_right; diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index 42c840559..19c856c28 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -31,19 +31,17 @@ public: virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; virtual void gizmo_draw(double timecode, GLTextureCoords& coords) override; + public slots: void toggle_uniform_scale(bool enabled); + private: - DoubleField* position_x; - DoubleField* position_y; - DoubleField* scale_x; - DoubleField* scale_y; - BoolField* uniform_scale_field; - DoubleField* rotation; - DoubleField* anchor_x_box; - DoubleField* anchor_y_box; - DoubleField* opacity; - ComboField* blend_mode_box; + Vec2Input* position; + Vec2Input* scale; + BoolInput* uniform_scale_field; + DoubleInput* rotation; + Vec2Input* anchor_point; + DoubleInput* opacity; EffectGizmo* top_left_gizmo; EffectGizmo* top_center_gizmo; diff --git a/effects/internal/voideffect.cpp b/effects/internal/voideffect.cpp index 435d32e94..a82aaffdd 100644 --- a/effects/internal/voideffect.cpp +++ b/effects/internal/voideffect.cpp @@ -34,9 +34,8 @@ VoidEffect::VoidEffect(Clip* c, const QString& n) : Effect(c, nullptr) { } else { display_name = n; } - EffectRow* row = new EffectRow(this, tr("Missing Effect"), false, false); - new LabelField(row, display_name); + new LabelWidget(this, tr("Missing Effect"), display_name); name = display_name; diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index 102b02b90..c400ff89b 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -29,8 +29,7 @@ #include "ui/collapsiblewidget.h" VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - EffectRow* volume_row = new EffectRow(this, tr("Volume")); - volume_val = new DoubleField(volume_row, "volume"); + volume_val = new DoubleInput(this, "volume", tr("Volume")); // set defaults volume_val->SetDefault(1); diff --git a/effects/internal/volumeeffect.h b/effects/internal/volumeeffect.h index c4a7a6180..19c855217 100644 --- a/effects/internal/volumeeffect.h +++ b/effects/internal/volumeeffect.h @@ -34,7 +34,7 @@ public: int channel_count, int type) override; - DoubleField* volume_val; + DoubleInput* volume_val; }; #endif // VOLUMEEFFECT_H diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp index bbde8503a..954f658c0 100644 --- a/effects/internal/vsthost.cpp +++ b/effects/internal/vsthost.cpp @@ -236,13 +236,10 @@ VSTHost::VSTHost(Clip* c, const EffectMeta *em) : { plugin = nullptr; - EffectRow* file_row = new EffectRow(this, tr("Plugin"), true, false); - file_field = new FileField(file_row, "filename"); + file_field = new FileInput(this, "filename", tr("Plugin"), true, false); connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()), Qt::QueuedConnection); - EffectRow* interface_row = new EffectRow(this, tr("Interface"), false, false); - - show_interface_btn = new ButtonField(interface_row, tr("Show")); + show_interface_btn = new ButtonWidget(this, tr("Interface"), tr("Show")); show_interface_btn->SetCheckable(true); show_interface_btn->SetEnabled(false); connect(show_interface_btn, SIGNAL(Toggled(bool)), this, SLOT(show_interface(bool))); diff --git a/effects/internal/vsthost.h b/effects/internal/vsthost.h index f934750d8..301d2ef89 100644 --- a/effects/internal/vsthost.h +++ b/effects/internal/vsthost.h @@ -65,8 +65,8 @@ private slots: void uncheck_show_button(); void change_plugin(); private: - FileField* file_field; - ButtonField* show_interface_btn; + FileInput* file_field; + ButtonWidget* show_interface_btn; void loadPlugin(); void freePlugin(); diff --git a/effects/shaders/boxblur.xml b/effects/shaders/boxblur.xml index 59bf40ca2..b430794c9 100644 --- a/effects/shaders/boxblur.xml +++ b/effects/shaders/boxblur.xml @@ -1,13 +1,7 @@ - - - - - - - - - + + + \ No newline at end of file diff --git a/effects/transition.cpp b/effects/transition.cpp index 469aa59dd..5c1809d97 100644 --- a/effects/transition.cpp +++ b/effects/transition.cpp @@ -44,8 +44,7 @@ Transition::Transition(Clip *c, Clip *s, const EffectMeta* em) : Effect(c, em), secondary_clip(s) { - EffectRow* length_row = new EffectRow(this, tr("Length"), false, false); - length_field = new DoubleField(length_row, "length"); + length_field = new DoubleInput(this, "length", tr("Length"), false, false); length_field->SetDefault(30); length_field->SetMinimum(1); length_field->SetDisplayType(LabelSlider::FrameNumber); diff --git a/effects/transition.h b/effects/transition.h index 01a47cd8b..20adde1ba 100644 --- a/effects/transition.h +++ b/effects/transition.h @@ -61,7 +61,7 @@ public: static TransitionPtr CreateFromMeta(Clip *c, Clip *s, const EffectMeta* em); private: - DoubleField* length_field; + DoubleInput* length_field; private slots: void UpdateMaximumLength(); diff --git a/nodes/inputs.h b/nodes/inputs.h new file mode 100644 index 000000000..796cbaffb --- /dev/null +++ b/nodes/inputs.h @@ -0,0 +1,15 @@ +#ifndef INPUTS_H +#define INPUTS_H + +#include "inputs/vecinput.h" +#include "inputs/boolinput.h" +#include "inputs/comboinput.h" +#include "inputs/colorinput.h" +#include "inputs/stringinput.h" +#include "inputs/fileinput.h" +#include "inputs/fontinput.h" + +#include "widgets/labelwidget.h" +#include "widgets/buttonwidget.h" + +#endif // INPUTS_H diff --git a/nodes/inputs/boolinput.cpp b/nodes/inputs/boolinput.cpp new file mode 100644 index 000000000..7ae067ed0 --- /dev/null +++ b/nodes/inputs/boolinput.cpp @@ -0,0 +1,14 @@ +#include "boolinput.h" + +BoolInput::BoolInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + BoolField* bool_field = new BoolField(this); + connect(bool_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool))); + AddField(bool_field); +} + +bool BoolInput::GetBoolAt(double timecode) +{ + return static_cast(Field(0))->GetBoolAt(timecode); +} diff --git a/nodes/inputs/boolinput.h b/nodes/inputs/boolinput.h new file mode 100644 index 000000000..d04c89a6f --- /dev/null +++ b/nodes/inputs/boolinput.h @@ -0,0 +1,36 @@ +#ifndef BOOLINPUT_H +#define BOOLINPUT_H + +#include "effects/effectrow.h" + +class BoolInput : public EffectRow +{ + Q_OBJECT +public: + BoolInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + /** + * @brief Get the boolean value at a given timecode + * + * A wrapper for BoolField::GetBoolAt(). + * + * @param timecode + * + * The timecode to retrieve the value at + * + * @return + * + * The boolean value at this timecode + */ + bool GetBoolAt(double timecode); + +signals: + /** + * @brief Emitted whenever the UI widget's boolean value has changed + * + * Wrapper for BoolField::Toggled(). + */ + void Toggled(bool); +}; + +#endif // BOOLINPUT_H diff --git a/nodes/inputs/colorinput.cpp b/nodes/inputs/colorinput.cpp new file mode 100644 index 000000000..eab5010f4 --- /dev/null +++ b/nodes/inputs/colorinput.cpp @@ -0,0 +1,12 @@ +#include "colorinput.h" + +ColorInput::ColorInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + AddField(new ColorField(this)); +} + +QColor ColorInput::GetColorAt(double timecode) +{ + static_cast(Field(0))->GetColorAt(timecode); +} diff --git a/nodes/inputs/colorinput.h b/nodes/inputs/colorinput.h new file mode 100644 index 000000000..d46acfd38 --- /dev/null +++ b/nodes/inputs/colorinput.h @@ -0,0 +1,28 @@ +#ifndef COLORINPUT_H +#define COLORINPUT_H + +#include "effects/effectrow.h" + +class ColorInput : public EffectRow +{ + Q_OBJECT +public: + ColorInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + /** + * @brief Get the color value at a given timecode + * + * Wrapper for ColorField::GetColorAt(). + * + * @param timecode + * + * The timecode to retrieve the color at + * + * @return + * + * The color value at this timecode + */ + QColor GetColorAt(double timecode); +}; + +#endif // COLORINPUT_H diff --git a/nodes/inputs/comboinput.cpp b/nodes/inputs/comboinput.cpp new file mode 100644 index 000000000..aadeeef41 --- /dev/null +++ b/nodes/inputs/comboinput.cpp @@ -0,0 +1,14 @@ +#include "comboinput.h" + +ComboInput::ComboInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + ComboField* combo_field = new ComboField(this); + connect(combo_field, SIGNAL(DataChanged(const QVariant&)), this, SIGNAL(DataChanged(const QVariant&))); + AddField(combo_field); +} + +void ComboInput::AddItem(const QString &text, const QVariant &data) +{ + static_cast(Field(0))->AddItem(text, data); +} diff --git a/nodes/inputs/comboinput.h b/nodes/inputs/comboinput.h new file mode 100644 index 000000000..d48eae16d --- /dev/null +++ b/nodes/inputs/comboinput.h @@ -0,0 +1,36 @@ +#ifndef COMBOINPUT_H +#define COMBOINPUT_H + +#include "effects/effectrow.h" + +class ComboInput : public EffectRow +{ + Q_OBJECT +public: + ComboInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + /** + * @brief Add an item to this ComboInput + * + * Wrapper for ComboField::AddItem. + * + * @param text + * + * The text to show at this index. + * + * @param data + * + * The data to be retrieved at this index. + */ + void AddItem(const QString& text, const QVariant& data); + +signals: + /** + * @brief Signal emitted whenever a connected widget's data gets changed + * + * Wrapper for ComboField::DataChanged. + */ + void DataChanged(const QVariant&); +}; + +#endif // COMBOINPUT_H diff --git a/nodes/inputs/doubleinput.cpp b/nodes/inputs/doubleinput.cpp deleted file mode 100644 index dded84c1f..000000000 --- a/nodes/inputs/doubleinput.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "doubleinput.h" - -DoubleInput::DoubleInput() -{ - -} diff --git a/nodes/inputs/doubleinput.h b/nodes/inputs/doubleinput.h deleted file mode 100644 index 58e468496..000000000 --- a/nodes/inputs/doubleinput.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef DOUBLEINPUT_H -#define DOUBLEINPUT_H - -#include "effects/effectrow.h" - -class DoubleInput : public EffectRow -{ -public: - DoubleInput(); -}; - -#endif // DOUBLEINPUT_H diff --git a/nodes/inputs/fileinput.cpp b/nodes/inputs/fileinput.cpp new file mode 100644 index 000000000..011506747 --- /dev/null +++ b/nodes/inputs/fileinput.cpp @@ -0,0 +1,12 @@ +#include "fileinput.h" + +FileInput::FileInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + AddField(new FileField(this)); +} + +QString FileInput::GetFileAt(double timecode) +{ + return static_cast(Field(0))->GetFileAt(timecode); +} diff --git a/nodes/inputs/fileinput.h b/nodes/inputs/fileinput.h new file mode 100644 index 000000000..9473a9c13 --- /dev/null +++ b/nodes/inputs/fileinput.h @@ -0,0 +1,28 @@ +#ifndef FILEINPUT_H +#define FILEINPUT_H + +#include "effects/effectrow.h" + +class FileInput : public EffectRow +{ + Q_OBJECT +public: + FileInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + /** + * @brief Get the filename at the given timecode + * + * A convenience function, equivalent to GetValueAt(timecode).toString() + * + * @param timecode + * + * The timecode to retrieve the filename at + * + * @return + * + * The filename at this timecode + */ + QString GetFileAt(double timecode); +}; + +#endif // FILEINPUT_H diff --git a/nodes/inputs/fontinput.cpp b/nodes/inputs/fontinput.cpp new file mode 100644 index 000000000..3e0041136 --- /dev/null +++ b/nodes/inputs/fontinput.cpp @@ -0,0 +1,12 @@ +#include "fontinput.h" + +FontInput::FontInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + AddField(new FontField(this)); +} + +QString FontInput::GetFontAt(double timecode) +{ + return static_cast(Field(0))->GetFontAt(timecode); +} diff --git a/nodes/inputs/fontinput.h b/nodes/inputs/fontinput.h new file mode 100644 index 000000000..b1ac07979 --- /dev/null +++ b/nodes/inputs/fontinput.h @@ -0,0 +1,28 @@ +#ifndef FONTINPUT_H +#define FONTINPUT_H + +#include "effects/effectrow.h" + +class FontInput : public EffectRow +{ + Q_OBJECT +public: + FontInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + /** + * @brief Get the font family name at the given timecode + * + * Wrapper for FontField::GetFontAt(). + * + * @param timecode + * + * The timecode to retrieve the font family name at + * + * @return + * + * The font family name at this timecode + */ + QString GetFontAt(double timecode); +}; + +#endif // FONTINPUT_H diff --git a/nodes/inputs/stringinput.cpp b/nodes/inputs/stringinput.cpp new file mode 100644 index 000000000..0169c3826 --- /dev/null +++ b/nodes/inputs/stringinput.cpp @@ -0,0 +1,12 @@ +#include "stringinput.h" + +StringInput::StringInput(Effect* parent, const QString& id, const QString& name, bool rich_text, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable) +{ + AddField(new StringField(this, rich_text)); +} + +QString StringInput::GetStringAt(double timecode) +{ + return static_cast(Field(0))->GetStringAt(timecode); +} diff --git a/nodes/inputs/stringinput.h b/nodes/inputs/stringinput.h new file mode 100644 index 000000000..4498bbae9 --- /dev/null +++ b/nodes/inputs/stringinput.h @@ -0,0 +1,33 @@ +#ifndef STRINGINPUT_H +#define STRINGINPUT_H + +#include "effects/effectrow.h" + +class StringInput : public EffectRow +{ + Q_OBJECT +public: + StringInput(Effect* parent, + const QString& id, + const QString& name, + bool rich_text = true, + bool savable = true, + bool keyframable = true); + + /** + * @brief Get the string at the given timecode + * + * Wrappre for StringField::GetStringAt(). + * + * @param timecode + * + * The timecode to retrieve the string at + * + * @return + * + * The string at this timecode + */ + QString GetStringAt(double timecode); +}; + +#endif // STRINGINPUT_H diff --git a/nodes/inputs/vecinput.cpp b/nodes/inputs/vecinput.cpp new file mode 100644 index 000000000..82e74ea60 --- /dev/null +++ b/nodes/inputs/vecinput.cpp @@ -0,0 +1,186 @@ +#include "vecinput.h" + +#include +#include +#include + +VecInput::VecInput(Effect* parent, const QString& id, const QString& name, int values, bool savable, bool keyframable) : + EffectRow(parent, id, name, savable, keyframable), + single_value_mode_(false), + values_(values) +{ + Q_ASSERT(values_ >= 1 && values_ <= 4); + + for (int i=0;i(Field(i))->SetMinimum(minimum); + } +} + +void VecInput::SetMaximum(double maximum) +{ + for (int i=0;i(Field(i))->SetMaximum(maximum); + } +} + +void VecInput::SetDefault(double def) +{ + for (int i=0;i(Field(i))->SetDefault(def); + } +} + +void VecInput::SetDefault(const QVector &def) +{ + for (int i=0;i(Field(i))->SetDefault(def.at(i)); + } +} + +void VecInput::SetDisplayType(LabelSlider::DisplayType type) +{ + for (int i=0;i(Field(i))->SetDisplayType(type); + } +} + +void VecInput::SetFrameRate(const double &rate) +{ + for (int i=0;i(Field(i))->SetFrameRate(rate); + } +} + +void VecInput::SetSingleValueMode(bool on) +{ + qDebug() << "i'm here"; + + single_value_mode_ = on; + + // Disable all fields except the first + for (int i=1;iSetEnabled(!single_value_mode_); + } +} + +DoubleInput::DoubleInput(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : + VecInput(parent, id, name, 1, savable, keyframable) +{ +} + +double DoubleInput::GetDoubleAt(double timecode) +{ + return static_cast(Field(0))->GetDoubleAt(timecode); +} + +Vec2Input::Vec2Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : + VecInput(parent, id, name, 2, savable, keyframable) +{ +} + +QVector2D Vec2Input::GetVector2DAt(double timecode) +{ + return GetValueAt(timecode).value(); +} + +QVariant Vec2Input::GetValueAt(double timecode) +{ + QVector2D vec2; + vec2.setX(static_cast(Field(0))->GetDoubleAt(timecode)); + + if (single_value_mode_) { + vec2.setY(static_cast(Field(0))->GetDoubleAt(timecode)); + } else { + vec2.setY(static_cast(Field(1))->GetDoubleAt(timecode)); + } + + return vec2; +} + +void Vec2Input::SetValueAt(double timecode, const QVariant &value) +{ + QVector2D vec2 = value.value(); + + static_cast(Field(0))->SetValueAt(timecode, vec2.x()); + static_cast(Field(1))->SetValueAt(timecode, vec2.y()); +} + +Vec3Input::Vec3Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : + VecInput(parent, id, name, 3, savable, keyframable) +{ +} + +QVector3D Vec3Input::GetVector3DAt(double timecode) +{ + return GetValueAt(timecode).value(); +} + +QVariant Vec3Input::GetValueAt(double timecode) +{ + QVector3D vec3; + vec3.setX(static_cast(Field(0))->GetDoubleAt(timecode)); + + if (single_value_mode_) { + vec3.setY(static_cast(Field(0))->GetDoubleAt(timecode)); + vec3.setZ(static_cast(Field(0))->GetDoubleAt(timecode)); + } else { + vec3.setY(static_cast(Field(1))->GetDoubleAt(timecode)); + vec3.setZ(static_cast(Field(2))->GetDoubleAt(timecode)); + } + + return vec3; +} + +void Vec3Input::SetValueAt(double timecode, const QVariant &value) +{ + QVector3D vec3 = value.value(); + + static_cast(Field(0))->SetValueAt(timecode, vec3.x()); + static_cast(Field(1))->SetValueAt(timecode, vec3.y()); + static_cast(Field(2))->SetValueAt(timecode, vec3.z()); +} + +Vec4Input::Vec4Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : + VecInput(parent, id, name, 4, savable, keyframable) +{ +} + +QVector4D Vec4Input::GetVector4DAt(double timecode) +{ + return GetValueAt(timecode).value(); +} + +QVariant Vec4Input::GetValueAt(double timecode) +{ + QVector4D vec4; + vec4.setX(static_cast(Field(0))->GetDoubleAt(timecode)); + + if (single_value_mode_) { + vec4.setY(static_cast(Field(0))->GetDoubleAt(timecode)); + vec4.setZ(static_cast(Field(0))->GetDoubleAt(timecode)); + vec4.setZ(static_cast(Field(0))->GetDoubleAt(timecode)); + } else { + vec4.setY(static_cast(Field(1))->GetDoubleAt(timecode)); + vec4.setZ(static_cast(Field(2))->GetDoubleAt(timecode)); + vec4.setW(static_cast(Field(3))->GetDoubleAt(timecode)); + } + + return vec4; +} + +void Vec4Input::SetValueAt(double timecode, const QVariant &value) +{ + QVector4D vec4 = value.value(); + + static_cast(Field(0))->SetValueAt(timecode, vec4.x()); + static_cast(Field(1))->SetValueAt(timecode, vec4.y()); + static_cast(Field(2))->SetValueAt(timecode, vec4.z()); + static_cast(Field(3))->SetValueAt(timecode, vec4.w()); +} diff --git a/nodes/inputs/vecinput.h b/nodes/inputs/vecinput.h new file mode 100644 index 000000000..9698308d1 --- /dev/null +++ b/nodes/inputs/vecinput.h @@ -0,0 +1,78 @@ +#ifndef VEC2INPUT_H +#define VEC2INPUT_H + +#include + +#include "effects/effectrow.h" + +class VecInput : public EffectRow +{ + Q_OBJECT +public: + VecInput(Effect* parent, const QString& id, const QString& name, int values, bool savable = true, bool keyframable = true); + + void SetMinimum(double minimum); + void SetMaximum(double maximum); + void SetDefault(double def); + + void SetDefault(const QVector &def); + + /** + * @brief Sets the UI display type to a member of LabelSlider::DisplayType. + */ + void SetDisplayType(LabelSlider::DisplayType type); + + /** + * @brief For a timecode-based display type, sets the frame rate to be used for the displayed timecode + * + * \see SetDisplayType() and LabelSlider::SetFrameRate(). + */ + void SetFrameRate(const double& rate); + +protected: + bool single_value_mode_; + +public slots: + void SetSingleValueMode(bool on); + +private: + + int values_; +}; + +class DoubleInput : public VecInput +{ +public: + DoubleInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + double GetDoubleAt(double timecode); +}; + +class Vec2Input : public VecInput { +public: + Vec2Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + QVector2D GetVector2DAt(double timecode); + virtual QVariant GetValueAt(double timecode) override; + virtual void SetValueAt(double timecode, const QVariant &value) override; +}; + +class Vec3Input : public VecInput { +public: + Vec3Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + QVector3D GetVector3DAt(double timecode); + virtual QVariant GetValueAt(double timecode) override; + virtual void SetValueAt(double timecode, const QVariant &value) override; +}; + +class Vec4Input : public VecInput { +public: + Vec4Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + + QVector4D GetVector4DAt(double timecode); + virtual QVariant GetValueAt(double timecode) override; + virtual void SetValueAt(double timecode, const QVariant &value) override; +}; + +#endif // VEC2INPUT_H diff --git a/nodes/nodedatatypes.h b/nodes/nodedatatypes.h index ebf20eadf..3250457e4 100644 --- a/nodes/nodedatatypes.h +++ b/nodes/nodedatatypes.h @@ -12,13 +12,13 @@ namespace nodes { * Predetermined types of fields. Used throughout Olive to identify what kind of data to expect from GetValueAt(). * * This enum is also currently used to match an external XML effect's fields with the correct derived class (e.g. - * EFFECT_FIELD_DOUBLE matches to DoubleField). + * EFFECT_FIELD_DOUBLE matches to DoubleInput). */ enum DataType { /** Invalid data type. Used only for error handling. */ kInvalid, - /** Values are doubles. Also corresponds to DoubleField. */ + /** Values are doubles. Also corresponds to DoubleInput. */ kFloat, /** Value is an 2-component vector of floats. */ @@ -33,22 +33,22 @@ enum DataType { /** Value is an array of floats. This cannot be an input field, and can only be passed between nodes. */ kArray, - /** Values are colors. Equivalent to kVec4 but represents as a color. Corresponds to ColorField. */ + /** Values are colors. Equivalent to kVec4 but represents as a color. Corresponds to ColorInput. */ kColor, - /** Values are strings. Also corresponds to StringField. */ + /** Values are strings. Also corresponds to StringInput. */ kString, - /** Values are booleans. Also corresponds to BoolField. */ + /** Values are booleans. Also corresponds to BoolInput. */ kBoolean, - /** Values are arbitrary data. Also corresponds to ComboField. */ + /** Values are arbitrary data. Also corresponds to ComboInput. */ kCombo, - /** Values are font family names (in string). Also corresponds to FontField. */ + /** Values are font family names (in string). Also corresponds to FontInput. */ kFont, - /** Values are filenames (in string). Also corresponds to FileField. */ + /** Values are filenames (in string). Also corresponds to FileInput. */ kFile, /** Values are integers. */ diff --git a/nodes/widgets/buttonwidget.cpp b/nodes/widgets/buttonwidget.cpp new file mode 100644 index 000000000..bde083736 --- /dev/null +++ b/nodes/widgets/buttonwidget.cpp @@ -0,0 +1,23 @@ +#include "buttonwidget.h" + +ButtonWidget::ButtonWidget(Effect* parent, const QString& name, const QString& text) : + EffectRow(parent, nullptr, name, false, false) +{ + ButtonField* button_field = new ButtonField(this, text); + + connect(button_field, SIGNAL(CheckedChanged(bool)), this, SIGNAL(CheckedChanged(bool))); + connect(button_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool))); + connect(this, SLOT(SetChecked(bool)), button_field, SLOT(SetChecked(bool))); + + AddField(button_field); +} + +void ButtonWidget::SetCheckable(bool c) +{ + static_cast(Field(0))->SetCheckable(c); +} + +void ButtonWidget::SetChecked(bool c) +{ + static_cast(Field(0))->SetChecked(c); +} diff --git a/nodes/widgets/buttonwidget.h b/nodes/widgets/buttonwidget.h new file mode 100644 index 000000000..e7e068f24 --- /dev/null +++ b/nodes/widgets/buttonwidget.h @@ -0,0 +1,35 @@ +#ifndef BUTTONWIDGET_H +#define BUTTONWIDGET_H + +#include "effects/effectrow.h" + +class ButtonWidget : public EffectRow +{ +public: + ButtonWidget(Effect* parent, const QString& name, const QString& text); + + /** + * @brief Wrapper for ButtonField::SetCheckable. + */ + void SetCheckable(bool c); + +public slots: + /** + * @brief Wrapper for ButtonField::SetChecked() + */ + void SetChecked(bool c); + +signals: + /** + * @brief Wrapper for ButtonField::CheckedChanged() + */ + void CheckedChanged(bool); + + /** + * @brief Wrapper for ButtonField::Toggled() + */ + void Toggled(bool); + +}; + +#endif // BUTTONWIDGET_H diff --git a/nodes/widgets/labelwidget.cpp b/nodes/widgets/labelwidget.cpp new file mode 100644 index 000000000..02396436c --- /dev/null +++ b/nodes/widgets/labelwidget.cpp @@ -0,0 +1,7 @@ +#include "labelwidget.h" + +LabelWidget::LabelWidget(Effect *parent, const QString &name, const QString &text) : + EffectRow(parent, nullptr, name, false, false) +{ + AddField(new LabelField(this, text)); +} diff --git a/nodes/widgets/labelwidget.h b/nodes/widgets/labelwidget.h new file mode 100644 index 000000000..af066f9a4 --- /dev/null +++ b/nodes/widgets/labelwidget.h @@ -0,0 +1,12 @@ +#ifndef LABELWIDGET_H +#define LABELWIDGET_H + +#include "effects/effectrow.h" + +class LabelWidget : public EffectRow +{ +public: + LabelWidget(Effect* parent, const QString& name, const QString& text); +}; + +#endif // LABELWIDGET_H diff --git a/olive.pro b/olive.pro index d9d2f8f17..2c7cb1043 100644 --- a/olive.pro +++ b/olive.pro @@ -194,7 +194,15 @@ SOURCES += \ panels/effectspanel.cpp \ nodes/nodedatatypes.cpp \ nodes/nodeplug.cpp \ - nodes/inputs/doubleinput.cpp + nodes/inputs/boolinput.cpp \ + nodes/inputs/comboinput.cpp \ + nodes/inputs/colorinput.cpp \ + nodes/inputs/stringinput.cpp \ + nodes/inputs/fontinput.cpp \ + nodes/inputs/vecinput.cpp \ + nodes/widgets/labelwidget.cpp \ + nodes/inputs/fileinput.cpp \ + nodes/widgets/buttonwidget.cpp HEADERS += \ ui/mainwindow.h \ @@ -343,7 +351,16 @@ HEADERS += \ panels/effectspanel.h \ nodes/nodedatatypes.h \ nodes/nodeplug.h \ - nodes/inputs/doubleinput.h + nodes/inputs.h \ + nodes/inputs/boolinput.h \ + nodes/inputs/comboinput.h \ + nodes/inputs/colorinput.h \ + nodes/inputs/stringinput.h \ + nodes/inputs/fontinput.h \ + nodes/inputs/vecinput.h \ + nodes/widgets/labelwidget.h \ + nodes/inputs/fileinput.h \ + nodes/widgets/buttonwidget.h FORMS += diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 6430a80fd..370a0cb09 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -149,8 +149,8 @@ void GraphEditor::update_panel() { int slider_index = 0; for (int i=0;iFieldCount();i++) { EffectField* field = row->Field(i); - if (field->type() == olive::nodes::kFloat) { - field->UpdateWidgetValue(field_sliders_.at(slider_index), field->Now()); + if (field->type() == EffectField::EFFECT_FIELD_DOUBLE) { + field->UpdateWidgetValue(field_sliders_.at(slider_index), row->GetParentEffect()->Now()); slider_index++; } } @@ -186,7 +186,7 @@ void GraphEditor::set_row(EffectRow *r) { if (r != nullptr && r->IsKeyframing()) { for (int i=0;iFieldCount();i++) { EffectField* field = r->Field(i); - if (field->type() == olive::nodes::kFloat) { + if (field->type() == EffectField::EFFECT_FIELD_DOUBLE) { QPushButton* slider_button = new QPushButton(); slider_button->setCheckable(true); slider_button->setChecked(field->IsEnabled()); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index c0b3f7a0a..bc3b8774a 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -260,6 +260,13 @@ void Timeline::toggle_show_all() { } } +void Timeline::toggle_links() +{ + if (sequence_ != nullptr) { + sequence_->ToggleLinksOnSelected(); + } +} + void Timeline::add_transition() { ComboAction* ca = new ComboAction(); bool adding = false; diff --git a/panels/timeline.h b/panels/timeline.h index b838e7d09..0606aef06 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -150,6 +150,7 @@ protected: public slots: void repaint_timeline(); void toggle_show_all(); + void toggle_links(); void deselect(); void split_at_playhead(); void ripple_delete(); diff --git a/project/footage.cpp b/project/footage.cpp index e1fd0fc5a..a08d052fc 100644 --- a/project/footage.cpp +++ b/project/footage.cpp @@ -159,8 +159,8 @@ FootageStream* Footage::get_stream_from_file_index(bool video, int index) { QString Footage::get_interlacing_name(int interlacing) { switch (interlacing) { case VIDEO_PROGRESSIVE: return QCoreApplication::translate("InterlacingName", "None (Progressive)"); - case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Top Field First"); - case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Bottom Field First"); + case VIDEO_TOP_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Upper Field First"); + case VIDEO_BOTTOM_FIELD_FIRST: return QCoreApplication::translate("InterlacingName", "Lower Field First"); default: return QCoreApplication::translate("InterlacingName", "Invalid"); } } diff --git a/ui/effectui.cpp b/ui/effectui.cpp index d86658584..b3d03053d 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -127,9 +127,9 @@ EffectUI::EffectUI(Effect* e) : widgets_[i][j] = widget; - layout_->addWidget(widget, i, column, 1, field->GetColumnSpan()); + layout_->addWidget(widget, i, column, 1, 1); - column += field->GetColumnSpan(); + column++; } // Find maximum column to place keyframe controls @@ -237,7 +237,7 @@ void EffectUI::UpdateFromEffect() // Check if this UI object is attached to one effect or many if (additional_effects_.isEmpty()) { - field->UpdateWidgetValue(Widget(j, k), field->Now()); + field->UpdateWidgetValue(Widget(j, k), effect->Now()); } else { @@ -247,14 +247,14 @@ void EffectUI::UpdateFromEffect() EffectField* previous_field = i > 0 ? additional_effects_.at(i-1)->row(j)->Field(k) : field; EffectField* additional_field = additional_effects_.at(i)->row(j)->Field(k); - if (additional_field->GetValueAt(additional_field->Now()) != previous_field->GetValueAt(previous_field->Now())) { + if (additional_field->GetValueAt(additional_effects_.at(i)->Now()) != previous_field->GetValueAt(additional_effects_.at(i-1)->Now())) { same_value = false; break; } } if (same_value) { - field->UpdateWidgetValue(Widget(j, k), field->Now()); + field->UpdateWidgetValue(Widget(j, k), effect->Now()); } else { field->UpdateWidgetValue(Widget(j, k), qSNaN()); } From e73011e192e1c8b963f454a98940e958fe1a3cb5 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 11 Apr 2019 01:40:37 +1000 Subject: [PATCH 107/133] made column span redundant --- effects/effect.h | 2 -- nodes/nodegraph.cpp | 8 ++++++++ nodes/nodegraph.h | 15 +++++++++++++++ olive.pro | 6 ++++-- panels/nodeeditor.cpp | 3 +++ ui/effectui.cpp | 26 ++++++-------------------- ui/texteditex.cpp | 3 +++ 7 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 nodes/nodegraph.cpp create mode 100644 nodes/nodegraph.h diff --git a/effects/effect.h b/effects/effect.h index 00aacb01d..ec1ebca23 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -180,8 +180,6 @@ public: int getIterations(); void setIterations(int i); - const char* ffmpeg_filter; - virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); virtual void process_shader(double timecode, GLTextureCoords&, int iteration); virtual void process_coords(double timecode, GLTextureCoords& coords, int data); diff --git a/nodes/nodegraph.cpp b/nodes/nodegraph.cpp new file mode 100644 index 000000000..8f7a00e03 --- /dev/null +++ b/nodes/nodegraph.cpp @@ -0,0 +1,8 @@ +#include "nodegraph.h" + +#include + +NodeGraph::NodeGraph() +{ + +} diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h new file mode 100644 index 000000000..9bf6cafe2 --- /dev/null +++ b/nodes/nodegraph.h @@ -0,0 +1,15 @@ +#ifndef NODEGRAPH_H +#define NODEGRAPH_H + +#include "effects/effect.h" + +class NodeGraph +{ +public: + NodeGraph(); + +private: + Effect* end_node_; +}; + +#endif // NODEGRAPH_H diff --git a/olive.pro b/olive.pro index 2c7cb1043..16f17add3 100644 --- a/olive.pro +++ b/olive.pro @@ -202,7 +202,8 @@ SOURCES += \ nodes/inputs/vecinput.cpp \ nodes/widgets/labelwidget.cpp \ nodes/inputs/fileinput.cpp \ - nodes/widgets/buttonwidget.cpp + nodes/widgets/buttonwidget.cpp \ + nodes/nodegraph.cpp HEADERS += \ ui/mainwindow.h \ @@ -360,7 +361,8 @@ HEADERS += \ nodes/inputs/vecinput.h \ nodes/widgets/labelwidget.h \ nodes/inputs/fileinput.h \ - nodes/widgets/buttonwidget.h + nodes/widgets/buttonwidget.h \ + nodes/nodegraph.h FORMS += diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 55e67157b..c6310638c 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -17,6 +17,9 @@ NodeEditor::NodeEditor(QWidget *parent) : setWidget(central_widget); QVBoxLayout* layout = new QVBoxLayout(central_widget); + layout->setSpacing(0); + layout->setMargin(0); + layout->addWidget(&view_); view_.setInteractive(true); diff --git a/ui/effectui.cpp b/ui/effectui.cpp index b3d03053d..5e31dd025 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -90,6 +90,7 @@ EffectUI::EffectUI(Effect* e) : ui->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); SetContents(ui); + title_bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); SetExpanded(e->IsExpanded()); @@ -103,9 +104,8 @@ EffectUI::EffectUI(Effect* e) : this, SLOT(show_context_menu(const QPoint&))); - int maximum_column = 0; - widgets_.resize(e->row_count()); + keyframe_navigators_.resize(e->row_count()); for (int i=0;irow_count();i++) { EffectRow* row = e->row(i); @@ -119,7 +119,7 @@ EffectUI::EffectUI(Effect* e) : widgets_[i].resize(row->FieldCount()); - int column = 1; + QGridLayout* field_layout = new QGridLayout(); for (int j=0;jFieldCount();j++) { EffectField* field = row->Field(j); @@ -127,22 +127,9 @@ EffectUI::EffectUI(Effect* e) : widgets_[i][j] = widget; - layout_->addWidget(widget, i, column, 1, 1); - - column++; + field_layout->addWidget(widget, 0, j); } - - // Find maximum column to place keyframe controls - maximum_column = qMax(row->FieldCount(), maximum_column); - } - - // Create keyframe controls - maximum_column++; - - keyframe_navigators_.resize(e->row_count()); - - for (int i=0;irow_count();i++) { - EffectRow* row = e->row(i); + layout_->addLayout(field_layout, i, 1); KeyframeNavigator* nav; @@ -154,7 +141,7 @@ EffectUI::EffectUI(Effect* e) : AttachKeyframeNavigationToRow(row, nav); - layout_->addWidget(nav, i, maximum_column); + layout_->addWidget(nav, i, 2); } else { @@ -163,7 +150,6 @@ EffectUI::EffectUI(Effect* e) : } keyframe_navigators_[i] = nav; - } enabled_check->setChecked(e->IsEnabled()); diff --git a/ui/texteditex.cpp b/ui/texteditex.cpp index bc4a047e3..bdf9a756d 100644 --- a/ui/texteditex.cpp +++ b/ui/texteditex.cpp @@ -33,6 +33,9 @@ TextEditEx::TextEditEx(QWidget *parent, bool enable_rich_text) : { QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); + text_editor_ = new QTextEdit(); connect(text_editor_, SIGNAL(textChanged()), this, SLOT(queue_text_modified())); layout->addWidget(text_editor_); From 358d1ba16a44eb6ef8becedcf48ea2997052eb1c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 11 Apr 2019 10:23:38 +1000 Subject: [PATCH 108/133] audio monitor functional with float system --- rendering/audio.cpp | 26 ++++++++--------------- ui/audiomonitor.cpp | 52 ++++++++++++++++++++++++++++++++------------- ui/audiomonitor.h | 22 ++++++++++++++----- 3 files changed, 63 insertions(+), 37 deletions(-) diff --git a/rendering/audio.cpp b/rendering/audio.cpp index 1faaf4bf1..105524ab9 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -209,33 +209,25 @@ int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { // send audio to device audio_write_lock.lock(); - qint64 actual_write = audio_io_device->write(reinterpret_cast(audio_ibuffer+offset), max); + qint64 actual_write = audio_io_device->write(reinterpret_cast(&audio_ibuffer[offset]), max); - /* if (actual_write > 0) { // average values and send to audio monitor int channels = audio_output->format().channelCount(); - qint64 lim = offset + actual_write; - QVector averages; + qint64 lim = offset + (actual_write/sizeof(float)); + QVector averages; averages.resize(channels); - averages.fill(0); + averages.fill(0.0); - int counter = 0; - qint16 sample; - for (qint64 i=offset;iaudio_monitor->set_value(averages); } - */ - memset(audio_ibuffer+offset, 0, actual_write); + memset(&audio_ibuffer[offset], 0, actual_write); audio_ibuffer_read += (actual_write / sizeof(float)); @@ -246,7 +238,7 @@ int AudioSenderThread::send_audio_to_output(qint64 offset, int max) { double log_volume(double linear) { // expects a value between 0 and 1 (or more if amplifying) - return (qExp(linear)-1)/(M_E-1); + return (qExp(linear)-1.0f)/(M_E-1.0f); } void int32_to_char_array(qint32 i, char* array) { diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp index b3628ad74..61e1f07ee 100644 --- a/ui/audiomonitor.cpp +++ b/ui/audiomonitor.cpp @@ -37,25 +37,33 @@ extern "C" { } AudioMonitor::AudioMonitor(QWidget *parent) : - QWidget(parent) + QWidget(parent), + peaked_(false) { clear_timer.setInterval(500); connect(&clear_timer, SIGNAL(timeout()), this, SLOT(clear())); } -void AudioMonitor::set_value(const QVector &ivalues) { - value_lock.lock(); - values = ivalues; - value_lock.unlock(); +void AudioMonitor::set_value(const QVector &ivalues) { + if (value_lock.tryLock()) { + values = ivalues; - QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); - QMetaObject::invokeMethod(&clear_timer, "start", Qt::QueuedConnection); + if (peaked_.size() != values.size()) { + peaked_.resize(values.size()); + peaked_.fill(false); + } + + value_lock.unlock(); + + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); + QMetaObject::invokeMethod(&clear_timer, "start", Qt::QueuedConnection); + } } void AudioMonitor::clear() { clear_timer.stop(); - - values.fill(1); + peaked_.fill(false); + values.fill(0.0); update(); } @@ -67,6 +75,12 @@ void AudioMonitor::resizeEvent(QResizeEvent *e) { QWidget::resizeEvent(e); } +void AudioMonitor::mousePressEvent(QMouseEvent *) +{ + peaked_.fill(false); + update(); +} + void AudioMonitor::paintEvent(QPaintEvent *) { value_lock.lock(); if (values.size() > 0) { @@ -74,18 +88,26 @@ void AudioMonitor::paintEvent(QPaintEvent *) { int channel_x = AUDIO_MONITOR_GAP; int channel_count = values.size(); int channel_width = (width()/channel_count) - AUDIO_MONITOR_GAP; - int i; - for (i=0;i 1.0f) { + peaked_[i] = true; + } - r.setHeight(qRound(r.height()*(values.at(i)))); - peak = (r.height() == 0); + // We draw an inverted dark shadow over the gradient to represent to are not lit up + // TODO change to decibel representation + float val = 1.0f - qMin(1.0f, values.at(i)); + r.setHeight(qRound(r.height()*val)); QRect peak_rect(channel_x, 0, channel_width, AUDIO_MONITOR_PEAK_HEIGHT); - if (peak) { + if (peaked_[i]) { + // We're peaked on this channel, so we light up the peak light p.fillRect(peak_rect, QColor(255, 0, 0)); } else { p.fillRect(peak_rect, QColor(64, 0, 0)); diff --git a/ui/audiomonitor.h b/ui/audiomonitor.h index 70898ce60..f6a4505c2 100644 --- a/ui/audiomonitor.h +++ b/ui/audiomonitor.h @@ -53,22 +53,29 @@ public: * An array of doubles between 0.0 and 1.0 to display the amplitude. 0.0 is no audio, 1.0 is full volume. Each array * entry is a channel and the audio monitor will automatically adjust to the channel count in the array. */ - void set_value(const QVector& values); + void set_value(const QVector &values); protected: /** * @brief Internal paint event * - * Paints the + * Paints the audio monitor */ - void paintEvent(QPaintEvent *); + virtual void paintEvent(QPaintEvent *) override; /** * @brief Internal resize event handler * * Triggers a repaint when the widget is resized. */ - void resizeEvent(QResizeEvent *); + virtual void resizeEvent(QResizeEvent *) override; + + /** + * @brief Internal mouse press handler + * + * @param event + */ + virtual void mousePressEvent(QMouseEvent *event) override; signals: @@ -83,7 +90,7 @@ private: /** * @brief Internal value storage */ - QVector values; + QVector values; /** * @brief Value mutex @@ -100,6 +107,11 @@ private: */ QTimer clear_timer; + /** + * @brief Internal variable for whether audio played as peaked or not + */ + QVector peaked_; + private slots: /** * @brief Slot to clear the audio monitor From 1285d70c624c8cedfeeb1c320507bd3a28c3c50a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 11 Apr 2019 11:48:20 +1000 Subject: [PATCH 109/133] node socket UI --- effects/effectrow.cpp | 10 ++++++++++ effects/effectrow.h | 28 ++++++++++++++++++++++++++++ nodes/inputs/boolinput.cpp | 2 ++ nodes/inputs/colorinput.cpp | 2 ++ nodes/inputs/comboinput.cpp | 2 ++ nodes/inputs/fileinput.cpp | 2 ++ nodes/inputs/fontinput.cpp | 2 ++ nodes/inputs/stringinput.cpp | 2 ++ nodes/inputs/vecinput.cpp | 11 +++++++++++ nodes/nodegraph.h | 1 - ui/effectui.cpp | 12 ++++++++++-- ui/effectui.h | 2 +- ui/nodeui.cpp | 32 +++++++++++++++++++++++++++----- ui/nodeui.h | 6 ++++-- 14 files changed, 103 insertions(+), 11 deletions(-) diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index 79972c30a..cf236e68c 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -60,6 +60,11 @@ void EffectRow::AddField(EffectField *field) fields_.append(field); } +void EffectRow::AddNodeInput(olive::nodes::DataType type) +{ + accepted_datatypes_.append(type); +} + bool EffectRow::IsKeyframing() { return keyframing_; } @@ -102,6 +107,11 @@ void EffectRow::SetEnabled(bool enabled) } } +bool EffectRow::CanConnectNodes() +{ + return !accepted_datatypes_.isEmpty(); +} + void EffectRow::SetKeyframingEnabled(bool enabled) { if (enabled == keyframing_) { return; diff --git a/effects/effectrow.h b/effects/effectrow.h index 7bfb4379f..bcd42f117 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -211,6 +211,17 @@ public: */ void SetEnabled(bool enabled); + /** + * @brief Check if nodes can be connected to this input. + * + * Connecting is enabled by adding an accepted node input using AddNodeInput(). + * + * @return + * + * TRUE if nodes can be connected. + */ + bool CanConnectNodes(); + protected: /** * @brief Add a field to this row @@ -224,6 +235,18 @@ protected: */ void AddField(EffectField* Field); + /** + * @brief Adds a node data type that can be accepted by this input + * + * Allows this input to take a node connection from a data type specified by type. An input can take several data + * types. + * + * @param type + * + * The data type to add + */ + void AddNodeInput(olive::nodes::DataType type); + public slots: /** * @brief Go to previous keyframe @@ -340,6 +363,11 @@ private: * get freed automatically. */ QVector fields_; + + /** + * @brief Internal array of accepted node data types + */ + QVector accepted_datatypes_; }; #endif // EFFECTROW_H diff --git a/nodes/inputs/boolinput.cpp b/nodes/inputs/boolinput.cpp index 7ae067ed0..9ef9a32ae 100644 --- a/nodes/inputs/boolinput.cpp +++ b/nodes/inputs/boolinput.cpp @@ -6,6 +6,8 @@ BoolInput::BoolInput(Effect* parent, const QString& id, const QString& name, boo BoolField* bool_field = new BoolField(this); connect(bool_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool))); AddField(bool_field); + + AddNodeInput(olive::nodes::kBoolean); } bool BoolInput::GetBoolAt(double timecode) diff --git a/nodes/inputs/colorinput.cpp b/nodes/inputs/colorinput.cpp index eab5010f4..c23d3a83d 100644 --- a/nodes/inputs/colorinput.cpp +++ b/nodes/inputs/colorinput.cpp @@ -4,6 +4,8 @@ ColorInput::ColorInput(Effect* parent, const QString& id, const QString& name, b EffectRow(parent, id, name, savable, keyframable) { AddField(new ColorField(this)); + + AddNodeInput(olive::nodes::kColor); } QColor ColorInput::GetColorAt(double timecode) diff --git a/nodes/inputs/comboinput.cpp b/nodes/inputs/comboinput.cpp index aadeeef41..d34ea7d88 100644 --- a/nodes/inputs/comboinput.cpp +++ b/nodes/inputs/comboinput.cpp @@ -6,6 +6,8 @@ ComboInput::ComboInput(Effect* parent, const QString& id, const QString& name, b ComboField* combo_field = new ComboField(this); connect(combo_field, SIGNAL(DataChanged(const QVariant&)), this, SIGNAL(DataChanged(const QVariant&))); AddField(combo_field); + + AddNodeInput(olive::nodes::kCombo); } void ComboInput::AddItem(const QString &text, const QVariant &data) diff --git a/nodes/inputs/fileinput.cpp b/nodes/inputs/fileinput.cpp index 011506747..65d872ef7 100644 --- a/nodes/inputs/fileinput.cpp +++ b/nodes/inputs/fileinput.cpp @@ -4,6 +4,8 @@ FileInput::FileInput(Effect* parent, const QString& id, const QString& name, boo EffectRow(parent, id, name, savable, keyframable) { AddField(new FileField(this)); + + AddNodeInput(olive::nodes::kFile); } QString FileInput::GetFileAt(double timecode) diff --git a/nodes/inputs/fontinput.cpp b/nodes/inputs/fontinput.cpp index 3e0041136..506a402d9 100644 --- a/nodes/inputs/fontinput.cpp +++ b/nodes/inputs/fontinput.cpp @@ -4,6 +4,8 @@ FontInput::FontInput(Effect* parent, const QString& id, const QString& name, boo EffectRow(parent, id, name, savable, keyframable) { AddField(new FontField(this)); + + AddNodeInput(olive::nodes::kFont); } QString FontInput::GetFontAt(double timecode) diff --git a/nodes/inputs/stringinput.cpp b/nodes/inputs/stringinput.cpp index 0169c3826..ea840992c 100644 --- a/nodes/inputs/stringinput.cpp +++ b/nodes/inputs/stringinput.cpp @@ -4,6 +4,8 @@ StringInput::StringInput(Effect* parent, const QString& id, const QString& name, EffectRow(parent, id, name, savable, keyframable) { AddField(new StringField(this, rich_text)); + + AddNodeInput(olive::nodes::kString); } QString StringInput::GetStringAt(double timecode) diff --git a/nodes/inputs/vecinput.cpp b/nodes/inputs/vecinput.cpp index 82e74ea60..907da26cf 100644 --- a/nodes/inputs/vecinput.cpp +++ b/nodes/inputs/vecinput.cpp @@ -14,6 +14,17 @@ VecInput::VecInput(Effect* parent, const QString& id, const QString& name, int v for (int i=0;i 1) { + AddNodeInput(olive::nodes::kVec2); + } + if (values > 2) { + AddNodeInput(olive::nodes::kVec3); + } + if (values > 3) { + AddNodeInput(olive::nodes::kVec4); + } } void VecInput::SetMinimum(double minimum) diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h index 9bf6cafe2..e060dacee 100644 --- a/nodes/nodegraph.h +++ b/nodes/nodegraph.h @@ -9,7 +9,6 @@ public: NodeGraph(); private: - Effect* end_node_; }; #endif // NODEGRAPH_H diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 5e31dd025..2a9e72467 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -202,11 +202,19 @@ int EffectUI::GetRowY(int row, QWidget* mapToWidget) { QLabel* row_label = labels_.at(row); + int mapped_coord; + if (mapToWidget == nullptr) { + mapped_coord = contents->pos().y(); + } else { + // FIXME Problematic now that EffectUIs are used outside of EffectControls + mapped_coord = mapToWidget->mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y(); + mapped_coord -= title_bar->height(); + } + // Get center point of label (label->rect()->center()->y() - instead of y()+height/2 - produces an inaccurate result) return row_label->y() + row_label->height() / 2 - + mapToWidget->mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - - title_bar->height(); + + mapped_coord; } void EffectUI::UpdateFromEffect() diff --git a/ui/effectui.h b/ui/effectui.h index 86627e96e..2888c04aa 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -95,7 +95,7 @@ public: * * The row's Y position. */ - int GetRowY(int row, QWidget *mapToWidget); + int GetRowY(int row, QWidget *mapToWidget = nullptr); /** * @brief Update widgets with the current Effect's values. diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 718365c46..49eeecb25 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -10,7 +10,10 @@ #include #include +#include "ui/effectui.h" + const int kRoundedRectRadius = 5; +const int kNodePlugSize = 6; NodeUI::NodeUI() : central_widget_(nullptr) @@ -33,24 +36,29 @@ void NodeUI::AddToScene(QGraphicsScene *scene) if (central_widget_ != nullptr) { proxy_ = scene->addWidget(central_widget_); - proxy_->setPos(pos() + QPoint(kRoundedRectRadius, kRoundedRectRadius)); + proxy_->setPos(pos() + QPoint(1 + kRoundedRectRadius, 1 + kRoundedRectRadius)); proxy_->setParentItem(this); } } void NodeUI::Resize(const QSize &s) { - QRectF rectangle = rect(); - + QRectF rectangle; + rectangle.setTopLeft(pos()); rectangle.setSize(s + 2 * QSize(kRoundedRectRadius, kRoundedRectRadius)); + QRectF inner_rect = rectangle; + inner_rect.translate(kNodePlugSize / 2, 0); + + rectangle.setWidth(rectangle.width() + kNodePlugSize); + path_ = QPainterPath(); - path_.addRoundedRect(rectangle, kRoundedRectRadius, kRoundedRectRadius); + path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius); setRect(rectangle); } -void NodeUI::SetWidget(QWidget *widget) +void NodeUI::SetWidget(EffectUI *widget) { central_widget_ = widget; } @@ -59,6 +67,20 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW { Q_UNUSED(widget) + if (proxy_ != nullptr) { + Effect* e = central_widget_->GetEffect(); + + for (int i=0;irow_count();i++) { + if (e->row(i)->CanConnectNodes()) { + int y = central_widget_->GetRowY(i); + + painter->setPen(Qt::black); + painter->setBrush(Qt::gray); + painter->drawEllipse(QPointF(rect().x() + kNodePlugSize / 2, proxy_->pos().y() + y), kNodePlugSize, kNodePlugSize); + } + } + } + QPalette palette = qApp->palette(); if (option->state & QStyle::State_Selected) { diff --git a/ui/nodeui.h b/ui/nodeui.h index 1bde2f392..98cbe8e88 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -4,6 +4,8 @@ #include #include +class EffectUI; + class NodeUI : public QGraphicsRectItem { public: NodeUI(); @@ -11,11 +13,11 @@ public: void AddToScene(QGraphicsScene* scene); void Resize(const QSize& s); - void SetWidget(QWidget* widget); + void SetWidget(EffectUI* widget); protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; private: - QWidget* central_widget_; + EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; QPainterPath path_; }; From e10be0db88f87487c70000174df836a7da2626d7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 11 Apr 2019 20:13:50 +1000 Subject: [PATCH 110/133] node socket UI implemented --- nodes/inputs/vecinput.cpp | 2 - nodes/nodeimageoutput.cpp | 6 +++ nodes/nodeimageoutput.h | 11 +++++ nodes/{medianode.cpp => nodemedia.cpp} | 0 nodes/{medianode.h => nodemedia.h} | 4 +- olive.pro | 10 +++-- ui/nodeui.cpp | 61 +++++++++++++++++++++----- ui/nodeui.h | 4 ++ 8 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 nodes/nodeimageoutput.cpp create mode 100644 nodes/nodeimageoutput.h rename nodes/{medianode.cpp => nodemedia.cpp} (100%) rename nodes/{medianode.h => nodemedia.h} (71%) diff --git a/nodes/inputs/vecinput.cpp b/nodes/inputs/vecinput.cpp index 907da26cf..7cdf7d01b 100644 --- a/nodes/inputs/vecinput.cpp +++ b/nodes/inputs/vecinput.cpp @@ -71,8 +71,6 @@ void VecInput::SetFrameRate(const double &rate) void VecInput::SetSingleValueMode(bool on) { - qDebug() << "i'm here"; - single_value_mode_ = on; // Disable all fields except the first diff --git a/nodes/nodeimageoutput.cpp b/nodes/nodeimageoutput.cpp new file mode 100644 index 000000000..b8b23c2fa --- /dev/null +++ b/nodes/nodeimageoutput.cpp @@ -0,0 +1,6 @@ +#include "nodeimageoutput.h" + +NodeImageOutput::NodeImageOutput() +{ + +} diff --git a/nodes/nodeimageoutput.h b/nodes/nodeimageoutput.h new file mode 100644 index 000000000..69fc8ddea --- /dev/null +++ b/nodes/nodeimageoutput.h @@ -0,0 +1,11 @@ +#ifndef NODEIMAGEOUTPUT_H +#define NODEIMAGEOUTPUT_H + + +class NodeImageOutput +{ +public: + NodeImageOutput(); +}; + +#endif // NODEIMAGEOUTPUT_H \ No newline at end of file diff --git a/nodes/medianode.cpp b/nodes/nodemedia.cpp similarity index 100% rename from nodes/medianode.cpp rename to nodes/nodemedia.cpp diff --git a/nodes/medianode.h b/nodes/nodemedia.h similarity index 71% rename from nodes/medianode.h rename to nodes/nodemedia.h index 7fb634375..9e7d87cf3 100644 --- a/nodes/medianode.h +++ b/nodes/nodemedia.h @@ -1,10 +1,10 @@ #ifndef MEDIANODE_H #define MEDIANODE_H -class MediaNode +class NodeMedia { public: - MediaNode(); + NodeMedia(); }; #endif // MEDIANODE_H diff --git a/olive.pro b/olive.pro index 16f17add3..cf23b00cb 100644 --- a/olive.pro +++ b/olive.pro @@ -189,7 +189,6 @@ SOURCES += \ ui/waveform.cpp \ panels/nodeeditor.cpp \ ui/nodeview.cpp \ - nodes/medianode.cpp \ ui/nodeui.cpp \ panels/effectspanel.cpp \ nodes/nodedatatypes.cpp \ @@ -203,7 +202,9 @@ SOURCES += \ nodes/widgets/labelwidget.cpp \ nodes/inputs/fileinput.cpp \ nodes/widgets/buttonwidget.cpp \ - nodes/nodegraph.cpp + nodes/nodegraph.cpp \ + nodes/nodemedia.cpp \ + nodes/nodeimageoutput.cpp HEADERS += \ ui/mainwindow.h \ @@ -347,7 +348,6 @@ HEADERS += \ ui/waveform.h \ panels/nodeeditor.h \ ui/nodeview.h \ - nodes/medianode.h \ ui/nodeui.h \ panels/effectspanel.h \ nodes/nodedatatypes.h \ @@ -362,7 +362,9 @@ HEADERS += \ nodes/widgets/labelwidget.h \ nodes/inputs/fileinput.h \ nodes/widgets/buttonwidget.h \ - nodes/nodegraph.h + nodes/nodegraph.h \ + nodes/nodemedia.h \ + nodes/nodeimageoutput.h FORMS += diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 49eeecb25..2d5ad970a 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "ui/effectui.h" @@ -67,17 +68,13 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW { Q_UNUSED(widget) - if (proxy_ != nullptr) { - Effect* e = central_widget_->GetEffect(); + QVector sockets = GetNodeSocketRects(); + if (!sockets.isEmpty()) { + painter->setPen(Qt::black); + painter->setBrush(Qt::gray); - for (int i=0;irow_count();i++) { - if (e->row(i)->CanConnectNodes()) { - int y = central_widget_->GetRowY(i); - - painter->setPen(Qt::black); - painter->setBrush(Qt::gray); - painter->drawEllipse(QPointF(rect().x() + kNodePlugSize / 2, proxy_->pos().y() + y), kNodePlugSize, kNodePlugSize); - } + for (int i=0;idrawEllipse(sockets.at(i)); } } @@ -91,3 +88,47 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW painter->setBrush(palette.window()); painter->drawPath(path_); } + +void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + QVector sockets = GetNodeSocketRects(); + + bool clicked_socket = false; + + for (int i=0;ipos())) { + clicked_socket = true; + break; + } + } + + if (clicked_socket) { + qDebug() << "CLICKED SOCKET!"; + } else { + QGraphicsItem::mousePressEvent(event); + } +} + +QVector NodeUI::GetNodeSocketRects() +{ + QVector rects; + + if (proxy_ != nullptr) { + Effect* e = central_widget_->GetEffect(); + + for (int i=0;irow_count();i++) { + if (e->row(i)->CanConnectNodes()) { + int y = central_widget_->GetRowY(i); + + rects.append(QRectF(rect().x(), + proxy_->pos().y() + y - kNodePlugSize/2, + kNodePlugSize, + kNodePlugSize)); + + + } + } + } + + return rects; +} diff --git a/ui/nodeui.h b/ui/nodeui.h index 98cbe8e88..fe278accf 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -16,7 +16,11 @@ public: void SetWidget(EffectUI* widget); protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + + virtual void mousePressEvent(QGraphicsSceneMouseEvent * event) override; private: + QVector GetNodeSocketRects(); + EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; QPainterPath path_; From b8e0d12a630eb7bf4a74c7c7641c9df8c7d24dc7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 00:19:24 +1000 Subject: [PATCH 111/133] moved files around, created new classes to be utilized later --- decoders/decoder.cpp | 6 + decoders/decoder.h | 11 ++ decoders/ffmpegdecoder.cpp | 6 + decoders/ffmpegdecoder.h | 11 ++ effects/effect.cpp | 190 +------------------------ nodes/nodemedia.cpp | 6 - nodes/nodes.h | 8 ++ nodes/{ => nodes}/nodeimageoutput.cpp | 0 nodes/{ => nodes}/nodeimageoutput.h | 0 nodes/nodes/nodemedia.cpp | 6 + nodes/{ => nodes}/nodemedia.h | 0 nodes/nodes/nodeshader.cpp | 191 ++++++++++++++++++++++++++ nodes/nodes/nodeshader.h | 12 ++ olive.pro | 15 +- 14 files changed, 265 insertions(+), 197 deletions(-) create mode 100644 decoders/decoder.cpp create mode 100644 decoders/decoder.h create mode 100644 decoders/ffmpegdecoder.cpp create mode 100644 decoders/ffmpegdecoder.h delete mode 100644 nodes/nodemedia.cpp create mode 100644 nodes/nodes.h rename nodes/{ => nodes}/nodeimageoutput.cpp (100%) rename nodes/{ => nodes}/nodeimageoutput.h (100%) create mode 100644 nodes/nodes/nodemedia.cpp rename nodes/{ => nodes}/nodemedia.h (100%) create mode 100644 nodes/nodes/nodeshader.cpp create mode 100644 nodes/nodes/nodeshader.h diff --git a/decoders/decoder.cpp b/decoders/decoder.cpp new file mode 100644 index 000000000..8de99a7b3 --- /dev/null +++ b/decoders/decoder.cpp @@ -0,0 +1,6 @@ +#include "decoder.h" + +Decoder::Decoder() +{ + +} diff --git a/decoders/decoder.h b/decoders/decoder.h new file mode 100644 index 000000000..8c2bd04d0 --- /dev/null +++ b/decoders/decoder.h @@ -0,0 +1,11 @@ +#ifndef DECODER_H +#define DECODER_H + + +class Decoder +{ +public: + Decoder(); +}; + +#endif // DECODER_H \ No newline at end of file diff --git a/decoders/ffmpegdecoder.cpp b/decoders/ffmpegdecoder.cpp new file mode 100644 index 000000000..26cf3c579 --- /dev/null +++ b/decoders/ffmpegdecoder.cpp @@ -0,0 +1,6 @@ +#include "ffmpegdecoder.h" + +FFmpegDecoder::FFmpegDecoder() +{ + +} diff --git a/decoders/ffmpegdecoder.h b/decoders/ffmpegdecoder.h new file mode 100644 index 000000000..52f614f40 --- /dev/null +++ b/decoders/ffmpegdecoder.h @@ -0,0 +1,11 @@ +#ifndef FFMPEGDECODER_H +#define FFMPEGDECODER_H + + +class FFmpegDecoder +{ +public: + FFmpegDecoder(); +}; + +#endif // FFMPEGDECODER_H \ No newline at end of file diff --git a/effects/effect.cpp b/effects/effect.cpp index 478c7c2f3..49b6f8201 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -54,6 +54,7 @@ #include "undo/undostack.h" #include "rendering/shadergenerators.h" #include "global/timing.h" +#include "nodes/nodes.h" #include "effects/internal/transformeffect.h" #include "effects/internal/texteffect.h" @@ -93,7 +94,7 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { } } else if (!em->filename.isEmpty()) { // load effect from file - return std::make_shared(c, em); + return std::make_shared(c, em); } else { qCritical() << "Invalid effect data"; QMessageBox::critical(olive::MainWindow, @@ -126,192 +127,7 @@ Effect::Effect(Clip* c, const EffectMeta *em) : enabled_(true), expanded_(true), texture_ctx(nullptr) -{ - if (em != nullptr) { - // set up UI from effect file - name = em->name; - - if (!em->filename.isEmpty() && em->internal == -1) { - QFile effect_file(em->filename); - if (effect_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&effect_file); - - while (!reader.atEnd()) { - if (reader.name() == "field" && reader.isStartElement()) { - int type = olive::nodes::kInvalid; - QString id; - QString name; - - // get field type - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename << "- ID, type, and name cannot be empty."; - } else { - EffectRow* field = nullptr; - - switch (type) { - case olive::nodes::kFloat: - { - - DoubleInput* double_field = new DoubleInput(this, id, name); - - for (int i=0;iSetDefault(attr.value().toDouble()); - } else if (attr.name() == "min") { - double_field->SetMinimum(attr.value().toDouble()); - } else if (attr.name() == "max") { - double_field->SetMaximum(attr.value().toDouble()); - } - } - - field = double_field; - } - break; - case olive::nodes::kColor: - { - QColor color; - - field = new ColorInput(this, id, name); - - for (int i=0;iSetValueAt(0, color); - } - break; - case olive::nodes::kString: - field = new StringInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - case olive::nodes::kBoolean: - field = new BoolInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value() == "1"); - } - } - break; - case olive::nodes::kCombo: - { - ComboInput* combo_field = new ComboInput(this, id, name); - int combo_default_index = 0; - for (int i=0;iAddItem(reader.text().toString(), combo_item_count); - combo_item_count++; - } - } - combo_field->SetValueAt(0, combo_default_index); - field = combo_field; - } - break; - case olive::nodes::kFont: - field = new FontInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - case olive::nodes::kFile: - field = new FileInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - } - } - } else if (reader.name() == "shader" && reader.isStartElement()) { - SetFlags(Flags() | ShaderFlag); - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; - } - break; - } - } - }*/ - reader.readNext(); - } - - effect_file.close(); - } else { - qCritical() << "Failed to open effect file" << em->filename; - } - } - } +{ } Effect::~Effect() { diff --git a/nodes/nodemedia.cpp b/nodes/nodemedia.cpp deleted file mode 100644 index c844a1c59..000000000 --- a/nodes/nodemedia.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "medianode.h" - -MediaNode::MediaNode() -{ - -} diff --git a/nodes/nodes.h b/nodes/nodes.h new file mode 100644 index 000000000..e3135d7e1 --- /dev/null +++ b/nodes/nodes.h @@ -0,0 +1,8 @@ +#ifndef NODES_H +#define NODES_H + +#include "nodes/nodemedia.h" +#include "nodes/nodeimageoutput.h" +#include "nodes/nodeshader.h" + +#endif // NODES_H diff --git a/nodes/nodeimageoutput.cpp b/nodes/nodes/nodeimageoutput.cpp similarity index 100% rename from nodes/nodeimageoutput.cpp rename to nodes/nodes/nodeimageoutput.cpp diff --git a/nodes/nodeimageoutput.h b/nodes/nodes/nodeimageoutput.h similarity index 100% rename from nodes/nodeimageoutput.h rename to nodes/nodes/nodeimageoutput.h diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp new file mode 100644 index 000000000..7e62aaaae --- /dev/null +++ b/nodes/nodes/nodemedia.cpp @@ -0,0 +1,6 @@ +#include "nodemedia.h" + +NodeMedia::NodeMedia() +{ + +} diff --git a/nodes/nodemedia.h b/nodes/nodes/nodemedia.h similarity index 100% rename from nodes/nodemedia.h rename to nodes/nodes/nodemedia.h diff --git a/nodes/nodes/nodeshader.cpp b/nodes/nodes/nodeshader.cpp new file mode 100644 index 000000000..c5118fe87 --- /dev/null +++ b/nodes/nodes/nodeshader.cpp @@ -0,0 +1,191 @@ +#include "nodeshader.h" + +NodeShader::NodeShader(Clip* c, const EffectMeta *em) : + Effect(c, em) +{ + if (em != nullptr) { + // set up UI from effect file + name = em->name; + + if (!em->filename.isEmpty() && em->internal == -1) { + QFile effect_file(em->filename); + if (effect_file.open(QFile::ReadOnly)) { + QXmlStreamReader reader(&effect_file); + + while (!reader.atEnd()) { + if (reader.name() == "field" && reader.isStartElement()) { + int type = olive::nodes::kInvalid; + QString id; + QString name; + + // get field type + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename << "- ID, type, and name cannot be empty."; + } else { + EffectRow* field = nullptr; + + switch (type) { + case olive::nodes::kFloat: + { + + DoubleInput* double_field = new DoubleInput(this, id, name); + + for (int i=0;iSetDefault(attr.value().toDouble()); + } else if (attr.name() == "min") { + double_field->SetMinimum(attr.value().toDouble()); + } else if (attr.name() == "max") { + double_field->SetMaximum(attr.value().toDouble()); + } + } + + field = double_field; + } + break; + case olive::nodes::kColor: + { + QColor color; + + field = new ColorInput(this, id, name); + + for (int i=0;iSetValueAt(0, color); + } + break; + case olive::nodes::kString: + field = new StringInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; + case olive::nodes::kBoolean: + field = new BoolInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value() == "1"); + } + } + break; + case olive::nodes::kCombo: + { + ComboInput* combo_field = new ComboInput(this, id, name); + int combo_default_index = 0; + for (int i=0;iAddItem(reader.text().toString(), combo_item_count); + combo_item_count++; + } + } + combo_field->SetValueAt(0, combo_default_index); + field = combo_field; + } + break; + case olive::nodes::kFont: + field = new FontInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; + case olive::nodes::kFile: + field = new FileInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; + } + } + } else if (reader.name() == "shader" && reader.isStartElement()) { + SetFlags(Flags() | ShaderFlag); + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename; + enable_superimpose = false; + } + break; + } + } + }*/ + reader.readNext(); + } + + effect_file.close(); + } else { + qCritical() << "Failed to open effect file" << em->filename; + } + } + } +} diff --git a/nodes/nodes/nodeshader.h b/nodes/nodes/nodeshader.h new file mode 100644 index 000000000..298e495cc --- /dev/null +++ b/nodes/nodes/nodeshader.h @@ -0,0 +1,12 @@ +#ifndef NODESHADER_H +#define NODESHADER_H + +#include "effects/effect.h" + +class NodeShader : public Effect { + Q_OBJECT +public: + NodeShader(Clip *c, const EffectMeta *em); +}; + +#endif // NODESHADER_H diff --git a/olive.pro b/olive.pro index cf23b00cb..f737408d5 100644 --- a/olive.pro +++ b/olive.pro @@ -203,8 +203,11 @@ SOURCES += \ nodes/inputs/fileinput.cpp \ nodes/widgets/buttonwidget.cpp \ nodes/nodegraph.cpp \ - nodes/nodemedia.cpp \ - nodes/nodeimageoutput.cpp + nodes/nodes/nodeimageoutput.cpp \ + nodes/nodes/nodemedia.cpp \ + nodes/nodes/nodeshader.cpp \ + decoders/ffmpegdecoder.cpp \ + decoders/decoder.cpp HEADERS += \ ui/mainwindow.h \ @@ -363,8 +366,12 @@ HEADERS += \ nodes/inputs/fileinput.h \ nodes/widgets/buttonwidget.h \ nodes/nodegraph.h \ - nodes/nodemedia.h \ - nodes/nodeimageoutput.h + nodes/nodes/nodemedia.h \ + nodes/nodes/nodeimageoutput.h \ + nodes/nodes/nodeshader.h \ + nodes/nodes.h \ + decoders/ffmpegdecoder.h \ + decoders/decoder.h FORMS += From a0f3ab2a6b49c7aef4f58623aefaf162d9ff0ba1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 01:00:33 +1000 Subject: [PATCH 112/133] started node graph design --- nodes/nodegraph.cpp | 8 +++++++- nodes/nodegraph.h | 41 ++++++++++++++++++++++++++++++++++++++++- timeline/clip.h | 3 +++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/nodes/nodegraph.cpp b/nodes/nodegraph.cpp index 8f7a00e03..381bd53cd 100644 --- a/nodes/nodegraph.cpp +++ b/nodes/nodegraph.cpp @@ -2,7 +2,13 @@ #include -NodeGraph::NodeGraph() +NodeGraph::NodeGraph() : + output_node_(nullptr) { } + +Effect *NodeGraph::OutputNode() +{ + return output_node_.get(); +} diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h index e060dacee..a450abf16 100644 --- a/nodes/nodegraph.h +++ b/nodes/nodegraph.h @@ -8,7 +8,46 @@ class NodeGraph public: NodeGraph(); -private: + /** + * @brief Add a node to this graph + * + * The graph takes ownership of the node. + * + * @param node + */ + void AddNode(EffectPtr node); + + /** + * @brief Process the graph + * + * Using the output node set by SetOutputNode(), this function will work backwards and perform every action in the + * node hierarchy necessary to eventually process the output node. After this function returns, the output node should + * contain valid values ready for accessing. + * + * Each node will cache its output for optimized playback and rendering. This will happen transparently and under most + * circumstances, this function will be able to return immediately. + */ + void Process(); + + /** + * @brief Set the output node for this node graph + * + * This node will be presented as the final node that all other nodes converge on. This node can be used to retrieve + * the result of the graph. + * + * @param node + * + * The node to set as the output node. The graph takes ownership of the node and the user cannot delete it. + */ + void SetOutputNode(EffectPtr node); + + /** + * @brief Returns the currently set output node + */ + Effect* OutputNode(); + +private: + EffectPtr output_node_; }; #endif // NODEGRAPH_H diff --git a/timeline/clip.h b/timeline/clip.h index 9df7ec244..b0614535d 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -38,6 +38,7 @@ #include "rendering/framebufferobject.h" #include "marker.h" #include "track.h" +#include "nodes/nodegraph.h" struct ClipSpeed { ClipSpeed(); @@ -180,6 +181,8 @@ private: Cacher cacher; long cacher_frame; + NodeGraph pipeline_; + QVector markers; QColor color_; bool open_; From 83620173aff59495abc8e910f3194bb152c4a976 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 01:44:49 +1000 Subject: [PATCH 113/133] ui for output rows on nodes --- effects/effect.cpp | 4 + effects/effectrow.cpp | 29 ++- effects/effectrow.h | 46 +++- effects/internal/transformeffect.cpp | 4 + nodes/nodes/nodeshader.cpp | 313 +++++++++++++-------------- ui/effectui.cpp | 67 +++--- ui/nodeui.cpp | 13 +- 7 files changed, 271 insertions(+), 205 deletions(-) diff --git a/effects/effect.cpp b/effects/effect.cpp index 49b6f8201..db02e443c 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -128,6 +128,10 @@ Effect::Effect(Clip* c, const EffectMeta *em) : expanded_(true), texture_ctx(nullptr) { + if (em != nullptr) { + // set up UI from effect metadata + name = em->name; + } } Effect::~Effect() { diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index cf236e68c..b96349cc0 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -37,13 +37,18 @@ #include "ui/keyframenavigator.h" #include "ui/clickablelabel.h" -EffectRow::EffectRow(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : +EffectRow::EffectRow(Effect *parent, + const QString &id, + const QString &name, + bool savable, + bool keyframable) : QObject(parent), id_(id), name_(name), keyframable_(keyframable), keyframing_(false), - savable_(savable) + savable_(savable), + output_type_(olive::nodes::kInvalid) { Q_ASSERT(parent != nullptr); @@ -62,7 +67,9 @@ void EffectRow::AddField(EffectField *field) void EffectRow::AddNodeInput(olive::nodes::DataType type) { - accepted_datatypes_.append(type); + Q_ASSERT(output_type_ == olive::nodes::kInvalid); + + accepted_inputs_.append(type); } bool EffectRow::IsKeyframing() { @@ -107,9 +114,21 @@ void EffectRow::SetEnabled(bool enabled) } } -bool EffectRow::CanConnectNodes() +void EffectRow::SetOutputDataType(olive::nodes::DataType type) { - return !accepted_datatypes_.isEmpty(); + Q_ASSERT(accepted_inputs_.isEmpty()); + + output_type_ = type; +} + +bool EffectRow::IsNodeInput() +{ + return !accepted_inputs_.isEmpty(); +} + +bool EffectRow::IsNodeOutput() +{ + return output_type_ != olive::nodes::kInvalid; } void EffectRow::SetKeyframingEnabled(bool enabled) { diff --git a/effects/effectrow.h b/effects/effectrow.h index bcd42f117..1afc6dfa4 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -51,6 +51,7 @@ class ClickableLabel; class EffectRow : public QObject { Q_OBJECT public: + /** * @brief EffectRow Constructor * @@ -82,7 +83,11 @@ public: * Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent * the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false. */ - EffectRow(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + EffectRow(Effect* parent, + const QString& id, + const QString& name, + bool savable = true, + bool keyframable = true); /** * @brief Retrieve the EffectField at this index. Must be less than FieldCount(). @@ -212,15 +217,33 @@ public: void SetEnabled(bool enabled); /** - * @brief Check if nodes can be connected to this input. + * @brief Check if nodes can be connected to this as an input. * * Connecting is enabled by adding an accepted node input using AddNodeInput(). * * @return * - * TRUE if nodes can be connected. + * TRUE if nodes can be connected as an input. */ - bool CanConnectNodes(); + bool IsNodeInput(); + + /** + * @brief Check if nodes can be connected to this as an output + * + * Connecting is enabled by setting an output data type in SetOutputDataType(). + * + * @return + * + * TRUE if nodes can be connected as an output + */ + bool IsNodeOutput(); + + /** + * @brief Set output data type + * + * Set the type of data this row outputs to type + */ + void SetOutputDataType(olive::nodes::DataType type); protected: /** @@ -365,9 +388,20 @@ private: QVector fields_; /** - * @brief Internal array of accepted node data types + * @brief Internal array of accepted node data types. + * + * Is mutally-exclusive with accepted_outputs_, i.e. you cannot have values added to this and also a value set in + * accepted_outputs_. */ - QVector accepted_datatypes_; + QVector accepted_inputs_; + + /** + * @brief Internal value for what kind of data this row outputs + * + * Is mutally-exclusive with accepted_inputs_, i.e. you cannot have values added to it and also a value set in + * this. + */ + olive::nodes::DataType output_type_; }; #endif // EFFECTROW_H diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index c48ec341d..c769b63e5 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -67,6 +67,10 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) opacity->SetMaximum(100); opacity->SetDefault(100); + // TEMP - Create matrix output + EffectRow* matrix_output = new EffectRow(this, "matrix", "Matrix", false, false); + matrix_output->SetOutputDataType(olive::nodes::kMatrix); + // set up gizmos top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT); top_left_gizmo->set_cursor(Qt::SizeFDiagCursor); diff --git a/nodes/nodes/nodeshader.cpp b/nodes/nodes/nodeshader.cpp index c5118fe87..64bc64155 100644 --- a/nodes/nodes/nodeshader.cpp +++ b/nodes/nodes/nodeshader.cpp @@ -3,189 +3,184 @@ NodeShader::NodeShader(Clip* c, const EffectMeta *em) : Effect(c, em) { - if (em != nullptr) { - // set up UI from effect file - name = em->name; + if (em != nullptr && !em->filename.isEmpty() && em->internal == -1) { + QFile effect_file(em->filename); + if (effect_file.open(QFile::ReadOnly)) { + QXmlStreamReader reader(&effect_file); - if (!em->filename.isEmpty() && em->internal == -1) { - QFile effect_file(em->filename); - if (effect_file.open(QFile::ReadOnly)) { - QXmlStreamReader reader(&effect_file); + while (!reader.atEnd()) { + if (reader.name() == "field" && reader.isStartElement()) { + int type = olive::nodes::kInvalid; + QString id; + QString name; - while (!reader.atEnd()) { - if (reader.name() == "field" && reader.isStartElement()) { - int type = olive::nodes::kInvalid; - QString id; - QString name; - - // get field type - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename << "- ID, type, and name cannot be empty."; - } else { - EffectRow* field = nullptr; + if (id.isEmpty() || name.isEmpty() || type == olive::nodes::kInvalid) { + qCritical() << "Couldn't load field from" << em->filename << "- ID, type, and name cannot be empty."; + } else { + EffectRow* field = nullptr; - switch (type) { - case olive::nodes::kFloat: - { + switch (type) { + case olive::nodes::kFloat: + { - DoubleInput* double_field = new DoubleInput(this, id, name); + DoubleInput* double_field = new DoubleInput(this, id, name); - for (int i=0;iSetDefault(attr.value().toDouble()); - } else if (attr.name() == "min") { - double_field->SetMinimum(attr.value().toDouble()); - } else if (attr.name() == "max") { - double_field->SetMaximum(attr.value().toDouble()); - } + for (int i=0;iSetDefault(attr.value().toDouble()); + } else if (attr.name() == "min") { + double_field->SetMinimum(attr.value().toDouble()); + } else if (attr.name() == "max") { + double_field->SetMaximum(attr.value().toDouble()); } - - field = double_field; } - break; - case olive::nodes::kColor: - { - QColor color; - field = new ColorInput(this, id, name); + field = double_field; + } + break; + case olive::nodes::kColor: + { + QColor color; - for (int i=0;iSetValueAt(0, color); } - break; - case olive::nodes::kString: - field = new StringInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } + + field->SetValueAt(0, color); + } + break; + case olive::nodes::kString: + field = new StringInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); } - break; - case olive::nodes::kBoolean: - field = new BoolInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value() == "1"); - } + } + break; + case olive::nodes::kBoolean: + field = new BoolInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value() == "1"); } - break; - case olive::nodes::kCombo: - { - ComboInput* combo_field = new ComboInput(this, id, name); - int combo_default_index = 0; - for (int i=0;iAddItem(reader.text().toString(), combo_item_count); - combo_item_count++; - } + combo_field->AddItem(reader.text().toString(), combo_item_count); + combo_item_count++; } - combo_field->SetValueAt(0, combo_default_index); - field = combo_field; - } - break; - case olive::nodes::kFont: - field = new FontInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; - case olive::nodes::kFile: - field = new FileInput(this, id, name); - for (int i=0;iSetValueAt(0, attr.value().toString()); - } - } - break; } + combo_field->SetValueAt(0, combo_default_index); + field = combo_field; } - } else if (reader.name() == "shader" && reader.isStartElement()) { - SetFlags(Flags() | ShaderFlag); - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; + break; + case olive::nodes::kFont: + field = new FontInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); } - break; } + break; + case olive::nodes::kFile: + field = new FileInput(this, id, name); + for (int i=0;iSetValueAt(0, attr.value().toString()); + } + } + break; } - }*/ - reader.readNext(); - } - - effect_file.close(); - } else { - qCritical() << "Failed to open effect file" << em->filename; + } + } else if (reader.name() == "shader" && reader.isStartElement()) { + SetFlags(Flags() | ShaderFlag); + const QXmlStreamAttributes& attributes = reader.attributes(); + for (int i=0;ifilename; + enable_superimpose = false; + } + break; + } + } + }*/ + reader.readNext(); } + + effect_file.close(); + } else { + qCritical() << "Failed to open effect file" << em->filename; } } } diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 2a9e72467..bdfbeb813 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -115,41 +115,50 @@ EffectUI::EffectUI(Effect* e) : labels_.append(row_label); - layout_->addWidget(row_label, i, 0); + if (row->IsNodeOutput()) { - widgets_[i].resize(row->FieldCount()); - - QGridLayout* field_layout = new QGridLayout(); - for (int j=0;jFieldCount();j++) { - EffectField* field = row->Field(j); - - QWidget* widget = field->CreateWidget(); - - widgets_[i][j] = widget; - - field_layout->addWidget(widget, 0, j); - } - layout_->addLayout(field_layout, i, 1); - - KeyframeNavigator* nav; - - if (row->IsKeyframable()) { - - nav = new KeyframeNavigator(); - - nav->enable_keyframes(row->IsKeyframing()); - - AttachKeyframeNavigationToRow(row, nav); - - layout_->addWidget(nav, i, 2); + row_label->setAlignment(Qt::AlignRight); + layout_->addWidget(row_label, i, 2); } else { - nav = nullptr; + layout_->addWidget(row_label, i, 0); + + widgets_[i].resize(row->FieldCount()); + + QGridLayout* field_layout = new QGridLayout(); + for (int j=0;jFieldCount();j++) { + EffectField* field = row->Field(j); + + QWidget* widget = field->CreateWidget(); + + widgets_[i][j] = widget; + + field_layout->addWidget(widget, 0, j); + } + layout_->addLayout(field_layout, i, 1); + + KeyframeNavigator* nav; + + if (row->IsKeyframable()) { + + nav = new KeyframeNavigator(); + + nav->enable_keyframes(row->IsKeyframing()); + + AttachKeyframeNavigationToRow(row, nav); + + layout_->addWidget(nav, i, 2); + + } else { + + nav = nullptr; + + } + + keyframe_navigators_[i] = nav; } - - keyframe_navigators_[i] = nav; } enabled_check->setChecked(e->IsEnabled()); diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 2d5ad970a..9a8f68c49 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -14,7 +14,7 @@ #include "ui/effectui.h" const int kRoundedRectRadius = 5; -const int kNodePlugSize = 6; +const int kNodePlugSize = 10; NodeUI::NodeUI() : central_widget_(nullptr) @@ -117,15 +117,16 @@ QVector NodeUI::GetNodeSocketRects() Effect* e = central_widget_->GetEffect(); for (int i=0;irow_count();i++) { - if (e->row(i)->CanConnectNodes()) { - int y = central_widget_->GetRowY(i); - rects.append(QRectF(rect().x(), + EffectRow* row = e->row(i); + qreal x = (row->IsNodeOutput()) ? rect().right() - kNodePlugSize : rect().x(); + int y = central_widget_->GetRowY(i); + + if (row->IsNodeInput() || row->IsNodeOutput()) { + rects.append(QRectF(x, proxy_->pos().y() + y - kNodePlugSize/2, kNodePlugSize, kNodePlugSize)); - - } } } From a0f1dfef0e16d88fc1ca3f560bbf43f26e658b88 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 01:59:41 +1000 Subject: [PATCH 114/133] catch mouse events when clicking on node sockets --- ui/nodeui.cpp | 30 +++++++++++++++++++++++++----- ui/nodeui.h | 4 ++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 9a8f68c49..aacda3065 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -17,7 +17,8 @@ const int kRoundedRectRadius = 5; const int kNodePlugSize = 10; NodeUI::NodeUI() : - central_widget_(nullptr) + central_widget_(nullptr), + clicked_socket_(false) { setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -93,22 +94,41 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) { QVector sockets = GetNodeSocketRects(); - bool clicked_socket = false; + clicked_socket_ = false; for (int i=0;ipos())) { - clicked_socket = true; + clicked_socket_ = true; break; } } - if (clicked_socket) { - qDebug() << "CLICKED SOCKET!"; + if (clicked_socket_) { + qDebug() << "Clicked socket!"; + event->accept(); } else { QGraphicsItem::mousePressEvent(event); } } +void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (clicked_socket_) { + + } else { + QGraphicsItem::mouseMoveEvent(event); + } +} + +void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (clicked_socket_) { + + } else { + QGraphicsItem::mouseReleaseEvent(event); + } +} + QVector NodeUI::GetNodeSocketRects() { QVector rects; diff --git a/ui/nodeui.h b/ui/nodeui.h index fe278accf..3cb50d2a6 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -18,12 +18,16 @@ protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; virtual void mousePressEvent(QGraphicsSceneMouseEvent * event) override; + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; private: QVector GetNodeSocketRects(); EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; QPainterPath path_; + + bool clicked_socket_; }; #endif // NODEUI_H From 4e4d2b1881262d47de4630d1ecd624fc1347f851 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 02:23:50 +1000 Subject: [PATCH 115/133] bezier node edges --- ui/nodeui.cpp | 39 +++++++++++++++++++++++++++++++-------- ui/nodeui.h | 6 +++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index aacda3065..ebc3f10bd 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -12,13 +12,14 @@ #include #include "ui/effectui.h" +#include "global/math.h" const int kRoundedRectRadius = 5; const int kNodePlugSize = 10; NodeUI::NodeUI() : central_widget_(nullptr), - clicked_socket_(false) + clicked_socket_(-1) { setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -94,17 +95,20 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) { QVector sockets = GetNodeSocketRects(); - clicked_socket_ = false; + clicked_socket_ = -1; for (int i=0;ipos())) { - clicked_socket_ = true; + clicked_socket_ = i; break; } } - if (clicked_socket_) { - qDebug() << "Clicked socket!"; + if (clicked_socket_ > -1) { + drag_line_start_ = pos() + sockets.at(clicked_socket_).center(); + drag_line_ = scene()->addPath(GetEdgePath(drag_line_start_, event->scenePos()), + QPen(Qt::white, 2)); + event->accept(); } else { QGraphicsItem::mousePressEvent(event); @@ -113,7 +117,11 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - if (clicked_socket_) { + if (clicked_socket_ > -1) { + + drag_line_->setPath(GetEdgePath(drag_line_start_, event->scenePos())); + + event->accept(); } else { QGraphicsItem::mouseMoveEvent(event); @@ -122,13 +130,28 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - if (clicked_socket_) { - + if (clicked_socket_ > -1) { + scene()->removeItem(drag_line_); + delete drag_line_; + event->accept(); } else { QGraphicsItem::mouseReleaseEvent(event); } } +QPainterPath NodeUI::GetEdgePath(const QPointF &start_pos, const QPointF &end_pos) +{ + double mid_x = double_lerp(start_pos.x(), end_pos.x(), 0.5); + + QPainterPath path_; + path_.moveTo(start_pos); + path_.cubicTo(QPointF(mid_x, start_pos.y()), + QPointF(mid_x, end_pos.y()), + end_pos); + + return path_; +} + QVector NodeUI::GetNodeSocketRects() { QVector rects; diff --git a/ui/nodeui.h b/ui/nodeui.h index 3cb50d2a6..2ac1b0c48 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -22,12 +22,16 @@ protected: virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; private: QVector GetNodeSocketRects(); + QPainterPath GetEdgePath(const QPointF& start_pos, const QPointF& end_pos); EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; QPainterPath path_; - bool clicked_socket_; + QGraphicsPathItem* drag_line_; + QPointF drag_line_start_; + + int clicked_socket_; }; #endif // NODEUI_H From b21745f6942c02430cbb9d36bb63a0915007ea86 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 21:50:52 +1000 Subject: [PATCH 116/133] merged effects into node structure --- dialogs/autocutsilencedialog.cpp | 2 +- dialogs/speeddialog.cpp | 20 +- effects/effectfield.cpp | 2 +- effects/effectfield.h | 1 - effects/effectgizmo.cpp | 4 +- effects/effectgizmo.h | 4 +- effects/effectloaders.cpp | 225 ++++++--------- effects/effectrow.cpp | 10 +- effects/effectrow.h | 6 +- effects/fields/boolfield.cpp | 3 +- effects/fields/colorfield.cpp | 3 +- effects/fields/combofield.cpp | 3 +- effects/fields/doublefield.cpp | 3 +- effects/fields/doublefield.h | 2 + effects/fields/filefield.cpp | 3 +- effects/fields/fontfield.cpp | 3 +- effects/fields/stringfield.cpp | 3 +- effects/internal/audionoiseeffect.cpp | 32 ++- effects/internal/audionoiseeffect.h | 14 +- effects/internal/cornerpineffect.cpp | 39 ++- effects/internal/cornerpineffect.h | 15 +- effects/internal/crossdissolvetransition.cpp | 39 ++- effects/internal/crossdissolvetransition.h | 13 +- effects/internal/cubetransition.h | 2 +- .../internal/exponentialfadetransition.cpp | 35 ++- effects/internal/exponentialfadetransition.h | 10 +- effects/internal/fillleftrighteffect.cpp | 32 ++- effects/internal/fillleftrighteffect.h | 14 +- effects/internal/linearfadetransition.cpp | 32 ++- effects/internal/linearfadetransition.h | 10 +- .../internal/logarithmicfadetransition.cpp | 35 ++- effects/internal/logarithmicfadetransition.h | 10 +- effects/internal/paneffect.cpp | 32 ++- effects/internal/paneffect.h | 14 +- effects/internal/richtexteffect.cpp | 41 ++- effects/internal/richtexteffect.h | 16 +- effects/internal/shakeeffect.cpp | 39 ++- effects/internal/shakeeffect.h | 14 +- effects/internal/solideffect.cpp | 41 ++- effects/internal/solideffect.h | 15 +- effects/internal/texteffect.cpp | 41 ++- effects/internal/texteffect.h | 15 +- effects/internal/timecodeeffect.cpp | 41 ++- effects/internal/timecodeeffect.h | 15 +- effects/internal/toneeffect.cpp | 32 ++- effects/internal/toneeffect.h | 17 +- effects/internal/transformeffect.cpp | 39 ++- effects/internal/transformeffect.h | 15 +- effects/internal/voideffect.cpp | 69 +++-- effects/internal/voideffect.h | 21 +- effects/internal/volumeeffect.cpp | 32 ++- effects/internal/volumeeffect.h | 15 +- effects/internal/vsthost.cpp | 36 ++- effects/internal/vsthost.h | 16 +- effects/transition.cpp | 46 ++- effects/transition.h | 22 +- global/clipboard.cpp | 3 +- global/clipboard.h | 1 + global/global.cpp | 12 +- nodes/inputs/boolinput.cpp | 2 +- nodes/inputs/boolinput.h | 2 +- nodes/inputs/colorinput.cpp | 4 +- nodes/inputs/colorinput.h | 2 +- nodes/inputs/comboinput.cpp | 2 +- nodes/inputs/comboinput.h | 2 +- nodes/inputs/fileinput.cpp | 2 +- nodes/inputs/fileinput.h | 2 +- nodes/inputs/fontinput.cpp | 2 +- nodes/inputs/fontinput.h | 2 +- nodes/inputs/stringinput.cpp | 2 +- nodes/inputs/stringinput.h | 2 +- nodes/inputs/vecinput.cpp | 10 +- nodes/inputs/vecinput.h | 10 +- effects/effect.cpp => nodes/node.cpp | 271 +++++++++--------- effects/effect.h => nodes/node.h | 111 ++++--- nodes/nodegraph.cpp | 2 +- nodes/nodegraph.h | 10 +- nodes/nodes/nodeimageoutput.cpp | 38 ++- nodes/nodes/nodeimageoutput.h | 15 +- nodes/nodes/nodemedia.cpp | 37 ++- nodes/nodes/nodemedia.h | 14 +- nodes/nodes/nodeshader.cpp | 81 ++++-- nodes/nodes/nodeshader.h | 26 +- nodes/widgets/buttonwidget.cpp | 2 +- nodes/widgets/buttonwidget.h | 2 +- nodes/widgets/labelwidget.cpp | 2 +- nodes/widgets/labelwidget.h | 2 +- olive.pro | 9 +- panels/effectcontrols.cpp | 49 ++-- panels/effectcontrols.h | 4 +- panels/effectspanel.cpp | 18 +- panels/effectspanel.h | 6 +- panels/grapheditor.cpp | 4 +- panels/timeline.cpp | 40 +-- panels/timeline.h | 4 +- panels/viewer.cpp | 6 +- project/loadthread.cpp | 43 +-- rendering/cacher.cpp | 12 +- rendering/renderfunctions.cpp | 47 +-- rendering/renderfunctions.h | 21 +- rendering/renderthread.cpp | 19 +- rendering/renderthread.h | 5 +- timeline/clip.cpp | 20 +- timeline/clip.h | 14 +- timeline/ghost.h | 1 + timeline/sequence.cpp | 28 +- timeline/sequence.h | 4 +- timeline/timelinefunctions.cpp | 8 +- timeline/track.cpp | 10 +- timeline/track.h | 23 +- timeline/tracklist.cpp | 4 +- timeline/tracklist.h | 6 +- timeline/tracktypes.h | 13 + ui/effectui.cpp | 22 +- ui/effectui.h | 13 +- ui/graphview.cpp | 2 +- ui/keyframedrawing.cpp | 2 +- ui/keyframeview.cpp | 4 +- ui/keyframeview.h | 2 +- ui/nodeui.cpp | 2 +- ui/timelinearea.cpp | 2 +- ui/timelinearea.h | 2 +- ui/timelineview.cpp | 38 +-- ui/viewerwidget.cpp | 1 + ui/viewerwidget.h | 4 +- undo/undo.cpp | 15 +- undo/undo.h | 26 +- 127 files changed, 1674 insertions(+), 867 deletions(-) rename effects/effect.cpp => nodes/node.cpp (77%) rename effects/effect.h => nodes/node.h (76%) create mode 100644 timeline/tracktypes.h diff --git a/dialogs/autocutsilencedialog.cpp b/dialogs/autocutsilencedialog.cpp index 3659cde2c..e5176bcb2 100644 --- a/dialogs/autocutsilencedialog.cpp +++ b/dialogs/autocutsilencedialog.cpp @@ -127,7 +127,7 @@ void AutoCutSilenceDialog::cut_silence() { Clip* clip = clips_.at(j); // Check if this clip is an audio footage clip - if (clip->type() == Track::kTypeAudio + if (clip->type() == olive::kTypeAudio && clip->media() != nullptr && clip->media_stream()->preview_done) { // TODO provide warning for preview not being done diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index b1482e2eb..9b7cad23e 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -33,7 +33,7 @@ #include "panels/timeline.h" #include "undo/undo.h" #include "undo/undostack.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "project/media.h" SpeedDialog::SpeedDialog(QWidget *parent, QVector clips) : QDialog(parent) { @@ -103,7 +103,7 @@ int SpeedDialog::exec() { // get default frame rate/percentage clip_percent = c->speed().value; - if (c->type() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { bool process_video = true; if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_FOOTAGE) { FootageStream* ms = c->media_stream(); @@ -131,7 +131,7 @@ int SpeedDialog::exec() { enable_frame_rate = true; } - } else if (c->type() == Track::kTypeAudio) { + } else if (c->type() == olive::kTypeAudio) { maintain_pitch->setEnabled(true); if (!multiple_audio) { @@ -192,7 +192,7 @@ void SpeedDialog::percent_update() { Clip* c = clips_.at(i); // get frame rate - if (frame_rate->isEnabled() && c->type() == Track::kTypeVideo) { + if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { double clip_fr = c->media_frame_rate() * percent->value(); if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { @@ -236,7 +236,7 @@ void SpeedDialog::duration_update() { } // get frame rate - if (frame_rate->isEnabled() && c->type() == Track::kTypeVideo) { + if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { double clip_fr = c->media_frame_rate() * clip_pc; if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { @@ -276,7 +276,7 @@ void SpeedDialog::frame_rate_update() { old_pc_val = qSNaN(); } - if (c->type() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { // what would the new speed be based on this frame rate double new_clip_speed = frame_rate->value() / c->media_frame_rate(); if (!got_pc_val) { @@ -301,7 +301,7 @@ void SpeedDialog::frame_rate_update() { for (int i=0;itype() == Track::kTypeAudio) { + if (c->type() == olive::kTypeAudio) { long new_clip_len = (qIsNaN(old_pc_val) || qIsNaN(pc_val)) ? c->length() : qRound((c->length() * c->speed().value) / pc_val); @@ -376,7 +376,7 @@ void SpeedDialog::accept() { } // set maintain audio pitch if the user made a selection - if (c->type() == Track::kTypeAudio + if (c->type() == olive::kTypeAudio && maintain_pitch->checkState() != Qt::PartiallyChecked && c->speed().maintain_audio_pitch != maintain_pitch->isChecked()) { audio_pitch_action->AddSetting(c, maintain_pitch->isChecked()); @@ -418,7 +418,7 @@ void SpeedDialog::accept() { if (i > 0 && !qFuzzyCompare(cached_speed, c->speed().value)) { can_change_all = false; } - if (c->type() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { if (qIsNaN(cached_fr)) { cached_fr = c->media_frame_rate(); } else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) { @@ -431,7 +431,7 @@ void SpeedDialog::accept() { // make changes for (int i=0;itype() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple); } else if (can_change_all) { set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple); diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index c636e3a61..5968d7577 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -27,7 +27,7 @@ #include "global/config.h" #include "global/timing.h" #include "effects/effectrow.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "undo/undo.h" #include "timeline/clip.h" #include "timeline/sequence.h" diff --git a/effects/effectfield.h b/effects/effectfield.h index 890a5fed8..e72e3d685 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -26,7 +26,6 @@ #include #include "effects/keyframe.h" -#include "undo/undo.h" #include "undo/undostack.h" #include "nodes/nodedatatypes.h" diff --git a/effects/effectgizmo.cpp b/effects/effectgizmo.cpp index cac508a4a..db810016c 100644 --- a/effects/effectgizmo.cpp +++ b/effects/effectgizmo.cpp @@ -22,9 +22,9 @@ #include "ui/labelslider.h" #include "effects/fields/doublefield.h" -#include "effects/effect.h" +#include "nodes/node.h" -EffectGizmo::EffectGizmo(Effect *parent, int type) : +EffectGizmo::EffectGizmo(Node *parent, int type) : QObject(parent), x_field1(nullptr), x_field_multi1(1.0), diff --git a/effects/effectgizmo.h b/effects/effectgizmo.h index 371b5aea9..aea73fcf2 100644 --- a/effects/effectgizmo.h +++ b/effects/effectgizmo.h @@ -39,12 +39,12 @@ enum GizmoType { #include class DoubleField; -class Effect; +class Node; class EffectGizmo : public QObject { Q_OBJECT public: - EffectGizmo(Effect* parent, int type); + EffectGizmo(Node* parent, int type); QVector world_pos; QVector screen_pos; diff --git a/effects/effectloaders.cpp b/effects/effectloaders.cpp index ebb840fbd..ae04cce81 100644 --- a/effects/effectloaders.cpp +++ b/effects/effectloaders.cpp @@ -24,13 +24,36 @@ #include #include -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/transition.h" #include "global/path.h" #include "panels/panels.h" #include "panels/effectcontrols.h" #include "global/config.h" +#include "effects/internal/transformeffect.h" +#include "effects/internal/texteffect.h" +#include "effects/internal/timecodeeffect.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 "effects/internal/shakeeffect.h" +#include "effects/internal/cornerpineffect.h" +#include "effects/internal/vsthost.h" +#include "effects/internal/fillleftrighteffect.h" +#include "effects/internal/richtexteffect.h" + +#include "effects/internal/crossdissolvetransition.h" +#include "effects/internal/linearfadetransition.h" +#include "effects/internal/logarithmicfadetransition.h" +#include "effects/internal/exponentialfadetransition.h" + +#include "nodes/nodes/nodemedia.h" +#include "nodes/nodes/nodeimageoutput.h" +#include "nodes/nodes/nodeshader.h" + QMutex olive::effects_loaded; void load_internal_effects() { @@ -38,103 +61,35 @@ void load_internal_effects() { qWarning() << "Shaders are disabled, some effects may be nonfunctional"; } - EffectMeta em; + olive::node_library.resize(kInvalidNode); + olive::node_library.fill(nullptr); - // load internal effects - em.path = ":/internalshaders"; - - em.type = EFFECT_TYPE_EFFECT; - em.subtype = Track::kTypeAudio; - - em.name = "Volume"; - em.internal = EFFECT_INTERNAL_VOLUME; - olive::effects.append(em); - - em.name = "Pan"; - em.internal = EFFECT_INTERNAL_PAN; - olive::effects.append(em); - - em.name = "VST Plugin 2.x"; - em.internal = EFFECT_INTERNAL_VST; - olive::effects.append(em); - - em.name = "Tone"; - em.internal = EFFECT_INTERNAL_TONE; - olive::effects.append(em); - - em.name = "Noise"; - em.internal = EFFECT_INTERNAL_NOISE; - olive::effects.append(em); - - em.name = "Fill Left/Right"; - em.internal = EFFECT_INTERNAL_FILLLEFTRIGHT; - olive::effects.append(em); - - em.subtype = Track::kTypeVideo; - - em.name = "Transform"; - em.category = "Distort"; - em.internal = EFFECT_INTERNAL_TRANSFORM; - olive::effects.append(em); - - em.name = "Corner Pin"; - em.internal = EFFECT_INTERNAL_CORNERPIN; - olive::effects.append(em); - - /*em.name = "Mask"; - em.internal = EFFECT_INTERNAL_MASK; - olive::effects.append(em);*/ - - em.name = "Shake"; - em.internal = EFFECT_INTERNAL_SHAKE; - olive::effects.append(em); - - em.name = "Text"; - em.category = "Render"; - em.internal = EFFECT_INTERNAL_TEXT; - olive::effects.append(em); - - em.name = "Rich Text"; - em.category = "Render"; - em.internal = EFFECT_INTERNAL_RICHTEXT; - olive::effects.append(em); - - em.name = "Timecode"; - em.internal = EFFECT_INTERNAL_TIMECODE; - olive::effects.append(em); - - em.name = "Solid"; - em.internal = EFFECT_INTERNAL_SOLID; - olive::effects.append(em); - - // internal transitions - em.type = EFFECT_TYPE_TRANSITION; - em.category = ""; - - em.name = "Cross Dissolve"; - em.internal = TRANSITION_INTERNAL_CROSSDISSOLVE; - olive::effects.append(em); - - em.subtype = Track::kTypeAudio; - - em.name = "Linear Fade"; - em.internal = TRANSITION_INTERNAL_LINEARFADE; - olive::effects.append(em); - - em.name = "Exponential Fade"; - em.internal = TRANSITION_INTERNAL_EXPONENTIALFADE; - olive::effects.append(em); - - em.name = "Logarithmic Fade"; - em.internal = TRANSITION_INTERNAL_LOGARITHMICFADE; - olive::effects.append(em); + olive::node_library[kTransformEffect] = std::make_shared(nullptr); + olive::node_library[kTextInput] = std::make_shared(nullptr); + olive::node_library[kSolidInput] = std::make_shared(nullptr); + olive::node_library[kNoiseInput] = std::make_shared(nullptr); + olive::node_library[kVolumeEffect] = std::make_shared(nullptr); + olive::node_library[kPanEffect] = std::make_shared(nullptr); + olive::node_library[kToneInput] = std::make_shared(nullptr); + olive::node_library[kShakeEffect] = std::make_shared(nullptr); + olive::node_library[kTimecodeEffect] = std::make_shared(nullptr); + olive::node_library[kFillLeftRightEffect] = std::make_shared(nullptr); + olive::node_library[kVstEffect] = std::make_shared(nullptr); + olive::node_library[kCornerPinEffect] = std::make_shared(nullptr); + olive::node_library[kRichTextInput] = std::make_shared(nullptr); + olive::node_library[kMediaInput] = std::make_shared(nullptr); + olive::node_library[kImageOutput] = std::make_shared(nullptr); + olive::node_library[kCrossDissolveTransition] = std::make_shared(nullptr); + olive::node_library[kLinearFadeTransition] = std::make_shared(nullptr); + olive::node_library[kExponentialFadeTransition] = std::make_shared(nullptr); + olive::node_library[kLogarithmicFadeTransition] = std::make_shared(nullptr); } void load_shader_effects_worker(const QString& effects_path) { QDir effects_dir(effects_path); if (effects_dir.exists()) { - QList entries = effects_dir.entryList({"*.xml", "*.blend"}, + QList entries = effects_dir.entryList({"*.xml"}, QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); for (int i=0;i blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files); - for (int i=0;i(nullptr, + effect_name, + effect_id, + effect_cat, + file_url)); + } else { + qCritical() << "Invalid effect found in" << entries.at(i); + } + break; + } + reader.readNext(); + } + + file.close(); } } } diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index b96349cc0..7e2abaa7c 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -32,12 +32,12 @@ #include "panels/effectcontrols.h" #include "panels/viewer.h" #include "panels/grapheditor.h" -#include "effect.h" +#include "nodes/node.h" #include "ui/viewerwidget.h" #include "ui/keyframenavigator.h" #include "ui/clickablelabel.h" -EffectRow::EffectRow(Effect *parent, +EffectRow::EffectRow(Node *parent, const QString &id, const QString &name, bool savable, @@ -77,7 +77,7 @@ bool EffectRow::IsKeyframing() { } void EffectRow::SetKeyframingInternal(bool b) { - if (GetParentEffect()->meta->type != EFFECT_TYPE_TRANSITION) { + if (GetParentEffect()->type() != EFFECT_TYPE_TRANSITION) { keyframing_ = b; emit KeyframingSetChanged(keyframing_); } @@ -315,9 +315,9 @@ void EffectRow::SetKeyframeOnAllFields(ComboAction* ca) { panel_effect_controls->update_keyframes(); } -Effect *EffectRow::GetParentEffect() +Node *EffectRow::GetParentEffect() { - return static_cast(parent()); + return static_cast(parent()); } const QString &EffectRow::name() { diff --git a/effects/effectrow.h b/effects/effectrow.h index 1afc6dfa4..cbdf2256b 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -24,7 +24,7 @@ #include #include -class Effect; +class Node; class QGridLayout; class EffectField; class QLabel; @@ -83,7 +83,7 @@ public: * Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent * the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false. */ - EffectRow(Effect* parent, + EffectRow(Node* parent, const QString& id, const QString& name, bool savable = true, @@ -126,7 +126,7 @@ public: * * @return The parent Effect object that this row is attached to. */ - Effect* GetParentEffect(); + Node* GetParentEffect(); /** * @brief Return the row's name diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index f706bc08a..ac3be8ea6 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -22,7 +22,8 @@ #include -#include "effects/effect.h" +#include "nodes/node.h" +#include "undo/undo.h" BoolField::BoolField(EffectRow *parent) : EffectField(parent, EffectField::EFFECT_FIELD_BOOL) diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index 1797681a4..5702b4582 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -23,7 +23,8 @@ #include #include "ui/colorbutton.h" -#include "effects/effect.h" +#include "nodes/node.h" +#include "undo/undo.h" ColorField::ColorField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_COLOR) diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 7471f7e16..c9542a000 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -22,8 +22,9 @@ #include -#include "effects/effect.h" +#include "nodes/node.h" #include "ui/comboboxex.h" +#include "undo/undo.h" ComboField::ComboField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_COMBO) diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 9f70bf033..14ef3ec15 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -20,7 +20,8 @@ #include "doublefield.h" -#include "effects/effect.h" +#include "nodes/node.h" +#include "undo/undo.h" DoubleField::DoubleField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_DOUBLE), diff --git a/effects/fields/doublefield.h b/effects/fields/doublefield.h index 7c1b962e2..11734244e 100644 --- a/effects/fields/doublefield.h +++ b/effects/fields/doublefield.h @@ -24,6 +24,8 @@ #include "../effectfield.h" #include "ui/labelslider.h" +class KeyframeDataChange; + /** * @brief The DoubleField class * diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index b38a035c4..cc7f7a244 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -23,7 +23,8 @@ #include #include "ui/embeddedfilechooser.h" -#include "effects/effect.h" +#include "nodes/node.h" +#include "undo/undo.h" FileField::FileField(EffectRow* parent) : EffectField(parent, EffectField::EFFECT_FIELD_FILE) diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index 7738f9bc5..24e51615a 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -24,7 +24,8 @@ #include #include "ui/comboboxex.h" -#include "effects/effect.h" +#include "nodes/node.h" +#include "undo/undo.h" // NOTE/TODO: This shares a lot of similarity with ComboInput, and could probably be a derived class of it diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 6c2720113..6da53cd93 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -23,9 +23,10 @@ #include #include -#include "effects/effect.h" +#include "nodes/node.h" #include "ui/texteditex.h" #include "global/config.h" +#include "undo/undo.h" StringField::StringField(EffectRow* parent, bool rich_text) : EffectField(parent, EffectField::EFFECT_FIELD_STRING), diff --git a/effects/internal/audionoiseeffect.cpp b/effects/internal/audionoiseeffect.cpp index a1323c0f6..128921978 100644 --- a/effects/internal/audionoiseeffect.cpp +++ b/effects/internal/audionoiseeffect.cpp @@ -23,7 +23,7 @@ #include #include -AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { +AudioNoiseEffect::AudioNoiseEffect(Clip* c) : Node(c) { amount_val = new DoubleInput(this, "amount", tr("Amount")); amount_val->SetMinimum(0); amount_val->SetDefault(20); @@ -33,6 +33,36 @@ AudioNoiseEffect::AudioNoiseEffect(Clip* c, const EffectMeta *em) : Effect(c, em mix_val->SetValueAt(0, true); } +QString AudioNoiseEffect::name() +{ + return tr("Noise"); +} + +QString AudioNoiseEffect::id() +{ + return "org.olivevideoeditor.Olive.noise"; +} + +QString AudioNoiseEffect::description() +{ + return tr("Generate audio noise that can be mixed with this clip."); +} + +EffectType AudioNoiseEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType AudioNoiseEffect::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr AudioNoiseEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void AudioNoiseEffect::process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/audionoiseeffect.h b/effects/internal/audionoiseeffect.h index 4e1c173ad..d950080b5 100644 --- a/effects/internal/audionoiseeffect.h +++ b/effects/internal/audionoiseeffect.h @@ -21,12 +21,20 @@ #ifndef AUDIONOISEEFFECT_H #define AUDIONOISEEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class AudioNoiseEffect : public Effect { +class AudioNoiseEffect : public Node { Q_OBJECT public: - AudioNoiseEffect(Clip* c, const EffectMeta* em); + AudioNoiseEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/cornerpineffect.cpp b/effects/internal/cornerpineffect.cpp index 14bd4f4ba..2e5f93281 100644 --- a/effects/internal/cornerpineffect.cpp +++ b/effects/internal/cornerpineffect.cpp @@ -24,8 +24,8 @@ #include "timeline/clip.h" #include "global/debug.h" -CornerPinEffect::CornerPinEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - SetFlags(Effect::CoordsFlag | Effect::ShaderFlag); +CornerPinEffect::CornerPinEffect(Clip* c) : Node(c) { + SetFlags(Node::CoordsFlag | Node::ShaderFlag); top_left = new Vec2Input(this, "topleft", tr("Top Left")); @@ -58,6 +58,41 @@ CornerPinEffect::CornerPinEffect(Clip* c, const EffectMeta *em) : Effect(c, em) shader_frag_path_ = "cornerpin.frag"; } +QString CornerPinEffect::name() +{ + return tr("Corner Pin"); +} + +QString CornerPinEffect::id() +{ + return "org.olivevideoeditor.Olive.cornerpin"; +} + +QString CornerPinEffect::category() +{ + return tr("Distort"); +} + +QString CornerPinEffect::description() +{ + return tr("Distort/warp this clip by pinning each of its four corners."); +} + +EffectType CornerPinEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType CornerPinEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr CornerPinEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void CornerPinEffect::process_coords(double timecode, GLTextureCoords &coords, int) { coords.vertex_top_left += top_left->GetVector2DAt(timecode); coords.vertex_top_right += top_right->GetVector2DAt(timecode); diff --git a/effects/internal/cornerpineffect.h b/effects/internal/cornerpineffect.h index 573164254..9226d7c40 100644 --- a/effects/internal/cornerpineffect.h +++ b/effects/internal/cornerpineffect.h @@ -21,12 +21,21 @@ #ifndef CORNERPINEFFECT_H #define CORNERPINEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class CornerPinEffect : public Effect { +class CornerPinEffect : public Node { Q_OBJECT public: - CornerPinEffect(Clip* c, const EffectMeta* em); + CornerPinEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + void process_coords(double timecode, GLTextureCoords& coords, int data); void process_shader(double timecode, GLTextureCoords& coords, int iterations); void gizmo_draw(double timecode, GLTextureCoords& coords); diff --git a/effects/internal/crossdissolvetransition.cpp b/effects/internal/crossdissolvetransition.cpp index c5c35f12c..dc895acea 100644 --- a/effects/internal/crossdissolvetransition.cpp +++ b/effects/internal/crossdissolvetransition.cpp @@ -22,8 +22,43 @@ #include -CrossDissolveTransition::CrossDissolveTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) { - SetFlags(Effect::CoordsFlag); +CrossDissolveTransition::CrossDissolveTransition(Clip* c) : Transition(c) { + SetFlags(Node::CoordsFlag); +} + +QString CrossDissolveTransition::name() +{ + return tr("Cross Dissolve"); +} + +QString CrossDissolveTransition::id() +{ + return "org.olivevideoeditor.Olive.crossdissolve"; +} + +QString CrossDissolveTransition::category() +{ + return tr("Dissolves"); +} + +QString CrossDissolveTransition::description() +{ + return tr("Dissolve clips evenly."); +} + +EffectType CrossDissolveTransition::type() +{ + return EFFECT_TYPE_TRANSITION; +} + +olive::TrackType CrossDissolveTransition::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr CrossDissolveTransition::Create(Clip *c) +{ + return std::make_shared(c); } void CrossDissolveTransition::process_coords(double progress, GLTextureCoords& coords, int data) { diff --git a/effects/internal/crossdissolvetransition.h b/effects/internal/crossdissolvetransition.h index 42afc7b93..153665def 100644 --- a/effects/internal/crossdissolvetransition.h +++ b/effects/internal/crossdissolvetransition.h @@ -25,8 +25,17 @@ class CrossDissolveTransition : public Transition { public: - CrossDissolveTransition(Clip *c, Clip *s, const EffectMeta* em); - void process_coords(double timecode, GLTextureCoords &, int data); + CrossDissolveTransition(Clip *c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + + virtual void process_coords(double timecode, GLTextureCoords &, int data) override; }; #endif // CROSSDISSOLVETRANSITION_H diff --git a/effects/internal/cubetransition.h b/effects/internal/cubetransition.h index a2c4da552..b1dbe0607 100644 --- a/effects/internal/cubetransition.h +++ b/effects/internal/cubetransition.h @@ -25,7 +25,7 @@ class CubeTransition : public Transition { public: - CubeTransition(Clip* c, Clip* s, const EffectMeta* em); + CubeTransition(Clip* c, Clip* s); void process_coords(double timecode, GLTextureCoords &, int data); }; diff --git a/effects/internal/exponentialfadetransition.cpp b/effects/internal/exponentialfadetransition.cpp index 292384b4d..8c5aae400 100644 --- a/effects/internal/exponentialfadetransition.cpp +++ b/effects/internal/exponentialfadetransition.cpp @@ -22,7 +22,40 @@ #include -ExponentialFadeTransition::ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} +ExponentialFadeTransition::ExponentialFadeTransition(Clip* c) : + Transition(c) +{ +} + +QString ExponentialFadeTransition::name() +{ + return tr("Exponential Fade"); +} + +QString ExponentialFadeTransition::id() +{ + return "org.olivevideoeditor.Olive.exponentialfade"; +} + +QString ExponentialFadeTransition::description() +{ + return tr("An exponential audio fade that starts slow and ends fast."); +} + +EffectType ExponentialFadeTransition::type() +{ + return EFFECT_TYPE_TRANSITION; +} + +olive::TrackType ExponentialFadeTransition::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr ExponentialFadeTransition::Create(Clip *c) +{ + return std::make_shared(c); +} void ExponentialFadeTransition::process_audio(double timecode_start, double timecode_end, diff --git a/effects/internal/exponentialfadetransition.h b/effects/internal/exponentialfadetransition.h index a9201f5dc..19e326a06 100644 --- a/effects/internal/exponentialfadetransition.h +++ b/effects/internal/exponentialfadetransition.h @@ -25,7 +25,15 @@ class ExponentialFadeTransition : public Transition { public: - ExponentialFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + ExponentialFadeTransition(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/fillleftrighteffect.cpp b/effects/internal/fillleftrighteffect.cpp index bbc04285e..141583124 100644 --- a/effects/internal/fillleftrighteffect.cpp +++ b/effects/internal/fillleftrighteffect.cpp @@ -25,12 +25,42 @@ enum FillType { FILL_TYPE_RIGHT }; -FillLeftRightEffect::FillLeftRightEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { +FillLeftRightEffect::FillLeftRightEffect(Clip* c) : Node(c) { fill_type = new ComboInput(this, "type", tr("Type")); fill_type->AddItem(tr("Fill Left with Right"), FILL_TYPE_LEFT); fill_type->AddItem(tr("Fill Right with Left"), FILL_TYPE_RIGHT); } +QString FillLeftRightEffect::name() +{ + return tr("Fill Left/Right"); +} + +QString FillLeftRightEffect::id() +{ + return "org.olivevideoeditor.Olive.fillleftright"; +} + +QString FillLeftRightEffect::description() +{ + return tr("Replaces either the left or right channel with the other"); +} + +EffectType FillLeftRightEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType FillLeftRightEffect::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr FillLeftRightEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void FillLeftRightEffect::process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/fillleftrighteffect.h b/effects/internal/fillleftrighteffect.h index a80ae0227..1acd1de54 100644 --- a/effects/internal/fillleftrighteffect.h +++ b/effects/internal/fillleftrighteffect.h @@ -21,12 +21,20 @@ #ifndef FILLLEFTRIGHTEFFECT_H #define FILLLEFTRIGHTEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class FillLeftRightEffect : public Effect { +class FillLeftRightEffect : public Node { Q_OBJECT public: - FillLeftRightEffect(Clip* c, const EffectMeta* em); + FillLeftRightEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/linearfadetransition.cpp b/effects/internal/linearfadetransition.cpp index ddb89d0af..e89e311dc 100644 --- a/effects/internal/linearfadetransition.cpp +++ b/effects/internal/linearfadetransition.cpp @@ -20,7 +20,37 @@ #include "linearfadetransition.h" -LinearFadeTransition::LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} +LinearFadeTransition::LinearFadeTransition(Clip* c) : Transition(c) {} + +QString LinearFadeTransition::name() +{ + return tr("Linear Fade"); +} + +QString LinearFadeTransition::id() +{ + return "org.olivevideoeditor.Olive.linearfade"; +} + +QString LinearFadeTransition::description() +{ + return tr("An linear audio fade that fades evenly at a constant rate."); +} + +EffectType LinearFadeTransition::type() +{ + return EFFECT_TYPE_TRANSITION; +} + +olive::TrackType LinearFadeTransition::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr LinearFadeTransition::Create(Clip *c) +{ + return std::make_shared(c); +} void LinearFadeTransition::process_audio(double timecode_start, double timecode_end, diff --git a/effects/internal/linearfadetransition.h b/effects/internal/linearfadetransition.h index abc5b7383..b2add8670 100644 --- a/effects/internal/linearfadetransition.h +++ b/effects/internal/linearfadetransition.h @@ -25,7 +25,15 @@ class LinearFadeTransition : public Transition { public: - LinearFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + LinearFadeTransition(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/logarithmicfadetransition.cpp b/effects/internal/logarithmicfadetransition.cpp index fe2f444ff..79fc82875 100644 --- a/effects/internal/logarithmicfadetransition.cpp +++ b/effects/internal/logarithmicfadetransition.cpp @@ -22,7 +22,40 @@ #include -LogarithmicFadeTransition::LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em) : Transition(c, s, em) {} +LogarithmicFadeTransition::LogarithmicFadeTransition(Clip* c) : + Transition(c) +{ +} + +QString LogarithmicFadeTransition::name() +{ + return tr("Logarithmic Fade"); +} + +QString LogarithmicFadeTransition::id() +{ + return "org.olivevideoeditor.Olive.logarithmicfade"; +} + +QString LogarithmicFadeTransition::description() +{ + return tr("An logarithmic audio fade that starts fast and ends slow."); +} + +EffectType LogarithmicFadeTransition::type() +{ + return EFFECT_TYPE_TRANSITION; +} + +olive::TrackType LogarithmicFadeTransition::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr LogarithmicFadeTransition::Create(Clip *c) +{ + return std::make_shared(c); +} void LogarithmicFadeTransition::process_audio(double timecode_start, double timecode_end, diff --git a/effects/internal/logarithmicfadetransition.h b/effects/internal/logarithmicfadetransition.h index 5cd03f57a..0d3d2cbd1 100644 --- a/effects/internal/logarithmicfadetransition.h +++ b/effects/internal/logarithmicfadetransition.h @@ -25,7 +25,15 @@ class LogarithmicFadeTransition : public Transition { public: - LogarithmicFadeTransition(Clip* c, Clip* s, const EffectMeta* em); + LogarithmicFadeTransition(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip* c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/paneffect.cpp b/effects/internal/paneffect.cpp index 57b55074b..223933f8a 100644 --- a/effects/internal/paneffect.cpp +++ b/effects/internal/paneffect.cpp @@ -28,13 +28,43 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" -PanEffect::PanEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { +PanEffect::PanEffect(Clip* c) : Node(c) { pan_val = new DoubleInput(this, "pan", tr("Pan")); pan_val->SetMinimum(-100); pan_val->SetDefault(0); pan_val->SetMaximum(100); } +QString PanEffect::name() +{ + return tr("Pan"); +} + +QString PanEffect::id() +{ + return "org.olivevideoeditor.Olive.pan"; +} + +QString PanEffect::description() +{ + return tr("Modifying the panning on a stereo audio clip."); +} + +EffectType PanEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType PanEffect::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr PanEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void PanEffect::process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/paneffect.h b/effects/internal/paneffect.h index f0765ba24..ac6ea6034 100644 --- a/effects/internal/paneffect.h +++ b/effects/internal/paneffect.h @@ -21,12 +21,20 @@ #ifndef PANEFFECT_H #define PANEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class PanEffect : public Effect { +class PanEffect : public Node { Q_OBJECT public: - PanEffect(Clip* c, const EffectMeta* em); + PanEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/richtexteffect.cpp b/effects/internal/richtexteffect.cpp index b773048a8..43d7ec399 100644 --- a/effects/internal/richtexteffect.cpp +++ b/effects/internal/richtexteffect.cpp @@ -34,10 +34,10 @@ enum AutoscrollDirection { SCROLL_RIGHT, }; -RichTextEffect::RichTextEffect(Clip *c, const EffectMeta *em) : - Effect(c, em) +RichTextEffect::RichTextEffect(Clip *c) : + Node(c) { - SetFlags(Effect::SuperimposeFlag); + SetFlags(Node::SuperimposeFlag); text_val = new StringInput(this, "text", tr("Text")); @@ -82,6 +82,41 @@ RichTextEffect::RichTextEffect(Clip *c, const EffectMeta *em) : ""); } +QString RichTextEffect::name() +{ + return tr("Rich Text"); +} + +QString RichTextEffect::id() +{ + return "org.olivevideoeditor.Olive.richtext"; +} + +QString RichTextEffect::category() +{ + return tr("Render"); +} + +QString RichTextEffect::description() +{ + return tr("Render formatted rich text over a clip."); +} + +EffectType RichTextEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType RichTextEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr RichTextEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void RichTextEffect::redraw(double timecode) { QPainter p(&img); diff --git a/effects/internal/richtexteffect.h b/effects/internal/richtexteffect.h index 3853d07ac..8944d802c 100644 --- a/effects/internal/richtexteffect.h +++ b/effects/internal/richtexteffect.h @@ -21,13 +21,23 @@ #ifndef RICHTEXTEFFECT_H #define RICHTEXTEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class RichTextEffect : public Effect { +class RichTextEffect : public Node { Q_OBJECT public: - RichTextEffect(Clip* c, const EffectMeta *em); + RichTextEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void redraw(double timecode) override; + protected: virtual bool AlwaysUpdate() override; private: diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 786812e9d..7d623dd19 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -32,8 +32,8 @@ #include "panels/timeline.h" #include "global/debug.h" -ShakeEffect::ShakeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { - SetFlags(Effect::CoordsFlag); +ShakeEffect::ShakeEffect(Clip* c) : Node(c) { + SetFlags(Node::CoordsFlag); intensity_val = new DoubleInput(this, "intensity", tr("Intensity")); intensity_val->SetMinimum(0); @@ -79,3 +79,38 @@ void ShakeEffect::process_coords(double timecode, GLTextureCoords& coords, int) coords.matrix.rotate(QQuaternion::fromEulerAngles(0.0f, 0.0f, rotoff)); } + +QString ShakeEffect::name() +{ + return tr("Shake"); +} + +QString ShakeEffect::id() +{ + return "org.olivevideoeditor.Olive.shake"; +} + +QString ShakeEffect::category() +{ + return tr("Distort"); +} + +QString ShakeEffect::description() +{ + return tr("Simulate a camera shake movement."); +} + +EffectType ShakeEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType ShakeEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr ShakeEffect::Create(Clip *c) +{ + return std::make_shared(c); +} diff --git a/effects/internal/shakeeffect.h b/effects/internal/shakeeffect.h index e0bea64fc..a9e6e9829 100644 --- a/effects/internal/shakeeffect.h +++ b/effects/internal/shakeeffect.h @@ -21,16 +21,24 @@ #ifndef SHAKEEFFECT_H #define SHAKEEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" #define RANDOM_VAL_SIZE 30 -class ShakeEffect : public Effect { +class ShakeEffect : public Node { Q_OBJECT public: - ShakeEffect(Clip* c, const EffectMeta* em); + ShakeEffect(Clip* c); virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + DoubleInput* intensity_val; DoubleInput* rotation_val; DoubleInput* frequency_val; diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 1fb51f51b..452ea1e93 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -34,10 +34,10 @@ const int SMPTE_BARS = 7; const int SMPTE_STRIP_COUNT = 3; const int SMPTE_LOWER_BARS = 4; -SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : - Effect(c, em) +SolidEffect::SolidEffect(Clip* c) : + Node(c) { - SetFlags(Effect::SuperimposeFlag); + SetFlags(Node::SuperimposeFlag); // Field for solid type solid_type = new ComboInput(this, "type", tr("Type")); @@ -69,6 +69,41 @@ SolidEffect::SolidEffect(Clip* c, const EffectMeta* em) : fragPath = ":/shaders/solideffect.frag";*/ } +QString SolidEffect::name() +{ + return tr("Solid"); +} + +QString SolidEffect::id() +{ + return "org.olivevideoeditor.Olive.solid"; +} + +QString SolidEffect::category() +{ + return tr("Render"); +} + +QString SolidEffect::description() +{ + return tr("Render a solid color over this clip."); +} + +EffectType SolidEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType SolidEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr SolidEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void SolidEffect::redraw(double timecode) { int w = img.width(); int h = img.height(); diff --git a/effects/internal/solideffect.h b/effects/internal/solideffect.h index 26ca04351..f1bd50c1a 100644 --- a/effects/internal/solideffect.h +++ b/effects/internal/solideffect.h @@ -21,11 +21,11 @@ #ifndef SOLIDEFFECT_H #define SOLIDEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" #include -class SolidEffect : public Effect { +class SolidEffect : public Node { Q_OBJECT public: enum SolidType { @@ -34,7 +34,16 @@ public: SOLID_TYPE_CHECKERBOARD }; - SolidEffect(Clip* c, const EffectMeta *em); + SolidEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void redraw(double timecode) override; void SetType(SolidType type); diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 458ee4d4a..14515ad68 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -42,10 +42,10 @@ #include "ui/blur.h" #include "global/config.h" -TextEffect::TextEffect(Clip* c, const EffectMeta* em) : - Effect(c, em) +TextEffect::TextEffect(Clip* c) : + Node(c) { - SetFlags(Effect::SuperimposeFlag); + SetFlags(Node::SuperimposeFlag); text_val = new StringInput(this, "text", tr("Text"), false); @@ -121,6 +121,41 @@ TextEffect::TextEffect(Clip* c, const EffectMeta* em) : shader_frag_path_ = "dropshadow.frag"; } +QString TextEffect::name() +{ + return tr("Text"); +} + +QString TextEffect::id() +{ + return "org.olivevideoeditor.Olive.text"; +} + +QString TextEffect::category() +{ + return tr("Render"); +} + +QString TextEffect::description() +{ + return tr("Generate simple text over this clip"); +} + +EffectType TextEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType TextEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr TextEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void TextEffect::redraw(double timecode) { QColor bkg = set_color_button->GetColorAt(timecode); bkg.setAlpha(0); diff --git a/effects/internal/texteffect.h b/effects/internal/texteffect.h index 37a71f7bb..de4076616 100644 --- a/effects/internal/texteffect.h +++ b/effects/internal/texteffect.h @@ -21,15 +21,24 @@ #ifndef TEXTEFFECT_H #define TEXTEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" #include #include -class TextEffect : public Effect { +class TextEffect : public Node { Q_OBJECT public: - TextEffect(Clip* c, const EffectMeta *em); + TextEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void redraw(double timecode) override; private slots: void outline_enable(bool); diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 3353057e3..32a892cdf 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -42,10 +42,10 @@ #include "ui/colorbutton.h" #include "global/config.h" -TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) : - Effect(c, em) +TimecodeEffect::TimecodeEffect(Clip* c) : + Node(c) { - SetFlags(Effect::SuperimposeFlag); + SetFlags(Node::SuperimposeFlag); tc_select = new ComboInput(this, "tc_selector", tr("Timecode")); tc_select->AddItem(tr("Sequence"), true); @@ -74,6 +74,41 @@ TimecodeEffect::TimecodeEffect(Clip* c, const EffectMeta* em) : prepend_text = new StringInput(this, "prepend", tr("Prepend"), false); } +QString TimecodeEffect::name() +{ + return tr("Timecode"); +} + +QString TimecodeEffect::id() +{ + return "org.olivevideoeditor.Olive.timecode"; +} + +QString TimecodeEffect::category() +{ + return tr("Render"); +} + +QString TimecodeEffect::description() +{ + return tr("Render the media or sequence timecode on this clip."); +} + +EffectType TimecodeEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType TimecodeEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr TimecodeEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void TimecodeEffect::redraw(double timecode) { Sequence* sequence = parent_clip->track()->sequence(); diff --git a/effects/internal/timecodeeffect.h b/effects/internal/timecodeeffect.h index 693452d06..71051d421 100644 --- a/effects/internal/timecodeeffect.h +++ b/effects/internal/timecodeeffect.h @@ -21,15 +21,24 @@ #ifndef TIMECODEEFFECT_H #define TIMECODEEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" #include #include -class TimecodeEffect : public Effect { +class TimecodeEffect : public Node { Q_OBJECT public: - TimecodeEffect(Clip* c, const EffectMeta *em); + TimecodeEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void redraw(double timecode) override; DoubleInput* scale_val; ColorInput* color_val; diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index c207f76bb..b0b66fa46 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -27,7 +27,7 @@ #include "timeline/clip.h" #include "timeline/sequence.h" -ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_MIN) { +ToneEffect::ToneEffect(Clip* c) : Node(c), sinX(INT_MIN) { type_val = new ComboInput(this, "type", tr("Type")); type_val->AddItem(tr("Sine"), TONE_TYPE_SINE); @@ -45,6 +45,36 @@ ToneEffect::ToneEffect(Clip* c, const EffectMeta *em) : Effect(c, em), sinX(INT_ mix_val->SetValueAt(0, true); } +QString ToneEffect::name() +{ + return tr("Tone"); +} + +QString ToneEffect::id() +{ + return "org.olivevideoeditor.Olive.tone"; +} + +QString ToneEffect::description() +{ + return tr("Generate a sine wave tone to mix into this clip's audio."); +} + +EffectType ToneEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType ToneEffect::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr ToneEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void ToneEffect::process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/toneeffect.h b/effects/internal/toneeffect.h index 4420fbbb1..ddb723d79 100644 --- a/effects/internal/toneeffect.h +++ b/effects/internal/toneeffect.h @@ -21,12 +21,20 @@ #ifndef TONEEFFECT_H #define TONEEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class ToneEffect : public Effect { +class ToneEffect : public Node { Q_OBJECT public: - ToneEffect(Clip* c, const EffectMeta* em); + ToneEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, @@ -34,11 +42,12 @@ public: int channel_count, int type) override; +private: ComboInput* type_val; DoubleInput* freq_val; DoubleInput* amount_val; BoolInput* mix_val; -private: + int sinX; }; diff --git a/effects/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp index c769b63e5..07dd64898 100644 --- a/effects/internal/transformeffect.cpp +++ b/effects/internal/transformeffect.cpp @@ -43,8 +43,8 @@ #include "panels/viewer.h" #include "ui/viewerwidget.h" -TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) { - SetFlags(Effect::CoordsFlag); +TransformEffect::TransformEffect(Clip* c) : Node(c) { + SetFlags(Node::CoordsFlag); position = new Vec2Input(this, "pos", tr("Position")); @@ -125,6 +125,41 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) refresh(); } +QString TransformEffect::name() +{ + return tr("Transform"); +} + +QString TransformEffect::id() +{ + return "org.olivevideoeditor.Olive.transform"; +} + +QString TransformEffect::category() +{ + return tr("Distort"); +} + +QString TransformEffect::description() +{ + return tr("Transform the position, scale, and rotation of this clip."); +} + +EffectType TransformEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType TransformEffect::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr TransformEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void TransformEffect::refresh() { if (parent_clip != nullptr && parent_clip->track()->sequence() != nullptr) { diff --git a/effects/internal/transformeffect.h b/effects/internal/transformeffect.h index 19c856c28..2ead33e31 100644 --- a/effects/internal/transformeffect.h +++ b/effects/internal/transformeffect.h @@ -21,12 +21,21 @@ #ifndef TRANSFORMEFFECT_H #define TRANSFORMEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class TransformEffect : public Effect { +class TransformEffect : public Node { Q_OBJECT public: - TransformEffect(Clip* c, const EffectMeta* em); + TransformEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void refresh() override; virtual void process_coords(double timecode, GLTextureCoords& coords, int data) override; diff --git a/effects/internal/voideffect.cpp b/effects/internal/voideffect.cpp index a82aaffdd..f5eb5a8d2 100644 --- a/effects/internal/voideffect.cpp +++ b/effects/internal/voideffect.cpp @@ -27,24 +27,55 @@ #include "ui/collapsiblewidget.h" #include "global/debug.h" -VoidEffect::VoidEffect(Clip* c, const QString& n) : Effect(c, nullptr) { - QString display_name; - if (n.isEmpty()) { - display_name = tr("(unknown)"); - } else { - display_name = n; +VoidEffect::VoidEffect(Clip* c, const QString& n, const QString& id) : + Node(c), + display_name_(n), + id_(id) +{ + if (display_name_.isEmpty()) { + display_name_ = tr("(unknown)"); } - new LabelWidget(this, tr("Missing Effect"), display_name); - - name = display_name; - - void_meta.type = EFFECT_TYPE_EFFECT; - meta = &void_meta; + new LabelWidget(this, tr("Missing Effect"), display_name_); } -EffectPtr VoidEffect::copy(Clip* c) { - EffectPtr copy = std::make_shared(c, name); +QString VoidEffect::name() +{ + return display_name_; +} + +QString VoidEffect::id() +{ + return id_; +} + +QString VoidEffect::description() +{ + return QString(); +} + +EffectType VoidEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType VoidEffect::subtype() +{ + return olive::kTypeVideo; +} + +bool VoidEffect::IsCreatable() +{ + return false; +} + +NodePtr VoidEffect::Create(Clip *) +{ + return nullptr; +} + +NodePtr VoidEffect::copy(Clip* c) { + NodePtr copy = std::make_shared(c, display_name_, id_); copy->SetEnabled(IsEnabled()); copy_field_keyframes(copy); return copy; @@ -53,7 +84,7 @@ EffectPtr VoidEffect::copy(Clip* c) { void VoidEffect::load(QXmlStreamReader &stream) { QString tag = stream.name().toString(); - QXmlStreamWriter writer(&bytes); + QXmlStreamWriter writer(&bytes_); // copy XML from reader to writer while (!stream.atEnd() && !(stream.name() == tag && stream.isEndElement())) { @@ -75,18 +106,18 @@ void VoidEffect::load(QXmlStreamReader &stream) { } void VoidEffect::save(QXmlStreamWriter &stream) { - if (!name.isEmpty()) { - stream.writeAttribute("name", name); + if (!display_name_.isEmpty()) { + stream.writeAttribute("name", display_name_); stream.writeAttribute("enabled", QString::number(IsEnabled())); // force xml writer to expand tag, ignored when loading stream.writeStartElement("void"); stream.writeEndElement(); - if (!bytes.isEmpty()) { + if (!bytes_.isEmpty()) { // write stored data QIODevice* device = stream.device(); - device->write(bytes); + device->write(bytes_); } } } diff --git a/effects/internal/voideffect.h b/effects/internal/voideffect.h index cfd93141a..e551f4ae0 100644 --- a/effects/internal/voideffect.h +++ b/effects/internal/voideffect.h @@ -27,19 +27,28 @@ * isn't lost if the user saves over the project. */ -#include "effects/effect.h" +#include "nodes/node.h" -class VoidEffect : public Effect { +class VoidEffect : public Node { Q_OBJECT public: - VoidEffect(Clip* c, const QString& n); + VoidEffect(Clip* c, const QString& n, const QString &id); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual bool IsCreatable() override; + virtual NodePtr Create(Clip *c) override; + virtual NodePtr copy(Clip* c) override; - virtual EffectPtr copy(Clip* c) override; virtual void load(QXmlStreamReader &stream) override; virtual void save(QXmlStreamWriter &stream) override; private: - QByteArray bytes; - EffectMeta void_meta; + QByteArray bytes_; + QString display_name_; + QString id_; }; #endif // VOIDEFFECT_H diff --git a/effects/internal/volumeeffect.cpp b/effects/internal/volumeeffect.cpp index c400ff89b..96c66b676 100644 --- a/effects/internal/volumeeffect.cpp +++ b/effects/internal/volumeeffect.cpp @@ -28,7 +28,7 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" -VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { +VolumeEffect::VolumeEffect(Clip* c) : Node(c) { volume_val = new DoubleInput(this, "volume", tr("Volume")); // set defaults @@ -36,6 +36,36 @@ VolumeEffect::VolumeEffect(Clip* c, const EffectMeta *em) : Effect(c, em) { volume_val->SetDisplayType(LabelSlider::Decibel); } +QString VolumeEffect::name() +{ + return tr("Volume"); +} + +QString VolumeEffect::id() +{ + return "org.olivevideoeditor.Olive.volume"; +} + +QString VolumeEffect::description() +{ + return tr("Adjust the volume of this clip's audio"); +} + +EffectType VolumeEffect::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType VolumeEffect::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr VolumeEffect::Create(Clip *c) +{ + return std::make_shared(c); +} + void VolumeEffect::process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/internal/volumeeffect.h b/effects/internal/volumeeffect.h index 19c855217..d8cdbf81f 100644 --- a/effects/internal/volumeeffect.h +++ b/effects/internal/volumeeffect.h @@ -21,12 +21,20 @@ #ifndef VOLUMEEFFECT_H #define VOLUMEEFFECT_H -#include "effects/effect.h" +#include "nodes/node.h" -class VolumeEffect : public Effect { +class VolumeEffect : public Node { Q_OBJECT public: - VolumeEffect(Clip* c, const EffectMeta* em); + VolumeEffect(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, @@ -34,6 +42,7 @@ public: int channel_count, int type) override; +private: DoubleInput* volume_val; }; diff --git a/effects/internal/vsthost.cpp b/effects/internal/vsthost.cpp index 954f658c0..c4d9f2aff 100644 --- a/effects/internal/vsthost.cpp +++ b/effects/internal/vsthost.cpp @@ -227,8 +227,8 @@ void VSTHost::send_data_cache_to_plugin() dispatcher(plugin, effSetChunk, 0, int32_t(data_cache.size()), static_cast(data_cache.data()), 0); } -VSTHost::VSTHost(Clip* c, const EffectMeta *em) : - Effect(c, em), +VSTHost::VSTHost(Clip* c) : + Node(c), plugin(nullptr), dialog(nullptr), input_cache(BLOCK_SIZE), @@ -249,6 +249,36 @@ VSTHost::~VSTHost() { freePlugin(); } +QString VSTHost::name() +{ + return tr("VST Plugin 2.x"); +} + +QString VSTHost::id() +{ + return "org.olivevideoeditor.Olive.vst2x"; +} + +QString VSTHost::description() +{ + return tr("Use a VST 2.x plugin on this clip's audio."); +} + +EffectType VSTHost::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType VSTHost::subtype() +{ + return olive::kTypeAudio; +} + +NodePtr VSTHost::Create(Clip *c) +{ + return std::make_shared(c); +} + void VSTHost::process_audio(double timecode_start, double timecode_end, float **samples, @@ -291,7 +321,7 @@ void VSTHost::custom_load(QXmlStreamReader &stream) { } void VSTHost::save(QXmlStreamWriter &stream) { - Effect::save(stream); + Node::save(stream); if (plugin != nullptr) { char* p = nullptr; int32_t length = int32_t(dispatcher(plugin, effGetChunk, 0, 0, &p, 0)); diff --git a/effects/internal/vsthost.h b/effects/internal/vsthost.h index 301d2ef89..39c0be1fd 100644 --- a/effects/internal/vsthost.h +++ b/effects/internal/vsthost.h @@ -24,7 +24,7 @@ #include #include -#include "effects/effect.h" +#include "nodes/node.h" #include "include/vestige.h" // Plugin's dispatcher function @@ -46,11 +46,19 @@ private: void destroy(); }; -class VSTHost : public Effect { +class VSTHost : public Node { Q_OBJECT public: - VSTHost(Clip* c, const EffectMeta* em); - ~VSTHost(); + VSTHost(Clip* c); + virtual ~VSTHost() override; + + virtual QString name() override; + virtual QString id() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; + virtual void process_audio(double timecode_start, double timecode_end, float **samples, diff --git a/effects/transition.cpp b/effects/transition.cpp index 5c1809d97..af25547ad 100644 --- a/effects/transition.cpp +++ b/effects/transition.cpp @@ -40,27 +40,34 @@ #include #include -Transition::Transition(Clip *c, Clip *s, const EffectMeta* em) : - Effect(c, em), - secondary_clip(s) +Transition::Transition(Clip *c) : + Node(c), + secondary_clip(nullptr) { length_field = new DoubleInput(this, "length", tr("Length"), false, false); length_field->SetDefault(30); length_field->SetMinimum(1); length_field->SetDisplayType(LabelSlider::FrameNumber); - length_field->SetFrameRate(parent_clip->track()->sequence() == nullptr ? - parent_clip->cached_frame_rate() : parent_clip->track()->sequence()->frame_rate); + + if (parent_clip != nullptr) { + length_field->SetFrameRate(parent_clip->track()->sequence() == nullptr ? + parent_clip->cached_frame_rate() : parent_clip->track()->sequence()->frame_rate); + } connect(length_field, SIGNAL(Changed()), this, SLOT(UpdateMaximumLength())); } -TransitionPtr Transition::copy(Clip *c, Clip *s) { - return Transition::Create(c, s, meta, get_true_length()); +NodePtr Transition::copy(Clip *c) { + NodePtr node = Node::copy(c); + + static_cast(node.get())->set_length(get_true_length()); + + return node; } void Transition::save(QXmlStreamWriter &stream) { stream.writeAttribute("length", QString::number(get_true_length())); - Effect::save(stream); + Node::save(stream); } void Transition::set_length(int l) { @@ -96,17 +103,18 @@ Clip* Transition::get_closed_clip() { return nullptr; } -TransitionPtr Transition::CreateFromMeta(Clip* c, Clip* s, const EffectMeta* em) { +/* +TransitionPtr Transition::CreateFromMeta(Clip* c, Clip* s) { if (!em->filename.isEmpty()) { // load effect from file return TransitionPtr(new Transition(c, s, em)); } else if (em->internal >= 0 && em->internal < TRANSITION_INTERNAL_COUNT) { // must be an internal effect switch (em->internal) { - case TRANSITION_INTERNAL_CROSSDISSOLVE: return TransitionPtr(new CrossDissolveTransition(c, s, em)); - case TRANSITION_INTERNAL_LINEARFADE: return TransitionPtr(new LinearFadeTransition(c, s, em)); - case TRANSITION_INTERNAL_EXPONENTIALFADE: return TransitionPtr(new ExponentialFadeTransition(c, s, em)); - case TRANSITION_INTERNAL_LOGARITHMICFADE: return TransitionPtr(new LogarithmicFadeTransition(c, s, em)); + case kCrossDissolveTransition: return TransitionPtr(new CrossDissolveTransition(c, s, em)); + case kLinearFadeTransition: return TransitionPtr(new LinearFadeTransition(c, s, em)); + case kExponentialFadeTransition: return TransitionPtr(new ExponentialFadeTransition(c, s, em)); + case kLogarithmicFadeTransition: return TransitionPtr(new LogarithmicFadeTransition(c, s, em)); //case TRANSITION_INTERNAL_CUBE: return TransitionPtr(new CubeTransition(c, s, em)); } } else { @@ -118,6 +126,7 @@ TransitionPtr Transition::CreateFromMeta(Clip* c, Clip* s, const EffectMeta* em) } return nullptr; } +*/ void Transition::UpdateMaximumLength() { @@ -154,14 +163,3 @@ long Transition::GetMaximumEmptySpaceOnClip(Clip *c) return maximum_transition_length; } - -TransitionPtr Transition::Create(Clip* c, Clip* s, const EffectMeta* em, long length) { - TransitionPtr t(CreateFromMeta(c, s, em)); - if (t != nullptr) { - if (length > 0) { - t->set_length(length); - } - return t; - } - return nullptr; -} diff --git a/effects/transition.h b/effects/transition.h index 20adde1ba..7be393c5c 100644 --- a/effects/transition.h +++ b/effects/transition.h @@ -21,7 +21,8 @@ #ifndef TRANSITION_H #define TRANSITION_H -#include "effect.h" +#include "nodes/node.h" +#include "nodes/inputs.h" enum TransitionType { kTransitionNone, @@ -29,23 +30,16 @@ enum TransitionType { kTransitionClosing }; -enum TransitionInternal { - TRANSITION_INTERNAL_CROSSDISSOLVE, - TRANSITION_INTERNAL_LINEARFADE, - TRANSITION_INTERNAL_EXPONENTIALFADE, - TRANSITION_INTERNAL_LOGARITHMICFADE, - //TRANSITION_INTERNAL_CUBE, - TRANSITION_INTERNAL_COUNT -}; - class Transition; using TransitionPtr = std::shared_ptr; -class Transition : public Effect { +class Transition : public Node { Q_OBJECT public: - Transition(Clip* c, Clip* s, const EffectMeta* em); - virtual TransitionPtr copy(Clip* c, Clip* s); + Transition(Clip* c); + + virtual NodePtr copy(Clip* c) override; + Clip* secondary_clip; virtual void save(QXmlStreamWriter& stream) override; @@ -57,8 +51,6 @@ public: Clip* get_opened_clip(); Clip* get_closed_clip(); - static TransitionPtr Create(Clip* c, Clip* s, const EffectMeta* em, long length = 0); - static TransitionPtr CreateFromMeta(Clip *c, Clip *s, const EffectMeta* em); private: DoubleInput* length_field; diff --git a/global/clipboard.cpp b/global/clipboard.cpp index c2157ba22..22d1c4bfc 100644 --- a/global/clipboard.cpp +++ b/global/clipboard.cpp @@ -21,8 +21,9 @@ #include "clipboard.h" #include "timeline/clip.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/transition.h" +#include "undo/undo.h" Clipboard::Clipboard() : type_(CLIPBOARD_TYPE_CLIP) diff --git a/global/clipboard.h b/global/clipboard.h index 9a2366256..be3190c2d 100644 --- a/global/clipboard.h +++ b/global/clipboard.h @@ -24,6 +24,7 @@ #include #include "effects/transition.h" +#include "project/media.h" using VoidPtr = std::shared_ptr; diff --git a/global/global.cpp b/global/global.cpp index 6b3dfc238..a98e82772 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -389,15 +389,15 @@ void OliveGlobal::PasteInternal(Sequence *s, bool insert) Clip* c = selected_clips.at(i); for (int j=0;j(olive::clipboard.Get(j)); - if (c->type() == e->meta->subtype) { + NodePtr e = std::static_pointer_cast(olive::clipboard.Get(j)); + if (c->type() == e->subtype()) { int found = -1; if (ask_conflict) { replace = false; skip = false; } for (int k=0;keffects.size();k++) { - if (c->effects.at(k)->meta == e->meta) { + if (c->effects.at(k)->id() == e->id()) { found = k; break; } @@ -407,7 +407,7 @@ void OliveGlobal::PasteInternal(Sequence *s, bool insert) box.setWindowTitle(tr("Effect already exists")); box.setText(tr("Clip '%1' already contains a '%2' effect. " "Would you like to replace it with the pasted one or add it as a separate effect?") - .arg(c->name(), e->meta->name)); + .arg(c->name(), e->name())); box.setIcon(QMessageBox::Icon::Question); box.addButton(tr("Add"), QMessageBox::YesRole); @@ -432,9 +432,9 @@ void OliveGlobal::PasteInternal(Sequence *s, bool insert) } else if (found >= 0 && replace) { ca->append(new EffectDeleteCommand(c->effects.at(found).get())); - ca->append(new AddEffectCommand(c, e->copy(c), nullptr, found)); + ca->append(new AddEffectCommand(c, e->copy(c), kInvalidNode, found)); } else { - ca->append(new AddEffectCommand(c, e->copy(c), nullptr)); + ca->append(new AddEffectCommand(c, e->copy(c), kInvalidNode)); } } } diff --git a/nodes/inputs/boolinput.cpp b/nodes/inputs/boolinput.cpp index 9ef9a32ae..ff58c005a 100644 --- a/nodes/inputs/boolinput.cpp +++ b/nodes/inputs/boolinput.cpp @@ -1,6 +1,6 @@ #include "boolinput.h" -BoolInput::BoolInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : +BoolInput::BoolInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { BoolField* bool_field = new BoolField(this); diff --git a/nodes/inputs/boolinput.h b/nodes/inputs/boolinput.h index d04c89a6f..e10660fb3 100644 --- a/nodes/inputs/boolinput.h +++ b/nodes/inputs/boolinput.h @@ -7,7 +7,7 @@ class BoolInput : public EffectRow { Q_OBJECT public: - BoolInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + BoolInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Get the boolean value at a given timecode diff --git a/nodes/inputs/colorinput.cpp b/nodes/inputs/colorinput.cpp index c23d3a83d..6c12129d6 100644 --- a/nodes/inputs/colorinput.cpp +++ b/nodes/inputs/colorinput.cpp @@ -1,6 +1,6 @@ #include "colorinput.h" -ColorInput::ColorInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : +ColorInput::ColorInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { AddField(new ColorField(this)); @@ -10,5 +10,5 @@ ColorInput::ColorInput(Effect* parent, const QString& id, const QString& name, b QColor ColorInput::GetColorAt(double timecode) { - static_cast(Field(0))->GetColorAt(timecode); + return static_cast(Field(0))->GetColorAt(timecode); } diff --git a/nodes/inputs/colorinput.h b/nodes/inputs/colorinput.h index d46acfd38..486f18e2e 100644 --- a/nodes/inputs/colorinput.h +++ b/nodes/inputs/colorinput.h @@ -7,7 +7,7 @@ class ColorInput : public EffectRow { Q_OBJECT public: - ColorInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + ColorInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Get the color value at a given timecode diff --git a/nodes/inputs/comboinput.cpp b/nodes/inputs/comboinput.cpp index d34ea7d88..b257ea66e 100644 --- a/nodes/inputs/comboinput.cpp +++ b/nodes/inputs/comboinput.cpp @@ -1,6 +1,6 @@ #include "comboinput.h" -ComboInput::ComboInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : +ComboInput::ComboInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { ComboField* combo_field = new ComboField(this); diff --git a/nodes/inputs/comboinput.h b/nodes/inputs/comboinput.h index d48eae16d..25dbc4e91 100644 --- a/nodes/inputs/comboinput.h +++ b/nodes/inputs/comboinput.h @@ -7,7 +7,7 @@ class ComboInput : public EffectRow { Q_OBJECT public: - ComboInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + ComboInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Add an item to this ComboInput diff --git a/nodes/inputs/fileinput.cpp b/nodes/inputs/fileinput.cpp index 65d872ef7..77d2406bf 100644 --- a/nodes/inputs/fileinput.cpp +++ b/nodes/inputs/fileinput.cpp @@ -1,6 +1,6 @@ #include "fileinput.h" -FileInput::FileInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : +FileInput::FileInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { AddField(new FileField(this)); diff --git a/nodes/inputs/fileinput.h b/nodes/inputs/fileinput.h index 9473a9c13..1793e5b97 100644 --- a/nodes/inputs/fileinput.h +++ b/nodes/inputs/fileinput.h @@ -7,7 +7,7 @@ class FileInput : public EffectRow { Q_OBJECT public: - FileInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + FileInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Get the filename at the given timecode diff --git a/nodes/inputs/fontinput.cpp b/nodes/inputs/fontinput.cpp index 506a402d9..c5a901c7e 100644 --- a/nodes/inputs/fontinput.cpp +++ b/nodes/inputs/fontinput.cpp @@ -1,6 +1,6 @@ #include "fontinput.h" -FontInput::FontInput(Effect* parent, const QString& id, const QString& name, bool savable, bool keyframable) : +FontInput::FontInput(Node* parent, const QString& id, const QString& name, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { AddField(new FontField(this)); diff --git a/nodes/inputs/fontinput.h b/nodes/inputs/fontinput.h index b1ac07979..cfcb34c55 100644 --- a/nodes/inputs/fontinput.h +++ b/nodes/inputs/fontinput.h @@ -7,7 +7,7 @@ class FontInput : public EffectRow { Q_OBJECT public: - FontInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + FontInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); /** * @brief Get the font family name at the given timecode diff --git a/nodes/inputs/stringinput.cpp b/nodes/inputs/stringinput.cpp index ea840992c..0cdfa6922 100644 --- a/nodes/inputs/stringinput.cpp +++ b/nodes/inputs/stringinput.cpp @@ -1,6 +1,6 @@ #include "stringinput.h" -StringInput::StringInput(Effect* parent, const QString& id, const QString& name, bool rich_text, bool savable, bool keyframable) : +StringInput::StringInput(Node* parent, const QString& id, const QString& name, bool rich_text, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable) { AddField(new StringField(this, rich_text)); diff --git a/nodes/inputs/stringinput.h b/nodes/inputs/stringinput.h index 4498bbae9..d738d2fe3 100644 --- a/nodes/inputs/stringinput.h +++ b/nodes/inputs/stringinput.h @@ -7,7 +7,7 @@ class StringInput : public EffectRow { Q_OBJECT public: - StringInput(Effect* parent, + StringInput(Node* parent, const QString& id, const QString& name, bool rich_text = true, diff --git a/nodes/inputs/vecinput.cpp b/nodes/inputs/vecinput.cpp index 7cdf7d01b..2626720f0 100644 --- a/nodes/inputs/vecinput.cpp +++ b/nodes/inputs/vecinput.cpp @@ -4,7 +4,7 @@ #include #include -VecInput::VecInput(Effect* parent, const QString& id, const QString& name, int values, bool savable, bool keyframable) : +VecInput::VecInput(Node* parent, const QString& id, const QString& name, int values, bool savable, bool keyframable) : EffectRow(parent, id, name, savable, keyframable), single_value_mode_(false), values_(values) @@ -79,7 +79,7 @@ void VecInput::SetSingleValueMode(bool on) } } -DoubleInput::DoubleInput(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : +DoubleInput::DoubleInput(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) : VecInput(parent, id, name, 1, savable, keyframable) { } @@ -89,7 +89,7 @@ double DoubleInput::GetDoubleAt(double timecode) return static_cast(Field(0))->GetDoubleAt(timecode); } -Vec2Input::Vec2Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : +Vec2Input::Vec2Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) : VecInput(parent, id, name, 2, savable, keyframable) { } @@ -121,7 +121,7 @@ void Vec2Input::SetValueAt(double timecode, const QVariant &value) static_cast(Field(1))->SetValueAt(timecode, vec2.y()); } -Vec3Input::Vec3Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : +Vec3Input::Vec3Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) : VecInput(parent, id, name, 3, savable, keyframable) { } @@ -156,7 +156,7 @@ void Vec3Input::SetValueAt(double timecode, const QVariant &value) static_cast(Field(2))->SetValueAt(timecode, vec3.z()); } -Vec4Input::Vec4Input(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) : +Vec4Input::Vec4Input(Node *parent, const QString &id, const QString &name, bool savable, bool keyframable) : VecInput(parent, id, name, 4, savable, keyframable) { } diff --git a/nodes/inputs/vecinput.h b/nodes/inputs/vecinput.h index 9698308d1..7fda0aaca 100644 --- a/nodes/inputs/vecinput.h +++ b/nodes/inputs/vecinput.h @@ -9,7 +9,7 @@ class VecInput : public EffectRow { Q_OBJECT public: - VecInput(Effect* parent, const QString& id, const QString& name, int values, bool savable = true, bool keyframable = true); + VecInput(Node* parent, const QString& id, const QString& name, int values, bool savable = true, bool keyframable = true); void SetMinimum(double minimum); void SetMaximum(double maximum); @@ -43,14 +43,14 @@ private: class DoubleInput : public VecInput { public: - DoubleInput(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + DoubleInput(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); double GetDoubleAt(double timecode); }; class Vec2Input : public VecInput { public: - Vec2Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + Vec2Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); QVector2D GetVector2DAt(double timecode); virtual QVariant GetValueAt(double timecode) override; @@ -59,7 +59,7 @@ public: class Vec3Input : public VecInput { public: - Vec3Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + Vec3Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); QVector3D GetVector3DAt(double timecode); virtual QVariant GetValueAt(double timecode) override; @@ -68,7 +68,7 @@ public: class Vec4Input : public VecInput { public: - Vec4Input(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); + Vec4Input(Node* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true); QVector4D GetVector4DAt(double timecode); virtual QVariant GetValueAt(double timecode) override; diff --git a/effects/effect.cpp b/nodes/node.cpp similarity index 77% rename from effects/effect.cpp rename to nodes/node.cpp index db02e443c..efb3146e3 100644 --- a/effects/effect.cpp +++ b/nodes/node.cpp @@ -18,7 +18,7 @@ ***/ -#include "effect.h" +#include "node.h" #include #include @@ -50,61 +50,47 @@ #include "global/math.h" #include "global/clipboard.h" #include "global/config.h" -#include "transition.h" +#include "effects/transition.h" #include "undo/undostack.h" #include "rendering/shadergenerators.h" #include "global/timing.h" #include "nodes/nodes.h" -#include "effects/internal/transformeffect.h" -#include "effects/internal/texteffect.h" -#include "effects/internal/timecodeeffect.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 "effects/internal/shakeeffect.h" -#include "effects/internal/cornerpineffect.h" -#include "effects/internal/vsthost.h" -#include "effects/internal/fillleftrighteffect.h" -#include "effects/internal/richtexteffect.h" +QVector olive::node_library; -QVector olive::effects; -QVector olive::blend_modes; -QString olive::generated_blending_shader; - -EffectPtr Effect::Create(Clip* c, const EffectMeta* em) { - if (em->internal >= 0 && em->internal < EFFECT_INTERNAL_COUNT) { - // must be an internal effect - switch (em->internal) { - case EFFECT_INTERNAL_TRANSFORM: return std::make_shared(c, em); - case EFFECT_INTERNAL_TEXT: return std::make_shared(c, em); - case EFFECT_INTERNAL_TIMECODE: return std::make_shared(c, em); - case EFFECT_INTERNAL_SOLID: return std::make_shared(c, em); - case EFFECT_INTERNAL_NOISE: return std::make_shared(c, em); - case EFFECT_INTERNAL_VOLUME: return std::make_shared(c, em); - case EFFECT_INTERNAL_PAN: return std::make_shared(c, em); - case EFFECT_INTERNAL_TONE: return std::make_shared(c, em); - case EFFECT_INTERNAL_SHAKE: return std::make_shared(c, em); - case EFFECT_INTERNAL_CORNERPIN: return std::make_shared(c, em); - case EFFECT_INTERNAL_FILLLEFTRIGHT: return std::make_shared(c, em); - case EFFECT_INTERNAL_VST: return std::make_shared(c, em); - case EFFECT_INTERNAL_RICHTEXT: return std::make_shared(c, em); - } - } else if (!em->filename.isEmpty()) { - // load effect from file - return std::make_shared(c, em); - } else { +/* +NodePtr Node::Create(Clip* c) { + // must be an internal effect + switch (em->internal) { + case kTransformEffect: return std::make_shared(c, em); + case kTextInput: return std::make_shared(c, em); + case kTimecodeEffect: return std::make_shared(c, em); + case kSolidInput: return std::make_shared(c, em); + case kNoiseInput: return std::make_shared(c, em); + case kVolumeEffect: return std::make_shared(c, em); + case kPanEffect: return std::make_shared(c, em); + case kToneInput: return std::make_shared(c, em); + case kShakeEffect: return std::make_shared(c, em); + case kCornerPinEffect: return std::make_shared(c, em); + case kFillLeftRightEffect: return std::make_shared(c, em); + case kVstEffect: return std::make_shared(c, em); + case kRichTextInput: return std::make_shared(c, em); + case kMediaInput: return std::make_shared(c, em); + case kShaderEffect: return std::make_shared(c, em); + case kImageOutput: return std::make_shared(c, em); + default: qCritical() << "Invalid effect data"; QMessageBox::critical(olive::MainWindow, QCoreApplication::translate("Effect", "Invalid effect"), - QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be corrupt. Try reinstalling it or Olive.").arg(em->name)); + QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be " + "corrupt. Try reinstalling it or Olive.").arg(em->name)); + return nullptr; } - return nullptr; } +*/ -const EffectMeta* Effect::GetInternalMeta(int internal_id, int type) { +/* +const EffectMeta* Node::GetInternalMeta(int internal_id, int type) { for (int i=0;iname; - } +{ } -Effect::~Effect() { +Node::~Node() { if (isOpen) { close(); } @@ -150,13 +132,23 @@ Effect::~Effect() { } } -void Effect::AddRow(EffectRow *row) +QString Node::category() +{ + return QString(); +} + +bool Node::IsCreatable() +{ + return true; +} + +void Node::AddRow(EffectRow *row) { row->setParent(this); rows.append(row); } -void Effect::copy_field_keyframes(EffectPtr e) { +void Node::copy_field_keyframes(NodePtr e) { for (int i=0;irows.at(i); @@ -177,40 +169,43 @@ void Effect::copy_field_keyframes(EffectPtr e) { } } -EffectRow* Effect::row(int i) { +EffectRow* Node::row(int i) { return rows.at(i); } -int Effect::row_count() { +int Node::row_count() { return rows.size(); } -EffectGizmo *Effect::add_gizmo(int type) { +EffectGizmo *Node::add_gizmo(int type) { EffectGizmo* gizmo = new EffectGizmo(this, type); gizmos.append(gizmo); return gizmo; } -EffectGizmo *Effect::gizmo(int i) { +EffectGizmo *Node::gizmo(int i) { return gizmos.at(i); } -int Effect::gizmo_count() { +int Node::gizmo_count() { return gizmos.size(); } -void Effect::refresh() {} +void Node::refresh() {} -void Effect::FieldChanged() { - update_ui(false); +void Node::FieldChanged() { + // Update the UI if a field has been modified, but don't both if this effect is inactive + if (parent_clip != nullptr) { + update_ui(false); + } } -void Effect::delete_self() { +void Node::delete_self() { olive::undo_stack.push(new EffectDeleteCommand(this)); update_ui(true); } -void Effect::move_up() { +void Node::move_up() { int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == 0) { return; @@ -225,7 +220,7 @@ void Effect::move_up() { panel_sequence_viewer->viewer_widget()->frame_update(); } -void Effect::move_down() { +void Node::move_down() { int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == parent_clip->effects.size()-1) { return; @@ -240,7 +235,7 @@ void Effect::move_down() { panel_sequence_viewer->viewer_widget()->frame_update(); } -void Effect::save_to_file() { +void Node::save_to_file() { // save effect settings to file QString file = QFileDialog::getSaveFileName(olive::MainWindow, tr("Save Effect Settings"), @@ -270,7 +265,7 @@ void Effect::save_to_file() { } } -void Effect::load_from_file() { +void Node::load_from_file() { // load effect settings from file QString file = QFileDialog::getOpenFileName(olive::MainWindow, tr("Load Effect Settings"), @@ -296,31 +291,31 @@ void Effect::load_from_file() { } } -bool Effect::AlwaysUpdate() +bool Node::AlwaysUpdate() { return false; } -bool Effect::IsEnabled() { +bool Node::IsEnabled() { return enabled_; } -bool Effect::IsExpanded() +bool Node::IsExpanded() { return expanded_; } -void Effect::SetExpanded(bool e) +void Node::SetExpanded(bool e) { expanded_ = e; } -void Effect::SetEnabled(bool b) { +void Node::SetEnabled(bool b) { enabled_ = b; emit EnabledChanged(b); } -void Effect::load(QXmlStreamReader& stream) { +void Node::load(QXmlStreamReader& stream) { /* int row_count = 0; @@ -412,9 +407,9 @@ void Effect::load(QXmlStreamReader& stream) { */ } -void Effect::custom_load(QXmlStreamReader &) {} +void Node::custom_load(QXmlStreamReader &) {} -void Effect::save(QXmlStreamWriter& stream) { +void Node::save(QXmlStreamWriter& stream) { /* stream.writeAttribute("name", meta->category + "/" + meta->name); stream.writeAttribute("enabled", QString::number(IsEnabled())); @@ -451,7 +446,7 @@ void Effect::save(QXmlStreamWriter& stream) { */ } -void Effect::load_from_string(const QByteArray &s) { +void Node::load_from_string(const QByteArray &s) { // clear existing keyframe data for (int i=0;ipath.isEmpty() || (shader_vert_path_.isEmpty() && shader_frag_path_.isEmpty())) return; QList effects_paths = get_effects_paths(); const QString& test_fn = shader_vert_path_.isEmpty() ? shader_frag_path_ : shader_vert_path_; @@ -534,9 +532,11 @@ void Effect::validate_meta_path() { return; } } + */ } -void Effect::open() { +void Node::open() { + /* if (isOpen) { qWarning() << "Tried to open an effect that was already open"; close(); @@ -548,7 +548,7 @@ void Effect::open() { validate_meta_path(); QString frag_shader_str; - QString frag_file_url = QDir(meta->path).filePath(shader_frag_path_); + QString frag_file_url = QDir(file).filePath(shader_frag_path_); QFile frag_file(frag_file_url); if (frag_file.open(QFile::ReadOnly)) { frag_shader_str = frag_file.readAll(); @@ -570,41 +570,16 @@ void Effect::open() { shader_program_ = olive::shader::GetPipeline(shader_func, frag_shader_str); } - - /* - bool shader_compiled = true; - if (!shader_vert_path_.isEmpty()) { - if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Vertex, meta->path + "/" + shader_vert_path_)) { - qInfo() << "Vertex shader added successfully"; - } else { - shader_compiled = false; - qWarning() << "Vertex shader could not be added"; - } - } - if (!shader_frag_path_.isEmpty()) { - if (shader_program_->addShaderFromSourceFile(QOpenGLShader::Fragment, meta->path + "/" + shader_frag_path_)) { - qInfo() << "Fragment shader added successfully"; - } else { - shader_compiled = false; - qWarning() << "Fragment shader could not be added"; - } - } - if (shader_compiled) { - if (shader_program_->link()) { - qInfo() << "Shader program linked successfully"; - } else { - qWarning() << "Shader program failed to link"; - } - } - */ isOpen = true; } } else { isOpen = true; } + */ + isOpen = true; } -void Effect::close() { +void Node::close() { if (!isOpen) { qWarning() << "Tried to close an effect that was already closed"; } @@ -613,43 +588,43 @@ void Effect::close() { isOpen = false; } -bool Effect::is_shader_linked() { +bool Node::is_shader_linked() { return shader_program_ != nullptr && shader_program_->isLinked(); } -QOpenGLShaderProgram *Effect::GetShaderPipeline() +QOpenGLShaderProgram *Node::GetShaderPipeline() { return shader_program_.get(); } -int Effect::Flags() +int Node::Flags() { return flags_; } -void Effect::SetFlags(int flags) +void Node::SetFlags(int flags) { flags_ = flags; } -int Effect::getIterations() { +int Node::getIterations() { return iterations; } -void Effect::setIterations(int i) { +void Node::setIterations(int i) { iterations = i; } -void Effect::process_image(double, uint8_t *, uint8_t *, int){} +void Node::process_image(double, uint8_t *, uint8_t *, int){} -EffectPtr Effect::copy(Clip *c) { - EffectPtr copy = Effect::Create(c, meta); +NodePtr Node::copy(Clip *c) { + NodePtr copy = Create(c); copy->SetEnabled(IsEnabled()); copy_field_keyframes(copy); return copy; } -void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { +void Node::process_shader(double timecode, GLTextureCoords&, int iteration) { /* shader_program_->bind(); @@ -704,9 +679,9 @@ void Effect::process_shader(double timecode, GLTextureCoords&, int iteration) { */ } -void Effect::process_coords(double, GLTextureCoords&, int) {} +void Node::process_coords(double, GLTextureCoords&, int) {} -GLuint Effect::process_superimpose(QOpenGLContext* ctx, double timecode) { +GLuint Node::process_superimpose(QOpenGLContext* ctx, double timecode) { bool dimensions_changed = false; bool redrew_image = false; @@ -760,11 +735,11 @@ GLuint Effect::process_superimpose(QOpenGLContext* ctx, double timecode) { return texture; } -void Effect::process_audio(double, double, float **, int, int, int) {} +void Node::process_audio(double, double, float **, int, int, int) {} -void Effect::gizmo_draw(double, GLTextureCoords &) {} +void Node::gizmo_draw(double, GLTextureCoords &) {} -void Effect::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) { +void Node::gizmo_move(EffectGizmo* gizmo, int x_movement, int y_movement, double timecode, bool done) { // Loop through each gizmo to find `gizmo` for (int i=0;i 0); } -double Effect::Now() +double Node::Now() { return playhead_to_clip_seconds(parent_clip, parent_clip->track()->sequence()->playhead); } -long Effect::NowInFrames() +long Node::NowInFrames() { return playhead_to_clip_frame(parent_clip, parent_clip->track()->sequence()->playhead); } -void Effect::redraw(double) { +void Node::redraw(double) { /* // run javascript QPainter p(&img); @@ -912,7 +887,7 @@ void Effect::redraw(double) { */ } -bool Effect::valueHasChanged(double timecode) { +bool Node::valueHasChanged(double timecode) { if (cachedValues.isEmpty()) { for (int i=0;ifunctions()->glDeleteTextures(1, &texture); texture = 0; @@ -951,7 +926,18 @@ void Effect::delete_texture() { } } -const EffectMeta* Effect::GetMetaFromName(const QString& input) { +int GetNodeLibraryIndexFromId(const QString& id) { + for (int i=0;iid() == id) { + return i; + } + } + + return -1; +} + +/* +const EffectMeta* Node::GetMetaFromName(const QString& input) { int split_index = input.indexOf('/'); QString category; if (split_index > -1) { @@ -968,3 +954,4 @@ const EffectMeta* Effect::GetMetaFromName(const QString& input) { } return nullptr; } +*/ diff --git a/effects/effect.h b/nodes/node.h similarity index 76% rename from effects/effect.h rename to nodes/node.h index ec1ebca23..c67a317af 100644 --- a/effects/effect.h +++ b/nodes/node.h @@ -40,44 +40,45 @@ #include #include -#include "ui/collapsiblewidget.h" -#include "effectrow.h" -#include "effectgizmo.h" +#include "timeline/tracktypes.h" #include "rendering/qopenglshaderprogramptr.h" -#include "nodes/inputs.h" +#include "inputs.h" +#include "effects/effectgizmo.h" + +class EffectGizmo; +class KeyframeDataChange; class Clip; +using ClipPtr = std::shared_ptr; -class Effect; -using EffectPtr = std::shared_ptr; +class Node; +using NodePtr = std::shared_ptr; -struct EffectMeta { - QString name; - QString category; - QString filename; - QString path; - QString tooltip; - int internal; - int type; - int subtype; +enum NodeType { + kTransformEffect, + kTextInput, + kSolidInput, + kNoiseInput, + kVolumeEffect, + kPanEffect, + kToneInput, + kShakeEffect, + kTimecodeEffect, + kMaskEffect, + kFillLeftRightEffect, + kVstEffect, + kCornerPinEffect, + kRichTextInput, + kMediaInput, + kShaderEffect, + kImageOutput, + kCrossDissolveTransition, + kLinearFadeTransition, + kExponentialFadeTransition, + kLogarithmicFadeTransition, + kInvalidNode }; -struct BlendMode { - QString name; - QString url; - QString function_name; - - bool loaded; -}; - -namespace olive { - extern QVector effects; - extern QVector blend_modes; - - // TODO weird place to put this? - extern QString generated_blending_shader; -} - double log_volume(double linear); enum EffectType { @@ -92,24 +93,6 @@ enum EffectKeyframeType { EFFECT_KEYFRAME_HOLD }; -enum EffectInternal { - EFFECT_INTERNAL_TRANSFORM, - EFFECT_INTERNAL_TEXT, - EFFECT_INTERNAL_SOLID, - EFFECT_INTERNAL_NOISE, - EFFECT_INTERNAL_VOLUME, - EFFECT_INTERNAL_PAN, - EFFECT_INTERNAL_TONE, - EFFECT_INTERNAL_SHAKE, - EFFECT_INTERNAL_TIMECODE, - EFFECT_INTERNAL_MASK, - EFFECT_INTERNAL_FILLLEFTRIGHT, - EFFECT_INTERNAL_VST, - EFFECT_INTERNAL_CORNERPIN, - EFFECT_INTERNAL_RICHTEXT, - EFFECT_INTERNAL_COUNT -}; - struct GLTextureCoords { QMatrix4x4 matrix; @@ -123,20 +106,25 @@ struct GLTextureCoords { QVector2D texture_bottom_left; QVector2D texture_bottom_right; - int blendmode; float opacity; }; -class Effect : public QObject { +class Node : public QObject { Q_OBJECT public: - Effect(Clip *c, const EffectMeta* em); - ~Effect(); + Node(Clip *c); + ~Node(); Clip* parent_clip; - const EffectMeta* meta; - int id; - QString name; + + virtual QString name() = 0; + virtual QString id() = 0; + virtual QString category(); + virtual QString description() = 0; + virtual EffectType type() = 0; + virtual olive::TrackType subtype() = 0; + virtual bool IsCreatable(); + virtual NodePtr Create(Clip *c) = 0; void AddRow(EffectRow* row); @@ -152,8 +140,8 @@ public: virtual void refresh(); - virtual EffectPtr copy(Clip* c); - void copy_field_keyframes(EffectPtr e); + virtual NodePtr copy(Clip* c); + void copy_field_keyframes(NodePtr e); virtual void load(QXmlStreamReader& stream); virtual void custom_load(QXmlStreamReader& stream); @@ -233,9 +221,6 @@ public: return distribution(generator); } - static EffectPtr Create(Clip *c, const EffectMeta *em); - static const EffectMeta* GetInternalMeta(int internal_id, int type); - static const EffectMeta* GetMetaFromName(const QString& input); public slots: void FieldChanged(); void SetEnabled(bool b); @@ -287,4 +272,8 @@ private: void validate_meta_path(); }; +namespace olive { + extern QVector node_library; +} + #endif // EFFECT_H diff --git a/nodes/nodegraph.cpp b/nodes/nodegraph.cpp index 381bd53cd..c2d912821 100644 --- a/nodes/nodegraph.cpp +++ b/nodes/nodegraph.cpp @@ -8,7 +8,7 @@ NodeGraph::NodeGraph() : } -Effect *NodeGraph::OutputNode() +Node *NodeGraph::OutputNode() { return output_node_.get(); } diff --git a/nodes/nodegraph.h b/nodes/nodegraph.h index a450abf16..4ce08b030 100644 --- a/nodes/nodegraph.h +++ b/nodes/nodegraph.h @@ -1,7 +1,7 @@ #ifndef NODEGRAPH_H #define NODEGRAPH_H -#include "effects/effect.h" +#include "nodes/node.h" class NodeGraph { @@ -15,7 +15,7 @@ public: * * @param node */ - void AddNode(EffectPtr node); + void AddNode(NodePtr node); /** * @brief Process the graph @@ -39,15 +39,15 @@ public: * * The node to set as the output node. The graph takes ownership of the node and the user cannot delete it. */ - void SetOutputNode(EffectPtr node); + void SetOutputNode(NodePtr node); /** * @brief Returns the currently set output node */ - Effect* OutputNode(); + Node* OutputNode(); private: - EffectPtr output_node_; + NodePtr output_node_; }; #endif // NODEGRAPH_H diff --git a/nodes/nodes/nodeimageoutput.cpp b/nodes/nodes/nodeimageoutput.cpp index b8b23c2fa..a6b2502c9 100644 --- a/nodes/nodes/nodeimageoutput.cpp +++ b/nodes/nodes/nodeimageoutput.cpp @@ -1,6 +1,42 @@ #include "nodeimageoutput.h" -NodeImageOutput::NodeImageOutput() +NodeImageOutput::NodeImageOutput(Clip *c) : + Node(c) { } + +QString NodeImageOutput::name() +{ + return tr("Image Output"); +} + +QString NodeImageOutput::id() +{ + return "org.olivevideoeditor.Olive.imageoutput"; +} + +QString NodeImageOutput::category() +{ + return tr("Outputs"); +} + +QString NodeImageOutput::description() +{ + return tr("Used for outputting images outside of the node graph."); +} + +EffectType NodeImageOutput::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType NodeImageOutput::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr NodeImageOutput::Create(Clip *c) +{ + return std::make_shared(c); +} diff --git a/nodes/nodes/nodeimageoutput.h b/nodes/nodes/nodeimageoutput.h index 69fc8ddea..c4ebd2798 100644 --- a/nodes/nodes/nodeimageoutput.h +++ b/nodes/nodes/nodeimageoutput.h @@ -1,11 +1,20 @@ #ifndef NODEIMAGEOUTPUT_H #define NODEIMAGEOUTPUT_H +#include "nodes/node.h" -class NodeImageOutput +class NodeImageOutput : public Node { public: - NodeImageOutput(); + NodeImageOutput(Clip* c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; }; -#endif // NODEIMAGEOUTPUT_H \ No newline at end of file +#endif // NODEIMAGEOUTPUT_H diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index 7e62aaaae..0035e900c 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -1,6 +1,41 @@ #include "nodemedia.h" -NodeMedia::NodeMedia() +NodeMedia::NodeMedia(Clip* c) : + Node(c) { +} +QString NodeMedia::name() +{ + return tr("Media"); +} + +QString NodeMedia::id() +{ + return "org.olivevideoeditor.Olive.media"; +} + +QString NodeMedia::category() +{ + return tr("Inputs"); +} + +QString NodeMedia::description() +{ + return tr("Retrieve frames from a media source."); +} + +EffectType NodeMedia::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType NodeMedia::subtype() +{ + return olive::kTypeVideo; +} + +NodePtr NodeMedia::Create(Clip *c) +{ + return std::make_shared(c); } diff --git a/nodes/nodes/nodemedia.h b/nodes/nodes/nodemedia.h index 9e7d87cf3..4d97716ca 100644 --- a/nodes/nodes/nodemedia.h +++ b/nodes/nodes/nodemedia.h @@ -1,10 +1,20 @@ #ifndef MEDIANODE_H #define MEDIANODE_H -class NodeMedia +#include "nodes/node.h" + +class NodeMedia : public Node { public: - NodeMedia(); + NodeMedia(Clip *c); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual NodePtr Create(Clip *c) override; }; #endif // MEDIANODE_H diff --git a/nodes/nodes/nodeshader.cpp b/nodes/nodes/nodeshader.cpp index 64bc64155..c71a3e1a1 100644 --- a/nodes/nodes/nodeshader.cpp +++ b/nodes/nodes/nodeshader.cpp @@ -1,10 +1,18 @@ #include "nodeshader.h" -NodeShader::NodeShader(Clip* c, const EffectMeta *em) : - Effect(c, em) +NodeShader::NodeShader(Clip* c, + const QString &name, + const QString &id, + const QString &category, + const QString &filename) : + Node(c), + name_(name), + id_(id), + category_(category), + filename_(filename) { - if (em != nullptr && !em->filename.isEmpty() && em->internal == -1) { - QFile effect_file(em->filename); + if (!filename_.isEmpty()) { + QFile effect_file(filename_); if (effect_file.open(QFile::ReadOnly)) { QXmlStreamReader reader(&effect_file); @@ -29,7 +37,7 @@ NodeShader::NodeShader(Clip* c, const EffectMeta *em) : } if (id.isEmpty() || name.isEmpty() || type == olive::nodes::kInvalid) { - qCritical() << "Couldn't load field from" << em->filename << "- ID, type, and name cannot be empty."; + qCritical() << "Couldn't load field from" << filename_ << "- ID, type, and name cannot be empty."; } else { EffectRow* field = nullptr; @@ -158,29 +166,56 @@ NodeShader::NodeShader(Clip* c, const EffectMeta *em) : shader_function_name_ = attr.value().toString(); } } - }/* else if (reader.name() == "superimpose" && reader.isStartElement()) { - enable_superimpose = true; - const QXmlStreamAttributes& attributes = reader.attributes(); - for (int i=0;ifilename; - enable_superimpose = false; - } - break; - } - } - }*/ + } else if (reader.name() == "description" && reader.isStartElement()) { + reader.readNext(); + description_ = reader.text().toString(); + } reader.readNext(); } effect_file.close(); } else { - qCritical() << "Failed to open effect file" << em->filename; + qCritical() << "Failed to open effect file" << filename; } } } + +QString NodeShader::name() +{ + return name_; +} + +QString NodeShader::id() +{ + return id_; +} + +QString NodeShader::category() +{ + return category_; +} + +QString NodeShader::description() +{ + return description_; +} + +EffectType NodeShader::type() +{ + return EFFECT_TYPE_EFFECT; +} + +olive::TrackType NodeShader::subtype() +{ + return olive::kTypeVideo; +} + +bool NodeShader::IsCreatable() +{ + return !filename_.isEmpty(); +} + +NodePtr NodeShader::Create(Clip *) +{ + Q_ASSERT(false); +} diff --git a/nodes/nodes/nodeshader.h b/nodes/nodes/nodeshader.h index 298e495cc..a9f401bbd 100644 --- a/nodes/nodes/nodeshader.h +++ b/nodes/nodes/nodeshader.h @@ -1,12 +1,32 @@ #ifndef NODESHADER_H #define NODESHADER_H -#include "effects/effect.h" +#include "nodes/node.h" -class NodeShader : public Effect { +class NodeShader : public Node { Q_OBJECT public: - NodeShader(Clip *c, const EffectMeta *em); + NodeShader(Clip *c, + const QString& name, + const QString& id, + const QString& category, + const QString& filename); + + virtual QString name() override; + virtual QString id() override; + virtual QString category() override; + virtual QString description() override; + virtual EffectType type() override; + virtual olive::TrackType subtype() override; + virtual bool IsCreatable() override; + virtual NodePtr Create(Clip *c) override; + +private: + QString name_; + QString id_; + QString category_; + QString description_; + QString filename_; }; #endif // NODESHADER_H diff --git a/nodes/widgets/buttonwidget.cpp b/nodes/widgets/buttonwidget.cpp index bde083736..c4e8f3690 100644 --- a/nodes/widgets/buttonwidget.cpp +++ b/nodes/widgets/buttonwidget.cpp @@ -1,6 +1,6 @@ #include "buttonwidget.h" -ButtonWidget::ButtonWidget(Effect* parent, const QString& name, const QString& text) : +ButtonWidget::ButtonWidget(Node* parent, const QString& name, const QString& text) : EffectRow(parent, nullptr, name, false, false) { ButtonField* button_field = new ButtonField(this, text); diff --git a/nodes/widgets/buttonwidget.h b/nodes/widgets/buttonwidget.h index e7e068f24..cd35ba7a9 100644 --- a/nodes/widgets/buttonwidget.h +++ b/nodes/widgets/buttonwidget.h @@ -6,7 +6,7 @@ class ButtonWidget : public EffectRow { public: - ButtonWidget(Effect* parent, const QString& name, const QString& text); + ButtonWidget(Node* parent, const QString& name, const QString& text); /** * @brief Wrapper for ButtonField::SetCheckable. diff --git a/nodes/widgets/labelwidget.cpp b/nodes/widgets/labelwidget.cpp index 02396436c..6d22601c8 100644 --- a/nodes/widgets/labelwidget.cpp +++ b/nodes/widgets/labelwidget.cpp @@ -1,6 +1,6 @@ #include "labelwidget.h" -LabelWidget::LabelWidget(Effect *parent, const QString &name, const QString &text) : +LabelWidget::LabelWidget(Node *parent, const QString &name, const QString &text) : EffectRow(parent, nullptr, name, false, false) { AddField(new LabelField(this, text)); diff --git a/nodes/widgets/labelwidget.h b/nodes/widgets/labelwidget.h index af066f9a4..97e86d158 100644 --- a/nodes/widgets/labelwidget.h +++ b/nodes/widgets/labelwidget.h @@ -6,7 +6,7 @@ class LabelWidget : public EffectRow { public: - LabelWidget(Effect* parent, const QString& name, const QString& text); + LabelWidget(Node* parent, const QString& name, const QString& text); }; #endif // LABELWIDGET_H diff --git a/olive.pro b/olive.pro index f737408d5..9d4b6542d 100644 --- a/olive.pro +++ b/olive.pro @@ -106,7 +106,6 @@ SOURCES += \ effects/internal/logarithmicfadetransition.cpp \ effects/internal/cornerpineffect.cpp \ global/math.cpp \ - effects/effect.cpp \ effects/effectrow.cpp \ effects/effectgizmo.cpp \ ui/resizablescrollbar.cpp \ @@ -207,7 +206,8 @@ SOURCES += \ nodes/nodes/nodemedia.cpp \ nodes/nodes/nodeshader.cpp \ decoders/ffmpegdecoder.cpp \ - decoders/decoder.cpp + decoders/decoder.cpp \ + nodes/node.cpp HEADERS += \ ui/mainwindow.h \ @@ -265,7 +265,6 @@ HEADERS += \ effects/internal/logarithmicfadetransition.h \ effects/internal/cornerpineffect.h \ global/math.h \ - effects/effect.h \ effects/effectrow.h \ effects/internal/cubetransition.h \ effects/effectgizmo.h \ @@ -371,7 +370,9 @@ HEADERS += \ nodes/nodes/nodeshader.h \ nodes/nodes.h \ decoders/ffmpegdecoder.h \ - decoders/decoder.h + decoders/decoder.h \ + nodes/node.h \ + timeline/tracktypes.h FORMS += diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 1c92954f8..bc81c16a2 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -32,7 +32,7 @@ #include #include "panels/panels.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/effectloaders.h" #include "effects/transition.h" #include "timeline/clip.h" @@ -95,24 +95,24 @@ void EffectControls::menu_select(QAction* q) { for (int i=0;itype() == effect_menu_subtype) { - const EffectMeta* meta = reinterpret_cast(q->data().value()); + NodeType node_type = static_cast(q->data().toInt()); if (effect_menu_type == EFFECT_TYPE_TRANSITION) { if (c->opening_transition == nullptr) { ca->append(new AddTransitionCommand(c, nullptr, nullptr, - meta, + node_type, olive::config.default_transition_length)); } if (c->closing_transition == nullptr) { ca->append(new AddTransitionCommand(nullptr, c, nullptr, - meta, + node_type, olive::config.default_transition_length)); } } else { - ca->append(new AddEffectCommand(c, nullptr, meta)); + ca->append(new AddEffectCommand(c, nullptr, node_type)); } } } @@ -138,7 +138,7 @@ void EffectControls::scroll_to_frame(long frame) { scroll_to_frame_internal(horizontalScrollBar, frame - keyframeView->visible_in, zoom, keyframeView->width()); } -void EffectControls::show_effect_menu(int type, Track::Type subtype) { +void EffectControls::show_effect_menu(EffectType type, olive::TrackType subtype) { effect_menu_type = type; effect_menu_subtype = subtype; @@ -147,24 +147,25 @@ void EffectControls::show_effect_menu(int type, Track::Type subtype) { Menu effects_menu(this); effects_menu.setToolTipsVisible(true); - for (int i=0;itype() == type && node->subtype() == subtype) { QAction* action = new QAction(&effects_menu); - action->setText(em.name); - action->setData(reinterpret_cast(&em)); - if (!em.tooltip.isEmpty()) { - action->setToolTip(em.tooltip); + action->setText(node->name()); + action->setData(i); + if (!node->description().isEmpty()) { + action->setToolTip(node->description()); } QMenu* parent = &effects_menu; - if (!em.category.isEmpty()) { + if (!node->category().isEmpty()) { bool found = false; for (int j=0;jmenu() != nullptr) { - if (action->menu()->title() == em.category) { + if (action->menu()->title() == node->category()) { parent = action->menu(); found = true; break; @@ -174,12 +175,12 @@ void EffectControls::show_effect_menu(int type, Track::Type subtype) { if (!found) { parent = new Menu(&effects_menu); parent->setToolTipsVisible(true); - parent->setTitle(em.category); + parent->setTitle(node->category()); bool found = false; for (int i=0;itext() > em.category) { + if (comp_action->text() > node->category()) { effects_menu.insertMenu(comp_action, parent); found = true; break; @@ -444,19 +445,19 @@ bool EffectControls::focused() } void EffectControls::video_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, Track::kTypeVideo); + show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeVideo); } void EffectControls::audio_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, Track::kTypeAudio); + show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeAudio); } void EffectControls::video_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, Track::kTypeVideo); + show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeVideo); } void EffectControls::audio_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, Track::kTypeAudio); + show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeAudio); } void EffectControls::resizeEvent(QResizeEvent*) { @@ -491,12 +492,12 @@ void EffectControls::LoadEvent() QVBoxLayout* layout = nullptr; EffectUI* container = open_effects_.at(i); - Effect* e = open_effects_.at(i)->GetEffect(); + Node* e = open_effects_.at(i)->GetEffect(); - if (e->meta->subtype == Track::kTypeVideo) { + if (e->subtype() == olive::kTypeVideo) { vcontainer->setVisible(true); layout = video_effect_layout; - } else if (e->meta->subtype == Track::kTypeAudio) { + } else if (e->subtype() == olive::kTypeAudio) { acontainer->setVisible(true); layout = audio_effect_layout; } diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index fabe93116..1387744f4 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -94,14 +94,14 @@ protected: virtual void ClearEvent() override; virtual void LoadEvent() override; private: - void show_effect_menu(int type, Track::Type subtype); + void show_effect_menu(EffectType type, olive::TrackType subtype); void load_keyframes(); void UpdateTitle(); void setup_ui(); int effect_menu_type; - Track::Type effect_menu_subtype; + olive::TrackType effect_menu_subtype; QString panel_name; QWidget* video_effect_area; diff --git a/panels/effectspanel.cpp b/panels/effectspanel.cpp index cd99fc6d9..3c76db66b 100644 --- a/panels/effectspanel.cpp +++ b/panels/effectspanel.cpp @@ -76,7 +76,7 @@ void EffectsPanel::Load() { Clip* c = selected_clips_.at(i); // Create a list of the effects we'll open - QVector effects_to_open; + QVector effects_to_open; // Determine based on the current selections whether to load all effects or just the transitions bool whole_clip_is_selected = c->IsSelected(); @@ -100,7 +100,7 @@ void EffectsPanel::Load() { // Check if we've already opened an effect of this type before bool already_opened = false; for (int k=0;kGetEffect()->meta == effects_to_open.at(j)->meta + if (open_effects_.at(k)->GetEffect()->id() == effects_to_open.at(j)->id() && !open_effects_.at(k)->IsAttachedToClip(c)) { open_effects_.at(k)->AddAdditionalEffect(effects_to_open.at(j)); @@ -137,7 +137,7 @@ void EffectsPanel::Load() { LoadEvent(); } -bool EffectsPanel::IsEffectSelected(Effect *e) +bool EffectsPanel::IsEffectSelected(Node *e) { for (int i=0;iGetEffect() == e && open_effects_.at(i)->IsSelected()) { @@ -174,9 +174,9 @@ void EffectsPanel::copy(bool del) { for (int i=0;iIsSelected()) { - Effect* e = open_effects_.at(i)->GetEffect(); + Node* e = open_effects_.at(i)->GetEffect(); - if (e->meta->type == EFFECT_TYPE_EFFECT) { + if (e->type() == EFFECT_TYPE_EFFECT) { if (!cleared) { olive::clipboard.Clear(); @@ -205,12 +205,12 @@ void EffectsPanel::copy(bool del) { } } -void EffectsPanel::DeleteEffect(ComboAction* ca, Effect* effect_ref) { - if (effect_ref->meta->type == EFFECT_TYPE_EFFECT) { +void EffectsPanel::DeleteEffect(ComboAction* ca, Node* effect_ref) { + if (effect_ref->type() == EFFECT_TYPE_EFFECT) { ca->append(new EffectDeleteCommand(effect_ref)); - } else if (effect_ref->meta->type == EFFECT_TYPE_TRANSITION) { + } else if (effect_ref->type() == EFFECT_TYPE_TRANSITION) { // Retrieve shared ptr for this transition @@ -258,7 +258,7 @@ void EffectsPanel::DeleteSelectedEffects() { } } -void EffectsPanel::open_effect(Effect* e) { +void EffectsPanel::open_effect(Node* e) { EffectUI* container = new EffectUI(e); connect(container, SIGNAL(CutRequested()), this, SLOT(cut())); diff --git a/panels/effectspanel.h b/panels/effectspanel.h index cb1617b7a..e3d52bd5e 100644 --- a/panels/effectspanel.h +++ b/panels/effectspanel.h @@ -18,7 +18,7 @@ public: virtual bool focused() override; - bool IsEffectSelected(Effect* e); + bool IsEffectSelected(Node* e); void DeleteSelectedEffects(); public slots: @@ -32,8 +32,8 @@ protected: QVector open_effects_; private: void Load(); - void open_effect(Effect *e); - void DeleteEffect(ComboAction* ca, Effect* effect_ref); + void open_effect(Node *e); + void DeleteEffect(ComboAction* ca, Node* effect_ref); private slots: void deselect_all_effects(QWidget*); }; diff --git a/panels/grapheditor.cpp b/panels/grapheditor.cpp index 370a0cb09..9622a5831 100644 --- a/panels/grapheditor.cpp +++ b/panels/grapheditor.cpp @@ -30,7 +30,7 @@ #include "timeline/timelinetools.h" #include "ui/labelslider.h" #include "ui/graphview.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/effectfields.h" #include "effects/effectrow.h" #include "timeline/clip.h" @@ -210,7 +210,7 @@ void GraphEditor::set_row(EffectRow *r) { if (found_vals) { row = r; current_row_desc->setText(row->GetParentEffect()->parent_clip->name() - + " :: " + row->GetParentEffect()->meta->name + + " :: " + row->GetParentEffect()->name() + " :: " + row->name()); header->set_visible_in(r->GetParentEffect()->parent_clip->timeline_in()); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index bc3b8774a..e23f7e125 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -221,10 +221,10 @@ void Timeline::SetSequence(SequencePtr sequence) return; } - sequence_ = sequence; + sequence_ = sequence; update_sequence(); - video_area->SetTrackList(sequence_.get(), Track::kTypeVideo); - audio_area->SetTrackList(sequence_.get(), Track::kTypeAudio); + video_area->SetTrackList(sequence_.get(), olive::kTypeVideo); + audio_area->SetTrackList(sequence_.get(), olive::kTypeAudio); repaint_timeline(); emit SequenceChanged(sequence_); @@ -276,14 +276,14 @@ void Timeline::add_transition() { for (int i=0;itype() == Track::kTypeVideo) ? TRANSITION_INTERNAL_CROSSDISSOLVE - : TRANSITION_INTERNAL_LINEARFADE; + NodeType transition_to_add = (c->type() == olive::kTypeVideo) ? kCrossDissolveTransition + : kLinearFadeTransition; if (c->opening_transition == nullptr) { ca->append(new AddTransitionCommand(c, nullptr, nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + transition_to_add, olive::config.default_transition_length)); adding = true; } @@ -292,7 +292,7 @@ void Timeline::add_transition() { ca->append(new AddTransitionCommand(nullptr, c, nullptr, - Effect::GetInternalMeta(transition_to_add, EFFECT_TYPE_TRANSITION), + transition_to_add, olive::config.default_transition_length)); adding = true; } @@ -873,23 +873,23 @@ void Timeline::transition_tool_click() { Menu transition_menu(this); - for (int i=0;itype() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) { + QAction* a = transition_menu.addAction(node->name()); a->setObjectName("v"); - a->setData(reinterpret_cast(&em)); + a->setData(i); } } transition_menu.addSeparator(); - for (int i=0;itype() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) { + QAction* a = transition_menu.addAction(node->name()); a->setObjectName("a"); - a->setData(reinterpret_cast(&em)); + a->setData(i); } } @@ -901,12 +901,12 @@ void Timeline::transition_tool_click() { } void Timeline::transition_menu_select(QAction* a) { - transition_tool_meta = reinterpret_cast(a->data().value()); + transition_tool_meta = static_cast(a->data().toInt()); if (a->objectName() == "v") { - transition_tool_side = Track::kTypeVideo; + transition_tool_side = olive::kTypeVideo; } else { - transition_tool_side = Track::kTypeAudio; + transition_tool_side = olive::kTypeAudio; } timeline_area->setCursor(Qt::CrossCursor); diff --git a/panels/timeline.h b/panels/timeline.h index 0606aef06..ba47ca69d 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -114,8 +114,8 @@ public: bool transition_tool_proc; Clip* transition_tool_open_clip; Clip* transition_tool_close_clip; - const EffectMeta* transition_tool_meta; - Track::Type transition_tool_side; + NodeType transition_tool_meta; + olive::TrackType transition_tool_side; // hand tool variables bool hand_moving; diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 79ec033bd..f0c6756a5 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -752,7 +752,7 @@ void Viewer::set_media(Media* m) { new_sequence->frame_rate = video_stream.video_frame_rate * footage->speed; } - ClipPtr c = std::make_shared(new_sequence->GetTrackList(Track::kTypeVideo)->First()); + ClipPtr c = std::make_shared(new_sequence->GetTrackList(olive::kTypeVideo)->First()); c->set_media(media, video_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate)); @@ -760,7 +760,7 @@ void Viewer::set_media(Media* m) { // FIXME: Move this magic number to Config c->set_timeline_out(150); } - Track* track = new_sequence->GetTrackList(Track::kTypeVideo)->First(); + Track* track = new_sequence->GetTrackList(olive::kTypeVideo)->First(); c->set_track(track); c->set_clip_in(0); c->refresh(); @@ -774,7 +774,7 @@ void Viewer::set_media(Media* m) { const FootageStream& audio_stream = footage->audio_tracks.at(0); new_sequence->audio_frequency = audio_stream.audio_frequency; - Track* track = new_sequence->GetTrackList(Track::kTypeAudio)->First(); + Track* track = new_sequence->GetTrackList(olive::kTypeAudio)->First(); ClipPtr c = std::make_shared(track); c->set_media(media, audio_stream.file_index); c->set_timeline_in(0); diff --git a/project/loadthread.cpp b/project/loadthread.cpp index a6a82ea64..dee719ecc 100644 --- a/project/loadthread.cpp +++ b/project/loadthread.cpp @@ -60,7 +60,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { QString tag = stream.name().toString(); // variables to store effect metadata in - int effect_id = -1; + QString effect_id; QString effect_name; bool effect_enabled = true; long effect_length = -1; @@ -68,10 +68,10 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { // loop through attributes for effect metadata for (int j=0;jid() == effect_id) { + type = static_cast(i); + break; + } + } } olive::effects_loaded.unlock(); - int type; + TransitionType ttype; if (tag == "opening") { - type = kTransitionOpening; + ttype = kTransitionOpening; } else if (tag == "closing") { - type = kTransitionClosing; + ttype = kTransitionClosing; } else { - type = kTransitionNone; + ttype = kTransitionNone; } // effect construction if (cancelled_) return; - if (type == kTransitionNone) { - if (meta == nullptr) { + if (ttype == kTransitionNone) { + if (type == kInvalidNode) { // create void effect - EffectPtr ve(new VoidEffect(c, effect_name)); + NodePtr ve = std::make_shared(c, effect_name, effect_id); ve->SetEnabled(effect_enabled); ve->load(stream); @@ -147,7 +152,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { c->effects.append(ve); } else { - EffectPtr e(Effect::Create(c, meta)); + NodePtr e = olive::node_library[type]->Create(c); e->SetEnabled(effect_enabled); e->load(stream); @@ -156,14 +161,16 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) { c->effects.append(e); } } else { - TransitionPtr t = Transition::Create(c, nullptr, meta); - if (effect_length > -1) t->set_length(effect_length); + TransitionPtr t = std::static_pointer_cast(olive::node_library[type]->Create(c)); + if (effect_length > -1) { + t->set_length(effect_length); + } t->SetEnabled(effect_enabled); t->load(stream); t->moveToThread(QApplication::instance()->thread()); - if (type == kTransitionOpening) { + if (ttype == kTransitionOpening) { c->opening_transition = t; } else { c->closing_transition = t; diff --git a/rendering/cacher.cpp b/rendering/cacher.cpp index ed1c770cc..61abd3292 100644 --- a/rendering/cacher.cpp +++ b/rendering/cacher.cpp @@ -62,7 +62,7 @@ void apply_audio_effects(Clip* clip, double timecode_start, AVFrame* frame, int timecode_end = timecode_start + samples_to_seconds(nb_samples, frame->channels, frame->sample_rate); for (int j=0;jeffects.size();j++) { - Effect* e = clip->effects.at(j).get(); + Node* e = clip->effects.at(j).get(); if (e->IsEnabled()) { e->process_audio(timecode_start, timecode_end, reinterpret_cast(frame->data), nb_samples, nb_channels, kTransitionNone); } @@ -805,7 +805,7 @@ void Cacher::CacheVideoWorker() { void Cacher::Reset() { // if we seek to a whole other place in the timeline, we'll need to reset the cache with new values if (clip->media() == nullptr) { - if (clip->type() == Track::kTypeAudio) { + if (clip->type() == olive::kTypeAudio) { // a null-media audio clip is usually an auto-generated sound clip such as Tone or Noise reached_end = false; audio_target_frame = playhead_; @@ -871,7 +871,7 @@ Cacher::Cacher(Clip* c) : void Cacher::OpenWorker() { // set some defaults for the audio cacher - if (clip->type() == Track::kTypeAudio) { + if (clip->type() == olive::kTypeAudio) { audio_reset_ = false; frame_sample_index_ = -1; audio_buffer_write = 0; @@ -879,7 +879,7 @@ void Cacher::OpenWorker() { reached_end = false; if (clip->media() == nullptr) { - if (clip->type() == Track::kTypeAudio) { + if (clip->type() == olive::kTypeAudio) { frame_ = av_frame_alloc(); frame_->format = kDestSampleFmt; frame_->channel_layout = clip->track()->sequence()->audio_layout; @@ -1127,7 +1127,7 @@ void Cacher::OpenWorker() { } void Cacher::CacheWorker() { - if (clip->type() == Track::kTypeVideo) { + if (clip->type() == olive::kTypeVideo) { // clip is a video track, start caching video CacheVideoWorker(); } else { @@ -1218,7 +1218,7 @@ void Cacher::Open() caching_ = true; queued_ = false; - start((clip->type() == Track::kTypeVideo) ? QThread::HighPriority : QThread::TimeCriticalPriority); + start((clip->type() == olive::kTypeVideo) ? QThread::HighPriority : QThread::TimeCriticalPriority); } void Cacher::Cache(long playhead, bool scrubbing, QVector& nests, int playback_speed) diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index 054a41f2e..ba1e94778 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -34,7 +34,7 @@ extern "C" { #include "timeline/clip.h" #include "timeline/sequence.h" #include "project/media.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "project/footage.h" #include "effects/transition.h" #include "ui/collapsiblewidget.h" @@ -110,7 +110,6 @@ void olive::rendering::Blit(QOpenGLShaderProgram* pipeline, bool flipped, QMatri pipeline->setUniformValue("mvp_matrix", matrix); pipeline->setUniformValue("texture", 0); - GLuint vertex_location = pipeline->attributeLocation("a_position"); m_vbo.bind(); func->glEnableVertexAttribArray(vertex_location); @@ -176,7 +175,7 @@ GLuint draw_clip(QOpenGLContext* ctx, void process_effect(QOpenGLContext* ctx, QOpenGLShaderProgram* pipeline, Clip* c, - Effect* e, + Node* e, double timecode, GLTextureCoords& coords, GLuint& composite_texture, @@ -184,11 +183,11 @@ void process_effect(QOpenGLContext* ctx, bool& texture_failed, int data) { if (e->IsEnabled()) { - if (e->Flags() & Effect::CoordsFlag) { + if (e->Flags() & Node::CoordsFlag) { e->process_coords(timecode, coords, data); } - bool can_process_shaders = ((e->Flags() & Effect::ShaderFlag) && olive::runtime_config.shaders_are_enabled); - if (can_process_shaders || (e->Flags() & Effect::SuperimposeFlag)) { + bool can_process_shaders = ((e->Flags() & Node::ShaderFlag) && olive::runtime_config.shaders_are_enabled); + if (can_process_shaders || (e->Flags() & Node::SuperimposeFlag)) { if (!e->is_open()) { e->open(); @@ -201,7 +200,7 @@ void process_effect(QOpenGLContext* ctx, fbo_switcher = !fbo_switcher; } } - if (e->Flags() & Effect::SuperimposeFlag) { + if (e->Flags() & Node::SuperimposeFlag) { GLuint superimpose_texture = e->process_superimpose(ctx, timecode); if (superimpose_texture == 0) { @@ -227,7 +226,7 @@ void process_effect(QOpenGLContext* ctx, } GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { - GLuint final_fbo = params.type == Track::kTypeVideo ? params.main_buffer->buffer() : 0; + GLuint final_fbo = params.type == olive::kTypeVideo ? params.main_buffer->buffer() : 0; Sequence* s = params.seq; long playhead = s->playhead; @@ -240,7 +239,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { playhead = rescale_frame_number(playhead, params.nests.at(i)->track()->sequence()->frame_rate, s->frame_rate); } - if (params.type == Track::kTypeVideo && !params.nests.last()->fbo.isEmpty()) { + if (params.type == olive::kTypeVideo && !params.nests.last()->fbo.isEmpty()) { params.nests.last()->fbo.at(0).BindBuffer(); params.ctx->functions()->glClear(GL_COLOR_BUFFER_BIT); final_fbo = params.nests.last()->fbo.at(0).buffer(); @@ -270,7 +269,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { Footage* m = c->media()->to_footage(); // does the clip have a valid media source? - if (!m->invalid && !(c->type() == Track::kTypeAudio && !is_audio_device_set())) { + if (!m->invalid && !(c->type() == olive::kTypeAudio && !is_audio_device_set())) { // is the media process and ready? if (m->ready) { @@ -287,7 +286,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { clip_is_active = true; // increment audio track count - if (c->type() == Track::kTypeAudio) audio_track_count++; + if (c->type() == olive::kTypeAudio) audio_track_count++; } else if (c->IsOpen()) { @@ -321,7 +320,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // track sorting is only necessary for video clips // audio clips are mixed equally, so we skip sorting for those - if (params.type == Track::kTypeVideo) { + if (params.type == olive::kTypeVideo) { // insertion sort by track for (int j=0;jfunctions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -370,7 +369,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { if (got_mutex && c->IsOpen()) { // if clip is a video clip - if (c->type() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { // textureID variable contains texture to be drawn on screen at the end GLuint textureID = 0; @@ -504,7 +503,6 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { coords.texture_top_right = QVector2D(1.0f, 0.0f); coords.texture_bottom_left = QVector2D(0.0f, 1.0f); coords.texture_bottom_right = QVector2D(1.0f, 1.0f); - coords.blendmode = -1; coords.opacity = 1.0; // == EFFECT CODE START == @@ -515,7 +513,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // run through all of the clip's effects for (int j=0;jeffects.size();j++) { - Effect* e = c->effects.at(j).get(); + Node* e = c->effects.at(j).get(); process_effect(params.ctx, params.pipeline, c, e, timecode, coords, textureID, fbo_switcher, params.texture_failed, kTransitionNone); } @@ -685,9 +683,11 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // copy front buffer to back buffer (only if we're using a blending mode) + /* if (coords.blendmode >= 0) { draw_clip(params.ctx, params.pipeline, back_buffer_2, comp_texture, true); } + */ @@ -700,7 +700,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, final_fbo); // Check if we're using a blend mode (< 0 means no blend mode) - if (coords.blendmode < 0) { + //if (coords.blendmode < 0) { params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, backend_tex_1); @@ -708,7 +708,9 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); - } else { + //} else { + + /* // load background texture into texture unit 0 params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 @@ -739,7 +741,9 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { params.ctx->functions()->glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0 params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, 0); - } + */ + + //} // unbind framebuffer params.ctx->functions()->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); @@ -750,7 +754,7 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { // == END FINAL DRAW ON SEQUENCE BUFFER == } } - } else if (c->type() == Track::kTypeAudio) { + } else if (c->type() == olive::kTypeAudio) { if (c->media() != nullptr && c->media()->get_type() == MEDIA_TYPE_SEQUENCE) { params.nests.append(c); compose_sequence(params); @@ -795,11 +799,10 @@ void olive::rendering::compose_audio(Viewer* viewer, Sequence* seq, int playback params.viewer = viewer; params.ctx = nullptr; params.seq = seq; - params.type = Track::kTypeAudio; + params.type = olive::kTypeAudio; params.gizmos = nullptr; params.wait_for_mutexes = wait_for_mutexes; params.playback_speed = playback_speed; - params.blend_mode_program = nullptr; compose_sequence(params); } diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h index e38ee8bb6..658ecc422 100644 --- a/rendering/renderfunctions.h +++ b/rendering/renderfunctions.h @@ -28,7 +28,7 @@ namespace OCIO = OCIO_NAMESPACE::v1; #include "timeline/sequence.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "panels/viewer.h" /** @@ -80,16 +80,16 @@ struct ComposeSequenceParams { /** * @brief Set compose mode to video or audio * - * Accepts Track::kTypeVideo to render video, Track::kTypeAudio if this function should render audio. + * Accepts olive::kTypeVideo to render video, olive::kTypeAudio if this function should render audio. */ - Track::Type type; + olive::TrackType type; /** * @brief Set to the Effect whose gizmos were chosen to be drawn on screen * * The currently active Effect that compose_sequence() will update the gizmos of. */ - Effect* gizmos; + Node* gizmos; /** * @brief A variable that compose_sequence() will set to **TRUE** if any of the clips couldn't be shown. @@ -138,19 +138,6 @@ struct ComposeSequenceParams { */ int playback_speed; - /** - * @brief Blending mode shader - * - * Used only for video rendering. Never accessed with audio rendering. - * - * A program containing the current active - * blending mode shader that can be bound during rendering. Must be compiled and linked beforehand. See - * RenderThread::blend_mode_program for how this is properly set up. - * - * \see ComposeSequenceParams::video - */ - QOpenGLShaderProgram* blend_mode_program; - /** * @brief Premultiply alpha shader * diff --git a/rendering/renderthread.cpp b/rendering/renderthread.cpp index c92b323bd..ea6f9dacd 100644 --- a/rendering/renderthread.cpp +++ b/rendering/renderthread.cpp @@ -40,7 +40,6 @@ RenderThread::RenderThread() : gizmos(nullptr), share_ctx(nullptr), ctx(nullptr), - blend_mode_program(nullptr), seq(nullptr), tex_width(-1), tex_height(-1), @@ -50,7 +49,8 @@ RenderThread::RenderThread() : ocio_shader(nullptr), running(true), ocio_config_date(0), - front_buffer_switcher(false) + front_buffer_switcher(false), + pipeline_program(nullptr) { surface.create(); } @@ -101,17 +101,10 @@ void RenderThread::run() { back_buffer_2.Create(ctx, seq->width, seq->height); } - // If there's no blending mode shader, create it now - if (blend_mode_program == nullptr) { + // If there's no pipeline shader, create it now + if (pipeline_program == nullptr) { delete_shaders(); - blend_mode_program = std::make_shared(); - blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert"); - olive::effects_loaded.lock(); - blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader); - olive::effects_loaded.unlock(); - blend_mode_program->link(); - pipeline_program = olive::shader::GetPipeline(); } @@ -206,11 +199,10 @@ void RenderThread::paint() { params.viewer = nullptr; params.ctx = ctx; params.seq = seq; - params.type = Track::kTypeVideo; + params.type = olive::kTypeVideo; params.texture_failed = false; params.wait_for_mutexes = true; params.playback_speed = playback_speed_; - params.blend_mode_program = blend_mode_program.get(); params.pipeline = pipeline_program.get(); params.backend_buffer1 = &back_buffer_1; params.backend_buffer2 = &back_buffer_2; @@ -380,7 +372,6 @@ void RenderThread::delete_buffers() { } void RenderThread::delete_shaders() { - blend_mode_program = nullptr; pipeline_program = nullptr; } diff --git a/rendering/renderthread.h b/rendering/renderthread.h index 89e33f521..cea0a7099 100644 --- a/rendering/renderthread.h +++ b/rendering/renderthread.h @@ -30,7 +30,7 @@ #include #include "timeline/sequence.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "rendering/framebufferobject.h" #include "qopenglshaderprogramptr.h" @@ -44,7 +44,7 @@ public: QMutex* get_texture_mutex(); const GLuint& get_texture(); - Effect* gizmos; + Node* gizmos; void paint(); void start_render(QOpenGLContext* share, Sequence *s, @@ -94,7 +94,6 @@ private: QOffscreenSurface surface; QOpenGLContext* share_ctx; QOpenGLContext* ctx; - QOpenGLShaderProgramPtr blend_mode_program; QOpenGLShaderProgramPtr pipeline_program; FramebufferObject back_buffer_1; diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 2dcdd9f76..57e705fb3 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -22,7 +22,7 @@ #include -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/transition.h" #include "project/footage.h" #include "global/config.h" @@ -116,7 +116,7 @@ Selection Clip::ToSelection() return Selection(timeline_in(), timeline_out(), track()); } -Track::Type Clip::type() +olive::TrackType Clip::type() { return track()->type(); } @@ -147,7 +147,7 @@ FootageStream *Clip::media_stream() { if (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) { - return media()->to_footage()->get_stream_from_file_index(type() == Track::kTypeVideo, media_stream_index()); + return media()->to_footage()->get_stream_from_file_index(type() == olive::kTypeVideo, media_stream_index()); } return nullptr; @@ -199,9 +199,9 @@ void Clip::refresh() { if (replaced && media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE) { Footage* m = media()->to_footage(); - if (type() == Track::kTypeVideo && m->video_tracks.size() > 0) { + if (type() == olive::kTypeVideo && m->video_tracks.size() > 0) { set_media(media(), m->video_tracks.at(0).file_index); - } else if (type() == Track::kTypeAudio && m->audio_tracks.size() > 0) { + } else if (type() == olive::kTypeAudio && m->audio_tracks.size() > 0) { set_media(media(), m->audio_tracks.at(0).file_index); } } @@ -220,7 +220,7 @@ QVector &Clip::get_markers() { return markers; } -int Clip::IndexOfEffect(Effect *e) +int Clip::IndexOfEffect(Node *e) { for (int i=0;iget_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; @@ -449,7 +449,7 @@ long Clip::media_length() { case MEDIA_TYPE_FOOTAGE: { Footage* m = media_->to_footage(); - const FootageStream* ms = m->get_stream_from_file_index(type() == Track::kTypeVideo, media_stream_index()); + const FootageStream* ms = m->get_stream_from_file_index(type() == olive::kTypeVideo, media_stream_index()); if (ms != nullptr && ms->infinite_length) { return LONG_MAX; } else { @@ -517,7 +517,7 @@ void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_t // move keyframes for (int i=0;irow_count();j++) { EffectRow* r = e->row(j); for (int l=0;lFieldCount();l++) { @@ -710,7 +710,7 @@ bool Clip::Retrieve() bool Clip::UsesCacher() { - return type() == Track::kTypeAudio || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE); + return type() == olive::kTypeAudio || (media() != nullptr && media()->get_type() == MEDIA_TYPE_FOOTAGE); } ClipSpeed::ClipSpeed() : diff --git a/timeline/clip.h b/timeline/clip.h index b0614535d..30ea217ce 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -30,15 +30,17 @@ #include "rendering/cacher.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/transition.h" #include "undo/comboaction.h" #include "project/media.h" #include "project/footage.h" #include "rendering/framebufferobject.h" #include "marker.h" -#include "track.h" #include "nodes/nodegraph.h" +#include "selection.h" + +class Track; struct ClipSpeed { ClipSpeed(); @@ -46,8 +48,6 @@ struct ClipSpeed { bool maintain_audio_pitch; }; -using ClipPtr = std::shared_ptr; - class Clip { public: Clip(Track *s); @@ -62,7 +62,7 @@ public: Selection ToSelection(); - Track::Type type(); + olive::TrackType type(); const QColor& color(); void set_color(int r, int g, int b); @@ -129,8 +129,8 @@ public: QVector& get_markers(); // other variables (should be deep copied/duplicated in copy()) - int IndexOfEffect(Effect* e); - QList effects; + int IndexOfEffect(Node* e); + QList effects; QVector linked; TransitionPtr opening_transition; TransitionPtr closing_transition; diff --git a/timeline/ghost.h b/timeline/ghost.h index f95314fe3..14ee4b131 100644 --- a/timeline/ghost.h +++ b/timeline/ghost.h @@ -3,6 +3,7 @@ #include "effects/transition.h" #include "track.h" +#include "project/media.h" namespace olive { namespace timeline { diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 1a1d17fe3..75340e974 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -36,10 +36,10 @@ Sequence::Sequence() : wrapper_sequence(false) { // Set up tracks - track_lists_.resize(Track::kTypeCount); + track_lists_.resize(olive::kTypeCount); for (int i=0;i(i)); + track_lists_[i] = new TrackList(this, static_cast(i)); } } @@ -133,7 +133,7 @@ QVector Sequence::GetAllClips() return all_clips; } -TrackList *Sequence::GetTrackList(Track::Type type) +TrackList *Sequence::GetTrackList(olive::TrackType type) { return track_lists_.at(type); } @@ -230,13 +230,13 @@ void Sequence::AddClipsFromGhosts(ComboAction* ca, const QVector& ghosts) } if (olive::config.add_default_effects_to_clips) { - if (c->type() == Track::kTypeVideo) { + if (c->type() == olive::kTypeVideo) { // add default video effects - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); - } else if (c->type() == Track::kTypeAudio) { + c->effects.append(olive::node_library[kTransformEffect]->Create(c.get())); + } else if (c->type() == olive::kTypeAudio) { // add default audio effects - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kVolumeEffect]->Create(c.get())); + c->effects.append(olive::node_library[kPanEffect]->Create(c.get())); } } } @@ -265,7 +265,7 @@ void Sequence::MoveClip(Clip *c, ComboAction *ca, long iin, long iout, long icli ca->append(new AddTransitionCommand(nullptr, c->opening_transition->secondary_clip, c->opening_transition, - nullptr, + kInvalidNode, 0)); } @@ -277,7 +277,7 @@ void Sequence::MoveClip(Clip *c, ComboAction *ca, long iin, long iout, long icli ca->append(new AddTransitionCommand(nullptr, c, c->closing_transition, - nullptr, + kInvalidNode, 0)); } } @@ -905,9 +905,9 @@ void Sequence::RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_ } -Effect *Sequence::GetSelectedGizmo() +Node *Sequence::GetSelectedGizmo() { - Effect* gizmo_ptr = nullptr; + Node* gizmo_ptr = nullptr; QVector clips = GetAllClips(); @@ -923,7 +923,7 @@ Effect *Sequence::GetSelectedGizmo() // none selected for (int j=0;jeffects.size();j++) { - Effect* e = c->effects.at(j).get(); + Node* e = c->effects.at(j).get(); // retrieve gizmo data from effect if (e->are_gizmos_enabled()) { @@ -1156,7 +1156,7 @@ ClipPtr Sequence::SplitClip(ComboAction *ca, bool transitions, Clip* pre, long f ca->append(new AddTransitionCommand(nullptr, pre->opening_transition->secondary_clip, pre->opening_transition, - nullptr, + kInvalidNode, 0) ); diff --git a/timeline/sequence.h b/timeline/sequence.h index 41c2cfbb7..94051ae83 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -47,7 +47,7 @@ public: long GetEndFrame(); QVector GetAllClips(); - TrackList* GetTrackList(Track::Type type); + TrackList* GetTrackList(olive::TrackType type); /** * @brief Close all open clips in a Sequence @@ -97,7 +97,7 @@ public: void RippleDeleteEmptySpace(ComboAction *ca, Track *track, long point); void RippleDeleteArea(ComboAction* ca, long ripple_point, long ripple_length); - Effect* GetSelectedGizmo(); + Node* GetSelectedGizmo(); bool IsClipSelected(Clip* clip, bool containing = true); bool IsTransitionSelected(Transition* t); diff --git a/timeline/timelinefunctions.cpp b/timeline/timelinefunctions.cpp index beaf69929..fbbd1985b 100644 --- a/timeline/timelinefunctions.cpp +++ b/timeline/timelinefunctions.cpp @@ -119,7 +119,7 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jaudio_tracks.size();j++) { if (m->audio_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeAudio)->TrackAt(j); + g.track = seq->GetTrackList(olive::kTypeAudio)->TrackAt(j); g.media_stream = m->audio_tracks.at(j).file_index; ghosts.append(g); } @@ -130,7 +130,7 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, || import_data.type() == olive::timeline::kImportBoth) { for (int j=0;jvideo_tracks.size();j++) { if (m->video_tracks.at(j).enabled) { - g.track = seq->GetTrackList(Track::kTypeVideo)->TrackAt(j); + g.track = seq->GetTrackList(olive::kTypeVideo)->TrackAt(j); g.media_stream = m->video_tracks.at(j).file_index; ghosts.append(g); } @@ -146,13 +146,13 @@ QVector olive::timeline::CreateGhostsFromMedia(Sequence *seq, if (import_data.type() == olive::timeline::kImportVideoOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = seq->GetTrackList(Track::kTypeVideo)->First(); + g.track = seq->GetTrackList(olive::kTypeVideo)->First(); ghosts.append(g); } if (import_data.type() == olive::timeline::kImportAudioOnly || import_data.type() == olive::timeline::kImportBoth) { - g.track = seq->GetTrackList(Track::kTypeAudio)->First(); + g.track = seq->GetTrackList(olive::kTypeAudio)->First(); ghosts.append(g); } diff --git a/timeline/track.cpp b/timeline/track.cpp index 37cbc7a91..9273b67ed 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -9,7 +9,7 @@ int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; int olive::timeline::kTrackHeightIncrement = 10; -Track::Track(TrackList* parent, Type type) : +Track::Track(TrackList* parent, olive::TrackType type) : parent_(parent), type_(type), muted_(false), @@ -58,7 +58,7 @@ void Track::Save(QXmlStreamWriter &stream) stream.writeEndElement(); // track } -Track::Type Track::type() +olive::TrackType Track::type() { return type_; } @@ -80,11 +80,11 @@ QString Track::name() int display_index = Index() + 1; switch (type_) { - case kTypeVideo: + case olive::kTypeVideo: return tr("Video %1").arg(display_index); - case kTypeAudio: + case olive::kTypeAudio: return tr("Audio %1").arg(display_index); - case kTypeSubtitle: + case olive::kTypeSubtitle: return tr("Subtitle %1").arg(display_index); default: return tr("Unknown %1").arg(display_index); diff --git a/timeline/track.h b/timeline/track.h index 847f27415..310cd69e9 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -4,7 +4,13 @@ #include #include -#include "effects/effect.h" +#include "nodes/node.h" +#include "tracktypes.h" +#include "undo/comboaction.h" +#include "timeline/selection.h" + +class Sequence; +class Transition; namespace olive { namespace timeline { @@ -35,14 +41,7 @@ class Track : public QObject { Q_OBJECT public: - enum Type { - kTypeVideo, - kTypeAudio, - kTypeSubtitle, - kTypeCount - }; - - Track(TrackList* parent, Type type); + Track(TrackList* parent, olive::TrackType type); Track* copy(TrackList* parent); Sequence* sequence(); @@ -50,7 +49,7 @@ public: void Save(QXmlStreamWriter& stream); - Type type(); + olive::TrackType type(); int height(); void set_height(int h); @@ -110,10 +109,10 @@ private: void ResizeClipArray(int new_size); TrackList* parent_; - Type type_; + olive::TrackType type_; int height_; QVector clips_; - QVector effects_; + QVector effects_; QVector selections_; bool muted_; diff --git a/timeline/tracklist.cpp b/timeline/tracklist.cpp index 5dbb3ac52..a735750b2 100644 --- a/timeline/tracklist.cpp +++ b/timeline/tracklist.cpp @@ -2,7 +2,7 @@ #include "timeline/sequence.h" -TrackList::TrackList(Sequence *parent, Track::Type type) : +TrackList::TrackList(Sequence *parent, olive::TrackType type) : QObject(parent), type_(type) { @@ -91,7 +91,7 @@ QVector TrackList::tracks() return tracks_; } -Track::Type TrackList::type() +olive::TrackType TrackList::type() { return type_; } diff --git a/timeline/tracklist.h b/timeline/tracklist.h index 4186cf84b..5b657aa06 100644 --- a/timeline/tracklist.h +++ b/timeline/tracklist.h @@ -7,7 +7,7 @@ class TrackList : public QObject { Q_OBJECT public: - TrackList(Sequence* parent, Track::Type type); + TrackList(Sequence* parent, olive::TrackType type); TrackList* copy(Sequence* parent); void Save(QXmlStreamWriter& stream); @@ -21,7 +21,7 @@ public: Track* TrackAt(int i); QVector tracks(); - Track::Type type(); + olive::TrackType type(); Sequence* GetParent(); @@ -31,7 +31,7 @@ signals: private: void ResizeTrackArray(int i); - Track::Type type_; + olive::TrackType type_; QVector tracks_; }; diff --git a/timeline/tracktypes.h b/timeline/tracktypes.h new file mode 100644 index 000000000..c49b049a7 --- /dev/null +++ b/timeline/tracktypes.h @@ -0,0 +1,13 @@ +#ifndef TRACKTYPES_H +#define TRACKTYPES_H + +namespace olive { + enum TrackType { + kTypeVideo, + kTypeAudio, + kTypeSubtitle, + kTypeCount + }; +}; + +#endif // TRACKTYPES_H diff --git a/ui/effectui.cpp b/ui/effectui.cpp index bdfbeb813..62a1cdb35 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -29,7 +29,7 @@ #include "ui/menu.h" #include "panels/panels.h" -EffectUI::EffectUI(Effect* e) : +EffectUI::EffectUI(Node* e) : effect_(e), node_parent_(nullptr) { @@ -38,7 +38,7 @@ EffectUI::EffectUI(Effect* e) : QString effect_name; // If this effect is actually a transition - if (e->meta->type == EFFECT_TYPE_TRANSITION) { + if (e->type() == EFFECT_TYPE_TRANSITION) { Transition* t = static_cast(e); @@ -70,17 +70,17 @@ EffectUI::EffectUI(Effect* e) : // See if the transition is the clip's opening or closing transition and label it accordingly if (both_selected) { - effect_name = t->name; + effect_name = t->name(); } else if (selected_clip->opening_transition.get() == t) { - effect_name = tr("%1 (Opening)").arg(t->name); + effect_name = tr("%1 (Opening)").arg(t->name()); } else { - effect_name = tr("%1 (Closing)").arg(t->name); + effect_name = tr("%1 (Closing)").arg(t->name()); } } else { // Otherwise just set the title normally - effect_name = e->name; + effect_name = e->name(); } @@ -166,10 +166,10 @@ EffectUI::EffectUI(Effect* e) : connect(enabled_check, SIGNAL(toggled(bool)), e, SLOT(FieldChanged())); } -void EffectUI::AddAdditionalEffect(Effect *e) +void EffectUI::AddAdditionalEffect(Node *e) { // Ensure this is the same kind of effect and will be fully compatible - Q_ASSERT(e->meta == effect_->meta); + Q_ASSERT(e->id() == effect_->id()); // Add multiple modifer to header label (but only once) if (additional_effects_.isEmpty()) { @@ -199,7 +199,7 @@ void EffectUI::AddAdditionalEffect(Effect *e) } } -Effect *EffectUI::GetEffect() +Node *EffectUI::GetEffect() { return effect_; } @@ -228,7 +228,7 @@ int EffectUI::GetRowY(int row, QWidget* mapToWidget) { void EffectUI::UpdateFromEffect() { - Effect* effect = GetEffect(); + Node* effect = GetEffect(); for (int j=0;jrow_count();j++) { @@ -327,7 +327,7 @@ void EffectUI::AttachKeyframeNavigationToRow(EffectRow *row, KeyframeNavigator * } void EffectUI::show_context_menu(const QPoint& pos) { - if (effect_->meta->type == EFFECT_TYPE_EFFECT) { + if (effect_->type() == EFFECT_TYPE_EFFECT) { Menu menu; Clip* c = effect_->parent_clip; diff --git a/ui/effectui.h b/ui/effectui.h index 2888c04aa..9eb12201c 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -22,8 +22,9 @@ #define EFFECTUI_H #include "collapsiblewidget.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "ui/nodeui.h" +#include "ui/keyframenavigator.h" /** * @brief The EffectUI class @@ -49,7 +50,7 @@ public: * * The Effect to make a UI of. It must be a valid object. */ - EffectUI(Effect* e); + EffectUI(Node* e); /** * @brief Attach additional effects to this UI @@ -62,7 +63,7 @@ public: * * The Effect to add to this UI object. */ - void AddAdditionalEffect(Effect* e); + void AddAdditionalEffect(Node* e); /** * @brief Get the primary Effect that this UI object was created for @@ -71,7 +72,7 @@ public: * * The Effect object passed to the constrcutor whe creating this EffectUI. */ - Effect* GetEffect(); + Node* GetEffect(); /** * @brief Get the Y position of a given row @@ -175,12 +176,12 @@ private: /** * @brief Internal reference to the Effect this object was constructed around. */ - Effect* effect_; + Node* effect_; /** * @brief Internal array of additional Effect objects attached to this UI. */ - QVector additional_effects_; + QVector additional_effects_; /** * @brief Layout for UI widgets diff --git a/ui/graphview.cpp b/ui/graphview.cpp index 5885d7f76..637bf3e3e 100644 --- a/ui/graphview.cpp +++ b/ui/graphview.cpp @@ -33,7 +33,7 @@ #include "ui/keyframedrawing.h" #include "undo/undo.h" #include "undo/undostack.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "timeline/clip.h" #include "ui/rectangleselect.h" #include "ui/menu.h" diff --git a/ui/keyframedrawing.cpp b/ui/keyframedrawing.cpp index 895319b7a..ce4a49f24 100644 --- a/ui/keyframedrawing.cpp +++ b/ui/keyframedrawing.cpp @@ -20,7 +20,7 @@ #include "keyframedrawing.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "timeline/clip.h" #define KEYFRAME_POINT_COUNT 4 diff --git a/ui/keyframeview.cpp b/ui/keyframeview.cpp index e1926c394..44c122fc1 100644 --- a/ui/keyframeview.cpp +++ b/ui/keyframeview.cpp @@ -23,7 +23,7 @@ #include #include -#include "effects/effect.h" +#include "nodes/node.h" #include "ui/collapsiblewidget.h" #include "panels/panels.h" #include "timeline/clip.h" @@ -120,7 +120,7 @@ void KeyframeView::paintEvent(QPaintEvent*) { for (int j=0;jGetEffect(); + Node* e = container->GetEffect(); if (container->IsExpanded()) { for (int j=0;jrow_count();j++) { diff --git a/ui/keyframeview.h b/ui/keyframeview.h index 534438f66..918b40ee3 100644 --- a/ui/keyframeview.h +++ b/ui/keyframeview.h @@ -27,7 +27,7 @@ #include "ui/effectui.h" class Clip; -class Effect; +class Node; class EffectRow; class EffectField; class TimelineHeader; diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index ebc3f10bd..9e29e6db5 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -157,7 +157,7 @@ QVector NodeUI::GetNodeSocketRects() QVector rects; if (proxy_ != nullptr) { - Effect* e = central_widget_->GetEffect(); + Node* e = central_widget_->GetEffect(); for (int i=0;irow_count();i++) { diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index 5fe9d29ff..e0c795ab8 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -49,7 +49,7 @@ TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignm connect(view_, SIGNAL(requestScrollChange(int)), scrollbar_, SLOT(setValue(int))); } -void TimelineArea::SetTrackList(Sequence *sequence, Track::Type track_list) +void TimelineArea::SetTrackList(Sequence *sequence, olive::TrackType track_list) { if (track_list_ != nullptr) { disconnect(track_list_, SIGNAL(TrackCountChanged()), this, SLOT(RefreshLabels())); diff --git a/ui/timelinearea.h b/ui/timelinearea.h index 50e499e71..c4c929bf8 100644 --- a/ui/timelinearea.h +++ b/ui/timelinearea.h @@ -14,7 +14,7 @@ class TimelineArea : public QWidget public: TimelineArea(Timeline *timeline, olive::timeline::Alignment alignment = olive::timeline::kAlignmentTop); - void SetTrackList(Sequence* sequence, Track::Type track_list); + void SetTrackList(Sequence* sequence, olive::TrackType track_list); void SetAlignment(olive::timeline::Alignment alignment); TrackList* track_list(); diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 91356b676..403410de0 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -56,7 +56,7 @@ #include "ui/focusfilter.h" #include "dialogs/clippropertiesdialog.h" #include "global/debug.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "effects/internal/solideffect.h" #include "timeline/track.h" #include "global/math.h" @@ -143,7 +143,7 @@ void TimelineView::show_context_menu(const QPoint& pos) { bool audio_clips_are_selected = false; for (int i=0;itype() == Track::kTypeVideo) { + if (selected_clips.at(i)->type() == olive::kTypeVideo) { video_clips_are_selected = true; } else { audio_clips_are_selected = true; @@ -611,7 +611,7 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { // if the user is creating an object if (ParentTimeline()->creating) { - Track::Type create_type = Track::kTypeVideo; + olive::TrackType create_type = olive::kTypeVideo; switch (ParentTimeline()->creating_object) { case olive::timeline::ADD_OBJ_TITLE: case olive::timeline::ADD_OBJ_SOLID: @@ -620,7 +620,7 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { case olive::timeline::ADD_OBJ_TONE: case olive::timeline::ADD_OBJ_NOISE: case olive::timeline::ADD_OBJ_AUDIO: - create_type = Track::kTypeAudio; + create_type = olive::kTypeAudio; break; } @@ -1024,24 +1024,24 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { add.append(c); ca->append(new AddClipCommand(add)); - if (c->type() == Track::kTypeVideo && olive::config.add_default_effects_to_clips) { - // default video effects (before custom effects) - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TRANSFORM, EFFECT_TYPE_EFFECT))); + if (c->type() == olive::kTypeVideo && olive::config.add_default_effects_to_clips) { + // default video effects (before custom effects) + c->effects.append(olive::node_library[kTransformEffect]->Create(c.get())); } switch (ParentTimeline()->creating_object) { case olive::timeline::ADD_OBJ_TITLE: c->set_name(tr("Title")); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_RICHTEXT, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kRichTextInput]->Create(c.get())); break; case olive::timeline::ADD_OBJ_SOLID: c->set_name(tr("Solid Color")); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_SOLID, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kSolidInput]->Create(c.get())); break; case olive::timeline::ADD_OBJ_BARS: { c->set_name(tr("Bars")); - EffectPtr e = Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_SOLID, EFFECT_TYPE_EFFECT)); + NodePtr e = olive::node_library[kSolidInput]->Create(c.get()); // Auto-select bars SolidEffect* solid_effect = static_cast(e.get()); @@ -1052,20 +1052,20 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { break; case olive::timeline::ADD_OBJ_TONE: c->set_name(tr("Tone")); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_TONE, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kToneInput]->Create(c.get())); break; case olive::timeline::ADD_OBJ_NOISE: c->set_name(tr("Noise")); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_NOISE, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kNoiseInput]->Create(c.get())); break; default: break; } - if (c->type() == Track::kTypeAudio && olive::config.add_default_effects_to_clips) { + if (c->type() == olive::kTypeAudio && olive::config.add_default_effects_to_clips) { // default audio effects (after custom effects) - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_VOLUME, EFFECT_TYPE_EFFECT))); - c->effects.append(Effect::Create(c.get(), Effect::GetInternalMeta(EFFECT_INTERNAL_PAN, EFFECT_TYPE_EFFECT))); + c->effects.append(olive::node_library[kVolumeEffect]->Create(c.get())); + c->effects.append(olive::node_library[kPanEffect]->Create(c.get())); } push_undo = true; @@ -1432,7 +1432,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { ca->append(new AddTransitionCommand(nullptr, transition->secondary_clip, transition, - nullptr, + kInvalidNode, 0)); } else { @@ -1447,7 +1447,7 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { ca->append(new AddTransitionCommand(nullptr, transition->secondary_clip, transition, - nullptr, + kInvalidNode, 0)); } @@ -2821,7 +2821,7 @@ void TimelineView::draw_transition(QPainter& p, Clip* c, const QRect& clip_rect, if (draw_text) { p.setPen(Qt::white); - p.drawText(transition_text_rect, 0, t->meta->name, &transition_text_rect); + p.drawText(transition_text_rect, 0, t->name(), &transition_text_rect); } } p.setPen(Qt::black); @@ -2920,7 +2920,7 @@ void TimelineView::paintEvent(QPaintEvent*) { // draw thumbnail/waveform long media_length = clip->media_length(); - if (clip->type() == Track::kTypeVideo) { + if (clip->type() == olive::kTypeVideo) { // draw thumbnail int thumb_y = p.fontMetrics().height()+olive::timeline::kClipTextPadding+olive::timeline::kClipTextPadding; if (thumb_x < width() && thumb_y < height()) { diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 186fcc02a..5fb7dd057 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -63,6 +63,7 @@ extern "C" { #include "ui/menu.h" #include "ui/waveform.h" #include "mainwindow.h" +#include "effects/effectgizmo.h" const int kTitleActionSafeVertexSize = 84; diff --git a/ui/viewerwidget.h b/ui/viewerwidget.h index d5cb80bc2..f2d0fc7f5 100644 --- a/ui/viewerwidget.h +++ b/ui/viewerwidget.h @@ -34,7 +34,7 @@ #include "timeline/clip.h" #include "project/footage.h" -#include "effects/effect.h" +#include "nodes/node.h" #include "ui/viewerwindow.h" #include "ui/viewercontainer.h" #include "rendering/renderthread.h" @@ -84,7 +84,7 @@ private: bool dragging; void seek_from_click(int x); QMatrix4x4 get_matrix(); - Effect* gizmos; + Node* gizmos; int drag_start_x; int drag_start_y; int gizmo_x_mvmt; diff --git a/undo/undo.cpp b/undo/undo.cpp index 82738c21e..f9140cc5b 100644 --- a/undo/undo.cpp +++ b/undo/undo.cpp @@ -189,7 +189,7 @@ void SetTimelineInOutCommand::doRedo() { } } -AddEffectCommand::AddEffectCommand(Clip* c, EffectPtr e, const EffectMeta *m, int insert_pos) { +AddEffectCommand::AddEffectCommand(Clip* c, NodePtr e, NodeType m, int insert_pos) { clip = c; ref = e; meta = m; @@ -207,7 +207,7 @@ void AddEffectCommand::doUndo() { void AddEffectCommand::doRedo() { if (ref == nullptr) { - ref = Effect::Create(clip, meta); + ref = olive::node_library[meta]->Create(clip); } if (pos < 0) { clip->effects.append(ref); @@ -219,7 +219,7 @@ void AddEffectCommand::doRedo() { AddTransitionCommand::AddTransitionCommand(Clip* iopen, Clip* iclose, TransitionPtr copy, - const EffectMeta *itransition, + NodeType itransition, int ilength) { open_ = iopen; close_ = iclose; @@ -251,9 +251,10 @@ void AddTransitionCommand::doRedo() { // create new transition object if (new_transition_ref_ == nullptr) { if (transition_to_copy_ == nullptr) { - new_transition_ref_ = Transition::CreateFromMeta(primary, secondary, transition_meta_); + new_transition_ref_ = std::static_pointer_cast(olive::node_library[transition_meta_]->Create(primary)); + new_transition_ref_->secondary_clip = secondary; } else { - new_transition_ref_ = transition_to_copy_->copy(primary, nullptr); + new_transition_ref_ = std::static_pointer_cast(transition_to_copy_->copy(primary)); } } @@ -544,7 +545,7 @@ void ReplaceClipMediaCommand::doRedo() { update_ui(true); } -EffectDeleteCommand::EffectDeleteCommand(Effect *e) : +EffectDeleteCommand::EffectDeleteCommand(Node *e) : effect_(e) {} @@ -1132,7 +1133,7 @@ void UpdateViewer::doRedo() { panel_sequence_viewer->viewer_widget()->frame_update(); } -SetEffectData::SetEffectData(Effect *e, const QByteArray &s) { +SetEffectData::SetEffectData(Node *e, const QByteArray &s) { effect = e; data = s; } diff --git a/undo/undo.h b/undo/undo.h index fd6bd0669..9ce89d493 100644 --- a/undo/undo.h +++ b/undo/undo.h @@ -30,6 +30,7 @@ #include "comboaction.h" +#include "nodes/node.h" #include "timeline/marker.h" #include "timeline/selection.h" #include "effects/keyframe.h" @@ -43,13 +44,12 @@ using SequencePtr = std::shared_ptr; class Media; using MediaPtr = std::shared_ptr; -class Effect; -using EffectPtr = std::shared_ptr; +class Node; +using NodePtr = std::shared_ptr; class Transition; using TransitionPtr = std::shared_ptr; -struct EffectMeta; class EffectRow; class EffectField; @@ -124,27 +124,27 @@ private: class AddEffectCommand : public OliveAction { public: - AddEffectCommand(Clip* c, EffectPtr e, const EffectMeta* m, int insert_pos = -1); + AddEffectCommand(Clip* c, NodePtr e, NodeType m, int insert_pos = -1); virtual void doUndo() override; virtual void doRedo() override; private: Clip* clip; - const EffectMeta* meta; - EffectPtr ref; + NodeType meta; + NodePtr ref; int pos; bool done; }; class AddTransitionCommand : public OliveAction { public: - AddTransitionCommand(Clip* iopen, Clip* iclose, TransitionPtr copy, const EffectMeta* itransition, int ilength); + AddTransitionCommand(Clip* iopen, Clip* iclose, TransitionPtr copy, NodeType itransition, int ilength); virtual void doUndo() override; virtual void doRedo() override; private: Clip* open_; Clip* close_; TransitionPtr transition_to_copy_; - const EffectMeta* transition_meta_; + NodeType transition_meta_; int length_; TransitionPtr old_open_transition_; TransitionPtr old_close_transition_; @@ -273,12 +273,12 @@ private: class EffectDeleteCommand : public OliveAction { public: - EffectDeleteCommand(Effect* e); + EffectDeleteCommand(Node* e); virtual void doUndo() override; virtual void doRedo() override; private: - Effect* effect_; - EffectPtr deleted_obj_; + Node* effect_; + NodePtr deleted_obj_; Clip* parent_clip_; int index_; }; @@ -593,11 +593,11 @@ public: class SetEffectData : public OliveAction { public: - SetEffectData(Effect* e, const QByteArray &s); + SetEffectData(Node* e, const QByteArray &s); virtual void doUndo() override; virtual void doRedo() override; private: - Effect* effect; + Node* effect; QByteArray data; QByteArray old_data; }; From 41767cca5e8e73aa9b2ef5ec1c315ef443ebeb44 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 21:55:17 +1000 Subject: [PATCH 117/133] flipped zoom on node view to normal --- ui/nodeview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index f3c8a9ea7..be1681fa1 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -49,7 +49,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) void NodeView::wheelEvent(QWheelEvent *event) { - if (event->angleDelta().y() > 0) { + if (event->angleDelta().y() < 0) { scale(0.9, 0.9); } else { scale(1.1, 1.1); From 6b67727bd89512f57986a7dfd47b17fe7b86c5e9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 12 Apr 2019 22:17:33 +1000 Subject: [PATCH 118/133] minor node editor enhancements --- panels/nodeeditor.cpp | 23 +++++++++++++++-------- ui/nodeui.cpp | 2 +- ui/nodeview.cpp | 10 +++++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index c6310638c..73d24bd30 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -33,18 +33,25 @@ void NodeEditor::Retranslate() void NodeEditor::LoadEvent() { nodes_.resize(open_effects_.size()); - for (int i=0;iGetEffect()->parent_clip; - effect_ui->SetNodeParent(node_ui); - effect_ui->SetSelectable(false); + for (int i=0;iSetWidget(effect_ui); - node_ui->AddToScene(&scene_); + if (effect_ui->GetEffect()->parent_clip == first_clip) { + NodeUI* node_ui = new NodeUI(); - nodes_[i] = node_ui; + effect_ui->SetNodeParent(node_ui); + effect_ui->SetSelectable(false); + + node_ui->SetWidget(effect_ui); + node_ui->AddToScene(&scene_); + + nodes_[i] = node_ui; + } + } } } diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 9e29e6db5..de263e07d 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -15,7 +15,7 @@ #include "global/math.h" const int kRoundedRectRadius = 5; -const int kNodePlugSize = 10; +const int kNodePlugSize = 12; NodeUI::NodeUI() : central_widget_(nullptr), diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index be1681fa1..3998fab9c 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -49,9 +49,13 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) void NodeView::wheelEvent(QWheelEvent *event) { - if (event->angleDelta().y() < 0) { - scale(0.9, 0.9); + if (event->modifiers() & Qt::ControlModifier) { + if (event->angleDelta().y() < 0) { + scale(0.9, 0.9); + } else { + scale(1.1, 1.1); + } } else { - scale(1.1, 1.1); + QGraphicsView::wheelEvent(event); } } From 8cd96eae645a2cf16332c1f674bec16c0bfadf2e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 13 Apr 2019 02:40:58 +1000 Subject: [PATCH 119/133] nodes can now be connected through the node editor --- effects/effectrow.cpp | 57 +++++++++++++++++- effects/effectrow.h | 83 ++++++++++++++++++++++---- nodes/inputs/boolinput.cpp | 2 +- nodes/inputs/colorinput.cpp | 2 +- nodes/inputs/comboinput.cpp | 2 +- nodes/inputs/fileinput.cpp | 2 +- nodes/inputs/fontinput.cpp | 2 +- nodes/inputs/stringinput.cpp | 2 +- nodes/inputs/vecinput.cpp | 8 +-- nodes/node.cpp | 26 +++++++++ nodes/node.h | 9 ++- nodes/nodeedge.cpp | 21 +++++++ nodes/nodeedge.h | 39 +++++++++++++ nodes/nodeplug.cpp | 11 ---- nodes/nodeplug.h | 13 ----- nodes/nodes/nodemedia.cpp | 2 + olive.pro | 10 ++-- panels/nodeeditor.cpp | 49 +++++++++++++--- panels/nodeeditor.h | 5 ++ ui/nodeedgeui.cpp | 67 +++++++++++++++++++++ ui/nodeedgeui.h | 26 +++++++++ ui/nodeui.cpp | 109 ++++++++++++++++++++++++++++------- ui/nodeui.h | 16 ++++- ui/nodeview.cpp | 2 +- 24 files changed, 480 insertions(+), 85 deletions(-) create mode 100644 nodes/nodeedge.cpp create mode 100644 nodes/nodeedge.h delete mode 100644 nodes/nodeplug.cpp delete mode 100644 nodes/nodeplug.h create mode 100644 ui/nodeedgeui.cpp create mode 100644 ui/nodeedgeui.h diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index 7e2abaa7c..e60bea8b1 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -65,13 +65,54 @@ void EffectRow::AddField(EffectField *field) fields_.append(field); } -void EffectRow::AddNodeInput(olive::nodes::DataType type) +void EffectRow::AddAcceptedNodeInput(olive::nodes::DataType type) { Q_ASSERT(output_type_ == olive::nodes::kInvalid); accepted_inputs_.append(type); } +void EffectRow::ConnectEdge(EffectRow *output, EffectRow *input) +{ + Q_ASSERT(output->IsNodeOutput()); + Q_ASSERT(input->IsNodeInput()); + + if (!input->node_edges_.isEmpty()) { + DisconnectEdge(input->node_edges_.first()); + } + + NodeEdgePtr edge = std::make_shared(output, input); + + output->node_edges_.append(edge); + input->node_edges_.append(edge); + + emit output->EdgesChanged(); + emit input->EdgesChanged(); +} + +void EffectRow::DisconnectEdge(NodeEdgePtr edge) +{ + EffectRow* output = edge->output(); + EffectRow* input = edge->input(); + + output->node_edges_.removeAll(edge); + input->node_edges_.removeAll(edge); + + emit output->EdgesChanged(); + emit input->EdgesChanged(); +} + +QVector EffectRow::edges() +{ + QVector edges; + + for (int i=0;i edges(); + protected: /** * @brief Add a field to this row @@ -258,18 +317,6 @@ protected: */ void AddField(EffectField* Field); - /** - * @brief Adds a node data type that can be accepted by this input - * - * Allows this input to take a node connection from a data type specified by type. An input can take several data - * types. - * - * @param type - * - * The data type to add - */ - void AddNodeInput(olive::nodes::DataType type); - public slots: /** * @brief Go to previous keyframe @@ -332,6 +379,13 @@ signals: */ void Clicked(); + /** + * @brief Edges changed signal + * + * Signal emitted any time an edge is connected or disconnected from this row + */ + void EdgesChanged(); + private slots: /** * @brief Set keyframing enabled state @@ -402,6 +456,11 @@ private: * this. */ olive::nodes::DataType output_type_; + + /** + * @brief Internal array of node edges. Access with AddEdge() and RemoveEdge(). + */ + QVector node_edges_; }; #endif // EFFECTROW_H diff --git a/nodes/inputs/boolinput.cpp b/nodes/inputs/boolinput.cpp index ff58c005a..aa98181d6 100644 --- a/nodes/inputs/boolinput.cpp +++ b/nodes/inputs/boolinput.cpp @@ -7,7 +7,7 @@ BoolInput::BoolInput(Node* parent, const QString& id, const QString& name, bool connect(bool_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool))); AddField(bool_field); - AddNodeInput(olive::nodes::kBoolean); + AddAcceptedNodeInput(olive::nodes::kBoolean); } bool BoolInput::GetBoolAt(double timecode) diff --git a/nodes/inputs/colorinput.cpp b/nodes/inputs/colorinput.cpp index 6c12129d6..ade47dbd2 100644 --- a/nodes/inputs/colorinput.cpp +++ b/nodes/inputs/colorinput.cpp @@ -5,7 +5,7 @@ ColorInput::ColorInput(Node* parent, const QString& id, const QString& name, boo { AddField(new ColorField(this)); - AddNodeInput(olive::nodes::kColor); + AddAcceptedNodeInput(olive::nodes::kColor); } QColor ColorInput::GetColorAt(double timecode) diff --git a/nodes/inputs/comboinput.cpp b/nodes/inputs/comboinput.cpp index b257ea66e..395ff6ebf 100644 --- a/nodes/inputs/comboinput.cpp +++ b/nodes/inputs/comboinput.cpp @@ -7,7 +7,7 @@ ComboInput::ComboInput(Node* parent, const QString& id, const QString& name, boo connect(combo_field, SIGNAL(DataChanged(const QVariant&)), this, SIGNAL(DataChanged(const QVariant&))); AddField(combo_field); - AddNodeInput(olive::nodes::kCombo); + AddAcceptedNodeInput(olive::nodes::kCombo); } void ComboInput::AddItem(const QString &text, const QVariant &data) diff --git a/nodes/inputs/fileinput.cpp b/nodes/inputs/fileinput.cpp index 77d2406bf..4f8d5c808 100644 --- a/nodes/inputs/fileinput.cpp +++ b/nodes/inputs/fileinput.cpp @@ -5,7 +5,7 @@ FileInput::FileInput(Node* parent, const QString& id, const QString& name, bool { AddField(new FileField(this)); - AddNodeInput(olive::nodes::kFile); + AddAcceptedNodeInput(olive::nodes::kFile); } QString FileInput::GetFileAt(double timecode) diff --git a/nodes/inputs/fontinput.cpp b/nodes/inputs/fontinput.cpp index c5a901c7e..ec9ba7077 100644 --- a/nodes/inputs/fontinput.cpp +++ b/nodes/inputs/fontinput.cpp @@ -5,7 +5,7 @@ FontInput::FontInput(Node* parent, const QString& id, const QString& name, bool { AddField(new FontField(this)); - AddNodeInput(olive::nodes::kFont); + AddAcceptedNodeInput(olive::nodes::kFont); } QString FontInput::GetFontAt(double timecode) diff --git a/nodes/inputs/stringinput.cpp b/nodes/inputs/stringinput.cpp index 0cdfa6922..121051ea8 100644 --- a/nodes/inputs/stringinput.cpp +++ b/nodes/inputs/stringinput.cpp @@ -5,7 +5,7 @@ StringInput::StringInput(Node* parent, const QString& id, const QString& name, b { AddField(new StringField(this, rich_text)); - AddNodeInput(olive::nodes::kString); + AddAcceptedNodeInput(olive::nodes::kString); } QString StringInput::GetStringAt(double timecode) diff --git a/nodes/inputs/vecinput.cpp b/nodes/inputs/vecinput.cpp index 2626720f0..f99666c6b 100644 --- a/nodes/inputs/vecinput.cpp +++ b/nodes/inputs/vecinput.cpp @@ -15,15 +15,15 @@ VecInput::VecInput(Node* parent, const QString& id, const QString& name, int val AddField(new DoubleField(this)); } - AddNodeInput(olive::nodes::kFloat); + AddAcceptedNodeInput(olive::nodes::kFloat); if (values > 1) { - AddNodeInput(olive::nodes::kVec2); + AddAcceptedNodeInput(olive::nodes::kVec2); } if (values > 2) { - AddNodeInput(olive::nodes::kVec3); + AddAcceptedNodeInput(olive::nodes::kVec3); } if (values > 3) { - AddNodeInput(olive::nodes::kVec4); + AddAcceptedNodeInput(olive::nodes::kVec4); } } diff --git a/nodes/node.cpp b/nodes/node.cpp index efb3146e3..a2d6b7d9b 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -148,6 +148,11 @@ void Node::AddRow(EffectRow *row) rows.append(row); } +int Node::IndexOfRow(EffectRow *row) +{ + return rows.indexOf(row); +} + void Node::copy_field_keyframes(NodePtr e) { for (int i=0;i Node::GetAllEdges() +{ + QVector edges; + + for (int i=0;iedges()); + } + + return edges; +} + void Node::refresh() {} void Node::FieldChanged() { @@ -310,6 +326,11 @@ void Node::SetExpanded(bool e) expanded_ = e; } +void Node::SetPos(const QPointF &pos) +{ + pos_ = pos; +} + void Node::SetEnabled(bool b) { enabled_ = b; emit EnabledChanged(b); @@ -615,6 +636,11 @@ void Node::setIterations(int i) { iterations = i; } +const QPointF &Node::pos() +{ + return pos_; +} + void Node::process_image(double, uint8_t *, uint8_t *, int){} NodePtr Node::copy(Clip *c) { diff --git a/nodes/node.h b/nodes/node.h index c67a317af..b58fa1d78 100644 --- a/nodes/node.h +++ b/nodes/node.h @@ -127,7 +127,7 @@ public: virtual NodePtr Create(Clip *c) = 0; void AddRow(EffectRow* row); - + int IndexOfRow(EffectRow* row); EffectRow* row(int i); int row_count(); @@ -135,6 +135,8 @@ public: EffectGizmo* gizmo(int i); int gizmo_count(); + QVector GetAllEdges(); + bool IsEnabled(); bool IsExpanded(); @@ -168,6 +170,8 @@ public: int getIterations(); void setIterations(int i); + const QPointF& pos(); + virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size); virtual void process_shader(double timecode, GLTextureCoords&, int iteration); virtual void process_coords(double timecode, GLTextureCoords& coords, int data); @@ -225,6 +229,7 @@ public slots: void FieldChanged(); void SetEnabled(bool b); void SetExpanded(bool e); + void SetPos(const QPointF& pos); signals: void EnabledChanged(bool); private slots: @@ -264,6 +269,8 @@ private: QVector gizmo_dragging_actions_; + QPointF pos_; + // superimpose functions virtual void redraw(double timecode); bool valueHasChanged(double timecode); diff --git a/nodes/nodeedge.cpp b/nodes/nodeedge.cpp new file mode 100644 index 000000000..da21eaac8 --- /dev/null +++ b/nodes/nodeedge.cpp @@ -0,0 +1,21 @@ +#include "nodeedge.h" + +#include + +#include "effects/effectrow.h" + +NodeEdge::NodeEdge(EffectRow *output, EffectRow *input) : + output_(output), + input_(input) +{ +} + +EffectRow *NodeEdge::output() +{ + return output_; +} + +EffectRow *NodeEdge::input() +{ + return input_; +} diff --git a/nodes/nodeedge.h b/nodes/nodeedge.h new file mode 100644 index 000000000..a6b49581d --- /dev/null +++ b/nodes/nodeedge.h @@ -0,0 +1,39 @@ +#ifndef NODEEDGE_H +#define NODEEDGE_H + +#include + +class EffectRow; + +class NodeEdge; +using NodeEdgePtr = std::shared_ptr; + +class NodeEdge +{ +public: + /** + * @brief NodeEdge Constructor + * + * Creates a node edge and stores its two connections. + * + * This should not be used directly. Use the static function EffectRow::ConnectEdge instead. + * + * @param output + * + * Output/from EffectRow that this edge "starts" at + * + * @param input + * + * Input/to EffectRow that this edge "ends" at + */ + NodeEdge(EffectRow* output, EffectRow* input); + + EffectRow* output(); + EffectRow* input(); + +private: + EffectRow* output_; + EffectRow* input_; +}; + +#endif // NODEEDGE_H diff --git a/nodes/nodeplug.cpp b/nodes/nodeplug.cpp deleted file mode 100644 index bb60f8397..000000000 --- a/nodes/nodeplug.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "nodeplug.h" - -NodePlug::NodePlug() -{ - -} - -bool NodePlug::IsConnected() -{ - -} diff --git a/nodes/nodeplug.h b/nodes/nodeplug.h deleted file mode 100644 index 764afc716..000000000 --- a/nodes/nodeplug.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef NODEPLUG_H -#define NODEPLUG_H - - -class NodePlug -{ -public: - NodePlug(); - - bool IsConnected(); -}; - -#endif // NODEPLUG_H diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index 0035e900c..e835a884a 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -3,6 +3,8 @@ NodeMedia::NodeMedia(Clip* c) : Node(c) { + EffectRow* matrix_input = new EffectRow(this, "matrix", tr("Matrix"), true, false); + matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix); } QString NodeMedia::name() diff --git a/olive.pro b/olive.pro index 9d4b6542d..404cee082 100644 --- a/olive.pro +++ b/olive.pro @@ -191,7 +191,6 @@ SOURCES += \ ui/nodeui.cpp \ panels/effectspanel.cpp \ nodes/nodedatatypes.cpp \ - nodes/nodeplug.cpp \ nodes/inputs/boolinput.cpp \ nodes/inputs/comboinput.cpp \ nodes/inputs/colorinput.cpp \ @@ -207,7 +206,9 @@ SOURCES += \ nodes/nodes/nodeshader.cpp \ decoders/ffmpegdecoder.cpp \ decoders/decoder.cpp \ - nodes/node.cpp + nodes/node.cpp \ + nodes/nodeedge.cpp \ + ui/nodeedgeui.cpp HEADERS += \ ui/mainwindow.h \ @@ -353,7 +354,6 @@ HEADERS += \ ui/nodeui.h \ panels/effectspanel.h \ nodes/nodedatatypes.h \ - nodes/nodeplug.h \ nodes/inputs.h \ nodes/inputs/boolinput.h \ nodes/inputs/comboinput.h \ @@ -372,7 +372,9 @@ HEADERS += \ decoders/ffmpegdecoder.h \ decoders/decoder.h \ nodes/node.h \ - timeline/tracktypes.h + timeline/tracktypes.h \ + nodes/nodeedge.h \ + ui/nodeedgeui.h FORMS += diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 73d24bd30..af7a1c89d 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -24,6 +24,8 @@ NodeEditor::NodeEditor(QWidget *parent) : view_.setInteractive(true); view_.setDragMode(QGraphicsView::RubberBandDrag); + + connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(ItemsChanged())); } void NodeEditor::Retranslate() @@ -32,9 +34,9 @@ void NodeEditor::Retranslate() void NodeEditor::LoadEvent() { - nodes_.resize(open_effects_.size()); - if (!open_effects_.isEmpty()) { + QVector added_edges; + Clip* first_clip = open_effects_.first()->GetEffect()->parent_clip; for (int i=0;iSetWidget(effect_ui); node_ui->AddToScene(&scene_); + node_ui->setPos(effect_ui->GetEffect()->pos()); - nodes_[i] = node_ui; + nodes_.append(node_ui); + + // Get node edges + QVector edges = open_effects_.at(i)->GetEffect()->GetAllEdges(); + for (int j=0;jadjust(); + } + + foreach (NodeUI* node, nodes_) { + node->Widget()->GetEffect()->SetPos(node->pos()); + } } diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index a404629c3..fd3ed0967 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -6,6 +6,7 @@ #include "effectspanel.h" #include "ui/nodeview.h" #include "ui/nodeui.h" +#include "ui/nodeedgeui.h" class NodeEditor : public EffectsPanel { Q_OBJECT @@ -22,6 +23,10 @@ private: QGraphicsScene scene_; NodeView view_; QVector nodes_; + QVector edges_; + +private slots: + void ItemsChanged(); }; diff --git a/ui/nodeedgeui.cpp b/ui/nodeedgeui.cpp new file mode 100644 index 000000000..61c0bde8c --- /dev/null +++ b/ui/nodeedgeui.cpp @@ -0,0 +1,67 @@ +#include "nodeedgeui.h" + +#include +#include + +#include "global/math.h" +#include "ui/effectui.h" + +NodeEdgeUI::NodeEdgeUI(NodeEdge* edge) : + edge_(edge), + output_node_(nullptr), + input_node_(nullptr) +{ + setPen(QPen(Qt::white, 2)); +} + +void NodeEdgeUI::adjust() +{ + if (output_node_ == nullptr) { + // Locate the output and input nodes' UIs + + QList all_items = scene()->items(); + + for (int j=0;j(all_items.at(j)); + if (node != nullptr) { + + // Check if this node has the output row + int row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->output()); + + if (row_index > -1) { + output_node_ = node; + output_index_ = row_index; + } + + // Check if this node has the input row + row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->input()); + + if (row_index > -1) { + input_node_ = node; + input_index_ = row_index; + } + } + } + } + + setPath(GetEdgePath(output_node_->pos() + output_node_->GetNodeSocketRects().at(output_index_).center(), + input_node_->pos() + input_node_->GetNodeSocketRects().at(input_index_).center())); +} + +NodeEdge *NodeEdgeUI::edge() +{ + return edge_; +} + +QPainterPath NodeEdgeUI::GetEdgePath(const QPointF &start_pos, const QPointF &end_pos) +{ + double mid_x = double_lerp(start_pos.x(), end_pos.x(), 0.5); + + QPainterPath path_; + path_.moveTo(start_pos); + path_.cubicTo(QPointF(mid_x, start_pos.y()), + QPointF(mid_x, end_pos.y()), + end_pos); + + return path_; +} diff --git a/ui/nodeedgeui.h b/ui/nodeedgeui.h new file mode 100644 index 000000000..3634334d2 --- /dev/null +++ b/ui/nodeedgeui.h @@ -0,0 +1,26 @@ +#ifndef NODEEDGEUI_H +#define NODEEDGEUI_H + +#include + +#include "nodeui.h" + +class NodeEdgeUI : public QGraphicsPathItem { +public: + NodeEdgeUI(NodeEdge* edge); + + static QPainterPath GetEdgePath(const QPointF& start_pos, const QPointF& end_pos); + + void adjust(); + NodeEdge* edge(); + +private: + NodeEdge* edge_; + + NodeUI* output_node_; + int output_index_; + NodeUI* input_node_; + int input_index_; +}; + +#endif // NODEEDGEUI_H diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index de263e07d..6b433bf84 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -12,13 +12,15 @@ #include #include "ui/effectui.h" -#include "global/math.h" +#include "ui/nodeedgeui.h" const int kRoundedRectRadius = 5; const int kNodePlugSize = 12; NodeUI::NodeUI() : central_widget_(nullptr), + proxy_(nullptr), + drag_destination_(nullptr), clicked_socket_(-1) { setFlag(QGraphicsItem::ItemIsMovable, true); @@ -29,7 +31,6 @@ NodeUI::~NodeUI() { if (scene() != nullptr) { scene()->removeItem(proxy_); - scene()->removeItem(this); } } @@ -55,8 +56,8 @@ void NodeUI::Resize(const QSize &s) rectangle.setWidth(rectangle.width() + kNodePlugSize); - path_ = QPainterPath(); - path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius); + drag_path_ = QPainterPath(); + drag_path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius); setRect(rectangle); } @@ -66,6 +67,11 @@ void NodeUI::SetWidget(EffectUI *widget) central_widget_ = widget; } +EffectUI *NodeUI::Widget() +{ + return central_widget_; +} + void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget) @@ -88,7 +94,7 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW painter->setPen(palette.base().color()); } painter->setBrush(palette.window()); - painter->drawPath(path_); + painter->drawPath(drag_path_); } void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) @@ -96,6 +102,7 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) QVector sockets = GetNodeSocketRects(); clicked_socket_ = -1; + drag_destination_ = nullptr; for (int i=0;ipos())) { @@ -106,8 +113,7 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) if (clicked_socket_ > -1) { drag_line_start_ = pos() + sockets.at(clicked_socket_).center(); - drag_line_ = scene()->addPath(GetEdgePath(drag_line_start_, event->scenePos()), - QPen(Qt::white, 2)); + drag_line_ = scene()->addPath(NodeEdgeUI::GetEdgePath(drag_line_start_, event->scenePos())); event->accept(); } else { @@ -119,7 +125,65 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { if (clicked_socket_ > -1) { - drag_line_->setPath(GetEdgePath(drag_line_start_, event->scenePos())); + bool line_is_touching_node = false; + + QPointF drag_line_end = event->scenePos(); + + // Get all the graphics items at this mouse position + QList mouse_items = scene()->items(event->scenePos()); + for (int j=0;j(mouse_items.at(j)); + + if (mouse_node != nullptr) { + // If so, get this node's sockets and loop through them + QVector mouse_nodes_sockets = mouse_node->GetNodeSocketRects(); + for (int i=0;iscenePos() - mouse_node->pos())) { + + // If so, see if the two rows can be connected + + EffectRow* local_row = central_widget_->GetEffect()->row(clicked_socket_); + EffectRow* remote_row = mouse_node->GetRowFromIndex(i); + + // Ensure one is an input and one is an output and whether their data types are compatible + if (local_row->IsNodeInput() != remote_row->IsNodeInput()) { + + // Determine which row is the input and which row is the output + EffectRow* input_row = (local_row->IsNodeInput()) ? local_row : remote_row; + EffectRow* output_row = (local_row->IsNodeInput()) ? remote_row : local_row; + + if (input_row->CanAcceptDataType(output_row->OutputDataType())) { + // This is a valid connection + line_is_touching_node = true; + + // Snap drag line to this socket + drag_line_end = mouse_node->pos() + mouse_nodes_sockets.at(i).center(); + + // Cancel loop since we have a connection now + j = mouse_items.size(); + + // Cache destination row + drag_destination_ = remote_row; + } + } + + break; + } + } + } + } + + if (line_is_touching_node) { + drag_line_->setPen(QPen(Qt::white, 2)); + } else { + drag_line_->setPen(QPen(Qt::gray, 2)); + } + + drag_line_->setPath(NodeEdgeUI::GetEdgePath(drag_line_start_, drag_line_end)); event->accept(); @@ -132,26 +196,16 @@ void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (clicked_socket_ > -1) { scene()->removeItem(drag_line_); - delete drag_line_; event->accept(); + + if (drag_destination_ != nullptr) { + EffectRow::ConnectEdge(central_widget_->GetEffect()->row(clicked_socket_), drag_destination_); + } } else { QGraphicsItem::mouseReleaseEvent(event); } } -QPainterPath NodeUI::GetEdgePath(const QPointF &start_pos, const QPointF &end_pos) -{ - double mid_x = double_lerp(start_pos.x(), end_pos.x(), 0.5); - - QPainterPath path_; - path_.moveTo(start_pos); - path_.cubicTo(QPointF(mid_x, start_pos.y()), - QPointF(mid_x, end_pos.y()), - end_pos); - - return path_; -} - QVector NodeUI::GetNodeSocketRects() { QVector rects; @@ -176,3 +230,14 @@ QVector NodeUI::GetNodeSocketRects() return rects; } + +EffectRow *NodeUI::GetRowFromIndex(int i) +{ + if (central_widget_ != nullptr) { + Node* e = central_widget_->GetEffect(); + if (i < e->row_count()) { + return e->row(i); + } + } + return nullptr; +} diff --git a/ui/nodeui.h b/ui/nodeui.h index 2ac1b0c48..2b1f30696 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -5,6 +5,8 @@ #include class EffectUI; +class EffectRow; +class NodeEdge; class NodeUI : public QGraphicsRectItem { public: @@ -14,24 +16,32 @@ public: void AddToScene(QGraphicsScene* scene); void Resize(const QSize& s); void SetWidget(EffectUI* widget); + EffectUI* Widget(); + + QVector GetNodeSocketRects(); + protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; virtual void mousePressEvent(QGraphicsSceneMouseEvent * event) override; virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + private: - QVector GetNodeSocketRects(); - QPainterPath GetEdgePath(const QPointF& start_pos, const QPointF& end_pos); + EffectRow *GetRowFromIndex(int i); EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; - QPainterPath path_; QGraphicsPathItem* drag_line_; QPointF drag_line_start_; + EffectRow* drag_destination_; + QPainterPath drag_path_; int clicked_socket_; + }; + + #endif // NODEUI_H diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 3998fab9c..94366749d 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -42,9 +42,9 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) void NodeView::mouseReleaseEvent(QMouseEvent *event) { if (!hand_moving_) { - hand_moving_ = false; QGraphicsView::mouseReleaseEvent(event); } + hand_moving_ = false; } void NodeView::wheelEvent(QWheelEvent *event) From e20b1d35c23e88c9d8c136e74f6b01d8e61c1bb9 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 13 Apr 2019 10:12:36 +1000 Subject: [PATCH 120/133] enhancements to node edge creation --- effects/effectrow.cpp | 24 +++++++------- effects/effectrow.h | 2 +- nodes/node.cpp | 4 +-- nodes/node.h | 2 +- panels/nodeeditor.cpp | 77 ++++++++++++++++++++++++++++++++++++------- panels/nodeeditor.h | 8 +++++ ui/nodeui.cpp | 65 +++++++++++++++++++++++++++++++++--- ui/nodeui.h | 3 ++ 8 files changed, 154 insertions(+), 31 deletions(-) diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index e60bea8b1..274ce3479 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -74,9 +74,17 @@ void EffectRow::AddAcceptedNodeInput(olive::nodes::DataType type) void EffectRow::ConnectEdge(EffectRow *output, EffectRow *input) { - Q_ASSERT(output->IsNodeOutput()); - Q_ASSERT(input->IsNodeInput()); + // Make sure one is an output and one is an input + Q_ASSERT(output->IsNodeInput() != input->IsNodeInput()); + // Swap them if necessary + if (input->IsNodeOutput()) { + EffectRow* temp = output; + output = input; + input = temp; + } + + // Inputs can only have one edge, so we disconnect it here if there is one if (!input->node_edges_.isEmpty()) { DisconnectEdge(input->node_edges_.first()); } @@ -87,7 +95,6 @@ void EffectRow::ConnectEdge(EffectRow *output, EffectRow *input) input->node_edges_.append(edge); emit output->EdgesChanged(); - emit input->EdgesChanged(); } void EffectRow::DisconnectEdge(NodeEdgePtr edge) @@ -99,18 +106,11 @@ void EffectRow::DisconnectEdge(NodeEdgePtr edge) input->node_edges_.removeAll(edge); emit output->EdgesChanged(); - emit input->EdgesChanged(); } -QVector EffectRow::edges() +QVector EffectRow::edges() { - QVector edges; - - for (int i=0;i edges(); + QVector edges(); protected: /** diff --git a/nodes/node.cpp b/nodes/node.cpp index a2d6b7d9b..8f2d95deb 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -196,9 +196,9 @@ int Node::gizmo_count() { return gizmos.size(); } -QVector Node::GetAllEdges() +QVector Node::GetAllEdges() { - QVector edges; + QVector edges; for (int i=0;iedges()); diff --git a/nodes/node.h b/nodes/node.h index b58fa1d78..2acf02ef9 100644 --- a/nodes/node.h +++ b/nodes/node.h @@ -135,7 +135,7 @@ public: EffectGizmo* gizmo(int i); int gizmo_count(); - QVector GetAllEdges(); + QVector GetAllEdges(); bool IsEnabled(); bool IsExpanded(); diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index af7a1c89d..7c5eebeba 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -53,12 +53,58 @@ void NodeEditor::LoadEvent() node_ui->setPos(effect_ui->GetEffect()->pos()); nodes_.append(node_ui); + } + } + } + + LoadEdges(); +} + +void NodeEditor::ClearEvent() +{ + foreach (NodeUI* node, nodes_) { + scene_.removeItem(node); + } + + nodes_.clear(); + + ClearEdges(); +} + +void NodeEditor::ClearEdges() +{ + foreach (NodeEdgeUI* edge, edges_) { + scene_.removeItem(edge); + } + + edges_.clear(); + + DisconnectAllRows(); +} + +void NodeEditor::LoadEdges() +{ + if (!open_effects_.isEmpty()) { + QVector added_edges; + + Clip* first_clip = open_effects_.first()->GetEffect()->parent_clip; + + for (int i=0;iGetEffect(); + + if (n->parent_clip == first_clip) { + + // Connect all rows to this + for (int j=0;jrow_count();j++) { + ConnectRow(n->row(j)); + } // Get node edges - QVector edges = open_effects_.at(i)->GetEffect()->GetAllEdges(); + QVector edges = n->GetAllEdges(); for (int j=0;jWidget()->GetEffect()->SetPos(node->pos()); } } + +void NodeEditor::ReloadEdges() +{ + qDebug() << "reload edges called"; + + ClearEdges(); + LoadEdges(); +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index fd3ed0967..4de6d70de 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -24,9 +24,17 @@ private: NodeView view_; QVector nodes_; QVector edges_; + QVector connected_rows_; + + void ClearEdges(); + void LoadEdges(); + + void ConnectRow(EffectRow* row); + void DisconnectAllRows(); private slots: void ItemsChanged(); + void ReloadEdges(); }; diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 6b433bf84..17180ec3f 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -112,9 +112,47 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) } if (clicked_socket_ > -1) { - drag_line_start_ = pos() + sockets.at(clicked_socket_).center(); + + // See if this socket already has an edge connected + QVector edges = central_widget_->GetEffect()->row(clicked_socket_)->edges(); + if (!edges.isEmpty()) { + + NodeEdge* e = edges.last().get(); + + EffectRow* other_row = (e->input()->GetParentEffect() == central_widget_->GetEffect()) ? + e->output() : e->input(); + + Node* other_node = other_row->GetParentEffect(); + + int other_row_index = other_node->IndexOfRow(other_row); + + NodeUI* other_node_ui = FindUIFromNode(other_node); + + // Start an edge at the opposite end + drag_line_start_ = other_node_ui->pos() + other_node_ui->GetNodeSocketRects().at(other_row_index).center(); + + drag_source_ = other_row; + + // Disconnect the existing edge, and treat our dynamic one as an edit of that one + EffectRow::DisconnectEdge(edges.last()); + + } else { + + // Start a new edge here + drag_line_start_ = pos() + sockets.at(clicked_socket_).center(); + + drag_source_ = central_widget_->GetEffect()->row(clicked_socket_); + + } + drag_line_ = scene()->addPath(NodeEdgeUI::GetEdgePath(drag_line_start_, event->scenePos())); + if (!edges.isEmpty()) { + drag_line_->setPen(QPen(Qt::white, 2)); + } else { + drag_line_->setPen(QPen(Qt::gray, 2)); + } + event->accept(); } else { QGraphicsItem::mousePressEvent(event); @@ -135,8 +173,8 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) // See if the graphics item here is a node NodeUI* mouse_node = dynamic_cast(mouse_items.at(j)); - if (mouse_node != nullptr) { + // If so, get this node's sockets and loop through them QVector mouse_nodes_sockets = mouse_node->GetNodeSocketRects(); for (int i=0;iGetEffect()->row(clicked_socket_); + EffectRow* local_row = drag_source_; EffectRow* remote_row = mouse_node->GetRowFromIndex(i); // Ensure one is an input and one is an output and whether their data types are compatible @@ -199,7 +237,7 @@ void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) event->accept(); if (drag_destination_ != nullptr) { - EffectRow::ConnectEdge(central_widget_->GetEffect()->row(clicked_socket_), drag_destination_); + EffectRow::ConnectEdge(drag_source_, drag_destination_); } } else { QGraphicsItem::mouseReleaseEvent(event); @@ -241,3 +279,22 @@ EffectRow *NodeUI::GetRowFromIndex(int i) } return nullptr; } + +NodeUI *NodeUI::FindUIFromNode(Node* n) +{ + QList all_items = scene()->items(); + + for (int j=0;j(all_items.at(j)); + if (node != nullptr) { + + // Check if this node has the specified + + if (node->Widget()->GetEffect() == n) { + return node; + } + } + } + + return nullptr; +} diff --git a/ui/nodeui.h b/ui/nodeui.h index 2b1f30696..b8cd53c5a 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -7,6 +7,7 @@ class EffectUI; class EffectRow; class NodeEdge; +class Node; class NodeUI : public QGraphicsRectItem { public: @@ -29,12 +30,14 @@ protected: private: EffectRow *GetRowFromIndex(int i); + NodeUI* FindUIFromNode(Node* n); EffectUI* central_widget_; QGraphicsProxyWidget* proxy_; QGraphicsPathItem* drag_line_; QPointF drag_line_start_; + EffectRow* drag_source_; EffectRow* drag_destination_; QPainterPath drag_path_; From 6bf62017eabc1c7d444d20e1ab6e7ab8d2b53c5d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 13 Apr 2019 15:57:09 +1000 Subject: [PATCH 121/133] right click menu in node view for adding nodes --- global/global.cpp | 107 ++++++++++++++ global/global.h | 255 +++++++++++++++++--------------- main.cpp | 2 +- nodes/node.cpp | 44 +----- nodes/nodes/nodeimageoutput.cpp | 3 +- nodes/nodes/nodemedia.cpp | 3 + panels/effectcontrols.cpp | 110 +------------- panels/effectcontrols.h | 5 - panels/effectspanel.cpp | 6 + panels/effectspanel.h | 1 + panels/nodeeditor.cpp | 23 ++- panels/nodeeditor.h | 4 +- panels/timeline.cpp | 17 +-- panels/timeline.h | 1 - ui/nodeui.cpp | 7 +- ui/nodeview.cpp | 8 + ui/nodeview.h | 5 + ui/timelineview.cpp | 2 +- 18 files changed, 309 insertions(+), 294 deletions(-) diff --git a/global/global.cpp b/global/global.cpp index a98e82772..c17e8ec91 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -43,11 +43,13 @@ #include "dialogs/newsequencedialog.h" #include "dialogs/loaddialog.h" #include "dialogs/autocutsilencedialog.h" +#include "effects/effectloaders.h" #include "project/loadthread.h" #include "project/savethread.h" #include "timeline/sequence.h" #include "ui/mediaiconservice.h" #include "ui/mainwindow.h" +#include "ui/menu.h" #include "ui/updatenotification.h" #include "undo/undostack.h" @@ -450,6 +452,40 @@ void OliveGlobal::PasteInternal(Sequence *s, bool insert) } } +void OliveGlobal::EffectMenuAction(QAction *q) +{ + ComboAction* ca = new ComboAction(); + + NodeType node_type = static_cast(q->data().toInt()); + Node* n = olive::node_library[node_type].get(); + + for (int i=0;itype() == n->subtype()) { + if (n->type() == EFFECT_TYPE_TRANSITION) { + if (c->opening_transition == nullptr) { + ca->append(new AddTransitionCommand(c, + nullptr, + nullptr, + node_type, + olive::config.default_transition_length)); + } + if (c->closing_transition == nullptr) { + ca->append(new AddTransitionCommand(nullptr, + c, + nullptr, + node_type, + olive::config.default_transition_length)); + } + } else { + ca->append(new AddEffectCommand(c, nullptr, node_type)); + } + } + } + olive::undo_stack.push(ca); + update_ui(true); +} + void OliveGlobal::ImportProject(const QString &fn) { LoadProject(fn, false); @@ -644,6 +680,77 @@ bool OliveGlobal::CheckForActiveSequence(bool show_msg) return true; } +void OliveGlobal::ShowEffectMenu(EffectType type, olive::TrackType subtype, const QVector selected_clips) +{ + effect_menu_selected_clips = selected_clips; + + olive::effects_loaded.lock(); + + Menu effects_menu(olive::MainWindow); + effects_menu.setToolTipsVisible(true); + + for (int i=0;itype() == type && node->subtype() == subtype) { + QAction* action = new QAction(&effects_menu); + action->setText(node->name()); + action->setData(i); + if (!node->description().isEmpty()) { + action->setToolTip(node->description()); + } + + QMenu* parent = &effects_menu; + if (!node->category().isEmpty()) { + bool found = false; + for (int j=0;jmenu() != nullptr) { + if (action->menu()->title() == node->category()) { + parent = action->menu(); + found = true; + break; + } + } + } + if (!found) { + parent = new Menu(&effects_menu); + parent->setToolTipsVisible(true); + parent->setTitle(node->category()); + + bool found = false; + for (int i=0;itext() > node->category()) { + effects_menu.insertMenu(comp_action, parent); + found = true; + break; + } + } + if (!found) effects_menu.addMenu(parent); + } + } + + bool found = false; + for (int i=0;iactions().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); + } + } + + olive::effects_loaded.unlock(); + + connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(EffectMenuAction(QAction*))); + effects_menu.exec(QCursor::pos()); +} + void OliveGlobal::undo() { // workaround to prevent crash (and also users should never need to do this) if (!Timeline::IsImporting()) { diff --git a/global/global.h b/global/global.h index a8a0ee47f..f4434cf79 100644 --- a/global/global.h +++ b/global/global.h @@ -34,23 +34,23 @@ * A resource for various global functions used throughout Olive. */ class OliveGlobal : public QObject { - Q_OBJECT + Q_OBJECT public: - /** + /** * @brief OliveGlobal Constructor * * Creates Olive Global object. Also sets some default runtime settings and the application name. */ - OliveGlobal(); + OliveGlobal(); - /** + /** * @brief Returns the file dialog filter used when interfacing with Olive project files. * * @return The file filter string used by QFileDialog to limit the files shown to Olive (*.ove) files. */ - const QString& get_project_file_filter(); + const QString& get_project_file_filter(); - /** + /** * @brief Change the current active project filename * * Triggered to change the current active project filename. Call this before calling any internal project @@ -63,18 +63,18 @@ public: * The URL of the project file to work with. Can be an empty string, in which case Olive will treat the project * as an unsaved project. */ - void update_project_filename(const QString& s); + void update_project_filename(const QString& s); - /** + /** * @brief Check whether an auto-recovery file exists and ask the user if they want to load it. * * Usually called on initialization. Checks if an auto-recovery file exists (meaning the last session of Olive * didn't close correctly). If it finds one, asks the user if they want to load it. If so, loads the auto-recovery * project. */ - void check_for_autorecovery_file(); + void check_for_autorecovery_file(); - /** + /** * @brief Get whether the project is currently being rendered or not. Useful for determining whether to treat the * render as online or offline. * @@ -82,9 +82,9 @@ public: * * TRUE if the project is being exported, FALSE if not. */ - bool is_exporting(); + bool is_exporting(); - /** + /** * @brief Set the application state depending on if the user is exporting a video * * Some background functions shouldn't run while Olive is exporting a video. This function will disable/enable them @@ -100,9 +100,9 @@ public: * * **TRUE** if Olive is about to export a video. **FALSE** if Olive has finished exporting. */ - void set_export_state(bool rendering); + void set_export_state(bool rendering); - /** + /** * @brief Set the application's "modified" state * * Primarily controls whether the application prompts the user to save the project upon closing or not. Also @@ -113,9 +113,9 @@ public: * * TRUE if the project has been modified, FALSE if it has not. */ - void set_modified(bool modified); + void set_modified(bool modified); - /** + /** * @brief Get application's current "modified" state * * Currently just a wrapper around MainWindow::isWindowModified(), but use this instead in case it changes. @@ -125,9 +125,9 @@ public: * * TRUE if the project has been modified since the last save. */ - bool is_modified(); + bool is_modified(); - /** + /** * @brief Set a project to load just after launching * * Called by main() if Olive was called with a project file as a running argument. Sets up Olive to load the @@ -137,54 +137,54 @@ public: * * The URL of the project file to load. */ - void load_project_on_launch(const QString& s); + void load_project_on_launch(const QString& s); - /** + /** * @brief Retrieves the URL of the config file containing the autorecovery projects * @return The URL as a string */ - QString get_recent_project_list_file(); + QString get_recent_project_list_file(); - /** + /** * @brief (Re)load translation file from olive::config */ - void load_translation_from_config(); + void load_translation_from_config(); - /** + /** * @brief Set native UI styling on a given widget * * @param w * * The widget to set styling on. */ - static void SetNativeStyling(QWidget* w); + static void SetNativeStyling(QWidget* w); - /** + /** * @brief Adds a project URL to the recent projects list * * @param url * * The project URL to add */ - void add_recent_project(const QString& url); + void add_recent_project(const QString& url); - /** + /** * @brief Load recent projects from file * * Should be called on application startup. */ - void load_recent_projects(); + void load_recent_projects(); - /** + /** * @brief Total count of recent projects * * @return * * Number of recent projects in the list */ - int recent_project_count(); + int recent_project_count(); - /** + /** * @brief Get the recent project at a given index * * @param index @@ -193,18 +193,18 @@ public: * * The recent project at index */ - const QString& recent_project(int index); + const QString& recent_project(int index); - /** + /** * @brief Retrieves the filename of the autorecovery file to save to during this session * * @return * * A URL pointing to the autorecovery file */ - const QString& get_autorecovery_filename(); + const QString& get_autorecovery_filename(); - /** + /** * @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not * * Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in @@ -214,29 +214,32 @@ public: * * TRUE if there is an active Sequence, FALSE if not. */ - bool CheckForActiveSequence(bool show_msg = true); + bool CheckForActiveSequence(bool show_msg = true); + + + void ShowEffectMenu(EffectType type, olive::TrackType subtype, const QVector selected_clips); public slots: - /** + /** * @brief Undo user's last action */ - void undo(); + void undo(); - /** + /** * @brief Redo user's last action */ - void redo(); + void redo(); - /** + /** * @brief Paste contents of clipboard * * Pastes contents of clipboard. Seeing as several types of data can be copied into the clipboard, this * function will automatically determine what type of data is in the clipboard and paste it in the correct * location (e.g. clip data will go to the Timeline, effect data will go to Effect Controls). */ - void paste(); + void paste(); - /** + /** * @brief Paste contents of clipboard, making space for it when possible * * Pastes contents of clipboard (same as paste()). If the clipboard contains clip data, the clips are cut at the @@ -244,25 +247,25 @@ public slots: * semi-non-destructive as a result (as opposed to paste() overwriting clips). If the clipboard contains effect * data, the functionality is identical to paste(). */ - void paste_insert(); + void paste_insert(); - /** + /** * @brief Create new project. * * Confirms whether the current project can be closed, and if so, clears all current project data and resets * program state. Standard `File > New` behavior. */ - void new_project(); + void new_project(); - /** + /** * @brief Open a project from file. * * Confirms whether the current project can be closed, and if so, shows an open file dialog to allow the user to * select a project file and then triggers a project load with it. */ - void OpenProject(); + void OpenProject(); - /** + /** * @brief Import project from file * * Imports an Olive project into the current project, effectively merging them. @@ -271,9 +274,9 @@ public slots: * * The filename of the project to import. */ - void ImportProject(const QString& fn); + void ImportProject(const QString& fn); - /** + /** * @brief Open recent project from list * * Triggers a project load from the internal recent projects list. @@ -282,9 +285,9 @@ public slots: * * Index in the list of the project fille to load */ - void open_recent(int index); + void open_recent(int index); - /** + /** * @brief Shows a save file dialog and saves the project as the resulting filename * * Shows a save file dialog for the user to save their current project as a different filename from the current @@ -294,9 +297,9 @@ public slots: * if a user is closing an unsaved project, clicks "Yes" to save, we know if they actually saved or not and won't * continue closing the project if they didn't. */ - bool save_project_as(); + bool save_project_as(); - /** + /** * @brief Saves the current project to file * * If the project has been saved already, this function will overwrite the project file with the current project @@ -306,9 +309,9 @@ public slots: * value of save_project_as(). Useful if the user closing an unsaved project, clicks "Yes" to save, we know if they * actually saved or not and won't continue closing the project if they didn't. */ - bool save_project(); + bool save_project(); - /** + /** * @brief Determine whether the current project can be closed. * * Queried any time the current project is going to be closed (e.g. starting a new project, loading a project, @@ -320,93 +323,93 @@ public slots: * returns **TRUE**. If it does and the user clicks YES, this returns the result of save_project(). If the user * clicks NO, this returns **TRUE**. If the user clicks CANCEL, this returns **FALSE**. */ - bool can_close_project(); + bool can_close_project(); - /** + /** * @brief Opens the NewSequenceDialog to create a new Sequence */ - void open_new_sequence_dialog(); + void open_new_sequence_dialog(); - /** + /** * @brief Open a file dialog for importing files into the project */ - void open_import_dialog(); + void open_import_dialog(); - /** + /** * @brief Open the Export dialog to trigger an export of the current sequence. */ - void open_export_dialog(); + void open_export_dialog(); - /** + /** * @brief Open the About Olive dialog. */ - void open_about_dialog(); + void open_about_dialog(); - /** + /** * @brief Open the Debug Log window. */ - void open_debug_log(); + void open_debug_log(); - /** + /** * @brief Open the Speed/Duration dialog. */ - void open_speed_dialog(); + void open_speed_dialog(); - /** + /** * @brief Open the auto-cut silence dialog. */ - void open_autocut_silence_dialog(); + void open_autocut_silence_dialog(); - /** + /** * @brief Open the Action Search overlay. */ - void open_action_search(); + void open_action_search(); - /** + /** * @brief Clears the current undo stack. * * Clears all current commands in the undo stack. Mostly used for debugging. */ - void clear_undo_stack(); + void clear_undo_stack(); - /** + /** * @brief Function called when Olive has finished starting up * * Sets up some last things for Olive that must be run after Olive has completed initialization. If a project was * loaded as a command line argument, it's loaded here. */ - void finished_initialize(); + void finished_initialize(); - /** + /** * @brief Save an auto-recovery file of the current project. * * Call this function to save the current state of the project as an auto-recovery project. Called regularly by * `autorecovery_timer`. */ - void save_autorecovery_file(); + void save_autorecovery_file(); - /** + /** * @brief Opens the Preferences dialog */ - void open_preferences(); + void open_preferences(); - /** + /** * @brief Clear the recent projects list * * Also saves the cleared recent projects to the config file making it permanent. */ - void clear_recent_projects(); + void clear_recent_projects(); - /** + /** * @brief Slot for when the primary sequence has changed. * * Usually by opening a sequence or bringing a corresponding * Timeline widget on top. */ - void PrimarySequenceChanged(); + void PrimarySequenceChanged(); private: - /** + /** * @brief Internal function to handle loading a project from file * * Start loading a project. Doesn't check if the current project can be closed, doesn't check if the project exists. @@ -422,9 +425,9 @@ private: * beside the original project file so that it does not overwrite the original and so that the user is not working * on the autorecovery project in Olive's application data directory. */ - void OpenProjectWorker(QString fn, bool autorecovery); + void OpenProjectWorker(QString fn, bool autorecovery); - /** + /** * @brief Create a LoadDialog and start a LoadThread to load data from a project * * Loads data from an Olive project file creating a LoadDialog to show visual information and a LoadThread to load @@ -450,49 +453,49 @@ private: * TRUE if the current project should be closed before opening, FALSE if the project should be imported into the * currently open one. */ - void LoadProject(const QString& fn, bool autorecovery); + void LoadProject(const QString& fn, bool autorecovery); - /** + /** * @brief Indiscriminately clear the project without prompting the user * * Will clear the entire project without prompting to save. This is dangerous, use new_project() instead for * anything initiated by the user. */ - void ClearProject(); + void ClearProject(); - /** + /** * @brief Saves current recent project list to the configuration file * * This should be called whenever the recent projects change so the changes can be persistent. */ - void save_recent_projects(); + void save_recent_projects(); - /** + /** * @brief Internal pasting function */ - void PasteInternal(Sequence* s, bool insert); + void PasteInternal(Sequence* s, bool insert); - /** + /** * @brief File filter used for any file dialogs relating to Olive project files. */ - QString project_file_filter; + QString project_file_filter; - /** + /** * @brief Regular interval to save an auto-recovery project. */ - QTimer autorecovery_timer; + QTimer autorecovery_timer; - /** + /** * @brief Internal variable set to **TRUE** by main() if a project file was set as an argument */ - bool enable_load_project_on_init; + bool enable_load_project_on_init; - /** + /** * @brief Internal translator object that interfaces with the currently loaded language file */ - std::unique_ptr translator; + std::unique_ptr translator; - /** + /** * @brief Internal variable for whether the project has changed since the last autorecovery * * Set by set_modified(), which should be called alongside any change made to the project file and is "unset" when @@ -500,45 +503,57 @@ private: * prevents an autorecovery file saving multiple times if the project hasn't actually changed since the last * autorecovery, but still hasn't been saved into the original file yet. */ - bool changed_since_last_autorecovery; + bool changed_since_last_autorecovery; - /** + /** * @brief Internal variable for rendering state (set by set_rendering_state() and accessed by is_rendering() ). */ - bool rendering_; + bool rendering_; - /** + /** * @brief Internal variable for the filename to the autorecovery project file */ - QString autorecovery_filename; + QString autorecovery_filename; - /** + /** * @brief Internal list of recent projects */ - QStringList recent_projects; + QStringList recent_projects; + + /** + * @brief Internal array of selected clips that a menu created by ShowEffectMenu will act on + */ + QVector effect_menu_selected_clips; private slots: - + /** + * @brief Receiver for a menu initiated by ShowEffectMenu + * + * Adds the selected effect/node to the clips in + * + * @param q + */ + void EffectMenuAction(QAction* q); }; namespace olive { - /** +/** * @brief Object resource for various global functions used throughout Olive */ - extern std::unique_ptr Global; +extern std::unique_ptr Global; - /** +/** * @brief Currently active project filename * * Filename for the currently active project. Empty means the file has not * been saved yet. */ - extern QString ActiveProjectFilename; +extern QString ActiveProjectFilename; - /** +/** * @brief Current application name */ - extern QString AppName; +extern QString AppName; } #endif // OLIVEGLOBAL_H diff --git a/main.cpp b/main.cpp index e387d92e0..7b4cfeeee 100644 --- a/main.cpp +++ b/main.cpp @@ -147,5 +147,5 @@ int main(int argc, char *argv[]) { w.showMaximized(); } - return a.exec(); + return a.exec(); } diff --git a/nodes/node.cpp b/nodes/node.cpp index 8f2d95deb..17c11c6c6 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -47,6 +47,7 @@ #include "global/debug.h" #include "global/path.h" #include "ui/mainwindow.h" +#include "ui/menu.h" #include "global/math.h" #include "global/clipboard.h" #include "global/config.h" @@ -55,51 +56,10 @@ #include "rendering/shadergenerators.h" #include "global/timing.h" #include "nodes/nodes.h" +#include "effects/effectloaders.h" QVector olive::node_library; -/* -NodePtr Node::Create(Clip* c) { - // must be an internal effect - switch (em->internal) { - case kTransformEffect: return std::make_shared(c, em); - case kTextInput: return std::make_shared(c, em); - case kTimecodeEffect: return std::make_shared(c, em); - case kSolidInput: return std::make_shared(c, em); - case kNoiseInput: return std::make_shared(c, em); - case kVolumeEffect: return std::make_shared(c, em); - case kPanEffect: return std::make_shared(c, em); - case kToneInput: return std::make_shared(c, em); - case kShakeEffect: return std::make_shared(c, em); - case kCornerPinEffect: return std::make_shared(c, em); - case kFillLeftRightEffect: return std::make_shared(c, em); - case kVstEffect: return std::make_shared(c, em); - case kRichTextInput: return std::make_shared(c, em); - case kMediaInput: return std::make_shared(c, em); - case kShaderEffect: return std::make_shared(c, em); - case kImageOutput: return std::make_shared(c, em); - default: - qCritical() << "Invalid effect data"; - QMessageBox::critical(olive::MainWindow, - QCoreApplication::translate("Effect", "Invalid effect"), - QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be " - "corrupt. Try reinstalling it or Olive.").arg(em->name)); - return nullptr; - } -} -*/ - -/* -const EffectMeta* Node::GetInternalMeta(int internal_id, int type) { - for (int i=0;iAddAcceptedNodeInput(olive::nodes::kTexture); } QString NodeImageOutput::name() diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index e835a884a..f5b3e3272 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -5,6 +5,9 @@ NodeMedia::NodeMedia(Clip* c) : { EffectRow* matrix_input = new EffectRow(this, "matrix", tr("Matrix"), true, false); matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix); + + EffectRow* texture_output = new EffectRow(this, "texture", tr("Texture"), true, false); + texture_output->SetOutputDataType(olive::nodes::kTexture); } QString NodeMedia::name() diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index bc81c16a2..b06716e8b 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -49,6 +49,7 @@ #include "ui/icons.h" #include "global/clipboard.h" #include "global/config.h" +#include "global/global.h" #include "ui/timelineheader.h" #include "ui/keyframeview.h" #include "ui/resizablescrollbar.h" @@ -90,36 +91,6 @@ void EffectControls::set_zoom(bool in) { } } -void EffectControls::menu_select(QAction* q) { - ComboAction* ca = new ComboAction(); - for (int i=0;itype() == effect_menu_subtype) { - NodeType node_type = static_cast(q->data().toInt()); - if (effect_menu_type == EFFECT_TYPE_TRANSITION) { - if (c->opening_transition == nullptr) { - ca->append(new AddTransitionCommand(c, - nullptr, - nullptr, - node_type, - olive::config.default_transition_length)); - } - if (c->closing_transition == nullptr) { - ca->append(new AddTransitionCommand(nullptr, - c, - nullptr, - node_type, - olive::config.default_transition_length)); - } - } else { - ca->append(new AddEffectCommand(c, nullptr, node_type)); - } - } - } - olive::undo_stack.push(ca); - update_ui(true); -} - void EffectControls::update_keyframes() { for (int i=0;ivisible_in, zoom, keyframeView->width()); } -void EffectControls::show_effect_menu(EffectType type, olive::TrackType subtype) { - effect_menu_type = type; - effect_menu_subtype = subtype; - - olive::effects_loaded.lock(); - - Menu effects_menu(this); - effects_menu.setToolTipsVisible(true); - - for (int i=0;itype() == type && node->subtype() == subtype) { - QAction* action = new QAction(&effects_menu); - action->setText(node->name()); - action->setData(i); - if (!node->description().isEmpty()) { - action->setToolTip(node->description()); - } - - QMenu* parent = &effects_menu; - if (!node->category().isEmpty()) { - bool found = false; - for (int j=0;jmenu() != nullptr) { - if (action->menu()->title() == node->category()) { - parent = action->menu(); - found = true; - break; - } - } - } - if (!found) { - parent = new Menu(&effects_menu); - parent->setToolTipsVisible(true); - parent->setTitle(node->category()); - - bool found = false; - for (int i=0;itext() > node->category()) { - effects_menu.insertMenu(comp_action, parent); - found = true; - break; - } - } - if (!found) effects_menu.addMenu(parent); - } - } - - bool found = false; - for (int i=0;iactions().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); - } - } - - olive::effects_loaded.unlock(); - - connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*))); - effects_menu.exec(QCursor::pos()); -} - void EffectControls::UpdateTitle() { if (selected_clips_.isEmpty()) { setWindowTitle(panel_name + tr("(none)")); @@ -445,19 +345,19 @@ bool EffectControls::focused() } void EffectControls::video_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeVideo); + olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, olive::kTypeVideo, selected_clips_); } void EffectControls::audio_effect_click() { - show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeAudio); + olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, olive::kTypeAudio, selected_clips_); } void EffectControls::video_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeVideo); + olive::Global->ShowEffectMenu(EFFECT_TYPE_TRANSITION, olive::kTypeVideo, selected_clips_); } void EffectControls::audio_transition_click() { - show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeAudio); + olive::Global->ShowEffectMenu(EFFECT_TYPE_TRANSITION, olive::kTypeAudio, selected_clips_); } void EffectControls::resizeEvent(QResizeEvent*) { diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index 1387744f4..94f41eb72 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -76,8 +76,6 @@ public: public slots: void update_keyframes(); private slots: - void menu_select(QAction* q); - void video_effect_click(); void audio_effect_click(); void video_transition_click(); @@ -94,14 +92,11 @@ protected: virtual void ClearEvent() override; virtual void LoadEvent() override; private: - void show_effect_menu(EffectType type, olive::TrackType subtype); void load_keyframes(); void UpdateTitle(); void setup_ui(); - int effect_menu_type; - olive::TrackType effect_menu_subtype; QString panel_name; QWidget* video_effect_area; diff --git a/panels/effectspanel.cpp b/panels/effectspanel.cpp index 3c76db66b..fab4f34ef 100644 --- a/panels/effectspanel.cpp +++ b/panels/effectspanel.cpp @@ -16,6 +16,8 @@ EffectsPanel::~EffectsPanel() } void EffectsPanel::Clear(bool clear_cache) { + AboutToClearEvent(); + // clear existing clips deselect_all_effects(nullptr); @@ -205,6 +207,10 @@ void EffectsPanel::copy(bool del) { } } +void EffectsPanel::AboutToClearEvent() +{ +} + void EffectsPanel::DeleteEffect(ComboAction* ca, Node* effect_ref) { if (effect_ref->type() == EFFECT_TYPE_EFFECT) { diff --git a/panels/effectspanel.h b/panels/effectspanel.h index e3d52bd5e..1100dd94f 100644 --- a/panels/effectspanel.h +++ b/panels/effectspanel.h @@ -25,6 +25,7 @@ public slots: void cut(); void copy(bool del = false); protected: + virtual void AboutToClearEvent(); virtual void ClearEvent(); virtual void LoadEvent(); diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 7c5eebeba..387559a94 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -6,6 +6,8 @@ #include #include +#include "global/global.h" + NodeEditor::NodeEditor(QWidget *parent) : EffectsPanel(parent), view_(&scene_) @@ -26,6 +28,12 @@ NodeEditor::NodeEditor(QWidget *parent) : view_.setDragMode(QGraphicsView::RubberBandDrag); connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(ItemsChanged())); + connect(&view_, SIGNAL(RequestContextMenu()), this, SLOT(ContextMenu())); +} + +NodeEditor::~NodeEditor() +{ + Clear(true); } void NodeEditor::Retranslate() @@ -60,10 +68,10 @@ void NodeEditor::LoadEvent() LoadEdges(); } -void NodeEditor::ClearEvent() +void NodeEditor::AboutToClearEvent() { foreach (NodeUI* node, nodes_) { - scene_.removeItem(node); + delete node; } nodes_.clear(); @@ -75,6 +83,7 @@ void NodeEditor::ClearEdges() { foreach (NodeEdgeUI* edge, edges_) { scene_.removeItem(edge); + delete edge; } edges_.clear(); @@ -150,8 +159,14 @@ void NodeEditor::ItemsChanged() void NodeEditor::ReloadEdges() { - qDebug() << "reload edges called"; - ClearEdges(); LoadEdges(); } + +void NodeEditor::ContextMenu() +{ + if (!open_effects_.isEmpty()) { + Clip* c = open_effects_.first()->GetEffect()->parent_clip; + olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, c->type(), {c}); + } +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index 4de6d70de..b6cb01316 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -12,12 +12,13 @@ class NodeEditor : public EffectsPanel { Q_OBJECT public: NodeEditor(QWidget* parent = nullptr); + virtual ~NodeEditor() override; virtual void Retranslate() override; protected: virtual void LoadEvent() override; - virtual void ClearEvent() override; + virtual void AboutToClearEvent() override; private: QGraphicsScene scene_; @@ -35,6 +36,7 @@ private: private slots: void ItemsChanged(); void ReloadEdges(); + void ContextMenu(); }; diff --git a/panels/timeline.cpp b/panels/timeline.cpp index e23f7e125..0cb4c0f1b 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -873,22 +873,24 @@ void Timeline::transition_tool_click() { Menu transition_menu(this); + transition_menu.addAction(tr("Video Transitions"))->setEnabled(false); + for (int i=0;itype() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) { + if (node != nullptr && node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) { QAction* a = transition_menu.addAction(node->name()); - a->setObjectName("v"); a->setData(i); } } transition_menu.addSeparator(); + transition_menu.addAction(tr("Audio Transitions"))->setEnabled(false); + for (int i=0;itype() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) { + if (node != nullptr && node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeAudio) { QAction* a = transition_menu.addAction(node->name()); - a->setObjectName("a"); a->setData(i); } } @@ -902,13 +904,6 @@ void Timeline::transition_tool_click() { void Timeline::transition_menu_select(QAction* a) { transition_tool_meta = static_cast(a->data().toInt()); - - if (a->objectName() == "v") { - transition_tool_side = olive::kTypeVideo; - } else { - transition_tool_side = olive::kTypeAudio; - } - timeline_area->setCursor(Qt::CrossCursor); olive::timeline::current_tool = olive::timeline::TIMELINE_TOOL_TRANSITION; toolTransitionButton->setChecked(true); diff --git a/panels/timeline.h b/panels/timeline.h index ba47ca69d..1830c6bd4 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -115,7 +115,6 @@ public: Clip* transition_tool_open_clip; Clip* transition_tool_close_clip; NodeType transition_tool_meta; - olive::TrackType transition_tool_side; // hand tool variables bool hand_moving; diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 17180ec3f..08ef0b877 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -29,8 +29,8 @@ NodeUI::NodeUI() : NodeUI::~NodeUI() { - if (scene() != nullptr) { - scene()->removeItem(proxy_); + if (proxy_ != nullptr) { + proxy_->setParentItem(nullptr); } } @@ -164,6 +164,7 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (clicked_socket_ > -1) { bool line_is_touching_node = false; + drag_destination_ = nullptr; QPointF drag_line_end = event->scenePos(); @@ -234,6 +235,8 @@ void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (clicked_socket_ > -1) { scene()->removeItem(drag_line_); + delete drag_line_; + event->accept(); if (drag_destination_ != nullptr) { diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 94366749d..b3c33f413 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -4,6 +4,9 @@ #include #include +#include "ui/menu.h" +#include "nodes/node.h" + NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent), hand_moving_(false) @@ -20,6 +23,11 @@ void NodeView::mousePressEvent(QMouseEvent *event) if (event->button() == Qt::MidButton) { hand_moving_ = true; drag_start_ = event->pos(); + } else if (event->button() == Qt::RightButton && scene()->itemAt(mapToScene(event->pos()), QTransform()) == nullptr) { + + // If the user clicked with the right button on empty space, show the context menu + emit RequestContextMenu(); + } else { QGraphicsView::mousePressEvent(event); } diff --git a/ui/nodeview.h b/ui/nodeview.h index bd751aa4c..648aef23d 100644 --- a/ui/nodeview.h +++ b/ui/nodeview.h @@ -3,11 +3,16 @@ #include +#include "timeline/tracktypes.h" + class NodeView : public QGraphicsView { Q_OBJECT public: NodeView(QGraphicsScene *scene, QWidget* parent = nullptr); +signals: + void RequestContextMenu(); + protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 403410de0..ed6bc07f5 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -2741,7 +2741,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // cursor is hovering over a clip // check if the clip and transition are both the same sign (meaning video/audio are the same) - if (track_list_->type() == ParentTimeline()->transition_tool_side) { + if (track_list_->type() == olive::node_library[ParentTimeline()->transition_tool_meta]->subtype()) { // the range within which the transition tool will assume the user wants to make a shared transition // between two clips rather than just one transition on one clip From 336ef69d738c71d0c4bc49ea0de7f8562e640501 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 13 Apr 2019 20:28:56 +1000 Subject: [PATCH 122/133] allow multiple connections to be made from an output --- ui/nodeui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 08ef0b877..e58906bad 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -115,12 +115,13 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) // See if this socket already has an edge connected QVector edges = central_widget_->GetEffect()->row(clicked_socket_)->edges(); - if (!edges.isEmpty()) { + if (!edges.isEmpty() && edges.last()->input()->GetParentEffect() == central_widget_->GetEffect()) { NodeEdge* e = edges.last().get(); - EffectRow* other_row = (e->input()->GetParentEffect() == central_widget_->GetEffect()) ? - e->output() : e->input(); + // If this node is the edge's "input", then we'll drag that instead of creating a new edge + + EffectRow* other_row = e->output(); Node* other_node = other_row->GetParentEffect(); @@ -136,6 +137,8 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) // Disconnect the existing edge, and treat our dynamic one as an edit of that one EffectRow::DisconnectEdge(edges.last()); + + } else { // Start a new edge here From 1b9a963eeb029f9d87cd5a2a1219dbf612cda11c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 14 Apr 2019 11:13:00 +1000 Subject: [PATCH 123/133] derive clips, tracks, and sequences from the same base --- dialogs/speeddialog.cpp | 14 ++-- effects/internal/richtexteffect.cpp | 4 +- effects/internal/shakeeffect.cpp | 1 - effects/internal/solideffect.cpp | 1 - effects/internal/texteffect.cpp | 1 - effects/internal/timecodeeffect.cpp | 2 +- effects/internal/toneeffect.cpp | 4 +- effects/transition.cpp | 21 +++-- effects/transition.h | 6 +- nodes/node.cpp | 18 +++-- nodes/node.h | 11 ++- olive.pro | 6 +- panels/timeline.cpp | 8 -- rendering/renderfunctions.cpp | 4 +- timeline/clip.cpp | 117 ++++++++++++++++++---------- timeline/clip.h | 24 ++++-- timeline/sequence.cpp | 22 +++++- timeline/sequence.h | 9 ++- timeline/timelineobject.cpp | 29 +++++++ timeline/timelineobject.h | 32 ++++++++ timeline/track.cpp | 34 ++++++++ timeline/track.h | 10 ++- ui/effectui.cpp | 8 +- ui/effectui.h | 2 +- ui/timelinearea.cpp | 2 - ui/viewerwidget.cpp | 2 +- 26 files changed, 284 insertions(+), 108 deletions(-) create mode 100644 timeline/timelineobject.cpp create mode 100644 timeline/timelineobject.h diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index 9b7cad23e..9c82c2705 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -113,7 +113,7 @@ int SpeedDialog::exec() { } if (process_video) { - double media_frame_rate = c->media_frame_rate(); + double media_frame_rate = c->MediaFrameRate(); // get "default" frame rate" if (enable_frame_rate) { @@ -193,7 +193,7 @@ void SpeedDialog::percent_update() { // get frame rate if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { - double clip_fr = c->media_frame_rate() * percent->value(); + double clip_fr = c->MediaFrameRate() * percent->value(); if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); @@ -237,7 +237,7 @@ void SpeedDialog::duration_update() { // get frame rate if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { - double clip_fr = c->media_frame_rate() * clip_pc; + double clip_fr = c->MediaFrameRate() * clip_pc; if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); @@ -278,7 +278,7 @@ void SpeedDialog::frame_rate_update() { if (c->type() == olive::kTypeVideo) { // what would the new speed be based on this frame rate - double new_clip_speed = frame_rate->value() / c->media_frame_rate(); + double new_clip_speed = frame_rate->value() / c->MediaFrameRate(); if (!got_pc_val) { pc_val = new_clip_speed; got_pc_val = true; @@ -420,8 +420,8 @@ void SpeedDialog::accept() { } if (c->type() == olive::kTypeVideo) { if (qIsNaN(cached_fr)) { - cached_fr = c->media_frame_rate(); - } else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) { + cached_fr = c->MediaFrameRate(); + } else if (!qFuzzyCompare(cached_fr, c->MediaFrameRate())) { can_change_all = false; break; } @@ -432,7 +432,7 @@ void SpeedDialog::accept() { for (int i=0;itype() == olive::kTypeVideo) { - set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple); + set_speed(ca, c, frame_rate->value() / c->MediaFrameRate(), ripple->isChecked(), earliest_point, longest_ripple); } else if (can_change_all) { set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple); } diff --git a/effects/internal/richtexteffect.cpp b/effects/internal/richtexteffect.cpp index 43d7ec399..2aa46ed2f 100644 --- a/effects/internal/richtexteffect.cpp +++ b/effects/internal/richtexteffect.cpp @@ -143,8 +143,8 @@ void RichTextEffect::redraw(double timecode) double scroll_progress = 0; if (auto_scroll_dir != SCROLL_OFF) { - double clip_length_secs = double(parent_clip->length()) / parent_clip->media_frame_rate(); - scroll_progress = (timecode - double(parent_clip->clip_in()) / parent_clip->media_frame_rate()) / clip_length_secs; + double clip_length_secs = double(parent_clip->length()) / parent_clip->MediaFrameRate(); + scroll_progress = (timecode - double(parent_clip->clip_in()) / parent_clip->MediaFrameRate()) / clip_length_secs; } if (auto_scroll_dir == SCROLL_OFF || auto_scroll_dir == SCROLL_LEFT || auto_scroll_dir == SCROLL_RIGHT) { diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index 7d623dd19..d7bdbfbca 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -28,7 +28,6 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" #include "timeline/clip.h" -#include "timeline/sequence.h" #include "panels/timeline.h" #include "global/debug.h" diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 452ea1e93..73ea6344b 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -28,7 +28,6 @@ #include #include "timeline/clip.h" -#include "timeline/sequence.h" const int SMPTE_BARS = 7; const int SMPTE_STRIP_COUNT = 3; diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 14515ad68..9d3d49c09 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -36,7 +36,6 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" #include "timeline/clip.h" -#include "timeline/sequence.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" #include "ui/blur.h" diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 32a892cdf..40c809846 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -118,7 +118,7 @@ void TimecodeEffect::redraw(double timecode) { olive::config.timecode_view, sequence->frame_rate); } else { - double media_rate = parent_clip->media_frame_rate(); + double media_rate = parent_clip->MediaFrameRate(); display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(qRound(timecode * media_rate), olive::config.timecode_view, media_rate); diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index b0b66fa46..96dbaf4db 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -25,7 +25,7 @@ #define TONE_TYPE_SINE 0 #include "timeline/clip.h" -#include "timeline/sequence.h" +#include "rendering/audio.h" ToneEffect::ToneEffect(Clip* c) : Node(c), sinX(INT_MIN) { type_val = new ComboInput(this, "type", tr("Type")); @@ -90,7 +90,7 @@ void ToneEffect::process_audio(double timecode_start, double timecode = timecode_start+(interval*i); float tone_sample = qSin((2*M_PI*sinX*freq_val->GetDoubleAt(timecode)) - /parent_clip->track()->sequence()->audio_frequency) + /current_audio_freq()) *log_volume(amount_val->GetDoubleAt(timecode)*0.01); for (int j=0;jSetDisplayType(LabelSlider::FrameNumber); if (parent_clip != nullptr) { - length_field->SetFrameRate(parent_clip->track()->sequence() == nullptr ? - parent_clip->cached_frame_rate() : parent_clip->track()->sequence()->frame_rate); + length_field->SetFrameRate(GetParentClip()->track()->sequence() == nullptr ? + GetParentClip()->cached_frame_rate() : GetParentClip()->SequenceFrameRate()); } connect(length_field, SIGNAL(Changed()), this, SLOT(UpdateMaximumLength())); } -NodePtr Transition::copy(Clip *c) { +Clip *Transition::GetParentClip() +{ + return static_cast(parent_clip); +} + +NodePtr Transition::copy(TimelineObject *c) { NodePtr node = Node::copy(c); static_cast(node.get())->set_length(get_true_length()); @@ -86,8 +91,8 @@ int Transition::get_length() { } Clip* Transition::get_opened_clip() { - if (parent_clip->opening_transition.get() == this) { - return parent_clip; + if (GetParentClip()->opening_transition.get() == this) { + return GetParentClip(); } else if (secondary_clip != nullptr && secondary_clip->opening_transition.get() == this) { return secondary_clip; } @@ -95,8 +100,8 @@ Clip* Transition::get_opened_clip() { } Clip* Transition::get_closed_clip() { - if (parent_clip->closing_transition.get() == this) { - return parent_clip; + if (GetParentClip()->closing_transition.get() == this) { + return GetParentClip(); } else if (secondary_clip != nullptr && secondary_clip->closing_transition.get() == this) { return secondary_clip; } @@ -131,7 +136,7 @@ TransitionPtr Transition::CreateFromMeta(Clip* c, Clip* s) { void Transition::UpdateMaximumLength() { // Get the maximum area this transition can occupy on the clip - long maximum_length = GetMaximumEmptySpaceOnClip(parent_clip); + long maximum_length = GetMaximumEmptySpaceOnClip(GetParentClip()); // If this clip is a shared transition, get the maximum area this can occupy on the other clip too if (secondary_clip != nullptr) { diff --git a/effects/transition.h b/effects/transition.h index 7be393c5c..00309c42a 100644 --- a/effects/transition.h +++ b/effects/transition.h @@ -24,6 +24,8 @@ #include "nodes/node.h" #include "nodes/inputs.h" +class Clip; + enum TransitionType { kTransitionNone, kTransitionOpening, @@ -38,7 +40,9 @@ class Transition : public Node { public: Transition(Clip* c); - virtual NodePtr copy(Clip* c) override; + virtual NodePtr copy(TimelineObject* c) override; + + Clip* GetParentClip(); Clip* secondary_clip; diff --git a/nodes/node.cpp b/nodes/node.cpp index 17c11c6c6..a43f9b5b5 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -60,7 +60,7 @@ QVector olive::node_library; -Node::Node(Clip* c) : +Node::Node(TimelineObject* c) : parent_clip(c), flags_(0), shader_program_(nullptr), @@ -182,6 +182,7 @@ void Node::delete_self() { } void Node::move_up() { + /* int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == 0) { return; @@ -194,9 +195,11 @@ void Node::move_up() { olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); + */ } void Node::move_down() { + /* int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == parent_clip->effects.size()-1) { return; @@ -209,6 +212,7 @@ void Node::move_down() { olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); + */ } void Node::save_to_file() { @@ -603,7 +607,7 @@ const QPointF &Node::pos() void Node::process_image(double, uint8_t *, uint8_t *, int){} -NodePtr Node::copy(Clip *c) { +NodePtr Node::copy(TimelineObject *c) { NodePtr copy = Create(c); copy->SetEnabled(IsEnabled()); copy_field_keyframes(copy); @@ -671,8 +675,8 @@ GLuint Node::process_superimpose(QOpenGLContext* ctx, double timecode) { bool dimensions_changed = false; bool redrew_image = false; - int width = parent_clip->media_width(); - int height = parent_clip->media_height(); + int width = parent_clip->MediaWidth(); + int height = parent_clip->MediaHeight(); if (width != img.width() || height != img.height()) { img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied); @@ -806,10 +810,10 @@ void Node::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& pro projection, QRect(0, 0, - parent_clip->track()->sequence()->width, - parent_clip->track()->sequence()->height)); + parent_clip->SequenceWidth(), + parent_clip->SequenceHeight())); - g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->track()->sequence()->height-screen_pos.y()); + g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->SequenceHeight()-screen_pos.y()); } } diff --git a/nodes/node.h b/nodes/node.h index 2acf02ef9..77db3fb8f 100644 --- a/nodes/node.h +++ b/nodes/node.h @@ -48,8 +48,7 @@ class EffectGizmo; class KeyframeDataChange; -class Clip; -using ClipPtr = std::shared_ptr; +class TimelineObject; class Node; using NodePtr = std::shared_ptr; @@ -112,10 +111,10 @@ struct GLTextureCoords { class Node : public QObject { Q_OBJECT public: - Node(Clip *c); + Node(TimelineObject *c); ~Node(); - Clip* parent_clip; + TimelineObject* parent_clip; virtual QString name() = 0; virtual QString id() = 0; @@ -124,7 +123,7 @@ public: virtual EffectType type() = 0; virtual olive::TrackType subtype() = 0; virtual bool IsCreatable(); - virtual NodePtr Create(Clip *c) = 0; + virtual NodePtr Create(TimelineObject *c) = 0; void AddRow(EffectRow* row); int IndexOfRow(EffectRow* row); @@ -142,7 +141,7 @@ public: virtual void refresh(); - virtual NodePtr copy(Clip* c); + virtual NodePtr copy(TimelineObject* c); void copy_field_keyframes(NodePtr e); virtual void load(QXmlStreamReader& stream); diff --git a/olive.pro b/olive.pro index 404cee082..472fa9430 100644 --- a/olive.pro +++ b/olive.pro @@ -208,7 +208,8 @@ SOURCES += \ decoders/decoder.cpp \ nodes/node.cpp \ nodes/nodeedge.cpp \ - ui/nodeedgeui.cpp + ui/nodeedgeui.cpp \ + timeline/timelineobject.cpp HEADERS += \ ui/mainwindow.h \ @@ -374,7 +375,8 @@ HEADERS += \ nodes/node.h \ timeline/tracktypes.h \ nodes/nodeedge.h \ - ui/nodeedgeui.h + ui/nodeedgeui.h \ + timeline/timelineobject.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 0cb4c0f1b..c2c51493a 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1108,11 +1108,3 @@ void Timeline::visibility_changed_slot(bool visibility) emit SequenceChanged(sequence_); } } - -void olive::timeline::MultiplyTrackSizesByDPI() -{ - kTrackDefaultHeight *= QApplication::desktop()->devicePixelRatio(); - kTrackMinHeight *= QApplication::desktop()->devicePixelRatio(); - kTrackHeightIncrement *= QApplication::desktop()->devicePixelRatio(); - kTimelineLabelFixedWidth *= QApplication::desktop()->devicePixelRatio(); -} diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index ba1e94778..f3767bfcd 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -375,8 +375,8 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { GLuint textureID = 0; // store video source dimensions - int video_width = c->media_width(); - int video_height = c->media_height(); + int video_width = c->MediaWidth(); + int video_height = c->MediaHeight(); // prepare framebuffers for backend drawing operations if (c->fbo.isEmpty()) { diff --git a/timeline/clip.cpp b/timeline/clip.cpp index 57e705fb3..aa17d29d6 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -297,7 +297,7 @@ void Clip::Save(QXmlStreamWriter &stream) if (this == transition->secondary_clip) { // if so, just save a reference to the other clip stream.writeAttribute("shared", - QString::number(transition->parent_clip->load_id)); + QString::number(transition->GetParentClip()->load_id)); } else { // otherwise save the whole transition transition->save(stream); @@ -426,16 +426,89 @@ long Clip::length() { return timeline_out_ - timeline_in_; } -double Clip::media_frame_rate() { +int Clip::MediaWidth() { + if (media_ != nullptr) { + switch (media_->get_type()) { + case MEDIA_TYPE_FOOTAGE: + { + const FootageStream* ms = media_stream(); + if (ms != nullptr) return ms->video_width; + break; + } + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = media_->to_sequence().get(); + return s->width; + } + } + } + + if (track() != nullptr) { + return SequenceWidth(); + } + + return 0; +} + +int Clip::MediaHeight() { + if (media_ != nullptr) { + switch (media_->get_type()) { + case MEDIA_TYPE_FOOTAGE: + { + const FootageStream* ms = media_stream(); + if (ms != nullptr) return ms->video_height; + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = media_->to_sequence().get(); + return s->height; + } + } + } + + if (track() != nullptr) { + return SequenceHeight(); + } + + return 0; +} + +double Clip::MediaFrameRate() { Q_ASSERT(type() == olive::kTypeVideo); + if (media_ != nullptr) { double rate = media_->get_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; } - if (track() != nullptr) return track()->sequence()->frame_rate; + + if (track() != nullptr) { + return SequenceFrameRate(); + } + return qSNaN(); } +int Clip::SequenceWidth() +{ + return track()->sequence()->width; +} + +int Clip::SequenceHeight() +{ + return track()->sequence()->height; +} + +double Clip::SequenceFrameRate() +{ + return track()->sequence()->frame_rate; +} + +long Clip::SequencePlayhead() +{ + return track()->sequence()->playhead; +} + long Clip::media_length() { if (this->track() != nullptr) { double fr = this->track()->sequence()->frame_rate; @@ -467,44 +540,6 @@ long Clip::media_length() { return 0; } -int Clip::media_width() { - if (media_ == nullptr && track() != nullptr) return track()->sequence()->width; - switch (media_->get_type()) { - case MEDIA_TYPE_FOOTAGE: - { - const FootageStream* ms = media_stream(); - if (ms != nullptr) return ms->video_width; - if (track() != nullptr) return track()->sequence()->width; - break; - } - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = media_->to_sequence().get(); - return s->width; - } - } - return 0; -} - -int Clip::media_height() { - if (media_ == nullptr && track() != nullptr) return track()->sequence()->height; - switch (media_->get_type()) { - case MEDIA_TYPE_FOOTAGE: - { - const FootageStream* ms = media_stream(); - if (ms != nullptr) return ms->video_height; - if (track() != nullptr) return track()->sequence()->height; - } - break; - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = media_->to_sequence().get(); - return s->height; - } - } - return 0; -} - void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points) { if (change_timeline_points) { track()->sequence()->MoveClip(this, diff --git a/timeline/clip.h b/timeline/clip.h index 30ea217ce..b264b0d0c 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -39,6 +39,7 @@ #include "marker.h" #include "nodes/nodegraph.h" #include "selection.h" +#include "timelineobject.h" class Track; @@ -48,10 +49,13 @@ struct ClipSpeed { bool maintain_audio_pitch; }; -class Clip { +class Clip; +using ClipPtr = std::shared_ptr; + +class Clip : public TimelineObject { public: Clip(Track *s); - ~Clip(); + virtual ~Clip() override; ClipPtr copy(Track *s); void Save(QXmlStreamWriter& stream); @@ -71,9 +75,17 @@ public: Media* media(); FootageStream* media_stream(); int media_stream_index(); - int media_width(); - int media_height(); - double media_frame_rate(); + + virtual int MediaWidth() override; + virtual int MediaHeight() override; + virtual double MediaFrameRate() override; + + virtual int SequenceWidth() override; + virtual int SequenceHeight() override; + virtual double SequenceFrameRate() override; + + virtual long SequencePlayhead() override; + long media_length(); void set_media(Media* m, int s); @@ -181,8 +193,6 @@ private: Cacher cacher; long cacher_frame; - NodeGraph pipeline_; - QVector markers; QColor color_; bool open_; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 75340e974..4c8ee1b0b 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -100,6 +100,26 @@ void Sequence::Save(QXmlStreamWriter &stream) stream.writeEndElement(); } +int Sequence::SequenceWidth() +{ + return width; +} + +int Sequence::SequenceHeight() +{ + return height; +} + +double Sequence::SequenceFrameRate() +{ + return frame_rate; +} + +long Sequence::SequencePlayhead() +{ + return playhead; +} + long Sequence::GetEndFrame() { long end_frame = 0; @@ -271,7 +291,7 @@ void Sequence::MoveClip(Clip *c, ComboAction *ca, long iin, long iout, long icli if (c->closing_transition != nullptr && c->closing_transition->secondary_clip != nullptr - && c->closing_transition->parent_clip->timeline_in() != iout) { + && c->closing_transition->GetParentClip()->timeline_in() != iout) { // separate transition ca->append(new SetPointer(reinterpret_cast(&c->closing_transition->secondary_clip), nullptr)); ca->append(new AddTransitionCommand(nullptr, diff --git a/timeline/sequence.h b/timeline/sequence.h index 94051ae83..8d9dde9c0 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -29,8 +29,9 @@ #include "selection.h" #include "tracklist.h" #include "ghost.h" +#include "timelineobject.h" -class Sequence : public QObject { +class Sequence : public TimelineObject { Q_OBJECT public: Sequence(); @@ -45,6 +46,12 @@ public: int audio_frequency; int audio_layout; + virtual int SequenceWidth() override; + virtual int SequenceHeight() override; + virtual double SequenceFrameRate() override; + + virtual long SequencePlayhead() override; + long GetEndFrame(); QVector GetAllClips(); TrackList* GetTrackList(olive::TrackType type); diff --git a/timeline/timelineobject.cpp b/timeline/timelineobject.cpp new file mode 100644 index 000000000..5331e8939 --- /dev/null +++ b/timeline/timelineobject.cpp @@ -0,0 +1,29 @@ +#include "timelineobject.h" + +TimelineObject::TimelineObject() +{ +} + +TimelineObject::~TimelineObject() +{ +} + +int TimelineObject::MediaWidth() +{ + return SequenceWidth(); +} + +int TimelineObject::MediaHeight() +{ + return SequenceHeight(); +} + +double TimelineObject::MediaFrameRate() +{ + return SequenceFrameRate(); +} + +NodeGraph *TimelineObject::pipeline() +{ + return &pipeline_; +} diff --git a/timeline/timelineobject.h b/timeline/timelineobject.h new file mode 100644 index 000000000..56e07220c --- /dev/null +++ b/timeline/timelineobject.h @@ -0,0 +1,32 @@ +#ifndef TIMELINEOBJECT_H +#define TIMELINEOBJECT_H + +#include "nodes/nodegraph.h" + +/** + * @brief The TimelineObject class + * + * A base class for Clip, Sequence, and Track to allow compatibility between each of them and any effects nodes. + */ +class TimelineObject : public QObject +{ +public: + TimelineObject(); + virtual ~TimelineObject(); + + virtual int MediaWidth(); + virtual int MediaHeight(); + virtual double MediaFrameRate(); + + virtual int SequenceWidth() = 0; + virtual int SequenceHeight() = 0; + virtual double SequenceFrameRate() = 0; + + virtual long SequencePlayhead() = 0; + + NodeGraph* pipeline(); +private: + NodeGraph pipeline_; +}; + +#endif // TIMELINEOBJECT_H diff --git a/timeline/track.cpp b/timeline/track.cpp index 9273b67ed..bc0e36f9a 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -1,5 +1,8 @@ #include "track.h" +#include +#include + #include "timeline/clip.h" #include "timeline/tracklist.h" #include "timeline/sequence.h" @@ -9,6 +12,8 @@ int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; int olive::timeline::kTrackHeightIncrement = 10; +int olive::timeline::kTimelineLabelFixedWidth = 200; + Track::Track(TrackList* parent, olive::TrackType type) : parent_(parent), type_(type), @@ -206,6 +211,26 @@ bool Track::ContainsClip(Clip *c) return false; } +int Track::SequenceWidth() +{ + return sequence()->width; +} + +int Track::SequenceHeight() +{ + return sequence()->height; +} + +double Track::SequenceFrameRate() +{ + return sequence()->frame_rate; +} + +long Track::SequencePlayhead() +{ + return sequence()->playhead; +} + Track *Track::Previous() { int index = Index(); @@ -433,3 +458,12 @@ void Track::SetLocked(bool locked) { locked_ = locked; } + +void olive::timeline::MultiplyTrackSizesByDPI() +{ + kTrackDefaultHeight *= qApp->fontMetrics().height() * 3; + kTrackMinHeight *= qApp->fontMetrics().height(); + kTrackHeightIncrement *= qApp->fontMetrics().height() / 2; + kTimelineLabelFixedWidth *= QApplication::desktop()->devicePixelRatio(); +} + diff --git a/timeline/track.h b/timeline/track.h index 310cd69e9..d3d3a4efb 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -8,6 +8,8 @@ #include "tracktypes.h" #include "undo/comboaction.h" #include "timeline/selection.h" +#include "timelineobject.h" +#include "clip.h" class Sequence; class Transition; @@ -37,7 +39,7 @@ namespace olive { class TrackList; -class Track : public QObject +class Track : public TimelineObject { Q_OBJECT public: @@ -68,6 +70,12 @@ public: Clip* GetClipFromPoint(long point); bool ContainsClip(Clip* c); + virtual int SequenceWidth() override; + virtual int SequenceHeight() override; + virtual double SequenceFrameRate() override; + + virtual long SequencePlayhead() override; + Track* Previous(); Track* Next(); Track* Sibling(int diff); diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 62a1cdb35..7d1113d74 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -43,7 +43,7 @@ EffectUI::EffectUI(Node* e) : Transition* t = static_cast(e); // Since effects can have two clip attachments, find out which one is selected - Clip* selected_clip = t->parent_clip; + Clip* selected_clip = t->GetParentClip(); bool both_selected = false; // Check if this is a shared transition @@ -54,12 +54,12 @@ EffectUI::EffectUI(Node* e) : selected_clip = t->secondary_clip; - if (t->parent_clip->IsSelected()) { + if (t->GetParentClip()->IsSelected()) { // Both clips are selected both_selected = true; } - } else if (!t->parent_clip->IsSelected()) { + } else if (!t->GetParentClip()->IsSelected()) { // Neither are selected, but the naming scheme (no "opening" or "closing" modifier) will be the same both_selected = true; @@ -267,7 +267,7 @@ void EffectUI::UpdateFromEffect() } } -bool EffectUI::IsAttachedToClip(Clip *c) +bool EffectUI::IsAttachedToClip(TimelineObject *c) { if (GetEffect()->parent_clip == c) { return true; diff --git a/ui/effectui.h b/ui/effectui.h index 9eb12201c..5372540dc 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -131,7 +131,7 @@ public: * * True is an Effect from this Clip is already attached to this EffectUI. */ - bool IsAttachedToClip(Clip* c); + bool IsAttachedToClip(TimelineObject* c); void SetNodeParent(NodeUI* parent); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index e0c795ab8..a9d1c9c9a 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -5,8 +5,6 @@ #include "panels/timeline.h" #include "global/config.h" -int olive::timeline::kTimelineLabelFixedWidth = 200; - TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignment) : timeline_(timeline), track_list_(nullptr), diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 5fb7dd057..6670a028c 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -344,7 +344,7 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) { x_movement, y_movement, get_timecode(gizmos->parent_clip, - gizmos->parent_clip->track()->sequence()->playhead), + gizmos->parent_clip->SequencePlayhead()), done); gizmo_x_mvmt += x_movement; From de69558ee563d9368efeef1e80aa8ee39994b7f6 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 14 Apr 2019 12:47:19 +1000 Subject: [PATCH 124/133] Revert "derive clips, tracks, and sequences from the same base" This reverts commit 1b9a963eeb029f9d87cd5a2a1219dbf612cda11c. --- dialogs/speeddialog.cpp | 14 ++-- effects/internal/richtexteffect.cpp | 4 +- effects/internal/shakeeffect.cpp | 1 + effects/internal/solideffect.cpp | 1 + effects/internal/texteffect.cpp | 1 + effects/internal/timecodeeffect.cpp | 2 +- effects/internal/toneeffect.cpp | 4 +- effects/transition.cpp | 21 ++--- effects/transition.h | 6 +- nodes/node.cpp | 18 ++--- nodes/node.h | 11 +-- olive.pro | 6 +- panels/timeline.cpp | 8 ++ rendering/renderfunctions.cpp | 4 +- timeline/clip.cpp | 117 ++++++++++------------------ timeline/clip.h | 24 ++---- timeline/sequence.cpp | 22 +----- timeline/sequence.h | 9 +-- timeline/timelineobject.cpp | 29 ------- timeline/timelineobject.h | 32 -------- timeline/track.cpp | 34 -------- timeline/track.h | 10 +-- ui/effectui.cpp | 8 +- ui/effectui.h | 2 +- ui/timelinearea.cpp | 2 + ui/viewerwidget.cpp | 2 +- 26 files changed, 108 insertions(+), 284 deletions(-) delete mode 100644 timeline/timelineobject.cpp delete mode 100644 timeline/timelineobject.h diff --git a/dialogs/speeddialog.cpp b/dialogs/speeddialog.cpp index 9c82c2705..9b7cad23e 100644 --- a/dialogs/speeddialog.cpp +++ b/dialogs/speeddialog.cpp @@ -113,7 +113,7 @@ int SpeedDialog::exec() { } if (process_video) { - double media_frame_rate = c->MediaFrameRate(); + double media_frame_rate = c->media_frame_rate(); // get "default" frame rate" if (enable_frame_rate) { @@ -193,7 +193,7 @@ void SpeedDialog::percent_update() { // get frame rate if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { - double clip_fr = c->MediaFrameRate() * percent->value(); + double clip_fr = c->media_frame_rate() * percent->value(); if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); @@ -237,7 +237,7 @@ void SpeedDialog::duration_update() { // get frame rate if (frame_rate->isEnabled() && c->type() == olive::kTypeVideo) { - double clip_fr = c->MediaFrameRate() * clip_pc; + double clip_fr = c->media_frame_rate() * clip_pc; if (got_fr) { if (!qIsNaN(fr_val) && !qFuzzyCompare(fr_val, clip_fr)) { fr_val = qSNaN(); @@ -278,7 +278,7 @@ void SpeedDialog::frame_rate_update() { if (c->type() == olive::kTypeVideo) { // what would the new speed be based on this frame rate - double new_clip_speed = frame_rate->value() / c->MediaFrameRate(); + double new_clip_speed = frame_rate->value() / c->media_frame_rate(); if (!got_pc_val) { pc_val = new_clip_speed; got_pc_val = true; @@ -420,8 +420,8 @@ void SpeedDialog::accept() { } if (c->type() == olive::kTypeVideo) { if (qIsNaN(cached_fr)) { - cached_fr = c->MediaFrameRate(); - } else if (!qFuzzyCompare(cached_fr, c->MediaFrameRate())) { + cached_fr = c->media_frame_rate(); + } else if (!qFuzzyCompare(cached_fr, c->media_frame_rate())) { can_change_all = false; break; } @@ -432,7 +432,7 @@ void SpeedDialog::accept() { for (int i=0;itype() == olive::kTypeVideo) { - set_speed(ca, c, frame_rate->value() / c->MediaFrameRate(), ripple->isChecked(), earliest_point, longest_ripple); + set_speed(ca, c, frame_rate->value() / c->media_frame_rate(), ripple->isChecked(), earliest_point, longest_ripple); } else if (can_change_all) { set_speed(ca, c, frame_rate->value() / cached_fr, ripple->isChecked(), earliest_point, longest_ripple); } diff --git a/effects/internal/richtexteffect.cpp b/effects/internal/richtexteffect.cpp index 2aa46ed2f..43d7ec399 100644 --- a/effects/internal/richtexteffect.cpp +++ b/effects/internal/richtexteffect.cpp @@ -143,8 +143,8 @@ void RichTextEffect::redraw(double timecode) double scroll_progress = 0; if (auto_scroll_dir != SCROLL_OFF) { - double clip_length_secs = double(parent_clip->length()) / parent_clip->MediaFrameRate(); - scroll_progress = (timecode - double(parent_clip->clip_in()) / parent_clip->MediaFrameRate()) / clip_length_secs; + double clip_length_secs = double(parent_clip->length()) / parent_clip->media_frame_rate(); + scroll_progress = (timecode - double(parent_clip->clip_in()) / parent_clip->media_frame_rate()) / clip_length_secs; } if (auto_scroll_dir == SCROLL_OFF || auto_scroll_dir == SCROLL_LEFT || auto_scroll_dir == SCROLL_RIGHT) { diff --git a/effects/internal/shakeeffect.cpp b/effects/internal/shakeeffect.cpp index d7bdbfbca..7d623dd19 100644 --- a/effects/internal/shakeeffect.cpp +++ b/effects/internal/shakeeffect.cpp @@ -28,6 +28,7 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" #include "timeline/clip.h" +#include "timeline/sequence.h" #include "panels/timeline.h" #include "global/debug.h" diff --git a/effects/internal/solideffect.cpp b/effects/internal/solideffect.cpp index 73ea6344b..452ea1e93 100644 --- a/effects/internal/solideffect.cpp +++ b/effects/internal/solideffect.cpp @@ -28,6 +28,7 @@ #include #include "timeline/clip.h" +#include "timeline/sequence.h" const int SMPTE_BARS = 7; const int SMPTE_STRIP_COUNT = 3; diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 9d3d49c09..14515ad68 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -36,6 +36,7 @@ #include "ui/labelslider.h" #include "ui/collapsiblewidget.h" #include "timeline/clip.h" +#include "timeline/sequence.h" #include "ui/comboboxex.h" #include "ui/colorbutton.h" #include "ui/blur.h" diff --git a/effects/internal/timecodeeffect.cpp b/effects/internal/timecodeeffect.cpp index 40c809846..32a892cdf 100644 --- a/effects/internal/timecodeeffect.cpp +++ b/effects/internal/timecodeeffect.cpp @@ -118,7 +118,7 @@ void TimecodeEffect::redraw(double timecode) { olive::config.timecode_view, sequence->frame_rate); } else { - double media_rate = parent_clip->MediaFrameRate(); + double media_rate = parent_clip->media_frame_rate(); display_timecode = prepend_text->GetStringAt(timecode) + frame_to_timecode(qRound(timecode * media_rate), olive::config.timecode_view, media_rate); diff --git a/effects/internal/toneeffect.cpp b/effects/internal/toneeffect.cpp index 96dbaf4db..b0b66fa46 100644 --- a/effects/internal/toneeffect.cpp +++ b/effects/internal/toneeffect.cpp @@ -25,7 +25,7 @@ #define TONE_TYPE_SINE 0 #include "timeline/clip.h" -#include "rendering/audio.h" +#include "timeline/sequence.h" ToneEffect::ToneEffect(Clip* c) : Node(c), sinX(INT_MIN) { type_val = new ComboInput(this, "type", tr("Type")); @@ -90,7 +90,7 @@ void ToneEffect::process_audio(double timecode_start, double timecode = timecode_start+(interval*i); float tone_sample = qSin((2*M_PI*sinX*freq_val->GetDoubleAt(timecode)) - /current_audio_freq()) + /parent_clip->track()->sequence()->audio_frequency) *log_volume(amount_val->GetDoubleAt(timecode)*0.01); for (int j=0;jSetDisplayType(LabelSlider::FrameNumber); if (parent_clip != nullptr) { - length_field->SetFrameRate(GetParentClip()->track()->sequence() == nullptr ? - GetParentClip()->cached_frame_rate() : GetParentClip()->SequenceFrameRate()); + length_field->SetFrameRate(parent_clip->track()->sequence() == nullptr ? + parent_clip->cached_frame_rate() : parent_clip->track()->sequence()->frame_rate); } connect(length_field, SIGNAL(Changed()), this, SLOT(UpdateMaximumLength())); } -Clip *Transition::GetParentClip() -{ - return static_cast(parent_clip); -} - -NodePtr Transition::copy(TimelineObject *c) { +NodePtr Transition::copy(Clip *c) { NodePtr node = Node::copy(c); static_cast(node.get())->set_length(get_true_length()); @@ -91,8 +86,8 @@ int Transition::get_length() { } Clip* Transition::get_opened_clip() { - if (GetParentClip()->opening_transition.get() == this) { - return GetParentClip(); + if (parent_clip->opening_transition.get() == this) { + return parent_clip; } else if (secondary_clip != nullptr && secondary_clip->opening_transition.get() == this) { return secondary_clip; } @@ -100,8 +95,8 @@ Clip* Transition::get_opened_clip() { } Clip* Transition::get_closed_clip() { - if (GetParentClip()->closing_transition.get() == this) { - return GetParentClip(); + if (parent_clip->closing_transition.get() == this) { + return parent_clip; } else if (secondary_clip != nullptr && secondary_clip->closing_transition.get() == this) { return secondary_clip; } @@ -136,7 +131,7 @@ TransitionPtr Transition::CreateFromMeta(Clip* c, Clip* s) { void Transition::UpdateMaximumLength() { // Get the maximum area this transition can occupy on the clip - long maximum_length = GetMaximumEmptySpaceOnClip(GetParentClip()); + long maximum_length = GetMaximumEmptySpaceOnClip(parent_clip); // If this clip is a shared transition, get the maximum area this can occupy on the other clip too if (secondary_clip != nullptr) { diff --git a/effects/transition.h b/effects/transition.h index 00309c42a..7be393c5c 100644 --- a/effects/transition.h +++ b/effects/transition.h @@ -24,8 +24,6 @@ #include "nodes/node.h" #include "nodes/inputs.h" -class Clip; - enum TransitionType { kTransitionNone, kTransitionOpening, @@ -40,9 +38,7 @@ class Transition : public Node { public: Transition(Clip* c); - virtual NodePtr copy(TimelineObject* c) override; - - Clip* GetParentClip(); + virtual NodePtr copy(Clip* c) override; Clip* secondary_clip; diff --git a/nodes/node.cpp b/nodes/node.cpp index a43f9b5b5..17c11c6c6 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -60,7 +60,7 @@ QVector olive::node_library; -Node::Node(TimelineObject* c) : +Node::Node(Clip* c) : parent_clip(c), flags_(0), shader_program_(nullptr), @@ -182,7 +182,6 @@ void Node::delete_self() { } void Node::move_up() { - /* int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == 0) { return; @@ -195,11 +194,9 @@ void Node::move_up() { olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); - */ } void Node::move_down() { - /* int index_of_effect = parent_clip->IndexOfEffect(this); if (index_of_effect == parent_clip->effects.size()-1) { return; @@ -212,7 +209,6 @@ void Node::move_down() { olive::undo_stack.push(command); panel_effect_controls->Reload(); panel_sequence_viewer->viewer_widget()->frame_update(); - */ } void Node::save_to_file() { @@ -607,7 +603,7 @@ const QPointF &Node::pos() void Node::process_image(double, uint8_t *, uint8_t *, int){} -NodePtr Node::copy(TimelineObject *c) { +NodePtr Node::copy(Clip *c) { NodePtr copy = Create(c); copy->SetEnabled(IsEnabled()); copy_field_keyframes(copy); @@ -675,8 +671,8 @@ GLuint Node::process_superimpose(QOpenGLContext* ctx, double timecode) { bool dimensions_changed = false; bool redrew_image = false; - int width = parent_clip->MediaWidth(); - int height = parent_clip->MediaHeight(); + int width = parent_clip->media_width(); + int height = parent_clip->media_height(); if (width != img.width() || height != img.height()) { img = QImage(width, height, QImage::Format_RGBA8888_Premultiplied); @@ -810,10 +806,10 @@ void Node::gizmo_world_to_screen(const QMatrix4x4& matrix, const QMatrix4x4& pro projection, QRect(0, 0, - parent_clip->SequenceWidth(), - parent_clip->SequenceHeight())); + parent_clip->track()->sequence()->width, + parent_clip->track()->sequence()->height)); - g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->SequenceHeight()-screen_pos.y()); + g->screen_pos[j] = QPoint(screen_pos.x(), parent_clip->track()->sequence()->height-screen_pos.y()); } } diff --git a/nodes/node.h b/nodes/node.h index 77db3fb8f..2acf02ef9 100644 --- a/nodes/node.h +++ b/nodes/node.h @@ -48,7 +48,8 @@ class EffectGizmo; class KeyframeDataChange; -class TimelineObject; +class Clip; +using ClipPtr = std::shared_ptr; class Node; using NodePtr = std::shared_ptr; @@ -111,10 +112,10 @@ struct GLTextureCoords { class Node : public QObject { Q_OBJECT public: - Node(TimelineObject *c); + Node(Clip *c); ~Node(); - TimelineObject* parent_clip; + Clip* parent_clip; virtual QString name() = 0; virtual QString id() = 0; @@ -123,7 +124,7 @@ public: virtual EffectType type() = 0; virtual olive::TrackType subtype() = 0; virtual bool IsCreatable(); - virtual NodePtr Create(TimelineObject *c) = 0; + virtual NodePtr Create(Clip *c) = 0; void AddRow(EffectRow* row); int IndexOfRow(EffectRow* row); @@ -141,7 +142,7 @@ public: virtual void refresh(); - virtual NodePtr copy(TimelineObject* c); + virtual NodePtr copy(Clip* c); void copy_field_keyframes(NodePtr e); virtual void load(QXmlStreamReader& stream); diff --git a/olive.pro b/olive.pro index 472fa9430..404cee082 100644 --- a/olive.pro +++ b/olive.pro @@ -208,8 +208,7 @@ SOURCES += \ decoders/decoder.cpp \ nodes/node.cpp \ nodes/nodeedge.cpp \ - ui/nodeedgeui.cpp \ - timeline/timelineobject.cpp + ui/nodeedgeui.cpp HEADERS += \ ui/mainwindow.h \ @@ -375,8 +374,7 @@ HEADERS += \ nodes/node.h \ timeline/tracktypes.h \ nodes/nodeedge.h \ - ui/nodeedgeui.h \ - timeline/timelineobject.h + ui/nodeedgeui.h FORMS += diff --git a/panels/timeline.cpp b/panels/timeline.cpp index c2c51493a..0cb4c0f1b 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -1108,3 +1108,11 @@ void Timeline::visibility_changed_slot(bool visibility) emit SequenceChanged(sequence_); } } + +void olive::timeline::MultiplyTrackSizesByDPI() +{ + kTrackDefaultHeight *= QApplication::desktop()->devicePixelRatio(); + kTrackMinHeight *= QApplication::desktop()->devicePixelRatio(); + kTrackHeightIncrement *= QApplication::desktop()->devicePixelRatio(); + kTimelineLabelFixedWidth *= QApplication::desktop()->devicePixelRatio(); +} diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp index f3767bfcd..ba1e94778 100644 --- a/rendering/renderfunctions.cpp +++ b/rendering/renderfunctions.cpp @@ -375,8 +375,8 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams ¶ms) { GLuint textureID = 0; // store video source dimensions - int video_width = c->MediaWidth(); - int video_height = c->MediaHeight(); + int video_width = c->media_width(); + int video_height = c->media_height(); // prepare framebuffers for backend drawing operations if (c->fbo.isEmpty()) { diff --git a/timeline/clip.cpp b/timeline/clip.cpp index aa17d29d6..57e705fb3 100644 --- a/timeline/clip.cpp +++ b/timeline/clip.cpp @@ -297,7 +297,7 @@ void Clip::Save(QXmlStreamWriter &stream) if (this == transition->secondary_clip) { // if so, just save a reference to the other clip stream.writeAttribute("shared", - QString::number(transition->GetParentClip()->load_id)); + QString::number(transition->parent_clip->load_id)); } else { // otherwise save the whole transition transition->save(stream); @@ -426,89 +426,16 @@ long Clip::length() { return timeline_out_ - timeline_in_; } -int Clip::MediaWidth() { - if (media_ != nullptr) { - switch (media_->get_type()) { - case MEDIA_TYPE_FOOTAGE: - { - const FootageStream* ms = media_stream(); - if (ms != nullptr) return ms->video_width; - break; - } - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = media_->to_sequence().get(); - return s->width; - } - } - } - - if (track() != nullptr) { - return SequenceWidth(); - } - - return 0; -} - -int Clip::MediaHeight() { - if (media_ != nullptr) { - switch (media_->get_type()) { - case MEDIA_TYPE_FOOTAGE: - { - const FootageStream* ms = media_stream(); - if (ms != nullptr) return ms->video_height; - } - break; - case MEDIA_TYPE_SEQUENCE: - { - Sequence* s = media_->to_sequence().get(); - return s->height; - } - } - } - - if (track() != nullptr) { - return SequenceHeight(); - } - - return 0; -} - -double Clip::MediaFrameRate() { +double Clip::media_frame_rate() { Q_ASSERT(type() == olive::kTypeVideo); - if (media_ != nullptr) { double rate = media_->get_frame_rate(media_stream_index()); if (!qIsNaN(rate)) return rate; } - - if (track() != nullptr) { - return SequenceFrameRate(); - } - + if (track() != nullptr) return track()->sequence()->frame_rate; return qSNaN(); } -int Clip::SequenceWidth() -{ - return track()->sequence()->width; -} - -int Clip::SequenceHeight() -{ - return track()->sequence()->height; -} - -double Clip::SequenceFrameRate() -{ - return track()->sequence()->frame_rate; -} - -long Clip::SequencePlayhead() -{ - return track()->sequence()->playhead; -} - long Clip::media_length() { if (this->track() != nullptr) { double fr = this->track()->sequence()->frame_rate; @@ -540,6 +467,44 @@ long Clip::media_length() { return 0; } +int Clip::media_width() { + if (media_ == nullptr && track() != nullptr) return track()->sequence()->width; + switch (media_->get_type()) { + case MEDIA_TYPE_FOOTAGE: + { + const FootageStream* ms = media_stream(); + if (ms != nullptr) return ms->video_width; + if (track() != nullptr) return track()->sequence()->width; + break; + } + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = media_->to_sequence().get(); + return s->width; + } + } + return 0; +} + +int Clip::media_height() { + if (media_ == nullptr && track() != nullptr) return track()->sequence()->height; + switch (media_->get_type()) { + case MEDIA_TYPE_FOOTAGE: + { + const FootageStream* ms = media_stream(); + if (ms != nullptr) return ms->video_height; + if (track() != nullptr) return track()->sequence()->height; + } + break; + case MEDIA_TYPE_SEQUENCE: + { + Sequence* s = media_->to_sequence().get(); + return s->height; + } + } + return 0; +} + void Clip::refactor_frame_rate(ComboAction* ca, double multiplier, bool change_timeline_points) { if (change_timeline_points) { track()->sequence()->MoveClip(this, diff --git a/timeline/clip.h b/timeline/clip.h index b264b0d0c..30ea217ce 100644 --- a/timeline/clip.h +++ b/timeline/clip.h @@ -39,7 +39,6 @@ #include "marker.h" #include "nodes/nodegraph.h" #include "selection.h" -#include "timelineobject.h" class Track; @@ -49,13 +48,10 @@ struct ClipSpeed { bool maintain_audio_pitch; }; -class Clip; -using ClipPtr = std::shared_ptr; - -class Clip : public TimelineObject { +class Clip { public: Clip(Track *s); - virtual ~Clip() override; + ~Clip(); ClipPtr copy(Track *s); void Save(QXmlStreamWriter& stream); @@ -75,17 +71,9 @@ public: Media* media(); FootageStream* media_stream(); int media_stream_index(); - - virtual int MediaWidth() override; - virtual int MediaHeight() override; - virtual double MediaFrameRate() override; - - virtual int SequenceWidth() override; - virtual int SequenceHeight() override; - virtual double SequenceFrameRate() override; - - virtual long SequencePlayhead() override; - + int media_width(); + int media_height(); + double media_frame_rate(); long media_length(); void set_media(Media* m, int s); @@ -193,6 +181,8 @@ private: Cacher cacher; long cacher_frame; + NodeGraph pipeline_; + QVector markers; QColor color_; bool open_; diff --git a/timeline/sequence.cpp b/timeline/sequence.cpp index 4c8ee1b0b..75340e974 100644 --- a/timeline/sequence.cpp +++ b/timeline/sequence.cpp @@ -100,26 +100,6 @@ void Sequence::Save(QXmlStreamWriter &stream) stream.writeEndElement(); } -int Sequence::SequenceWidth() -{ - return width; -} - -int Sequence::SequenceHeight() -{ - return height; -} - -double Sequence::SequenceFrameRate() -{ - return frame_rate; -} - -long Sequence::SequencePlayhead() -{ - return playhead; -} - long Sequence::GetEndFrame() { long end_frame = 0; @@ -291,7 +271,7 @@ void Sequence::MoveClip(Clip *c, ComboAction *ca, long iin, long iout, long icli if (c->closing_transition != nullptr && c->closing_transition->secondary_clip != nullptr - && c->closing_transition->GetParentClip()->timeline_in() != iout) { + && c->closing_transition->parent_clip->timeline_in() != iout) { // separate transition ca->append(new SetPointer(reinterpret_cast(&c->closing_transition->secondary_clip), nullptr)); ca->append(new AddTransitionCommand(nullptr, diff --git a/timeline/sequence.h b/timeline/sequence.h index 8d9dde9c0..94051ae83 100644 --- a/timeline/sequence.h +++ b/timeline/sequence.h @@ -29,9 +29,8 @@ #include "selection.h" #include "tracklist.h" #include "ghost.h" -#include "timelineobject.h" -class Sequence : public TimelineObject { +class Sequence : public QObject { Q_OBJECT public: Sequence(); @@ -46,12 +45,6 @@ public: int audio_frequency; int audio_layout; - virtual int SequenceWidth() override; - virtual int SequenceHeight() override; - virtual double SequenceFrameRate() override; - - virtual long SequencePlayhead() override; - long GetEndFrame(); QVector GetAllClips(); TrackList* GetTrackList(olive::TrackType type); diff --git a/timeline/timelineobject.cpp b/timeline/timelineobject.cpp deleted file mode 100644 index 5331e8939..000000000 --- a/timeline/timelineobject.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "timelineobject.h" - -TimelineObject::TimelineObject() -{ -} - -TimelineObject::~TimelineObject() -{ -} - -int TimelineObject::MediaWidth() -{ - return SequenceWidth(); -} - -int TimelineObject::MediaHeight() -{ - return SequenceHeight(); -} - -double TimelineObject::MediaFrameRate() -{ - return SequenceFrameRate(); -} - -NodeGraph *TimelineObject::pipeline() -{ - return &pipeline_; -} diff --git a/timeline/timelineobject.h b/timeline/timelineobject.h deleted file mode 100644 index 56e07220c..000000000 --- a/timeline/timelineobject.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TIMELINEOBJECT_H -#define TIMELINEOBJECT_H - -#include "nodes/nodegraph.h" - -/** - * @brief The TimelineObject class - * - * A base class for Clip, Sequence, and Track to allow compatibility between each of them and any effects nodes. - */ -class TimelineObject : public QObject -{ -public: - TimelineObject(); - virtual ~TimelineObject(); - - virtual int MediaWidth(); - virtual int MediaHeight(); - virtual double MediaFrameRate(); - - virtual int SequenceWidth() = 0; - virtual int SequenceHeight() = 0; - virtual double SequenceFrameRate() = 0; - - virtual long SequencePlayhead() = 0; - - NodeGraph* pipeline(); -private: - NodeGraph pipeline_; -}; - -#endif // TIMELINEOBJECT_H diff --git a/timeline/track.cpp b/timeline/track.cpp index bc0e36f9a..9273b67ed 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -1,8 +1,5 @@ #include "track.h" -#include -#include - #include "timeline/clip.h" #include "timeline/tracklist.h" #include "timeline/sequence.h" @@ -12,8 +9,6 @@ int olive::timeline::kTrackDefaultHeight = 40; int olive::timeline::kTrackMinHeight = 30; int olive::timeline::kTrackHeightIncrement = 10; -int olive::timeline::kTimelineLabelFixedWidth = 200; - Track::Track(TrackList* parent, olive::TrackType type) : parent_(parent), type_(type), @@ -211,26 +206,6 @@ bool Track::ContainsClip(Clip *c) return false; } -int Track::SequenceWidth() -{ - return sequence()->width; -} - -int Track::SequenceHeight() -{ - return sequence()->height; -} - -double Track::SequenceFrameRate() -{ - return sequence()->frame_rate; -} - -long Track::SequencePlayhead() -{ - return sequence()->playhead; -} - Track *Track::Previous() { int index = Index(); @@ -458,12 +433,3 @@ void Track::SetLocked(bool locked) { locked_ = locked; } - -void olive::timeline::MultiplyTrackSizesByDPI() -{ - kTrackDefaultHeight *= qApp->fontMetrics().height() * 3; - kTrackMinHeight *= qApp->fontMetrics().height(); - kTrackHeightIncrement *= qApp->fontMetrics().height() / 2; - kTimelineLabelFixedWidth *= QApplication::desktop()->devicePixelRatio(); -} - diff --git a/timeline/track.h b/timeline/track.h index d3d3a4efb..310cd69e9 100644 --- a/timeline/track.h +++ b/timeline/track.h @@ -8,8 +8,6 @@ #include "tracktypes.h" #include "undo/comboaction.h" #include "timeline/selection.h" -#include "timelineobject.h" -#include "clip.h" class Sequence; class Transition; @@ -39,7 +37,7 @@ namespace olive { class TrackList; -class Track : public TimelineObject +class Track : public QObject { Q_OBJECT public: @@ -70,12 +68,6 @@ public: Clip* GetClipFromPoint(long point); bool ContainsClip(Clip* c); - virtual int SequenceWidth() override; - virtual int SequenceHeight() override; - virtual double SequenceFrameRate() override; - - virtual long SequencePlayhead() override; - Track* Previous(); Track* Next(); Track* Sibling(int diff); diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 7d1113d74..62a1cdb35 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -43,7 +43,7 @@ EffectUI::EffectUI(Node* e) : Transition* t = static_cast(e); // Since effects can have two clip attachments, find out which one is selected - Clip* selected_clip = t->GetParentClip(); + Clip* selected_clip = t->parent_clip; bool both_selected = false; // Check if this is a shared transition @@ -54,12 +54,12 @@ EffectUI::EffectUI(Node* e) : selected_clip = t->secondary_clip; - if (t->GetParentClip()->IsSelected()) { + if (t->parent_clip->IsSelected()) { // Both clips are selected both_selected = true; } - } else if (!t->GetParentClip()->IsSelected()) { + } else if (!t->parent_clip->IsSelected()) { // Neither are selected, but the naming scheme (no "opening" or "closing" modifier) will be the same both_selected = true; @@ -267,7 +267,7 @@ void EffectUI::UpdateFromEffect() } } -bool EffectUI::IsAttachedToClip(TimelineObject *c) +bool EffectUI::IsAttachedToClip(Clip *c) { if (GetEffect()->parent_clip == c) { return true; diff --git a/ui/effectui.h b/ui/effectui.h index 5372540dc..9eb12201c 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -131,7 +131,7 @@ public: * * True is an Effect from this Clip is already attached to this EffectUI. */ - bool IsAttachedToClip(TimelineObject* c); + bool IsAttachedToClip(Clip* c); void SetNodeParent(NodeUI* parent); diff --git a/ui/timelinearea.cpp b/ui/timelinearea.cpp index a9d1c9c9a..e0c795ab8 100644 --- a/ui/timelinearea.cpp +++ b/ui/timelinearea.cpp @@ -5,6 +5,8 @@ #include "panels/timeline.h" #include "global/config.h" +int olive::timeline::kTimelineLabelFixedWidth = 200; + TimelineArea::TimelineArea(Timeline* timeline, olive::timeline::Alignment alignment) : timeline_(timeline), track_list_(nullptr), diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 6670a028c..5fb7dd057 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -344,7 +344,7 @@ void ViewerWidget::move_gizmos(QMouseEvent *event, bool done) { x_movement, y_movement, get_timecode(gizmos->parent_clip, - gizmos->parent_clip->SequencePlayhead()), + gizmos->parent_clip->track()->sequence()->playhead), done); gizmo_x_mvmt += x_movement; From dcd44abde1a61c77ab8bea87940ae6fb6fcc92ae Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 14 Apr 2019 22:19:08 +1000 Subject: [PATCH 125/133] fixed windows compile issue --- ui/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 153080f88..cade09482 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -466,7 +466,7 @@ void MainWindow::Restyle() // Windows menus have the option of being native, so we may not need this CSS #ifdef Q_OS_WIN - if (!olive::CurrentConfig.use_native_menu_styling) { + if (!olive::config.use_native_menu_styling) { #endif stylesheet.append("QMenu::separator { background: #404040; }"); #ifdef Q_OS_WIN From 79d54987b40c70303f2ae005fec3a05cd18a44ad Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 14 Apr 2019 23:29:00 +1000 Subject: [PATCH 126/133] fixed other windows compile issue --- dialogs/preferencesdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index f70a369b1..a1baa855d 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -912,7 +912,7 @@ void PreferencesDialog::setup_ui() { // Native menu styling is only available on Windows. Environments like Ubuntu and Mac use the native menu system by // default QCheckBox* native_menus = new QCheckBox(tr("Use Native Menu Styling")); - AddBoolPair(native_menus, &olive::CurrentConfig.use_native_menu_styling, true); + AddBoolPair(native_menus, &olive::config.use_native_menu_styling, true); appearance_layout->addWidget(native_menus, row, 0, 1, 3); row++; From 310ca7c2a0b10690255e0a327d086ff031b64035 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 17 Apr 2019 09:21:40 +1000 Subject: [PATCH 127/133] fixed msvc build issues --- nodes/nodes/nodeshader.cpp | 1 + olive.pro | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nodes/nodes/nodeshader.cpp b/nodes/nodes/nodeshader.cpp index c71a3e1a1..d5d924e7c 100644 --- a/nodes/nodes/nodeshader.cpp +++ b/nodes/nodes/nodeshader.cpp @@ -218,4 +218,5 @@ bool NodeShader::IsCreatable() NodePtr NodeShader::Create(Clip *) { Q_ASSERT(false); + return nullptr; } diff --git a/olive.pro b/olive.pro index 404cee082..7cc3ac1bb 100644 --- a/olive.pro +++ b/olive.pro @@ -48,7 +48,7 @@ system("which git") { CONFIG += c++11 -QMAKE_CXXFLAGS += -Wno-reorder +gcc:QMAKE_CXXFLAGS += -Wno-reorder SOURCES += \ main.cpp \ From 1c6d7d0c6f2128b91b81abc6977324bc14f73dc7 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 17 Apr 2019 09:57:40 +1000 Subject: [PATCH 128/133] correct xml structure --- effects/shaders/boxblur.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effects/shaders/boxblur.xml b/effects/shaders/boxblur.xml index b430794c9..53140ed99 100644 --- a/effects/shaders/boxblur.xml +++ b/effects/shaders/boxblur.xml @@ -1,5 +1,5 @@ - + From 8c9809f657c0c1f1b9ada8c0f93f3a42d11761e1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 21 Apr 2019 22:56:13 +1000 Subject: [PATCH 129/133] ported master fixes to furtherocio --- panels/timeline.cpp | 2 +- timeline/track.cpp | 41 ++++++++++++++++++++--------------------- ui/timelineview.cpp | 28 +++++++++++++++++----------- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/panels/timeline.cpp b/panels/timeline.cpp index c0b3f7a0a..d0402d2f9 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -746,7 +746,7 @@ void Timeline::split_at_playhead() } long getFrameFromScreenPoint(double zoom, int x) { - long f = qRound(double(x) / zoom); + long f = qFloor(double(x) / zoom); if (f < 0) { return 0; } diff --git a/timeline/track.cpp b/timeline/track.cpp index 37cbc7a91..0f360e994 100644 --- a/timeline/track.cpp +++ b/timeline/track.cpp @@ -246,8 +246,7 @@ bool Track::IsClipSelected(Clip *clip, bool containing) for (int i=0;itimeline_in() >= s.in() && clip->timeline_out() <= s.out()) - || (!containing && !(clip->timeline_in() < s.in() && clip->timeline_out() < s.in()) - && !(clip->timeline_in() > s.in() && clip->timeline_out() > s.in())))) { + || (!containing && !(clip->timeline_in() >= s.out() || clip->timeline_out() <= s.in())))) { return true; } } @@ -340,27 +339,27 @@ void Track::ClearSelections() void Track::DeselectArea(long in, long out) { int selection_count = selections_.size(); - for (int i=0;i= in && s.out() <= out) { - // whole selection is in deselect area - selections_.removeAt(i); - i--; - selection_count--; - } else if (s.in() < in && s.out() > out) { - // middle of selection is in deselect area - Selection new_sel(out, s.out(), s.track()); - selections_.append(new_sel); + if (s.in() >= in && s.out() <= out) { + // whole selection is in deselect area + selections_.removeAt(i); + i--; + selection_count--; + } else if (s.in() < in && s.out() > out) { + // middle of selection is in deselect area + Selection new_sel(out, s.out(), s.track()); + selections_.append(new_sel); - s.set_out(in); - } else if (s.in() < in && s.out() > in) { - // only out point is in deselect area - s.set_out(in); - } else if (s.in() < out && s.out() > out) { - // only in point is in deselect area - s.set_in(out); - } + s.set_out(in); + } else if (s.in() < in && s.out() > in) { + // only out point is in deselect area + s.set_out(in); + } else if (s.in() < out && s.out() > out) { + // only in point is in deselect area + s.set_in(out); + } } } diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index c18d30af5..755ec4f03 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -719,6 +719,7 @@ void TimelineView::mousePressEvent(QMouseEvent *event) { } hovered_clip->track()->SelectArea(s_in, s_out); } + } else { // if the clip is not already selected @@ -2497,9 +2498,9 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { // // threshold around a trim point that the cursor can be within and still considered "trimming" - int lim = 5; - long mouse_frame_lower = ParentTimeline()->getTimelineFrameFromScreenPoint(pos.x()-lim)-1; - long mouse_frame_upper = ParentTimeline()->getTimelineFrameFromScreenPoint(pos.x()+lim)+1; + int lim = 10; // FIXME Magic number for the clip trimming threshold + int mouse_frame_lower = pos.x() - lim; + int mouse_frame_upper = pos.x() + lim; // used to determine whether we the cursor found a trim point or not bool found = false; @@ -2553,11 +2554,14 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } } + int visual_in_point = ParentTimeline()->getTimelineScreenPointFromFrame(c->timeline_in()); + int visual_out_point = ParentTimeline()->getTimelineScreenPointFromFrame(c->timeline_out()); + // is the cursor hovering around the clip's IN point? - if (c->timeline_in() > mouse_frame_lower && c->timeline_in() < mouse_frame_upper) { + if (visual_in_point > mouse_frame_lower && visual_in_point < mouse_frame_upper) { // test how close this IN point is to the cursor - long nc = qAbs(c->timeline_in() + 1 - ParentTimeline()->cursor_frame); + int nc = qAbs(visual_in_point + 1 - pos.x()); // and test whether it's closer than the last in/out point we found if (nc < closeness) { @@ -2572,10 +2576,10 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { } // is the cursor hovering around the clip's OUT point? - if (c->timeline_out() > mouse_frame_lower && c->timeline_out() < mouse_frame_upper) { + if (visual_out_point > mouse_frame_lower && visual_out_point < mouse_frame_upper) { // test how close this OUT point is to the cursor - long nc = qAbs(c->timeline_out() - 1 - ParentTimeline()->cursor_frame); + int nc = qAbs(visual_out_point - 1 - pos.x()); // and test whether it's closer than the last in/out point we found if (nc < closeness) { @@ -2597,13 +2601,14 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c->opening_transition != nullptr) { // cache the timeline frame where the transition ends - long transition_point = c->timeline_in() + c->opening_transition->get_true_length(); + int transition_point = ParentTimeline()->getTimelineScreenPointFromFrame(c->timeline_in() + + c->opening_transition->get_true_length()); // check if the cursor is hovering around it (within the threshold) if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { // similar to above, test how close it is and if it's closer, make this active - long nc = qAbs(transition_point - 1 - ParentTimeline()->cursor_frame); + int nc = qAbs(transition_point - 1 - pos.x()); if (nc < closeness) { ParentTimeline()->trim_target = c; ParentTimeline()->trim_type = olive::timeline::TRIM_OUT; @@ -2618,13 +2623,14 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) { if (c->closing_transition != nullptr) { // cache the timeline frame where the transition starts - long transition_point = c->timeline_out() - c->closing_transition->get_true_length(); + int transition_point = ParentTimeline()->getTimelineScreenPointFromFrame(c->timeline_out() + - c->closing_transition->get_true_length()); // check if the cursor is hovering around it (within the threshold) if (transition_point > mouse_frame_lower && transition_point < mouse_frame_upper) { // similar to above, test how close it is and if it's closer, make this active - long nc = qAbs(transition_point + 1 - ParentTimeline()->cursor_frame); + int nc = qAbs(transition_point + 1 - pos.x()); if (nc < closeness) { ParentTimeline()->trim_target = c; ParentTimeline()->trim_type = olive::timeline::TRIM_IN; From 945242e165f1fa54b8f96b95a5dcff79d20a8c2a Mon Sep 17 00:00:00 2001 From: Troy James Sobotka Date: Fri, 19 Apr 2019 15:25:41 -0700 Subject: [PATCH 130/133] Fix #853 lower_control_layout QSizePolicy Changing the QSizePolicy to `Maximum` prevents the central control buttons from wiggling as the variable width font changes width dimensions. --- panels/viewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 79ec033bd..87765ece1 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -619,7 +619,7 @@ void Viewer::setup_ui() { lower_control_layout->setMargin(0); QSizePolicy timecode_container_policy(QSizePolicy::Minimum, QSizePolicy::Maximum); - QSizePolicy lower_control_policy(QSizePolicy::Expanding, QSizePolicy::Maximum); + QSizePolicy lower_control_policy(QSizePolicy::Maximum, QSizePolicy::Maximum); // Current time code container QWidget* current_timecode_container = new QWidget(); From ae37b927ec4c57242755479da8f733fd30cefaea Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 18 Apr 2019 02:57:21 +1000 Subject: [PATCH 131/133] fixes #812 --- effects/shaders/vignette.frag | 4 +++- effects/shaders/vignette.xml | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/effects/shaders/vignette.frag b/effects/shaders/vignette.frag index 86d13b14b..fffd8c192 100644 --- a/effects/shaders/vignette.frag +++ b/effects/shaders/vignette.frag @@ -1,5 +1,7 @@ uniform float lensRadiusX; uniform float lensRadiusY; +uniform float centerX; +uniform float centerY; uniform bool circular; uniform vec2 resolution; // uniform vec2 lensRadius; // 0.45, 0.38 @@ -14,7 +16,7 @@ vec4 process(vec4 c) { vignetteCoord.x *= ar; vignetteCoord.x -= (1.0-(1.0/ar)); } - float dist = distance(vignetteCoord, vec2(0.5,0.5)); + float dist = distance(vignetteCoord, vec2(0.5 + centerX*0.01, 0.5 + centerY*0.01)); float size = (lensRadiusX*0.01); return vec4(c.rgb * smoothstep(size, size*0.99*(1.0-lensRadiusY*0.01), dist), c.a); } diff --git a/effects/shaders/vignette.xml b/effects/shaders/vignette.xml index 5156a84c4..a85e94be5 100644 --- a/effects/shaders/vignette.xml +++ b/effects/shaders/vignette.xml @@ -9,5 +9,9 @@ + + + + \ No newline at end of file From 43284ced8b8a0c30295e69482f89c13ec813fb99 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 18 Apr 2019 02:39:11 +1000 Subject: [PATCH 132/133] cherrypicked relevant commits from master --- global/config.cpp | 7 ++++++- global/config.h | 5 +++++ ui/mainwindow.cpp | 5 +++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/global/config.cpp b/global/config.cpp index cd22f52a0..917bc66cc 100644 --- a/global/config.cpp +++ b/global/config.cpp @@ -82,7 +82,8 @@ Config::Config() playback_bit_depth(olive::PIX_FMT_RGBA16F), export_bit_depth(olive::PIX_FMT_RGBA32F), dont_use_proxies_on_export(true), - maximum_recent_projects(10) + maximum_recent_projects(10), + locked_panels(false) {} void Config::load(QString path) { @@ -267,6 +268,9 @@ void Config::load(QString path) { } else if (stream.name() == "DontUseProxiesOnExport") { stream.readNext(); dont_use_proxies_on_export = (stream.text() == "1"); + } else if (stream.name() == "LockedPanels") { + stream.readNext(); + locked_panels = (stream.text() == "1"); } } } @@ -349,6 +353,7 @@ void Config::save(QString path) { stream.writeTextElement("PlaybackBitDepth", QString::number(playback_bit_depth)); stream.writeTextElement("ExportBitDepth", QString::number(export_bit_depth)); stream.writeTextElement("DontUseProxiesOnExport", QString::number(dont_use_proxies_on_export)); + stream.writeTextElement("LockedPanels", QString::number(locked_panels)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/global/config.h b/global/config.h index 0291a5e3a..293bcdcca 100644 --- a/global/config.h +++ b/global/config.h @@ -614,6 +614,11 @@ struct Config { */ int maximum_recent_projects; + /** + * @brief Sets whether panels should load locked or not + */ + bool locked_panels; + /** * @brief Load config from file * diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index cade09482..cbe7acb3a 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -285,6 +285,9 @@ MainWindow::MainWindow(QWidget *parent) : olive::Global->check_for_autorecovery_file(); + // lock panels if the config says so + set_panels_locked(olive::config.locked_panels); + // set up output audio device init_audio(); @@ -1193,6 +1196,8 @@ void MainWindow::set_panels_locked(bool locked) panel->setTitleBarWidget(nullptr); } } + + olive::config.locked_panels = locked; } void MainWindow::fileMenu_About_To_Be_Shown() { From 9b2f91f483c2940554de42b17d44d600b4009299 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 24 Apr 2019 10:26:42 +1000 Subject: [PATCH 133/133] show node properties as a sidebar --- panels/nodeeditor.cpp | 60 +++++++++++++++++++++---- panels/nodeeditor.h | 5 ++- rendering/audio.cpp | 3 +- ui/effectui.cpp | 28 +----------- ui/effectui.h | 9 ---- ui/nodeedgeui.cpp | 4 +- ui/nodeui.cpp | 101 ++++++++++++++++++++++++------------------ ui/nodeui.h | 11 +++-- 8 files changed, 124 insertions(+), 97 deletions(-) diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 387559a94..2d632ff5d 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "global/global.h" @@ -11,23 +12,39 @@ NodeEditor::NodeEditor(QWidget *parent) : EffectsPanel(parent), view_(&scene_) -{ +{ setWindowTitle(tr("Node Editor")); resize(720, 480); QWidget* central_widget = new QWidget(); setWidget(central_widget); + QSplitter* splitter = new QSplitter(); + splitter->setOrientation(Qt::Horizontal); + splitter->setChildrenCollapsible(false); + + splitter->addWidget(&view_); + + QSizePolicy view_policy; + view_policy.setHorizontalStretch(1); + view_.setSizePolicy(view_policy); + view_.setInteractive(true); + view_.setDragMode(QGraphicsView::RubberBandDrag); + + prop_layout_ = new QVBoxLayout(&props_); + prop_layout_->setMargin(0); + prop_layout_->setSpacing(0); + prop_layout_->addStretch(); + splitter->addWidget(&props_); + QVBoxLayout* layout = new QVBoxLayout(central_widget); layout->setSpacing(0); layout->setMargin(0); - layout->addWidget(&view_); - - view_.setInteractive(true); - view_.setDragMode(QGraphicsView::RubberBandDrag); + layout->addWidget(splitter); connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(ItemsChanged())); + connect(&scene_, SIGNAL(selectionChanged()), this, SLOT(UpdateNodeProperties())); connect(&view_, SIGNAL(RequestContextMenu()), this, SLOT(ContextMenu())); } @@ -49,18 +66,21 @@ void NodeEditor::LoadEvent() for (int i=0;iGetEffect(); - if (effect_ui->GetEffect()->parent_clip == first_clip) { + if (node->parent_clip == first_clip) { NodeUI* node_ui = new NodeUI(); - effect_ui->SetNodeParent(node_ui); effect_ui->SetSelectable(false); - node_ui->SetWidget(effect_ui); + node_ui->SetNode(node); node_ui->AddToScene(&scene_); node_ui->setPos(effect_ui->GetEffect()->pos()); nodes_.append(node_ui); + + effect_ui->setVisible(false); + prop_layout_->insertWidget(prop_layout_->count()-1, effect_ui); } } } @@ -153,7 +173,7 @@ void NodeEditor::ItemsChanged() } foreach (NodeUI* node, nodes_) { - node->Widget()->GetEffect()->SetPos(node->pos()); + node->GetNode()->SetPos(node->pos()); } } @@ -170,3 +190,25 @@ void NodeEditor::ContextMenu() olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, c->type(), {c}); } } + +void NodeEditor::UpdateNodeProperties() +{ + // Find matching widget + for (int j=0;jsetVisible(false); + } + + QList sel = scene_.selectedItems(); + for (int i=0;i(sel.at(i)); + + if (node_ui != nullptr) { + for (int j=0;jGetEffect() == node_ui->GetNode()) { + open_effects_.at(j)->setVisible(true); + break; + } + } + } + } +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index b6cb01316..f38ff4ee9 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -23,9 +23,12 @@ protected: private: QGraphicsScene scene_; NodeView view_; + QWidget props_; QVector nodes_; QVector edges_; QVector connected_rows_; + QVBoxLayout* prop_layout_; + QVector nodes_open_in_props_; void ClearEdges(); void LoadEdges(); @@ -37,7 +40,7 @@ private slots: void ItemsChanged(); void ReloadEdges(); void ContextMenu(); - + void UpdateNodeProperties(); }; #endif // NODEEDITOR_H diff --git a/rendering/audio.cpp b/rendering/audio.cpp index 105524ab9..f16604edd 100644 --- a/rendering/audio.cpp +++ b/rendering/audio.cpp @@ -153,7 +153,8 @@ void clear_audio_ibuffer() { } int current_audio_freq() { - return olive::Global->is_exporting() ? audio_rendering_rate : audio_output->format().sampleRate(); + return olive::Global->is_exporting() + ? audio_rendering_rate : audio_output->format().sampleRate(); } qint64 get_buffer_offset_from_frame(double framerate, long frame) { diff --git a/ui/effectui.cpp b/ui/effectui.cpp index 62a1cdb35..cccf6b286 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -30,8 +30,7 @@ #include "panels/panels.h" EffectUI::EffectUI(Node* e) : - effect_(e), - node_parent_(nullptr) + effect_(e) { Q_ASSERT(e != nullptr); @@ -282,31 +281,6 @@ bool EffectUI::IsAttachedToClip(Clip *c) return false; } -void EffectUI::SetNodeParent(NodeUI *parent) -{ - node_parent_ = parent; -} - -void EffectUI::resizeEvent(QResizeEvent *event) -{ - if (node_parent_ != nullptr) { - node_parent_->Resize(event->size()); - } -} - -bool EffectUI::event(QEvent *event) -{ - if (node_parent_ != nullptr - && (event->type() == QEvent::MouseButtonPress - || event->type() == QEvent::MouseButtonRelease - || event->type() == QEvent::MouseMove - || event->type() == QEvent::MouseButtonDblClick) - && node_parent_->scene()->sendEvent(node_parent_, event)) { - return true; - } - return CollapsibleWidget::event(event); -} - QWidget *EffectUI::Widget(int row, int field) { return widgets_.at(row).at(field); diff --git a/ui/effectui.h b/ui/effectui.h index 9eb12201c..fe80c45e4 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -133,11 +133,7 @@ public: */ bool IsAttachedToClip(Clip* c); - void SetNodeParent(NodeUI* parent); - protected: - virtual void resizeEvent(QResizeEvent* event) override; - virtual bool event(QEvent* event) override; signals: /** @@ -203,11 +199,6 @@ private: */ QVector keyframe_navigators_; - /** - * @brief Internal reference to node parent - */ - NodeUI* node_parent_; - /** * @brief Attach a KeyframeNavigator object to an EffectRow. * diff --git a/ui/nodeedgeui.cpp b/ui/nodeedgeui.cpp index 61c0bde8c..a275af92f 100644 --- a/ui/nodeedgeui.cpp +++ b/ui/nodeedgeui.cpp @@ -26,7 +26,7 @@ void NodeEdgeUI::adjust() if (node != nullptr) { // Check if this node has the output row - int row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->output()); + int row_index = node->GetNode()->IndexOfRow(edge_->output()); if (row_index > -1) { output_node_ = node; @@ -34,7 +34,7 @@ void NodeEdgeUI::adjust() } // Check if this node has the input row - row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->input()); + row_index = node->GetNode()->IndexOfRow(edge_->input()); if (row_index > -1) { input_node_ = node; diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index e58906bad..14808ce3e 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -15,11 +15,11 @@ #include "ui/nodeedgeui.h" const int kRoundedRectRadius = 5; +const int kTextPadding = 4; const int kNodePlugSize = 12; NodeUI::NodeUI() : - central_widget_(nullptr), - proxy_(nullptr), + node_(nullptr), drag_destination_(nullptr), clicked_socket_(-1) { @@ -27,34 +27,23 @@ NodeUI::NodeUI() : setFlag(QGraphicsItem::ItemIsSelectable, true); } -NodeUI::~NodeUI() -{ - if (proxy_ != nullptr) { - proxy_->setParentItem(nullptr); - } -} - void NodeUI::AddToScene(QGraphicsScene *scene) { scene->addItem(this); - - if (central_widget_ != nullptr) { - proxy_ = scene->addWidget(central_widget_); - proxy_->setPos(pos() + QPoint(1 + kRoundedRectRadius, 1 + kRoundedRectRadius)); - proxy_->setParentItem(this); - } } -void NodeUI::Resize(const QSize &s) +void NodeUI::SetNode(Node *n) { + node_ = n; + QRectF rectangle; + rectangle.setTopLeft(pos()); - rectangle.setSize(s + 2 * QSize(kRoundedRectRadius, kRoundedRectRadius)); + rectangle.setSize(QSizeF(200, GetRowY(node_->row_count()))); QRectF inner_rect = rectangle; - inner_rect.translate(kNodePlugSize / 2, 0); - - rectangle.setWidth(rectangle.width() + kNodePlugSize); + inner_rect.setX(inner_rect.x() + kNodePlugSize/2); + inner_rect.setWidth(inner_rect.width() - kNodePlugSize/2); drag_path_ = QPainterPath(); drag_path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius); @@ -62,20 +51,16 @@ void NodeUI::Resize(const QSize &s) setRect(rectangle); } -void NodeUI::SetWidget(EffectUI *widget) +Node *NodeUI::GetNode() { - central_widget_ = widget; -} - -EffectUI *NodeUI::Widget() -{ - return central_widget_; + return node_; } void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget) + // Draw node sockets QVector sockets = GetNodeSocketRects(); if (!sockets.isEmpty()) { painter->setPen(Qt::black); @@ -86,6 +71,7 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW } } + // Draw main background rounded rectangle QPalette palette = qApp->palette(); if (option->state & QStyle::State_Selected) { @@ -95,6 +81,34 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW } painter->setBrush(palette.window()); painter->drawPath(drag_path_); + + // Draw node title + painter->setPen(palette.text().color()); + int left_text_x = rect().x() + kNodePlugSize/2 + kTextPadding; + painter->drawText(left_text_x, + GetRowY(-1) + qApp->fontMetrics().ascent(), + node_->name()); + + // Draw node row names + for (int i=0;irow_count();i++) { + int text_x; + + if (node_->row(i)->IsNodeOutput()) { + // right alignment + text_x = rect().right() - kNodePlugSize/2 - qApp->fontMetrics().width(node_->row(i)->name()) - kTextPadding; + } else { + // left alignment + text_x = left_text_x; + } + + painter->drawText(text_x, + GetRowY(i) + qApp->fontMetrics().ascent(), + node_->row(i)->name()); + } + + // Draw title splitter line + int line_y = GetRowY(-1) + qApp->fontMetrics().height() + kTextPadding/2; + painter->drawLine(rect().x() + kNodePlugSize/2, line_y, rect().right() - kNodePlugSize/2, line_y); } void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) @@ -114,8 +128,8 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) if (clicked_socket_ > -1) { // See if this socket already has an edge connected - QVector edges = central_widget_->GetEffect()->row(clicked_socket_)->edges(); - if (!edges.isEmpty() && edges.last()->input()->GetParentEffect() == central_widget_->GetEffect()) { + QVector edges = node_->row(clicked_socket_)->edges(); + if (!edges.isEmpty() && edges.last()->input()->GetParentEffect() == node_) { NodeEdge* e = edges.last().get(); @@ -144,7 +158,7 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event) // Start a new edge here drag_line_start_ = pos() + sockets.at(clicked_socket_).center(); - drag_source_ = central_widget_->GetEffect()->row(clicked_socket_); + drag_source_ = node_->row(clicked_socket_); } @@ -254,18 +268,19 @@ QVector NodeUI::GetNodeSocketRects() { QVector rects; - if (proxy_ != nullptr) { - Node* e = central_widget_->GetEffect(); + if (node_ != nullptr) { + Node* e = node_; for (int i=0;irow_count();i++) { - EffectRow* row = e->row(i); - qreal x = (row->IsNodeOutput()) ? rect().right() - kNodePlugSize : rect().x(); - int y = central_widget_->GetRowY(i); + EffectRow* row = e->row(i); if (row->IsNodeInput() || row->IsNodeOutput()) { + qreal x = (row->IsNodeOutput()) ? rect().right() - kNodePlugSize : rect().x(); + int y = GetRowY(i) + qApp->fontMetrics().height()/2; + rects.append(QRectF(x, - proxy_->pos().y() + y - kNodePlugSize/2, + rect().y() + y - kNodePlugSize/2, kNodePlugSize, kNodePlugSize)); } @@ -277,11 +292,8 @@ QVector NodeUI::GetNodeSocketRects() EffectRow *NodeUI::GetRowFromIndex(int i) { - if (central_widget_ != nullptr) { - Node* e = central_widget_->GetEffect(); - if (i < e->row_count()) { - return e->row(i); - } + if (node_ != nullptr && i < node_->row_count()) { + return node_->row(i); } return nullptr; } @@ -296,7 +308,7 @@ NodeUI *NodeUI::FindUIFromNode(Node* n) // Check if this node has the specified - if (node->Widget()->GetEffect() == n) { + if (node->node_ == n) { return node; } } @@ -304,3 +316,8 @@ NodeUI *NodeUI::FindUIFromNode(Node* n) return nullptr; } + +int NodeUI::GetRowY(int index) +{ + return (qApp->fontMetrics().height() + kTextPadding) * (index + 1) + kTextPadding; +} diff --git a/ui/nodeui.h b/ui/nodeui.h index b8cd53c5a..0558bbe01 100644 --- a/ui/nodeui.h +++ b/ui/nodeui.h @@ -12,12 +12,11 @@ class Node; class NodeUI : public QGraphicsRectItem { public: NodeUI(); - virtual ~NodeUI() override; void AddToScene(QGraphicsScene* scene); - void Resize(const QSize& s); - void SetWidget(EffectUI* widget); - EffectUI* Widget(); + //void Resize(const QSize& s); + void SetNode(Node* n); + Node* GetNode(); QVector GetNodeSocketRects(); @@ -31,9 +30,9 @@ protected: private: EffectRow *GetRowFromIndex(int i); NodeUI* FindUIFromNode(Node* n); + int GetRowY(int index); - EffectUI* central_widget_; - QGraphicsProxyWidget* proxy_; + Node* node_; QGraphicsPathItem* drag_line_; QPointF drag_line_start_;