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
|
||||
Reference in New Issue
Block a user