Support nice monoforum userpics.

This commit is contained in:
John Preston 2025-06-03 17:17:36 +04:00
parent dfb6600104
commit d775760f98
19 changed files with 221 additions and 109 deletions

View file

@ -559,7 +559,7 @@ void GroupInfoBox::prepare() {
&_navigation->parentController()->window(), &_navigation->parentController()->window(),
Ui::UserpicButton::Role::ChoosePhoto, Ui::UserpicButton::Role::ChoosePhoto,
st::defaultUserpicButton, st::defaultUserpicButton,
(_type == Type::Forum)); (_type == Type::Forum) ? Ui::PeerUserpicShape::Forum : Ui::PeerUserpicShape::Auto);
_photo->showCustomOnChosen(); _photo->showCustomOnChosen();
_title.create( _title.create(
this, this,

View file

@ -604,7 +604,7 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
container, container,
userpicPeer, userpicPeer,
st::mainMenuUserpic, st::mainMenuUserpic,
peer->userpicForceForumShape()); peer->userpicShape());
userpic->showSavedMessagesOnSelf(true); userpic->showSavedMessagesOnSelf(true);
Ui::IconWithTitle( Ui::IconWithTitle(
container, container,

View file

@ -427,20 +427,30 @@ QImage *PeerData::userpicCloudImage(Ui::PeerUserpicView &view) const {
void PeerData::paintUserpic( void PeerData::paintUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view, Ui::PeerUserpicView &view,
const PaintUserpicContext &context) const { PaintUserpicContext context) const {
if (const auto broadcast = monoforumBroadcast()) { if (const auto broadcast = monoforumBroadcast()) {
if (context.shape == Ui::PeerUserpicShape::Auto) {
context.shape = Ui::PeerUserpicShape::Monoforum;
}
broadcast->paintUserpic(p, view, context); broadcast->paintUserpic(p, view, context);
return; return;
} }
const auto size = context.size; const auto size = context.size;
const auto cloud = userpicCloudImage(view); const auto cloud = userpicCloudImage(view);
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
if (context.shape == Ui::PeerUserpicShape::Auto) {
context.shape = isForum()
? Ui::PeerUserpicShape::Forum
: isMonoforum()
? Ui::PeerUserpicShape::Monoforum
: Ui::PeerUserpicShape::Circle;
}
Ui::ValidateUserpicCache( Ui::ValidateUserpicCache(
view, view,
cloud, cloud,
cloud ? nullptr : ensureEmptyUserpic().get(), cloud ? nullptr : ensureEmptyUserpic().get(),
size * ratio, size * ratio,
context.forumLayout); context.shape);
p.drawImage(QRect(context.position, QSize(size, size)), view.cached); p.drawImage(QRect(context.position, QSize(size, size)), view.cached);
} }
@ -1176,8 +1186,12 @@ not_null<const PeerData*> PeerData::userpicPaintingPeer() const {
return const_cast<PeerData*>(this)->userpicPaintingPeer(); return const_cast<PeerData*>(this)->userpicPaintingPeer();
} }
bool PeerData::userpicForceForumShape() const { Ui::PeerUserpicShape PeerData::userpicShape() const {
return monoforumBroadcast() != nullptr; return isForum()
? Ui::PeerUserpicShape::Forum
: isMonoforum()
? Ui::PeerUserpicShape::Monoforum
: Ui::PeerUserpicShape::Circle;
} }
ChannelData *PeerData::monoforumBroadcast() const { ChannelData *PeerData::monoforumBroadcast() const {

View file

@ -186,6 +186,12 @@ struct PeerBarDetails {
int paysPerMessage = 0; int paysPerMessage = 0;
}; };
struct PaintUserpicContext {
QPoint position;
int size = 0;
Ui::PeerUserpicShape shape = Ui::PeerUserpicShape::Auto;
};
class PeerData { class PeerData {
protected: protected:
PeerData(not_null<Data::Session*> owner, PeerId id); PeerData(not_null<Data::Session*> owner, PeerId id);
@ -310,7 +316,7 @@ public:
[[nodiscard]] not_null<const PeerData*> migrateToOrMe() const; [[nodiscard]] not_null<const PeerData*> migrateToOrMe() const;
[[nodiscard]] not_null<PeerData*> userpicPaintingPeer(); [[nodiscard]] not_null<PeerData*> userpicPaintingPeer();
[[nodiscard]] not_null<const PeerData*> userpicPaintingPeer() const; [[nodiscard]] not_null<const PeerData*> userpicPaintingPeer() const;
[[nodiscard]] bool userpicForceForumShape() const; [[nodiscard]] Ui::PeerUserpicShape userpicShape() const;
// isMonoforum() ? monoforumLink() : nullptr // isMonoforum() ? monoforumLink() : nullptr
[[nodiscard]] ChannelData *monoforumBroadcast() const; [[nodiscard]] ChannelData *monoforumBroadcast() const;
@ -348,15 +354,10 @@ public:
bool hasVideo); bool hasVideo);
void setUserpicPhoto(const MTPPhoto &data); void setUserpicPhoto(const MTPPhoto &data);
struct PaintUserpicContext {
QPoint position;
int size = 0;
bool forumLayout = false;
};
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view, Ui::PeerUserpicView &view,
const PaintUserpicContext &context) const; PaintUserpicContext context) const;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view, Ui::PeerUserpicView &view,
@ -367,7 +368,9 @@ public:
paintUserpic(p, view, { paintUserpic(p, view, {
.position = { x, y }, .position = { x, y },
.size = size, .size = size,
.forumLayout = !forceCircle && (isForum() || isMonoforum()), .shape = (forceCircle
? Ui::PeerUserpicShape::Circle
: Ui::PeerUserpicShape::Auto),
}); });
} }
void paintUserpicLeft( void paintUserpicLeft(

View file

@ -196,7 +196,7 @@ bool HiddenSenderInfo::paintCustomUserpic(
image.isNull() ? nullptr : &image, image.isNull() ? nullptr : &image,
image.isNull() ? &emptyUserpic : nullptr, image.isNull() ? &emptyUserpic : nullptr,
size * style::DevicePixelRatio(), size * style::DevicePixelRatio(),
false); Ui::PeerUserpicShape::Circle);
p.drawImage(QRect(x, y, size, size), view.cached); p.drawImage(QRect(x, y, size, size), view.cached);
return valid; return valid;
} }

