From 6bd41a07a1fdf726e9ed9809da120cc3ea473aee Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 6 Nov 2025 12:45:55 +0300 Subject: [PATCH] Added dedicated class for ministar particles effect. --- .../ui/effects/ministar_particles.cpp | 163 ++++++++++++++++++ .../ui/effects/ministar_particles.h | 56 ++++++ Telegram/cmake/td_ui.cmake | 2 + 3 files changed, 221 insertions(+) create mode 100644 Telegram/SourceFiles/ui/effects/ministar_particles.cpp create mode 100644 Telegram/SourceFiles/ui/effects/ministar_particles.h diff --git a/Telegram/SourceFiles/ui/effects/ministar_particles.cpp b/Telegram/SourceFiles/ui/effects/ministar_particles.cpp new file mode 100644 index 0000000000..3226789212 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/ministar_particles.cpp @@ -0,0 +1,163 @@ +/* +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/ministar_particles.h" + +#include "ui/painter.h" +#include "ui/color_int_conversion.h" +#include "base/random.h" + +#include + +namespace Ui { + +StarParticles::StarParticles(Type type, int count, int size) +: _type(type) +, _count(count) +, _starSize(size) { + generate(); +} + +void StarParticles::setSpeed(float speed) { + _speed = speed; +} + +void StarParticles::setVisible(float visible) { + _visible = visible; +} + +void StarParticles::setColor(const QColor &color) { + _color = color; +} + +QImage StarParticles::generateStarCache(int size, QColor color) { + const auto ratio = style::DevicePixelRatio(); + auto image = QImage( + size * ratio, + size * ratio, + QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(ratio); + image.fill(Qt::transparent); + + auto p = QPainter(&image); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(color); + p.setPen(Qt::NoPen); + + auto star = QPainterPath(); + const auto center = size / 2.; + const auto outerRadius = size / 2.; + const auto innerRadius = size / 5.; + const auto points = 4; + + for (auto i = 0; i < points * 2; ++i) { + const auto angle = M_PI * i / points - M_PI / 2; + const auto radius = (i % 2 == 0) ? outerRadius : innerRadius; + const auto x = center + radius * std::cos(angle); + const auto y = center + radius * std::sin(angle); + if (i == 0) { + star.moveTo(x, y); + } else { + star.lineTo(x, y); + } + } + star.closeSubpath(); + p.drawPath(star); + + return image; +} + +void StarParticles::generate() { + _particles.clear(); + _particles.reserve(_count); + + constexpr auto kRandomPerParticle = 12; + auto random = bytes::vector(_count * kRandomPerParticle); + base::RandomFill(random.data(), random.size()); + auto idx = 0; + const auto next = [&] { return uchar(random[idx++]) / 255.; }; + + for (auto i = 0; i < _count; ++i) { + auto p = Particle(); + p.x = next(); + p.y = next(); + p.size = 0.3 + next() * 0.7; + p.rotation = next() * 360.; + p.vrotation = (next() - 0.5) * 180.; + p.alpha = 0.3 + next() * 0.7; + + if (_type == Type::Right) { + p.vx = 0.5 + next() * 1.5; + p.vy = (next() - 0.5) * 1.5; + } else if (_type == Type::Radial) { + const auto angle = next() * 2 * M_PI; + const auto speed = 0.5 + next(); + p.vx = std::cos(angle) * speed; + p.vy = std::sin(angle) * speed; + } else { + const auto angle = next() * 2 * M_PI; + const auto speed = 0.3 + next() * 0.5; + p.vx = -std::cos(angle) * speed; + p.vy = -std::sin(angle) * speed; + } + + _particles.push_back(p); + } +} + +void StarParticles::paint(QPainter &p, const QRect &rect, crl::time now) { + if (_lastTime == 0) { + _lastTime = now; + return; + } + + const auto colorKey = _color.rgba(); + const auto i = _starCache.find(colorKey); + const auto &cache = (i != end(_starCache)) + ? i->second + : _starCache.emplace( + colorKey, + generateStarCache(_starSize, _color)).first->second; + + const auto dt = (now - _lastTime) / 1000.; + _lastTime = now; + + const auto visibleCount = int(_count * _visible); + for (auto i = 0; i < visibleCount; ++i) { + auto &particle = _particles[i]; + + particle.x += particle.vx * _speed * dt; + particle.y += particle.vy * _speed * dt; + particle.rotation += particle.vrotation * dt; + + if (particle.x < -0.1 || particle.x > 1.1 + || particle.y < -0.1 || particle.y > 1.1) { + auto random = bytes::vector(1); + base::RandomFill(random.data(), random.size()); + particle.x = (_type == Type::Right) ? 0. : 0.5; + particle.y = (_type == Type::Right) + ? (uchar(random[0]) / 255.) + : 0.5; + } + + const auto x = rect.x() + particle.x * rect.width(); + const auto y = rect.y() + particle.y * rect.height(); + const auto size = cache.width() + / cache.devicePixelRatio() * particle.size; + + p.save(); + p.setOpacity(particle.alpha); + p.translate(x, y); + p.rotate(particle.rotation); + p.drawImage( + QRectF(-size / 2, -size / 2, size, size), + cache); + p.restore(); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/ministar_particles.h b/Telegram/SourceFiles/ui/effects/ministar_particles.h new file mode 100644 index 0000000000..5b667d3534 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/ministar_particles.h @@ -0,0 +1,56 @@ +/* +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 +*/ +#pragma once + +#include "ui/rp_widget.h" + +namespace Ui { + +class StarParticles final { +public: + enum class Type { + Right, + Radial, + RadialInside, + }; + + StarParticles(Type type, int count, int size); + + void setSpeed(float speed); + void setVisible(float visible); + void setColor(const QColor &color); + void paint(QPainter &p, const QRect &rect, crl::time now); + +private: + struct Particle { + float x = 0.; + float y = 0.; + float vx = 0.; + float vy = 0.; + float rotation = 0.; + float vrotation = 0.; + float size = 0.; + float alpha = 0.; + crl::time born = 0; + }; + + void generate(); + [[nodiscard]] QImage generateStarCache(int size, QColor color); + + Type _type; + int _count = 0; + int _starSize = 0; + float _speed = 1.f; + float _visible = 1.f; + QColor _color = QColor(255, 200, 70); + std::vector _particles; + base::flat_map _starCache; + crl::time _lastTime = 0; +}; + +} // namespace Ui diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index dba9465a1f..6adf58bd83 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -432,6 +432,8 @@ PRIVATE ui/effects/glare.h ui/effects/loading_element.cpp ui/effects/loading_element.h + ui/effects/ministar_particles.cpp + ui/effects/ministar_particles.h ui/effects/outline_segments.cpp ui/effects/outline_segments.h ui/effects/premium_bubble.cpp