premium_stars.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "ui/effects/premium_stars.h"
  8. #include "base/random.h"
  9. #include "ui/effects/animation_value_f.h"
  10. #include <QtCore/QtMath>
  11. namespace Ui {
  12. namespace Premium {
  13. constexpr auto kDeformationMax = 0.1;
  14. MiniStars::MiniStars(
  15. Fn<void(const QRect &r)> updateCallback,
  16. bool opaque,
  17. Type type)
  18. : _availableAngles((type != Type::SlowStars)
  19. ? std::vector<Interval>{
  20. Interval{ -10, 40 },
  21. Interval{ 180 + 10 - 40, 40 },
  22. Interval{ 180 + 15, 50 },
  23. Interval{ -15 - 50, 50 },
  24. }
  25. : std::vector<Interval>{ Interval{ -90, 180 }, Interval{ 90, 180 } })
  26. , _lifeLength((type != Type::SlowStars)
  27. ? Interval{ 150 / 5, 200 / 5 }
  28. : Interval{ 150 * 2, 200 * 2 })
  29. , _deathTime((type != Type::SlowStars)
  30. ? Interval{ 1500, 2000 }
  31. : Interval{ 1500 * 2, 2000 * 2 })
  32. , _size({ 5, 10 })
  33. , _alpha({ opaque ? 100 : 40, opaque ? 100 : 60 })
  34. , _sinFactor({ 10, 190 })
  35. , _spritesCount({ 0, ((type == Type::MonoStars) ? 1 : 2) })
  36. , _appearProgressTill((type != Type::SlowStars) ? 0.2 : 0.01)
  37. , _disappearProgressAfter(0.8)
  38. , _distanceProgressStart(0.5)
  39. , _sprite(u":/gui/icons/settings/starmini.svg"_q)
  40. , _animation([=](crl::time now) {
  41. if (now > _nextBirthTime && !_paused) {
  42. createStar(now);
  43. }
  44. if (_rectToUpdate.isValid()) {
  45. updateCallback(base::take(_rectToUpdate));
  46. }
  47. }) {
  48. if (type == Type::BiStars) {
  49. _secondSprite = std::make_unique<QSvgRenderer>(
  50. u":/gui/icons/settings/star.svg"_q);
  51. }
  52. if (anim::Disabled()) {
  53. const auto from = _deathTime.from + _deathTime.length;
  54. auto r = bytes::vector(from + 1);
  55. base::RandomFill(r.data(), r.size());
  56. for (auto i = -from; i < 0; i += randomInterval(_lifeLength, r[-i])) {
  57. createStar(i);
  58. }
  59. updateCallback(_rectToUpdate);
  60. } else {
  61. _animation.start();
  62. }
  63. }
  64. int MiniStars::randomInterval(
  65. const Interval &interval,
  66. const bytes::type &random) const {
  67. return interval.from + (uchar(random) % interval.length);
  68. }
  69. crl::time MiniStars::timeNow() const {
  70. return anim::Disabled() ? 0 : crl::now();
  71. }
  72. void MiniStars::paint(QPainter &p, const QRectF &rect) {
  73. const auto center = rect.center();
  74. const auto opacity = p.opacity();
  75. const auto now = timeNow();
  76. for (const auto &ministar : _ministars) {
  77. const auto progress = (now - ministar.birthTime)
  78. / float64(ministar.deathTime - ministar.birthTime);
  79. if (progress > 1.) {
  80. continue;
  81. }
  82. const auto appearProgress = std::clamp(
  83. progress / _appearProgressTill,
  84. 0.,
  85. 1.);
  86. const auto rsin = float(std::sin(ministar.angle * M_PI / 180.));
  87. const auto rcos = float(std::cos(ministar.angle * M_PI / 180.));
  88. const auto end = QPointF(
  89. rect.width() / kSizeFactor * rcos,
  90. rect.height() / kSizeFactor * rsin);
  91. const auto alphaProgress = 1.
  92. - (std::clamp(progress - _disappearProgressAfter, 0., 1.)
  93. / (1. - _disappearProgressAfter));
  94. p.setOpacity(ministar.alpha
  95. * alphaProgress
  96. * appearProgress
  97. * opacity);
  98. const auto deformResult = progress * 360;
  99. const auto rsinDeform = float(
  100. std::sin(ministar.sinFactor * deformResult * M_PI / 180.));
  101. const auto deformH = 1. + kDeformationMax * rsinDeform;
  102. const auto deformW = 1. / deformH;
  103. const auto distanceProgress = _distanceProgressStart + progress;
  104. const auto starSide = ministar.size * appearProgress;
  105. const auto widthFade = (std::abs(rcos) >= std::abs(rsin));
  106. const auto starWidth = starSide
  107. * (widthFade ? alphaProgress : 1.)
  108. * deformW;
  109. const auto starHeight = starSide
  110. * (!widthFade ? alphaProgress : 1.)
  111. * deformH;
  112. const auto renderRect = QRectF(
  113. center.x()
  114. + anim::interpolateF(0, end.x(), distanceProgress)
  115. - starWidth / 2.,
  116. center.y()
  117. + anim::interpolateF(0, end.y(), distanceProgress)
  118. - starHeight / 2.,
  119. starWidth,
  120. starHeight);
  121. ministar.sprite->render(&p, renderRect);
  122. _rectToUpdate |= renderRect.toRect();
  123. }
  124. p.setOpacity(opacity);
  125. }
  126. void MiniStars::setPaused(bool paused) {
  127. _paused = paused;
  128. }
  129. void MiniStars::createStar(crl::time now) {
  130. constexpr auto kRandomSize = 9;
  131. auto random = bytes::vector(kRandomSize);
  132. base::RandomFill(random.data(), random.size());
  133. auto i = 0;
  134. auto next = [&] { return random[i++]; };
  135. _nextBirthTime = now + randomInterval(_lifeLength, next());
  136. const auto &angleInterval = _availableAngles[
  137. uchar(next()) % _availableAngles.size()];
  138. auto ministar = MiniStar{
  139. .birthTime = now,
  140. .deathTime = now + randomInterval(_deathTime, next()),
  141. .angle = randomInterval(angleInterval, next()),
  142. .size = float64(randomInterval(_size, next())),
  143. .alpha = float64(randomInterval(_alpha, next())) / 100.,
  144. .sinFactor = randomInterval(_sinFactor, next()) / 100.
  145. * ((uchar(next()) % 2) == 1 ? 1. : -1.),
  146. .sprite = ((randomInterval(_spritesCount, next()) && _secondSprite)
  147. ? _secondSprite.get()
  148. : &_sprite),
  149. };
  150. for (auto i = 0; i < _ministars.size(); i++) {
  151. if (ministar.birthTime > _ministars[i].deathTime) {
  152. _ministars[i] = ministar;
  153. return;
  154. }
  155. }
  156. _ministars.push_back(ministar);
  157. }
  158. } // namespace Premium
  159. } // namespace Ui