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