Implement blurred background for photo editor.
This commit is contained in:
parent
9513aaa768
commit
1d7ad701b4
2 changed files with 177 additions and 4 deletions
|
@ -7,32 +7,188 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "editor/editor_layer_widget.h"
|
#include "editor/editor_layer_widget.h"
|
||||||
|
|
||||||
|
#include "ui/painter.h"
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
|
||||||
|
constexpr auto kCacheBackgroundFullTimeout = crl::time(1000);
|
||||||
|
constexpr auto kFadeBackgroundDuration = crl::time(200);
|
||||||
|
|
||||||
|
// Thread: Main.
|
||||||
|
[[nodiscard]] bool IsNightMode() {
|
||||||
|
return (st::windowBg->c.lightnessF() < 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QColor BlurOverlayColor(bool night) {
|
||||||
|
return QColor(16, 16, 16, night ? 128 : 192);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QImage ProcessBackground(QImage image, bool night) {
|
||||||
|
const auto size = image.size();
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
p.fillRect(
|
||||||
|
QRect(QPoint(), image.size() / image.devicePixelRatio()),
|
||||||
|
BlurOverlayColor(night));
|
||||||
|
p.end();
|
||||||
|
return Images::DitherImage(
|
||||||
|
Images::BlurLargeImage(
|
||||||
|
std::move(image).scaled(
|
||||||
|
size / style::ConvertScale(4),
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation),
|
||||||
|
24).scaled(
|
||||||
|
size,
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
LayerWidget::LayerWidget(
|
LayerWidget::LayerWidget(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
base::unique_qptr<Ui::RpWidget> content)
|
base::unique_qptr<Ui::RpWidget> content)
|
||||||
: Ui::LayerWidget(parent)
|
: Ui::LayerWidget(parent)
|
||||||
, _content(std::move(content)) {
|
, _content(std::move(content))
|
||||||
|
, _backgroundTimer([=] { checkCacheBackground(); }) {
|
||||||
_content->setParent(this);
|
_content->setParent(this);
|
||||||
_content->show();
|
_content->show();
|
||||||
|
|
||||||
paintRequest(
|
paintRequest(
|
||||||
) | rpl::start_with_next([=](const QRect &clip) {
|
) | rpl::start_with_next([=](const QRect &clip) {
|
||||||
auto p = QPainter(this);
|
auto p = QPainter(this);
|
||||||
p.fillRect(clip, st::photoEditorBg);
|
const auto faded = _backgroundFade.value(1.);
|
||||||
|
if (faded < 1.) {
|
||||||
|
p.drawImage(rect(), _backgroundBack);
|
||||||
|
if (faded > 0.) {
|
||||||
|
p.setOpacity(faded);
|
||||||
|
p.drawImage(rect(), _background);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.drawImage(rect(), _background);
|
||||||
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayerWidget::eventHook(QEvent *e) {
|
||||||
|
return RpWidget::eventHook(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::start() {
|
||||||
|
_backgroundNight = IsNightMode();
|
||||||
|
_background = ProcessBackground(renderBackground(), _backgroundNight);
|
||||||
|
|
||||||
sizeValue(
|
sizeValue(
|
||||||
) | rpl::start_with_next([=](const QSize &size) {
|
) | rpl::start_with_next([=](const QSize &size) {
|
||||||
|
checkBackgroundStale();
|
||||||
_content->resize(size);
|
_content->resize(size);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
style::PaletteChanged() | rpl::start_with_next([=] {
|
||||||
|
checkBackgroundStale();
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::checkBackgroundStale() {
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto &ready = _backgroundNext.isNull()
|
||||||
|
? _background
|
||||||
|
: _backgroundNext;
|
||||||
|
if (ready.size() == size() * ratio
|
||||||
|
&& _backgroundNight == IsNightMode()) {
|
||||||
|
_backgroundTimer.cancel();
|
||||||
|
} else if (!_backgroundCaching && !_backgroundTimer.isActive()) {
|
||||||
|
_lastAreaChangeTime = crl::now();
|
||||||
|
_backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage LayerWidget::renderBackground() {
|
||||||
|
const auto target = parentWidget()->parentWidget();
|
||||||
|
Ui::SendPendingMoveResizeEvents(target);
|
||||||
|
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
auto image = QImage(size() * ratio, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image.setDevicePixelRatio(ratio);
|
||||||
|
|
||||||
|
const auto shown = !parentWidget()->isHidden();
|
||||||
|
if (shown) parentWidget()->hide();
|
||||||
|
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
Ui::RenderWidget(p, target, QPoint(), geometry());
|
||||||
|
p.end();
|
||||||
|
|
||||||
|
if (shown) parentWidget()->show();
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::checkCacheBackground() {
|
||||||
|
if (_backgroundCaching || _backgroundTimer.isActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto now = crl::now();
|
||||||
|
if (now - _lastAreaChangeTime < kCacheBackgroundFullTimeout
|
||||||
|
&& QGuiApplication::mouseButtons() != 0) {
|
||||||
|
_backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::cacheBackground() {
|
||||||
|
_backgroundCaching = true;
|
||||||
|
const auto weak = Ui::MakeWeak(this);
|
||||||
|
const auto night = IsNightMode();
|
||||||
|
crl::async([weak, night, image = renderBackground()]() mutable {
|
||||||
|
auto result = ProcessBackground(image, night);
|
||||||
|
crl::on_main([weak, night, result = std::move(result)]() mutable {
|
||||||
|
if (const auto strong = weak.data()) {
|
||||||
|
strong->backgroundReady(std::move(result), night);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::backgroundReady(QImage background, bool night) {
|
||||||
|
_backgroundCaching = false;
|
||||||
|
|
||||||
|
const auto required = size() * style::DevicePixelRatio();
|
||||||
|
if (background.size() == required && night == IsNightMode()) {
|
||||||
|
_backgroundNext = std::move(background);
|
||||||
|
_backgroundNight = night;
|
||||||
|
if (!_backgroundFade.animating()) {
|
||||||
|
startBackgroundFade();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
} else if (_background.size() != required) {
|
||||||
|
_backgroundTimer.callOnce(kCacheBackgroundFastTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LayerWidget::startBackgroundFade() {
|
||||||
|
if (_backgroundNext.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_backgroundBack = std::move(_background);
|
||||||
|
_background = base::take(_backgroundNext);
|
||||||
|
_backgroundFade.start([=] {
|
||||||
|
update();
|
||||||
|
if (!_backgroundFade.animating()) {
|
||||||
|
_backgroundBack = QImage();
|
||||||
|
startBackgroundFade();
|
||||||
|
}
|
||||||
|
}, 0., 1., kFadeBackgroundDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayerWidget::parentResized() {
|
void LayerWidget::parentResized() {
|
||||||
resizeToWidth(parentWidget()->width());
|
resizeToWidth(parentWidget()->width());
|
||||||
|
if (_background.isNull()) {
|
||||||
|
start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayerWidget::keyPressEvent(QKeyEvent *e) {
|
void LayerWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "editor/photo_editor_common.h"
|
#include "editor/photo_editor_common.h"
|
||||||
#include "base/unique_qptr.h"
|
#include "base/unique_qptr.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
enum class ImageRoundRadius;
|
enum class ImageRoundRadius;
|
||||||
|
|
||||||
|
@ -30,12 +31,28 @@ public:
|
||||||
void parentResized() override;
|
void parentResized() override;
|
||||||
bool closeByOutsideClick() const override;
|
bool closeByOutsideClick() const override;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
|
bool eventHook(QEvent *e) override;
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
private:
|
void start();
|
||||||
|
void cacheBackground();
|
||||||
|
void checkBackgroundStale();
|
||||||
|
void checkCacheBackground();
|
||||||
|
[[nodiscard]] QImage renderBackground();
|
||||||
|
void backgroundReady(QImage background, bool night);
|
||||||
|
void startBackgroundFade();
|
||||||
|
|
||||||
const base::unique_qptr<Ui::RpWidget> _content;
|
const base::unique_qptr<Ui::RpWidget> _content;
|
||||||
|
QImage _backgroundBack;
|
||||||
|
QImage _background;
|
||||||
|
QImage _backgroundNext;
|
||||||
|
Ui::Animations::Simple _backgroundFade;
|
||||||
|
base::Timer _backgroundTimer;
|
||||||
|
crl::time _lastAreaChangeTime = 0;
|
||||||
|
bool _backgroundCaching = false;
|
||||||
|
bool _backgroundNight = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue