ZaStoGram_desktop/Telegram/SourceFiles/ui/effects/star_burst.cpp
2026-10-08 22:24:48 +04:00

132 lines
3.8 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/star_burst.h"
#include "ui/effects/animation_value.h"
#include "ui/effects/drifting_particles.h"
#include <QtCore/QFile>
#include <QtCore/QtMath>
namespace Ui {
namespace {
[[nodiscard]] float64 Value(
ParticlesRandom &random,
const StarBurstRange &range) {
return random.value(range.from, range.till);
}
} // namespace
StarBurst::StarBurst(const StarBurstDescriptor &descriptor)
: _appearTill(descriptor.appearTill)
, _fadeAfter(descriptor.fadeAfter)
, _deformation(descriptor.deformation)
, _duration(descriptor.delay + descriptor.spread + descriptor.lifeMax) {
const auto path = u":/gui/icons/settings/starmini.svg"_q;
if (const auto color = descriptor.color) {
auto file = QFile(path);
if (file.open(QIODevice::ReadOnly)) {
_sprite.load(file.readAll().replace(
QByteArray("#fff"),
color->name().toUtf8()));
}
} else {
_sprite.load(path);
}
if (!_sprite.isValid()) {
return;
}
auto total = 0;
for (const auto &side : descriptor.sides) {
total += side.count;
}
auto random = ParticlesRandom();
_stars.reserve(total);
for (const auto &side : descriptor.sides) {
for (auto i = 0; i != side.count; ++i) {
_stars.push_back({
.birth = descriptor.delay
+ crl::time(random.value(0., descriptor.spread)),
.life = crl::time(random.value(
descriptor.lifeMin,
descriptor.lifeMax)),
.side = side.sign,
.angle = Value(random, side.angle),
.reach = Value(random, side.reach),
.fall = Value(random, descriptor.fall),
.start = QPointF(
side.sign * Value(random, descriptor.startX),
Value(random, descriptor.startY)),
.size = Value(random, descriptor.size),
.alpha = Value(random, descriptor.alpha),
.sinFactor = Value(random, descriptor.twinkle)
* (random.chance(2) ? -1. : 1.),
});
}
}
}
std::unique_ptr<StarBurst> StarBurst::Make(
const StarBurstDescriptor &descriptor) {
auto result = std::unique_ptr<StarBurst>(new StarBurst(descriptor));
return result->_sprite.isValid() ? std::move(result) : nullptr;
}
crl::time StarBurst::duration() const {
return _duration;
}
void StarBurst::paint(QPainter &p, const StarBurstFrame &frame) const {
const auto origin = frame.origin;
const auto emitter = frame.emitter;
const auto extent = frame.extent;
p.save();
if (!frame.clip.isEmpty()) {
p.setClipPath(frame.clip, Qt::IntersectClip);
}
const auto opacity = p.opacity();
for (const auto &star : _stars) {
const auto elapsed = frame.elapsed - star.birth;
if (elapsed < 0 || elapsed >= star.life) {
continue;
}
const auto progress = elapsed / float64(star.life);
const auto appear = std::clamp(progress / _appearTill, 0., 1.);
const auto fade = 1. - std::clamp(
(progress - _fadeAfter) / (1. - _fadeAfter),
0.,
1.);
const auto travel = anim::easeOutCubic(1., progress);
const auto radians = star.angle * M_PI / 180.;
const auto centre = origin
+ star.start * emitter
+ QPointF(star.side * std::cos(radians), std::sin(radians))
* (star.reach * extent * travel)
+ QPointF(0., star.fall * extent * progress * progress);
const auto deformH = 1. + _deformation
* std::sin(star.sinFactor * progress * 2. * M_PI);
const auto deformW = 1. / deformH;
const auto side = star.size * extent * appear;
const auto width = side * fade * deformW;
const auto height = side * deformH;
p.setOpacity(opacity * star.alpha * appear * fade);
_sprite.render(
&p,
QRectF(
centre.x() - width / 2.,
centre.y() - height / 2.,
width,
height));
}
p.setOpacity(opacity);
p.restore();
}
} // namespace Ui