View file

@ -368,7 +368,7 @@ void Item::setupTop() {
_top.get(), _top.get(),
_thread->peer()->userpicPaintingPeer(), _thread->peer()->userpicPaintingPeer(),
st::previewUserpic, st::previewUserpic,
_thread->peer()->userpicForceForumShape()); _thread->peer()->userpicShape());
if (userpic) { if (userpic) {
userpic->showSavedMessagesOnSelf(true); userpic->showSavedMessagesOnSelf(true);
userpic->setAttribute(Qt::WA_TransparentForMouseEvents); userpic->setAttribute(Qt::WA_TransparentForMouseEvents);

View file

@ -936,7 +936,7 @@ void TopBarWidget::refreshInfoButton() {
Ui::UserpicButton::Role::Custom, Ui::UserpicButton::Role::Custom,
Ui::UserpicButton::Source::PeerPhoto, Ui::UserpicButton::Source::PeerPhoto,
st::topBarInfoButton, st::topBarInfoButton,
infoPeer->userpicForceForumShape()); infoPeer->userpicShape());
info->showSavedMessagesOnSelf(true); info->showSavedMessagesOnSelf(true);
_info.destroy(); _info.destroy();
_info = std::move(info); _info = std::move(info);

View file

@ -632,7 +632,7 @@ Cover::Cover(
Ui::UserpicButton::Role::OpenPhoto, Ui::UserpicButton::Role::OpenPhoto,
Ui::UserpicButton::Source::PeerPhoto, Ui::UserpicButton::Source::PeerPhoto,
_st.photo, _st.photo,
_peer->userpicForceForumShape())) _peer->userpicShape()))
, _changePersonal((role == Role::Info , _changePersonal((role == Role::Info
|| topic || topic
|| !_peer->isUser() || !_peer->isUser()

View file

@ -39,11 +39,10 @@ QByteArray SessionSettings::serialize() const {
+ Serialize::bytearraySize(autoDownload) + Serialize::bytearraySize(autoDownload)
+ sizeof(qint32) * 11 + sizeof(qint32) * 11
+ (_mutePeriods.size() * sizeof(quint64)) + (_mutePeriods.size() * sizeof(quint64))
+ sizeof(qint32) * 2 + sizeof(qint32) * 3
+ _hiddenPinnedMessages.size() * (sizeof(quint64) * 4)
+ sizeof(qint32)
+ _groupEmojiSectionHidden.size() * sizeof(quint64) + _groupEmojiSectionHidden.size() * sizeof(quint64)
+ sizeof(qint32) * 2; + sizeof(qint32) * 3
+ _hiddenPinnedMessages.size() * (sizeof(quint64) * 4);
auto result = QByteArray(); auto result = QByteArray();
result.reserve(size); result.reserve(size);

View file

@ -119,7 +119,7 @@ void InfoBox(
data.bot, data.bot,
st::websiteBigUserpic)), st::websiteBigUserpic)),
st::sessionBigCoverPadding)->entity(); st::sessionBigCoverPadding)->entity();
userpic->forceForumShape(true); userpic->overrideShape(Ui::PeerUserpicShape::Forum);
userpic->setAttribute(Qt::WA_TransparentForMouseEvents); userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto nameWrap = box->addRow( const auto nameWrap = box->addRow(
@ -224,25 +224,11 @@ PaintRoundImageCallback Row::generatePaintUserpicCallback(bool forceRound) {
const auto peer = _data.bot; const auto peer = _data.bot;
auto userpic = _userpic = peer->createUserpicView(); auto userpic = _userpic = peer->createUserpicView();
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(); peer->paintUserpic(p, _userpic, {
if (const auto cloud = peer->userpicCloudImage(userpic)) { .position = QPoint(x, y),
Ui::ValidateUserpicCache( .size = size,
userpic, .shape = Ui::PeerUserpicShape::Forum,
cloud, });
nullptr,
size * ratio,
true);
p.drawImage(QRect(x, y, size, size), userpic.cached);
} else {
if (_emptyUserpic.isNull()) {
_emptyUserpic = PeerData::GenerateUserpicImage(
peer,
_userpic,
size * ratio,
size * ratio * Ui::ForumUserpicRadiusMultiplier());
}
p.drawImage(QRect(x, y, size, size), _emptyUserpic);
}
}; };
} }

