Merge branch 'modblending' into furtherocio
This commit is contained in:
@@ -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("<font color='%1'><b>[%2]</b> %3 (%4:%5, %6)</font><br>")
|
||||
.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("<font color='%1'><b>[%2]</b> %3 (%4:%5, %6)</font><br>")
|
||||
.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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/internalshaders">
|
||||
<file>blending.frag</file>
|
||||
<file>common.vert</file>
|
||||
<file>cornerpin.frag</file>
|
||||
<file>cornerpin.vert</file>
|
||||
|
||||
@@ -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;i<olive::blend_modes.size();i++) {
|
||||
blend_mode_box->add_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;i<field->keyframes.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;i<field->keyframes.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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+1
-2
@@ -285,6 +285,5 @@ void Config::save(QString path) {
|
||||
}
|
||||
|
||||
RuntimeConfig::RuntimeConfig() :
|
||||
shaders_are_enabled(true),
|
||||
disable_blending(false)
|
||||
shaders_are_enabled(true)
|
||||
{}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
+3
-2
@@ -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 <QFile>
|
||||
@@ -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") {
|
||||
|
||||
@@ -34,7 +34,7 @@ extern "C" {
|
||||
#include <libavfilter/avfilter.h>
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[]) {
|
||||
olive::Global = std::unique_ptr<OliveGlobal>(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 <file>\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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;i<effects.size();i++) {
|
||||
const EffectMeta& em = effects.at(i);
|
||||
for (int i=0;i<olive::effects.size();i++) {
|
||||
const EffectMeta& em = olive::effects.at(i);
|
||||
|
||||
if (em.type == type && em.subtype == subtype) {
|
||||
QAction* action = new QAction(&effects_menu);
|
||||
@@ -254,7 +255,7 @@ void EffectControls::show_effect_menu(int type, int subtype) {
|
||||
}
|
||||
}
|
||||
|
||||
effects_loaded.unlock();
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
|
||||
effects_menu.exec(QCursor::pos());
|
||||
|
||||
@@ -74,8 +74,6 @@ public:
|
||||
ResizableScrollBar* horizontalScrollBar;
|
||||
QScrollBar* verticalScrollBar;
|
||||
|
||||
QMutex effects_loaded;
|
||||
|
||||
void add_effect_paste_action(QMenu* menu);
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
+4
-4
@@ -1793,8 +1793,8 @@ void Timeline::transition_tool_click() {
|
||||
|
||||
QMenu transition_menu(this);
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
const EffectMeta& em = effects.at(i);
|
||||
for (int i=0;i<olive::effects.size();i++) {
|
||||
const EffectMeta& em = olive::effects.at(i);
|
||||
if (em.type == EFFECT_TYPE_TRANSITION && em.subtype == EFFECT_TYPE_VIDEO) {
|
||||
QAction* a = transition_menu.addAction(em.name);
|
||||
a->setObjectName("v");
|
||||
@@ -1804,8 +1804,8 @@ void Timeline::transition_tool_click() {
|
||||
|
||||
transition_menu.addSeparator();
|
||||
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
const EffectMeta& em = effects.at(i);
|
||||
for (int i=0;i<olive::effects.size();i++) {
|
||||
const EffectMeta& em = olive::effects.at(i);
|
||||
if (em.type == EFFECT_TYPE_TRANSITION && em.subtype == EFFECT_TYPE_AUDIO) {
|
||||
QAction* a = transition_menu.addAction(em.name);
|
||||
a->setObjectName("a");
|
||||
|
||||
+769
-767
File diff suppressed because it is too large
Load Diff
+15
-38
@@ -58,7 +58,21 @@ struct EffectMeta {
|
||||
int type;
|
||||
int subtype;
|
||||
};
|
||||
extern QVector<EffectMeta> effects;
|
||||
|
||||
struct BlendMode {
|
||||
QString name;
|
||||
QString url;
|
||||
QString function_name;
|
||||
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
extern QVector<EffectMeta> effects;
|
||||
extern QVector<BlendMode> 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
|
||||
|
||||
+306
-167
@@ -33,239 +33,378 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QMutex olive::effects_loaded;
|
||||
|
||||
#ifndef NOFREI0R
|
||||
#include <frei0r.h>
|
||||
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<QString> effects_paths = get_effects_paths();
|
||||
QList<QString> effects_paths = get_effects_paths();
|
||||
|
||||
for (int h=0;h<effects_paths.size();h++) {
|
||||
const QString& effects_path = effects_paths.at(h);
|
||||
QDir effects_dir(effects_path);
|
||||
if (effects_dir.exists()) {
|
||||
QList<QString> entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files);
|
||||
for (int i=0;i<entries.size();i++) {
|
||||
QFile file(effects_path + "/" + entries.at(i));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "Could not open" << entries.at(i);
|
||||
return;
|
||||
}
|
||||
for (int h=0;h<effects_paths.size();h++) {
|
||||
const QString& effects_path = effects_paths.at(h);
|
||||
QDir effects_dir(effects_path);
|
||||
if (effects_dir.exists()) {
|
||||
// Load XML metadata for GLSL shader effects
|
||||
QList<QString> entries = effects_dir.entryList(QStringList("*.xml"), QDir::Files);
|
||||
for (int i=0;i<entries.size();i++) {
|
||||
QFile file(effects_path + "/" + entries.at(i));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "Could not open" << entries.at(i);
|
||||
return;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(&file);
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "effect") {
|
||||
QString effect_name = "";
|
||||
QString effect_cat = "";
|
||||
const QXmlStreamAttributes attr = reader.attributes();
|
||||
for (int j=0;j<attr.size();j++) {
|
||||
if (attr.at(j).name() == "name") {
|
||||
effect_name = attr.at(j).value().toString();
|
||||
} else if (attr.at(j).name() == "category") {
|
||||
effect_cat = attr.at(j).value().toString();
|
||||
}
|
||||
}
|
||||
if (!effect_name.isEmpty()) {
|
||||
EffectMeta em;
|
||||
em.type = EFFECT_TYPE_EFFECT;
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
em.name = effect_name;
|
||||
em.category = effect_cat;
|
||||
em.filename = file.fileName();
|
||||
em.path = effects_path;
|
||||
em.internal = -1;
|
||||
effects.append(em);
|
||||
} else {
|
||||
qCritical() << "Invalid effect found in" << entries.at(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
QXmlStreamReader reader(&file);
|
||||
while (!reader.atEnd()) {
|
||||
if (reader.name() == "effect") {
|
||||
QString effect_name = "";
|
||||
QString effect_cat = "";
|
||||
const QXmlStreamAttributes attr = reader.attributes();
|
||||
for (int j=0;j<attr.size();j++) {
|
||||
if (attr.at(j).name() == "name") {
|
||||
effect_name = attr.at(j).value().toString();
|
||||
} else if (attr.at(j).name() == "category") {
|
||||
effect_cat = attr.at(j).value().toString();
|
||||
}
|
||||
}
|
||||
if (!effect_name.isEmpty()) {
|
||||
EffectMeta em;
|
||||
em.type = EFFECT_TYPE_EFFECT;
|
||||
em.subtype = EFFECT_TYPE_VIDEO;
|
||||
em.name = effect_name;
|
||||
em.category = effect_cat;
|
||||
em.filename = file.fileName();
|
||||
em.path = effects_path;
|
||||
em.internal = -1;
|
||||
olive::effects.append(em);
|
||||
} else {
|
||||
qCritical() << "Invalid effect found in" << entries.at(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
reader.readNext();
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Load blending mode shaders from file
|
||||
QList<QString> blend_mode_entries = effects_dir.entryList(QStringList("*.blend"), QDir::Files);
|
||||
for (int i=0;i<blend_mode_entries.size();i++) {
|
||||
BlendMode b;
|
||||
b.loaded = false;
|
||||
b.url = effects_dir.filePath(blend_mode_entries.at(i));
|
||||
b.name = QFileInfo(b.url).baseName();
|
||||
olive::blend_modes.append(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_effects() {
|
||||
EffectInit* init_thread = new EffectInit();
|
||||
QObject::connect(init_thread, SIGNAL(finished()), init_thread, SLOT(deleteLater()));
|
||||
init_thread->start();
|
||||
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<QString>& loaded_names) {
|
||||
QDir search_dir(dir);
|
||||
if (search_dir.exists()) {
|
||||
QList<QString> entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
for (int j=0;j<entry_list.size();j++) {
|
||||
QString entry_path = search_dir.filePath(entry_list.at(j));
|
||||
if (QFileInfo(entry_path).isDir()) {
|
||||
load_frei0r_effects_worker(entry_path, em, loaded_names);
|
||||
} else {
|
||||
ModulePtr effect = LibLoad(entry_path);
|
||||
if (effect != nullptr) {
|
||||
f0rGetPluginInfo get_info_func = reinterpret_cast<f0rGetPluginInfo>(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<QString> entry_list = search_dir.entryList(LibFilter(), QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
|
||||
for (int j=0;j<entry_list.size();j++) {
|
||||
QString entry_path = search_dir.filePath(entry_list.at(j));
|
||||
if (QFileInfo(entry_path).isDir()) {
|
||||
load_frei0r_effects_worker(entry_path, em, loaded_names);
|
||||
} else {
|
||||
ModulePtr effect = LibLoad(entry_path);
|
||||
if (effect != nullptr) {
|
||||
f0rGetPluginInfo get_info_func = reinterpret_cast<f0rGetPluginInfo>(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<QString> effect_dirs = get_effects_paths();
|
||||
QList<QString> 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<QString> loaded_names;
|
||||
QVector<QString> 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<effect_dirs.size();i++) {
|
||||
load_frei0r_effects_worker(effect_dirs.at(i), em, loaded_names);
|
||||
}
|
||||
for (int i=0;i<effect_dirs.size();i++) {
|
||||
load_frei0r_effects_worker(effect_dirs.at(i), em, loaded_names);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void IncludeBlendingShader(BlendMode& b) {
|
||||
if (!b.loaded) {
|
||||
QFile blending_file(b.url);
|
||||
|
||||
if (blending_file.open(QFile::ReadOnly)) {
|
||||
|
||||
QTextStream stream(&blending_file);
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
if (line.length() > 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<olive::blend_modes.size();i++) {
|
||||
if (QFileInfo(olive::blend_modes.at(i).url) == QFileInfo(include_path)) {
|
||||
|
||||
// It is, so we'll include it now rather than later
|
||||
IncludeBlendingShader(olive::blend_modes[i]);
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// include the file, but don't add it to the blend mode list
|
||||
BlendMode b;
|
||||
b.url = include_path;
|
||||
IncludeBlendingShader(b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Assume this line is code and add it to the shader
|
||||
olive::generated_blending_shader.append(line);
|
||||
olive::generated_blending_shader.append("\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
blending_file.close();
|
||||
|
||||
} else {
|
||||
qWarning() << "Failed to open blending shader" << b.url;
|
||||
}
|
||||
|
||||
b.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateBlendingShader()
|
||||
{
|
||||
olive::generated_blending_shader = "#version 110\n" // Start with GLSL version identifier
|
||||
"\n"
|
||||
"uniform sampler2D background;\n" // background (base) texture color
|
||||
"uniform sampler2D foreground;\n" // foreground (blend) texture color
|
||||
"varying vec2 vTexCoord;\n" // texture coordinate
|
||||
"uniform float opacity;\n" // foreground opacity setting
|
||||
"uniform int blendmode;\n" // blending mode switcher
|
||||
"\n"
|
||||
"\n";
|
||||
|
||||
// Import code from each blend mode file
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
IncludeBlendingShader(olive::blend_modes[i]);
|
||||
}
|
||||
|
||||
// Create monolithic switcher function
|
||||
|
||||
olive::generated_blending_shader.append("vec3 blend(vec3 base, vec3 blend, float opacity) {\n");
|
||||
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
if (i == 0) {
|
||||
olive::generated_blending_shader.append(" if (blendmode == 0) {\n");
|
||||
} else {
|
||||
olive::generated_blending_shader.append(QString(" else if (blendmode == %1) {\n").arg(i));
|
||||
}
|
||||
|
||||
olive::generated_blending_shader.append(QString(" return %1(base, blend, opacity);\n").arg(olive::blend_modes.at(i).function_name));
|
||||
olive::generated_blending_shader.append(" }");
|
||||
}
|
||||
|
||||
// Write the main() function for the shader
|
||||
|
||||
olive::generated_blending_shader.append("\n return blend;\n" // default return value
|
||||
"}\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec4 bg_color = texture2D(background, vTexCoord);\n" // Get background texture color
|
||||
" vec4 fg_color = texture2D(foreground, vTexCoord);\n" // Get foreground texture color
|
||||
" if (fg_color.a > 0.0) {\n"
|
||||
" float true_opacity = opacity * fg_color.a;\n"
|
||||
" vec3 unmultipled_fg = max(vec3(0.0), min(vec3(1.0), fg_color.rgb / fg_color.a));\n"
|
||||
" vec3 blended_rgb = blend(bg_color.rgb, unmultipled_fg, true_opacity);\n" // Use switcher function above to blend RGBs
|
||||
" vec4 composite = vec4(blended_rgb, bg_color.a + true_opacity);\n"
|
||||
" gl_FragColor = composite;\n"
|
||||
" } else {\n"
|
||||
" gl_FragColor = bg_color;\n"
|
||||
" }\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -22,7 +22,20 @@
|
||||
#define EFFECTLOADERS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
|
||||
namespace olive {
|
||||
extern QMutex effects_loaded;
|
||||
}
|
||||
|
||||
void init_effects();
|
||||
|
||||
class EffectInit : public QThread {
|
||||
public:
|
||||
EffectInit();
|
||||
protected:
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif // EFFECTLOADERS_H
|
||||
|
||||
@@ -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
|
||||
|
||||
+314
-326
@@ -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<Clip*> 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<Clip*> 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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user