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/add.blend b/effects/add.blend
new file mode 100644
index 000000000..886c2e617
--- /dev/null
+++ b/effects/add.blend
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..098e734a3
--- /dev/null
+++ b/effects/average.blend
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 000000000..4cea15cd4
--- /dev/null
+++ b/effects/color-burn.blend
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..495ff401c
--- /dev/null
+++ b/effects/color-dodge.blend
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..f5a81931f
--- /dev/null
+++ b/effects/darken.blend
@@ -0,0 +1,14 @@
+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
new file mode 100644
index 000000000..e65ba45eb
--- /dev/null
+++ b/effects/difference.blend
@@ -0,0 +1,10 @@
+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/exclusion.blend b/effects/exclusion.blend
new file mode 100644
index 000000000..292fe17ce
--- /dev/null
+++ b/effects/exclusion.blend
@@ -0,0 +1,10 @@
+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)
+#olive name Exclusion
\ No newline at end of file
diff --git a/effects/glow.blend b/effects/glow.blend
new file mode 100644
index 000000000..a770cb917
--- /dev/null
+++ b/effects/glow.blend
@@ -0,0 +1,12 @@
+#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)
+#olive name Glow
\ No newline at end of file
diff --git a/effects/hard-light.blend b/effects/hard-light.blend
new file mode 100644
index 000000000..ed917e72d
--- /dev/null
+++ b/effects/hard-light.blend
@@ -0,0 +1,12 @@
+#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)
+#olive name Hard Light
\ No newline at end of file
diff --git a/effects/hard-mix.blend b/effects/hard-mix.blend
new file mode 100644
index 000000000..4ba29a4dc
--- /dev/null
+++ b/effects/hard-mix.blend
@@ -0,0 +1,16 @@
+#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)
+#olive name Hard Mix
\ No newline at end of file
diff --git a/effects/internal/blending.frag b/effects/internal/blending.frag
deleted file mode 100644
index c8d14d593..000000000
--- a/effects/internal/blending.frag
+++ /dev/null
@@ -1,207 +0,0 @@
-#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;
-
-uniform sampler2D background;
-uniform sampler2D foreground;
-
-uniform int blendmode;
-uniform 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);
- vec4 fg_color = texture2D(foreground, vTexCoord);
-
- // 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
- || 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);
- // vec4 full_composite = vec4(mix(bg_color.rgb, composite, alpha_opac), bg_color.a + alpha_opac);
-
- 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/internal/transformeffect.cpp b/effects/internal/transformeffect.cpp
index 07dd08d3f..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"
@@ -44,230 +45,212 @@
#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
+ olive::effects_loaded.lock();
+ for (int i=0;iadd_combo_item(olive::blend_modes.at(i).name, i);
+ }
+ olive::effects_loaded.unlock();
- 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/lighten.blend b/effects/lighten.blend
new file mode 100644
index 000000000..37f3708da
--- /dev/null
+++ b/effects/lighten.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Lighten
\ No newline at end of file
diff --git a/effects/linear-burn.blend b/effects/linear-burn.blend
new file mode 100644
index 000000000..6bbce530c
--- /dev/null
+++ b/effects/linear-burn.blend
@@ -0,0 +1,16 @@
+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)
+#olive name Linear Burn
\ No newline at end of file
diff --git a/effects/linear-dodge.blend b/effects/linear-dodge.blend
new file mode 100644
index 000000000..1a5cb5709
--- /dev/null
+++ b/effects/linear-dodge.blend
@@ -0,0 +1,16 @@
+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)
+#olive name Linear Dodge
\ No newline at end of file
diff --git a/effects/linear-light.blend b/effects/linear-light.blend
new file mode 100644
index 000000000..97d8b02cc
--- /dev/null
+++ b/effects/linear-light.blend
@@ -0,0 +1,17 @@
+#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)
+#olive name Linear Light
\ No newline at end of file
diff --git a/effects/multiply.blend b/effects/multiply.blend
new file mode 100644
index 000000000..55ed2d341
--- /dev/null
+++ b/effects/multiply.blend
@@ -0,0 +1,10 @@
+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)
+#olive name Multiply
\ No newline at end of file
diff --git a/effects/negation.blend b/effects/negation.blend
new file mode 100644
index 000000000..6b8c812b5
--- /dev/null
+++ b/effects/negation.blend
@@ -0,0 +1,10 @@
+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)
+#olive name Negation
\ No newline at end of file
diff --git a/effects/normal.blend b/effects/normal.blend
new file mode 100644
index 000000000..a3fc1b63b
--- /dev/null
+++ b/effects/normal.blend
@@ -0,0 +1,10 @@
+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)
+#olive name Normal
\ No newline at end of file
diff --git a/effects/overlay.blend b/effects/overlay.blend
new file mode 100644
index 000000000..4500d21be
--- /dev/null
+++ b/effects/overlay.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Overlay
\ No newline at end of file
diff --git a/effects/phoenix.blend b/effects/phoenix.blend
new file mode 100644
index 000000000..096139621
--- /dev/null
+++ b/effects/phoenix.blend
@@ -0,0 +1,10 @@
+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)
+#olive name Phoenix
\ No newline at end of file
diff --git a/effects/pin-light.blend b/effects/pin-light.blend
new file mode 100644
index 000000000..bff45cf31
--- /dev/null
+++ b/effects/pin-light.blend
@@ -0,0 +1,17 @@
+#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)
+#olive name Pin Light
\ No newline at end of file
diff --git a/effects/reflect.blend b/effects/reflect.blend
new file mode 100644
index 000000000..75578520b
--- /dev/null
+++ b/effects/reflect.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Reflect
\ No newline at end of file
diff --git a/effects/screen.blend b/effects/screen.blend
new file mode 100644
index 000000000..f2c4ce838
--- /dev/null
+++ b/effects/screen.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Screen
\ No newline at end of file
diff --git a/effects/soft-light.blend b/effects/soft-light.blend
new file mode 100644
index 000000000..8a6991214
--- /dev/null
+++ b/effects/soft-light.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Soft Light
\ No newline at end of file
diff --git a/effects/substract.blend b/effects/substract.blend
new file mode 100644
index 000000000..2e3804719
--- /dev/null
+++ b/effects/substract.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Substract
\ No newline at end of file
diff --git a/effects/subtract.blend b/effects/subtract.blend
new file mode 100644
index 000000000..aa93f2fab
--- /dev/null
+++ b/effects/subtract.blend
@@ -0,0 +1,14 @@
+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)
+#olive name Subtract
\ No newline at end of file
diff --git a/effects/vivid-light.blend b/effects/vivid-light.blend
new file mode 100644
index 000000000..2f5fc9469
--- /dev/null
+++ b/effects/vivid-light.blend
@@ -0,0 +1,17 @@
+#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)
+#olive name Vivid Light
\ 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/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/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/mainwindow.cpp b/mainwindow.cpp
index 2ed4ea568..dd0ac9e8d 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -948,6 +948,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()) {
diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp
index 174baa795..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,13 +195,13 @@ 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);
- for (int i=0;isetObjectName("v");
@@ -1804,8 +1804,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..fab503d07 100644
--- a/project/effect.cpp
+++ b/project/effect.cpp
@@ -67,12 +67,14 @@
#include
#include
-QVector effects;
+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) {
+ 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 +92,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 +944,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..d97c2214b 100644
--- a/project/effect.h
+++ b/project/effect.h
@@ -58,7 +58,21 @@ struct EffectMeta {
int type;
int subtype;
};
-extern QVector effects;
+
+struct BlendMode {
+ QString name;
+ QString url;
+ QString function_name;
+
+ bool loaded;
+};
+
+namespace olive {
+ extern QVector effects;
+ extern QVector blend_modes;
+
+ extern QString generated_blending_shader;
+}
double log_volume(double linear);
@@ -94,36 +108,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;
@@ -281,11 +265,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 443caec1e..15f9ecc8b 100644
--- a/project/effectloaders.cpp
+++ b/project/effectloaders.cpp
@@ -33,239 +33,378 @@
#include
+QMutex olive::effects_loaded;
+
#ifndef NOFREI0R
#include
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;i blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), 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;i 0 && line.at(0) == '#') {
+
+ if (line.startsWith("#olive name ")) {
+
+ // The blending mode can specify its own name
+ b.name = line.mid(12);
+
+ } else if (line.startsWith("#pragma glslify: export(")) {
+
+ // Get function name
+ b.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;
+
+ QString include_fn = line.mid(index_of_last_bracked, line.length() - index_of_last_bracked - 1);
+ include_fn.append(".blend");
+ QString include_path = QFileInfo(b.url).dir().filePath(include_fn);
+
+ // see if this file is already in the blend modes list
+ bool found = false;
+ for (int i=0;i 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");
+}
+
EffectInit::EffectInit() {
- panel_effect_controls->effects_loaded.lock();
+ olive::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";
+ GenerateBlendingShader();
+ olive::effects_loaded.unlock();
+ qInfo() << "Finished initializing effects";
}
diff --git a/project/effectloaders.h b/project/effectloaders.h
index 9a4c2a86e..1c09268d4 100644
--- a/project/effectloaders.h
+++ b/project/effectloaders.h
@@ -22,7 +22,20 @@
#define EFFECTLOADERS_H
#include
+#include
+#include
+
+namespace olive {
+ extern QMutex effects_loaded;
+}
void init_effects();
+class EffectInit : public QThread {
+public:
+ EffectInit();
+protected:
+ void run();
+};
+
#endif // EFFECTLOADERS_H
diff --git a/rendering/renderfunctions.cpp b/rendering/renderfunctions.cpp
index 4f6a08990..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;
}
@@ -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
diff --git a/rendering/renderfunctions.h b/rendering/renderfunctions.h
index 3c1f49734..1d7ed9dc5 100644
--- a/rendering/renderfunctions.h
+++ b/rendering/renderfunctions.h
@@ -31,375 +31,363 @@
#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 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..8babc9edc 100644
--- a/rendering/renderthread.cpp
+++ b/rendering/renderthread.cpp
@@ -32,13 +32,13 @@ namespace OCIO = OCIO_NAMESPACE;
#include "rendering/renderfunctions.h"
#include "project/sequence.h"
+#include "project/effectloaders.h"
RenderThread::RenderThread() :
gizmos(nullptr),
share_ctx(nullptr),
ctx(nullptr),
blend_mode_program(nullptr),
- premultiply_program(nullptr),
seq(nullptr),
tex_width(-1),
tex_height(-1),
@@ -99,13 +99,10 @@ 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");
+ 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();
- premultiply_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert");
- premultiply_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/internalshaders/premultiply.frag");
- premultiply_program->link();
}
// draw frame
@@ -150,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();
@@ -277,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;