tdesktop/Telegram/SourceFiles/ui/effects/premium_stars.cpp

153 lines
4.2 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/effects/premium_stars.h"
#include "base/random.h"
#include "ui/effects/animation_value_f.h"
#include <QtCore/QtMath>
namespace Ui {
namespace Premium {
constexpr auto kDeformationMax = 0.1;
MiniStars::MiniStars(Fn<void(const QRect &r)> updateCallback, bool opaque)
: _availableAngles({
Interval{ -10, 40 },
Interval{ 180 + 10 - 40, 40 },
Interval{ 180 + 15, 50 },
Interval{ -15 - 50, 50 },
})
, _lifeLength({ 150 / 5, 200 / 5 })
, _deathTime({ 1500, 2000 })
, _size({ 5, 10 })
, _alpha({ opaque ? 100 : 40, opaque ? 100 : 60 })
, _sinFactor({ 10, 190 })
, _appearProgressTill(0.2)
, _disappearProgressAfter(0.8)
, _distanceProgressStart(0.5)
, _sprite(u":/gui/icons/settings/starmini.svg"_q)
, _animation([=](crl::time now) {
if (now > _nextBirthTime && !_paused) {
createStar(now);
_nextBirthTime = now + randomInterval(_lifeLength);
}
if (_rectToUpdate.isValid()) {
updateCallback(base::take(_rectToUpdate));
}
}) {
if (anim::Disabled()) {
const auto from = _deathTime.from + _deathTime.length;
for (auto i = -from; i < 0; i += randomInterval(_lifeLength)) {
createStar(i);
}
updateCallback(_rectToUpdate);
} else {
_animation.start();
}
}
int MiniStars::randomInterval(const Interval &interval) const {
return interval.from + base::RandomIndex(interval.length);
}
crl::time MiniStars::timeNow() const {
return anim::Disabled() ? 0 : crl::now();
}
void MiniStars::paint(QPainter &p, const QRectF &rect) {
const auto center = rect.center();
const auto opacity = p.opacity();
for (const auto &ministar : _ministars) {
const auto progress = (timeNow() - ministar.birthTime)
/ float64(ministar.deathTime - ministar.birthTime);
if (progress > 1.) {
continue;
}
const auto appearProgress = std::clamp(
progress / _appearProgressTill,
0.,
1.);
const auto rsin = float(std::sin(ministar.angle * M_PI / 180.));
const auto rcos = float(std::cos(ministar.angle * M_PI / 180.));
const auto end = QPointF(
rect.width() / kSizeFactor * rcos,
rect.height() / kSizeFactor * rsin);
const auto alphaProgress = 1.
- (std::clamp(progress - _disappearProgressAfter, 0., 1.)
/ (1. - _disappearProgressAfter));
p.setOpacity(ministar.alpha
* alphaProgress
* appearProgress
* opacity);
const auto deformResult = progress * 360;
const auto rsinDeform = float(
std::sin(ministar.sinFactor * deformResult * M_PI / 180.));
const auto deformH = 1. + kDeformationMax * rsinDeform;
const auto deformW = 1. / deformH;
const auto distanceProgress = _distanceProgressStart + progress;
const auto starSide = ministar.size * appearProgress;
const auto widthFade = (std::abs(rcos) >= std::abs(rsin));
const auto starWidth = starSide
* (widthFade ? alphaProgress : 1.)
* deformW;
const auto starHeight = starSide
* (!widthFade ? alphaProgress : 1.)
* deformH;
const auto renderRect = QRectF(
center.x()
+ anim::interpolateF(0, end.x(), distanceProgress)
- starWidth / 2.,
center.y()
+ anim::interpolateF(0, end.y(), distanceProgress)
- starHeight / 2.,
starWidth,
starHeight);
_sprite.render(&p, renderRect);
_rectToUpdate |= renderRect.toRect();
}
p.setOpacity(opacity);
}
void MiniStars::setPaused(bool paused) {
_paused = paused;
}
int MiniStars::angle() const {
const auto &interval = _availableAngles[
base::RandomIndex(_availableAngles.size())];
return base::RandomIndex(interval.length) + interval.from;
}
void MiniStars::createStar(crl::time now) {
auto ministar = MiniStar{
.birthTime = now,
.deathTime = now + randomInterval(_deathTime),
.angle = angle(),
.size = float64(randomInterval(_size)),
.alpha = float64(randomInterval(_alpha)) / 100.,
.sinFactor = randomInterval(_sinFactor) / 100.
* (base::RandomIndex(2) == 1 ? 1. : -1.),
};
for (auto i = 0; i < _ministars.size(); i++) {
if (ministar.birthTime > _ministars[i].deathTime) {
_ministars[i] = ministar;
return;
}
}
_ministars.push_back(ministar);
}
} // namespace Premium
} // namespace Ui