Move background caching to Window::SessionController.

This commit is contained in:
John Preston 2021-08-13 18:39:50 +03:00
parent 1bc5277d51
commit 2667bb3568
5 changed files with 168 additions and 161 deletions

View file

@ -7,14 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mainwidget.h"
#include <rpl/combine.h>
#include <rpl/merge.h>
#include <rpl/flatten_latest.h>
#include "api/api_updates.h"
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_wall_paper.h"
#include "data/data_web_page.h"
#include "data/data_game.h"
#include "data/data_peer_values.h"
@ -122,9 +120,6 @@ namespace {
// Send channel views each second.
constexpr auto kSendViewsTimeout = crl::time(1000);
// Cache background scaled image after 3s.
constexpr auto kCacheBackgroundTimeout = 3000;
} // namespace
enum StackItemType {
@ -239,7 +234,6 @@ MainWidget::MainWidget(
, _dialogs(this, _controller)
, _history(this, _controller)
, _playerPlaylist(this, _controller)
, _cacheBackgroundTimer([=] { cacheBackground(); })
, _viewsIncrementTimer([=] { viewsIncrement(); })
, _changelogs(Core::Changelogs::Create(&controller->session())) {
setupConnectingWidget();
@ -345,15 +339,6 @@ MainWidget::MainWidget(
QCoreApplication::instance()->installEventFilter(this);
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->updates(
) | rpl::start_with_next([=](const Update &update) {
if (update.type == Update::Type::New
|| update.type == Update::Type::Changed) {
clearCachedBackground();
}
}, lifetime());
subscribe(Media::Player::instance()->playerWidgetOver(), [this](bool over) {
if (over) {
if (_playerPlaylist->isHidden()) {
@ -780,76 +765,6 @@ bool MainWidget::selectingPeer() const {
return _hider ? true : false;
}
void MainWidget::cacheBackground() {
const auto background = Window::Theme::Background();
if (background->colorForFill()) {
return;
}
const auto gradient = background->gradientForFill();
const auto patternOpacity = background->paper().patternOpacity();
const auto &bg = background->pixmap();
if (background->tile() || bg.isNull()) {
auto result = gradient.isNull()
? QImage(
_willCacheFor.size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied)
: gradient.scaled(
_willCacheFor.size() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
if (!bg.isNull()) {
QPainter p(&result);
if (!gradient.isNull()) {
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
const auto &tiled = background->pixmapForTiled();
auto w = tiled.width() / cRetinaFactor();
auto h = tiled.height() / cRetinaFactor();
auto sx = 0;
auto sy = 0;
auto cx = qCeil(_willCacheFor.width() / w);
auto cy = qCeil(_willCacheFor.height() / h);
for (int i = sx; i < cx; ++i) {
for (int j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, j * h), tiled);
}
}
}
_cachedX = 0;
_cachedY = 0;
_cachedBackground = Ui::PixmapFromImage(std::move(result));
} else {
QRect to, from;
Window::Theme::ComputeBackgroundRects(_willCacheFor, bg.size(), to, from);
auto image = bg.toImage().copy(from).scaled(
to.width() * cIntRetinaFactor(),
to.height() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
auto result = gradient.isNull()
? std::move(image)
: gradient.scaled(
image.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
if (!gradient.isNull()) {
QPainter p(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
p.drawImage(QRect(QPoint(), to.size()), image);
}
image = QImage();
_cachedX = to.x();
_cachedY = to.y();
_cachedBackground = Ui::PixmapFromImage(std::move(result));
_cachedBackground.setDevicePixelRatio(cRetinaFactor());
}
_cachedFor = _willCacheFor;
}
crl::time MainWidget::highlightStartTime(not_null<const HistoryItem*> item) const {
return _history->highlightStartTime(item);
}
@ -1179,25 +1094,6 @@ void MainWidget::dialogsCancelled() {
_history->activate();
}
void MainWidget::clearCachedBackground() {
_cachedBackground = QPixmap();
_cacheBackgroundTimer.cancel();
update();
}
QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) {
if (!_cachedBackground.isNull() && forRect == _cachedFor) {
x = _cachedX;
y = _cachedY;
return _cachedBackground;
}
if (_willCacheFor != forRect || !_cacheBackgroundTimer.isActive()) {
_willCacheFor = forRect;
_cacheBackgroundTimer.callOnce(kCacheBackgroundTimeout);
}
return QPixmap();
}
void MainWidget::setChatBackground(
const Data::WallPaper &background,
QImage &&image) {

View file

@ -185,8 +185,6 @@ public:
void searchMessages(const QString &query, Dialogs::Key inChat);
QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
void setChatBackground(
const Data::WallPaper &background,
QImage &&image = QImage());
@ -299,9 +297,6 @@ private:
void showAll();
void clearHider(not_null<Window::HistoryHider*> instance);
void cacheBackground();
void clearCachedBackground();
[[nodiscard]] auto floatPlayerDelegate()
-> not_null<Media::Player::FloatDelegate*>;
not_null<Ui::RpWidget*> floatPlayerWidget() override;
@ -389,12 +384,6 @@ private:
int _exportTopBarHeight = 0;
int _contentScrollAddToY = 0;
QPixmap _cachedBackground;
QRect _cachedFor, _willCacheFor;
int _cachedX = 0;
int _cachedY = 0;
base::Timer _cacheBackgroundTimer;
PhotoData *_deletingPhoto = nullptr;
base::flat_map<not_null<PeerData*>, base::flat_set<MsgId>> _viewsIncremented;

View file

@ -97,51 +97,50 @@ void SectionWidget::PaintBackground(
return;
}
auto fromy = controller->content()->backgroundFromY();
auto x = 0, y = 0;
auto cached = controller->content()->cachedBackground(fill, x, y);
if (cached.isNull()) {
const auto gradient = background->gradientForFill();
const auto patternOpacity = background->paper().patternOpacity();
const auto &bg = background->pixmap();
if (background->tile() || bg.isNull()) {
if (!gradient.isNull()) {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(fill, gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
if (!bg.isNull()) {
auto &tiled = background->pixmapForTiled();
auto left = clip.left();
auto top = clip.top();
auto right = clip.left() + clip.width();
auto bottom = clip.top() + clip.height();
auto w = tiled.width() / cRetinaFactor();
auto h = tiled.height() / cRetinaFactor();
auto sx = qFloor(left / w);
auto sy = qFloor((top - fromy) / h);
auto cx = qCeil(right / w);
auto cy = qCeil((bottom - fromy) / h);
for (auto i = sx; i < cx; ++i) {
for (auto j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), tiled);
}
}
}
} else {
auto hq = PainterHighQualityEnabler(p);
QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, bg.size(), to, from);
if (!gradient.isNull()) {
p.drawImage(to, gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
to.moveTop(to.top() + fromy);
p.drawPixmap(to, bg, from);
auto cached = controller->cachedBackground(fill);
if (!cached.pixmap.isNull()) {
p.drawPixmap(cached.x, fromy + cached.y, cached.pixmap);
return;
}
const auto gradient = background->gradientForFill();
const auto patternOpacity = background->paper().patternOpacity();
const auto &bg = background->pixmap();
if (!bg.isNull() && !background->tile()) {
auto hq = PainterHighQualityEnabler(p);
QRect to, from;
Window::Theme::ComputeBackgroundRects(fill, bg.size(), to, from);
if (!gradient.isNull()) {
p.drawImage(to, gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
to.moveTop(to.top() + fromy);
p.drawPixmap(to, bg, from);
return;
}
if (!gradient.isNull()) {
auto hq = PainterHighQualityEnabler(p);
p.drawImage(fill, gradient);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
if (!bg.isNull()) {
auto &tiled = background->pixmapForTiled();
auto left = clip.left();
auto top = clip.top();
auto right = clip.left() + clip.width();
auto bottom = clip.top() + clip.height();
auto w = tiled.width() / cRetinaFactor();
auto h = tiled.height() / cRetinaFactor();
auto sx = qFloor(left / w);
auto sy = qFloor((top - fromy) / h);
auto cx = qCeil(right / w);
auto cy = qCeil((bottom - fromy) / h);
for (auto i = sx; i < cx; ++i) {
for (auto j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), tiled);
}
}
} else {
p.drawPixmap(x, fromy + y, cached);
}
}

View file

@ -59,6 +59,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "support/support_helper.h"
#include "storage/file_upload.h"
#include "facades.h"
#include "window/themes/window_theme.h"
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
#include "styles/style_layers.h" // st::boxLabel
@ -68,6 +69,9 @@ namespace {
constexpr auto kMaxChatEntryHistorySize = 50;
// Cache background scaled image after 3s.
constexpr auto kCacheBackgroundTimeout = 3000;
} // namespace
void ActivateWindow(not_null<SessionController*> controller) {
@ -463,7 +467,8 @@ SessionController::SessionController(
std::make_unique<ChatHelpers::TabbedSelector>(
_window->widget(),
this))
, _invitePeekTimer([=] { checkInvitePeek(); }) {
, _invitePeekTimer([=] { checkInvitePeek(); })
, _cacheBackgroundTimer([=] { cacheBackground(); }) {
init();
if (Media::Player::instance()->pauseGifByRoundVideo()) {
@ -506,6 +511,15 @@ SessionController::SessionController(
}));
}, _lifetime);
using Update = Window::Theme::BackgroundUpdate;
Window::Theme::Background()->updates(
) | rpl::start_with_next([=](const Update &update) {
if (update.type == Update::Type::New
|| update.type == Update::Type::Changed) {
clearCachedBackground();
}
}, lifetime());
session->addWindow(this);
}
@ -1303,6 +1317,99 @@ void SessionController::openDocument(
session().data().message(contextId));
}
CachedBackground SessionController::cachedBackground(QRect area) {
if (!_cachedBackground.pixmap.isNull()
&& area == _cachedBackground.area) {
return _cachedBackground;
}
if (_willCacheForArea != area || !_cacheBackgroundTimer.isActive()) {
_willCacheForArea = area;
_cacheBackgroundTimer.callOnce(kCacheBackgroundTimeout);
}
return {};
}
void SessionController::cacheBackground() {
const auto background = Window::Theme::Background();
if (background->colorForFill()) {
return;
}
const auto gradient = background->gradientForFill();
const auto patternOpacity = background->paper().patternOpacity();
const auto &bg = background->pixmap();
if (background->tile() || bg.isNull()) {
auto result = gradient.isNull()
? QImage(
_willCacheForArea.size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied)
: gradient.scaled(
_willCacheForArea.size() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
if (!bg.isNull()) {
QPainter p(&result);
if (!gradient.isNull()) {
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
}
const auto &tiled = background->pixmapForTiled();
auto w = tiled.width() / cRetinaFactor();
auto h = tiled.height() / cRetinaFactor();
auto sx = 0;
auto sy = 0;
auto cx = qCeil(_willCacheForArea.width() / w);
auto cy = qCeil(_willCacheForArea.height() / h);
for (int i = sx; i < cx; ++i) {
for (int j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, j * h), tiled);
}
}
}
_cachedBackground = CachedBackground{
.pixmap = Ui::PixmapFromImage(std::move(result)),
.area = _willCacheForArea,
};
} else {
QRect to, from;
Window::Theme::ComputeBackgroundRects(
_willCacheForArea,
bg.size(),
to,
from);
auto image = bg.toImage().copy(from).scaled(
to.width() * cIntRetinaFactor(),
to.height() * cIntRetinaFactor(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
auto result = gradient.isNull()
? std::move(image)
: gradient.scaled(
image.size(),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(cRetinaFactor());
if (!gradient.isNull()) {
QPainter p(&result);
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
p.setOpacity(patternOpacity);
p.drawImage(QRect(QPoint(), to.size()), image);
}
image = QImage();
_cachedBackground = {
.pixmap = Ui::PixmapFromImage(std::move(result)),
.area = _willCacheForArea,
.x = to.x(),
.y = to.y(),
};
}
}
void SessionController::clearCachedBackground() {
_cachedBackground = {};
_cacheBackgroundTimer.cancel();
}
SessionController::~SessionController() {
resetFakeUnreadWhileOpened();
}

View file

@ -56,6 +56,13 @@ class SectionMemento;
class Controller;
class FiltersMenu;
struct CachedBackground {
QPixmap pixmap;
QRect area;
int x = 0;
int y = 0;
};
enum class GifPauseReason {
Any = 0,
InlineResults = (1 << 0),
@ -393,6 +400,8 @@ public:
void toggleFiltersMenu(bool enabled);
[[nodiscard]] rpl::producer<> filtersMenuChanged() const;
[[nodiscard]] CachedBackground cachedBackground(QRect area);
rpl::lifetime &lifetime() {
return _lifetime;
}
@ -422,6 +431,9 @@ private:
void checkInvitePeek();
void cacheBackground();
void clearCachedBackground();
const not_null<Controller*> _window;
std::unique_ptr<Passport::FormController> _passportForm;
@ -449,6 +461,10 @@ private:
rpl::event_stream<> _filtersMenuChanged;
CachedBackground _cachedBackground;
QRect _willCacheForArea;
base::Timer _cacheBackgroundTimer;
rpl::lifetime _lifetime;
};