2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-22 10:27:09 +00:00

[Option][WIP] Profile pic rounding

This commit is contained in:
Eric Kotato 2023-10-02 18:02:19 +03:00
parent 551984e30b
commit 0c4ceba4d6
22 changed files with 274 additions and 33 deletions

View File

@ -997,6 +997,8 @@ PRIVATE
kotato/boxes/kotato_unpin_box.h
kotato/kotato_lang.cpp
kotato/kotato_lang.h
kotato/kotato_radius.cpp
kotato/kotato_radius.h
kotato/kotato_settings.cpp
kotato/kotato_settings.h
kotato/kotato_settings_menu.cpp

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peer_list_box.h"
#include "kotato/kotato_radius.h"
#include "kotato/kotato_lang.h"
#include "history/history.h" // chatListNameSortKey.
#include "main/session/session_show.h"
@ -904,9 +905,7 @@ void PeerListRow::createCheckbox(
const style::RoundImageCheckbox &st,
Fn<void()> updateCallback) {
const auto generateRadius = [=](int size) {
return (!special() && peer()->isForum())
? int(size * Ui::ForumUserpicRadiusMultiplier())
: std::optional<int>();
return int(size * Kotato::UserpicRadius(!special() && peer()->isForum()));
};
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(
st,

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/launcher.h"
#include "kotato/kotato_radius.h"
#include "kotato/kotato_settings.h"
#include "kotato/kotato_version.h"
#include "platform/platform_launcher.h"
@ -375,6 +376,7 @@ int Launcher::exec() {
Logs::start();
base::options::init(cWorkingDir() + "tdata/experimental_options.json");
Kotato::JsonSettings::Load();
Kotato::RefreshRadius();
// Must be called after options are inited.
initHighDpi();

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_peer.h"
#include "kotato/kotato_radius.h"
#include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_chat_participant_status.h"
@ -382,6 +383,7 @@ QImage PeerData::generateUserpicImage(
Ui::PeerUserpicView &view,
int size,
std::optional<int> radius) const {
const auto radiusOption = Kotato::UserpicRadius(isForum());
if (const auto userpic = userpicCloudImage(view)) {
auto image = userpic->scaled(
{ size, size },
@ -393,7 +395,13 @@ QImage PeerData::generateUserpicImage(
Images::CornersMask(radius / style::DevicePixelRatio()));
};
if (radius == 0) {
if (radiusOption == 0.0) {
return image;
} else if (radiusOption) {
return round(size * radiusOption);
} else {
return Images::Circle(std::move(image));
}
} else if (radius) {
return round(*radius);
} else if (isForum()) {
@ -409,7 +417,19 @@ QImage PeerData::generateUserpicImage(
Painter p(&result);
if (radius == 0) {
if (radiusOption == 0.0) {
ensureEmptyUserpic()->paintSquare(p, 0, 0, size, size);
} else if (radiusOption) {
ensureEmptyUserpic()->paintRounded(
p,
0,
0,
size,
size,
size * radiusOption);
} else {
ensureEmptyUserpic()->paintCircle(p, 0, 0, size, size);
}
} else if (radius) {
ensureEmptyUserpic()->paintRounded(p, 0, 0, size, size, *radius);
} else if (isForum()) {

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/dialogs_row.h"
#include "kotato/kotato_radius.h"
#include "ui/chat/chat_theme.h" // CountAverageColor.
#include "ui/color_contrast.h"
#include "ui/effects/outline_segments.h"
@ -416,7 +417,7 @@ void Row::PaintCornerBadgeFrame(
: st::dialogsCallBadgeSize;
const auto stroke = st::dialogsOnlineBadgeStroke;
const auto skip = online
? st::dialogsOnlineBadgeSkip
? Kotato::UserpicOnlineBadgeSkip()
: st::dialogsCallBadgeSkip;
const auto shrink = (size / 2) * (1. - topLayerProgress);

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/ui/dialogs_video_userpic.h"
#include "kotato/kotato_radius.h"
#include "core/file_location.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
@ -100,7 +101,7 @@ Media::Clip::FrameRequest VideoUserpic::request(int size) const {
.frame = { size, size },
.outer = { size, size },
.factor = cIntRetinaFactor(),
.radius = ImageRoundRadius::Ellipse,
.radius = Kotato::UserpicRadius(),
};
}

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_group_call_bar.h"
#include "kotato/kotato_radius.h"
#include "data/data_channel.h"
#include "data/data_user.h"
#include "data/data_changes.h"
@ -59,7 +60,7 @@ void GenerateUserpicsInRow(
q.setCompositionMode(QPainter::CompositionMode_Source);
q.setBrush(Qt::NoBrush);
q.setPen(pen);
q.drawEllipse(x, 0, single, single);
Kotato::DrawUserpicShape(q, x, 0, single, single, single);
x -= single - shift;
}
}

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/reactions/history_view_reactions.h"
#include "kotato/kotato_radius.h"
#include "history/history_item.h"
#include "history/history.h"
#include "history/view/history_view_message.h"
@ -623,7 +624,7 @@ void InlineList::paintSingleBg(
float64 opacity) const {
p.setOpacity(opacity);
if (!areTags()) {
const auto radius = fill.height() / 2.;
const auto radius = fill.height() * Kotato::UserpicRadius();
p.setBrush(color);
p.drawRoundedRect(fill, radius, radius);
return;

View File

@ -0,0 +1,143 @@
/*
This file is part of Kotatogram Desktop,
the unofficial app based on Telegram Desktop.
For license and copyright information please follow this link:
https://github.com/kotatogram/kotatogram-desktop/blob/dev/LEGAL
*/
#include "kotato/kotato_radius.h"
#include "kotato/kotato_settings.h"
#include "ui/painter.h"
#include "styles/style_chat.h"
#include "styles/style_dialogs.h"
namespace Kotato {
namespace {
struct Radius {
float64 userpicRadius = 0.5;
float64 forumUserpicRadius = 0.3;
bool useDefaultRadiusForForum = false;
style::point onlineBadgeSkip = st::dialogsOnlineBadgeSkip;
};
Radius radius;
} // namespace
void RefreshRadius() {
radius.userpicRadius = float64(JsonSettings::GetInt("userpic_corner_radius")) / 100.0;
radius.forumUserpicRadius = float64(JsonSettings::GetInt("userpic_corner_radius_forum")) / 100.0;
radius.useDefaultRadiusForForum = JsonSettings::GetBool("userpic_corner_radius_forum_use_default");
radius.onlineBadgeSkip = {
style::ConvertScale(int(2 * radius.userpicRadius) - 1),
style::ConvertScale(int(6 * radius.userpicRadius) - 1),
};
}
float64 UserpicRadius(bool isForum) {
if (isForum && !radius.useDefaultRadiusForForum) {
return radius.forumUserpicRadius;
}
return radius.userpicRadius;
}
void DrawUserpicShape(
QPainter &p,
QRect rect,
float64 size,
bool isForum) {
const auto r = UserpicRadius(isForum);
if (r >= 0.5) {
p.drawEllipse(rect);
} else if (r) {
p.drawRoundedRect(rect, size * r, size * r);
} else {
p.fillRect(rect, p.brush());
}
}
void DrawUserpicShape(
QPainter &p,
QRectF rect,
float64 size,
bool isForum) {
const auto r = UserpicRadius(isForum);
if (r >= 0.5) {
p.drawEllipse(rect);
} else if (r) {
p.drawRoundedRect(rect, size * r, size * r);
} else {
p.fillRect(rect, p.brush());
}
}
void DrawUserpicShape(
QPainter &p,
int x,
int y,
int w,
int h,
float64 size,
bool isForum) {
const auto r = UserpicRadius(isForum);
if (r >= 0.5) {
p.drawEllipse(x, y, w, h);
} else if (r) {
p.drawRoundedRect(x, y, w, h, size * r, size * r);
} else {
p.fillRect(x, y, w, h, p.brush());
}
}
style::point UserpicOnlineBadgeSkip() {
return radius.onlineBadgeSkip;
}
QPixmap MessageTailLeft(style::color color) {
const auto tail = st::historyBubbleTailInLeft;
QImage rect(tail.width(), tail.height(), QImage::Format_ARGB32_Premultiplied);
rect.fill(color->c);
{
auto p = QPainter(&rect);
PainterHighQualityEnabler hq(p);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setPen(Qt::NoPen);
p.setBrush(Qt::transparent);
p.drawRoundedRect(
tail.width()-st::msgPhotoSize+style::ConvertScale(1),
tail.height()-st::msgPhotoSize+style::ConvertScale(2),
st::msgPhotoSize,
st::msgPhotoSize,
st::msgPhotoSize * radius.userpicRadius,
st::msgPhotoSize * radius.userpicRadius);
}
return QPixmap::fromImage(rect);
}
QPixmap MessageTailRight(style::color color) {
const auto tail = st::historyBubbleTailInRight;
QImage rect(tail.width(), tail.height(), QImage::Format_ARGB32_Premultiplied);
rect.fill(color->c);
{
auto p = QPainter(&rect);
PainterHighQualityEnabler hq(p);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setPen(Qt::NoPen);
p.setBrush(Qt::transparent);
p.drawRoundedRect(
-style::ConvertScale(1),
tail.height()-st::msgPhotoSize+style::ConvertScale(2),
st::msgPhotoSize,
st::msgPhotoSize,
st::msgPhotoSize * radius.userpicRadius,
st::msgPhotoSize * radius.userpicRadius);
}
return QPixmap::fromImage(rect);
}
} // namespace Kotato

View File

@ -0,0 +1,38 @@
/*
This file is part of Kotatogram Desktop,
the unofficial app based on Telegram Desktop.
For license and copyright information please follow this link:
https://github.com/kotatogram/kotatogram-desktop/blob/dev/LEGAL
*/
#pragma once
namespace Kotato {
void RefreshRadius();
float64 UserpicRadius(bool isForum = false);
void DrawUserpicShape(
QPainter &p,
QRect rect,
float64 size,
bool isForum = false);
void DrawUserpicShape(
QPainter &p,
QRectF rect,
float64 size,
bool isForum = false);
void DrawUserpicShape(
QPainter &p,
int x,
int y,
int w,
int h,
float64 size,
bool isForum = false);
style::point UserpicOnlineBadgeSkip();
QPixmap MessageTailLeft(style::color color);
QPixmap MessageTailRight(style::color color);
} // namespace Kotato

View File

@ -333,6 +333,17 @@ const std::map<QString, Definition, std::greater<QString>> DefinitionMap {
.type = SettingType::IntSetting,
.defaultValue = 20,
.limitHandler = IntLimit(0, 200, 20), }},
{ "userpic_corner_radius", {
.type = SettingType::IntSetting,
.defaultValue = 50,
.limitHandler = IntLimit(0, 50), }},
{ "userpic_corner_radius_forum", {
.type = SettingType::IntSetting,
.defaultValue = 30,
.limitHandler = IntLimit(0, 50), }},
{ "userpic_corner_radius_forum_use_default", {
.type = SettingType::BoolSetting,
.defaultValue = false, }},
{ "always_show_top_userpic", {
.type = SettingType::BoolSetting,
.defaultValue = false, }},

View File

@ -45,7 +45,7 @@ QImage PrepareFrame(
const auto needResize = (original.size() != request.frame);
const auto needOuterFill = request.outer.isValid()
&& (request.outer != request.frame);
const auto needRounding = (request.radius != ImageRoundRadius::None);
const auto needRounding = (request.radius > 0.0);
const auto colorizing = (request.colored.alpha() != 0);
if (!needResize
&& !needOuterFill
@ -98,8 +98,7 @@ QImage PrepareFrame(
if (needRounding) {
cache = Images::Round(
std::move(cache),
request.radius,
request.corners);
Images::CornersMask((cache.width() * request.radius) / style::DevicePixelRatio()));
}
if (colorizing) {
cache = Images::Colored(std::move(cache), request.colored);

View File

@ -37,7 +37,7 @@ struct FrameRequest {
QSize frame;
QSize outer;
int factor = 0;
ImageRoundRadius radius = ImageRoundRadius::None;
float64 radius = 0.0;
RectParts corners = RectPart::AllCorners;
QColor colored = QColor(0, 0, 0, 0);
bool keepAlpha = false;

View File

@ -94,7 +94,7 @@ crl::time LastSettingsQueryMs/* = 0*/;
<toast launch="action=open&amp;)" + wid + LR"(">
<visual>
<binding template="ToastGeneric">
<image placement="appLogoOverride" hint-crop="circle" src=""/>
<image placement="appLogoOverride" src=""/>
<text hint-maxLines="1"></text>
<text></text>
<text></text>

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "settings/settings_information.h"
#include "kotato/kotato_radius.h"
#include "kotato/kotato_lang.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/vertical_layout_reorder.h"
@ -638,7 +639,7 @@ void SetupAccountsWrap(
pen.setWidthF(line);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawEllipse(rect);
Kotato::DrawUserpicShape(p, rect, size);
}
}, state->userpic.lifetime());

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/chat/choose_send_as.h"
#include "kotato/kotato_radius.h"
#include "boxes/peer_list_box.h"
#include "data/data_peer.h"
#include "data/data_channel.h"
@ -283,7 +284,8 @@ void SetupSendAsButton(
) | rpl::map([=](not_null<PeerData*> chosen) {
return Data::PeerUserpicImageValue(
chosen,
st::sendAsButton.size * style::DevicePixelRatio());
st::sendAsButton.size * style::DevicePixelRatio(),
st::sendAsButton.size * Kotato::UserpicRadius());
}) | rpl::flatten_latest();
}) | rpl::flatten_latest();

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/chat/group_call_userpics.h"
#include "kotato/kotato_radius.h"
#include "ui/paint/blobs.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
@ -273,7 +274,7 @@ void GroupCallUserpics::validateCache(Userpic &userpic) {
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setBrush(Qt::transparent);
p.setPen(pen);
p.drawEllipse(skip - size + shift, skip, size, size);
Kotato::DrawUserpicShape(p, skip - size + shift, skip, size, size, size);
}
}
}

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/chat/message_bubble.h"
#include "kotato/kotato_radius.h"
#include "ui/cached_round_corners.h"
#include "ui/image/image_prepare.h"
#include "ui/chat/chat_style.h"
@ -240,7 +241,11 @@ void PaintSolidBubble(QPainter &p, const SimpleBubble &args) {
: st.msgBgCornersSmall;
p.drawPixmap(x, y, corners.p[index]);
}, [&](const QPoint &bottomPosition) {
tail.paint(p, bottomPosition - tailShift, args.outerWidth);
p.drawPixmap(
bottomPosition - tailShift,
(args.rounding.bottomRight == Corner::Tail)
? Kotato::MessageTailRight(bg)
: Kotato::MessageTailLeft(bg));
return tail.width();
});
}

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/controls/userpic_button.h"
#include "kotato/kotato_radius.h"
#include "base/call_delayed.h"
#include "ui/effects/ripple_animation.h"
#include "ui/empty_userpic.h"
@ -135,7 +136,7 @@ void SetupSubButtonBackground(
auto hq = PainterHighQualityEnabler(p);
p.setBrush(st::boxBg);
p.setPen(Qt::NoPen);
p.drawEllipse(background->rect());
Kotato::DrawUserpicShape(p, background->rect(), background->rect().width());
}, background->lifetime());
upload->positionValue(
@ -579,9 +580,9 @@ void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) {
auto size = QSize{ _st.photoSize, _st.photoSize };
const auto ratio = style::DevicePixelRatio();
request.outer = request.resize = size * ratio;
if (useForumShape()) {
const auto radius = int(_st.photoSize
* Ui::ForumUserpicRadiusMultiplier());
const auto radiusOption = Kotato::UserpicRadius(useForumShape());
if (radiusOption < 0.5) {
const auto radius = int(_st.photoSize * radiusOption);
if (_roundingCorners[0].width() != radius * ratio) {
_roundingCorners = Images::CornersMask(radius);
}
@ -988,8 +989,9 @@ void UserpicButton::fillShape(QPainter &p, const style::color &color) const {
p.setPen(Qt::NoPen);
p.setBrush(color);
const auto size = _st.photoSize;
if (useForumShape()) {
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
const auto radiusOption = Kotato::UserpicRadius(useForumShape());
if (radiusOption < 0.5) {
const auto radius = size * radiusOption;
p.drawRoundedRect(0, 0, size, size, radius, radius);
} else {
p.drawEllipse(0, 0, size, size);

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h"
#include "ui/chat/chat_style.h"
#include "kotato/kotato_radius.h"
#include "ui/effects/animation_value.h"
#include "ui/emoji_config.h"
#include "ui/painter.h"
@ -378,7 +379,7 @@ void EmptyUserpic::PaintSavedMessages(
PainterHighQualityEnabler hq(p);
p.setBrush(std::move(bg));
p.setPen(Qt::NoPen);
p.drawEllipse(x, y, size, size);
Kotato::DrawUserpicShape(p, x, y, size, size, size);
PaintSavedMessagesInner(p, x, y, size, fg);
}
@ -417,7 +418,7 @@ void EmptyUserpic::PaintRepliesMessages(
PainterHighQualityEnabler hq(p);
p.setBrush(bg);
p.setPen(Qt::NoPen);
p.drawEllipse(x, y, size, size);
Kotato::DrawUserpicShape(p, x, y, size, size, size);
PaintRepliesMessagesInner(p, x, y, size, fg);
}

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/userpic_view.h"
#include "kotato/kotato_radius.h"
#include "ui/empty_userpic.h"
#include "ui/image/image_prepare.h"
@ -28,10 +29,12 @@ void ValidateUserpicCache(
bool forum) {
Expects(cloud != nullptr || empty != nullptr);
const auto radius = Kotato::UserpicRadius(forum);
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.radius != radius)
|| (view.forum != forumValue)
|| (cloud && !view.empty.null())
|| (empty && empty != view.empty.get())
@ -48,14 +51,14 @@ void ValidateUserpicCache(
full,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
if (forum) {
if (radius >= 0.5) {
view.cached = Images::Circle(std::move(view.cached));
} else if (radius) {
view.cached = Images::Round(
std::move(view.cached),
Images::CornersMask(size
* Ui::ForumUserpicRadiusMultiplier()
* radius
/ style::DevicePixelRatio()));
} else {
view.cached = Images::Circle(std::move(view.cached));
}
} else {
if (view.cached.size() != full) {
@ -64,16 +67,23 @@ void ValidateUserpicCache(
view.cached.fill(Qt::transparent);
auto p = QPainter(&view.cached);
if (forum) {
if (radius >= 0.5) {
empty->paintCircle(p, 0, 0, size, size);
} else if (radius) {
empty->paintRounded(
p,
0,
0,
size,
size,
size * Ui::ForumUserpicRadiusMultiplier());
size * radius);
} else {
empty->paintCircle(p, 0, 0, size, size);
empty->paintSquare(
p,
0,
0,
size,
size);
}
}
}

View File

@ -27,6 +27,7 @@ struct PeerUserpicView {
base::weak_ptr<const EmptyUserpic> empty;
uint32 paletteVersion : 31 = 0;
uint32 forum : 1 = 0;
float64 radius = -1.0;
};
[[nodiscard]] bool PeerUserpicLoading(const PeerUserpicView &view);