diff --git a/src/playlist/playlistview.cpp b/src/playlist/playlistview.cpp index 145f110b3..234792eae 100644 --- a/src/playlist/playlistview.cpp +++ b/src/playlist/playlistview.cpp @@ -1017,16 +1017,19 @@ void PlaylistView::ReloadSettings() { } } QString background_image_filename = s.value(kSettingBackgroundImageFilename).toString(); + int blur_radius = s.value("blur_radius").toInt(); // Check if background properties have changed. // We change properties only if they have actually changed, to avoid to call // set_background_image when it is not needed, as this will cause the fading // animation to start again. This also avoid to do useless // "force_background_redraw". if (background_image_filename != background_image_filename_ - || background_type != background_image_type_) { + || background_type != background_image_type_ || + blur_radius_ != blur_radius) { // Store background properties background_image_type_ = background_type; background_image_filename_ = background_image_filename; + blur_radius_ = blur_radius; if (background_image_type_ == Custom) { set_background_image(QImage(background_image_filename)); } else if (background_image_type_ == AlbumCover) { @@ -1190,18 +1193,93 @@ void PlaylistView::set_background_image(const QImage& image) { else background_image_ = image; + + // Apply opacity filter uchar* bits = background_image_.bits(); for (int i = 0; i < background_image_.height() * background_image_.bytesPerLine(); i+=4) { bits[i+3] = kBackgroundOpacity * 255; } + if (blur_radius_ != 0) + background_image_ = blur_background_image(background_image_, blur_radius_, false); + if (isVisible()) { previous_background_image_opacity_ = 1.0; fade_animation_->start(); } } + +QImage PlaylistView::blur_background_image(const QImage& image, 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); + QRect rect = image.rect(); + 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 PlaylistView::FadePreviousBackgroundImage(qreal value) { previous_background_image_opacity_ = value; if (qFuzzyCompare(previous_background_image_opacity_, qreal(0.0))) { diff --git a/src/playlist/playlistview.h b/src/playlist/playlistview.h index 8affa311d..75d94e0f9 100644 --- a/src/playlist/playlistview.h +++ b/src/playlist/playlistview.h @@ -73,6 +73,7 @@ class PlaylistView : public QTreeView { // Constants for settings: are persistent, values should not be changed static const char* kSettingBackgroundImageType; static const char* kSettingBackgroundImageFilename; + static const int kSettingBackgroundBlurLevel; static ColumnAlignmentMap DefaultColumnAlignment(); @@ -166,6 +167,7 @@ class PlaylistView : public QTreeView { // Save image as the background_image_ after applying some modifications (opacity, ...). // Should be used instead of modifying background_image_ directly void set_background_image(const QImage& image); + QImage blur_background_image(const QImage& image, int radius, bool alphaOnly); private: static const int kGlowIntensitySteps; @@ -173,7 +175,7 @@ class PlaylistView : public QTreeView { static const int kDropIndicatorWidth; static const int kDropIndicatorGradientWidth; static const qreal kBackgroundOpacity; - + QList GetEditableColumns(); QModelIndex NextEditableIndex(const QModelIndex& current); QModelIndex PrevEditableIndex(const QModelIndex& current); @@ -194,6 +196,7 @@ class PlaylistView : public QTreeView { // particular (in terms of format, opacity), you should probably use // set_background_image_type instead of modifying background_image_ directly QImage background_image_; + int blur_radius_; // Used if background image is a filemane QString background_image_filename_; QImage current_song_cover_art_; diff --git a/src/ui/appearancesettingspage.cpp b/src/ui/appearancesettingspage.cpp index c0ef3c7be..687d0081f 100644 --- a/src/ui/appearancesettingspage.cpp +++ b/src/ui/appearancesettingspage.cpp @@ -56,6 +56,7 @@ AppearanceSettingsPage::AppearanceSettingsPage(SettingsDialog* dialog) connect(ui_->select_foreground_color, SIGNAL(pressed()), SLOT(SelectForegroundColor())); connect(ui_->select_background_color, SIGNAL(pressed()), SLOT(SelectBackgroundColor())); connect(ui_->use_a_custom_color_set, SIGNAL(toggled(bool)), SLOT(UseCustomColorSetOptionChanged(bool))); + connect(ui_->blur_slider, SIGNAL(valueChanged(int)), SLOT(BlurLevelChanged(int))); connect(ui_->select_background_image_filename_button, SIGNAL(pressed()), SLOT(SelectBackgroundImage())); connect(ui_->use_custom_background_image, SIGNAL(toggled(bool)), @@ -114,6 +115,7 @@ void AppearanceSettingsPage::Load() { ui_->use_default_background->setChecked(true); } ui_->background_image_filename->setText(playlist_view_background_image_filename_); + s.endGroup(); // Moodbar settings @@ -154,7 +156,8 @@ void AppearanceSettingsPage::Save() { playlist_view_background_image_filename_); } s.setValue(PlaylistView::kSettingBackgroundImageType, - playlist_view_background_image_type_); + playlist_view_background_image_type_); + s.setValue("blur_radius", ui_->blur_slider->value()); s.endGroup(); // Moodbar settings @@ -230,6 +233,12 @@ void AppearanceSettingsPage::SelectBackgroundImage() { ui_->background_image_filename->setText(playlist_view_background_image_filename_); } +void AppearanceSettingsPage::BlurLevelChanged(int value) { + background_blur_radius_ = value; + ui_->background_blur_radius_label->setText(QString("%1px").arg(value)); + +} + void AppearanceSettingsPage::InitMoodbarPreviews() { #ifdef HAVE_MOODBAR if (initialised_moodbar_previews_) diff --git a/src/ui/appearancesettingspage.h b/src/ui/appearancesettingspage.h index f1b307efb..ae90730bb 100644 --- a/src/ui/appearancesettingspage.h +++ b/src/ui/appearancesettingspage.h @@ -42,6 +42,7 @@ private slots: void SelectBackgroundColor(); void UseCustomColorSetOptionChanged(bool); void SelectBackgroundImage(); + void BlurLevelChanged(int); private: static const int kMoodbarPreviewWidth; @@ -62,7 +63,8 @@ private: QColor current_background_color_; PlaylistView::BackgroundImageType playlist_view_background_image_type_; QString playlist_view_background_image_filename_; - + int background_blur_radius_; + bool initialised_moodbar_previews_; }; diff --git a/src/ui/appearancesettingspage.ui b/src/ui/appearancesettingspage.ui index e7ced16a4..c2719a5e8 100644 --- a/src/ui/appearancesettingspage.ui +++ b/src/ui/appearancesettingspage.ui @@ -155,6 +155,52 @@ + + + + + + true + + + Select blur radius: + + + + + + + true + + + 0px + + + + + + + 0 + + + 10 + + + 0 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1 + + + + +