diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index a2dce700d..da1ee09c2 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -120,6 +120,8 @@ void PreferencesDialog::save() { return; } + bool needs_restart = false; + config.css_path = custom_css_fn->text(); mainWindow->load_css_from_file(config.css_path); config.recording_mode = recordingComboBox->currentIndex() + 1; @@ -130,13 +132,26 @@ void PreferencesDialog::save() { config.upcoming_queue_type = upcoming_queue_type->currentIndex(); config.previous_queue_size = previous_queue_spinbox->value(); config.previous_queue_type = previous_queue_type->currentIndex(); + + if (config.effect_textbox_lines != effect_textbox_lines_field->value()) { + needs_restart = true; + } config.effect_textbox_lines = effect_textbox_lines_field->value(); + if (config.use_software_fallback != use_software_fallbacks_checkbox->isChecked()) { + needs_restart = true; + } + config.use_software_fallback = use_software_fallbacks_checkbox->isChecked(); + // save keyboard shortcuts for (int i=0;iset_action_shortcut(); } + if (needs_restart) { + QMessageBox::information(this, tr("Warning"), tr("Some changed settings will require restarting Olive to take effect")); + } + accept(); } @@ -304,6 +319,12 @@ void PreferencesDialog::setup_ui() { effect_textbox_lines_field->setValue(config.effect_textbox_lines); general_layout->addWidget(effect_textbox_lines_field, 3, 1, 1, 2); + // General -> Use Software Fallbacks When Possible + use_software_fallbacks_checkbox = new QCheckBox(general_tab); + use_software_fallbacks_checkbox->setText(tr("Use Software Fallbacks When Possible")); + use_software_fallbacks_checkbox->setChecked(config.use_software_fallback); + general_layout->addWidget(use_software_fallbacks_checkbox, 4, 0, 1, 1); + tabWidget->addTab(general_tab, tr("General")); // Behavior diff --git a/dialogs/preferencesdialog.h b/dialogs/preferencesdialog.h index 545f8d0af..e9a4e059f 100644 --- a/dialogs/preferencesdialog.h +++ b/dialogs/preferencesdialog.h @@ -61,6 +61,7 @@ private: QDoubleSpinBox* previous_queue_spinbox; QComboBox* previous_queue_type; QSpinBox* effect_textbox_lines_field; + QCheckBox* use_software_fallbacks_checkbox; QVector key_shortcut_actions; QVector key_shortcut_items; diff --git a/effects/internal/texteffect.cpp b/effects/internal/texteffect.cpp index 12368b18a..afdbf2607 100644 --- a/effects/internal/texteffect.cpp +++ b/effects/internal/texteffect.cpp @@ -21,13 +21,13 @@ #include "ui/colorbutton.h" #include "ui/fontcombobox.h" #include "dialogs/texteditdialog.h" +#include "io/config.h" #include "mainwindow.h" TextEffect::TextEffect(Clip *c, const EffectMeta* em) : Effect(c, em) { enable_superimpose = true; - //enable_shader = true; text_val = add_row(tr("Text"))->add_field(EFFECT_FIELD_STRING, "text", 2); QTextEdit* text_widget = static_cast(text_val->ui_element); @@ -93,6 +93,70 @@ TextEffect::TextEffect(Clip *c, const EffectMeta* em) : fragPath = "dropshadow.frag"; } +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::redraw(double timecode) { QColor bkg = set_color_button->get_color_value(timecode); bkg.setAlpha(0); @@ -190,6 +254,26 @@ void TextEffect::redraw(double timecode) { path.addText(text_x, text_y, font, lines.at(i)); } + // draw software shadow + if (!enable_shader && shadow_bool->get_bool_value(timecode)) { + p.setPen(Qt::NoPen); + int shadow_offset = shadow_distance->get_double_value(timecode); + + QPainterPath shadow_path(path); + shadow_path.translate(shadow_offset, shadow_offset); + + QColor col = shadow_color->get_color_value(timecode); + col.setAlpha(0); + img.fill(col); + + col.setAlphaF(shadow_opacity->get_double_value(timecode)*0.01); + p.setBrush(col); + p.drawPath(shadow_path); + + int blurSoftness = shadow_softness->get_double_value(timecode); + if (blurSoftness > 0) blurred2(img, img.rect(), blurSoftness, false); + } + // draw outline int outline_width_val = outline_width->get_double_value(timecode); if (outline_bool->get_bool_value(timecode) && outline_width_val > 0) { @@ -207,7 +291,7 @@ void TextEffect::redraw(double timecode) { } void TextEffect::shadow_enable(bool e) { - enable_shader = e; + enable_shader = (e && !config.use_software_fallback); close(); shadow_color->set_enabled(e); diff --git a/io/config.cpp b/io/config.cpp index cd77bf374..5771f1916 100644 --- a/io/config.cpp +++ b/io/config.cpp @@ -47,7 +47,8 @@ Config::Config() loop(true), pause_at_out_point(true), seek_also_selects(false), - effect_textbox_lines(3) + effect_textbox_lines(3), + use_software_fallback(false) {} void Config::load(QString path) { @@ -169,7 +170,10 @@ void Config::load(QString path) { } else if (stream.name() == "EffectTextboxLines") { stream.readNext(); effect_textbox_lines = stream.text().toInt(); - } + } else if (stream.name() == "UseSoftwareFallback") { + stream.readNext(); + use_software_fallback = (stream.text() == "1"); + } } } if (stream.hasError()) { @@ -229,7 +233,8 @@ void Config::save(QString path) { stream.writeTextElement("PauseAtOutPoint", QString::number(pause_at_out_point)); stream.writeTextElement("SeekAlsoSelects", QString::number(seek_also_selects)); stream.writeTextElement("CSSPath", css_path); - stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines)); + stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines)); + stream.writeTextElement("UseSoftwareFallback", QString::number(use_software_fallback)); stream.writeEndElement(); // configuration stream.writeEndDocument(); // doc diff --git a/io/config.h b/io/config.h index a3782b2ee..9a6806216 100644 --- a/io/config.h +++ b/io/config.h @@ -63,6 +63,7 @@ struct Config { bool seek_also_selects; QString css_path; int effect_textbox_lines; + bool use_software_fallback; void load(QString path); void save(QString path);