started frontend for effect keyframes
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#include "effects/effects.h"
|
||||
#include "paneffect.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef PANEFFECT_H
|
||||
#define PANEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class PanEffect : public Effect {
|
||||
public:
|
||||
PanEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_audio(quint8* samples, int nb_bytes);
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* pan_val;
|
||||
};
|
||||
|
||||
#endif // PANEFFECT_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "effects/effects.h"
|
||||
#include "volumeeffect.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef VOLUMEEFFECT_H
|
||||
#define VOLUMEEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class VolumeEffect : public Effect {
|
||||
public:
|
||||
VolumeEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_audio(quint8* samples, int nb_bytes);
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* volume_val;
|
||||
};
|
||||
|
||||
#endif // VOLUMEEFFECT_H
|
||||
+64
-14
@@ -4,7 +4,6 @@
|
||||
#include "panels/viewer.h"
|
||||
#include "ui/viewerwidget.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "effects/effects.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/undo.h"
|
||||
#include "ui/labelslider.h"
|
||||
@@ -12,19 +11,65 @@
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
#include "ui/checkboxex.h"
|
||||
#include "project/clip.h"
|
||||
|
||||
#include "effects/video/transformeffect.h"
|
||||
#include "effects/video/inverteffect.h"
|
||||
#include "effects/video/shakeeffect.h"
|
||||
#include "effects/video/solideffect.h"
|
||||
#include "effects/video/texteffect.h"
|
||||
|
||||
#include "effects/audio/paneffect.h"
|
||||
#include "effects/audio/volumeeffect.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QGridLayout>
|
||||
#include <QTextEdit>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
QVector<QString> video_effect_names;
|
||||
QVector<QString> audio_effect_names;
|
||||
|
||||
void init_effects() {
|
||||
video_effect_names.resize(VIDEO_EFFECT_COUNT);
|
||||
audio_effect_names.resize(AUDIO_EFFECT_COUNT);
|
||||
|
||||
video_effect_names[VIDEO_TRANSFORM_EFFECT] = "Transform";
|
||||
video_effect_names[VIDEO_SHAKE_EFFECT] = "Shake";
|
||||
video_effect_names[VIDEO_TEXT_EFFECT] = "Text";
|
||||
video_effect_names[VIDEO_SOLID_EFFECT] = "Solid";
|
||||
video_effect_names[VIDEO_INVERT_EFFECT] = "Invert";
|
||||
|
||||
audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume";
|
||||
audio_effect_names[AUDIO_PAN_EFFECT] = "Pan";
|
||||
}
|
||||
|
||||
Effect* create_effect(int effect_id, Clip* c) {
|
||||
if (c->track < 0) {
|
||||
switch (effect_id) {
|
||||
case VIDEO_TRANSFORM_EFFECT: return new TransformEffect(c); break;
|
||||
case VIDEO_SHAKE_EFFECT: return new ShakeEffect(c); break;
|
||||
case VIDEO_TEXT_EFFECT: return new TextEffect(c); break;
|
||||
case VIDEO_SOLID_EFFECT: return new SolidEffect(c); break;
|
||||
case VIDEO_INVERT_EFFECT: return new InvertEffect(c); break;
|
||||
}
|
||||
} else {
|
||||
switch (effect_id) {
|
||||
case AUDIO_VOLUME_EFFECT: return new VolumeEffect(c); break;
|
||||
case AUDIO_PAN_EFFECT: return new PanEffect(c); break;
|
||||
}
|
||||
}
|
||||
qDebug() << "[ERROR] Invalid effect ID";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Effect::Effect(Clip* c, int t, int i) :
|
||||
parent_clip(c),
|
||||
type(t),
|
||||
id(i),
|
||||
enable_ffmpeg(false),
|
||||
enable_qimage(false),
|
||||
enable_pre_gl(false),
|
||||
enable_post_gl(false)
|
||||
enable_image(false),
|
||||
enable_opengl(false)
|
||||
|
||||
{
|
||||
container = new CollapsibleWidget();
|
||||
@@ -57,6 +102,10 @@ EffectRow* Effect::row(int i) {
|
||||
return rows.at(i);
|
||||
}
|
||||
|
||||
int Effect::row_count() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
void Effect::refresh() {}
|
||||
|
||||
void Effect::field_changed() {
|
||||
@@ -142,14 +191,15 @@ void Effect::save(QXmlStreamWriter* stream) {
|
||||
}
|
||||
|
||||
Effect* Effect::copy(Clip*) {return NULL;}
|
||||
void Effect::process_gl(int*, int*) {}
|
||||
void Effect::post_gl() {}
|
||||
void Effect::process_image(QImage&) {}
|
||||
void Effect::process_gl(QOpenGLShaderProgram&, int*, int*) {}
|
||||
void Effect::process_audio(uint8_t*, int) {}
|
||||
|
||||
/* Effect Row Definitions */
|
||||
|
||||
EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, int row) : parent_effect(parent), ui(uilayout), name(n), ui_row(row) {
|
||||
ui->addWidget(new QLabel(name), row, 0);
|
||||
label = new QLabel(name);
|
||||
ui->addWidget(label, row, 0);
|
||||
|
||||
/*keyframe_enable = new CheckboxEx();
|
||||
keyframe_enable->setToolTip("Enable Keyframes");
|
||||
@@ -258,27 +308,27 @@ void EffectField::set_double_maximum_value(double v) {
|
||||
}
|
||||
|
||||
void EffectField::add_combo_item(const QString& name, const QVariant& data) {
|
||||
static_cast<QComboBox*>(ui_element)->addItem(name, data);
|
||||
static_cast<ComboBoxEx*>(ui_element)->addItem(name, data);
|
||||
}
|
||||
|
||||
int EffectField::get_combo_index() {
|
||||
return static_cast<QComboBox*>(ui_element)->currentIndex();
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentIndex();
|
||||
}
|
||||
|
||||
const QVariant EffectField::get_combo_data() {
|
||||
return static_cast<QComboBox*>(ui_element)->currentData();
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentData();
|
||||
}
|
||||
|
||||
const QString EffectField::get_combo_string() {
|
||||
return static_cast<QComboBox*>(ui_element)->currentText();
|
||||
return static_cast<ComboBoxEx*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_combo_index(int index) {
|
||||
static_cast<QComboBox*>(ui_element)->setCurrentIndex(index);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentIndexEx(index);
|
||||
}
|
||||
|
||||
void EffectField::set_combo_string(const QString& s) {
|
||||
static_cast<QComboBox*>(ui_element)->setCurrentText(s);
|
||||
static_cast<ComboBoxEx*>(ui_element)->setCurrentTextEx(s);
|
||||
}
|
||||
|
||||
bool EffectField::get_bool_value() {
|
||||
|
||||
+28
-6
@@ -5,6 +5,8 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QColor>
|
||||
#include <QOpenGLShaderProgram>
|
||||
class QLabel;
|
||||
class QWidget;
|
||||
class CollapsibleWidget;
|
||||
class QGridLayout;
|
||||
@@ -15,6 +17,26 @@ class QXmlStreamWriter;
|
||||
class Effect;
|
||||
class CheckboxEx;
|
||||
|
||||
enum VideoEffects {
|
||||
VIDEO_TRANSFORM_EFFECT,
|
||||
VIDEO_SHAKE_EFFECT,
|
||||
VIDEO_TEXT_EFFECT,
|
||||
VIDEO_SOLID_EFFECT,
|
||||
VIDEO_INVERT_EFFECT,
|
||||
VIDEO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
enum AudioEffects {
|
||||
AUDIO_VOLUME_EFFECT,
|
||||
AUDIO_PAN_EFFECT,
|
||||
AUDIO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
extern QVector<QString> video_effect_names;
|
||||
extern QVector<QString> audio_effect_names;
|
||||
void init_effects();
|
||||
Effect* create_effect(int effect_id, Clip* c);
|
||||
|
||||
#define EFFECT_TYPE_INVALID 0
|
||||
#define EFFECT_TYPE_VIDEO 1
|
||||
#define EFFECT_TYPE_AUDIO 2
|
||||
@@ -93,6 +115,7 @@ public:
|
||||
void set_keyframe(int field, long time);
|
||||
void move_keyframe(int field, long from, long to);
|
||||
void delete_keyframe(int field, long time);
|
||||
QLabel* label;
|
||||
private:
|
||||
Effect* parent_effect;
|
||||
QGridLayout* ui;
|
||||
@@ -116,6 +139,7 @@ public:
|
||||
|
||||
EffectRow* add_row(const QString &name);
|
||||
EffectRow* row(int i);
|
||||
int row_count();
|
||||
|
||||
bool is_enabled();
|
||||
|
||||
@@ -126,15 +150,13 @@ public:
|
||||
void load(QXmlStreamReader* stream);
|
||||
void save(QXmlStreamWriter* stream);
|
||||
|
||||
bool enable_ffmpeg;
|
||||
bool enable_qimage;
|
||||
bool enable_pre_gl;
|
||||
bool enable_post_gl;
|
||||
bool enable_image;
|
||||
bool enable_opengl;
|
||||
|
||||
const char* ffmpeg_filter;
|
||||
|
||||
virtual void process_gl(int* anchor_x, int* anchor_y);
|
||||
virtual void post_gl();
|
||||
virtual void process_image(QImage& img);
|
||||
virtual void process_gl(QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
|
||||
virtual void process_audio(quint8* samples, int nb_bytes);
|
||||
public slots:
|
||||
void field_changed();
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "effects/effects.h"
|
||||
|
||||
#include "project/clip.h"
|
||||
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
|
||||
QVector<QString> video_effect_names;
|
||||
QVector<QString> audio_effect_names;
|
||||
|
||||
void init_effects() {
|
||||
video_effect_names.resize(VIDEO_EFFECT_COUNT);
|
||||
audio_effect_names.resize(AUDIO_EFFECT_COUNT);
|
||||
|
||||
video_effect_names[VIDEO_TRANSFORM_EFFECT] = "Transform";
|
||||
video_effect_names[VIDEO_SHAKE_EFFECT] = "Shake";
|
||||
video_effect_names[VIDEO_TEXT_EFFECT] = "Text";
|
||||
video_effect_names[VIDEO_SOLID_EFFECT] = "Solid";
|
||||
video_effect_names[VIDEO_INVERT_EFFECT] = "Invert";
|
||||
|
||||
audio_effect_names[AUDIO_VOLUME_EFFECT] = "Volume";
|
||||
audio_effect_names[AUDIO_PAN_EFFECT] = "Pan";
|
||||
}
|
||||
|
||||
Effect* create_effect(int effect_id, Clip* c) {
|
||||
if (c->track < 0) {
|
||||
switch (effect_id) {
|
||||
case VIDEO_TRANSFORM_EFFECT: return new TransformEffect(c); break;
|
||||
case VIDEO_SHAKE_EFFECT: return new ShakeEffect(c); break;
|
||||
case VIDEO_TEXT_EFFECT: return new TextEffect(c); break;
|
||||
case VIDEO_SOLID_EFFECT: return new SolidEffect(c); break;
|
||||
case VIDEO_INVERT_EFFECT: return new InvertEffect(c); break;
|
||||
}
|
||||
} else {
|
||||
switch (effect_id) {
|
||||
case AUDIO_VOLUME_EFFECT: return new VolumeEffect(c); break;
|
||||
case AUDIO_PAN_EFFECT: return new PanEffect(c); break;
|
||||
}
|
||||
}
|
||||
qDebug() << "[ERROR] Invalid effect ID";
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
#ifndef EFFECTS_H
|
||||
#define EFFECTS_H
|
||||
|
||||
#include "effect.h"
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QFont>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QWidget>
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
class LabelSlider;
|
||||
class QOpenGLTexture;
|
||||
class QTextEdit;
|
||||
class QPushButton;
|
||||
class ColorButton;
|
||||
class ComboBoxEx;
|
||||
|
||||
enum VideoEffects {
|
||||
VIDEO_TRANSFORM_EFFECT,
|
||||
VIDEO_SHAKE_EFFECT,
|
||||
VIDEO_TEXT_EFFECT,
|
||||
VIDEO_SOLID_EFFECT,
|
||||
VIDEO_INVERT_EFFECT,
|
||||
VIDEO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
enum AudioEffects {
|
||||
AUDIO_VOLUME_EFFECT,
|
||||
AUDIO_PAN_EFFECT,
|
||||
AUDIO_EFFECT_COUNT
|
||||
};
|
||||
|
||||
extern QVector<QString> video_effect_names;
|
||||
extern QVector<QString> audio_effect_names;
|
||||
void init_effects();
|
||||
Effect* create_effect(int effect_id, Clip* c);
|
||||
|
||||
// video effects
|
||||
class TransformEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_gl(int* anchor_x, int* anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* position_x;
|
||||
EffectField* position_y;
|
||||
EffectField* scale_x;
|
||||
EffectField* scale_y;
|
||||
EffectField* uniform_scale_field;
|
||||
EffectField* rotation;
|
||||
EffectField* anchor_x_box;
|
||||
EffectField* anchor_y_box;
|
||||
EffectField* opacity;
|
||||
EffectField* blend_mode_box;
|
||||
public slots:
|
||||
void toggle_uniform_scale(bool enabled);
|
||||
private:
|
||||
int default_anchor_x;
|
||||
int default_anchor_y;
|
||||
};
|
||||
|
||||
class ShakeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShakeEffect(Clip* c);
|
||||
void process_gl(int* anchor_x, int* anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* intensity_val;
|
||||
EffectField* rotation_val;
|
||||
EffectField* frequency_val;
|
||||
public slots:
|
||||
void refresh();
|
||||
private:
|
||||
int shake_progress;
|
||||
int shake_limit;
|
||||
int next_x;
|
||||
int next_y;
|
||||
int next_rot;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int offset_rot;
|
||||
int prev_x;
|
||||
int prev_y;
|
||||
int prev_rot;
|
||||
int perp_x;
|
||||
int perp_y;
|
||||
double t;
|
||||
bool inside;
|
||||
};
|
||||
|
||||
class TextEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextEffect(Clip* c);
|
||||
~TextEffect();
|
||||
void post_gl();
|
||||
void refresh();
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* text_val;
|
||||
EffectField* size_val;
|
||||
EffectField* set_color_button;
|
||||
EffectField* set_font_combobox;
|
||||
EffectField* halign_field;
|
||||
EffectField* valign_field;
|
||||
EffectField* word_wrap_field;
|
||||
|
||||
EffectField* outline_bool;
|
||||
EffectField* outline_width;
|
||||
EffectField* outline_color;
|
||||
|
||||
EffectField* shadow_bool;
|
||||
EffectField* shadow_distance;
|
||||
EffectField* shadow_color;
|
||||
EffectField* shadow_softness;
|
||||
EffectField* shadow_opacity;
|
||||
private slots:
|
||||
void update_texture();
|
||||
void outline_enable(bool);
|
||||
void shadow_enable(bool);
|
||||
private:
|
||||
void destroy_texture();
|
||||
QOpenGLTexture* texture;
|
||||
QFont font;
|
||||
QImage pixmap;
|
||||
};
|
||||
|
||||
class SolidEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SolidEffect(Clip* c);
|
||||
void post_gl();
|
||||
EffectField* solid_type;
|
||||
EffectField* solid_color_field;
|
||||
EffectField* opacity_field;
|
||||
private slots:
|
||||
void enable_color();
|
||||
void update_texture();
|
||||
private:
|
||||
QOpenGLTexture* texture;
|
||||
};
|
||||
|
||||
class InvertEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvertEffect(Clip* c);
|
||||
void process_gl(int *anchor_x, int *anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* amount_val;
|
||||
};
|
||||
|
||||
// audio effects
|
||||
class VolumeEffect : public Effect {
|
||||
public:
|
||||
VolumeEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_audio(quint8* samples, int nb_bytes);
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* volume_val;
|
||||
};
|
||||
|
||||
class PanEffect : public Effect {
|
||||
public:
|
||||
PanEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_audio(quint8* samples, int nb_bytes);
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* pan_val;
|
||||
};
|
||||
|
||||
#endif // EFFECTS_H
|
||||
@@ -1,34 +0,0 @@
|
||||
#include "effects/effects.h"
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT) {
|
||||
enable_ffmpeg = true;
|
||||
ffmpeg_filter = "negate";
|
||||
|
||||
EffectRow* amount_row = add_row("Amount:");
|
||||
amount_val = amount_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
amount_val->set_double_default_value(100);
|
||||
}
|
||||
|
||||
Effect* InvertEffect::copy(Clip* c) {
|
||||
/*InvertEffect* i = new InvertEffect(c);
|
||||
i->amount_val->set_value(amount_val->value());
|
||||
return i;*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InvertEffect::process_gl(int* anchor_x, int* anchor_y) {
|
||||
|
||||
// gl_FragColor = vec4(1.0 - textureColor.r, 1.0 -textureColor.g, 1.0 -textureColor.b, 1);
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
#include "effects.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QTextEdit>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QColorDialog>
|
||||
#include <QFontDatabase>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
|
||||
TextEffect::TextEffect(Clip *c) :
|
||||
Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT),
|
||||
texture(NULL)
|
||||
{
|
||||
enable_post_gl = true;
|
||||
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2);
|
||||
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, 2);
|
||||
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
|
||||
EffectRow* alignment_row = add_row("Alignment:");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
halign_field->add_combo_item("Left", Qt::AlignLeft);
|
||||
halign_field->add_combo_item("Center", Qt::AlignHCenter);
|
||||
halign_field->add_combo_item("Right", Qt::AlignRight);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
valign_field->add_combo_item("Top", Qt::AlignTop);
|
||||
valign_field->add_combo_item("Center", Qt::AlignVCenter);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
|
||||
outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
size_val->set_double_default_value(48);
|
||||
text_val->set_string_value("Sample Text");
|
||||
halign_field->set_combo_index(1);
|
||||
valign_field->set_combo_index(1);
|
||||
word_wrap_field->set_bool_value(true);
|
||||
outline_color->set_color_value(Qt::black);
|
||||
shadow_color->set_color_value(Qt::black);
|
||||
shadow_opacity->set_double_default_value(100);
|
||||
shadow_softness->set_double_default_value(5);
|
||||
shadow_distance->set_double_default_value(5);
|
||||
shadow_opacity->set_double_default_value(80);
|
||||
outline_width->set_double_default_value(2);
|
||||
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
|
||||
connect(text_val, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(size_val, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(set_color_button, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(set_font_combobox, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(halign_field, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(valign_field, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(word_wrap_field, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(outline_bool, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(outline_color, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(outline_width, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(shadow_bool, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(shadow_color, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(shadow_distance, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(shadow_softness, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(shadow_opacity, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
|
||||
connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
|
||||
update_texture();
|
||||
}
|
||||
|
||||
TextEffect::~TextEffect() {
|
||||
destroy_texture();
|
||||
}
|
||||
|
||||
void TextEffect::refresh() {
|
||||
update_texture();
|
||||
}
|
||||
|
||||
void TextEffect::destroy_texture() {
|
||||
texture->destroy();
|
||||
}
|
||||
|
||||
void TextEffect::shadow_enable(bool e) {
|
||||
shadow_color->set_enabled(e);
|
||||
shadow_distance->set_enabled(e);
|
||||
shadow_softness->set_enabled(e);
|
||||
shadow_opacity->set_enabled(e);
|
||||
}
|
||||
|
||||
void TextEffect::outline_enable(bool e) {
|
||||
outline_color->set_enabled(e);
|
||||
outline_width->set_enabled(e);
|
||||
}
|
||||
|
||||
QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false)
|
||||
{
|
||||
int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
|
||||
int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1];
|
||||
|
||||
QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
int r1 = rect.top();
|
||||
int r2 = rect.bottom();
|
||||
int c1 = rect.left();
|
||||
int c2 = rect.right();
|
||||
|
||||
int bpl = result.bytesPerLine();
|
||||
int rgba[4];
|
||||
unsigned char* p;
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 3;
|
||||
|
||||
if (alphaOnly)
|
||||
i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r1) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += bpl;
|
||||
for (int j = r1; j < r2; j++, p += bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c1 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += 4;
|
||||
for (int j = c1; j < c2; j++, p += 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r2) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= bpl;
|
||||
for (int j = r1; j < r2; j++, p -= bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c2 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= 4;
|
||||
for (int j = c1; j < c2; j++, p -= 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextEffect::update_texture() {
|
||||
if (parent_clip->sequence != NULL) {
|
||||
if (pixmap.width() != parent_clip->sequence->width || pixmap.height() != parent_clip->sequence->height) {
|
||||
pixmap = QImage(parent_clip->sequence->width, parent_clip->sequence->height, QImage::Format_ARGB32);
|
||||
}
|
||||
pixmap.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&pixmap);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily(set_font_combobox->get_font_name());
|
||||
font.setPointSize(size_val->get_double_value());
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
QStringList lines = text_val->get_string_value().split('\n');
|
||||
|
||||
// word wrap function
|
||||
if (word_wrap_field->get_bool_value()) {
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
const QString& s = lines.at(i);
|
||||
if (fm.width(s) > pixmap.width()) {
|
||||
int last_space_index = 0;
|
||||
for (int j=0;j<s.length();j++) {
|
||||
if (s.at(j) == ' ') {
|
||||
if (fm.width(s.left(j)) > pixmap.width()) {
|
||||
break;
|
||||
} else {
|
||||
last_space_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_space_index > 0) {
|
||||
lines.insert(i+1, s.mid(last_space_index + 1));
|
||||
lines[i] = s.left(last_space_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
int text_height = fm.height()*lines.size();
|
||||
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
int text_x, text_y;
|
||||
|
||||
switch (halign_field->get_combo_data().toInt()) {
|
||||
case Qt::AlignLeft: text_x = 0; break;
|
||||
case Qt::AlignHCenter: text_x = (pixmap.width()/2) - (fm.width(lines.at(i))/2); break;
|
||||
case Qt::AlignRight: text_x = pixmap.width() - fm.width(lines.at(i)); break;
|
||||
case Qt::AlignJustify:
|
||||
// add spaces until the string is too big
|
||||
text_x = 0;
|
||||
while (fm.width(lines.at(i) < pixmap.width())) {
|
||||
bool space = false;
|
||||
QString spaced(lines.at(i));
|
||||
for (int i=0;i<spaced.length();i++) {
|
||||
if (spaced.at(i) == ' ') {
|
||||
// insert a space
|
||||
spaced.insert(i, ' ');
|
||||
space = true;
|
||||
|
||||
// scan to next non-space
|
||||
while (i < spaced.length() && spaced.at(i) == ' ') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fm.width(spaced) > pixmap.width() || !space) {
|
||||
break;
|
||||
} else {
|
||||
lines[i] = spaced;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valign_field->get_combo_data().toInt()) {
|
||||
case Qt::AlignTop: text_y = (fm.height()*i)+fm.ascent(); break;
|
||||
case Qt::AlignVCenter: text_y = ((pixmap.height()/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1)); break;
|
||||
case Qt::AlignBottom: text_y = (pixmap.height() - text_height - fm.descent()) + (fm.height()*(i+1)); break;
|
||||
}
|
||||
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
// draw shadow
|
||||
if (shadow_bool->get_bool_value()) {
|
||||
int shadow_offset = shadow_distance->get_double_value();
|
||||
QColor col = shadow_color->get_color_value();
|
||||
col.setAlphaF(shadow_opacity->get_double_value()*0.01);
|
||||
p.setBrush(col);
|
||||
p.drawPath(path);
|
||||
|
||||
QImage shadow = blurred(pixmap, pixmap.rect(), shadow_softness->get_double_value(), false);
|
||||
p.drawImage(shadow_offset, shadow_offset, shadow);
|
||||
}
|
||||
|
||||
// draw outline
|
||||
int outline_width_val = outline_width->get_double_value();
|
||||
if (outline_bool->get_bool_value() && outline_width_val > 0) {
|
||||
QPen outline(outline_color->get_color_value());
|
||||
outline.setWidth(outline_width_val);
|
||||
p.setPen(outline);
|
||||
}
|
||||
|
||||
// draw "master" text
|
||||
p.setBrush(set_color_button->get_color_value());
|
||||
p.drawPath(path);
|
||||
|
||||
p.end();
|
||||
|
||||
if (texture == NULL) {
|
||||
texture = new QOpenGLTexture(pixmap);
|
||||
} else {
|
||||
destroy_texture();
|
||||
texture->setData(pixmap);
|
||||
}
|
||||
|
||||
texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
|
||||
|
||||
// queue a repaint of the canvas
|
||||
field_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void TextEffect::post_gl() {
|
||||
if (texture != NULL) {
|
||||
texture->bind();
|
||||
|
||||
int half_width = pixmap.width()/2;
|
||||
int half_height = pixmap.height()/2;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex2f(-half_width, -half_height);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex2f(half_width, -half_height);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex2f(half_width, half_height);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex2f(-half_width, half_height);
|
||||
glEnd();
|
||||
|
||||
texture->release();
|
||||
}
|
||||
}
|
||||
|
||||
Effect* TextEffect::copy(Clip* c) {
|
||||
TextEffect* e = new TextEffect(c);
|
||||
e->text_val->set_string_value(text_val->get_string_value());
|
||||
e->size_val->set_double_value(size_val->get_double_value());
|
||||
e->set_color_button->set_color_value(set_color_button->get_color_value());
|
||||
e->set_font_combobox->set_font_name(set_font_combobox->get_font_name());
|
||||
e->halign_field->set_combo_index(halign_field->get_combo_index());
|
||||
e->valign_field->set_combo_index(valign_field->get_combo_index());
|
||||
return e;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "inverteffect.h"
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGridLayout>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamAttributes>
|
||||
|
||||
InvertEffect::InvertEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_INVERT_EFFECT), vert_shader(QOpenGLShader::Vertex), frag_shader(QOpenGLShader::Fragment) {
|
||||
enable_opengl = true;
|
||||
|
||||
EffectRow* amount_row = add_row("Amount:");
|
||||
amount_val = amount_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
amount_val->set_double_minimum_value(0);
|
||||
amount_val->set_double_maximum_value(100);
|
||||
|
||||
// set defaults
|
||||
amount_val->set_double_default_value(100);
|
||||
|
||||
connect(amount_val, SIGNAL(changed()), this, SLOT(compile()));
|
||||
|
||||
compile();
|
||||
}
|
||||
|
||||
void InvertEffect::compile() {
|
||||
double value = amount_val->get_double_value()*0.01;
|
||||
vert_shader.compileSourceCode("varying vec2 vTexCoord; void main() { vTexCoord = gl_MultiTexCoord0; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; }");
|
||||
frag_shader.compileSourceCode("uniform sampler2D myTexture; varying vec2 vTexCoord; void main(void) { vec4 textureColor = texture2D(myTexture, vTexCoord); gl_FragColor = vec4(textureColor.r+((1.0-textureColor.r-textureColor.r)*" + QString::number(value) + "), textureColor.g+((1.0-textureColor.g-textureColor.g)*" + QString::number(value) + "), textureColor.b+((1.0-textureColor.b-textureColor.b)*" + QString::number(value) + "), 1); }");
|
||||
field_changed();
|
||||
}
|
||||
|
||||
Effect* InvertEffect::copy(Clip* c) {
|
||||
InvertEffect* i = new InvertEffect(c);
|
||||
i->amount_val->set_double_value(amount_val->get_double_value());
|
||||
return i;
|
||||
}
|
||||
|
||||
void InvertEffect::process_gl(QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y) {
|
||||
shader_prog.addShader(&vert_shader);
|
||||
shader_prog.addShader(&frag_shader);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef INVERTEFFECT_H
|
||||
#define INVERTEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
//#include <QOpenGLShader>
|
||||
|
||||
class InvertEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
InvertEffect(Clip* c);
|
||||
void process_gl(QOpenGLShaderProgram& shader_prog, int *anchor_x, int *anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* amount_val;
|
||||
private slots:
|
||||
void compile();
|
||||
private:
|
||||
QOpenGLShader vert_shader;
|
||||
QOpenGLShader frag_shader;
|
||||
};
|
||||
|
||||
#endif // INVERTEFFECT_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "effects/effects.h"
|
||||
#include "shakeeffect.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "panels/timeline.h"
|
||||
|
||||
ShakeEffect::ShakeEffect(Clip *c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SHAKE_EFFECT), inside(false) {
|
||||
enable_pre_gl = true;
|
||||
enable_opengl = true;
|
||||
|
||||
EffectRow* intensity_row = add_row("Intensity:");
|
||||
intensity_val = intensity_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
@@ -57,7 +57,7 @@ Effect* ShakeEffect::copy(Clip* c) {
|
||||
return e;
|
||||
}
|
||||
|
||||
void ShakeEffect::process_gl(int*, int*) {
|
||||
void ShakeEffect::process_gl(QOpenGLShaderProgram&, int*, int*) {
|
||||
if (shake_progress > shake_limit) {
|
||||
double ival = intensity_val->get_double_value();
|
||||
if ((int)ival > 0) {
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef SHAKEEFFECT_H
|
||||
#define SHAKEEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class ShakeEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShakeEffect(Clip* c);
|
||||
void process_gl(QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* intensity_val;
|
||||
EffectField* rotation_val;
|
||||
EffectField* frequency_val;
|
||||
public slots:
|
||||
void refresh();
|
||||
private:
|
||||
int shake_progress;
|
||||
int shake_limit;
|
||||
int next_x;
|
||||
int next_y;
|
||||
int next_rot;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int offset_rot;
|
||||
int prev_x;
|
||||
int prev_y;
|
||||
int prev_rot;
|
||||
int perp_x;
|
||||
int perp_y;
|
||||
double t;
|
||||
bool inside;
|
||||
};
|
||||
|
||||
#endif // SHAKEEFFECT_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "effects.h"
|
||||
#include "solideffect.h"
|
||||
|
||||
#include <QOpenGLTexture>
|
||||
#include <QPainter>
|
||||
@@ -15,8 +15,8 @@
|
||||
#define SMPTE_STRIP_COUNT 3
|
||||
#define SMPTE_LOWER_BARS 4
|
||||
|
||||
SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT), texture(NULL) {
|
||||
enable_post_gl = true;
|
||||
SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFFECT) {
|
||||
enable_image = true;
|
||||
|
||||
solid_type = add_row("Type:")->add_field(EFFECT_FIELD_COMBO);
|
||||
solid_type->add_combo_item("Solid Color", SOLID_TYPE_COLOR);
|
||||
@@ -30,30 +30,30 @@ SolidEffect::SolidEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_SOLID_EFF
|
||||
opacity_field->set_double_maximum_value(100);
|
||||
opacity_field->set_double_default_value(100);
|
||||
|
||||
connect(solid_type, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(solid_type, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(solid_type, SIGNAL(changed()), this, SLOT(enable_color()));
|
||||
connect(solid_color_field, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(solid_color_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(opacity_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
update_texture();
|
||||
}
|
||||
|
||||
void SolidEffect::enable_color() {
|
||||
solid_color_field->set_enabled(solid_type->get_combo_data() == SOLID_TYPE_COLOR);
|
||||
}
|
||||
|
||||
void SolidEffect::update_texture() {
|
||||
QImage img(parent_clip->sequence->width, parent_clip->sequence->height, QImage::Format_RGB888);
|
||||
void SolidEffect::process_image(QImage& img) {
|
||||
QPainter p(&img);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
|
||||
switch (solid_type->get_combo_data().toInt()) {
|
||||
case SOLID_TYPE_COLOR:
|
||||
img.fill(solid_color_field->get_color_value());
|
||||
{
|
||||
QColor brush = solid_color_field->get_color_value();
|
||||
p.fillRect(0, 0, width, height, QColor(brush.red(), brush.green(), brush.blue(), opacity_field->get_double_value()*2.55));
|
||||
}
|
||||
break;
|
||||
case SOLID_TYPE_BARS:
|
||||
// draw smpte bars
|
||||
img.fill(Qt::black);
|
||||
|
||||
QPainter p(&img);
|
||||
int bar_width = qCeil((double) parent_clip->sequence->width / 7.0);
|
||||
int first_bar_height = qCeil((double) parent_clip->sequence->height / 3.0 * 2.0);
|
||||
int second_bar_height = qCeil((double) parent_clip->sequence->height / 12.5);
|
||||
@@ -130,41 +130,4 @@ void SolidEffect::update_texture() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (texture == NULL) {
|
||||
texture = new QOpenGLTexture(img);
|
||||
} else {
|
||||
texture->destroy();
|
||||
texture->setData(img);
|
||||
}
|
||||
|
||||
field_changed();
|
||||
}
|
||||
|
||||
void SolidEffect::post_gl() {
|
||||
if (texture != NULL) {
|
||||
float color[4];
|
||||
glGetFloatv(GL_CURRENT_COLOR, color);
|
||||
glColor4f(1.0, 1.0, 1.0, color[3]*(opacity_field->get_double_value()*0.01));
|
||||
|
||||
texture->bind();
|
||||
|
||||
int half_width = parent_clip->sequence->width/2;
|
||||
int half_height = parent_clip->sequence->height/2;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex2f(-half_width, -half_height);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex2f(half_width, -half_height);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex2f(half_width, half_height);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex2f(-half_width, half_height);
|
||||
glEnd();
|
||||
|
||||
texture->release();
|
||||
|
||||
glColor4f(color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef SOLIDEFFECT_H
|
||||
#define SOLIDEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class SolidEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SolidEffect(Clip* c);
|
||||
EffectField* solid_type;
|
||||
EffectField* solid_color_field;
|
||||
EffectField* opacity_field;
|
||||
void process_image(QImage &img);
|
||||
private slots:
|
||||
void enable_color();
|
||||
};
|
||||
|
||||
#endif // SOLIDEFFECT_H
|
||||
@@ -0,0 +1,370 @@
|
||||
#include "texteffect.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QTextEdit>
|
||||
#include <QPainter>
|
||||
#include <QPushButton>
|
||||
#include <QColorDialog>
|
||||
#include <QFontDatabase>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/clip.h"
|
||||
#include "project/sequence.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "ui/colorbutton.h"
|
||||
#include "ui/fontcombobox.h"
|
||||
|
||||
TextEffect::TextEffect(Clip *c) :
|
||||
Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT)
|
||||
{
|
||||
enable_image = true;
|
||||
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2);
|
||||
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, 2);
|
||||
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
set_color_button = add_row("Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
|
||||
EffectRow* alignment_row = add_row("Alignment:");
|
||||
halign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
halign_field->add_combo_item("Left", Qt::AlignLeft);
|
||||
halign_field->add_combo_item("Center", Qt::AlignHCenter);
|
||||
halign_field->add_combo_item("Right", Qt::AlignRight);
|
||||
halign_field->add_combo_item("Justify", Qt::AlignJustify);
|
||||
valign_field = alignment_row->add_field(EFFECT_FIELD_COMBO);
|
||||
valign_field->add_combo_item("Top", Qt::AlignTop);
|
||||
valign_field->add_combo_item("Center", Qt::AlignVCenter);
|
||||
valign_field->add_combo_item("Bottom", Qt::AlignBottom);
|
||||
|
||||
word_wrap_field = add_row("Word Wrap:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
|
||||
outline_bool = add_row("Outline:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
outline_color = add_row("Outline Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
outline_width = add_row("Outline Width:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
outline_width->set_double_minimum_value(0);
|
||||
|
||||
shadow_bool = add_row("Shadow:")->add_field(EFFECT_FIELD_BOOL, 2);
|
||||
shadow_color = add_row("Shadow Color:")->add_field(EFFECT_FIELD_COLOR, 2);
|
||||
shadow_distance = add_row("Shadow Distance:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_distance->set_double_minimum_value(0);
|
||||
shadow_softness = add_row("Shadow Softness:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_softness->set_double_minimum_value(0);
|
||||
shadow_opacity = add_row("Shadow Opacity:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
shadow_opacity->set_double_minimum_value(0);
|
||||
shadow_opacity->set_double_maximum_value(100);
|
||||
|
||||
size_val->set_double_default_value(48);
|
||||
text_val->set_string_value("Sample Text");
|
||||
halign_field->set_combo_index(1);
|
||||
valign_field->set_combo_index(1);
|
||||
word_wrap_field->set_bool_value(true);
|
||||
outline_color->set_color_value(Qt::black);
|
||||
shadow_color->set_color_value(Qt::black);
|
||||
shadow_opacity->set_double_default_value(100);
|
||||
shadow_softness->set_double_default_value(5);
|
||||
shadow_distance->set_double_default_value(5);
|
||||
shadow_opacity->set_double_default_value(80);
|
||||
outline_width->set_double_default_value(2);
|
||||
|
||||
outline_enable(false);
|
||||
shadow_enable(false);
|
||||
|
||||
connect(text_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(size_val, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(set_color_button, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(set_font_combobox, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(halign_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(valign_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(word_wrap_field, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(outline_bool, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(outline_color, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(outline_width, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(shadow_bool, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(shadow_color, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(shadow_distance, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(shadow_softness, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
connect(shadow_opacity, SIGNAL(changed()), this, SLOT(field_changed()));
|
||||
|
||||
connect(shadow_bool, SIGNAL(toggled(bool)), this, SLOT(shadow_enable(bool)));
|
||||
connect(outline_bool, SIGNAL(toggled(bool)), this, SLOT(outline_enable(bool)));
|
||||
}
|
||||
|
||||
void TextEffect::shadow_enable(bool e) {
|
||||
shadow_color->set_enabled(e);
|
||||
shadow_distance->set_enabled(e);
|
||||
shadow_softness->set_enabled(e);
|
||||
shadow_opacity->set_enabled(e);
|
||||
}
|
||||
|
||||
void TextEffect::outline_enable(bool e) {
|
||||
outline_color->set_enabled(e);
|
||||
outline_width->set_enabled(e);
|
||||
}
|
||||
|
||||
QImage blurred(const QImage& image, const QRect& rect, int radius, bool alphaOnly = false)
|
||||
{
|
||||
int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
|
||||
int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1];
|
||||
|
||||
QImage result = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
int r1 = rect.top();
|
||||
int r2 = rect.bottom();
|
||||
int c1 = rect.left();
|
||||
int c2 = rect.right();
|
||||
|
||||
int bpl = result.bytesPerLine();
|
||||
int rgba[4];
|
||||
unsigned char* p;
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 3;
|
||||
|
||||
if (alphaOnly)
|
||||
i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r1) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += bpl;
|
||||
for (int j = r1; j < r2; j++, p += bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c1 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += 4;
|
||||
for (int j = c1; j < c2; j++, p += 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r2) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= bpl;
|
||||
for (int j = r1; j < r2; j++, p -= bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c2 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= 4;
|
||||
for (int j = c1; j < c2; j++, p -= 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void blurred2(QImage& result, const QRect& rect, int radius, bool alphaOnly = false) {
|
||||
int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
|
||||
int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius-1];
|
||||
|
||||
int r1 = rect.top();
|
||||
int r2 = rect.bottom();
|
||||
int c1 = rect.left();
|
||||
int c2 = rect.right();
|
||||
|
||||
int bpl = result.bytesPerLine();
|
||||
int rgba[4];
|
||||
unsigned char* p;
|
||||
|
||||
int i1 = 0;
|
||||
int i2 = 3;
|
||||
|
||||
if (alphaOnly)
|
||||
i1 = i2 = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r1) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += bpl;
|
||||
for (int j = r1; j < r2; j++, p += bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c1 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p += 4;
|
||||
for (int j = c1; j < c2; j++, p += 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int col = c1; col <= c2; col++) {
|
||||
p = result.scanLine(r2) + col * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= bpl;
|
||||
for (int j = r1; j < r2; j++, p -= bpl)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
|
||||
for (int row = r1; row <= r2; row++) {
|
||||
p = result.scanLine(row) + c2 * 4;
|
||||
for (int i = i1; i <= i2; i++)
|
||||
rgba[i] = p[i] << 4;
|
||||
|
||||
p -= 4;
|
||||
for (int j = c1; j < c2; j++, p -= 4)
|
||||
for (int i = i1; i <= i2; i++)
|
||||
p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
void TextEffect::process_image(QImage& img) {
|
||||
QPainter p(&img);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
|
||||
// set font
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
font.setFamily(set_font_combobox->get_font_name());
|
||||
font.setPointSize(size_val->get_double_value());
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
QStringList lines = text_val->get_string_value().split('\n');
|
||||
|
||||
// word wrap function
|
||||
if (word_wrap_field->get_bool_value()) {
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
const QString& s = lines.at(i);
|
||||
if (fm.width(s) > width) {
|
||||
int last_space_index = 0;
|
||||
for (int j=0;j<s.length();j++) {
|
||||
if (s.at(j) == ' ') {
|
||||
if (fm.width(s.left(j)) > width) {
|
||||
break;
|
||||
} else {
|
||||
last_space_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (last_space_index > 0) {
|
||||
lines.insert(i+1, s.mid(last_space_index + 1));
|
||||
lines[i] = s.left(last_space_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
int text_height = fm.height()*lines.size();
|
||||
|
||||
for (int i=0;i<lines.size();i++) {
|
||||
int text_x, text_y;
|
||||
|
||||
switch (halign_field->get_combo_data().toInt()) {
|
||||
case Qt::AlignLeft: text_x = 0; break;
|
||||
case Qt::AlignHCenter: text_x = (width/2) - (fm.width(lines.at(i))/2); break;
|
||||
case Qt::AlignRight: text_x = width - fm.width(lines.at(i)); break;
|
||||
case Qt::AlignJustify:
|
||||
// add spaces until the string is too big
|
||||
text_x = 0;
|
||||
while (fm.width(lines.at(i) < width)) {
|
||||
bool space = false;
|
||||
QString spaced(lines.at(i));
|
||||
for (int i=0;i<spaced.length();i++) {
|
||||
if (spaced.at(i) == ' ') {
|
||||
// insert a space
|
||||
spaced.insert(i, ' ');
|
||||
space = true;
|
||||
|
||||
// scan to next non-space
|
||||
while (i < spaced.length() && spaced.at(i) == ' ') i++;
|
||||
}
|
||||
}
|
||||
if (fm.width(spaced) > width || !space) {
|
||||
break;
|
||||
} else {
|
||||
lines[i] = spaced;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (valign_field->get_combo_data().toInt()) {
|
||||
case Qt::AlignTop: text_y = (fm.height()*i)+fm.ascent(); break;
|
||||
case Qt::AlignVCenter: text_y = ((height/2) - (text_height/2) - fm.descent()) + (fm.height()*(i+1)); break;
|
||||
case Qt::AlignBottom: text_y = (height - text_height - fm.descent()) + (fm.height()*(i+1)); break;
|
||||
}
|
||||
|
||||
path.addText(text_x, text_y, font, lines.at(i));
|
||||
}
|
||||
|
||||
p.setPen(Qt::NoPen);
|
||||
|
||||
// draw shadow
|
||||
if (shadow_bool->get_bool_value()) {
|
||||
QImage shadow(width, height, QImage::Format_ARGB32_Premultiplied);
|
||||
shadow.fill(Qt::transparent);
|
||||
QPainter spaint(&shadow);
|
||||
|
||||
int shadow_offset = shadow_distance->get_double_value();
|
||||
QColor col = shadow_color->get_color_value();
|
||||
col.setAlphaF(shadow_opacity->get_double_value()*0.01);
|
||||
spaint.setBrush(col);
|
||||
spaint.drawPath(path);
|
||||
|
||||
blurred2(shadow, shadow.rect(), shadow_softness->get_double_value(), false);
|
||||
p.drawImage(shadow_offset, shadow_offset, shadow);
|
||||
|
||||
spaint.end();
|
||||
}
|
||||
|
||||
// draw outline
|
||||
int outline_width_val = outline_width->get_double_value();
|
||||
if (outline_bool->get_bool_value() && outline_width_val > 0) {
|
||||
QPen outline(outline_color->get_color_value());
|
||||
outline.setWidth(outline_width_val);
|
||||
p.setPen(outline);
|
||||
}
|
||||
|
||||
// draw "master" text
|
||||
p.setBrush(set_color_button->get_color_value());
|
||||
p.drawPath(path);
|
||||
}
|
||||
|
||||
Effect* TextEffect::copy(Clip* c) {
|
||||
TextEffect* e = new TextEffect(c);
|
||||
e->text_val->set_string_value(text_val->get_string_value());
|
||||
e->size_val->set_double_value(size_val->get_double_value());
|
||||
e->set_color_button->set_color_value(set_color_button->get_color_value());
|
||||
e->set_font_combobox->set_font_name(set_font_combobox->get_font_name());
|
||||
e->halign_field->set_combo_index(halign_field->get_combo_index());
|
||||
e->valign_field->set_combo_index(valign_field->get_combo_index());
|
||||
return e;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef TEXTEFFECT_H
|
||||
#define TEXTEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
#include <QFont>
|
||||
|
||||
class TextEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TextEffect(Clip* c);
|
||||
void process_image(QImage& img);
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* text_val;
|
||||
EffectField* size_val;
|
||||
EffectField* set_color_button;
|
||||
EffectField* set_font_combobox;
|
||||
EffectField* halign_field;
|
||||
EffectField* valign_field;
|
||||
EffectField* word_wrap_field;
|
||||
|
||||
EffectField* outline_bool;
|
||||
EffectField* outline_width;
|
||||
EffectField* outline_color;
|
||||
|
||||
EffectField* shadow_bool;
|
||||
EffectField* shadow_distance;
|
||||
EffectField* shadow_color;
|
||||
EffectField* shadow_softness;
|
||||
EffectField* shadow_opacity;
|
||||
private slots:
|
||||
void outline_enable(bool);
|
||||
void shadow_enable(bool);
|
||||
private:
|
||||
QFont font;
|
||||
};
|
||||
|
||||
#endif // TEXTEFFECT_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "effects/effects.h"
|
||||
#include "transformeffect.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
@@ -23,7 +23,7 @@
|
||||
#define BLEND_MODE_OVERLAY 3
|
||||
|
||||
TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TRANSFORM_EFFECT) {
|
||||
enable_pre_gl = true;
|
||||
enable_opengl = true;
|
||||
|
||||
EffectRow* position_row = add_row("Position:");
|
||||
position_x = position_row->add_field(EFFECT_FIELD_DOUBLE); // position X
|
||||
@@ -136,7 +136,7 @@ void TransformEffect::toggle_uniform_scale(bool enabled) {
|
||||
scale_y->set_enabled(!enabled);
|
||||
}
|
||||
|
||||
void TransformEffect::process_gl(int* anchor_x, int* anchor_y) {
|
||||
void TransformEffect::process_gl(QOpenGLShaderProgram&, int* anchor_x, int* anchor_y) {
|
||||
// position
|
||||
glTranslatef(position_x->get_double_value()-(parent_clip->sequence->width/2), position_y->get_double_value()-(parent_clip->sequence->height/2), 0);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef TRANSFORMEFFECT_H
|
||||
#define TRANSFORMEFFECT_H
|
||||
|
||||
#include "../effect.h"
|
||||
|
||||
class TransformEffect : public Effect {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TransformEffect(Clip* c);
|
||||
void refresh();
|
||||
void process_gl(QOpenGLShaderProgram& shader_prog, int* anchor_x, int* anchor_y);
|
||||
Effect* copy(Clip *c);
|
||||
|
||||
EffectField* position_x;
|
||||
EffectField* position_y;
|
||||
EffectField* scale_x;
|
||||
EffectField* scale_y;
|
||||
EffectField* uniform_scale_field;
|
||||
EffectField* rotation;
|
||||
EffectField* anchor_x_box;
|
||||
EffectField* anchor_y_box;
|
||||
EffectField* opacity;
|
||||
EffectField* blend_mode_box;
|
||||
public slots:
|
||||
void toggle_uniform_scale(bool enabled);
|
||||
private:
|
||||
int default_anchor_x;
|
||||
int default_anchor_y;
|
||||
};
|
||||
|
||||
#endif // TRANSFORMEFFECT_H
|
||||
@@ -206,6 +206,8 @@ void MainWindow::on_actionZoom_In_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->set_zoom(true);
|
||||
} else if (panel_effect_controls->keyframe_focus()) {
|
||||
panel_effect_controls->set_zoom(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +215,8 @@ void MainWindow::on_actionZoom_out_triggered()
|
||||
{
|
||||
if (panel_timeline->focused()) {
|
||||
panel_timeline->set_zoom(false);
|
||||
} else if (panel_effect_controls->keyframe_focus()) {
|
||||
panel_effect_controls->set_zoom(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,11 +44,7 @@ SOURCES += \
|
||||
ui/viewercontainer.cpp \
|
||||
dialogs/exportdialog.cpp \
|
||||
ui/collapsiblewidget.cpp \
|
||||
effects/transformeffect.cpp \
|
||||
panels/panels.cpp \
|
||||
effects/volumeeffect.cpp \
|
||||
effects/paneffect.cpp \
|
||||
effects/effects.cpp \
|
||||
playback/cacher.cpp \
|
||||
io/exportthread.cpp \
|
||||
ui/timelineheader.cpp \
|
||||
@@ -60,17 +56,21 @@ SOURCES += \
|
||||
ui/audiomonitor.cpp \
|
||||
project/undo.cpp \
|
||||
ui/scrollarea.cpp \
|
||||
effects/shakeeffect.cpp \
|
||||
effects/texteffect.cpp \
|
||||
ui/comboboxex.cpp \
|
||||
ui/colorbutton.cpp \
|
||||
effects/solideffect.cpp \
|
||||
dialogs/replaceclipmediadialog.cpp \
|
||||
effects/inverteffect.cpp \
|
||||
effects/linearfadetransition.cpp \
|
||||
ui/fontcombobox.cpp \
|
||||
ui/checkboxex.cpp \
|
||||
effects/effect.cpp
|
||||
effects/effect.cpp \
|
||||
effects/video/transformeffect.cpp \
|
||||
effects/audio/volumeeffect.cpp \
|
||||
effects/audio/paneffect.cpp \
|
||||
effects/video/texteffect.cpp \
|
||||
effects/video/solideffect.cpp \
|
||||
effects/video/shakeeffect.cpp \
|
||||
effects/video/inverteffect.cpp \
|
||||
ui/keyframeview.cpp
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
@@ -86,7 +86,6 @@ HEADERS += \
|
||||
project/clip.h \
|
||||
playback/playback.h \
|
||||
playback/audio.h \
|
||||
effects/effects.h \
|
||||
io/config.h \
|
||||
dialogs/newsequencedialog.h \
|
||||
ui/viewerwidget.h \
|
||||
@@ -110,7 +109,15 @@ HEADERS += \
|
||||
dialogs/replaceclipmediadialog.h \
|
||||
ui/fontcombobox.h \
|
||||
ui/checkboxex.h \
|
||||
effects/effect.h
|
||||
effects/effect.h \
|
||||
effects/video/transformeffect.h \
|
||||
effects/video/solideffect.h \
|
||||
effects/video/shakeeffect.h \
|
||||
effects/video/texteffect.h \
|
||||
effects/video/inverteffect.h \
|
||||
effects/audio/volumeeffect.h \
|
||||
effects/audio/paneffect.h \
|
||||
ui/keyframeview.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
|
||||
+50
-15
@@ -6,7 +6,7 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "panels/panels.h"
|
||||
#include "effects/effects.h"
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
#include "project/clip.h"
|
||||
#include "effects/effect.h"
|
||||
@@ -20,19 +20,33 @@
|
||||
|
||||
EffectControls::EffectControls(QWidget *parent) :
|
||||
QDockWidget(parent),
|
||||
ui(new Ui::EffectControls)
|
||||
ui(new Ui::EffectControls),
|
||||
zoom(1)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
init_effects();
|
||||
init_transitions();
|
||||
clear_effects(false);
|
||||
ui->headers->snapping = false;
|
||||
}
|
||||
|
||||
EffectControls::~EffectControls()
|
||||
{
|
||||
EffectControls::~EffectControls() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool EffectControls::keyframe_focus() {
|
||||
return ui->headers->hasFocus() || ui->keyframeView->hasFocus();
|
||||
}
|
||||
|
||||
void EffectControls::set_zoom(bool in) {
|
||||
if (in) {
|
||||
zoom *= 2;
|
||||
} else {
|
||||
zoom *= 0.5;
|
||||
}
|
||||
update_keyframes();
|
||||
}
|
||||
|
||||
void EffectControls::menu_select(QAction* q) {
|
||||
TimelineAction* ta = new TimelineAction();
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
@@ -55,9 +69,15 @@ void EffectControls::menu_select(QAction* q) {
|
||||
panel_timeline->redraw_all_clips(true);
|
||||
} else {
|
||||
reload_clips();
|
||||
panel_viewer->viewer_widget->update();
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControls::update_keyframes() {
|
||||
if (ui->headers->isVisible()) ui->headers->update_header(zoom);
|
||||
ui->keyframeView->update();
|
||||
}
|
||||
|
||||
void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
video_menu = video;
|
||||
transition_menu = transitions;
|
||||
@@ -109,24 +129,28 @@ void EffectControls::show_effect_menu(bool video, bool transitions) {
|
||||
|
||||
void EffectControls::clear_effects(bool clear_cache) {
|
||||
// clear existing clips
|
||||
ui->keyframeView->effects.clear();
|
||||
QVBoxLayout* video_layout = static_cast<QVBoxLayout*>(ui->video_effect_area->layout());
|
||||
QVBoxLayout* audio_layout = static_cast<QVBoxLayout*>(ui->audio_effect_area->layout());
|
||||
QLayoutItem* item;
|
||||
while ((item = video_layout->takeAt(0))) {
|
||||
item->widget()->setParent(NULL);
|
||||
disconnect(static_cast<CollapsibleWidget*>(item->widget()), SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*)));
|
||||
disconnect(static_cast<CollapsibleWidget*>(item->widget()), SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*)));
|
||||
disconnect(static_cast<CollapsibleWidget*>(item->widget()), SIGNAL(visibleChanged()), ui->keyframeView, SLOT(reload()));
|
||||
}
|
||||
while ((item = audio_layout->takeAt(0))) {
|
||||
item->widget()->setParent(NULL);
|
||||
disconnect(static_cast<CollapsibleWidget*>(item->widget()), SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*)));
|
||||
disconnect(static_cast<CollapsibleWidget*>(item->widget()), SIGNAL(visibleChanged()), ui->keyframeView, SLOT(reload()));
|
||||
}
|
||||
ui->vcontainer->setVisible(false);
|
||||
ui->acontainer->setVisible(false);
|
||||
ui->headers->setVisible(false);
|
||||
ui->keyframeView->setEnabled(false);
|
||||
if (clear_cache) selected_clips.clear();
|
||||
}
|
||||
|
||||
void EffectControls::deselect_all_effects(QWidget* sender) {
|
||||
QVector<Effect*> delete_effects;
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
Clip* c = sequence->get_clip(selected_clips.at(i));
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
@@ -138,26 +162,37 @@ void EffectControls::deselect_all_effects(QWidget* sender) {
|
||||
}
|
||||
|
||||
void EffectControls::load_effects() {
|
||||
// load in new clips
|
||||
// load in new clips
|
||||
long effects_in = LONG_MAX;
|
||||
long effects_out = 0;
|
||||
for (int i=0;i<selected_clips.size();i++) {
|
||||
Clip* c = sequence->get_clip(selected_clips.at(i));
|
||||
if (c->track < 0) {
|
||||
ui->vcontainer->setVisible(true);
|
||||
} else {
|
||||
ui->acontainer->setVisible(true);
|
||||
}
|
||||
Clip* c = sequence->get_clip(selected_clips.at(i));
|
||||
effects_in = qMin(effects_in, c->timeline_in);
|
||||
effects_out = qMax(effects_out, c->timeline_out);
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
CollapsibleWidget* container = c->effects.at(j)->container;
|
||||
Effect* e = c->effects.at(j);
|
||||
ui->keyframeView->effects.append(e);
|
||||
CollapsibleWidget* container = e->container;
|
||||
if (c->track < 0) {
|
||||
static_cast<QVBoxLayout*>(ui->video_effect_area->layout())->addWidget(container);
|
||||
ui->vcontainer->setVisible(true);
|
||||
} else {
|
||||
static_cast<QVBoxLayout*>(ui->audio_effect_area->layout())->addWidget(container);
|
||||
ui->acontainer->setVisible(true);
|
||||
}
|
||||
}
|
||||
connect(container, SIGNAL(deselect_others(QWidget*)), this, SLOT(deselect_all_effects(QWidget*)));
|
||||
connect(container, SIGNAL(visibleChanged()), ui->keyframeView, SLOT(reload()));
|
||||
}
|
||||
}
|
||||
if (selected_clips.size() > 0) {
|
||||
ui->keyframeView->setEnabled(true);
|
||||
ui->keyframeView->visible_in = effects_in;
|
||||
ui->keyframeView->visible_out = effects_out;
|
||||
ui->keyframeView->update();
|
||||
|
||||
ui->headers->set_visible_in(effects_in);
|
||||
ui->headers->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControls::delete_effects() {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
struct Clip;
|
||||
class QMenu;
|
||||
class Effect;
|
||||
class TimelineHeader;
|
||||
|
||||
namespace Ui {
|
||||
class EffectControls;
|
||||
@@ -24,7 +25,11 @@ public:
|
||||
void delete_effects();
|
||||
bool is_focused();
|
||||
void reload_clips();
|
||||
void update_keyframes();
|
||||
void set_zoom(bool in);
|
||||
bool keyframe_focus();
|
||||
|
||||
double zoom;
|
||||
private slots:
|
||||
void menu_select(QAction* q);
|
||||
void on_add_video_effect_button_clicked();
|
||||
@@ -36,10 +41,11 @@ private slots:
|
||||
void on_add_audio_transition_button_clicked();
|
||||
|
||||
private:
|
||||
Ui::EffectControls *ui;
|
||||
Ui::EffectControls *ui;
|
||||
QVector<int> selected_clips;
|
||||
void show_effect_menu(bool video, bool transitions);
|
||||
void load_effects();
|
||||
void load_keyframes();
|
||||
|
||||
bool video_menu;
|
||||
bool transition_menu;
|
||||
|
||||
+403
-300
@@ -23,7 +23,7 @@
|
||||
<string>Effect Controls</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -53,7 +53,7 @@
|
||||
<height>489</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -70,307 +70,396 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="vcontainer" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="veHeader" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#veHeader {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_video_effect_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Video Effect</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-effect.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VIDEO EFFECTS</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_video_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Video Transition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-transition.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="video_effect_area" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="acontainer" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="aeHeader" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#aeHeader {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_audio_effect_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Audio Effect</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-effect.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AUDIO EFFECTS</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_audio_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Audio Transition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-transition.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="audio_effect_area" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</spacer>
|
||||
<widget class="QWidget" name="effects_area" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="vcontainer" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="veHeader" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#veHeader {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_video_effect_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Video Effect</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-effect.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>VIDEO EFFECTS</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_video_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Video Transition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-transition.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="video_effect_area" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="acontainer" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="aeHeader" native="true">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#aeHeader {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_audio_effect_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Audio Effect</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-effect.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AUDIO EFFECTS</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="add_audio_transition_button">
|
||||
<property name="toolTip">
|
||||
<string>Add Audio Transition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/icons/add-transition.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="audio_effect_area" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QScrollArea" name="scrollArea_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>388</width>
|
||||
<height>487</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="TimelineHeader" name="headers" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>15</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="KeyframeView" name="keyframeView" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -379,6 +468,20 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>KeyframeView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>ui/keyframeview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TimelineHeader</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>ui/timelineheader.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include "playback/playback.h"
|
||||
#include "effects/effects.h"
|
||||
#include "effects/effect.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "project/sequence.h"
|
||||
#include "effects/effect.h"
|
||||
|
||||
+24
-24
@@ -254,7 +254,7 @@ void Timeline::update_sequence() {
|
||||
}
|
||||
|
||||
int Timeline::get_snap_range() {
|
||||
return getFrameFromScreenPoint(10);
|
||||
return getTimelineFrameFromScreenPoint(10);
|
||||
}
|
||||
|
||||
bool Timeline::focused() {
|
||||
@@ -265,9 +265,12 @@ void Timeline::repaint_timeline() {
|
||||
if (playing) {
|
||||
playhead = round(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * sequence->frame_rate));
|
||||
}
|
||||
ui->headers->update();
|
||||
|
||||
ui->headers->update_header(zoom);
|
||||
ui->video_area->update();
|
||||
ui->audio_area->update();
|
||||
panel_effect_controls->update_keyframes();
|
||||
|
||||
if (last_frame != playhead) {
|
||||
panel_viewer->viewer_widget->update();
|
||||
ui->audio_monitor->update();
|
||||
@@ -282,9 +285,10 @@ void Timeline::redraw_all_clips(bool changed) {
|
||||
panel_viewer->viewer_widget->update();
|
||||
}
|
||||
|
||||
ui->headers->update_header(zoom);
|
||||
ui->video_area->redraw_clips();
|
||||
ui->audio_area->redraw_clips();
|
||||
ui->headers->update();
|
||||
panel_effect_controls->update_keyframes();
|
||||
|
||||
panel_viewer->update_end_timecode();
|
||||
}
|
||||
@@ -396,25 +400,13 @@ void Timeline::set_zoom(bool in) {
|
||||
ui->timeline_area->horizontalScrollBar()->setValue(
|
||||
lerp(
|
||||
ui->timeline_area->horizontalScrollBar()->value(),
|
||||
getScreenPointFromFrame(playhead) - (ui->timeline_area->width()/2),
|
||||
getTimelineScreenPointFromFrame(playhead) - (ui->timeline_area->width()/2),
|
||||
0.99
|
||||
)
|
||||
);
|
||||
redraw_all_clips(false);
|
||||
}
|
||||
|
||||
/*void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length) {
|
||||
// ripple the selections
|
||||
for (int i=0;i<selections.size();i++) {
|
||||
Selection& s = selections[i];
|
||||
// only ripple the selection if it's within range of the ripple point
|
||||
if (s.old_in >= ripple_point) {
|
||||
s.in += ripple_length;
|
||||
s.out += ripple_length;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void Timeline::decheck_tool_buttons(QObject* sender) {
|
||||
for (int i=0;i<tool_buttons.count();i++) {
|
||||
tool_buttons[i]->setChecked(tool_buttons.at(i) == sender);
|
||||
@@ -1083,16 +1075,24 @@ void Timeline::deselect() {
|
||||
repaint_timeline();
|
||||
}
|
||||
|
||||
long Timeline::getFrameFromScreenPoint(int x) {
|
||||
long f = round((float) x / zoom);
|
||||
if (f < 0) {
|
||||
return 0;
|
||||
}
|
||||
return f;
|
||||
long getFrameFromScreenPoint(double zoom, int x) {
|
||||
long f = round((float) x / zoom);
|
||||
if (f < 0) {
|
||||
return 0;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
int Timeline::getScreenPointFromFrame(long frame) {
|
||||
return (int) round(frame*zoom);
|
||||
int getScreenPointFromFrame(double zoom, long frame) {
|
||||
return (int) round(frame*zoom);
|
||||
}
|
||||
|
||||
long Timeline::getTimelineFrameFromScreenPoint(int x) {
|
||||
return getFrameFromScreenPoint(zoom, x);
|
||||
}
|
||||
|
||||
int Timeline::getTimelineScreenPointFromFrame(long frame) {
|
||||
return getScreenPointFromFrame(zoom, frame);
|
||||
}
|
||||
|
||||
void Timeline::on_toolArrowButton_clicked() {
|
||||
|
||||
+4
-5
@@ -22,6 +22,8 @@ struct MediaStream;
|
||||
|
||||
int lerp(int a, int b, double t);
|
||||
long refactor_frame_number(long framenumber, double source_frame_rate, double target_frame_rate);
|
||||
int getScreenPointFromFrame(double zoom, long frame);
|
||||
long getFrameFromScreenPoint(double zoom, int x);
|
||||
|
||||
struct Ghost {
|
||||
int clip;
|
||||
@@ -100,8 +102,8 @@ public:
|
||||
void delete_in_out(bool ripple);
|
||||
|
||||
int get_snap_range();
|
||||
int getScreenPointFromFrame(long frame);
|
||||
long getFrameFromScreenPoint(int x);
|
||||
int getTimelineScreenPointFromFrame(long frame);
|
||||
long getTimelineFrameFromScreenPoint(int x);
|
||||
|
||||
bool snap_to_point(long point, long* l);
|
||||
void snap_to_clip(long* l, bool playhead_inclusive);
|
||||
@@ -177,9 +179,6 @@ public:
|
||||
// importing
|
||||
bool importing;
|
||||
|
||||
// ripple
|
||||
// void ripple(TimelineAction* ta, long ripple_point, long ripple_length);
|
||||
|
||||
Ui::Timeline *ui;
|
||||
public slots:
|
||||
void repaint_timeline();
|
||||
|
||||
@@ -328,6 +328,9 @@
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -379,6 +382,9 @@
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
|
||||
+63
-52
@@ -5,7 +5,7 @@
|
||||
#include "io/media.h"
|
||||
#include "playback/audio.h"
|
||||
#include "playback/playback.h"
|
||||
#include "effects/effects.h"
|
||||
#include "effects/effect.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/project.h"
|
||||
#include "effects/transition.h"
|
||||
@@ -187,6 +187,20 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
bool error = false;
|
||||
|
||||
int i = 0;
|
||||
|
||||
/* swscale solution - might be faster? but AVFilter solution is more "future-proof"
|
||||
if (!c->reached_end) {
|
||||
while (i < c->cache_size) {
|
||||
time = QDateTime::currentMSecsSinceEpoch();
|
||||
retrieve_next_frame_raw_data(c, cache->frames[i]);
|
||||
qDebug() << (QDateTime::currentMSecsSinceEpoch() - time);
|
||||
if (c->reached_end) break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* AVFilter solution - not definitely slower, may allow for cool things later */
|
||||
if (!c->reached_end) {
|
||||
while (i < c->cache_size) {
|
||||
av_frame_unref(cache->frames[i]);
|
||||
@@ -195,30 +209,6 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR(EAGAIN)) {
|
||||
if (c->filter_graph != NULL) {
|
||||
avfilter_graph_free(&c->filter_graph);
|
||||
}
|
||||
c->filter_graph = avfilter_graph_alloc();
|
||||
char args[512];
|
||||
snprintf(args, sizeof(args),
|
||||
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||
c->stream->codecpar->width, c->stream->codecpar->height, c->stream->codecpar->format,
|
||||
c->stream->time_base.num, c->stream->time_base.den,
|
||||
c->stream->codecpar->sample_aspect_ratio.num, c->stream->codecpar->sample_aspect_ratio.den);
|
||||
avfilter_graph_create_filter(&c->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", args, NULL, c->filter_graph);
|
||||
avfilter_graph_create_filter(&c->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, c->filter_graph);
|
||||
enum AVPixelFormat pix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE };
|
||||
av_opt_set_int_list(c->buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
AVFilterContext* gblur_ctx;
|
||||
// avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("gblur"), "ol_gblur", "sigma=20:steps=1", NULL, c->filter_graph);
|
||||
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("negate"), "ol_gblur", NULL, NULL, c->filter_graph);
|
||||
// avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("null"), "ol_gblur", NULL, NULL, c->filter_graph);
|
||||
avfilter_link(c->buffersrc_ctx, 0, gblur_ctx, 0);
|
||||
avfilter_link(gblur_ctx, 0, c->buffersink_ctx, 0);
|
||||
|
||||
avfilter_graph_config(c->filter_graph, NULL);
|
||||
|
||||
ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret >= 0) {
|
||||
if ((ret = av_buffersrc_add_frame_flags(c->buffersrc_ctx, c->frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
|
||||
@@ -246,7 +236,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cache->write_count = i;
|
||||
|
||||
if (!error) {
|
||||
@@ -262,13 +252,7 @@ void cache_video_worker(Clip* c, long playhead, ClipCache* cache) {
|
||||
void reset_cache(Clip* c, long target_frame) {
|
||||
// if we seek to a whole other place in the timeline, we'll need to reset the cache with new values
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->media_stream);
|
||||
if (ms->infinite_length) {
|
||||
// if this clip is a still image, we only need one frame
|
||||
if (!c->cache_A.written) {
|
||||
retrieve_next_frame_raw_data(c, c->sws_frame);
|
||||
c->cache_A.written = true;
|
||||
}
|
||||
} else {
|
||||
if (!ms->infinite_length) {
|
||||
// flush ffmpeg codecs
|
||||
avcodec_flush_buffers(c->codecCtx);
|
||||
|
||||
@@ -360,10 +344,10 @@ void open_clip_worker(Clip* clip) {
|
||||
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
// set up swscale context - primarily used for colorspace conversion
|
||||
// as "scaling" is actually done by OpenGL
|
||||
int dstW = ceil(clip->stream->codecpar->width/2)*2;
|
||||
int dstH = ceil(clip->stream->codecpar->height/2)*2;
|
||||
// int dstW = ceil(clip->stream->codecpar->width/2)*2;
|
||||
// int dstH = ceil(clip->stream->codecpar->height/2)*2;
|
||||
|
||||
clip->sws_ctx = sws_getContext(
|
||||
/*clip->sws_ctx = sws_getContext(
|
||||
clip->stream->codecpar->width,
|
||||
clip->stream->codecpar->height,
|
||||
static_cast<AVPixelFormat>(clip->stream->codecpar->format),
|
||||
@@ -374,25 +358,24 @@ void open_clip_worker(Clip* clip) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
);*/
|
||||
|
||||
// create memory cache for video
|
||||
if (ms->infinite_length) {
|
||||
clip->cache_size = 1;
|
||||
} else {
|
||||
clip->cache_size = ceil(av_q2d(av_guess_frame_rate(clip->formatCtx, clip->stream, NULL))/4); // cache is half a second in total
|
||||
clip->cache_size = (ms->infinite_length) ? 1 : ceil(av_q2d(av_guess_frame_rate(clip->formatCtx, clip->stream, NULL))/4); // cache is half a second in total
|
||||
|
||||
// infinite length doesn't need cache B
|
||||
clip->cache_A.frames = new AVFrame* [clip->cache_size];
|
||||
clip->cache_B.frames = new AVFrame* [clip->cache_size];
|
||||
for (int i=0;i<clip->cache_size;i++) {
|
||||
clip->cache_A.frames[i] = av_frame_alloc();
|
||||
clip->cache_B.frames[i] = av_frame_alloc();
|
||||
}
|
||||
// infinite length doesn't need cache B
|
||||
clip->cache_A.frames = new AVFrame* [clip->cache_size];
|
||||
clip->cache_B.frames = new AVFrame* [clip->cache_size];
|
||||
for (int i=0;i<clip->cache_size;i++) {
|
||||
clip->cache_A.frames[i] = av_frame_alloc();
|
||||
clip->cache_B.frames[i] = av_frame_alloc();
|
||||
}
|
||||
|
||||
clip->comp_frame_size = clip->stream->codecpar->width * clip->stream->codecpar->height * 4;
|
||||
clip->comp_frame = new uchar[clip->comp_frame_size];
|
||||
|
||||
// alloc temporary scale frame
|
||||
clip->sws_frame = av_frame_alloc();
|
||||
/*clip->sws_frame = av_frame_alloc();
|
||||
av_frame_make_writable(clip->sws_frame);
|
||||
clip->sws_frame->width = dstW;
|
||||
clip->sws_frame->height = dstH;
|
||||
@@ -400,7 +383,33 @@ void open_clip_worker(Clip* clip) {
|
||||
if (av_frame_get_buffer(clip->sws_frame, 0)) {
|
||||
qDebug() << "[ERROR] Could not allocate buffer for sws_frame";
|
||||
}
|
||||
clip->sws_frame->linesize[0] = clip->stream->codecpar->width*4;
|
||||
clip->sws_frame->linesize[0] = clip->stream->codecpar->width*4;*/
|
||||
|
||||
clip->filter_graph = avfilter_graph_alloc();
|
||||
char args[512];
|
||||
snprintf(args, sizeof(args),
|
||||
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
||||
clip->stream->codecpar->width, clip->stream->codecpar->height, clip->stream->codecpar->format,
|
||||
clip->stream->time_base.num, clip->stream->time_base.den,
|
||||
clip->stream->codecpar->sample_aspect_ratio.num, clip->stream->codecpar->sample_aspect_ratio.den);
|
||||
avfilter_graph_create_filter(&clip->buffersrc_ctx, avfilter_get_by_name("buffer"), "in", args, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&clip->buffersink_ctx, avfilter_get_by_name("buffersink"), "out", NULL, NULL, clip->filter_graph);
|
||||
enum AVPixelFormat pix_fmts[] = { static_cast<AVPixelFormat>(dest_format), AV_PIX_FMT_NONE };
|
||||
av_opt_set_int_list(clip->buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
|
||||
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
|
||||
avfilter_graph_config(clip->filter_graph, NULL);
|
||||
|
||||
/* old AVFilter code, looks like it'll be unusable
|
||||
AVFilterContext* gblur_ctx;
|
||||
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("gblur"), "ol_gblur", "sigma=20:steps=1", NULL, c->filter_graph);
|
||||
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("negate"), "ol_gblur", NULL, NULL, clip->filter_graph);
|
||||
avfilter_graph_create_filter(&gblur_ctx, avfilter_get_by_name("null"), "ol_gblur", NULL, NULL, clip->filter_graph);
|
||||
avfilter_link(clip->buffersrc_ctx, 0, gblur_ctx, 0);
|
||||
avfilter_link(gblur_ctx, 0, clip->buffersink_ctx, 0);
|
||||
avfilter_link(clip->buffersrc_ctx, 0, clip->buffersink_ctx, 0);
|
||||
*/
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
// if FFmpeg can't pick up the channel layout (usually WAV), assume
|
||||
// based on channel count (doesn't support surround sound sources yet)
|
||||
@@ -468,10 +477,12 @@ void close_clip_worker(Clip* clip) {
|
||||
// closes ffmpeg file handle and frees any memory used for caching
|
||||
MediaStream* ms = static_cast<Media*>(clip->media)->get_stream_from_file_index(clip->media_stream);
|
||||
if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
sws_freeContext(clip->sws_ctx);
|
||||
// sws_freeContext(clip->sws_ctx);
|
||||
|
||||
// TODO will eventually be in audio too
|
||||
av_frame_free(&clip->sws_frame);
|
||||
// av_frame_free(&clip->sws_frame);
|
||||
avfilter_graph_free(&clip->filter_graph);
|
||||
delete [] clip->comp_frame;
|
||||
} else if (clip->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
swr_free(&clip->swr_ctx);
|
||||
}
|
||||
|
||||
+18
-14
@@ -8,7 +8,7 @@
|
||||
#include "panels/panels.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
#include <algorithm>
|
||||
#include "effects/effect.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
@@ -17,6 +17,7 @@ extern "C" {
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <QObject>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QDebug>
|
||||
@@ -79,7 +80,7 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
|
||||
// do we need to update the texture?
|
||||
MediaStream* ms = static_cast<Media*>(c->media)->get_stream_from_file_index(c->media_stream);
|
||||
if ((!ms->infinite_length && c->texture_frame != clip_time) ||
|
||||
if ((!ms->infinite_length/* && c->texture_frame != clip_time*/) ||
|
||||
(ms->infinite_length && c->texture_frame == -1)) {
|
||||
AVFrame* current_frame = NULL;
|
||||
bool no_frame = false;
|
||||
@@ -88,16 +89,11 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
if (ms->infinite_length) { // if clip is a still frame, we only need one
|
||||
if (c->cache_A.written) {
|
||||
// retrieve cached frame
|
||||
current_frame = c->sws_frame;
|
||||
} else if (c->multithreaded) {
|
||||
if (c->lock.tryLock()) {
|
||||
// grab image (multi-threaded)
|
||||
cache_clip(c, 0, false, false, true, NULL);
|
||||
c->lock.unlock();
|
||||
}
|
||||
} else {
|
||||
// grab image (single-threaded)
|
||||
reset_cache(c, playhead);
|
||||
current_frame = c->cache_A.frames[0];
|
||||
} else if (c->lock.tryLock()) {
|
||||
// grab image
|
||||
cache_clip(c, 0, true, false, false, NULL);
|
||||
c->lock.unlock();
|
||||
}
|
||||
} else {
|
||||
// keeping a RAM cache improves performance, however it's detrimental when rendering
|
||||
@@ -169,7 +165,15 @@ void get_clip_frame(Clip* c, long playhead) {
|
||||
c->texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
|
||||
}
|
||||
|
||||
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, current_frame->data[0]);
|
||||
memcpy(c->comp_frame, current_frame->data[0], c->comp_frame_size);
|
||||
QImage img(c->comp_frame, current_frame->width, current_frame->height, QImage::Format_RGBA8888);
|
||||
for (int i=0;i<c->effects.size();i++) {
|
||||
if (c->effects.at(i)->enable_image) {
|
||||
c->effects.at(i)->process_image(img);
|
||||
}
|
||||
}
|
||||
|
||||
c->texture->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, c->comp_frame);
|
||||
c->texture_frame = clip_time;
|
||||
} else if (!no_frame) {
|
||||
texture_failed = true;
|
||||
@@ -240,7 +244,7 @@ void retrieve_next_frame_raw_data(Clip* c, AVFrame* output) {
|
||||
int ret = retrieve_next_frame(c, c->frame);
|
||||
if (ret >= 0) {
|
||||
if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
||||
// sws_scale(c->sws_ctx, c->frame->data, c->frame->linesize, 0, c->stream->codecpar->height, output->data, output->linesize);
|
||||
// output->pts = c->frame->best_effort_timestamp;
|
||||
} else if (c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
output->pts = c->frame->pts;
|
||||
|
||||
@@ -121,32 +121,6 @@ void Clip::refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::run_video_pre_effect_stack(long playhead, int* anchor_x, int* anchor_y) {
|
||||
for (int j=0;j<effects.size();j++) {
|
||||
if (effects.at(j)->enable_pre_gl && effects.at(j)->is_enabled()) effects.at(j)->process_gl(anchor_x, anchor_y);
|
||||
}
|
||||
|
||||
if (opening_transition != NULL) {
|
||||
int transition_progress = playhead - timeline_in;
|
||||
if (transition_progress < opening_transition->length) {
|
||||
opening_transition->process_transition((double)transition_progress/(double)opening_transition->length);
|
||||
}
|
||||
}
|
||||
|
||||
if (closing_transition != NULL) {
|
||||
int transition_progress = closing_transition->length - (playhead - timeline_in - getLength() + closing_transition->length);
|
||||
if (transition_progress < closing_transition->length) {
|
||||
closing_transition->process_transition((double)transition_progress/(double)closing_transition->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clip::run_video_post_effect_stack() {
|
||||
for (int j=0;j<effects.size();j++) {
|
||||
if (effects.at(j)->enable_post_gl && effects.at(j)->is_enabled()) effects.at(j)->post_gl();
|
||||
}
|
||||
}
|
||||
|
||||
Clip::~Clip() {
|
||||
if (open) {
|
||||
close_clip(this);
|
||||
|
||||
+5
-6
@@ -44,9 +44,7 @@ struct Clip
|
||||
Clip* copy(Sequence* s);
|
||||
void reset_audio();
|
||||
void reset();
|
||||
void refresh();
|
||||
void run_video_pre_effect_stack(long playhead, int *anchor_x, int *anchor_y);
|
||||
void run_video_post_effect_stack();
|
||||
void refresh();
|
||||
|
||||
// timeline variables
|
||||
Sequence* sequence;
|
||||
@@ -80,8 +78,9 @@ struct Clip
|
||||
AVCodec* codec;
|
||||
AVCodecContext* codecCtx;
|
||||
AVPacket* pkt;
|
||||
AVFrame* frame;
|
||||
AVFrame* sws_frame;
|
||||
AVFrame* frame;
|
||||
uchar* comp_frame;
|
||||
int comp_frame_size;
|
||||
|
||||
// ffmpeg filters
|
||||
AVFilterGraph* filter_graph;
|
||||
@@ -105,7 +104,7 @@ struct Clip
|
||||
QMutex open_lock;
|
||||
|
||||
// video playback variables
|
||||
SwsContext* sws_ctx;
|
||||
//SwsContext* sws_ctx;
|
||||
QOpenGLTexture* texture;
|
||||
long texture_frame;
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "playback/playback.h"
|
||||
#include "ui/sourcetable.h"
|
||||
#include "effects/effects.h"
|
||||
#include "effects/effect.h"
|
||||
#include "io/media.h"
|
||||
#include "playback/cacher.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
@@ -29,7 +29,7 @@ CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) {
|
||||
enabled_check->setChecked(true);
|
||||
header = new QLabel();
|
||||
collapse_button = new QPushButton("[-]");
|
||||
collapse_button->setStyleSheet("QPushButton { border: none; }");
|
||||
collapse_button->setStyleSheet("QPushButton { border: none; } QPushButton:hover { text-decoration: underline; }");
|
||||
collapse_button->setMaximumWidth(25);
|
||||
setText("<untitled>");
|
||||
title_bar_layout->addWidget(collapse_button);
|
||||
@@ -61,6 +61,10 @@ bool CollapsibleWidget::is_focused() {
|
||||
return title_bar->hasFocus();
|
||||
}
|
||||
|
||||
bool CollapsibleWidget::is_expanded() {
|
||||
return contents->isVisible();
|
||||
}
|
||||
|
||||
void CollapsibleWidget::setContents(QWidget* c) {
|
||||
bool existing = (contents != NULL);
|
||||
contents = c;
|
||||
@@ -82,6 +86,7 @@ void CollapsibleWidget::on_enabled_change(bool b) {
|
||||
void CollapsibleWidget::on_visible_change() {
|
||||
contents->setVisible(!contents->isVisible());
|
||||
collapse_button->setText(contents->isVisible() ? "[-]" : "[+]");
|
||||
emit visibleChanged();
|
||||
}
|
||||
|
||||
CollapsibleWidgetHeader::CollapsibleWidgetHeader(QWidget* parent) : QWidget(parent), selected(false) {}
|
||||
|
||||
@@ -30,19 +30,21 @@ public:
|
||||
void setContents(QWidget* c);
|
||||
void setText(const QString &);
|
||||
bool is_focused();
|
||||
bool is_expanded();
|
||||
|
||||
CheckboxEx* enabled_check;
|
||||
bool selected;
|
||||
QWidget* contents;
|
||||
CollapsibleWidgetHeader* title_bar;
|
||||
private:
|
||||
QLabel* header;
|
||||
CollapsibleWidgetHeader* title_bar;
|
||||
QVBoxLayout* layout;
|
||||
QPushButton* collapse_button;
|
||||
QWidget* contents;
|
||||
QFrame* line;
|
||||
|
||||
signals:
|
||||
void deselect_others(QWidget*);
|
||||
void visibleChanged();
|
||||
|
||||
private slots:
|
||||
void on_enabled_change(bool b);
|
||||
|
||||
+19
-5
@@ -1,6 +1,7 @@
|
||||
#include "comboboxex.h"
|
||||
|
||||
#include "project/undo.h"
|
||||
#include "panels/project.h"
|
||||
|
||||
#include <QUndoCommand>
|
||||
#include <QWheelEvent>
|
||||
@@ -8,32 +9,45 @@
|
||||
class ComboBoxExCommand : public QUndoCommand {
|
||||
public:
|
||||
ComboBoxExCommand(ComboBoxEx* obj, int old_index, int new_index) :
|
||||
combobox(obj), old_val(old_index), new_val(new_index), done(true) {}
|
||||
combobox(obj), old_val(old_index), new_val(new_index), done(true), old_project_changed(project_changed) {}
|
||||
void undo() {
|
||||
combobox->setCurrentIndex(old_val);
|
||||
done = false;
|
||||
project_changed = old_project_changed;
|
||||
}
|
||||
void redo() {
|
||||
if (!done) {
|
||||
combobox->setCurrentIndex(new_val);
|
||||
}
|
||||
project_changed = true;
|
||||
}
|
||||
private:
|
||||
ComboBoxEx* combobox;
|
||||
int old_val;
|
||||
int new_val;
|
||||
bool done;
|
||||
bool old_project_changed;
|
||||
};
|
||||
|
||||
ComboBoxEx::ComboBoxEx(QWidget *parent) : QComboBox(parent), index(0) {
|
||||
connect(this, SIGNAL(activated(int)), this, SLOT(index_changed(int)));
|
||||
}
|
||||
|
||||
void ComboBoxEx::setCurrentIndexEx(int i) {
|
||||
index = i;
|
||||
setCurrentIndex(i);
|
||||
}
|
||||
|
||||
void ComboBoxEx::setCurrentTextEx(const QString &text) {
|
||||
setCurrentText(text);
|
||||
index = currentIndex();
|
||||
}
|
||||
|
||||
void ComboBoxEx::index_changed(int i) {
|
||||
if (index != i) {
|
||||
undo_stack.push(new ComboBoxExCommand(this, index, i));
|
||||
index = i;
|
||||
}
|
||||
if (index != i) {
|
||||
undo_stack.push(new ComboBoxExCommand(this, index, i));
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
void ComboBoxEx::wheelEvent(QWheelEvent* e) {
|
||||
|
||||
+4
-2
@@ -8,10 +8,12 @@ class ComboBoxEx : public QComboBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ComboBoxEx(QWidget* parent = 0);
|
||||
void setCurrentIndexEx(int i);
|
||||
void setCurrentTextEx(const QString &text);
|
||||
private slots:
|
||||
void index_changed(int);
|
||||
void index_changed(int);
|
||||
private:
|
||||
int index;
|
||||
int index;
|
||||
void wheelEvent(QWheelEvent* e);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "keyframeview.h"
|
||||
|
||||
#include "effects/effect.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "panels/panels.h"
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "project/clip.h"
|
||||
#include "panels/timeline.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#define KEYFRAME_SIZE 5
|
||||
#define KEYFRAME_POINT_COUNT 4
|
||||
|
||||
KeyframeView::KeyframeView(QWidget *parent) : QWidget(parent), mouseover(false), visible_in(0), visible_out(0) {
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
setMouseTracking(true);
|
||||
reload();
|
||||
}
|
||||
|
||||
void KeyframeView::reload() {
|
||||
enable_reload = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void KeyframeView::paintEvent(QPaintEvent*) {
|
||||
QPainter p(this);
|
||||
|
||||
setMinimumWidth(getScreenPointFromFrame(panel_effect_controls->zoom, visible_out - visible_in));
|
||||
|
||||
rowY.clear();
|
||||
for (int i=0;i<effects.size();i++) {
|
||||
Effect* e = effects.at(i);
|
||||
if (e->container->is_expanded()) {
|
||||
for (int j=0;j<e->row_count();j++) {
|
||||
QLabel* label = e->row(j)->label;
|
||||
QWidget* contents = e->container->contents;
|
||||
long frame = 20;
|
||||
int keyframe_y = label->y() + (label->height()>>1) + mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() - e->container->title_bar->height();
|
||||
draw_keyframe(p, getScreenPointFromFrame(panel_effect_controls->zoom, frame), keyframe_y, false);
|
||||
rowY.append(keyframe_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rowY.size() > 0) {
|
||||
int playhead_x = getScreenPointFromFrame(panel_effect_controls->zoom, panel_timeline->playhead-visible_in);
|
||||
p.setPen(Qt::red);
|
||||
p.drawLine(playhead_x, 0, playhead_x, height());
|
||||
}
|
||||
|
||||
if (mouseover && mouseover_row < rowY.size()) {
|
||||
draw_keyframe(p, getScreenPointFromFrame(panel_effect_controls->zoom, mouseover_frame) - visible_in, rowY.at(mouseover_row), true);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeView::draw_keyframe(QPainter &p, int x, int y, bool semiTransparent) {
|
||||
QPoint points[KEYFRAME_POINT_COUNT] = {QPoint(x-KEYFRAME_SIZE, y), QPoint(x, y-KEYFRAME_SIZE), QPoint(x+KEYFRAME_SIZE, y), QPoint(x, y+KEYFRAME_SIZE)};
|
||||
int alpha = (semiTransparent) ? 128 : 255;
|
||||
p.setPen(QColor(0, 0, 0, alpha));
|
||||
p.setBrush(QColor(160, 160, 160, alpha));
|
||||
p.drawPolygon(points, KEYFRAME_POINT_COUNT);
|
||||
}
|
||||
|
||||
void KeyframeView::mouseMoveEvent(QMouseEvent* event) {
|
||||
unsetCursor();
|
||||
bool new_mo = false;
|
||||
for (int i=0;i<rowY.size();i++) {
|
||||
if (event->y() > rowY.at(i)-KEYFRAME_SIZE-KEYFRAME_SIZE && event->y() < rowY.at(i)+KEYFRAME_SIZE+KEYFRAME_SIZE) {
|
||||
setCursor(Qt::CrossCursor);
|
||||
mouseover_frame = getFrameFromScreenPoint(panel_effect_controls->zoom, event->x()) + visible_in;
|
||||
mouseover_row = i;
|
||||
new_mo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (new_mo || (new_mo != mouseover)) {
|
||||
update();
|
||||
}
|
||||
mouseover = new_mo;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef KEYFRAMEVIEW_H
|
||||
#define KEYFRAMEVIEW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPainter>
|
||||
|
||||
struct Clip;
|
||||
class Effect;
|
||||
class EffectRow;
|
||||
|
||||
class KeyframeView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeView(QWidget* parent = 0);
|
||||
QVector<Effect*> effects;
|
||||
|
||||
long visible_in;
|
||||
long visible_out;
|
||||
public slots:
|
||||
void reload();
|
||||
private:
|
||||
QVector<int> rowY;
|
||||
QVector<EffectRow*> rows;
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
/*void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);*/
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void draw_keyframe(QPainter& p, int x, int y, bool semiTransparent);
|
||||
bool enable_reload;
|
||||
bool mouseover;
|
||||
long mouseover_frame;
|
||||
int mouseover_row;
|
||||
};
|
||||
|
||||
#endif // KEYFRAMEVIEW_H
|
||||
+21
-11
@@ -12,18 +12,23 @@
|
||||
#define CLICK_RANGE 5
|
||||
#define PLAYHEAD_SIZE 6
|
||||
|
||||
TimelineHeader::TimelineHeader(QWidget *parent) : QWidget(parent), dragging(false), resizing_workarea(false) {
|
||||
TimelineHeader::TimelineHeader(QWidget *parent) : QWidget(parent), dragging(false), resizing_workarea(false), zoom(1), in_visible(0), snapping(true) {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void set_playhead(int mouse_x) {
|
||||
long frame = panel_timeline->getFrameFromScreenPoint(mouse_x);
|
||||
panel_timeline->snap_to_clip(&frame, false);
|
||||
void TimelineHeader::set_playhead(int mouse_x) {
|
||||
long frame = getFrameFromScreenPoint(zoom, mouse_x);
|
||||
if (snapping) panel_timeline->snap_to_clip(&frame, false);
|
||||
panel_timeline->seek(frame);
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
|
||||
void TimelineHeader::set_visible_in(long i) {
|
||||
in_visible = i;
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineHeader::set_in_point(long new_in) {
|
||||
long new_out = sequence->workarea_out;
|
||||
if (new_out == new_in) {
|
||||
@@ -66,7 +71,7 @@ void TimelineHeader::mousePressEvent(QMouseEvent* event) {
|
||||
void TimelineHeader::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (dragging) {
|
||||
if (resizing_workarea) {
|
||||
long frame = panel_timeline->getFrameFromScreenPoint(event->pos().x());
|
||||
long frame = getFrameFromScreenPoint(zoom, event->pos().x());
|
||||
panel_timeline->snap_to_clip(&frame, true);
|
||||
if (resizing_workarea_in) {
|
||||
temp_workarea_in = qMax(qMin(temp_workarea_out-1, frame), 0L);
|
||||
@@ -81,8 +86,8 @@ void TimelineHeader::mouseMoveEvent(QMouseEvent* event) {
|
||||
resizing_workarea = false;
|
||||
unsetCursor();
|
||||
if (sequence->using_workarea) {
|
||||
long min_frame = panel_timeline->getFrameFromScreenPoint(event->pos().x() - CLICK_RANGE) - 1;
|
||||
long max_frame = panel_timeline->getFrameFromScreenPoint(event->pos().x() + CLICK_RANGE) + 1;
|
||||
long min_frame = getFrameFromScreenPoint(zoom, event->pos().x() - CLICK_RANGE) - 1;
|
||||
long max_frame = getFrameFromScreenPoint(zoom, event->pos().x() + CLICK_RANGE) + 1;
|
||||
if (sequence->workarea_in > min_frame && sequence->workarea_in < max_frame) {
|
||||
resizing_workarea = true;
|
||||
resizing_workarea_in = true;
|
||||
@@ -112,6 +117,11 @@ void TimelineHeader::mouseReleaseEvent(QMouseEvent*) {
|
||||
panel_timeline->repaint_timeline();
|
||||
}
|
||||
|
||||
void TimelineHeader::update_header(double z) {
|
||||
zoom = z;
|
||||
update();
|
||||
}
|
||||
|
||||
void TimelineHeader::paintEvent(QPaintEvent*) {
|
||||
if (sequence != NULL) {
|
||||
QPainter p(this);
|
||||
@@ -120,7 +130,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) {
|
||||
int multiplier = 0;
|
||||
do {
|
||||
multiplier++;
|
||||
interval = panel_timeline->getScreenPointFromFrame(sequence->frame_rate*multiplier);
|
||||
interval = getScreenPointFromFrame(zoom, sequence->frame_rate*multiplier);
|
||||
} while (interval < 10);
|
||||
for (int i=0;i<width();i+=interval) {
|
||||
p.drawLine(i, 0, i, height());
|
||||
@@ -129,8 +139,8 @@ void TimelineHeader::paintEvent(QPaintEvent*) {
|
||||
// draw in/out selection
|
||||
int in_x;
|
||||
if (sequence->using_workarea) {
|
||||
in_x = panel_timeline->getScreenPointFromFrame(resizing_workarea ? temp_workarea_in : sequence->workarea_in);
|
||||
int out_x = panel_timeline->getScreenPointFromFrame(resizing_workarea ? temp_workarea_out :sequence->workarea_out);
|
||||
in_x = getScreenPointFromFrame(zoom, resizing_workarea ? temp_workarea_in : sequence->workarea_in);
|
||||
int out_x = getScreenPointFromFrame(zoom, resizing_workarea ? temp_workarea_out :sequence->workarea_out);
|
||||
p.fillRect(QRect(in_x, 0, out_x-in_x, height()), QColor(0, 192, 255, 128));
|
||||
p.setPen(Qt::white);
|
||||
p.drawLine(in_x, 0, in_x, height());
|
||||
@@ -138,7 +148,7 @@ void TimelineHeader::paintEvent(QPaintEvent*) {
|
||||
}
|
||||
|
||||
// draw playhead triangle
|
||||
in_x = panel_timeline->getScreenPointFromFrame(panel_timeline->playhead);
|
||||
in_x = getScreenPointFromFrame(zoom, panel_timeline->playhead - in_visible);
|
||||
QPoint start(in_x, height());
|
||||
QPainterPath path;
|
||||
path.moveTo(start);
|
||||
|
||||
+12
-1
@@ -7,9 +7,14 @@ class TimelineHeader : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TimelineHeader(QWidget *parent = 0);
|
||||
explicit TimelineHeader(QWidget *parent = 0);
|
||||
void set_in_point(long p);
|
||||
void set_out_point(long p);
|
||||
void set_visible_in(long i);
|
||||
|
||||
bool snapping;
|
||||
|
||||
void update_header(double z);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*);
|
||||
@@ -26,6 +31,12 @@ private:
|
||||
long temp_workarea_out;
|
||||
long sequence_end;
|
||||
|
||||
long in_visible;
|
||||
|
||||
double zoom;
|
||||
|
||||
void set_playhead(int mouse_x);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
+25
-25
@@ -11,7 +11,7 @@
|
||||
#include "panels/effectcontrols.h"
|
||||
#include "project/undo.h"
|
||||
|
||||
#include "effects/effects.h"
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
|
||||
#include <QPainter>
|
||||
@@ -123,8 +123,8 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
|
||||
if (got_video_values && got_audio_values) break;
|
||||
}
|
||||
} else {
|
||||
entry_point = panel_timeline->getFrameFromScreenPoint(pos.x());
|
||||
panel_timeline->drag_frame_start = entry_point + panel_timeline->getFrameFromScreenPoint(50);
|
||||
entry_point = panel_timeline->getTimelineFrameFromScreenPoint(pos.x());
|
||||
panel_timeline->drag_frame_start = entry_point + panel_timeline->getTimelineFrameFromScreenPoint(50);
|
||||
panel_timeline->drag_track_start = (bottom_align) ? -1 : 0;
|
||||
predicted_new_frame_rate = sequence->frame_rate;
|
||||
}
|
||||
@@ -369,7 +369,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
|
||||
panel_timeline->drag_track_start = panel_timeline->cursor_track;
|
||||
} else {
|
||||
QPoint pos = event->pos();
|
||||
panel_timeline->drag_frame_start = panel_timeline->getFrameFromScreenPoint(pos.x());
|
||||
panel_timeline->drag_frame_start = panel_timeline->getTimelineFrameFromScreenPoint(pos.x());
|
||||
panel_timeline->drag_track_start = getTrackFromScreenPoint(pos.y());
|
||||
}
|
||||
|
||||
@@ -867,7 +867,7 @@ void validate_snapping(const Ghost& g, long* frame_diff) {
|
||||
|
||||
void TimelineWidget::update_ghosts(QPoint& mouse_pos) {
|
||||
int mouse_track = getTrackFromScreenPoint(mouse_pos.y());
|
||||
long frame_diff = panel_timeline->getFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start;
|
||||
long frame_diff = panel_timeline->getTimelineFrameFromScreenPoint(mouse_pos.x()) - panel_timeline->drag_frame_start;
|
||||
int track_diff = (panel_timeline->tool == TIMELINE_TOOL_SLIDE || panel_timeline->transition_select != TA_NO_TRANSITION) ? 0 : mouse_track - panel_timeline->drag_track_start;
|
||||
long validator;
|
||||
|
||||
@@ -1073,7 +1073,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (sequence != NULL) {
|
||||
bool alt = (event->modifiers() & Qt::AltModifier);
|
||||
|
||||
panel_timeline->cursor_frame = panel_timeline->getFrameFromScreenPoint(event->pos().x());
|
||||
panel_timeline->cursor_frame = panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x());
|
||||
panel_timeline->cursor_track = getTrackFromScreenPoint(event->pos().y());
|
||||
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR) {
|
||||
@@ -1359,8 +1359,8 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
panel_timeline->rect_select_h = event->pos().y() - panel_timeline->rect_select_y;
|
||||
if (bottom_align) panel_timeline->rect_select_h -= height();
|
||||
|
||||
long frame_start = panel_timeline->getFrameFromScreenPoint(panel_timeline->rect_select_x);
|
||||
long frame_end = panel_timeline->getFrameFromScreenPoint(event->pos().x());
|
||||
long frame_start = panel_timeline->getTimelineFrameFromScreenPoint(panel_timeline->rect_select_x);
|
||||
long frame_end = panel_timeline->getTimelineFrameFromScreenPoint(event->pos().x());
|
||||
long frame_min = qMin(frame_start, frame_end);
|
||||
long frame_max = qMax(frame_start, frame_end);
|
||||
|
||||
@@ -1432,8 +1432,8 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
|
||||
int lim = 5;
|
||||
int mouse_track = getTrackFromScreenPoint(pos.y());
|
||||
long mouse_frame_lower = panel_timeline->getFrameFromScreenPoint(pos.x()-lim)-1;
|
||||
long mouse_frame_upper = panel_timeline->getFrameFromScreenPoint(pos.x()+lim)+1;
|
||||
long mouse_frame_lower = panel_timeline->getTimelineFrameFromScreenPoint(pos.x()-lim)-1;
|
||||
long mouse_frame_upper = panel_timeline->getTimelineFrameFromScreenPoint(pos.x()+lim)+1;
|
||||
bool found = false;
|
||||
bool cursor_contains_clip = false;
|
||||
int closeness = INT_MAX;
|
||||
@@ -1563,7 +1563,7 @@ void TimelineWidget::redraw_clips() {
|
||||
audio_track_limit = qMax(audio_track_limit, clip->track);
|
||||
}
|
||||
}
|
||||
int panel_width = panel_timeline->getScreenPointFromFrame(end_frame) + 100;
|
||||
int panel_width = panel_timeline->getTimelineScreenPointFromFrame(end_frame) + 100;
|
||||
int panel_height = 0;
|
||||
if (bottom_align) {
|
||||
for (int i=-1;i>=video_track_limit;i--) {
|
||||
@@ -1589,7 +1589,7 @@ void TimelineWidget::redraw_clips() {
|
||||
for (int i=0;i<sequence->clip_count();i++) {
|
||||
Clip* clip = sequence->get_clip(i);
|
||||
if (clip != NULL && is_track_visible(clip->track)) {
|
||||
QRect clip_rect(panel_timeline->getScreenPointFromFrame(clip->timeline_in), getScreenPointFromTrack(clip->track), clip->getLength() * panel_timeline->zoom, panel_timeline->calculate_track_height(clip->track, -1));
|
||||
QRect clip_rect(panel_timeline->getTimelineScreenPointFromFrame(clip->timeline_in), getScreenPointFromTrack(clip->track), clip->getLength() * panel_timeline->zoom, panel_timeline->calculate_track_height(clip->track, -1));
|
||||
clip_painter.fillRect(clip_rect, QColor(clip->color_r, clip->color_g, clip->color_b));
|
||||
|
||||
int thumb_x = clip_rect.x() + 1;
|
||||
@@ -1606,7 +1606,7 @@ void TimelineWidget::redraw_clips() {
|
||||
} else if (ms->preview_done) {
|
||||
// draw thumbnail/waveform
|
||||
long media_length = m->get_length_in_frames(clip->sequence->frame_rate);
|
||||
int waveform_limit = qMin(clip_rect.width(), panel_timeline->getScreenPointFromFrame(media_length - clip->clip_in));
|
||||
int waveform_limit = qMin(clip_rect.width(), panel_timeline->getTimelineScreenPointFromFrame(media_length - clip->clip_in));
|
||||
|
||||
if (waveform_limit < clip_rect.width()) {
|
||||
draw_checkerboard = true;
|
||||
@@ -1616,12 +1616,12 @@ void TimelineWidget::redraw_clips() {
|
||||
if (clip->track < 0) {
|
||||
int space_for_thumb = clip_rect.width();
|
||||
if (clip->opening_transition != NULL) {
|
||||
int ot_width = panel_timeline->getScreenPointFromFrame(clip->opening_transition->length);
|
||||
int ot_width = panel_timeline->getTimelineScreenPointFromFrame(clip->opening_transition->length);
|
||||
thumb_x += ot_width;
|
||||
space_for_thumb -= ot_width;
|
||||
}
|
||||
if (clip->closing_transition != NULL) {
|
||||
space_for_thumb -= panel_timeline->getScreenPointFromFrame(clip->closing_transition->length);
|
||||
space_for_thumb -= panel_timeline->getTimelineScreenPointFromFrame(clip->closing_transition->length);
|
||||
}
|
||||
int thumb_y = clip_painter.fontMetrics().height()+CLIP_TEXT_PADDING+CLIP_TEXT_PADDING;
|
||||
int thumb_height = clip_rect.height()-thumb_y;
|
||||
@@ -1688,7 +1688,7 @@ void TimelineWidget::redraw_clips() {
|
||||
for (int i=0;i<2;i++) {
|
||||
Transition* t = (i == 0) ? clip->opening_transition : clip->closing_transition;
|
||||
if (t != NULL) {
|
||||
int transition_width = panel_timeline->getScreenPointFromFrame(t->length);
|
||||
int transition_width = panel_timeline->getTimelineScreenPointFromFrame(t->length);
|
||||
int transition_height = clip_rect.height();
|
||||
int tr_y = clip_rect.y();
|
||||
int tr_x = 0;
|
||||
@@ -1780,8 +1780,8 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
const Selection& s = panel_timeline->selections.at(i);
|
||||
if (is_track_visible(s.track)) {
|
||||
int selection_y = getScreenPointFromTrack(s.track);
|
||||
int selection_x = panel_timeline->getScreenPointFromFrame(s.in);
|
||||
p.fillRect(selection_x, selection_y, panel_timeline->getScreenPointFromFrame(s.out) - selection_x, panel_timeline->calculate_track_height(s.track, -1), QColor(0, 0, 0, 64));
|
||||
int selection_x = panel_timeline->getTimelineScreenPointFromFrame(s.in);
|
||||
p.fillRect(selection_x, selection_y, panel_timeline->getTimelineScreenPointFromFrame(s.out) - selection_x, panel_timeline->calculate_track_height(s.track, -1), QColor(0, 0, 0, 64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1802,9 +1802,9 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
for (int i=0;i<panel_timeline->ghosts.size();i++) {
|
||||
const Ghost& g = panel_timeline->ghosts.at(i);
|
||||
if (is_track_visible(g.track)) {
|
||||
int ghost_x = panel_timeline->getScreenPointFromFrame(g.in);
|
||||
int ghost_x = panel_timeline->getTimelineScreenPointFromFrame(g.in);
|
||||
int ghost_y = getScreenPointFromTrack(g.track);
|
||||
int ghost_width = panel_timeline->getScreenPointFromFrame(g.out - g.in) - 1;
|
||||
int ghost_width = panel_timeline->getTimelineScreenPointFromFrame(g.out - g.in) - 1;
|
||||
int ghost_height = panel_timeline->calculate_track_height(g.track, -1) - 1;
|
||||
p.setPen(QColor(255, 255, 0));
|
||||
for (int j=0;j<GHOST_THICKNESS;j++) {
|
||||
@@ -1817,7 +1817,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
if (panel_timeline->splitting) {
|
||||
for (int i=0;i<panel_timeline->split_tracks.size();i++) {
|
||||
if (is_track_visible(panel_timeline->split_tracks.at(i))) {
|
||||
int cursor_x = panel_timeline->getScreenPointFromFrame(panel_timeline->drag_frame_start);
|
||||
int cursor_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->drag_frame_start);
|
||||
int cursor_y = getScreenPointFromTrack(panel_timeline->split_tracks.at(i));
|
||||
|
||||
p.setPen(QColor(64, 64, 64));
|
||||
@@ -1828,7 +1828,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
|
||||
// Draw playhead
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = panel_timeline->getScreenPointFromFrame(panel_timeline->playhead);
|
||||
int playhead_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->playhead);
|
||||
p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom());
|
||||
|
||||
// draw border
|
||||
@@ -1839,15 +1839,15 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
// draw snap point
|
||||
if (panel_timeline->snapped) {
|
||||
p.setPen(Qt::white);
|
||||
int snap_x = panel_timeline->getScreenPointFromFrame(panel_timeline->snap_point);
|
||||
int snap_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->snap_point);
|
||||
p.drawLine(snap_x, 0, snap_x, height());
|
||||
}
|
||||
|
||||
// Draw edit cursor
|
||||
if (panel_timeline->tool == TIMELINE_TOOL_EDIT || panel_timeline->tool == TIMELINE_TOOL_RAZOR) {
|
||||
if (is_track_visible(panel_timeline->cursor_track)) {
|
||||
int cursor_x = panel_timeline->getScreenPointFromFrame(panel_timeline->cursor_frame);
|
||||
int cursor_y = getScreenPointFromTrack(panel_timeline->cursor_track);
|
||||
int cursor_x = panel_timeline->getTimelineScreenPointFromFrame(panel_timeline->cursor_frame);
|
||||
int cursor_y = getScreenPointFromTrack(panel_timeline->cursor_track);
|
||||
|
||||
p.setPen(Qt::gray);
|
||||
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->calculate_track_height(panel_timeline->cursor_track, -1));
|
||||
|
||||
+24
-9
@@ -15,6 +15,7 @@
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QAudioOutput>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QtMath>
|
||||
|
||||
extern "C" {
|
||||
@@ -171,18 +172,34 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
if (flip) half_height = -half_height;
|
||||
glOrtho(-half_width, half_width, half_height, -half_height, -1, 1);
|
||||
int anchor_x = ms->video_width/2;
|
||||
int anchor_y = ms->video_height/2;
|
||||
int anchor_y = ms->video_height/2;
|
||||
|
||||
// perform all transform effects
|
||||
c->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y);
|
||||
QOpenGLShaderProgram shader;
|
||||
|
||||
for (int i=nests.size()-1;i>=0;i--) {
|
||||
nests.at(i)->run_video_pre_effect_stack(playhead, &anchor_x, &anchor_y);
|
||||
for (int j=0;j<c->effects.size();j++) {
|
||||
if (c->effects.at(j)->enable_opengl && c->effects.at(j)->is_enabled()) c->effects.at(j)->process_gl(shader, &anchor_x, &anchor_y);
|
||||
}
|
||||
|
||||
if (c->opening_transition != NULL) {
|
||||
int transition_progress = playhead - c->timeline_in;
|
||||
if (transition_progress < c->opening_transition->length) {
|
||||
c->opening_transition->process_transition((double)transition_progress/(double)c->opening_transition->length);
|
||||
}
|
||||
}
|
||||
|
||||
if (c->closing_transition != NULL) {
|
||||
int transition_progress = c->closing_transition->length - (playhead - c->timeline_in - c->getLength() + c->closing_transition->length);
|
||||
if (transition_progress < c->closing_transition->length) {
|
||||
c->closing_transition->process_transition((double)transition_progress/(double)c->closing_transition->length);
|
||||
}
|
||||
}
|
||||
|
||||
int anchor_right = ms->video_width - anchor_x;
|
||||
int anchor_bottom = ms->video_height - anchor_y;
|
||||
|
||||
bool use_gl_shaders = shader.link();
|
||||
if (use_gl_shaders) shader.bind();
|
||||
|
||||
c->texture->bind();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
@@ -196,10 +213,9 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
glVertex2f(-anchor_x, anchor_bottom);
|
||||
glEnd();
|
||||
|
||||
c->texture->release();
|
||||
c->texture->release();
|
||||
|
||||
// perform all transform effects
|
||||
c->run_video_post_effect_stack();
|
||||
if (use_gl_shaders) shader.release();
|
||||
}
|
||||
} else if (render_audio &&
|
||||
c->stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
|
||||
@@ -212,7 +228,6 @@ void ViewerWidget::compose_sequence(QVector<Clip*>& nests, bool render_audio) {
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
nests.append(c);
|
||||
compose_sequence(nests, render_audio);
|
||||
c->run_video_post_effect_stack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user