View file

@ -107,6 +107,7 @@ void VerticalButton::paintEvent(QPaintEvent *e) {
.availableWidth = _st.nameWidth, .availableWidth = _st.nameWidth,
.align = style::al_top, .align = style::al_top,
.paused = _delegate->buttonPaused(), .paused = _delegate->buttonPaused(),
.elisionLines = kMaxNameLines,
}); });
const auto &state = _data.badges; const auto &state = _data.badges;

View file

@ -160,12 +160,12 @@ UserpicButton::UserpicButton(
not_null<Window::Controller*> window, not_null<Window::Controller*> window,
Role role, Role role,
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape) PeerUserpicShape shape)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _controller(window->sessionController()) , _controller(window->sessionController())
, _window(window) , _window(window)
, _forceForumShape(forceForumShape) , _shape(shape)
, _role(role) { , _role(role) {
Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto); Expects(_role == Role::ChangePhoto || _role == Role::ChoosePhoto);
@ -181,13 +181,13 @@ UserpicButton::UserpicButton(
Role role, Role role,
Source source, Source source,
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape) PeerUserpicShape shape)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _controller(controller) , _controller(controller)
, _window(&controller->window()) , _window(&controller->window())
, _peer(peer) , _peer(peer)
, _forceForumShape(forceForumShape) , _shape(shape)
, _role(role) , _role(role)
, _source(source) { , _source(source) {
if (_source == Source::Custom) { if (_source == Source::Custom) {
@ -203,11 +203,11 @@ UserpicButton::UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, not_null<PeerData*> peer,
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape) PeerUserpicShape shape)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _peer(peer) , _peer(peer)
, _forceForumShape(forceForumShape) , _shape(shape)
, _role(Role::Custom) , _role(Role::Custom)
, _source(Source::PeerPhoto) { , _source(Source::PeerPhoto) {
Expects(_role != Role::OpenPhoto); Expects(_role != Role::OpenPhoto);
@ -407,7 +407,7 @@ void UserpicButton::choosePhotoLocally() {
CameraBox, CameraBox,
_window, _window,
_peer, _peer,
_forceForumShape, (_shape == PeerUserpicShape::Forum),
callback(ChosenType::Set))); callback(ChosenType::Set)));
}, &st::menuIconPhotoSet); }, &st::menuIconPhotoSet);
} }
@ -648,7 +648,8 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
auto size = QSize{ _st.photoSize, _st.photoSize }; auto size = QSize{ _st.photoSize, _st.photoSize };
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
request.outer = request.resize = size * ratio; request.outer = request.resize = size * ratio;
if (useForumShape()) { if (_shape == PeerUserpicShape::Monoforum) {
} else if (useForumShape()) {
const auto radius = int(_st.photoSize const auto radius = int(_st.photoSize
* Ui::ForumUserpicRadiusMultiplier()); * Ui::ForumUserpicRadiusMultiplier());
if (_roundingCorners[0].width() != radius * ratio) { if (_roundingCorners[0].width() != radius * ratio) {
@ -661,7 +662,24 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
} }
request.mask = _ellipseMask; request.mask = _ellipseMask;
} }
p.drawImage(QRect(photoPosition, size), _streamed->frame(request)); auto frame = _streamed->frame(request);
if (_shape == PeerUserpicShape::Monoforum) {
if (_monoforumMask.isNull()) {
_monoforumMask = MonoforumShapeMask(request.resize);
}
constexpr auto format = QImage::Format_ARGB32_Premultiplied;
if (frame.format() != format) {
frame = std::move(frame).convertToFormat(format);
}
auto q = QPainter(&frame);
q.setCompositionMode(QPainter::CompositionMode_DestinationIn);
q.drawImage(
QRect(QPoint(), frame.size() / frame.devicePixelRatio()),
_monoforumMask);
q.end();
}
p.drawImage(QRect(photoPosition, size), frame);
if (!paused) { if (!paused) {
_streamed->markFrameShown(); _streamed->markFrameShown();
} }
@ -892,9 +910,8 @@ void UserpicButton::processNewPeerPhoto() {
} }
bool UserpicButton::useForumShape() const { bool UserpicButton::useForumShape() const {
return _forceForumShape return (_shape == PeerUserpicShape::Forum)
|| (_peer && _peer->isForum()) || (_peer && _peer->isForum() && _shape == PeerUserpicShape::Auto);
|| (_peer && _peer->isMonoforum());
} }
void UserpicButton::grabOldUserpic() { void UserpicButton::grabOldUserpic() {
@ -946,8 +963,8 @@ void UserpicButton::switchChangePhotoOverlay(
} }
} }
void UserpicButton::forceForumShape(bool force) { void UserpicButton::overrideShape(PeerUserpicShape shape) {
_forceForumShape = force; _shape = shape;
prepare(); prepare();
} }
@ -1083,28 +1100,11 @@ void UserpicButton::prepareUserpicPixmap() {
_userpic = CreateSquarePixmap(size, [&](Painter &p) { _userpic = CreateSquarePixmap(size, [&](Painter &p) {
if (_userpicHasImage) { if (_userpicHasImage) {
if (_showPeerUserpic) { if (_showPeerUserpic) {
if (useForumShape()) { _peer->paintUserpic(p, _userpicView, {
const auto ratio = style::DevicePixelRatio(); .position = QPoint(),
if (const auto cloud = _peer->userpicCloudImage(_userpicView)) { .size = size,
Ui::ValidateUserpicCache( .shape = _shape,
_userpicView, });
cloud,
nullptr,
size * ratio,
true);
p.drawImage(QRect(0, 0, size, size), _userpicView.cached);
} else {
const auto empty = PeerData::GenerateUserpicImage(
_peer,
_userpicView,
size * ratio,
(size * ratio)
* Ui::ForumUserpicRadiusMultiplier());
p.drawImage(QRect(0, 0, size, size), empty);
}
} else {
_peer->paintUserpic(p, _userpicView, 0, 0, size);
}
} else if (_nonPersonalView) { } else if (_nonPersonalView) {
using Size = Data::PhotoSize; using Size = Data::PhotoSize;
if (const auto full = _nonPersonalView->image(Size::Large)) { if (const auto full = _nonPersonalView->image(Size::Large)) {

View file

@ -62,7 +62,7 @@ public:
not_null<::Window::Controller*> window, not_null<::Window::Controller*> window,
Role role, Role role,
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape = false); PeerUserpicShape shape = PeerUserpicShape::Auto);
UserpicButton( UserpicButton(
QWidget *parent, QWidget *parent,
not_null<::Window::SessionController*> controller, not_null<::Window::SessionController*> controller,
@ -70,12 +70,12 @@ public:
Role role, Role role,
Source source, Source source,
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape = false); PeerUserpicShape shape = PeerUserpicShape::Auto);
UserpicButton( UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, // Role::Custom, Source::PeerPhoto not_null<PeerData*> peer, // Role::Custom, Source::PeerPhoto
const style::UserpicButton &st, const style::UserpicButton &st,
bool forceForumShape = false); PeerUserpicShape shape = PeerUserpicShape::Auto);
~UserpicButton(); ~UserpicButton();
enum class ChosenType { enum class ChosenType {
@ -96,7 +96,7 @@ public:
bool enabled, bool enabled,
Fn<void(ChosenImage)> chosen); Fn<void(ChosenImage)> chosen);
void showSavedMessagesOnSelf(bool enabled); void showSavedMessagesOnSelf(bool enabled);
void forceForumShape(bool force); void overrideShape(PeerUserpicShape shape);
// Role::ChoosePhoto or Role::ChangePhoto // Role::ChoosePhoto or Role::ChangePhoto
[[nodiscard]] rpl::producer<ChosenImage> chosenImages() const { [[nodiscard]] rpl::producer<ChosenImage> chosenImages() const {
@ -163,8 +163,9 @@ private:
::Window::SessionController *_controller = nullptr; ::Window::SessionController *_controller = nullptr;
::Window::Controller *_window = nullptr; ::Window::Controller *_window = nullptr;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
bool _forceForumShape = false; PeerUserpicShape _shape = PeerUserpicShape::Auto;
PeerUserpicView _userpicView; PeerUserpicView _userpicView;
QImage _monoforumMask;
std::shared_ptr<Data::PhotoMedia> _nonPersonalView; std::shared_ptr<Data::PhotoMedia> _nonPersonalView;
Role _role = Role::ChangePhoto; Role _role = Role::ChangePhoto;
bool _notShownYet = true; bool _notShownYet = true;

View file

@ -250,22 +250,13 @@ QImage PeerUserpic::image(int size) {
auto p = Painter(&_frame); auto p = Painter(&_frame);
auto &view = _subscribed->view; auto &view = _subscribed->view;
if (!_forceRound) { _peer->paintUserpic(p, view, {
_peer->paintUserpic(p, view, 0, 0, size); .position = QPoint(),
} else if (const auto cloud = _peer->userpicCloudImage(view)) { .size = size,
const auto full = size * style::DevicePixelRatio(); .shape = (_forceRound
Ui::ValidateUserpicCache(view, cloud, nullptr, full, false); ? Ui::PeerUserpicShape::Circle
p.drawImage(QRect(0, 0, size, size), view.cached); : Ui::PeerUserpicShape::Auto),
} else { });
const auto full = size * style::DevicePixelRatio();
const auto r = full / 2.;
const auto empty = PeerData::GenerateUserpicImage(
_peer,
view,
full,
r);
p.drawImage(QRect(0, 0, size, size), empty);
}
} }
return _frame; return _frame;
} }

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_widgets.h" // style::IconButton #include "styles/style_widgets.h" // style::IconButton
#include "styles/style_info.h" // st::topBarCall #include "styles/style_info.h" // st::topBarCall
#include <QtCore/QMutex>
#include <QtSvg/QSvgRenderer> #include <QtSvg/QSvgRenderer>
namespace Ui { namespace Ui {
@ -366,6 +367,17 @@ void EmptyUserpic::paintSquare(
}); });
} }
void EmptyUserpic::paintMonoforum(
QPainter &p,
int x,
int y,
int outerWidth,
int size) const {
paint(p, x, y, outerWidth, size, [&] {
PaintMonoforumShape(p, QRect(x, y, size, size));
});
}
void EmptyUserpic::PaintSavedMessages( void EmptyUserpic::PaintSavedMessages(
QPainter &p, QPainter &p,
int x, int x,
@ -649,4 +661,86 @@ void EmptyUserpic::fillString(const QString &name) {
EmptyUserpic::~EmptyUserpic() = default; EmptyUserpic::~EmptyUserpic() = default;
void PaintMonoforumShape(QPainter &p, QRect rect) {
p.drawEllipse(rect);
auto path = QPainterPath();
path.moveTo(
rect.x() + rect.width() * 0.5,
rect.y() + rect.height() * 0.5);
path.arcTo(
QRectF(
rect.x() - rect.width() * 0.5,
rect.y(),
rect.width(),
rect.height()),
0,
-90);
path.arcTo(
QRectF(
rect.x() - rect.width() * 0.25,
rect.y() - rect.height() * 2,
rect.width() * 0.5,
rect.height() * 3),
-90,
45);
path.lineTo(
rect.x() + rect.width() * 0.5,
rect.y() + rect.height() * 0.5);
p.drawPath(path);
}
QImage MonoforumShapeMask(QSize size) {
auto result = QImage(size, QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
QPainter p(&result);
PainterHighQualityEnabler hq(p);
p.setBrush(Qt::white);
p.setPen(Qt::NoPen);
PaintMonoforumShape(p, QRect(QPoint(), size));
p.end();
return result;
}
const QImage &MonoforumShapeMaskCached(QSize size) {
const auto key = (uint64(uint32(size.width())) << 32)
| uint64(uint32(size.height()));
static auto Masks = base::flat_map<uint64, QImage>();
static auto Mutex = QMutex();
auto lock = QMutexLocker(&Mutex);
const auto i = Masks.find(key);
if (i != end(Masks)) {
return i->second;
}
lock.unlock();
auto mask = MonoforumShapeMask(size);
lock.relock();
return Masks.emplace(key, std::move(mask)).first->second;
}
QImage ApplyMonoforumShape(QImage image) {
const auto size = image.size();
auto mask = MonoforumShapeMaskCached(size);
constexpr auto format = QImage::Format_ARGB32_Premultiplied;
if (image.format() != format) {
image = std::move(image).convertToFormat(format);
}
auto p = QPainter(&image);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawImage(
QRect(QPoint(), image.size() / image.devicePixelRatio()),
mask);
p.end();
return image;
}
} // namespace Ui } // namespace Ui

View file

@ -46,6 +46,12 @@ public:
int y, int y,
int outerWidth, int outerWidth,
int size) const; int size) const;
void paintMonoforum(
QPainter &p,
int x,
int y,
int outerWidth,
int size) const;
[[nodiscard]] QPixmap generate(int size); [[nodiscard]] QPixmap generate(int size);
[[nodiscard]] std::pair<uint64, uint64> uniqueKey() const; [[nodiscard]] std::pair<uint64, uint64> uniqueKey() const;
@ -147,4 +153,9 @@ private:
}; };
void PaintMonoforumShape(QPainter &p, QRect rect);
[[nodiscard]] QImage MonoforumShapeMask(QSize size);
[[nodiscard]] const QImage &MonoforumShapeMaskCached(QSize size);
[[nodiscard]] QImage ApplyMonoforumShape(QImage image);
} // namespace Ui } // namespace Ui

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/userpic_view.h" #include "ui/userpic_view.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/painter.h"
#include "ui/image/image_prepare.h" #include "ui/image/image_prepare.h"
namespace Ui { namespace Ui {
@ -25,14 +26,14 @@ void ValidateUserpicCache(
const QImage *cloud, const QImage *cloud,
const EmptyUserpic *empty, const EmptyUserpic *empty,
int size, int size,
bool forum) { PeerUserpicShape shape) {
Expects(cloud != nullptr || empty != nullptr); Expects(cloud != nullptr || empty != nullptr);
const auto full = QSize(size, size); const auto full = QSize(size, size);
const auto version = style::PaletteVersion(); const auto version = style::PaletteVersion();
const auto forumValue = forum ? 1 : 0; const auto shapeValue = static_cast<uint32>(shape) & 3;
const auto regenerate = (view.cached.size() != QSize(size, size)) const auto regenerate = (view.cached.size() != QSize(size, size))
|| (view.forum != forumValue) || (view.shape != shapeValue)
|| (cloud && !view.empty.null()) || (cloud && !view.empty.null())
|| (empty && empty != view.empty.get()) || (empty && empty != view.empty.get())
|| (empty && view.paletteVersion != version); || (empty && view.paletteVersion != version);
@ -40,7 +41,7 @@ void ValidateUserpicCache(
return; return;
} }
view.empty = empty; view.empty = empty;
view.forum = forumValue; view.shape = shapeValue;
view.paletteVersion = version; view.paletteVersion = version;
if (cloud) { if (cloud) {
@ -48,7 +49,9 @@ void ValidateUserpicCache(
full, full,
Qt::IgnoreAspectRatio, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation); Qt::SmoothTransformation);
if (forum) { if (shape == PeerUserpicShape::Monoforum) {
view.cached = Ui::ApplyMonoforumShape(std::move(view.cached));
} else if (shape == PeerUserpicShape::Forum) {
view.cached = Images::Round( view.cached = Images::Round(
std::move(view.cached), std::move(view.cached),
Images::CornersMask(size Images::CornersMask(size
@ -64,7 +67,9 @@ void ValidateUserpicCache(
view.cached.fill(Qt::transparent); view.cached.fill(Qt::transparent);
auto p = QPainter(&view.cached); auto p = QPainter(&view.cached);
if (forum) { if (shape == PeerUserpicShape::Monoforum) {
empty->paintMonoforum(p, 0, 0, size, size);
} else if (shape == PeerUserpicShape::Forum) {
empty->paintRounded( empty->paintRounded(
p, p,
0, 0,

View file

@ -17,6 +17,13 @@ class EmptyUserpic;
[[nodiscard]] float64 ForumUserpicRadiusMultiplier(); [[nodiscard]] float64 ForumUserpicRadiusMultiplier();
enum class PeerUserpicShape : uint8 {
Auto,
Circle,
Forum,
Monoforum,
};
struct PeerUserpicView { struct PeerUserpicView {
[[nodiscard]] bool null() const { [[nodiscard]] bool null() const {
return cached.isNull() && !cloud && empty.null(); return cached.isNull() && !cloud && empty.null();
@ -25,8 +32,8 @@ struct PeerUserpicView {
QImage cached; QImage cached;
std::shared_ptr<QImage> cloud; std::shared_ptr<QImage> cloud;
base::weak_ptr<const EmptyUserpic> empty; base::weak_ptr<const EmptyUserpic> empty;
uint32 paletteVersion : 31 = 0; uint32 paletteVersion : 30 = 0;
uint32 forum : 1 = 0; uint32 shape : 2 = 0;
}; };
[[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view); [[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view);
@ -36,6 +43,6 @@ void ValidateUserpicCache(
const QImage *cloud, const QImage *cloud,
const EmptyUserpic *empty, const EmptyUserpic *empty,
int size, int size,
bool forum); PeerUserpicShape shape);
} // namespace Ui } // namespace Ui

View file

@ -656,7 +656,7 @@ Notification::Notification(
, _topicRootId(topicRootId) , _topicRootId(topicRootId)
, _sublist(history->peer->monoforumSublistFor(monoforumPeerId)) , _sublist(history->peer->monoforumSublistFor(monoforumPeerId))
, _monoforumPeerId(monoforumPeerId) , _monoforumPeerId(monoforumPeerId)
, _userpicView(_peer->createUserpicView()) , _userpicView(_peer->userpicPaintingPeer()->createUserpicView())
, _author(author) , _author(author)
, _reaction(reaction) , _reaction(reaction)
, _item(item) , _item(item)