fixed export and timeline crashes and way better titler
This commit is contained in:
@@ -486,7 +486,7 @@ void ExportDialog::on_pushButton_clicked()
|
||||
et->audio_enabled = ui->audioGroupbox->isChecked();
|
||||
if (et->audio_enabled) {
|
||||
et->audio_codec = format_acodecs.at(ui->acodecCombobox->currentIndex());
|
||||
et->audio_sampling_rate = 48000;
|
||||
et->audio_sampling_rate = ui->samplingRateSpinbox->value();
|
||||
et->audio_bitrate = ui->audiobitrateSpinbox->value();
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -143,11 +143,11 @@ EffectRow::EffectRow(Effect *parent, QGridLayout *uilayout, const QString &n, in
|
||||
ui->addWidget(new QLabel(name), row, 0);
|
||||
}
|
||||
|
||||
EffectField* EffectRow::add_field(int type) {
|
||||
EffectField* EffectRow::add_field(int type, int colspan) {
|
||||
EffectField* field = new EffectField(type);
|
||||
fields.append(field);
|
||||
QWidget* element = field->get_ui_element();
|
||||
ui->addWidget(element, ui_row, fields.size());
|
||||
ui->addWidget(element, ui_row, fields.size(), 1, colspan);
|
||||
return field;
|
||||
}
|
||||
|
||||
@@ -287,6 +287,10 @@ const QString EffectField::get_font_name() {
|
||||
return static_cast<FontCombobox*>(ui_element)->currentText();
|
||||
}
|
||||
|
||||
void EffectField::set_font_name(const QString& s) {
|
||||
static_cast<FontCombobox*>(ui_element)->setCurrentText(s);
|
||||
}
|
||||
|
||||
QColor EffectField::get_color_value() {
|
||||
return static_cast<ColorButton*>(ui_element)->get_color();
|
||||
}
|
||||
|
||||
+2
-1
@@ -59,6 +59,7 @@ public:
|
||||
void set_bool_value(bool b);
|
||||
|
||||
const QString get_font_name();
|
||||
void set_font_name(const QString& s);
|
||||
|
||||
QColor get_color_value();
|
||||
void set_color_value(QColor color);
|
||||
@@ -75,7 +76,7 @@ class EffectRow {
|
||||
public:
|
||||
EffectRow(Effect* parent, QGridLayout* uilayout, const QString& n, int row);
|
||||
~EffectRow();
|
||||
EffectField* add_field(int type);
|
||||
EffectField* add_field(int type, int colspan = 1);
|
||||
EffectField* field(int i);
|
||||
int fieldCount();
|
||||
void set_keyframe(int field, long time);
|
||||
|
||||
+17
-6
@@ -7,6 +7,7 @@
|
||||
#include <QFont>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QWidget>
|
||||
class QSpinBox;
|
||||
class QCheckBox;
|
||||
class LabelSlider;
|
||||
@@ -43,6 +44,7 @@ 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;
|
||||
@@ -97,8 +99,8 @@ public:
|
||||
TextEffect(Clip* c);
|
||||
~TextEffect();
|
||||
void post_gl();
|
||||
Effect* copy(Clip* c);
|
||||
void refresh();
|
||||
Effect* copy(Clip* c);
|
||||
|
||||
EffectField* text_val;
|
||||
EffectField* size_val;
|
||||
@@ -106,15 +108,24 @@ public:
|
||||
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();
|
||||
private:
|
||||
void destroy_texture();
|
||||
QOpenGLTexture* texture;
|
||||
QImage pixmap;
|
||||
QFont font;
|
||||
int width;
|
||||
int height;
|
||||
QOpenGLTexture* texture;
|
||||
QFont font;
|
||||
QImage pixmap;
|
||||
};
|
||||
|
||||
class SolidEffect : public Effect {
|
||||
|
||||
+222
-27
@@ -10,6 +10,7 @@
|
||||
#include <QFontDatabase>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/collapsiblewidget.h"
|
||||
@@ -21,22 +22,16 @@
|
||||
|
||||
TextEffect::TextEffect(Clip *c) :
|
||||
Effect(c, EFFECT_TYPE_VIDEO, VIDEO_TEXT_EFFECT),
|
||||
texture(NULL),
|
||||
width(0),
|
||||
height(0)
|
||||
texture(NULL)
|
||||
{
|
||||
EffectRow* text_row = add_row("Text:");
|
||||
text_val = text_row->add_field(EFFECT_FIELD_STRING);
|
||||
text_val = add_row("Text:")->add_field(EFFECT_FIELD_STRING, 2);
|
||||
|
||||
EffectRow* font_row = add_row("Font:");
|
||||
set_font_combobox = font_row->add_field(EFFECT_FIELD_FONT);
|
||||
set_font_combobox = add_row("Font:")->add_field(EFFECT_FIELD_FONT, 2);
|
||||
|
||||
EffectRow* size_row = add_row("Size:");
|
||||
size_val = size_row->add_field(EFFECT_FIELD_DOUBLE);
|
||||
size_val = add_row("Size:")->add_field(EFFECT_FIELD_DOUBLE, 2);
|
||||
size_val->set_double_minimum_value(0);
|
||||
|
||||
EffectRow* color_row = add_row("Color:");
|
||||
set_color_button = color_row->add_field(EFFECT_FIELD_COLOR);
|
||||
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);
|
||||
@@ -49,10 +44,35 @@ TextEffect::TextEffect(Clip *c) :
|
||||
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);
|
||||
|
||||
connect(text_val, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
connect(size_val, SIGNAL(changed()), this, SLOT(update_texture()));
|
||||
@@ -60,6 +80,15 @@ TextEffect::TextEffect(Clip *c) :
|
||||
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()));
|
||||
|
||||
update_texture();
|
||||
}
|
||||
@@ -76,26 +105,190 @@ void TextEffect::destroy_texture() {
|
||||
texture->destroy();
|
||||
}
|
||||
|
||||
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 (parent_clip->sequence->width != width || parent_clip->sequence->height != height) {
|
||||
width = parent_clip->sequence->width;
|
||||
height = parent_clip->sequence->height;
|
||||
pixmap = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
|
||||
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::TextAntialiasing, true);
|
||||
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());
|
||||
font.setStyleHint(QFont::Helvetica, QFont::PreferAntialias);
|
||||
p.setFont(font);
|
||||
QFontMetrics fm(font);
|
||||
|
||||
p.setPen(set_color_button->get_color_value());
|
||||
p.drawText(QRect(0, 0, width, height), halign_field->get_combo_data().toInt() | valign_field->get_combo_data().toInt() | Qt::TextWordWrap, text_val->get_string_value());
|
||||
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);
|
||||
@@ -115,8 +308,8 @@ void TextEffect::post_gl() {
|
||||
if (texture != NULL) {
|
||||
texture->bind();
|
||||
|
||||
int half_width = width/2;
|
||||
int half_height = height/2;
|
||||
int half_width = pixmap.width()/2;
|
||||
int half_height = pixmap.height()/2;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
@@ -134,10 +327,12 @@ void TextEffect::post_gl() {
|
||||
}
|
||||
|
||||
Effect* TextEffect::copy(Clip* c) {
|
||||
/*TextEffect* e = new TextEffect(c);
|
||||
e->text_val->setPlainText(text_val->toPlainText());
|
||||
e->size_val->set_value(size_val->value());
|
||||
e->set_color_button->set_color(set_color_button->get_color());
|
||||
return e;*/
|
||||
return NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,21 @@ TransformEffect::TransformEffect(Clip* c) : Effect(c, EFFECT_TYPE_VIDEO, VIDEO_T
|
||||
connect(uniform_scale_box, SIGNAL(toggled(bool)), this, SLOT(toggle_uniform_scale(bool)));
|
||||
}
|
||||
|
||||
Effect* TransformEffect::copy(Clip* c) {
|
||||
TransformEffect* t = new TransformEffect(c);
|
||||
t->position_x->set_double_value(position_x->get_double_value());
|
||||
t->position_y->set_double_value(position_y->get_double_value());
|
||||
t->scale_x->set_double_value(scale_x->get_double_value());
|
||||
t->scale_y->set_double_value(scale_y->get_double_value());
|
||||
t->uniform_scale_field->set_bool_value(uniform_scale_field->get_bool_value());
|
||||
t->rotation->set_double_value(rotation->get_double_value());
|
||||
t->anchor_x_box->set_double_value(anchor_x_box->get_double_value());
|
||||
t->anchor_y_box->set_double_value(anchor_y_box->get_double_value());
|
||||
t->opacity->set_double_value(opacity->get_double_value());
|
||||
t->blend_mode_box->set_combo_index(blend_mode_box->get_combo_index());
|
||||
return t;
|
||||
}
|
||||
|
||||
void TransformEffect::refresh() {
|
||||
if (parent_clip->sequence != NULL) {
|
||||
double default_pos_x = parent_clip->sequence->width/2;
|
||||
|
||||
@@ -24,10 +24,9 @@ VolumeEffect::VolumeEffect(Clip* c) : Effect(c, EFFECT_TYPE_AUDIO, AUDIO_VOLUME_
|
||||
void VolumeEffect::refresh() {}
|
||||
|
||||
Effect* VolumeEffect::copy(Clip* c) {
|
||||
/*VolumeEffect* v = new VolumeEffect(c);
|
||||
v->volume_val->set_value(volume_val->value());
|
||||
return v;*/
|
||||
return NULL;
|
||||
VolumeEffect* v = new VolumeEffect(c);
|
||||
v->volume_val->set_double_value(volume_val->get_double_value());
|
||||
return v;
|
||||
}
|
||||
|
||||
void VolumeEffect::process_audio(quint8* samples, int nb_bytes) {
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ void ExportThread::run() {
|
||||
panel_timeline->pause();
|
||||
// av_log_set_level(AV_LOG_DEBUG);
|
||||
|
||||
if (!panel_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
if (!panel_viewer->viewer_widget->context()->makeCurrent(&surface)) {
|
||||
qDebug() << "[ERROR] Make current failed";
|
||||
ed->export_error = "could not make OpenGL context current";
|
||||
return;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#include "clip.h"
|
||||
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
#include "io/media.h"
|
||||
#include "playback/playback.h"
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/project.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/effect.h"
|
||||
#include "effects/effect.h"
|
||||
#include "effects/transition.h"
|
||||
#include "playback/playback.h"
|
||||
#include "playback/audio.h"
|
||||
|
||||
Reference in New Issue
Block a user