From c519dbdf48d08ebbf2152f3c67c4ef684e338c1b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 15 Aug 2018 15:18:47 +1000 Subject: [PATCH] fixed export and timeline crashes and way better titler --- dialogs/exportdialog.cpp | 2 +- effects/effect.cpp | 8 +- effects/effect.h | 3 +- effects/effects.h | 23 +++- effects/texteffect.cpp | 249 ++++++++++++++++++++++++++++++++---- effects/transformeffect.cpp | 15 +++ effects/volumeeffect.cpp | 7 +- io/exportthread.cpp | 2 +- project/clip.cpp | 2 +- ui/viewerwidget.cpp | 2 +- 10 files changed, 269 insertions(+), 44 deletions(-) diff --git a/dialogs/exportdialog.cpp b/dialogs/exportdialog.cpp index 620cc2ff6..ae3bbb4b4 100644 --- a/dialogs/exportdialog.cpp +++ b/dialogs/exportdialog.cpp @@ -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(); } diff --git a/effects/effect.cpp b/effects/effect.cpp index 5e4316a74..eebf5a03b 100644 --- a/effects/effect.cpp +++ b/effects/effect.cpp @@ -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(ui_element)->currentText(); } +void EffectField::set_font_name(const QString& s) { + static_cast(ui_element)->setCurrentText(s); +} + QColor EffectField::get_color_value() { return static_cast(ui_element)->get_color(); } diff --git a/effects/effect.h b/effects/effect.h index aef31a227..ec754cc8d 100644 --- a/effects/effect.h +++ b/effects/effect.h @@ -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); diff --git a/effects/effects.h b/effects/effects.h index 2c6e3aba2..afd273085 100644 --- a/effects/effects.h +++ b/effects/effects.h @@ -7,6 +7,7 @@ #include #include #include +#include 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 { diff --git a/effects/texteffect.cpp b/effects/texteffect.cpp index 8f5761b5b..5aebf3504 100644 --- a/effects/texteffect.cpp +++ b/effects/texteffect.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #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 pixmap.width()) { + int last_space_index = 0; + for (int j=0;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;iget_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 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; } diff --git a/effects/transformeffect.cpp b/effects/transformeffect.cpp index 273931075..525c72cb9 100644 --- a/effects/transformeffect.cpp +++ b/effects/transformeffect.cpp @@ -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; diff --git a/effects/volumeeffect.cpp b/effects/volumeeffect.cpp index 56f247893..f2a8855ff 100644 --- a/effects/volumeeffect.cpp +++ b/effects/volumeeffect.cpp @@ -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) { diff --git a/io/exportthread.cpp b/io/exportthread.cpp index 05b069942..445bd0047 100644 --- a/io/exportthread.cpp +++ b/io/exportthread.cpp @@ -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; diff --git a/project/clip.cpp b/project/clip.cpp index 546f86fab..23ce91676 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -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" diff --git a/ui/viewerwidget.cpp b/ui/viewerwidget.cpp index 6f5c2239a..269272852 100644 --- a/ui/viewerwidget.cpp +++ b/ui/viewerwidget.cpp @@ -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"