Support adaptive forum userpic rounding radius.

This commit is contained in:
John Preston 2022-12-05 16:18:10 +04:00
parent 2407ac50bc
commit cb653df0f6
100 changed files with 662 additions and 724 deletions

View file

@ -171,6 +171,11 @@ void CheckChatInvite(
} // namespace Api } // namespace Api
struct ConfirmInviteBox::Participant {
not_null<UserData*> user;
Ui::PeerUserpicView userpic;
};
ConfirmInviteBox::ConfirmInviteBox( ConfirmInviteBox::ConfirmInviteBox(
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -356,7 +361,7 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
{ .options = Images::Option::RoundCircle })); { .options = Images::Option::RoundCircle }));
} }
} else if (_photoEmpty) { } else if (_photoEmpty) {
_photoEmpty->paint( _photoEmpty->paintCircle(
p, p,
(width() - st::confirmInvitePhotoSize) / 2, (width() - st::confirmInvitePhotoSize) / 2,
st::confirmInvitePhotoTop, st::confirmInvitePhotoTop,

View file

@ -21,7 +21,6 @@ class SessionController;
} // namespace Window } // namespace Window
namespace Data { namespace Data {
class CloudImageView;
class PhotoMedia; class PhotoMedia;
} // namespace Data } // namespace Data
@ -55,10 +54,7 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
struct Participant { struct Participant;
not_null<UserData*> user;
std::shared_ptr<Data::CloudImageView> userpic;
};
struct ChatInvite { struct ChatInvite {
QString title; QString title;
QString about; QString about;

View file

@ -114,7 +114,7 @@ struct Context {
struct Userpic { struct Userpic {
not_null<PeerData*> peer; not_null<PeerData*> peer;
QString customEntityData; QString customEntityData;
mutable std::shared_ptr<Data::CloudImageView> view; mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey; mutable InMemoryKey uniqueKey;
}; };
@ -380,7 +380,7 @@ bool UpdateUserpics(
const auto peer = not_null{ resolved.peer }; const auto peer = not_null{ resolved.peer };
const auto &data = ReactionEntityData(resolved.reaction); const auto &data = ReactionEntityData(resolved.reaction);
const auto i = ranges::find(was, peer, &Userpic::peer); const auto i = ranges::find(was, peer, &Userpic::peer);
if (i != end(was) && i->view) { if (i != end(was) && i->view.cloud) {
now.push_back(std::move(*i)); now.push_back(std::move(*i));
now.back().customEntityData = data; now.back().customEntityData = data;
continue; continue;

View file

@ -84,7 +84,7 @@ private:
}; };
struct PeerButton { struct PeerButton {
not_null<History*> history; not_null<History*> history;
std::shared_ptr<Data::CloudImageView> userpic; Ui::PeerUserpicView userpic;
Ui::Text::String name; Ui::Text::String name;
Button button; Button button;
}; };

View file

@ -157,7 +157,7 @@ PaintRoundImageCallback ExceptionRow::generatePaintUserpicCallback() {
const auto peer = this->peer(); const auto peer = this->peer();
const auto saved = peer->isSelf(); const auto saved = peer->isSelf();
const auto replies = peer->isRepliesChat(); const auto replies = peer->isRepliesChat();
auto userpic = saved ? nullptr : ensureUserpicView(); auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) { if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);

View file

@ -48,7 +48,7 @@ PaintRoundImageCallback PaintUserpicCallback(
}; };
} }
} }
auto userpic = std::shared_ptr<Data::CloudImageView>(); auto userpic = Ui::PeerUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size); peer->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
}; };
@ -635,8 +635,8 @@ QString PeerListRow::generateShortName() {
: peer()->shortName(); : peer()->shortName();
} }
std::shared_ptr<Data::CloudImageView> &PeerListRow::ensureUserpicView() { Ui::PeerUserpicView &PeerListRow::ensureUserpicView() {
if (!_userpic) { if (!_userpic.cloud && peer()->hasUserpic()) {
_userpic = peer()->createUserpicView(); _userpic = peer()->createUserpicView();
} }
return _userpic; return _userpic;
@ -646,7 +646,7 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback() {
const auto saved = _isSavedMessagesChat; const auto saved = _isSavedMessagesChat;
const auto replies = _isRepliesMessagesChat; const auto replies = _isRepliesMessagesChat;
const auto peer = this->peer(); const auto peer = this->peer();
auto userpic = saved ? nullptr : ensureUserpicView(); auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (saved) { if (saved) {
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size); Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
@ -840,10 +840,10 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
void PeerListRow::createCheckbox( void PeerListRow::createCheckbox(
const style::RoundImageCheckbox &st, const style::RoundImageCheckbox &st,
Fn<void()> updateCallback) { Fn<void()> updateCallback) {
const auto generateRadius = [=] { const auto generateRadius = [=](int size) {
return (!special() && peer()->isForum()) return (!special() && peer()->isForum())
? ImageRoundRadius::Large ? int(size * Ui::ForumUserpicRadiusMultiplier())
: ImageRoundRadius::Ellipse; : std::optional<int>();
}; };
_checkbox = std::make_unique<Ui::RoundImageCheckbox>( _checkbox = std::make_unique<Ui::RoundImageCheckbox>(
st, st,

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/unread_badge.h" #include "ui/unread_badge.h"
#include "ui/userpic_view.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "data/data_cloud_file.h" #include "data/data_cloud_file.h"
@ -84,7 +85,7 @@ public:
return _id; return _id;
} }
[[nodiscard]] std::shared_ptr<Data::CloudImageView> &ensureUserpicView(); [[nodiscard]] Ui::PeerUserpicView &ensureUserpicView();
[[nodiscard]] virtual QString generateName(); [[nodiscard]] virtual QString generateName();
[[nodiscard]] virtual QString generateShortName(); [[nodiscard]] virtual QString generateShortName();
@ -262,7 +263,7 @@ private:
PeerListRowId _id = 0; PeerListRowId _id = 0;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _userpic; mutable Ui::PeerUserpicView _userpic;
std::unique_ptr<Ui::RippleAnimation> _ripple; std::unique_ptr<Ui::RippleAnimation> _ripple;
std::unique_ptr<Ui::RoundImageCheckbox> _checkbox; std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
Ui::Text::String _name; Ui::Text::String _name;

View file

@ -632,7 +632,7 @@ auto ChooseTopicBoxController::Row::generatePaintUserpicCallback()
int y, int y,
int outerWidth, int outerWidth,
int size) { int size) {
auto view = std::shared_ptr<Data::CloudImageView>(); auto view = Ui::PeerUserpicView();
p.translate(x, y); p.translate(x, y);
_topic->paintUserpic(p, view, { _topic->paintUserpic(p, view, {
.st = &st::forumTopicRow, .st = &st::forumTopicRow,

View file

@ -1042,11 +1042,12 @@ void AddPermanentLinkBlock(
state->allUserpicsLoaded = ranges::all_of( state->allUserpicsLoaded = ranges::all_of(
state->list, state->list,
[](const HistoryView::UserpicInRow &element) { [](const HistoryView::UserpicInRow &element) {
return !element.peer->hasUserpic() || element.view->image(); return !element.peer->hasUserpic()
|| !Ui::PeerUserpicLoading(element.view);
}); });
state->content = Ui::JoinedCountContent{ state->content = Ui::JoinedCountContent{
.count = state->count, .count = state->count,
.userpics = state->cachedUserpics .userpics = state->cachedUserpics,
}; };
}; };
value->value( value->value(
@ -1087,7 +1088,7 @@ void AddPermanentLinkBlock(
} else if (element.peer->userpicUniqueKey(element.view) } else if (element.peer->userpicUniqueKey(element.view)
!= element.uniqueKey) { != element.uniqueKey) {
pushing = true; pushing = true;
} else if (!element.view->image()) { } else if (Ui::PeerUserpicLoading(element.view)) {
state->allUserpicsLoaded = false; state->allUserpicsLoaded = false;
} }
} }

View file

@ -36,7 +36,7 @@ struct UserpicState {
PeerShortInfoUserpic current; PeerShortInfoUserpic current;
std::optional<UserPhotosSlice> userSlice; std::optional<UserPhotosSlice> userSlice;
PhotoId userpicPhotoId = PeerData::kUnknownPhotoId; PhotoId userpicPhotoId = PeerData::kUnknownPhotoId;
std::shared_ptr<Data::CloudImageView> userpicView; Ui::PeerUserpicView userpicView;
std::shared_ptr<Data::PhotoMedia> photoView; std::shared_ptr<Data::PhotoMedia> photoView;
std::vector<std::shared_ptr<Data::PhotoMedia>> photoPreloads; std::vector<std::shared_ptr<Data::PhotoMedia>> photoPreloads;
InMemoryKey userpicKey; InMemoryKey userpicKey;
@ -76,27 +76,26 @@ void ProcessUserpic(
not_null<UserpicState*> state) { not_null<UserpicState*> state) {
state->current.videoDocument = nullptr; state->current.videoDocument = nullptr;
state->userpicKey = peer->userpicUniqueKey(state->userpicView); state->userpicKey = peer->userpicUniqueKey(state->userpicView);
if (!state->userpicView) { if (!state->userpicView.cloud) {
GenerateImage( GenerateImage(
state, state,
peer->generateUserpicImage( peer->generateUserpicImage(
state->userpicView, state->userpicView,
st::shortInfoWidth * style::DevicePixelRatio(), st::shortInfoWidth * style::DevicePixelRatio(),
ImageRoundRadius::None), 0),
false); false);
state->current.photoLoadingProgress = 1.; state->current.photoLoadingProgress = 1.;
state->photoView = nullptr; state->photoView = nullptr;
return; return;
} }
peer->loadUserpic(); peer->loadUserpic();
const auto image = state->userpicView->image(); if (Ui::PeerUserpicLoading(state->userpicView)) {
if (!image) {
state->current.photoLoadingProgress = 0.; state->current.photoLoadingProgress = 0.;
state->current.photo = QImage(); state->current.photo = QImage();
state->waitingLoad = true; state->waitingLoad = true;
return; return;
} }
GenerateImage(state, image, true); GenerateImage(state, *state->userpicView.cloud, true);
state->current.photoLoadingProgress = peer->userpicPhotoId() ? 0. : 1.; state->current.photoLoadingProgress = peer->userpicPhotoId() ? 0. : 1.;
state->photoView = nullptr; state->photoView = nullptr;
} }
@ -411,7 +410,7 @@ bool ProcessCurrent(
return state->waitingLoad return state->waitingLoad
&& (state->photoView && (state->photoView
? (!!state->photoView->image(Data::PhotoSize::Large)) ? (!!state->photoView->image(Data::PhotoSize::Large))
: (state->userpicView && state->userpicView->image())); : (!Ui::PeerUserpicLoading(state->userpicView)));
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
push(); push();
}, lifetime); }, lifetime);

View file

@ -536,7 +536,7 @@ void ShareBox::applyFilterUpdate(const QString &query) {
} }
PaintRoundImageCallback ForceRoundUserpicCallback(not_null<PeerData*> peer) { PaintRoundImageCallback ForceRoundUserpicCallback(not_null<PeerData*> peer) {
auto userpic = std::shared_ptr<Data::CloudImageView>(); auto userpic = Ui::PeerUserpicView();
auto cache = std::make_shared<QImage>(); auto cache = std::make_shared<QImage>();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable { return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
@ -956,9 +956,9 @@ ShareBox::Inner::Chat::Chat(
st.checkbox, st.checkbox,
updateCallback, updateCallback,
PaintUserpicCallback(peer, true), PaintUserpicCallback(peer, true),
[=] { return peer->isForum() [=](int size) { return peer->isForum()
? ImageRoundRadius::Large ? int(size * Ui::ForumUserpicRadiusMultiplier())
: ImageRoundRadius::Ellipse; }) : std::optional<int>(); })
, name(st.checkbox.imageRadius * 2) { , name(st.checkbox.imageRadius * 2) {
} }

View file

@ -25,7 +25,6 @@ class PowerSaveBlocker;
namespace Data { namespace Data {
class PhotoMedia; class PhotoMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {

View file

@ -159,7 +159,12 @@ void Userpic::refreshPhoto() {
_userPhotoFull = true; _userPhotoFull = true;
createCache(_photo->image(Data::PhotoSize::Thumbnail)); createCache(_photo->image(Data::PhotoSize::Thumbnail));
} else if (_userPhoto.isNull()) { } else if (_userPhoto.isNull()) {
createCache(_userpic ? _userpic->image() : nullptr); if (const auto cloud = _peer->userpicCloudImage(_userpic)) {
auto image = Image(base::duplicate(*cloud));
createCache(&image);
} else {
createCache(nullptr);
}
} }
} }
@ -200,7 +205,7 @@ void Userpic::createCache(Image *image) {
Ui::EmptyUserpic::UserpicColor( Ui::EmptyUserpic::UserpicColor(
Data::PeerColorIndex(_peer->id)), Data::PeerColorIndex(_peer->id)),
_peer->name() _peer->name()
).paint(p, 0, 0, size, size); ).paintCircle(p, 0, 0, size, size);
} }
//_userPhoto = Images::PixmapFast(Images::Round( //_userPhoto = Images::PixmapFast(Images::Round(
// std::move(filled), // std::move(filled),

View file

@ -8,13 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
class PeerData; class PeerData;
class Image; class Image;
namespace Data { namespace Data {
class CloudImageView;
class PhotoMedia; class PhotoMedia;
} // namespace Data } // namespace Data
@ -51,7 +51,7 @@ private:
Ui::RpWidget _content; Ui::RpWidget _content;
not_null<PeerData*> _peer; not_null<PeerData*> _peer;
std::shared_ptr<Data::CloudImageView> _userpic; Ui::PeerUserpicView _userpic;
std::shared_ptr<Data::PhotoMedia> _photo; std::shared_ptr<Data::PhotoMedia> _photo;
Ui::Animations::Simple _mutedAnimation; Ui::Animations::Simple _mutedAnimation;
QPixmap _userPhoto; QPixmap _userPhoto;

View file

@ -350,7 +350,7 @@ void MembersRow::updateBlobAnimation(crl::time now) {
} }
void MembersRow::ensureUserpicCache( void MembersRow::ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int size) { int size) {
Expects(_blobsAnimation != nullptr); Expects(_blobsAnimation != nullptr);
@ -401,7 +401,7 @@ void MembersRow::paintBlobs(
void MembersRow::paintScaledUserpic( void MembersRow::paintScaledUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,

View file

@ -19,6 +19,7 @@ struct GroupCallParticipant;
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Calls::Group { namespace Calls::Group {
@ -180,7 +181,7 @@ private:
void setVolume(int volume); void setVolume(int volume);
void ensureUserpicCache( void ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int size); int size);
void paintBlobs( void paintBlobs(
Painter &p, Painter &p,
@ -190,7 +191,7 @@ private:
int sizeh, PanelMode mode); int sizeh, PanelMode mode);
void paintScaledUserpic( void paintScaledUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,

View file

@ -59,7 +59,7 @@ private:
const not_null<QAction*> _dummyAction; const not_null<QAction*> _dummyAction;
const style::Menu &_st; const style::Menu &_st;
const not_null<PeerData*> _peer; const not_null<PeerData*> _peer;
std::shared_ptr<Data::CloudImageView> _userpicView; Ui::PeerUserpicView _userpicView;
Ui::Text::String _text; Ui::Text::String _text;
Ui::Text::String _name; Ui::Text::String _name;

View file

@ -29,7 +29,6 @@ class PowerSaveBlocker;
namespace Data { namespace Data {
class PhotoMedia; class PhotoMedia;
class CloudImageView;
class GroupCall; class GroupCall;
} // namespace Data } // namespace Data

View file

@ -462,7 +462,7 @@ void Viewport::RendererGL::validateUserpicFrame(
tileData.userpicFrame = tile->row()->peer()->generateUserpicImage( tileData.userpicFrame = tile->row()->peer()->generateUserpicImage(
tile->row()->ensureUserpicView(), tile->row()->ensureUserpicView(),
size.width(), size.width(),
ImageRoundRadius::None); 0);
} }
void Viewport::RendererGL::paintTile( void Viewport::RendererGL::paintTile(

View file

@ -80,7 +80,7 @@ void Viewport::RendererSW::validateUserpicFrame(
tile->row()->peer()->generateUserpicImage( tile->row()->peer()->generateUserpicImage(
tile->row()->ensureUserpicView(), tile->row()->ensureUserpicView(),
size.width(), size.width(),
ImageRoundRadius::None), 0),
kBlurRadius); kBlurRadius);
} }

View file

@ -165,6 +165,20 @@ struct FieldAutocomplete::StickerSuggestion {
QImage premiumLock; QImage premiumLock;
}; };
struct FieldAutocomplete::MentionRow {
not_null<UserData*> user;
Ui::Text::String name;
Ui::PeerUserpicView userpic;
};
struct FieldAutocomplete::BotCommandRow {
not_null<UserData*> user;
QString command;
QString description;
Ui::PeerUserpicView userpic;
Ui::Text::String descriptionText;
};
FieldAutocomplete::FieldAutocomplete( FieldAutocomplete::FieldAutocomplete(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller) not_null<Window::SessionController*> controller)

View file

@ -31,7 +31,6 @@ class SessionController;
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace SendMenu { namespace SendMenu {
@ -131,20 +130,8 @@ private:
class Inner; class Inner;
friend class Inner; friend class Inner;
struct StickerSuggestion; struct StickerSuggestion;
struct MentionRow;
struct MentionRow { struct BotCommandRow;
not_null<UserData*> user;
Ui::Text::String name;
std::shared_ptr<Data::CloudImageView> userpic;
};
struct BotCommandRow {
not_null<UserData*> user;
QString command;
QString description;
std::shared_ptr<Data::CloudImageView> userpic;
Ui::Text::String descriptionText;
};
using HashtagRows = std::vector<QString>; using HashtagRows = std::vector<QString>;
using BotCommandRows = std::vector<BotCommandRow>; using BotCommandRows = std::vector<BotCommandRow>;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "ui/round_rect.h" #include "ui/round_rect.h"
#include "ui/userpic_view.h"
namespace Ui { namespace Ui {
class InputField; class InputField;
@ -21,7 +22,6 @@ namespace Data {
class StickersSet; class StickersSet;
class StickersSetThumbnailView; class StickersSetThumbnailView;
class DocumentMedia; class DocumentMedia;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Lottie { namespace Lottie {
@ -73,7 +73,7 @@ struct StickerIcon {
ChannelData *megagroup = nullptr; ChannelData *megagroup = nullptr;
mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia; mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia;
mutable std::shared_ptr<Data::DocumentMedia> stickerMedia; mutable std::shared_ptr<Data::DocumentMedia> stickerMedia;
mutable std::shared_ptr<Data::CloudImageView> megagroupUserpic; mutable Ui::PeerUserpicView megagroupUserpic;
int pixw = 0; int pixw = 0;
int pixh = 0; int pixh = 0;
mutable rpl::lifetime lifetime; mutable rpl::lifetime lifetime;

View file

@ -22,13 +22,6 @@ CloudFile::~CloudFile() {
base::take(loader); base::take(loader);
} }
void CloudImageView::set(
not_null<Main::Session*> session,
QImage image) {
_image.emplace(std::move(image));
session->notifyDownloaderTaskFinished();
}
CloudImage::CloudImage() = default; CloudImage::CloudImage() = default;
CloudImage::CloudImage( CloudImage::CloudImage(
@ -37,10 +30,6 @@ CloudImage::CloudImage(
update(session, data); update(session, data);
} }
Image *CloudImageView::image() {
return _image ? &*_image : nullptr;
}
void CloudImage::set( void CloudImage::set(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const ImageWithLocation &data) { const ImageWithLocation &data) {
@ -52,11 +41,11 @@ void CloudImage::set(
_file.location = ImageLocation(); _file.location = ImageLocation();
_file.byteSize = 0; _file.byteSize = 0;
_file.flags = CloudFile::Flag(); _file.flags = CloudFile::Flag();
_view = std::weak_ptr<CloudImageView>(); _view = std::weak_ptr<QImage>();
} else if (was != now } else if (was != now
&& (!v::is<InMemoryLocation>(was) || v::is<InMemoryLocation>(now))) { && (!v::is<InMemoryLocation>(was) || v::is<InMemoryLocation>(now))) {
_file.location = ImageLocation(); _file.location = ImageLocation();
_view = std::weak_ptr<CloudImageView>(); _view = std::weak_ptr<QImage>();
} }
UpdateCloudFile( UpdateCloudFile(
_file, _file,
@ -65,9 +54,7 @@ void CloudImage::set(
kImageCacheTag, kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); }, [=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded, QByteArray) { [=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) { setToActive(session, std::move(preloaded));
view->set(session, data.preloaded);
}
}); });
} }
@ -81,9 +68,7 @@ void CloudImage::update(
kImageCacheTag, kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); }, [=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded, QByteArray) { [=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) { setToActive(session, std::move(preloaded));
view->set(session, data.preloaded);
}
}); });
} }
@ -107,16 +92,14 @@ void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) {
const auto autoLoading = false; const auto autoLoading = false;
const auto finalCheck = [=] { const auto finalCheck = [=] {
if (const auto active = activeView()) { if (const auto active = activeView()) {
return !active->image(); return active->isNull();
} else if (_file.flags & CloudFile::Flag::Loaded) { } else if (_file.flags & CloudFile::Flag::Loaded) {
return false; return false;
} }
return !(_file.flags & CloudFile::Flag::Loaded); return !(_file.flags & CloudFile::Flag::Loaded);
}; };
const auto done = [=](QImage result, QByteArray) { const auto done = [=](QImage result, QByteArray) {
if (const auto active = activeView()) { setToActive(session, std::move(result));
active->set(session, std::move(result));
}
}; };
LoadCloudFile( LoadCloudFile(
session, session,
@ -137,27 +120,37 @@ int CloudImage::byteSize() const {
return _file.byteSize; return _file.byteSize;
} }
std::shared_ptr<CloudImageView> CloudImage::createView() { std::shared_ptr<QImage> CloudImage::createView() {
if (auto active = activeView()) { if (auto active = activeView()) {
return active; return active;
} }
auto view = std::make_shared<CloudImageView>(); auto view = std::make_shared<QImage>();
_view = view; _view = view;
return view; return view;
} }
std::shared_ptr<CloudImageView> CloudImage::activeView() const { std::shared_ptr<QImage> CloudImage::activeView() const {
return _view.lock(); return _view.lock();
} }
bool CloudImage::isCurrentView( bool CloudImage::isCurrentView(const std::shared_ptr<QImage> &view) const {
const std::shared_ptr<CloudImageView> &view) const {
if (!view) { if (!view) {
return empty(); return empty();
} }
return !view.owner_before(_view) && !_view.owner_before(view); return !view.owner_before(_view) && !_view.owner_before(view);
} }
void CloudImage::setToActive(
not_null<Main::Session*> session,
QImage image) {
if (const auto view = activeView()) {
*view = image.isNull()
? Image::Empty()->original()
: std::move(image);
session->notifyDownloaderTaskFinished();
}
}
void UpdateCloudFile( void UpdateCloudFile(
CloudFile &file, CloudFile &file,
const ImageWithLocation &data, const ImageWithLocation &data,

View file

@ -44,17 +44,6 @@ struct CloudFile final {
base::flags<Flag> flags; base::flags<Flag> flags;
}; };
class CloudImageView final {
public:
void set(not_null<Main::Session*> session, QImage image);
[[nodiscard]] Image *image();
private:
std::optional<Image> _image;
};
class CloudImage final { class CloudImage final {
public: public:
CloudImage(); CloudImage();
@ -79,14 +68,16 @@ public:
[[nodiscard]] const ImageLocation &location() const; [[nodiscard]] const ImageLocation &location() const;
[[nodiscard]] int byteSize() const; [[nodiscard]] int byteSize() const;
[[nodiscard]] std::shared_ptr<CloudImageView> createView(); [[nodiscard]] std::shared_ptr<QImage> createView();
[[nodiscard]] std::shared_ptr<CloudImageView> activeView() const; [[nodiscard]] std::shared_ptr<QImage> activeView() const;
[[nodiscard]] bool isCurrentView( [[nodiscard]] bool isCurrentView(
const std::shared_ptr<CloudImageView> &view) const; const std::shared_ptr<QImage> &view) const;
private: private:
void setToActive(not_null<Main::Session*> session, QImage image);
CloudFile _file; CloudFile _file;
std::weak_ptr<CloudImageView> _view; std::weak_ptr<QImage> _view;
}; };

View file

@ -216,7 +216,7 @@ void Folder::loadUserpic() {
void Folder::paintUserpic( void Folder::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const { const Dialogs::Ui::PaintContext &context) const {
paintUserpic( paintUserpic(
p, p,

View file

@ -57,7 +57,7 @@ public:
void loadUserpic() override; void loadUserpic() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override; const Dialogs::Ui::PaintContext &context) const override;
void paintUserpic(Painter &p, int x, int y, int size) const; void paintUserpic(Painter &p, int x, int y, int size) const;
void paintUserpic( void paintUserpic(

View file

@ -568,7 +568,7 @@ void ForumTopic::loadUserpic() {
void ForumTopic::paintUserpic( void ForumTopic::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const { const Dialogs::Ui::PaintContext &context) const {
const auto &st = context.st; const auto &st = context.st;
auto position = QPoint(st->padding.left(), st->padding.top()); auto position = QPoint(st->padding.left(), st->padding.top());

View file

@ -150,7 +150,7 @@ public:
void loadUserpic() override; void loadUserpic() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override; const Dialogs::Ui::PaintContext &context) const override;
void clearUserpicLoops(); void clearUserpicLoops();

View file

@ -191,7 +191,7 @@ void PeerData::updateNameDelayed(
} }
} }
_name = newName; _name = newName;
_userpicEmpty = nullptr; invalidateEmptyUserpic();
auto flags = UpdateFlag::None | UpdateFlag::None; auto flags = UpdateFlag::None | UpdateFlag::None;
auto oldFirstLetters = base::flat_set<QChar>(); auto oldFirstLetters = base::flat_set<QChar>();
@ -231,13 +231,17 @@ not_null<Ui::EmptyUserpic*> PeerData::ensureEmptyUserpic() const {
const auto user = asUser(); const auto user = asUser();
_userpicEmpty = std::make_unique<Ui::EmptyUserpic>( _userpicEmpty = std::make_unique<Ui::EmptyUserpic>(
Ui::EmptyUserpic::UserpicColor(Data::PeerColorIndex(id)), Ui::EmptyUserpic::UserpicColor(Data::PeerColorIndex(id)),
user && user->isInaccessible() ((user && user->isInaccessible())
? Ui::EmptyUserpic::InaccessibleName() ? Ui::EmptyUserpic::InaccessibleName()
: name()); : name()));
} }
return _userpicEmpty.get(); return _userpicEmpty.get();
} }
void PeerData::invalidateEmptyUserpic() {
_userpicEmpty = nullptr;
}
ClickHandlerPtr PeerData::createOpenLink() { ClickHandlerPtr PeerData::createOpenLink() {
return std::make_shared<PeerClickHandler>(this); return std::make_shared<PeerClickHandler>(this);
} }
@ -265,50 +269,45 @@ void PeerData::setUserpicPhoto(const MTPPhoto &data) {
} }
} }
Image *PeerData::currentUserpic( QImage *PeerData::userpicCloudImage(Ui::PeerUserpicView &view) const {
std::shared_ptr<Data::CloudImageView> &view) const { if (!_userpic.isCurrentView(view.cloud)) {
if (!_userpic.isCurrentView(view)) { if (!_userpic.empty()) {
view = _userpic.createView(); view.cloud = _userpic.createView();
_userpic.load(&session(), userpicOrigin()); _userpic.load(&session(), userpicOrigin());
} else {
view.cloud = nullptr;
if (view.empty.null()) {
view.paletteVersion = 0;
}
}
} }
const auto image = view ? view->image() : nullptr; if (const auto image = view.cloud.get(); image && !image->isNull()) {
if (image) {
_userpicEmpty = nullptr; _userpicEmpty = nullptr;
return image;
} else if (isNotificationsUser()) { } else if (isNotificationsUser()) {
static auto result = Image( static auto result = Window::LogoNoMargin().scaledToWidth(
Window::LogoNoMargin().scaledToWidth( kUserpicSize,
kUserpicSize, Qt::SmoothTransformation);
Qt::SmoothTransformation));
return &result; return &result;
} }
return image; return nullptr;
} }
void PeerData::paintUserpic( void PeerData::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int size) const { int size) const {
if (const auto userpic = currentUserpic(view)) { const auto cloud = userpicCloudImage(view);
const auto rounding = isForum() const auto ratio = style::DevicePixelRatio();
? Images::Option::RoundLarge Ui::ValidateUserpicCache(
: Images::Option::RoundCircle; view,
p.drawPixmap( cloud,
x, cloud ? nullptr : ensureEmptyUserpic().get(),
y, size * ratio,
userpic->pix(size, size, { .options = rounding })); isForum());
} else if (isForum()) { p.drawImage(QRect(x, y, size, size), view.cached);
ensureEmptyUserpic()->paintRounded(
p,
x,
y,
x + size + x,
size,
st::roundRadiusLarge);
} else{
ensureEmptyUserpic()->paint(p, x, y, x + size + x, size);
}
} }
void PeerData::loadUserpic() { void PeerData::loadUserpic() {
@ -319,112 +318,76 @@ bool PeerData::hasUserpic() const {
return !_userpic.empty(); return !_userpic.empty();
} }
std::shared_ptr<Data::CloudImageView> PeerData::activeUserpicView() { Ui::PeerUserpicView PeerData::activeUserpicView() {
return _userpic.empty() ? nullptr : _userpic.activeView(); return { .cloud = _userpic.empty() ? nullptr : _userpic.activeView() };
} }
std::shared_ptr<Data::CloudImageView> PeerData::createUserpicView() { Ui::PeerUserpicView PeerData::createUserpicView() {
if (_userpic.empty()) { if (_userpic.empty()) {
return nullptr; return {};
} }
auto result = _userpic.createView(); auto result = _userpic.createView();
_userpic.load(&session(), userpicPhotoOrigin()); _userpic.load(&session(), userpicPhotoOrigin());
return result; return { .cloud = result };
} }
bool PeerData::useEmptyUserpic( bool PeerData::useEmptyUserpic(Ui::PeerUserpicView &view) const {
std::shared_ptr<Data::CloudImageView> &view) const { return !userpicCloudImage(view);
return !currentUserpic(view);
} }
InMemoryKey PeerData::userpicUniqueKey( InMemoryKey PeerData::userpicUniqueKey(Ui::PeerUserpicView &view) const {
std::shared_ptr<Data::CloudImageView> &view) const {
return useEmptyUserpic(view) return useEmptyUserpic(view)
? ensureEmptyUserpic()->uniqueKey() ? ensureEmptyUserpic()->uniqueKey()
: inMemoryKey(_userpic.location()); : inMemoryKey(_userpic.location());
} }
void PeerData::saveUserpic(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
generateUserpicImage(view, size * cIntRetinaFactor()).save(path, "PNG");
}
void PeerData::saveUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const {
generateUserpicImage(
view,
size * cIntRetinaFactor(),
ImageRoundRadius::Small).save(path, "PNG");
}
QPixmap PeerData::genUserpic(
std::shared_ptr<Data::CloudImageView> &view,
int size) const {
if (const auto userpic = currentUserpic(view)) {
const auto rounding = isForum()
? Images::Option::RoundLarge
: Images::Option::RoundCircle;
return userpic->pix(size, size, { .options = rounding });
}
const auto ratio = style::DevicePixelRatio();
auto result = QImage(
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
result.setDevicePixelRatio(ratio);
result.fill(Qt::transparent);
{
Painter p(&result);
paintUserpic(p, view, 0, 0, size);
}
return Ui::PixmapFromImage(std::move(result));
}
QImage PeerData::generateUserpicImage( QImage PeerData::generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int size) const {
return generateUserpicImage(
view,
size,
isForum() ? ImageRoundRadius::Large : ImageRoundRadius::Ellipse);
}
QImage PeerData::generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
int size, int size,
ImageRoundRadius radius) const { std::optional<int> radius) const {
if (const auto userpic = currentUserpic(view)) { if (const auto userpic = userpicCloudImage(view)) {
const auto options = (radius == ImageRoundRadius::Ellipse) auto image = userpic->scaled(
? Images::Option::RoundCircle
: (radius == ImageRoundRadius::Large)
? Images::Option::RoundLarge
: (radius == ImageRoundRadius::Small)
? Images::Option::RoundSmall
: Images::Option();
return userpic->pixNoCache(
{ size, size }, { size, size },
{ .options = options }).toImage(); Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
const auto round = [&](int radius) {
return Images::Round(
std::move(image),
Images::CornersMask(radius));
};
if (radius == 0) {
return image;
} else if (radius) {
return round(*radius);
} else if (isForum()) {
return round(size * Ui::ForumUserpicRadiusMultiplier());
} else {
return Images::Circle(std::move(image));
}
} }
auto result = QImage( auto result = QImage(
QSize(size, size), QSize(size, size),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent); result.fill(Qt::transparent);
{
Painter p(&result); Painter p(&result);
if (radius == ImageRoundRadius::Ellipse) { if (radius == 0) {
ensureEmptyUserpic()->paint(p, 0, 0, size, size); ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
} else if (radius == ImageRoundRadius::None) { } else if (radius) {
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size); ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, *radius);
} else if (radius == ImageRoundRadius::Large) { } else if (isForum()) {
const auto radius = st::roundRadiusLarge; ensureEmptyUserpic()->paintRounded(
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, radius); p,
} else { 0,
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size); 0,
} size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
} else {
ensureEmptyUserpic()->paintCircle(p, 0, 0, size, size);
} }
p.end();
return result; return result;
} }

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_flags.h" #include "data/data_flags.h"
#include "data/notify/data_peer_notify_settings.h" #include "data/notify/data_peer_notify_settings.h"
#include "data/data_cloud_file.h" #include "data/data_cloud_file.h"
#include "ui/userpic_view.h"
struct BotInfo; struct BotInfo;
class PeerData; class PeerData;
@ -36,13 +37,12 @@ class Forum;
class ForumTopic; class ForumTopic;
class Session; class Session;
class GroupCall; class GroupCall;
class CloudImageView;
struct ReactionId; struct ReactionId;
int PeerColorIndex(PeerId peerId); [[nodiscard]] int PeerColorIndex(PeerId peerId);
// Must be used only for PeerColor-s. // Must be used only for PeerColor-s.
PeerId FakePeerIdForJustName(const QString &name); [[nodiscard]] PeerId FakePeerIdForJustName(const QString &name);
class RestrictionCheckResult { class RestrictionCheckResult {
public: public:
@ -264,13 +264,13 @@ public:
void setUserpicPhoto(const MTPPhoto &data); void setUserpicPhoto(const MTPPhoto &data);
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int size) const; int size) const;
void paintUserpicLeft( void paintUserpicLeft(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int w, int w,
@ -279,30 +279,14 @@ public:
} }
void loadUserpic(); void loadUserpic();
[[nodiscard]] bool hasUserpic() const; [[nodiscard]] bool hasUserpic() const;
[[nodiscard]] std::shared_ptr<Data::CloudImageView> activeUserpicView(); [[nodiscard]] Ui::PeerUserpicView activeUserpicView();
[[nodiscard]] std::shared_ptr<Data::CloudImageView> createUserpicView(); [[nodiscard]] Ui::PeerUserpicView createUserpicView();
[[nodiscard]] bool useEmptyUserpic( [[nodiscard]] bool useEmptyUserpic(Ui::PeerUserpicView &view) const;
std::shared_ptr<Data::CloudImageView> &view) const; [[nodiscard]] InMemoryKey userpicUniqueKey(Ui::PeerUserpicView &view) const;
[[nodiscard]] InMemoryKey userpicUniqueKey(
std::shared_ptr<Data::CloudImageView> &view) const;
void saveUserpic(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const;
void saveUserpicRounded(
std::shared_ptr<Data::CloudImageView> &view,
const QString &path,
int size) const;
[[nodiscard]] QPixmap genUserpic(
std::shared_ptr<Data::CloudImageView> &view,
int size) const;
[[nodiscard]] QImage generateUserpicImage( [[nodiscard]] QImage generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int size) const;
[[nodiscard]] QImage generateUserpicImage(
std::shared_ptr<Data::CloudImageView> &view,
int size, int size,
ImageRoundRadius radius) const; std::optional<int> radius = {}) const;
[[nodiscard]] ImageLocation userpicLocation() const { [[nodiscard]] ImageLocation userpicLocation() const {
return _userpic.location(); return _userpic.location();
} }
@ -332,8 +316,7 @@ public:
return _openLink; return _openLink;
} }
[[nodiscard]] Image *currentUserpic( [[nodiscard]] QImage *userpicCloudImage(Ui::PeerUserpicView &view) const;
std::shared_ptr<Data::CloudImageView> &view) const;
[[nodiscard]] bool canPinMessages() const; [[nodiscard]] bool canPinMessages() const;
[[nodiscard]] bool canEditMessagesIndefinitely() const; [[nodiscard]] bool canEditMessagesIndefinitely() const;
@ -423,6 +406,7 @@ protected:
const QString &newUsername); const QString &newUsername);
void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo); void updateUserpic(PhotoId photoId, MTP::DcId dcId, bool hasVideo);
void clearUserpic(); void clearUserpic();
void invalidateEmptyUserpic();
private: private:
void fillNames(); void fillNames();

