mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Optimize bubble gradient background painting.
This commit is contained in:
parent
559d4cf4da
commit
ab6f5ae2ac
3 changed files with 135 additions and 36 deletions
|
@ -431,17 +431,20 @@ ChatPaintContext ChatTheme::preparePaintContext(
|
||||||
not_null<const ChatStyle*> st,
|
not_null<const ChatStyle*> st,
|
||||||
QRect viewport,
|
QRect viewport,
|
||||||
QRect clip) {
|
QRect clip) {
|
||||||
_bubblesBackground.area = viewport.size();
|
const auto area = viewport.size();
|
||||||
//if (!_bubblesBackgroundPrepared.isNull()
|
if (!_bubblesBackgroundPrepared.isNull()
|
||||||
// && _bubblesBackground.area != viewport.size()
|
&& _bubblesBackground.area != area) {
|
||||||
// && !viewport.isEmpty()) {
|
if (!_cacheBubblesTimer) {
|
||||||
// // #TODO bubbles delayed caching
|
_cacheBubblesTimer.emplace([=] { cacheBubbles(); });
|
||||||
// _bubblesBackground = CacheBackground({
|
}
|
||||||
// .prepared = _bubblesBackgroundPrepared,
|
if (_cacheBubblesArea != area
|
||||||
// .area = viewport.size(),
|
|| (!_cacheBubblesTimer->isActive()
|
||||||
// });
|
&& !_bubblesCachingRequest)) {
|
||||||
// _bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
_cacheBubblesArea = area;
|
||||||
//}
|
_lastBubblesAreaChangeTime = crl::now();
|
||||||
|
_cacheBubblesTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
.st = st,
|
.st = st,
|
||||||
.bubblesPattern = _bubblesBackgroundPattern.get(),
|
.bubblesPattern = _bubblesBackgroundPattern.get(),
|
||||||
|
@ -460,15 +463,15 @@ const BackgroundState &ChatTheme::backgroundState(QSize area) {
|
||||||
&& !background().gradientForFill.isNull()) {
|
&& !background().gradientForFill.isNull()) {
|
||||||
// We don't support direct painting of patterned gradients.
|
// We don't support direct painting of patterned gradients.
|
||||||
// So we need to sync-generate cache image here.
|
// So we need to sync-generate cache image here.
|
||||||
_willCacheForArea = area;
|
_cacheBackgroundArea = area;
|
||||||
setCachedBackground(CacheBackground(currentCacheRequest(area)));
|
setCachedBackground(CacheBackground(cacheBackgroundRequest(area)));
|
||||||
_cacheBackgroundTimer->cancel();
|
_cacheBackgroundTimer->cancel();
|
||||||
} else if (_backgroundState.now.area != area) {
|
} else if (_backgroundState.now.area != area) {
|
||||||
if (_willCacheForArea != area
|
if (_cacheBackgroundArea != area
|
||||||
|| (!_cacheBackgroundTimer->isActive()
|
|| (!_cacheBackgroundTimer->isActive()
|
||||||
&& !_backgroundCachingRequest)) {
|
&& !_backgroundCachingRequest)) {
|
||||||
_willCacheForArea = area;
|
_cacheBackgroundArea = area;
|
||||||
_lastAreaChangeTime = crl::now();
|
_lastBackgroundAreaChangeTime = crl::now();
|
||||||
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -495,7 +498,7 @@ void ChatTheme::generateNextBackgroundRotation() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
constexpr auto kAddRotationDoubled = (720 - 45);
|
constexpr auto kAddRotationDoubled = (720 - 45);
|
||||||
const auto request = currentCacheRequest(
|
const auto request = cacheBackgroundRequest(
|
||||||
_backgroundState.now.area,
|
_backgroundState.now.area,
|
||||||
kAddRotationDoubled);
|
kAddRotationDoubled);
|
||||||
if (!request) {
|
if (!request) {
|
||||||
|
@ -506,7 +509,7 @@ void ChatTheme::generateNextBackgroundRotation() {
|
||||||
if (!readyForBackgroundRotation()) {
|
if (!readyForBackgroundRotation()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto request = currentCacheRequest(
|
const auto request = cacheBackgroundRequest(
|
||||||
_backgroundState.now.area,
|
_backgroundState.now.area,
|
||||||
kAddRotationDoubled);
|
kAddRotationDoubled);
|
||||||
if (forRequest == request) {
|
if (forRequest == request) {
|
||||||
|
@ -518,7 +521,7 @@ void ChatTheme::generateNextBackgroundRotation() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ChatTheme::currentCacheRequest(QSize area, int addRotation) const
|
auto ChatTheme::cacheBackgroundRequest(QSize area, int addRotation) const
|
||||||
-> CacheBackgroundRequest {
|
-> CacheBackgroundRequest {
|
||||||
if (background().colorForFill) {
|
if (background().colorForFill) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -527,7 +530,6 @@ auto ChatTheme::currentCacheRequest(QSize area, int addRotation) const
|
||||||
.background = background(),
|
.background = background(),
|
||||||
.area = area,
|
.area = area,
|
||||||
.gradientRotationAdd = addRotation,
|
.gradientRotationAdd = addRotation,
|
||||||
// .recreateGradient = (addRotation != 0),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +537,7 @@ void ChatTheme::cacheBackground() {
|
||||||
Expects(_cacheBackgroundTimer.has_value());
|
Expects(_cacheBackgroundTimer.has_value());
|
||||||
|
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
if (now - _lastAreaChangeTime < kCacheBackgroundTimeout
|
if (now - _lastBackgroundAreaChangeTime < kCacheBackgroundTimeout
|
||||||
&& QGuiApplication::mouseButtons() != 0) {
|
&& QGuiApplication::mouseButtons() != 0) {
|
||||||
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||||
return;
|
return;
|
||||||
|
@ -545,7 +547,8 @@ void ChatTheme::cacheBackground() {
|
||||||
|
|
||||||
void ChatTheme::cacheBackgroundNow() {
|
void ChatTheme::cacheBackgroundNow() {
|
||||||
if (!_backgroundCachingRequest) {
|
if (!_backgroundCachingRequest) {
|
||||||
if (const auto request = currentCacheRequest(_willCacheForArea)) {
|
if (const auto request = cacheBackgroundRequest(
|
||||||
|
_cacheBackgroundArea)) {
|
||||||
cacheBackgroundAsync(request);
|
cacheBackgroundAsync(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,8 +566,8 @@ void ChatTheme::cacheBackgroundAsync(
|
||||||
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
|
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
|
||||||
if (done) {
|
if (done) {
|
||||||
done(std::move(result));
|
done(std::move(result));
|
||||||
} else if (const auto request = currentCacheRequest(
|
} else if (const auto request = cacheBackgroundRequest(
|
||||||
_willCacheForArea)) {
|
_cacheBackgroundArea)) {
|
||||||
if (_backgroundCachingRequest != request) {
|
if (_backgroundCachingRequest != request) {
|
||||||
cacheBackgroundAsync(request);
|
cacheBackgroundAsync(request);
|
||||||
} else {
|
} else {
|
||||||
|
@ -605,6 +608,64 @@ void ChatTheme::setCachedBackground(CacheBackgroundResult &&cached) {
|
||||||
kBackgroundFadeDuration);
|
kBackgroundFadeDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ChatTheme::cacheBubblesRequest(QSize area) const
|
||||||
|
-> CacheBackgroundRequest {
|
||||||
|
if (_bubblesBackgroundPrepared.isNull()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.background = {
|
||||||
|
.gradientForFill = _bubblesBackgroundPrepared,
|
||||||
|
},
|
||||||
|
.area = area,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTheme::cacheBubbles() {
|
||||||
|
Expects(_cacheBubblesTimer.has_value());
|
||||||
|
|
||||||
|
const auto now = crl::now();
|
||||||
|
if (now - _lastBubblesAreaChangeTime < kCacheBackgroundTimeout
|
||||||
|
&& QGuiApplication::mouseButtons() != 0) {
|
||||||
|
_cacheBubblesTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheBubblesNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTheme::cacheBubblesNow() {
|
||||||
|
if (!_bubblesCachingRequest) {
|
||||||
|
if (const auto request = cacheBackgroundRequest(
|
||||||
|
_cacheBubblesArea)) {
|
||||||
|
cacheBubblesAsync(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatTheme::cacheBubblesAsync(
|
||||||
|
const CacheBackgroundRequest &request) {
|
||||||
|
_bubblesCachingRequest = request;
|
||||||
|
const auto weak = base::make_weak(this);
|
||||||
|
crl::async([=] {
|
||||||
|
if (!weak) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
|
||||||
|
if (const auto request = cacheBubblesRequest(
|
||||||
|
_cacheBubblesArea)) {
|
||||||
|
if (_bubblesCachingRequest != request) {
|
||||||
|
cacheBubblesAsync(request);
|
||||||
|
} else {
|
||||||
|
_bubblesCachingRequest = {};
|
||||||
|
_bubblesBackground = std::move(result);
|
||||||
|
_bubblesBackgroundPattern->pixmap
|
||||||
|
= _bubblesBackground.pixmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<> ChatTheme::repaintBackgroundRequests() const {
|
rpl::producer<> ChatTheme::repaintBackgroundRequests() const {
|
||||||
return _repaintBackgroundRequests.events();
|
return _repaintBackgroundRequests.events();
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,12 +147,20 @@ private:
|
||||||
const CacheBackgroundRequest &request,
|
const CacheBackgroundRequest &request,
|
||||||
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
||||||
void setCachedBackground(CacheBackgroundResult &&cached);
|
void setCachedBackground(CacheBackgroundResult &&cached);
|
||||||
[[nodiscard]] CacheBackgroundRequest currentCacheRequest(
|
[[nodiscard]] CacheBackgroundRequest cacheBackgroundRequest(
|
||||||
QSize area,
|
QSize area,
|
||||||
int addRotation = 0) const;
|
int addRotation = 0) const;
|
||||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||||
void generateNextBackgroundRotation();
|
void generateNextBackgroundRotation();
|
||||||
|
|
||||||
|
void cacheBubbles();
|
||||||
|
void cacheBubblesNow();
|
||||||
|
void cacheBubblesAsync(
|
||||||
|
const CacheBackgroundRequest &request);
|
||||||
|
void setCachedBubbles(CacheBackgroundResult &&cached);
|
||||||
|
[[nodiscard]] CacheBackgroundRequest cacheBubblesRequest(
|
||||||
|
QSize area) const;
|
||||||
|
|
||||||
[[nodiscard]] style::colorizer bubblesAccentColorizer(
|
[[nodiscard]] style::colorizer bubblesAccentColorizer(
|
||||||
const QColor &accent) const;
|
const QColor &accent) const;
|
||||||
void adjustPalette(const ChatThemeDescriptor &descriptor);
|
void adjustPalette(const ChatThemeDescriptor &descriptor);
|
||||||
|
@ -167,11 +175,16 @@ private:
|
||||||
Animations::Simple _backgroundFade;
|
Animations::Simple _backgroundFade;
|
||||||
CacheBackgroundRequest _backgroundCachingRequest;
|
CacheBackgroundRequest _backgroundCachingRequest;
|
||||||
CacheBackgroundResult _backgroundNext;
|
CacheBackgroundResult _backgroundNext;
|
||||||
QSize _willCacheForArea;
|
QSize _cacheBackgroundArea;
|
||||||
crl::time _lastAreaChangeTime = 0;
|
crl::time _lastBackgroundAreaChangeTime = 0;
|
||||||
std::optional<base::Timer> _cacheBackgroundTimer;
|
std::optional<base::Timer> _cacheBackgroundTimer;
|
||||||
|
|
||||||
CachedBackground _bubblesBackground;
|
CachedBackground _bubblesBackground;
|
||||||
QImage _bubblesBackgroundPrepared;
|
QImage _bubblesBackgroundPrepared;
|
||||||
|
CacheBackgroundRequest _bubblesCachingRequest;
|
||||||
|
QSize _cacheBubblesArea;
|
||||||
|
crl::time _lastBubblesAreaChangeTime = 0;
|
||||||
|
std::optional<base::Timer> _cacheBubblesTimer;
|
||||||
std::unique_ptr<BubblePattern> _bubblesBackgroundPattern;
|
std::unique_ptr<BubblePattern> _bubblesBackgroundPattern;
|
||||||
|
|
||||||
rpl::event_stream<> _repaintBackgroundRequests;
|
rpl::event_stream<> _repaintBackgroundRequests;
|
||||||
|
|
|
@ -93,13 +93,11 @@ void PaintPatternBubble(Painter &p, const SimpleBubble &args) {
|
||||||
const auto fillBg = [&](const QRect &rect) {
|
const auto fillBg = [&](const QRect &rect) {
|
||||||
const auto fill = rect.intersected(args.patternViewport);
|
const auto fill = rect.intersected(args.patternViewport);
|
||||||
if (!fill.isEmpty()) {
|
if (!fill.isEmpty()) {
|
||||||
p.setClipRect(fill);
|
|
||||||
PaintPatternBubblePart(
|
PaintPatternBubblePart(
|
||||||
p,
|
p,
|
||||||
args.patternViewport,
|
args.patternViewport,
|
||||||
pattern->pixmap,
|
pattern->pixmap,
|
||||||
fill);
|
fill);
|
||||||
p.setClipping(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const auto fillSh = [&](const QRect &rect) {
|
const auto fillSh = [&](const QRect &rect) {
|
||||||
|
@ -346,10 +344,30 @@ void PaintPatternBubblePart(
|
||||||
const QRect &viewport,
|
const QRect &viewport,
|
||||||
const QPixmap &pixmap,
|
const QPixmap &pixmap,
|
||||||
const QRect &target) {
|
const QRect &target) {
|
||||||
// #TODO bubbles optimizes
|
const auto factor = pixmap.devicePixelRatio();
|
||||||
const auto to = viewport;
|
if (viewport.size() * factor == pixmap.size()) {
|
||||||
const auto from = QRect(QPoint(), pixmap.size());
|
const auto fill = target.intersected(viewport);
|
||||||
p.drawPixmap(to, pixmap, from);
|
if (fill.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.drawPixmap(fill, pixmap, QRect(
|
||||||
|
(fill.topLeft() - viewport.topLeft()) * factor,
|
||||||
|
fill.size() * factor));
|
||||||
|
} else {
|
||||||
|
const auto to = viewport;
|
||||||
|
const auto from = QRect(QPoint(), pixmap.size());
|
||||||
|
const auto deviceRect = QRect(
|
||||||
|
QPoint(),
|
||||||
|
QSize(p.device()->width(), p.device()->height()));
|
||||||
|
const auto clip = (target != deviceRect);
|
||||||
|
if (clip) {
|
||||||
|
p.setClipRect(target);
|
||||||
|
}
|
||||||
|
p.drawPixmap(to, pixmap, from);
|
||||||
|
if (clip) {
|
||||||
|
p.setClipping(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaintPatternBubblePart(
|
void PaintPatternBubblePart(
|
||||||
|
@ -392,7 +410,9 @@ void PaintPatternBubblePart(
|
||||||
QImage &cache) {
|
QImage &cache) {
|
||||||
Expects(paintContent != nullptr);
|
Expects(paintContent != nullptr);
|
||||||
|
|
||||||
if (cache.size() != target.size() * style::DevicePixelRatio()) {
|
const auto targetOrigin = target.topLeft();
|
||||||
|
const auto targetSize = target.size();
|
||||||
|
if (cache.size() != targetSize * style::DevicePixelRatio()) {
|
||||||
cache = QImage(
|
cache = QImage(
|
||||||
target.size() * style::DevicePixelRatio(),
|
target.size() * style::DevicePixelRatio(),
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
@ -400,10 +420,15 @@ void PaintPatternBubblePart(
|
||||||
}
|
}
|
||||||
cache.fill(Qt::transparent);
|
cache.fill(Qt::transparent);
|
||||||
auto q = Painter(&cache);
|
auto q = Painter(&cache);
|
||||||
q.translate(-target.topLeft());
|
q.translate(-targetOrigin);
|
||||||
paintContent(q);
|
paintContent(q);
|
||||||
|
q.translate(targetOrigin);
|
||||||
q.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
q.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
PaintPatternBubblePart(q, viewport, pixmap, target);
|
PaintPatternBubblePart(
|
||||||
|
q,
|
||||||
|
viewport.translated(-targetOrigin),
|
||||||
|
pixmap,
|
||||||
|
QRect(QPoint(), targetSize));
|
||||||
q.end();
|
q.end();
|
||||||
|
|
||||||
p.drawImage(target, cache);
|
p.drawImage(target, cache);
|
||||||
|
|
Loading…
Add table
Reference in a new issue