/* 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/credits_graphics.h" #include #include "data/data_credits.h" #include "data/data_file_origin.h" #include "data/data_photo.h" #include "data/data_photo_media.h" #include "data/data_session.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/effects/premium_graphics.h" #include "ui/empty_userpic.h" #include "ui/painter.h" #include "ui/rect.h" #include "styles/style_credits.h" #include "styles/style_intro.h" // introFragmentIcon. #include "styles/style_settings.h" #include "styles/style_dialogs.h" #include namespace Ui { using PaintRoundImageCallback = Fn; QImage GenerateStars(int height, int count) { constexpr auto kOutlineWidth = .6; constexpr auto kStrokeWidth = 3; constexpr auto kShift = 3; auto colorized = qs(Ui::Premium::ColorizedSvg( Ui::Premium::CreditsIconGradientStops())); colorized.replace( u"stroke=\"none\""_q, u"stroke=\"%1\""_q.arg(st::creditsStroke->c.name())); colorized.replace( u"stroke-width=\"1\""_q, u"stroke-width=\"%1\""_q.arg(kStrokeWidth)); auto svg = QSvgRenderer(colorized.toUtf8()); svg.setViewBox(svg.viewBox() + Margins(kStrokeWidth)); const auto starSize = Size(height - kOutlineWidth * 2); auto frame = QImage( QSize( (height + kShift * (count - 1)) * style::DevicePixelRatio(), height * style::DevicePixelRatio()), QImage::Format_ARGB32_Premultiplied); frame.setDevicePixelRatio(style::DevicePixelRatio()); frame.fill(Qt::transparent); const auto drawSingle = [&](QPainter &q) { const auto s = kOutlineWidth; q.save(); q.translate(s, s); q.setCompositionMode(QPainter::CompositionMode_Clear); svg.render(&q, QRectF(QPointF(s, 0), starSize)); svg.render(&q, QRectF(QPointF(s, s), starSize)); svg.render(&q, QRectF(QPointF(0, s), starSize)); svg.render(&q, QRectF(QPointF(-s, s), starSize)); svg.render(&q, QRectF(QPointF(-s, 0), starSize)); svg.render(&q, QRectF(QPointF(-s, -s), starSize)); svg.render(&q, QRectF(QPointF(0, -s), starSize)); svg.render(&q, QRectF(QPointF(s, -s), starSize)); q.setCompositionMode(QPainter::CompositionMode_SourceOver); svg.render(&q, Rect(starSize)); q.restore(); }; { auto q = QPainter(&frame); q.translate(frame.width() / style::DevicePixelRatio() - height, 0); for (auto i = count; i > 0; --i) { drawSingle(q); q.translate(-kShift, 0); } } return frame; } PaintRoundImageCallback GenerateCreditsPaintUserpicCallback( const Data::CreditsHistoryEntry &entry) { const auto bg = [&]() -> Ui::EmptyUserpic::BgColors { switch (entry.peerType) { case Data::CreditsHistoryEntry::PeerType::Peer: return Ui::EmptyUserpic::UserpicColor(0); case Data::CreditsHistoryEntry::PeerType::AppStore: return { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 }; case Data::CreditsHistoryEntry::PeerType::PlayMarket: return { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 }; case Data::CreditsHistoryEntry::PeerType::Fragment: return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 }; case Data::CreditsHistoryEntry::PeerType::PremiumBot: return { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 }; case Data::CreditsHistoryEntry::PeerType::Unsupported: return { st::historyPeerArchiveUserpicBg, st::historyPeerArchiveUserpicBg, }; } Unexpected("Unknown peer type."); }(); const auto userpic = std::make_shared(bg, QString()); return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { userpic->paintCircle(p, x, y, outerWidth, size); using PeerType = Data::CreditsHistoryEntry::PeerType; if (entry.peerType == PeerType::PremiumBot) { return; } const auto rect = QRect(x, y, size, size); ((entry.peerType == PeerType::AppStore) ? st::sessionIconiPhone : (entry.peerType == PeerType::PlayMarket) ? st::sessionIconAndroid : (entry.peerType == PeerType::Fragment) ? st::introFragmentIcon : st::dialogsInaccessibleUserpic).paintInCenter(p, rect); }; } Fn GenerateCreditsPaintEntryCallback( not_null photo, Fn update) { struct State { std::shared_ptr view; Image *imagePtr = nullptr; QImage image; rpl::lifetime downloadLifetime; bool entryImageLoaded = false; }; const auto state = std::make_shared(); state->view = photo->createMediaView(); photo->load(Data::PhotoSize::Thumbnail, {}); rpl::single(rpl::empty_value()) | rpl::then( photo->owner().session().downloaderTaskFinished() ) | rpl::start_with_next([=] { using Size = Data::PhotoSize; if (const auto large = state->view->image(Size::Large)) { state->imagePtr = large; } else if (const auto small = state->view->image(Size::Small)) { state->imagePtr = small; } else if (const auto t = state->view->image(Size::Thumbnail)) { state->imagePtr = t; } update(); if (state->view->loaded()) { state->entryImageLoaded = true; state->downloadLifetime.destroy(); } }, state->downloadLifetime); return [=](Painter &p, int x, int y, int outerWidth, int size) { if (state->imagePtr && (!state->entryImageLoaded || state->image.isNull())) { const auto image = state->imagePtr->original(); const auto minSize = std::min(image.width(), image.height()); state->image = Images::Prepare( image.copy( (image.width() - minSize) / 2, (image.height() - minSize) / 2, minSize, minSize), size * style::DevicePixelRatio(), { .options = Images::Option::RoundCircle }); } p.drawImage(x, y, state->image); }; } TextWithEntities GenerateEntryName(const Data::CreditsHistoryEntry &entry) { return ((entry.peerType == Data::CreditsHistoryEntry::PeerType::Fragment) ? tr::lng_bot_username_description1_link : tr::lng_credits_summary_history_entry_inner_in)( tr::now, TextWithEntities::Simple); } } // namespace Ui