View file

@ -515,17 +515,10 @@ bool ChannelHasActiveCall(not_null<ChannelData*> channel) {
rpl::producer<QImage> PeerUserpicImageValue( rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer, not_null<PeerData*> peer,
int size) { int size) {
return PeerUserpicImageValue(peer, size, ImageRoundRadius::Ellipse);
}
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius) {
return [=](auto consumer) { return [=](auto consumer) {
auto result = rpl::lifetime(); auto result = rpl::lifetime();
struct State { struct State {
std::shared_ptr<CloudImageView> view; Ui::PeerUserpicView view;
rpl::lifetime waiting; rpl::lifetime waiting;
InMemoryKey key = {}; InMemoryKey key = {};
bool empty = true; bool empty = true;
@ -534,7 +527,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
const auto state = result.make_state<State>(); const auto state = result.make_state<State>();
state->push = [=] { state->push = [=] {
const auto key = peer->userpicUniqueKey(state->view); const auto key = peer->userpicUniqueKey(state->view);
const auto loading = state->view && !state->view->image(); const auto loading = Ui::PeerUserpicLoading(state->view);
if (loading && !state->waiting) { if (loading && !state->waiting) {
peer->session().downloaderTaskFinished( peer->session().downloaderTaskFinished(
@ -548,8 +541,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
} }
state->key = key; state->key = key;
state->empty = false; state->empty = false;
consumer.put_next( consumer.put_next(peer->generateUserpicImage(state->view, size));
peer->generateUserpicImage(state->view, size, radius));
}; };
peer->session().changes().peerFlagsValue( peer->session().changes().peerFlagsValue(
peer, peer,

View file

@ -134,10 +134,6 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue( [[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer, not_null<PeerData*> peer,
int size); int size);
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius);
[[nodiscard]] const AllowedReactions &PeerAllowedReactions( [[nodiscard]] const AllowedReactions &PeerAllowedReactions(
not_null<PeerData*> peer); not_null<PeerData*> peer);

View file

@ -228,22 +228,27 @@ void UserData::setAccessHash(uint64 accessHash) {
if (accessHash == kInaccessibleAccessHashOld) { if (accessHash == kInaccessibleAccessHashOld) {
_accessHash = 0; _accessHash = 0;
_flags.add(Flag::Deleted); _flags.add(Flag::Deleted);
invalidateEmptyUserpic();
} else { } else {
_accessHash = accessHash; _accessHash = accessHash;
} }
} }
void UserData::setFlags(UserDataFlags which) { void UserData::setFlags(UserDataFlags which) {
if ((which & UserDataFlag::Deleted)
!= (flags() & UserDataFlag::Deleted)) {
invalidateEmptyUserpic();
}
_flags.set((flags() & UserDataFlag::Self) _flags.set((flags() & UserDataFlag::Self)
| (which & ~UserDataFlag::Self)); | (which & ~UserDataFlag::Self));
} }
void UserData::addFlags(UserDataFlags which) { void UserData::addFlags(UserDataFlags which) {
_flags.add(which & ~UserDataFlag::Self); setFlags(flags() | which);
} }
void UserData::removeFlags(UserDataFlags which) { void UserData::removeFlags(UserDataFlags which) {
_flags.remove(which & ~UserDataFlag::Self); setFlags(flags() & ~which);
} }
bool UserData::isVerified() const { bool UserData::isVerified() const {

View file

@ -25,10 +25,10 @@ class Session;
class Forum; class Forum;
class Folder; class Folder;
class ForumTopic; class ForumTopic;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Dialogs::Ui { namespace Dialogs::Ui {
@ -233,7 +233,7 @@ public:
virtual void loadUserpic() = 0; virtual void loadUserpic() = 0;
virtual void paintUserpic( virtual void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Ui::PaintContext &context) const = 0; const Ui::PaintContext &context) const = 0;
[[nodiscard]] TimeId chatListTimeId() const { [[nodiscard]] TimeId chatListTimeId() const {

View file

@ -1134,7 +1134,7 @@ void InnerWidget::paintSearchInFilter(
void InnerWidget::paintSearchInPeer( void InnerWidget::paintSearchInPeer(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int top, int top,
const Ui::Text::String &text) const { const Ui::Text::String &text) const {
const auto paintUserpic = [&](Painter &p, int x, int y, int size) { const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
@ -1168,7 +1168,7 @@ void InnerWidget::paintSearchInTopic(
Painter &p, Painter &p,
const Ui::PaintContext &context, const Ui::PaintContext &context,
not_null<Data::ForumTopic*> topic, not_null<Data::ForumTopic*> topic,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int top, int top,
const Ui::Text::String &text) const { const Ui::Text::String &text) const {
const auto paintUserpic = [&](Painter &p, int x, int y, int size) { const auto paintUserpic = [&](Painter &p, int x, int y, int size) {
@ -2846,7 +2846,7 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
_searchFromUserUserpic = _searchFromPeer->createUserpicView(); _searchFromUserUserpic = _searchFromPeer->createUserpicView();
} else { } else {
_cancelSearchFromUser->hide(); _cancelSearchFromUser->hide();
_searchFromUserUserpic = nullptr; _searchFromUserUserpic = {};
} }
if (_searchInChat || _searchFromPeer) { if (_searchInChat || _searchFromPeer) {
refreshSearchInChatLabel(); refreshSearchInChatLabel();
@ -2855,7 +2855,7 @@ void InnerWidget::searchInChat(Key key, PeerData *from) {
if (const auto peer = _searchInChat.peer()) { if (const auto peer = _searchInChat.peer()) {
_searchInChatUserpic = peer->createUserpicView(); _searchInChatUserpic = peer->createUserpicView();
} else { } else {
_searchInChatUserpic = nullptr; _searchInChatUserpic = {};
} }
moveCancelSearchButtons(); moveCancelSearchButtons();

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/dragging_scroll_manager.h" #include "ui/dragging_scroll_manager.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "base/flags.h" #include "base/flags.h"
#include "base/object_ptr.h" #include "base/object_ptr.h"
@ -39,7 +40,6 @@ class SessionController;
} // namespace Window } // namespace Window
namespace Data { namespace Data {
class CloudImageView;
class Thread; class Thread;
class Folder; class Folder;
class Forum; class Forum;
@ -340,7 +340,7 @@ private:
void paintSearchInPeer( void paintSearchInPeer(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int top, int top,
const Ui::Text::String &text) const; const Ui::Text::String &text) const;
void paintSearchInSaved( void paintSearchInSaved(
@ -355,7 +355,7 @@ private:
Painter &p, Painter &p,
const Ui::PaintContext &context, const Ui::PaintContext &context,
not_null<Data::ForumTopic*> topic, not_null<Data::ForumTopic*> topic,
std::shared_ptr<Data::CloudImageView> &userpic, Ui::PeerUserpicView &userpic,
int top, int top,
const Ui::Text::String &text) const; const Ui::Text::String &text) const;
template <typename PaintUserpic> template <typename PaintUserpic>
@ -470,8 +470,8 @@ private:
Key _searchInChat; Key _searchInChat;
History *_searchInMigrated = nullptr; History *_searchInMigrated = nullptr;
PeerData *_searchFromPeer = nullptr; PeerData *_searchFromPeer = nullptr;
mutable std::shared_ptr<Data::CloudImageView> _searchInChatUserpic; mutable Ui::PeerUserpicView _searchInChatUserpic;
mutable std::shared_ptr<Data::CloudImageView> _searchFromUserUserpic; mutable Ui::PeerUserpicView _searchFromUserUserpic;
Ui::Text::String _searchInChatText; Ui::Text::String _searchInChatText;
Ui::Text::String _searchFromUserText; Ui::Text::String _searchFromUserText;
RowDescriptor _menuRow; RowDescriptor _menuRow;

View file

@ -38,11 +38,11 @@ constexpr auto kNoneLayer = 0;
void PaintCornerBadgeTTLFrame( void PaintCornerBadgeTTLFrame(
QPainter &q, QPainter &q,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
float64 progress, float64 progress,
int photoSize) { int photoSize) {
const auto ttl = peer->messagesTTL(); const auto ttl = peer->messagesTTL();
if (!ttl || !view) { if (!ttl) {
return; return;
} }
using Radius = ImageRoundRadius; using Radius = ImageRoundRadius;
@ -54,7 +54,7 @@ void PaintCornerBadgeTTLFrame(
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto fullSize = photoSize; const auto fullSize = photoSize;
const auto blurredFull = Images::BlurLargeImage( const auto blurredFull = Images::BlurLargeImage(
peer->generateUserpicImage(view, fullSize * ratio, Radius::None), peer->generateUserpicImage(view, fullSize * ratio, 0),
kBlurRadius); kBlurRadius);
const auto partRect = CornerBadgeTTLRect(fullSize); const auto partRect = CornerBadgeTTLRect(fullSize);
const auto &partSize = partRect.width(); const auto &partSize = partRect.width();
@ -338,7 +338,7 @@ void Row::PaintCornerBadgeFrame(
not_null<CornerBadgeUserpic*> data, not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Ui::PaintContext &context) { const Ui::PaintContext &context) {
data->frame.fill(Qt::transparent); data->frame.fill(Qt::transparent);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text.h" #include "ui/text/text.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/unread_badge.h" #include "ui/unread_badge.h"
#include "ui/userpic_view.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "dialogs/ui/dialogs_message_view.h" #include "dialogs/ui/dialogs_message_view.h"
@ -20,10 +21,6 @@ namespace style {
struct DialogRow; struct DialogRow;
} // namespace style } // namespace style
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
} // namespace Ui } // namespace Ui
@ -69,13 +66,12 @@ public:
int outerWidth, int outerWidth,
const QColor *colorOverride = nullptr) const; const QColor *colorOverride = nullptr) const;
[[nodiscard]] auto userpicView() const [[nodiscard]] Ui::PeerUserpicView &userpicView() const {
-> std::shared_ptr<Data::CloudImageView> & {
return _userpic; return _userpic;
} }
private: private:
mutable std::shared_ptr<Data::CloudImageView> _userpic; mutable Ui::PeerUserpicView _userpic;
mutable std::unique_ptr<Ui::RippleAnimation> _ripple; mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
}; };
@ -182,7 +178,7 @@ private:
not_null<CornerBadgeUserpic*> data, not_null<CornerBadgeUserpic*> data,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Ui::PaintContext &context); const Ui::PaintContext &context);
Key _id; Key _id;

View file

@ -285,9 +285,9 @@ void PaintRow(
not_null<Entry*> entry, not_null<Entry*> entry,
VideoUserpic *videoUserpic, VideoUserpic *videoUserpic,
PeerData *from, PeerData *from,
Ui::PeerBadge &fromBadge, PeerBadge &fromBadge,
Fn<void()> customEmojiRepaint, Fn<void()> customEmojiRepaint,
const Ui::Text::String &fromName, const Text::String &fromName,
const HiddenSenderInfo *hiddenSenderInfo, const HiddenSenderInfo *hiddenSenderInfo,
HistoryItem *item, HistoryItem *item,
const Data::Draft *draft, const Data::Draft *draft,
@ -306,8 +306,8 @@ void PaintRow(
: context.selected : context.selected
? st::dialogsBgOver ? st::dialogsBgOver
: anim::brush( : anim::brush(
st::dialogsBg, st::dialogsBg,
st::dialogsBgOver, st::dialogsBgOver,
context.childListShown); context.childListShown);
p.fillRect(geometry, bg); p.fillRect(geometry, bg);
if (!(flags & Flag::TopicJumpRipple)) { if (!(flags & Flag::TopicJumpRipple)) {
@ -342,7 +342,7 @@ void PaintRow(
(flags & Flag::AllowUserOnline) ? history : nullptr, (flags & Flag::AllowUserOnline) ? history : nullptr,
context); context);
} else if (hiddenSenderInfo) { } else if (hiddenSenderInfo) {
hiddenSenderInfo->emptyUserpic.paint( hiddenSenderInfo->emptyUserpic.paintCircle(
p, p,
context.st->padding.left(), context.st->padding.left(),
context.st->padding.top(), context.st->padding.top(),

View file

@ -38,8 +38,8 @@ using namespace ::Ui;
class VideoUserpic; class VideoUserpic;
struct TopicJumpCorners { struct TopicJumpCorners {
Ui::CornersPixmaps normal; CornersPixmaps normal;
Ui::CornersPixmaps inverted; CornersPixmaps inverted;
QPixmap small; QPixmap small;
int invertedRadius = 0; int invertedRadius = 0;
int smallKey = 0; // = `-radius` if top right else `radius`. int smallKey = 0; // = `-radius` if top right else `radius`.

View file

@ -30,7 +30,7 @@ int VideoUserpic::frameIndex() const {
void VideoUserpic::paintLeft( void VideoUserpic::paintLeft(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int w, int w,
@ -133,7 +133,7 @@ void PaintUserpic(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, Ui::VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,

View file

@ -12,10 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class Painter; class Painter;
namespace Data { namespace Data {
class CloudImageView;
class PhotoMedia; class PhotoMedia;
} // namespace Data } // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Dialogs::Ui { namespace Dialogs::Ui {
using namespace ::Ui; using namespace ::Ui;
@ -29,7 +32,7 @@ public:
void paintLeft( void paintLeft(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, PeerUserpicView &view,
int x, int x,
int y, int y,
int w, int w,
@ -54,8 +57,8 @@ private:
void PaintUserpic( void PaintUserpic(
Painter &p, Painter &p,
not_null<PeerData*> peer, not_null<PeerData*> peer,
Ui::VideoUserpic *videoUserpic, VideoUserpic *videoUserpic,
std::shared_ptr<Data::CloudImageView> &view, PeerUserpicView &view,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "editor/editor_crop.h" #include "editor/editor_crop.h"
#include "ui/userpic_view.h"
#include "styles/style_editor.h" #include "styles/style_editor.h"
#include "styles/style_basic.h" #include "styles/style_basic.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
@ -149,7 +150,7 @@ void Crop::setCropPaint(QRectF &&rect) {
_painterPath.addEllipse(_cropPaint); _painterPath.addEllipse(_cropPaint);
} else if (_data.cropType == EditorData::CropType::RoundedRect) { } else if (_data.cropType == EditorData::CropType::RoundedRect) {
const auto radius = std::min(_cropPaint.width(), _cropPaint.height()) const auto radius = std::min(_cropPaint.width(), _cropPaint.height())
* st::roundRadiusLarge / float64(st::defaultDialogRow.photoSize); * Ui::ForumUserpicRadiusMultiplier();
_painterPath.addRoundedRect(_cropPaint, radius, radius); _painterPath.addRoundedRect(_cropPaint, radius, radius);
} else { } else {
_painterPath.addRect(_cropPaint); _painterPath.addRect(_cropPaint);

View file

@ -20,10 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace AdminLog { namespace AdminLog {
namespace { namespace {
@ -64,7 +60,7 @@ private:
QRect _checkRect; QRect _checkRect;
const not_null<UserData*> _user; const not_null<UserData*> _user;
std::shared_ptr<Data::CloudImageView> _userpic; Ui::PeerUserpicView _userpic;
Ui::Text::String _name; Ui::Text::String _name;
QString _statusText; QString _statusText;
bool _statusOnline = false; bool _statusOnline = false;

View file

@ -19,10 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct ChatRestrictionsInfo; struct ChatRestrictionsInfo;
namespace Data {
class CloudImageView;
} // namespace Data
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -38,6 +34,7 @@ enum class PointState : char;
namespace Ui { namespace Ui {
class PopupMenu; class PopupMenu;
class ChatStyle; class ChatStyle;
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Window { namespace Window {
@ -278,9 +275,8 @@ private:
std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData; std::map<not_null<const HistoryItem*>, not_null<Element*>> _itemsByData;
base::flat_map<not_null<const HistoryItem*>, TimeId> _itemDates; base::flat_map<not_null<const HistoryItem*>, TimeId> _itemDates;
base::flat_set<FullMsgId> _animatedStickersPlayed; base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map< base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
not_null<PeerData*>, base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
int _itemsTop = 0; int _itemsTop = 0;
int _itemsWidth = 0; int _itemsWidth = 0;
int _itemsHeight = 0; int _itemsHeight = 0;

View file

@ -2262,7 +2262,7 @@ void History::loadUserpic() {
void History::paintUserpic( void History::paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const { const Dialogs::Ui::PaintContext &context) const {
peer->paintUserpic( peer->paintUserpic(
p, p,

View file

@ -398,7 +398,7 @@ public:
void loadUserpic() override; void loadUserpic() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
std::shared_ptr<Data::CloudImageView> &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override; const Dialogs::Ui::PaintContext &context) const override;
void refreshChatListNameSortKey(); void refreshChatListNameSortKey();

View file

@ -1135,7 +1135,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
// paint the userpic if it intersects the painted rect // paint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > clip.top()) { if (userpicTop + st::msgPhotoSize > clip.top()) {
if (const auto from = view->data()->displayFrom()) { const auto item = view->data();
if (const auto from = item->displayFrom()) {
Dialogs::Ui::PaintUserpic( Dialogs::Ui::PaintUserpic(
p, p,
from, from,
@ -1146,28 +1147,25 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
width(), width(),
st::msgPhotoSize, st::msgPhotoSize,
context.paused); context.paused);
} else if (const auto info = view->data()->hiddenSenderInfo()) { } else if (const auto info = item->hiddenSenderInfo()) {
if (info->customUserpic.empty()) { if (info->customUserpic.empty()) {
info->emptyUserpic.paint( info->emptyUserpic.paintCircle(
p, p,
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
width(), width(),
st::msgPhotoSize); st::msgPhotoSize);
} else { } else {
const auto painted = info->paintCustomUserpic( auto &userpic = _hiddenSenderUserpics[item->id];
const auto valid = info->paintCustomUserpic(
p, p,
userpic,
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
width(), width(),
st::msgPhotoSize); st::msgPhotoSize);
if (!painted) { if (!valid) {
const auto itemId = view->data()->fullId(); info->customUserpic.load(&session(), item->fullId());
auto &v = _sponsoredUserpics[itemId.msg];
if (!info->customUserpic.isCurrentView(v)) {
v = info->customUserpic.createView();
info->customUserpic.load(&session(), itemId);
}
} }
} }
} else { } else {

View file

@ -20,7 +20,6 @@ struct ClickHandlerContext;
namespace Data { namespace Data {
struct Group; struct Group;
class CloudImageView;
} // namespace Data } // namespace Data
namespace HistoryView { namespace HistoryView {
@ -51,6 +50,7 @@ class PopupMenu;
enum class ReportReason; enum class ReportReason;
struct ChatPaintContext; struct ChatPaintContext;
class PathShiftGradient; class PathShiftGradient;
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Dialogs::Ui { namespace Dialogs::Ui {
@ -458,12 +458,9 @@ private:
bool _isChatWide = false; bool _isChatWide = false;
base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed; base::flat_set<not_null<const HistoryItem*>> _animatedStickersPlayed;
base::flat_map< base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
not_null<PeerData*>, base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache; base::flat_map<MsgId, Ui::PeerUserpicView> _hiddenSenderUserpics;
base::flat_map<
MsgId,
std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics;
base::flat_map< base::flat_map<
not_null<PeerData*>, not_null<PeerData*>,
std::unique_ptr<VideoUserpic>> _videoUserpics; std::unique_ptr<VideoUserpic>> _videoUserpics;

View file

@ -144,22 +144,31 @@ ClickHandlerPtr HiddenSenderInfo::ForwardClickHandler() {
bool HiddenSenderInfo::paintCustomUserpic( bool HiddenSenderInfo::paintCustomUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,
int size) const { int size) const {
const auto view = customUserpic.activeView(); Expects(!customUserpic.empty());
if (const auto image = view ? view->image() : nullptr) {
const auto circled = Images::Option::RoundCircle; auto valid = true;
p.drawPixmap( if (!customUserpic.isCurrentView(view.cloud)) {
x, view.cloud = customUserpic.createView();
y, valid = false;
image->pix(size, size, { .options = circled }));
return true;
} else {
emptyUserpic.paint(p, x, y, outerWidth, size);
return false;
} }
const auto image = *view.cloud;
if (image.isNull()) {
emptyUserpic.paintCircle(p, x, y, outerWidth, size);
return valid;
}
Ui::ValidateUserpicCache(
view,
image.isNull() ? nullptr : &image,
image.isNull() ? &emptyUserpic : nullptr,
size * style::DevicePixelRatio(),
false);
p.drawImage(QRect(x, y, size, size), view.cached);
return valid;
} }
void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {

View file

@ -19,6 +19,7 @@ class VoiceSeekClickHandler;
namespace Ui { namespace Ui {
struct ChatPaintContext; struct ChatPaintContext;
class ChatStyle; class ChatStyle;
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Data { namespace Data {
@ -94,6 +95,7 @@ public:
[[nodiscard]] const Ui::Text::String &nameText() const; [[nodiscard]] const Ui::Text::String &nameText() const;
[[nodiscard]] bool paintCustomUserpic( [[nodiscard]] bool paintCustomUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view,
int x, int x,
int y, int y,
int outerWidth, int outerWidth,

View file

@ -139,12 +139,12 @@ rpl::producer<Ui::GroupCallBarContent> GroupCallBarContentByCall(
state->someUserpicsNotLoaded = false; state->someUserpicsNotLoaded = false;
for (auto &userpic : state->userpics) { for (auto &userpic : state->userpics) {
userpic.peer->loadUserpic(); userpic.peer->loadUserpic();
const auto pic = userpic.peer->genUserpic( auto image = userpic.peer->generateUserpicImage(
userpic.view, userpic.view,
userpicSize); userpicSize);
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view); userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({ state->current.users.push_back({
.userpic = pic.toImage(), .userpic = std::move(image),
.userpicKey = userpic.uniqueKey, .userpicKey = userpic.uniqueKey,
.id = userpic.peer->id.value, .id = userpic.peer->id.value,
.speaking = userpic.speaking, .speaking = userpic.speaking,

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/userpic_view.h"
namespace Ui { namespace Ui {
struct GroupCallBarContent; struct GroupCallBarContent;
@ -15,7 +16,6 @@ struct GroupCallBarContent;
namespace Data { namespace Data {
class GroupCall; class GroupCall;
class CloudImageView;
} // namespace Data } // namespace Data
namespace style { namespace style {
@ -27,7 +27,7 @@ namespace HistoryView {
struct UserpicInRow { struct UserpicInRow {
not_null<PeerData*> peer; not_null<PeerData*> peer;
bool speaking = false; bool speaking = false;
mutable std::shared_ptr<Data::CloudImageView> view; mutable Ui::PeerUserpicView view;
mutable InMemoryKey uniqueKey; mutable InMemoryKey uniqueKey;
}; };

View file

@ -2132,7 +2132,8 @@ void ListWidget::paintEvent(QPaintEvent *e) {
// paint the userpic if it intersects the painted rect // paint the userpic if it intersects the painted rect
if (userpicTop + st::msgPhotoSize > clip.top()) { if (userpicTop + st::msgPhotoSize > clip.top()) {
if (const auto from = view->data()->displayFrom()) { const auto item = view->data();
if (const auto from = item->displayFrom()) {
from->paintUserpicLeft( from->paintUserpicLeft(
p, p,
_userpics[from], _userpics[from],
@ -2140,28 +2141,25 @@ void ListWidget::paintEvent(QPaintEvent *e) {
userpicTop, userpicTop,
view->width(), view->width(),
st::msgPhotoSize); st::msgPhotoSize);
} else if (const auto info = view->data()->hiddenSenderInfo()) { } else if (const auto info = item->hiddenSenderInfo()) {
if (info->customUserpic.empty()) { if (info->customUserpic.empty()) {
info->emptyUserpic.paint( info->emptyUserpic.paintCircle(
p, p,
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
view->width(), view->width(),
st::msgPhotoSize); st::msgPhotoSize);
} else { } else {
const auto painted = info->paintCustomUserpic( auto &userpic = _hiddenSenderUserpics[item->id];
const auto valid = info->paintCustomUserpic(
p, p,
userpic,
st::historyPhotoLeft, st::historyPhotoLeft,
userpicTop, userpicTop,
view->width(), view->width(),
st::msgPhotoSize); st::msgPhotoSize);
if (!painted) { if (!valid) {
const auto itemId = view->data()->fullId(); info->customUserpic.load(session, item->fullId());
auto &v = _sponsoredUserpics[itemId.msg];
if (!info->customUserpic.isCurrentView(v)) {
v = info->customUserpic.createView();
info->customUserpic.load(session, itemId);
}
} }
} }
} else { } else {

View file

@ -29,6 +29,7 @@ class PopupMenu;
class ChatTheme; class ChatTheme;
struct ChatPaintContext; struct ChatPaintContext;
enum class TouchScrollState; enum class TouchScrollState;
struct PeerUserpicView;
} // namespace Ui } // namespace Ui
namespace Window { namespace Window {
@ -37,7 +38,6 @@ class SessionController;
namespace Data { namespace Data {
struct Group; struct Group;
class CloudImageView;
struct Reaction; struct Reaction;
struct AllowedReactions; struct AllowedReactions;
} // namespace Data } // namespace Data
@ -637,12 +637,9 @@ private:
ItemRevealAnimation> _itemRevealAnimations; ItemRevealAnimation> _itemRevealAnimations;
int _itemsRevealHeight = 0; int _itemsRevealHeight = 0;
base::flat_set<FullMsgId> _animatedStickersPlayed; base::flat_set<FullMsgId> _animatedStickersPlayed;
base::flat_map< base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpics;
not_null<PeerData*>, base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> _userpicsCache;
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache; base::flat_map<MsgId, Ui::PeerUserpicView> _hiddenSenderUserpics;
base::flat_map<
MsgId,
std::shared_ptr<Data::CloudImageView>> _sponsoredUserpics;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient; const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;

View file

@ -1105,10 +1105,10 @@ void Message::paintCommentsButton(
auto &entry = list[i]; auto &entry = list[i];
const auto peer = entry.peer; const auto peer = entry.peer;
auto &view = entry.view; auto &view = entry.view;
const auto wasView = view.get(); const auto wasView = view.cloud.get();
if (views->recentRepliers[i] != peer->id if (views->recentRepliers[i] != peer->id
|| peer->userpicUniqueKey(view) != entry.uniqueKey || peer->userpicUniqueKey(view) != entry.uniqueKey
|| view.get() != wasView) { || view.cloud.get() != wasView) {
return true; return true;
} }
} }

View file

@ -86,12 +86,12 @@ rpl::producer<Ui::RequestsBarContent> RequestsBarContentByPeer(
state->someUserpicsNotLoaded = false; state->someUserpicsNotLoaded = false;
for (auto &userpic : state->userpics) { for (auto &userpic : state->userpics) {
userpic.peer->loadUserpic(); userpic.peer->loadUserpic();
const auto pic = userpic.peer->genUserpic( auto image = userpic.peer->generateUserpicImage(
userpic.view, userpic.view,
userpicSize); userpicSize);
userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view); userpic.uniqueKey = userpic.peer->userpicUniqueKey(userpic.view);
state->current.users.push_back({ state->current.users.push_back({
.userpic = pic.toImage(), .userpic = std::move(image),
.userpicKey = userpic.uniqueKey, .userpicKey = userpic.uniqueKey,
.id = userpic.peer->id.value, .id = userpic.peer->id.value,
}); });

View file

@ -96,8 +96,8 @@ Contact::Contact(
Contact::~Contact() { Contact::~Contact() {
history()->owner().unregisterContactView(_userId, _parent); history()->owner().unregisterContactView(_userId, _parent);
if (_userpic) { if (!_userpic.null()) {
_userpic = nullptr; _userpic = {};
_parent->checkHeavyPart(); _parent->checkHeavyPart();
} }
} }
@ -177,13 +177,13 @@ void Contact::draw(Painter &p, const PaintContext &context) const {
if (_userId) { if (_userId) {
QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw)); QRect rthumb(style::rtlrect(st.padding.left(), st.padding.top() - topMinus, st.thumbSize, st.thumbSize, paintw));
if (_contact) { if (_contact) {
const auto was = (_userpic != nullptr); const auto was = !_userpic.null();
_contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st.thumbSize); _contact->paintUserpic(p, _userpic, rthumb.x(), rthumb.y(), st.thumbSize);
if (!was && _userpic) { if (!was && !_userpic.null()) {
history()->owner().registerHeavyViewPart(_parent); history()->owner().registerHeavyViewPart(_parent);
} }
} else { } else {
_photoEmpty->paint(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize); _photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
} }
if (context.selected()) { if (context.selected()) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
@ -197,7 +197,7 @@ void Contact::draw(Painter &p, const PaintContext &context) const {
p.setPen(stm->msgFileThumbLinkFg); p.setPen(stm->msgFileThumbLinkFg);
p.drawTextLeft(nameleft, linktop, paintw, _link, _linkw); p.drawTextLeft(nameleft, linktop, paintw, _link, _linkw);
} else { } else {
_photoEmpty->paint(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize); _photoEmpty->paintCircle(p, st.padding.left(), st.padding.top() - topMinus, paintw, st.thumbSize);
} }
const auto namewidth = paintw - nameleft - nameright; const auto namewidth = paintw - nameleft - nameright;
@ -232,11 +232,11 @@ TextState Contact::textState(QPoint point, StateRequest request) const {
} }
void Contact::unloadHeavyPart() { void Contact::unloadHeavyPart() {
_userpic = nullptr; _userpic = {};
} }
bool Contact::hasHeavyPart() const { bool Contact::hasHeavyPart() const {
return (_userpic != nullptr); return !_userpic.null();
} }
} // namespace HistoryView } // namespace HistoryView

View file

@ -8,10 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "history/view/media/history_view_media.h" #include "history/view/media/history_view_media.h"
#include "ui/userpic_view.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class EmptyUserpic; class EmptyUserpic;
@ -72,7 +69,7 @@ private:
QString _fname, _lname, _phone; QString _fname, _lname, _phone;
Ui::Text::String _name; Ui::Text::String _name;
std::unique_ptr<Ui::EmptyUserpic> _photoEmpty; std::unique_ptr<Ui::EmptyUserpic> _photoEmpty;
mutable std::shared_ptr<Data::CloudImageView> _userpic; mutable Ui::PeerUserpicView _userpic;
ClickHandlerPtr _linkl; ClickHandlerPtr _linkl;
int _linkw = 0; int _linkw = 0;

View file

@ -245,17 +245,16 @@ void Location::draw(Painter &p, const PaintContext &context) const {
void Location::validateImageCache( void Location::validateImageCache(
QSize outer, QSize outer,
Ui::BubbleRounding rounding) const { Ui::BubbleRounding rounding) const {
Expects(_media != nullptr);
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
if (_imageCache.size() == (outer * ratio) if (_imageCache.size() == (outer * ratio)
&& _imageCacheRounding == rounding) { && _imageCacheRounding == rounding
return; || _media->isNull()) {
}
const auto thumbnail = _media->image();
if (!thumbnail) {
return; return;
} }
_imageCache = Images::Round( _imageCache = Images::Round(
thumbnail->original().scaled( _media->scaled(
outer * ratio, outer * ratio,
Qt::IgnoreAspectRatio, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation), Qt::SmoothTransformation),

View file

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
class CloudImage; class CloudImage;
class CloudImageView;
} // namespace Data } // namespace Data
namespace HistoryView { namespace HistoryView {
@ -81,7 +80,7 @@ private:
[[nodiscard]] int fullHeight() const; [[nodiscard]] int fullHeight() const;
const not_null<Data::CloudImage*> _data; const not_null<Data::CloudImage*> _data;
mutable std::shared_ptr<Data::CloudImageView> _media; mutable std::shared_ptr<QImage> _media;
Ui::Text::String _title, _description; Ui::Text::String _title, _description;
ClickHandlerPtr _link; ClickHandlerPtr _link;

View file

@ -46,6 +46,7 @@ struct Photo::Streamed {
::Media::Streaming::Instance instance; ::Media::Streaming::Instance instance;
::Media::Streaming::FrameRequest frozenRequest; ::Media::Streaming::FrameRequest frozenRequest;
QImage frozenFrame; QImage frozenFrame;
std::array<QImage, 4> roundingCorners;
QImage roundingMask; QImage roundingMask;
}; };
@ -352,22 +353,66 @@ void Photo::draw(Painter &p, const PaintContext &context) const {
} }
} }
void Photo::validateUserpicImageCache(QSize size, bool forum) const {
const auto forumValue = forum ? 1 : 0;
const auto large = _dataMedia->image(PhotoSize::Large);
const auto ratio = style::DevicePixelRatio();
const auto blurredValue = large ? 0 : 1;
if (_imageCache.size() == (size * ratio)
&& _imageCacheForum == forumValue
&& _imageCacheBlurred == blurredValue) {
return;
}
auto original = [&] {
if (large) {
return large->original();
} else if (const auto thumbnail = _dataMedia->image(
PhotoSize::Thumbnail)) {
return thumbnail->original();
} else if (const auto small = _dataMedia->image(
PhotoSize::Small)) {
return small->original();
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
return blurred->original();
} else {
return Image::Empty()->original();
}
}();
auto args = Images::PrepareArgs();
if (blurredValue) {
args = args.blurred();
}
original = Images::Prepare(std::move(original), size, args);
if (forumValue) {
original = Images::Round(
std::move(original),
Images::CornersMask(std::min(size.width(), size.height())
* Ui::ForumUserpicRadiusMultiplier()
* style::DevicePixelRatio()));
} else {
original = Images::Circle(std::move(original));
}
_imageCache = std::move(original);
_imageCacheForum = forumValue;
_imageCacheBlurred = blurredValue;
}
void Photo::validateImageCache( void Photo::validateImageCache(
QSize outer, QSize outer,
std::optional<Ui::BubbleRounding> rounding) const { std::optional<Ui::BubbleRounding> rounding) const {
const auto large = _dataMedia->image(PhotoSize::Large); const auto large = _dataMedia->image(PhotoSize::Large);
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
const auto shouldBeBlurred = !large; const auto blurredValue = large ? 0 : 1;
if (_imageCache.size() == (outer * ratio) if (_imageCache.size() == (outer * ratio)
&& _imageCacheRounding == rounding && _imageCacheRounding == rounding
&& _imageCacheBlurred == shouldBeBlurred) { && _imageCacheBlurred == blurredValue) {
return; return;
} }
_imageCache = Images::Round( _imageCache = Images::Round(
prepareImageCache(outer), prepareImageCache(outer),
MediaRoundingMask(rounding)); MediaRoundingMask(rounding));
_imageCacheRounding = rounding; _imageCacheRounding = rounding;
_imageCacheBlurred = shouldBeBlurred; _imageCacheBlurred = blurredValue;
} }
QImage Photo::prepareImageCache(QSize outer) const { QImage Photo::prepareImageCache(QSize outer) const {
@ -405,17 +450,23 @@ void Photo::paintUserpicFrame(
const auto rect = QRect(photoPosition, size); const auto rect = QRect(photoPosition, size);
const auto st = context.st; const auto st = context.st;
const auto sti = context.imageStyle(); const auto sti = context.imageStyle();
const auto forum = _parent->data()->history()->isForum();
if (_streamed if (_streamed
&& _streamed->instance.player().ready() && _streamed->instance.player().ready()
&& !_streamed->instance.player().videoSize().isEmpty()) { && !_streamed->instance.player().videoSize().isEmpty()) {
const auto ratio = style::DevicePixelRatio();
auto request = ::Media::Streaming::FrameRequest(); auto request = ::Media::Streaming::FrameRequest();
request.outer = size * cIntRetinaFactor(); request.outer = request.resize = size * ratio;
request.resize = size * cIntRetinaFactor();
const auto forum = _parent->data()->history()->isForum();
if (forum) { if (forum) {
const auto radius = int(std::min(size.width(), size.height())
* Ui::ForumUserpicRadiusMultiplier()
* ratio);
if (_streamed->roundingCorners[0].width() != radius) {
_streamed->roundingCorners = Images::CornersMask(radius);
}
request.rounding = Images::CornersMaskRef( request.rounding = Images::CornersMaskRef(
Images::CornersMask(ImageRoundRadius::Large)); _streamed->roundingCorners);
} else { } else {
if (_streamed->roundingMask.size() != request.outer) { if (_streamed->roundingMask.size() != request.outer) {
_streamed->roundingMask = Images::EllipseMask(size); _streamed->roundingMask = Images::EllipseMask(size);
@ -438,28 +489,8 @@ void Photo::paintUserpicFrame(
} }
return; return;
} }
const auto pix = [&] { validateUserpicImageCache(size, forum);
const auto forum = _parent->data()->history()->isForum(); p.drawImage(rect, _imageCache);
const auto args = Images::PrepareArgs{
.options = (forum
? Images::Option::RoundLarge
: Images::Option::RoundCircle),
};
if (const auto large = _dataMedia->image(PhotoSize::Large)) {
return large->pix(size, args);
} else if (const auto thumbnail = _dataMedia->image(
PhotoSize::Thumbnail)) {
return thumbnail->pix(size, args.blurred());
} else if (const auto small = _dataMedia->image(
PhotoSize::Small)) {
return small->pix(size, args.blurred());
} else if (const auto blurred = _dataMedia->thumbnailInline()) {
return blurred->pix(size, args.blurred());
} else {
return QPixmap();
}
}();
p.drawPixmap(rect, pix);
if (_data->videoCanBePlayed() && !_streamed) { if (_data->videoCanBePlayed() && !_streamed) {
const auto innerSize = st::msgFileLayout.thumbSize; const auto innerSize = st::msgFileLayout.thumbSize;

View file

@ -129,6 +129,7 @@ private:
void validateImageCache( void validateImageCache(
QSize outer, QSize outer,
std::optional<Ui::BubbleRounding> rounding) const; std::optional<Ui::BubbleRounding> rounding) const;
void validateUserpicImageCache(QSize size, bool forum) const;
[[nodiscard]] QImage prepareImageCache(QSize outer) const; [[nodiscard]] QImage prepareImageCache(QSize outer) const;
bool videoAutoplayEnabled() const; bool videoAutoplayEnabled() const;
@ -150,8 +151,9 @@ private:
mutable std::unique_ptr<Streamed> _streamed; mutable std::unique_ptr<Streamed> _streamed;
mutable QImage _imageCache; mutable QImage _imageCache;
mutable std::optional<Ui::BubbleRounding> _imageCacheRounding; mutable std::optional<Ui::BubbleRounding> _imageCacheRounding;
int _serviceWidth = 0; int _serviceWidth : 30 = 0;
mutable bool _imageCacheBlurred = false; mutable int _imageCacheForum : 1 = 0;
mutable int _imageCacheBlurred : 1 = 0;
}; };

View file

@ -181,6 +181,11 @@ struct Poll::CloseInformation {
Ui::Animations::Basic radial; Ui::Animations::Basic radial;
}; };
struct Poll::RecentVoter {
not_null<UserData*> user;
mutable Ui::PeerUserpicView userpic;
};
template <typename Callback> template <typename Callback>
Poll::SendingAnimation::SendingAnimation( Poll::SendingAnimation::SendingAnimation(
const QByteArray &option, const QByteArray &option,
@ -886,9 +891,9 @@ void Poll::paintRecentVoters(
auto created = false; auto created = false;
for (auto &recent : _recentVoters) { for (auto &recent : _recentVoters) {
const auto was = (recent.userpic != nullptr); const auto was = !recent.userpic.null();
recent.user->paintUserpic(p, recent.userpic, x, y, size); recent.user->paintUserpic(p, recent.userpic, x, y, size);
if (!was && recent.userpic) { if (!was && !recent.userpic.null()) {
created = true; created = true;
} }
const auto paintContent = [&](QPainter &p) { const auto paintContent = [&](QPainter &p) {
@ -1499,13 +1504,13 @@ void Poll::clickHandlerPressedChanged(
void Poll::unloadHeavyPart() { void Poll::unloadHeavyPart() {
for (auto &recent : _recentVoters) { for (auto &recent : _recentVoters) {
recent.userpic = nullptr; recent.userpic = {};
} }
} }
bool Poll::hasHeavyPart() const { bool Poll::hasHeavyPart() const {
for (auto &recent : _recentVoters) { for (auto &recent : _recentVoters) {
if (recent.userpic) { if (!recent.userpic.null()) {
return true; return true;
} }
} }

View file

@ -12,10 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h" #include "data/data_poll.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
class FireworksAnimation; class FireworksAnimation;
@ -75,11 +71,7 @@ private:
struct SendingAnimation; struct SendingAnimation;
struct Answer; struct Answer;
struct CloseInformation; struct CloseInformation;
struct RecentVoter;
struct RecentVoter {
not_null<UserData*> user;
mutable std::shared_ptr<Data::CloudImageView> userpic;
};
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;

View file

@ -524,9 +524,9 @@ void InlineList::resolveUserpicsImage(const Button &button) const {
for (auto &entry : userpics->list) { for (auto &entry : userpics->list) {
const auto peer = entry.peer; const auto peer = entry.peer;
auto &view = entry.view; auto &view = entry.view;
const auto wasView = view.get(); const auto wasView = view.cloud.get();
if (peer->userpicUniqueKey(view) != entry.uniqueKey if (peer->userpicUniqueKey(view) != entry.uniqueKey
|| view.get() != wasView) { || view.cloud.get() != wasView) {
return true; return true;
} }
} }

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_message_reaction_id.h" #include "data/data_message_reaction_id.h"
namespace Data { namespace Data {
class CloudImageView;
class Reactions; class Reactions;
} // namespace Data } // namespace Data

View file

@ -879,9 +879,17 @@ void Video::prepareThumbnail(QSize size) const {
} }
} }
} }
auto resultThumbnailImage = _documentMedia
? nullptr
: getResultThumb(fileOrigin());
const auto resultThumbnail = Image(resultThumbnailImage
? base::duplicate(*resultThumbnailImage)
: QImage());
const auto thumb = _documentMedia const auto thumb = _documentMedia
? _documentMedia->thumbnail() ? _documentMedia->thumbnail()
: getResultThumb(fileOrigin()); : resultThumbnailImage
? &resultThumbnail
: nullptr;
if (!thumb) { if (!thumb) {
return; return;
} }
@ -1245,7 +1253,7 @@ void Contact::prepareThumbnail(int width, int height) const {
w = width; w = width;
} }
} }
_thumb = thumb->pixNoCache( _thumb = Image(base::duplicate(*thumb)).pixNoCache(
QSize(w, h) * style::DevicePixelRatio(), QSize(w, h) * style::DevicePixelRatio(),
{ {
.options = Images::Option::TransparentBackground, .options = Images::Option::TransparentBackground,
@ -1403,7 +1411,7 @@ void Article::prepareThumbnail(int width, int height) const {
w = width; w = width;
} }
} }
_thumb = thumb->pixNoCache( _thumb = Image(base::duplicate(*thumb)).pixNoCache(
QSize(w, h) * style::DevicePixelRatio(), QSize(w, h) * style::DevicePixelRatio(),
{ {
.options = Images::Option::TransparentBackground, .options = Images::Option::TransparentBackground,

View file

@ -142,7 +142,7 @@ bool ItemBase::hasResultThumb() const {
|| !_result->_locationThumbnail.empty()); || !_result->_locationThumbnail.empty());
} }
Image *ItemBase::getResultThumb(Data::FileOrigin origin) const { QImage *ItemBase::getResultThumb(Data::FileOrigin origin) const {
if (_result && !_thumbnail) { if (_result && !_thumbnail) {
if (!_result->_thumbnail.empty()) { if (!_result->_thumbnail.empty()) {
_thumbnail = _result->_thumbnail.createView(); _thumbnail = _result->_thumbnail.createView();
@ -152,7 +152,9 @@ Image *ItemBase::getResultThumb(Data::FileOrigin origin) const {
_result->_locationThumbnail.load(_result->_session, origin); _result->_locationThumbnail.load(_result->_session, origin);
} }
} }
return _thumbnail->image(); return (_thumbnail && !_thumbnail->isNull())
? _thumbnail.get()
: nullptr;
} }
QPixmap ItemBase::getResultContactAvatar(int width, int height) const { QPixmap ItemBase::getResultContactAvatar(int width, int height) const {

View file

@ -16,10 +16,6 @@ namespace Ui {
class PathShiftGradient; class PathShiftGradient;
} // namespace Ui } // namespace Ui
namespace Data {
class CloudImageView;
} // namespace Data
namespace InlineBots { namespace InlineBots {
class Result; class Result;
@ -126,7 +122,7 @@ protected:
DocumentData *getResultDocument() const; DocumentData *getResultDocument() const;
PhotoData *getResultPhoto() const; PhotoData *getResultPhoto() const;
bool hasResultThumb() const; bool hasResultThumb() const;
Image *getResultThumb(Data::FileOrigin origin) const; QImage *getResultThumb(Data::FileOrigin origin) const;
QPixmap getResultContactAvatar(int width, int height) const; QPixmap getResultContactAvatar(int width, int height) const;
int getResultDuration() const; int getResultDuration() const;
QString getResultUrl() const; QString getResultUrl() const;
@ -148,7 +144,7 @@ protected:
private: private:
not_null<Context*> _context; not_null<Context*> _context;
mutable std::shared_ptr<Data::CloudImageView> _thumbnail; mutable std::shared_ptr<QImage> _thumbnail;
}; };

View file

@ -110,8 +110,9 @@ Session::Session(
_user, _user,
Data::PeerUpdate::Flag::Photo Data::PeerUpdate::Flag::Photo
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
[[maybe_unused]] const auto image = _user->currentUserpic( auto view = Ui::PeerUserpicView{ .cloud = _selfUserpicView };
_selfUserpicView); [[maybe_unused]] const auto image = _user->userpicCloudImage(view);
_selfUserpicView = view.cloud;
}, lifetime()); }, lifetime());
crl::on_main(this, [=] { crl::on_main(this, [=] {

View file

@ -32,7 +32,6 @@ class Templates;
namespace Data { namespace Data {
class Session; class Session;
class Changes; class Changes;
class CloudImageView;
} // namespace Data } // namespace Data
namespace Storage { namespace Storage {
@ -216,7 +215,7 @@ private:
const std::unique_ptr<Support::Helper> _supportHelper; const std::unique_ptr<Support::Helper> _supportHelper;
std::shared_ptr<Data::CloudImageView> _selfUserpicView; std::shared_ptr<QImage> _selfUserpicView;
rpl::variable<bool> _premiumPossible = false; rpl::variable<bool> _premiumPossible = false;
rpl::event_stream<bool> _termsLockChanges; rpl::event_stream<bool> _termsLockChanges;

View file

@ -2827,14 +2827,16 @@ void OverlayWidget::initStreamingThumbnail() {
_touchbarDisplay.fire(TouchBarItemType::Video); _touchbarDisplay.fire(TouchBarItemType::Video);
auto userpicImage = std::optional<Image>();
const auto computePhotoThumbnail = [&] { const auto computePhotoThumbnail = [&] {
const auto thumbnail = _photoMedia->image(Data::PhotoSize::Thumbnail); const auto thumbnail = _photoMedia->image(Data::PhotoSize::Thumbnail);
if (thumbnail) { if (thumbnail) {
return thumbnail; return thumbnail;
} else if (_peer && _peer->userpicPhotoId() == _photo->id) { } else if (_peer && _peer->userpicPhotoId() == _photo->id) {
if (const auto view = _peer->activeUserpicView()) { if (const auto view = _peer->activeUserpicView(); view.cloud) {
if (const auto image = view->image()) { if (!view.cloud->isNull()) {
return image; userpicImage.emplace(base::duplicate(*view.cloud));
return &*userpicImage;
} }
} }
} }
@ -3455,8 +3457,11 @@ void OverlayWidget::validatePhotoCurrentImage() {
&& !_message && !_message
&& _peer && _peer
&& _peer->hasUserpic()) { && _peer->hasUserpic()) {
if (const auto view = _peer->activeUserpicView()) { if (const auto view = _peer->activeUserpicView(); view.cloud) {
validatePhotoImage(view->image(), true); if (!view.cloud->isNull()) {
auto image = Image(base::duplicate(*view.cloud));
validatePhotoImage(&image, true);
}
} }
} }
if (_staticContent.isNull()) { if (_staticContent.isNull()) {

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "base/weak_ptr.h" #include "base/weak_ptr.h"
#include "window/notifications_utilities.h"
#include "styles/style_window.h" #include "styles/style_window.h"
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
@ -906,7 +907,7 @@ public:
void showNotification( void showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -1013,7 +1014,7 @@ Manager::Private::Private(not_null<Manager*> manager)
void Manager::Private::showNotification( void Manager::Private::showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -1041,12 +1042,8 @@ void Manager::Private::showNotification(
} }
if (!options.hideNameAndPhoto) { if (!options.hideNameAndPhoto) {
const auto userpic = peer->isSelf() notification->setImage(
? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize) Window::Notifications::GenerateUserpic(peer, userpicView));
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->genUserpic(userpicView, st::notifyMacPhotoSize);
notification->setImage(userpic.toImage());
} }
auto i = _notifications.find(key); auto i = _notifications.find(key);
@ -1183,7 +1180,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification( void Manager::doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -22,7 +22,7 @@ protected:
void doShowNativeNotification( void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -22,7 +22,7 @@ protected:
void doShowNativeNotification( void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "window/notification_utilities.h"
#include "styles/style_window.h" #include "styles/style_window.h"
#include <thread> #include <thread>
@ -189,7 +190,7 @@ public:
void showNotification( void showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -265,7 +266,7 @@ Manager::Private::Private(Manager *manager)
void Manager::Private::showNotification( void Manager::Private::showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -302,12 +303,8 @@ void Manager::Private::showNotification(
[notification setInformativeText:Q2NSString(msg)]; [notification setInformativeText:Q2NSString(msg)];
if (!options.hideNameAndPhoto if (!options.hideNameAndPhoto
&& [notification respondsToSelector:@selector(setContentImage:)]) { && [notification respondsToSelector:@selector(setContentImage:)]) {
auto userpic = peer->isSelf() NSImage *img = Q2NSImage(
? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize) Window::Notifications::GenerateUserpic(peer, userpicView));
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->genUserpic(userpicView, st::notifyMacPhotoSize);
NSImage *img = Q2NSImage(userpic.toImage());
[notification setContentImage:img]; [notification setContentImage:img];
} }
@ -475,7 +472,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification( void Manager::doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -84,7 +84,7 @@ QImage ArchiveUserpic(not_null<Data::Folder*> folder) {
auto result = PrepareImage(); auto result = PrepareImage();
Painter paint(&result); Painter paint(&result);
auto view = std::shared_ptr<Data::CloudImageView>(); auto view = Ui::PeerUserpicView();
folder->paintUserpic(paint, 0, 0, result.width()); folder->paintUserpic(paint, 0, 0, result.width());
return result; return result;
} }
@ -161,7 +161,7 @@ TimeId CalculateOnlineTill(not_null<PeerData*> peer) {
@implementation PinnedDialogsPanel { @implementation PinnedDialogsPanel {
struct Pin { struct Pin {
PeerData *peer = nullptr; PeerData *peer = nullptr;
std::shared_ptr<Data::CloudImageView> userpicView = nullptr; Ui::PeerUserpicView userpicView;
int index = -1; int index = -1;
QImage userpic; QImage userpic;
QImage unreadBadge; QImage unreadBadge;

View file

@ -405,15 +405,13 @@ void Create(Window::Notifications::System *system) {
#ifndef __MINGW32__ #ifndef __MINGW32__
class Manager::Private { class Manager::Private {
public: public:
using Type = Window::Notifications::CachedUserpics::Type; explicit Private(Manager *instance);
explicit Private(Manager *instance, Type type);
bool init(); bool init();
bool showNotification( bool showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -438,7 +436,7 @@ private:
bool showNotificationInTryCatch( bool showNotificationInTryCatch(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -460,9 +458,8 @@ private:
}; };
Manager::Private::Private(Manager *instance, Type type) Manager::Private::Private(Manager *instance)
: _cachedUserpics(type) : _guarded(std::make_shared<Manager*>(instance)) {
, _guarded(std::make_shared<Manager*>(instance)) {
ToastActivations( ToastActivations(
) | rpl::start_with_next([=](const ToastActivation &activation) { ) | rpl::start_with_next([=](const ToastActivation &activation) {
handleActivation(activation); handleActivation(activation);
@ -657,7 +654,7 @@ void Manager::Private::handleActivation(const ToastActivation &activation) {
bool Manager::Private::showNotification( bool Manager::Private::showNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -692,7 +689,7 @@ std::wstring Manager::Private::ensureSendButtonIcon() {
bool Manager::Private::showNotificationInTryCatch( bool Manager::Private::showNotificationInTryCatch(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -870,7 +867,7 @@ void Manager::Private::tryHide(const ToastNotification &notification) {
Manager::Manager(Window::Notifications::System *system) Manager::Manager(Window::Notifications::System *system)
: NativeManager(system) : NativeManager(system)
, _private(std::make_unique<Private>(this, Private::Type::Rounded)) { , _private(std::make_unique<Private>(this)) {
} }
bool Manager::init() { bool Manager::init() {
@ -890,7 +887,7 @@ Manager::~Manager() = default;
void Manager::doShowNativeNotification( void Manager::doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -30,7 +30,7 @@ protected:
void doShowNativeNotification( void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -8,10 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "profile/profile_block_widget.h" #include "profile/profile_block_widget.h"
#include "ui/userpic_view.h"
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
@ -33,7 +30,7 @@ public:
~Item(); ~Item();
const not_null<PeerData*> peer; const not_null<PeerData*> peer;
std::shared_ptr<Data::CloudImageView> userpic; Ui::PeerUserpicView userpic;
Ui::Text::String name; Ui::Text::String name;
QString statusText; QString statusText;
bool statusHasOnlineColor = false; bool statusHasOnlineColor = false;

View file

@ -644,7 +644,7 @@ void SetupAccountsWrap(
} }
Ui::RpWidget userpic; Ui::RpWidget userpic;
std::shared_ptr<Data::CloudImageView> view; Ui::PeerUserpicView view;
base::unique_qptr<Ui::PopupMenu> menu; base::unique_qptr<Ui::PopupMenu> menu;
}; };
const auto state = raw->lifetime().make_state<State>(raw); const auto state = raw->lifetime().make_state<State>(raw);

View file

@ -360,7 +360,7 @@ RoundImageCheckbox::RoundImageCheckbox(
const style::RoundImageCheckbox &st, const style::RoundImageCheckbox &st,
Fn<void()> updateCallback, Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage, PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius) Fn<std::optional<int>(int size)> roundingRadius)
: _st(st) : _st(st)
, _updateCallback(updateCallback) , _updateCallback(updateCallback)
, _paintRoundImage(std::move(paintRoundImage)) , _paintRoundImage(std::move(paintRoundImage))
@ -390,8 +390,8 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
if (selectionLevel > 0) { if (selectionLevel > 0) {
const auto radius = _roundingRadius const auto radius = _roundingRadius
? _roundingRadius() ? _roundingRadius(_st.imageRadius)
: ImageRoundRadius::Ellipse; : std::optional<int>();
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.setOpacity(std::clamp(selectionLevel, 0., 1.)); p.setOpacity(std::clamp(selectionLevel, 0., 1.));
p.setBrush(Qt::NoBrush); p.setBrush(Qt::NoBrush);
@ -405,11 +405,10 @@ void RoundImageCheckbox::paint(Painter &p, int x, int y, int outerWidth) const {
_st.imageRadius * 2, _st.imageRadius * 2,
_st.imageRadius * 2, _st.imageRadius * 2,
outerWidth); outerWidth);
if (radius == ImageRoundRadius::Ellipse) { if (!radius) {
p.drawEllipse(rect); p.drawEllipse(rect);
} else { } else {
const auto pxRadius = st::roundRadiusLarge; p.drawRoundedRect(rect, *radius, *radius);
p.drawRoundedRect(rect, pxRadius, pxRadius);
} }
p.setOpacity(1.); p.setOpacity(1.);
} }

View file

@ -52,7 +52,7 @@ public:
const style::RoundImageCheckbox &st, const style::RoundImageCheckbox &st,
Fn<void()> updateCallback, Fn<void()> updateCallback,
PaintRoundImage &&paintRoundImage, PaintRoundImage &&paintRoundImage,
Fn<ImageRoundRadius()> roundingRadius = nullptr); Fn<std::optional<int>(int size)> roundingRadius = nullptr);
void paint(Painter &p, int x, int y, int outerWidth) const; void paint(Painter &p, int x, int y, int outerWidth) const;
float64 checkedAnimationRatio() const; float64 checkedAnimationRatio() const;
@ -76,7 +76,7 @@ private:
const style::RoundImageCheckbox &_st; const style::RoundImageCheckbox &_st;
Fn<void()> _updateCallback; Fn<void()> _updateCallback;
PaintRoundImage _paintRoundImage; PaintRoundImage _paintRoundImage;
Fn<ImageRoundRadius()> _roundingRadius; Fn<std::optional<int>(int size)> _roundingRadius;
QPixmap _wideCache; QPixmap _wideCache;
Ui::Animations::Simple _selection; Ui::Animations::Simple _selection;

View file

@ -193,7 +193,7 @@ void PaintInaccessibleAccountInner(
} }
} }
[[nodiscard]] QPixmap Generate(int size, Fn<void(QPainter&)> callback) { [[nodiscard]] QImage Generate(int size, Fn<void(QPainter&)> callback) {
auto result = QImage( auto result = QImage(
QSize(size, size) * style::DevicePixelRatio(), QSize(size, size) * style::DevicePixelRatio(),
QImage::Format_ARGB32_Premultiplied); QImage::Format_ARGB32_Premultiplied);
@ -203,7 +203,7 @@ void PaintInaccessibleAccountInner(
Painter p(&result); Painter p(&result);
callback(p); callback(p);
} }
return Ui::PixmapFromImage(std::move(result)); return result;
} }
} // namespace } // namespace
@ -286,7 +286,7 @@ void EmptyUserpic::paint(
} }
} }
void EmptyUserpic::paint( void EmptyUserpic::paintCircle(
QPainter &p, QPainter &p,
int x, int x,
int y, int y,
@ -304,9 +304,6 @@ void EmptyUserpic::paintRounded(
int outerWidth, int outerWidth,
int size, int size,
int radius) const { int radius) const {
if (!radius) {
radius = st::roundRadiusSmall;
}
paint(p, x, y, outerWidth, size, [&] { paint(p, x, y, outerWidth, size, [&] {
p.drawRoundedRect(x, y, size, size, radius, radius); p.drawRoundedRect(x, y, size, size, radius, radius);
}); });
@ -338,21 +335,6 @@ void EmptyUserpic::PaintSavedMessages(
PaintSavedMessages(p, x, y, outerWidth, size, QBrush(bg), fg); PaintSavedMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
} }
void EmptyUserpic::PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size) {
auto bg = QLinearGradient(x, y, x, y + size);
bg.setStops({
{ 0., st::historyPeerSavedMessagesBg->c },
{ 1., st::historyPeerSavedMessagesBg2->c }
});
const auto &fg = st::historyPeerUserpicFg;
PaintSavedMessagesRounded(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintSavedMessages( void EmptyUserpic::PaintSavedMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -371,42 +353,12 @@ void EmptyUserpic::PaintSavedMessages(
PaintSavedMessagesInner(p, x, y, size, fg); PaintSavedMessagesInner(p, x, y, size, fg);
} }
void EmptyUserpic::PaintSavedMessagesRounded( QImage EmptyUserpic::GenerateSavedMessages(int size) {
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg) {
x = style::RightToLeft() ? (outerWidth - x - size) : x;
PainterHighQualityEnabler hq(p);
p.setBrush(std::move(bg));
p.setPen(Qt::NoPen);
p.drawRoundedRect(
x,
y,
size,
size,
st::roundRadiusSmall,
st::roundRadiusSmall);
PaintSavedMessagesInner(p, x, y, size, fg);
}
QPixmap EmptyUserpic::GenerateSavedMessages(int size) {
return Generate(size, [&](QPainter &p) { return Generate(size, [&](QPainter &p) {
PaintSavedMessages(p, 0, 0, size, size); PaintSavedMessages(p, 0, 0, size, size);
}); });
} }
QPixmap EmptyUserpic::GenerateSavedMessagesRounded(int size) {
return Generate(size, [&](QPainter &p) {
PaintSavedMessagesRounded(p, 0, 0, size, size);
});
}
void EmptyUserpic::PaintRepliesMessages( void EmptyUserpic::PaintRepliesMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -422,21 +374,6 @@ void EmptyUserpic::PaintRepliesMessages(
PaintRepliesMessages(p, x, y, outerWidth, size, QBrush(bg), fg); PaintRepliesMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
} }
void EmptyUserpic::PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size) {
auto bg = QLinearGradient(x, y, x, y + size);
bg.setStops({
{ 0., st::historyPeerSavedMessagesBg->c },
{ 1., st::historyPeerSavedMessagesBg2->c }
});
const auto &fg = st::historyPeerUserpicFg;
PaintRepliesMessagesRounded(p, x, y, outerWidth, size, QBrush(bg), fg);
}
void EmptyUserpic::PaintRepliesMessages( void EmptyUserpic::PaintRepliesMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -455,42 +392,12 @@ void EmptyUserpic::PaintRepliesMessages(
PaintRepliesMessagesInner(p, x, y, size, fg); PaintRepliesMessagesInner(p, x, y, size, fg);
} }
void EmptyUserpic::PaintRepliesMessagesRounded( QImage EmptyUserpic::GenerateRepliesMessages(int size) {
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg) {
x = style::RightToLeft() ? (outerWidth - x - size) : x;
PainterHighQualityEnabler hq(p);
p.setBrush(bg);
p.setPen(Qt::NoPen);
p.drawRoundedRect(
x,
y,
size,
size,
st::roundRadiusSmall,
st::roundRadiusSmall);
PaintRepliesMessagesInner(p, x, y, size, fg);
}
QPixmap EmptyUserpic::GenerateRepliesMessages(int size) {
return Generate(size, [&](QPainter &p) { return Generate(size, [&](QPainter &p) {
PaintRepliesMessages(p, 0, 0, size, size); PaintRepliesMessages(p, 0, 0, size, size);
}); });
} }
QPixmap EmptyUserpic::GenerateRepliesMessagesRounded(int size) {
return Generate(size, [&](QPainter &p) {
PaintRepliesMessagesRounded(p, 0, 0, size, size);
});
}
std::pair<uint64, uint64> EmptyUserpic::uniqueKey() const { std::pair<uint64, uint64> EmptyUserpic::uniqueKey() const {
const auto first = (uint64(0xFFFFFFFFU) << 32) const auto first = (uint64(0xFFFFFFFFU) << 32)
| anim::getPremultiplied(_colors.color1->c); | anim::getPremultiplied(_colors.color1->c);
@ -510,7 +417,7 @@ QPixmap EmptyUserpic::generate(int size) {
result.fill(Qt::transparent); result.fill(Qt::transparent);
{ {
auto p = QPainter(&result); auto p = QPainter(&result);
paint(p, 0, 0, size, size); paintCircle(p, 0, 0, size, size);
} }
return Ui::PixmapFromImage(std::move(result)); return Ui::PixmapFromImage(std::move(result));
} }

View file

@ -7,9 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "base/weak_ptr.h"
namespace Ui { namespace Ui {
class EmptyUserpic { class EmptyUserpic final : public base::has_weak_ptr {
public: public:
struct BgColors { struct BgColors {
const style::color color1; const style::color color1;
@ -24,7 +26,7 @@ public:
EmptyUserpic(const BgColors &colors, const QString &name); EmptyUserpic(const BgColors &colors, const QString &name);
void paint( void paintCircle(
QPainter &p, QPainter &p,
int x, int x,
int y, int y,
@ -52,12 +54,6 @@ public:
int y, int y,
int outerWidth, int outerWidth,
int size); int size);
static void PaintSavedMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size);
static void PaintSavedMessages( static void PaintSavedMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -66,16 +62,7 @@ public:
int size, int size,
QBrush bg, QBrush bg,
const style::color &fg); const style::color &fg);
static void PaintSavedMessagesRounded( [[nodiscard]] static QImage GenerateSavedMessages(int size);
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg);
[[nodiscard]] static QPixmap GenerateSavedMessages(int size);
[[nodiscard]] static QPixmap GenerateSavedMessagesRounded(int size);
static void PaintRepliesMessages( static void PaintRepliesMessages(
QPainter &p, QPainter &p,
@ -83,12 +70,6 @@ public:
int y, int y,
int outerWidth, int outerWidth,
int size); int size);
static void PaintRepliesMessagesRounded(
QPainter &p,
int x,
int y,
int outerWidth,
int size);
static void PaintRepliesMessages( static void PaintRepliesMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -97,16 +78,7 @@ public:
int size, int size,
QBrush bg, QBrush bg,
const style::color &fg); const style::color &fg);
static void PaintRepliesMessagesRounded( [[nodiscard]] static QImage GenerateRepliesMessages(int size);
QPainter &p,
int x,
int y,
int outerWidth,
int size,
QBrush bg,
const style::color &fg);
[[nodiscard]] static QPixmap GenerateRepliesMessages(int size);
[[nodiscard]] static QPixmap GenerateRepliesMessagesRounded(int size);
~EmptyUserpic(); ~EmptyUserpic();

View file

@ -354,7 +354,7 @@ void UserpicButton::setupPeerViewers() {
) | rpl::filter([=] { ) | rpl::filter([=] {
return _waiting; return _waiting;
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
if (!_userpicView || _userpicView->image()) { if (!Ui::PeerUserpicLoading(_userpicView)) {
_waiting = false; _waiting = false;
startNewPhotoShowing(); startNewPhotoShowing();
} }
@ -473,12 +473,17 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
: false; : false;
auto request = Media::Streaming::FrameRequest(); auto request = Media::Streaming::FrameRequest();
auto size = QSize{ _st.photoSize, _st.photoSize }; auto size = QSize{ _st.photoSize, _st.photoSize };
request.outer = size * cIntRetinaFactor(); const auto ratio = style::DevicePixelRatio();
request.resize = size * cIntRetinaFactor(); request.outer = request.resize = size * ratio;
const auto forum = _peer && _peer->isForum(); const auto forum = _peer && _peer->isForum();
if (forum) { if (forum) {
request.rounding = Images::CornersMaskRef( const auto radius = int(_st.photoSize
Images::CornersMask(ImageRoundRadius::Large)); * Ui::ForumUserpicRadiusMultiplier()
* ratio);
if (_roundingCorners[0].width() != radius) {
_roundingCorners = Images::CornersMask(radius);
}
request.rounding = Images::CornersMaskRef(_roundingCorners);
} else { } else {
if (_ellipseMask.size() != request.outer) { if (_ellipseMask.size() != request.outer) {
_ellipseMask = Images::EllipseMask(size); _ellipseMask = Images::EllipseMask(size);
@ -520,7 +525,7 @@ void UserpicButton::processPeerPhoto() {
Expects(_peer != nullptr); Expects(_peer != nullptr);
_userpicView = _peer->createUserpicView(); _userpicView = _peer->createUserpicView();
_waiting = _userpicView && !_userpicView->image(); _waiting = Ui::PeerUserpicLoading(_userpicView);
if (_waiting) { if (_waiting) {
_peer->loadUserpic(); _peer->loadUserpic();
} }
@ -798,7 +803,7 @@ void UserpicButton::fillShape(QPainter &p, const style::color &color) const {
p.setBrush(color); p.setBrush(color);
const auto size = _st.photoSize; const auto size = _st.photoSize;
if (_peer && _peer->isForum()) { if (_peer && _peer->isForum()) {
const auto radius = st::roundRadiusLarge; const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
p.drawRoundedRect(0, 0, size, size, radius, radius); p.drawRoundedRect(0, 0, size, size, radius, radius);
} else { } else {
p.drawEllipse(0, 0, size, size); p.drawEllipse(0, 0, size, size);
@ -811,8 +816,8 @@ void UserpicButton::prepareUserpicPixmap() {
} }
auto size = _st.photoSize; auto size = _st.photoSize;
_userpicHasImage = _peer _userpicHasImage = _peer
? (_peer->currentUserpic(_userpicView) || _role != Role::ChangePhoto) && (_peer->userpicCloudImage(_userpicView)
: false; || _role != Role::ChangePhoto);
_userpic = CreateSquarePixmap(size, [&](Painter &p) { _userpic = CreateSquarePixmap(size, [&](Painter &p) {
if (_userpicHasImage) { if (_userpicHasImage) {
_peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize); _peer->paintUserpic(p, _userpicView, 0, 0, _st.photoSize);

View file

@ -11,15 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/effects/cross_line.h" #include "ui/effects/cross_line.h"
#include "ui/userpic_view.h"
#include "styles/style_window.h" #include "styles/style_window.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
class PeerData; class PeerData;
namespace Data {
class CloudImageView;
} // namespace Data
namespace Window { namespace Window {
class Controller; class Controller;
class SessionController; class SessionController;
@ -159,7 +156,7 @@ private:
::Window::SessionController *_controller = nullptr; ::Window::SessionController *_controller = nullptr;
::Window::Controller *_window = nullptr; ::Window::Controller *_window = nullptr;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
std::shared_ptr<Data::CloudImageView> _userpicView; PeerUserpicView _userpicView;
QString _cropTitle; QString _cropTitle;
Role _role = Role::ChangePhoto; Role _role = Role::ChangePhoto;
bool _notShownYet = true; bool _notShownYet = true;
@ -169,28 +166,29 @@ private:
bool _userpicCustom = false; bool _userpicCustom = false;
bool _requestToUpload = false; bool _requestToUpload = false;
InMemoryKey _userpicUniqueKey; InMemoryKey _userpicUniqueKey;
Ui::Animations::Simple _a_appearance; Animations::Simple _a_appearance;
QImage _result; QImage _result;
QImage _ellipseMask; QImage _ellipseMask;
std::array<QImage, 4> _roundingCorners;
std::unique_ptr<Media::Streaming::Instance> _streamed; std::unique_ptr<Media::Streaming::Instance> _streamed;
PhotoData *_streamedPhoto = nullptr; PhotoData *_streamedPhoto = nullptr;
base::unique_qptr<Ui::PopupMenu> _menu; base::unique_qptr<PopupMenu> _menu;
bool _showSavedMessagesOnSelf = false; bool _showSavedMessagesOnSelf = false;
bool _canOpenPhoto = false; bool _canOpenPhoto = false;
bool _cursorInChangeOverlay = false; bool _cursorInChangeOverlay = false;
bool _changeOverlayEnabled = false; bool _changeOverlayEnabled = false;
Ui::Animations::Simple _changeOverlayShown; Animations::Simple _changeOverlayShown;
rpl::event_stream<QImage> _chosenImages; rpl::event_stream<QImage> _chosenImages;
rpl::event_stream<> _uploadPhotoRequests; rpl::event_stream<> _uploadPhotoRequests;
}; };
class SilentToggle class SilentToggle final
: public Ui::RippleButton : public RippleButton
, public Ui::AbstractTooltipShower { , public AbstractTooltipShower {
public: public:
SilentToggle(QWidget *parent, not_null<ChannelData*> channel); SilentToggle(QWidget *parent, not_null<ChannelData*> channel);
@ -218,7 +216,7 @@ private:
not_null<ChannelData*> _channel; not_null<ChannelData*> _channel;
bool _checked = false; bool _checked = false;
Ui::Animations::Simple _crossLineAnimation; Animations::Simple _crossLineAnimation;
}; };

View file

@ -0,0 +1,80 @@
/*
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/userpic_view.h"
#include "ui/empty_userpic.h"
#include "ui/image/image_prepare.h"
namespace Ui {
float64 ForumUserpicRadiusMultiplier() {
return 0.3;
}
bool PeerUserpicLoading(const PeerUserpicView &view) {
return view.cloud && view.cloud->isNull();
}
void ValidateUserpicCache(
PeerUserpicView &view,
const QImage *cloud,
const EmptyUserpic *empty,
int size,
bool forum) {
Expects(cloud != nullptr || empty != nullptr);
const auto full = QSize(size, size);
const auto version = style::PaletteVersion();
const auto forumValue = forum ? 1 : 0;
const auto regenerate = (view.cached.size() != QSize(size, size))
|| (view.forum != forumValue)
|| (cloud && !view.empty.null())
|| (empty && empty != view.empty.get())
|| (empty && view.paletteVersion != version);
if (!regenerate) {
return;
}
view.empty = empty;
view.forum = forumValue;
view.paletteVersion = version;
if (cloud) {
view.cached = cloud->scaled(
full,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
if (forum) {
view.cached = Images::Round(
std::move(view.cached),
Images::CornersMask(
size * Ui::ForumUserpicRadiusMultiplier()));
} else {
view.cached = Images::Circle(std::move(view.cached));
}
} else {
if (view.cached.size() != full) {
view.cached = QImage(full, QImage::Format_ARGB32_Premultiplied);
}
view.cached.fill(Qt::transparent);
auto p = QPainter(&view.cached);
if (forum) {
empty->paintRounded(
p,
0,
0,
size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
} else {
empty->paintCircle(p, 0, 0, size, size);
}
}
}
} // namespace Ui

View file

@ -0,0 +1,41 @@
/*
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 "base/weak_ptr.h"
#include <QtGui/QImage>
namespace Ui {
class EmptyUserpic;
[[nodiscard]] float64 ForumUserpicRadiusMultiplier();
struct PeerUserpicView {
[[nodiscard]] bool null() const {
return cached.isNull() && !cloud && empty.null();
}
QImage cached;
std::shared_ptr<QImage> cloud;
base::weak_ptr<const EmptyUserpic> empty;
int paletteVersion : 31 = 0;
int forum : 1 = 0;
};
[[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view);
void ValidateUserpicCache(
PeerUserpicView &view,
const QImage *cloud,
const EmptyUserpic *empty,
int size,
bool forum);
} // namespace Ui

View file

@ -15,13 +15,16 @@ class History;
namespace Data { namespace Data {
class Session; class Session;
class CloudImageView;
class ForumTopic; class ForumTopic;
class Thread; class Thread;
struct ItemNotification; struct ItemNotification;
enum class ItemNotificationType; enum class ItemNotificationType;
} // namespace Data } // namespace Data
namespace Ui {
struct PeerUserpicView;
} // namespace Ui
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -392,7 +395,7 @@ protected:
virtual void doShowNativeNotification( virtual void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,
@ -413,7 +416,7 @@ protected:
void doShowNativeNotification( void doShowNativeNotification(
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId topicRootId, MsgId topicRootId,
std::shared_ptr<Data::CloudImageView> &userpicView, Ui::PeerUserpicView &userpicView,
MsgId msgId, MsgId msgId,
const QString &title, const QString &title,
const QString &subtitle, const QString &subtitle,

View file

@ -660,7 +660,7 @@ Notification::Notification(
auto position = computePosition(st::notifyMinHeight); auto position = computePosition(st::notifyMinHeight);
updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight); updateGeometry(position.x(), position.y(), st::notifyWidth, st::notifyMinHeight);
_userpicLoaded = !_userpicView || (_userpicView->image() != nullptr); _userpicLoaded = !Ui::PeerUserpicLoading(_userpicView);
updateNotifyDisplay(); updateNotifyDisplay();
_hideTimer.setSingleShot(true); _hideTimer.setSingleShot(true);
@ -1004,7 +1004,7 @@ void Notification::updatePeerPhoto() {
return; return;
} }
_userpicView = _peer->createUserpicView(); _userpicView = _peer->createUserpicView();
if (_userpicView && !_userpicView->image()) { if (Ui::PeerUserpicLoading(_userpicView)) {
return; return;
} }
_userpicLoaded = true; _userpicLoaded = true;
@ -1024,7 +1024,7 @@ void Notification::updatePeerPhoto() {
st::notifyPhotoPos.y(), st::notifyPhotoPos.y(),
width(), width(),
st::notifyPhotoSize); st::notifyPhotoSize);
_userpicView = nullptr; _userpicView = {};
update(); update();
} }

View file

@ -11,16 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "ui/text/text.h" #include "ui/text/text.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/userpic_view.h"
#include "base/timer.h" #include "base/timer.h"
#include "base/binary_guard.h" #include "base/binary_guard.h"
#include "base/object_ptr.h" #include "base/object_ptr.h"
#include <QtCore/QTimer> #include <QtCore/QTimer>
namespace Data {
class CloudImageView;
} // namespace Data
namespace Ui { namespace Ui {
class IconButton; class IconButton;
class RoundButton; class RoundButton;
@ -290,7 +287,7 @@ private:
History *_history = nullptr; History *_history = nullptr;
Data::ForumTopic *_topic = nullptr; Data::ForumTopic *_topic = nullptr;
MsgId _topicRootId = 0; MsgId _topicRootId = 0;
std::shared_ptr<Data::CloudImageView> _userpicView; Ui::PeerUserpicView _userpicView;
QString _author; QString _author;
Data::ReactionId _reaction; Data::ReactionId _reaction;
HistoryItem *_item = nullptr; HistoryItem *_item = nullptr;

View file

@ -15,8 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "styles/style_window.h" #include "styles/style_window.h"
namespace Window { namespace Window::Notifications {
namespace Notifications {
namespace { namespace {
// Delete notify photo file after 1 minute of not using. // Delete notify photo file after 1 minute of not using.
@ -24,9 +23,16 @@ constexpr int kNotifyDeletePhotoAfterMs = 60000;
} // namespace } // namespace
CachedUserpics::CachedUserpics(Type type) QImage GenerateUserpic(not_null<PeerData*> peer, Ui::PeerUserpicView &view) {
: _type(type) return peer->isSelf()
, _clearTimer([=] { clear(); }) { ? Ui::EmptyUserpic::GenerateSavedMessages(st::notifyMacPhotoSize)
: peer->isRepliesChat()
? Ui::EmptyUserpic::GenerateRepliesMessages(st::notifyMacPhotoSize)
: peer->generateUserpicImage(view, st::notifyMacPhotoSize);
}
CachedUserpics::CachedUserpics()
: _clearTimer([=] { clear(); }) {
QDir().mkpath(cWorkingDir() + u"tdata/temp"_q); QDir().mkpath(cWorkingDir() + u"tdata/temp"_q);
} }
@ -37,14 +43,14 @@ CachedUserpics::~CachedUserpics() {
} }
// This works about 1200ms on Windows for a folder with one image O_o // This works about 1200ms on Windows for a folder with one image O_o
// base::Platform::DeleteDirectory(cWorkingDir() + u"tdata/temp"_q); //base::Platform::DeleteDirectory(cWorkingDir() + u"tdata/temp"_q);
} }
} }
QString CachedUserpics::get( QString CachedUserpics::get(
const InMemoryKey &key, const InMemoryKey &key,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view) { Ui::PeerUserpicView &view) {
auto ms = crl::now(); auto ms = crl::now();
auto i = _images.find(key); auto i = _images.find(key);
if (i != _images.cend()) { if (i != _images.cend()) {
@ -64,21 +70,7 @@ QString CachedUserpics::get(
cWorkingDir(), cWorkingDir(),
QString::number(base::RandomValue<uint64>(), 16)); QString::number(base::RandomValue<uint64>(), 16));
if (key.first || key.second) { if (key.first || key.second) {
if (peer->isSelf()) { GenerateUserpic(peer, view).save(v.path, "PNG");
const auto method = (_type == Type::Rounded)
? Ui::EmptyUserpic::GenerateSavedMessagesRounded
: Ui::EmptyUserpic::GenerateSavedMessages;
method(st::notifyMacPhotoSize).save(v.path, "PNG");
} else if (peer->isRepliesChat()) {
const auto method = (_type == Type::Rounded)
? Ui::EmptyUserpic::GenerateRepliesMessagesRounded
: Ui::EmptyUserpic::GenerateRepliesMessages;
method(st::notifyMacPhotoSize).save(v.path, "PNG");
} else if (_type == Type::Rounded) {
peer->saveUserpicRounded(view, v.path, st::notifyMacPhotoSize);
} else {
peer->saveUserpic(view, v.path, st::notifyMacPhotoSize);
}
} else { } else {
LogoNoMargin().save(v.path, "PNG"); LogoNoMargin().save(v.path, "PNG");
} }
@ -128,5 +120,4 @@ void CachedUserpics::clear() {
} }
} }
} // namespace Notifications } // namespace Window::Notifications
} // namespace Window

View file

@ -10,34 +10,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "base/timer.h" #include "base/timer.h"
namespace Data { namespace Ui {
class CloudImageView; struct PeerUserpicView;
} // namespace Data } // namespace Ui
namespace Window { namespace Window::Notifications {
namespace Notifications {
[[nodiscard]] QImage GenerateUserpic(
not_null<PeerData*> peer,
Ui::PeerUserpicView &view);
class CachedUserpics : public QObject { class CachedUserpics : public QObject {
public: public:
enum class Type { CachedUserpics();
Rounded,
Circled,
};
CachedUserpics(Type type);
~CachedUserpics(); ~CachedUserpics();
[[nodiscard]] QString get( [[nodiscard]] QString get(
const InMemoryKey &key, const InMemoryKey &key,
not_null<PeerData*> peer, not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &view); Ui::PeerUserpicView &view);
private: private:
void clear(); void clear();
void clearInMs(int ms); void clearInMs(int ms);
crl::time clear(crl::time ms); crl::time clear(crl::time ms);
Type _type = Type::Rounded;
struct Image { struct Image {
crl::time until = 0; crl::time until = 0;
QString path; QString path;
@ -49,5 +46,4 @@ private:
}; };
} // namesapce Notifications } // namespace Window::Notifications
} // namespace Window

View file

@ -982,7 +982,7 @@ void Generator::paintUserpic(int x, int y, Row::Type type, int index, QString le
image.fill(Qt::transparent); image.fill(Qt::transparent);
{ {
Painter p(&image); Painter p(&image);
userpic.paintRounded(p, 0, 0, size, size, size / 2); userpic.paintCircle(p, 0, 0, size, size);
} }
_p->drawImage(rtl() ? (_rect.width() - x - size) : x, y, image); _p->drawImage(rtl() ? (_rect.width() - x - size) : x, y, image);
} }

View file

@ -278,6 +278,8 @@ PRIVATE
ui/empty_userpic.h ui/empty_userpic.h
ui/grouped_layout.cpp ui/grouped_layout.cpp
ui/grouped_layout.h ui/grouped_layout.h
ui/userpic_view.cpp
ui/userpic_view.h
ui/widgets/fields/special_fields.cpp ui/widgets/fields/special_fields.cpp
ui/widgets/fields/special_fields.h ui/widgets/fields/special_fields.h
ui/widgets/fields/time_part_input_with_placeholder.cpp ui/widgets/fields/time_part_input_with_placeholder.cpp

@ -1 +1 @@
Subproject commit e8294bbc9c7f85e6909c3ed06fe03cfd39869613 Subproject commit fd3531b70750ffe7ec2213a899409c08c388ce4c

@ -1 +1 @@
Subproject commit 22ceaae4ed958d9711965fbe4c1385a7d8c60212 Subproject commit 59a7b94ef4bb4d4cf6597d53bdacccbf5720e694