Display reply background emoji.

This commit is contained in:
John Preston 2023-10-24 11:47:11 +04:00
parent 60fb5fdaf0
commit 9c23de7f1a
6 changed files with 211 additions and 10 deletions

View File

@ -627,9 +627,9 @@ bool PeerData::changeColorIndex(
: clearColorIndex();
}
bool PeerData::changeBackgroundEmoji(
bool PeerData::changeBackgroundEmojiId(
const tl::conditional<MTPlong> &cloudBackgroundEmoji) {
return changeBackgroundEmoji(cloudBackgroundEmoji
return changeBackgroundEmojiId(cloudBackgroundEmoji
? cloudBackgroundEmoji->v
: DocumentId());
}
@ -869,7 +869,11 @@ bool PeerData::clearColorIndex() {
return true;
}
bool PeerData::changeBackgroundEmoji(uint64 id) {
DocumentId PeerData::backgroundEmojiId() const {
return _backgroundEmojiId;
}
bool PeerData::changeBackgroundEmojiId(DocumentId id) {
if (_backgroundEmojiId == id) {
return false;
}

View File

@ -169,7 +169,8 @@ public:
}
bool changeColorIndex(uint8 index);
bool clearColorIndex();
bool changeBackgroundEmoji(uint64 id);
[[nodiscard]] DocumentId backgroundEmojiId() const;
bool changeBackgroundEmojiId(DocumentId id);
[[nodiscard]] bool isUser() const {
return peerIsUser(id);
@ -361,7 +362,7 @@ public:
void setSettings(const MTPPeerSettings &data);
bool changeColorIndex(const tl::conditional<MTPint> &cloudColorIndex);
bool changeBackgroundEmoji(
bool changeBackgroundEmojiId(
const tl::conditional<MTPlong> &cloudBackgroundEmoji);
enum class BlockStatus : char {

View File

@ -711,7 +711,7 @@ not_null<UserData*> Session::processUser(const MTPUser &data) {
flags |= UpdateFlag::Color;
decorationsUpdated = true;
}
if (result->changeBackgroundEmoji(data.vbackground_emoji_id())) {
if (result->changeBackgroundEmojiId(data.vbackground_emoji_id())) {
flags |= UpdateFlag::BackgroundEmoji;
decorationsUpdated = true;
}
@ -997,7 +997,7 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
flags |= UpdateFlag::Color;
decorationsUpdated = true;
}
if (result->changeBackgroundEmoji(data.vbackground_emoji_id())) {
if (result->changeBackgroundEmojiId(data.vbackground_emoji_id())) {
flags |= UpdateFlag::BackgroundEmoji;
decorationsUpdated = true;
}

View File

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "media/audio/media_audio.h"
#include "media/player/media_player_instance.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_media_types.h"
#include "data/data_session.h"
#include "data/data_user.h"
@ -55,6 +56,127 @@ namespace {
const auto kPsaForwardedPrefix = "cloud_lng_forwarded_psa_";
void ValidateBackgroundEmoji(
DocumentId backgroundEmojiId,
not_null<Ui::BackgroundEmojiData*> data,
not_null<Ui::BackgroundEmojiCache*> cache,
not_null<Ui::Text::QuotePaintCache*> quote,
not_null<const HistoryView::Element*> holder) {
if (data->firstFrameMask.isNull()) {
if (!cache->frames[0].isNull()) {
for (auto &frame : cache->frames) {
frame = QImage();
}
}
const auto tag = Data::CustomEmojiSizeTag::Isolated;
if (!data->emoji) {
const auto owner = &holder->history()->owner();
const auto repaint = crl::guard(holder, [=] {
holder->history()->owner().requestViewRepaint(holder);
});
data->emoji = owner->customEmojiManager().create(
backgroundEmojiId,
repaint,
tag);
}
if (!data->emoji->ready()) {
return;
}
const auto size = Data::FrameSizeFromTag(tag);
data->firstFrameMask = QImage(
QSize(size, size),
QImage::Format_ARGB32_Premultiplied);
data->firstFrameMask.fill(Qt::transparent);
data->firstFrameMask.setDevicePixelRatio(style::DevicePixelRatio());
auto p = Painter(&data->firstFrameMask);
data->emoji->paint(p, {
.textColor = QColor(255, 255, 255),
.position = QPoint(0, 0),
.internal = {
.forceFirstFrame = true,
},
});
p.end();
data->emoji = nullptr;
}
if (!cache->frames[0].isNull() && cache->color == quote->icon) {
return;
}
cache->color = quote->icon;
const auto ratio = style::DevicePixelRatio();
auto colorized = QImage(
data->firstFrameMask.size(),
QImage::Format_ARGB32_Premultiplied);
colorized.setDevicePixelRatio(ratio);
style::colorizeImage(
data->firstFrameMask,
cache->color,
&colorized,
QRect(), // src
QPoint(), // dst
true); // use alpha
const auto make = [&](int size) {
size = style::ConvertScale(size) * ratio;
auto result = colorized.scaled(
size,
size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
result.setDevicePixelRatio(ratio);
return result;
};
constexpr auto kSize1 = 12;
constexpr auto kSize2 = 16;
constexpr auto kSize3 = 20;
cache->frames[0] = make(kSize1);
cache->frames[1] = make(kSize2);
cache->frames[2] = make(kSize3);
}
void FillBackgroundEmoji(
Painter &p,
const QRect &rect,
bool quote,
const Ui::BackgroundEmojiCache &cache) {
p.setClipRect(rect);
const auto &frames = cache.frames;
const auto right = rect.x() + rect.width();
const auto paint = [&](int x, int y, int index, float64 opacity) {
y = style::ConvertScale(y);
if (y >= rect.height()) {
return;
}
p.setOpacity(opacity);
p.drawImage(
right - style::ConvertScale(x + (quote ? 12 : 0)),
rect.y() + y,
frames[index]);
};
paint(28, 4, 2, 0.32);
paint(51, 15, 1, 0.32);
paint(64, -2, 0, 0.28);
paint(87, 11, 1, 0.24);
paint(125, -2, 2, 0.16);
paint(28, 31, 1, 0.24);
paint(72, 33, 2, 0.2);
paint(46, 52, 1, 0.24);
paint(24, 55, 2, 0.18);
if (quote) {
paint(4, 23, 1, 0.28);
paint(0, 48, 0, 0.24);
}
p.setClipping(false);
p.setOpacity(1.);
}
} // namespace
void HistoryMessageVia::create(
@ -676,10 +798,18 @@ void HistoryMessageReply::paint(
const auto rect = QRect(x, y, w, _height);
const auto hasQuote = !_fields.quote.empty();
const auto selected = context.selected();
const auto colorIndexPlusOne = resolvedMessage
? (resolvedMessage->colorIndex() + 1)
const auto colorPeer = resolvedMessage
? resolvedMessage->displayFrom()
: resolvedStory
? (resolvedStory->peer()->colorIndex() + 1)
? resolvedStory->peer().get()
: nullptr;
const auto backgroundEmojiId = colorPeer
? colorPeer->backgroundEmojiId()
: DocumentId();
const auto colorIndexPlusOne = colorPeer
? (colorPeer->colorIndex() + 1)
: resolvedMessage
? (resolvedMessage->hiddenSenderInfo()->colorIndex + 1)
: 0;
const auto useColorIndex = colorIndexPlusOne && !context.outbg;
const auto twoColored = colorIndexPlusOne
@ -698,12 +828,33 @@ void HistoryMessageReply::paint(
const auto &quoteSt = hasQuote
? st::messageTextStyle.blockquote
: st::messageQuoteStyle;
const auto backgroundEmoji = backgroundEmojiId
? st->backgroundEmojiData(backgroundEmojiId).get()
: nullptr;
const auto backgroundEmojiCache = backgroundEmoji
? &backgroundEmoji->caches[Ui::BackgroundEmojiData::CacheIndex(
selected,
context.outbg,
inBubble,
colorIndexPlusOne)]
: nullptr;
const auto rippleColor = cache->bg;
if (!inBubble) {
cache->bg = QColor(0, 0, 0, 0);
}
Ui::Text::ValidateQuotePaintCache(*cache, quoteSt);
Ui::Text::FillQuotePaint(p, rect, *cache, quoteSt);
if (backgroundEmoji) {
ValidateBackgroundEmoji(
backgroundEmojiId,
backgroundEmoji,
backgroundEmojiCache,
cache,
holder);
if (!backgroundEmojiCache->frames[0].isNull()) {
FillBackgroundEmoji(p, rect, hasQuote, *backgroundEmojiCache);
}
}
if (!inBubble) {
cache->bg = rippleColor;
}

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_theme.h"
#include "ui/image/image_prepare.h" // ImageRoundRadius
#include "ui/text/text_custom_emoji.h"
#include "ui/color_contrast.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
@ -190,6 +191,17 @@ ColorIndexValues SimpleColorIndexValues(QColor color, bool twoColored) {
};
}
int BackgroundEmojiData::CacheIndex(
bool selected,
bool outbg,
bool inbubble,
uint8 colorIndexPlusOne) {
const auto base = colorIndexPlusOne
? (colorIndexPlusOne - 1)
: (kColorIndexCount + (!inbubble ? 0 : outbg ? 1 : 2));
return (base * 2) + (selected ? 1 : 0);
};
ChatStyle::ChatStyle() {
finalize();
make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
@ -553,6 +565,8 @@ ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
assignPalette(isolated);
}
ChatStyle::~ChatStyle() = default;
void ChatStyle::apply(not_null<ChatTheme*> theme) {
applyCustomPalette(theme->palette());
}
@ -802,6 +816,11 @@ const style::TextPalette &ChatStyle::coloredTextPalette(
return result.data;
}
not_null<BackgroundEmojiData*> ChatStyle::backgroundEmojiData(
uint64 id) const {
return &_backgroundEmojis[id];
}
not_null<Text::QuotePaintCache*> ChatStyle::coloredQuoteCache(
bool selected,
uint8 colorIndex) const {

View File

@ -21,6 +21,10 @@ struct TwoIconButton;
struct ScrollArea;
} // namespace style
namespace Ui::Text {
class CustomEmoji;
} // namespace Ui::Text
namespace Ui {
class ChatTheme;
@ -112,6 +116,23 @@ struct ReactionPaintInfo {
Fn<QRect(QPainter&)> effectPaint;
};
struct BackgroundEmojiCache {
QColor color;
std::array<QImage, 3> frames;
};
struct BackgroundEmojiData {
std::unique_ptr<Text::CustomEmoji> emoji;
QImage firstFrameMask;
std::array<BackgroundEmojiCache, 2 * (3 + kColorIndexCount)> caches;
[[nodiscard]] static int CacheIndex(
bool selected,
bool outbg,
bool inbubble,
uint8 colorIndexPlusOne);
};
struct ChatPaintContext {
not_null<const ChatStyle*> st;
const BubblePattern *bubblesPattern = nullptr;
@ -185,6 +206,7 @@ class ChatStyle final : public style::palette {
public:
ChatStyle();
explicit ChatStyle(not_null<const style::palette*> isolated);
~ChatStyle();
void apply(not_null<ChatTheme*> theme);
void applyCustomPalette(const style::palette *palette);
@ -242,6 +264,9 @@ public:
bool selected,
uint8 colorIndex) const;
[[nodiscard]] not_null<BackgroundEmojiData*> backgroundEmojiData(
uint64 id) const;
[[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersSmall() const;
[[nodiscard]] const CornersPixmaps &msgBotKbOverBgAddCornersLarge() const;
[[nodiscard]] const CornersPixmaps &msgSelectOverlayCorners(
@ -408,6 +433,7 @@ private:
mutable std::array<
ColoredPalette,
2 * kColorIndexCount> _coloredTextPalettes;
mutable base::flat_map<uint64, BackgroundEmojiData> _backgroundEmojis;
style::TextPalette _historyPsaForwardPalette;
style::TextPalette _imgReplyTextPalette;