mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Show inline path thumbnails for stickers.
This commit is contained in:
parent
f09b91ebb5
commit
4124c2eb57
21 changed files with 355 additions and 38 deletions
|
@ -779,6 +779,12 @@ void StickerSetBox::Inner::paintSticker(
|
||||||
ppos,
|
ppos,
|
||||||
width(),
|
width(),
|
||||||
image->pix(w, h));
|
image->pix(w, h));
|
||||||
|
} else {
|
||||||
|
ChatHelpers::PaintStickerThumbnailPath(
|
||||||
|
p,
|
||||||
|
media.get(),
|
||||||
|
QRect(ppos, QSize(w, h)),
|
||||||
|
st::windowBgRipple->c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -832,6 +832,13 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
|
||||||
} else if (const auto image = media->getStickerSmall()) {
|
} else if (const auto image = media->getStickerSmall()) {
|
||||||
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
||||||
p.drawPixmapLeft(ppos, width(), image->pix(w, h));
|
p.drawPixmapLeft(ppos, width(), image->pix(w, h));
|
||||||
|
} else {
|
||||||
|
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
||||||
|
ChatHelpers::PaintStickerThumbnailPath(
|
||||||
|
p,
|
||||||
|
media.get(),
|
||||||
|
QRect(ppos, QSize(w, h)),
|
||||||
|
st::windowBgRipple->c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1918,6 +1918,12 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
|
||||||
if (sticker.savedFrame.isNull()) {
|
if (sticker.savedFrame.isNull()) {
|
||||||
sticker.savedFrame = pixmap;
|
sticker.savedFrame = pixmap;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
PaintStickerThumbnailPath(
|
||||||
|
p,
|
||||||
|
media.get(),
|
||||||
|
QRect(ppos, QSize{ w, h }),
|
||||||
|
st::windowBgRipple->c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -188,4 +188,27 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
|
||||||
box);
|
box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PaintStickerThumbnailPath(
|
||||||
|
QPainter &p,
|
||||||
|
not_null<Data::DocumentMedia*> media,
|
||||||
|
QRect target,
|
||||||
|
QColor fg) {
|
||||||
|
const auto &path = media->thumbnailPath();
|
||||||
|
const auto dimensions = media->owner()->dimensions;
|
||||||
|
if (path.isEmpty() || dimensions.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p.save();
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setBrush(fg);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.translate(target.topLeft());
|
||||||
|
p.scale(
|
||||||
|
target.width() / float64(dimensions.width()),
|
||||||
|
target.height() / float64(dimensions.height()));
|
||||||
|
p.drawPath(path);
|
||||||
|
p.restore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
|
|
@ -71,4 +71,10 @@ enum class StickerLottieSize : uchar {
|
||||||
QSize box,
|
QSize box,
|
||||||
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
|
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
|
||||||
|
|
||||||
|
bool PaintStickerThumbnailPath(
|
||||||
|
QPainter &p,
|
||||||
|
not_null<Data::DocumentMedia*> media,
|
||||||
|
QRect target,
|
||||||
|
QColor fg);
|
||||||
|
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
|
|
@ -442,12 +442,17 @@ bool DocumentData::checkWallPaperProperties() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocumentData::updateThumbnails(
|
void DocumentData::updateThumbnails(
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail) {
|
const ImageWithLocation &videoThumbnail) {
|
||||||
if (!inlineThumbnailBytes.isEmpty()
|
if (!inlineThumbnail.bytes.isEmpty()
|
||||||
&& _inlineThumbnailBytes.isEmpty()) {
|
&& _inlineThumbnailBytes.isEmpty()) {
|
||||||
_inlineThumbnailBytes = inlineThumbnailBytes;
|
_inlineThumbnailBytes = inlineThumbnail.bytes;
|
||||||
|
if (inlineThumbnail.isPath) {
|
||||||
|
_flags |= Flag::InlineThumbnailIsPath;
|
||||||
|
} else {
|
||||||
|
_flags &= ~Flag::InlineThumbnailIsPath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Data::UpdateCloudFile(
|
Data::UpdateCloudFile(
|
||||||
_thumbnail,
|
_thumbnail,
|
||||||
|
|
|
@ -171,13 +171,16 @@ public:
|
||||||
[[nodiscard]] int videoThumbnailByteSize() const;
|
[[nodiscard]] int videoThumbnailByteSize() const;
|
||||||
|
|
||||||
void updateThumbnails(
|
void updateThumbnails(
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail);
|
const ImageWithLocation &videoThumbnail);
|
||||||
|
|
||||||
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
|
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
|
||||||
return _inlineThumbnailBytes;
|
return _inlineThumbnailBytes;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool inlineThumbnailIsPath() const {
|
||||||
|
return (_flags & Flag::InlineThumbnailIsPath);
|
||||||
|
}
|
||||||
void clearInlineThumbnailBytes() {
|
void clearInlineThumbnailBytes() {
|
||||||
_inlineThumbnailBytes = QByteArray();
|
_inlineThumbnailBytes = QByteArray();
|
||||||
}
|
}
|
||||||
|
@ -257,6 +260,7 @@ private:
|
||||||
DownloadCancelled = 0x10,
|
DownloadCancelled = 0x10,
|
||||||
LoadedInMediaCache = 0x20,
|
LoadedInMediaCache = 0x20,
|
||||||
HasAttachedStickers = 0x40,
|
HasAttachedStickers = 0x40,
|
||||||
|
InlineThumbnailIsPath = 0x80,
|
||||||
};
|
};
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
friend constexpr bool is_flag_type(Flag) { return true; };
|
friend constexpr bool is_flag_type(Flag) { return true; };
|
||||||
|
|
|
@ -172,7 +172,7 @@ void DocumentMedia::setGoodThumbnail(QImage thumbnail) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *DocumentMedia::thumbnailInline() const {
|
Image *DocumentMedia::thumbnailInline() const {
|
||||||
if (!_inlineThumbnail) {
|
if (!_inlineThumbnail && !_owner->inlineThumbnailIsPath()) {
|
||||||
const auto bytes = _owner->inlineThumbnailBytes();
|
const auto bytes = _owner->inlineThumbnailBytes();
|
||||||
if (!bytes.isEmpty()) {
|
if (!bytes.isEmpty()) {
|
||||||
auto image = Images::FromInlineBytes(bytes);
|
auto image = Images::FromInlineBytes(bytes);
|
||||||
|
@ -186,6 +186,19 @@ Image *DocumentMedia::thumbnailInline() const {
|
||||||
return _inlineThumbnail.get();
|
return _inlineThumbnail.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QPainterPath &DocumentMedia::thumbnailPath() const {
|
||||||
|
if (_pathThumbnail.isEmpty() && _owner->inlineThumbnailIsPath()) {
|
||||||
|
const auto bytes = _owner->inlineThumbnailBytes();
|
||||||
|
if (!bytes.isEmpty()) {
|
||||||
|
_pathThumbnail = Images::PathFromInlineBytes(bytes);
|
||||||
|
if (_pathThumbnail.isEmpty()) {
|
||||||
|
_owner->clearInlineThumbnailBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _pathThumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
Image *DocumentMedia::thumbnail() const {
|
Image *DocumentMedia::thumbnail() const {
|
||||||
return _thumbnail.get();
|
return _thumbnail.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
void setGoodThumbnail(QImage thumbnail);
|
void setGoodThumbnail(QImage thumbnail);
|
||||||
|
|
||||||
[[nodiscard]] Image *thumbnailInline() const;
|
[[nodiscard]] Image *thumbnailInline() const;
|
||||||
|
[[nodiscard]] const QPainterPath &thumbnailPath() const;
|
||||||
|
|
||||||
[[nodiscard]] Image *thumbnail() const;
|
[[nodiscard]] Image *thumbnail() const;
|
||||||
[[nodiscard]] QSize thumbnailSize() const;
|
[[nodiscard]] QSize thumbnailSize() const;
|
||||||
|
@ -102,6 +103,7 @@ private:
|
||||||
const not_null<DocumentData*> _owner;
|
const not_null<DocumentData*> _owner;
|
||||||
std::unique_ptr<Image> _goodThumbnail;
|
std::unique_ptr<Image> _goodThumbnail;
|
||||||
mutable std::unique_ptr<Image> _inlineThumbnail;
|
mutable std::unique_ptr<Image> _inlineThumbnail;
|
||||||
|
mutable QPainterPath _pathThumbnail;
|
||||||
std::unique_ptr<Image> _thumbnail;
|
std::unique_ptr<Image> _thumbnail;
|
||||||
std::unique_ptr<Image> _sticker;
|
std::unique_ptr<Image> _sticker;
|
||||||
QByteArray _bytes;
|
QByteArray _bytes;
|
||||||
|
|
|
@ -134,18 +134,25 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QByteArray FindInlineThumbnail(
|
[[nodiscard]] InlineImageLocation FindInlineThumbnail(
|
||||||
const QVector<MTPPhotoSize> &sizes) {
|
const QVector<MTPPhotoSize> &sizes) {
|
||||||
const auto i = ranges::find(
|
const auto i = ranges::find(
|
||||||
sizes,
|
sizes,
|
||||||
mtpc_photoStrippedSize,
|
mtpc_photoStrippedSize,
|
||||||
&MTPPhotoSize::type);
|
&MTPPhotoSize::type);
|
||||||
|
const auto j = ranges::find(
|
||||||
|
sizes,
|
||||||
|
mtpc_photoPathSize,
|
||||||
|
&MTPPhotoSize::type);
|
||||||
return (i != sizes.end())
|
return (i != sizes.end())
|
||||||
? i->c_photoStrippedSize().vbytes().v
|
? InlineImageLocation{ i->c_photoStrippedSize().vbytes().v, false }
|
||||||
: QByteArray();
|
: (j != sizes.end())
|
||||||
|
? InlineImageLocation{ j->c_photoPathSize().vbytes().v, true }
|
||||||
|
: InlineImageLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) {
|
[[nodiscard]] InlineImageLocation FindDocumentInlineThumbnail(
|
||||||
|
const MTPDdocument &data) {
|
||||||
return FindInlineThumbnail(data.vthumbs().value_or_empty());
|
return FindInlineThumbnail(data.vthumbs().value_or_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +200,8 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QByteArray FindPhotoInlineThumbnail(const MTPDphoto &data) {
|
[[nodiscard]] QByteArray FindPhotoInlineThumbnail(const MTPDphoto &data) {
|
||||||
return FindInlineThumbnail(data.vsizes().v);
|
const auto thumbnail = FindInlineThumbnail(data.vsizes().v);
|
||||||
|
return !thumbnail.isPath ? thumbnail.bytes : QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] int VideoStartTime(const MTPDvideoSize &data) {
|
[[nodiscard]] int VideoStartTime(const MTPDvideoSize &data) {
|
||||||
|
@ -2656,7 +2664,7 @@ not_null<DocumentData*> Session::processDocument(
|
||||||
data.vdate().v,
|
data.vdate().v,
|
||||||
data.vattributes().v,
|
data.vattributes().v,
|
||||||
qs(data.vmime_type()),
|
qs(data.vmime_type()),
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
thumbnail,
|
thumbnail,
|
||||||
ImageWithLocation(),
|
ImageWithLocation(),
|
||||||
data.vdc_id().v,
|
data.vdc_id().v,
|
||||||
|
@ -2673,7 +2681,7 @@ not_null<DocumentData*> Session::document(
|
||||||
TimeId date,
|
TimeId date,
|
||||||
const QVector<MTPDocumentAttribute> &attributes,
|
const QVector<MTPDocumentAttribute> &attributes,
|
||||||
const QString &mime,
|
const QString &mime,
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail,
|
const ImageWithLocation &videoThumbnail,
|
||||||
int32 dc,
|
int32 dc,
|
||||||
|
@ -2686,7 +2694,7 @@ not_null<DocumentData*> Session::document(
|
||||||
date,
|
date,
|
||||||
attributes,
|
attributes,
|
||||||
mime,
|
mime,
|
||||||
inlineThumbnailBytes,
|
inlineThumbnail,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
videoThumbnail,
|
videoThumbnail,
|
||||||
dc,
|
dc,
|
||||||
|
@ -2754,7 +2762,7 @@ DocumentData *Session::documentFromWeb(
|
||||||
base::unixtime::now(),
|
base::unixtime::now(),
|
||||||
data.vattributes().v,
|
data.vattributes().v,
|
||||||
data.vmime_type().v,
|
data.vmime_type().v,
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
ImageWithLocation{ .location = thumbnailLocation },
|
ImageWithLocation{ .location = thumbnailLocation },
|
||||||
ImageWithLocation{ .location = videoThumbnailLocation },
|
ImageWithLocation{ .location = videoThumbnailLocation },
|
||||||
session().mainDcId(),
|
session().mainDcId(),
|
||||||
|
@ -2776,7 +2784,7 @@ DocumentData *Session::documentFromWeb(
|
||||||
base::unixtime::now(),
|
base::unixtime::now(),
|
||||||
data.vattributes().v,
|
data.vattributes().v,
|
||||||
data.vmime_type().v,
|
data.vmime_type().v,
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
ImageWithLocation{ .location = thumbnailLocation },
|
ImageWithLocation{ .location = thumbnailLocation },
|
||||||
ImageWithLocation{ .location = videoThumbnailLocation },
|
ImageWithLocation{ .location = videoThumbnailLocation },
|
||||||
session().mainDcId(),
|
session().mainDcId(),
|
||||||
|
@ -2796,7 +2804,7 @@ void Session::documentApplyFields(
|
||||||
void Session::documentApplyFields(
|
void Session::documentApplyFields(
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
const MTPDdocument &data) {
|
const MTPDdocument &data) {
|
||||||
const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
|
const auto inlineThumbnail = FindDocumentInlineThumbnail(data);
|
||||||
const auto thumbnailSize = FindDocumentThumbnail(data);
|
const auto thumbnailSize = FindDocumentThumbnail(data);
|
||||||
const auto videoThumbnailSize = FindDocumentVideoThumbnail(data);
|
const auto videoThumbnailSize = FindDocumentVideoThumbnail(data);
|
||||||
const auto prepared = Images::FromPhotoSize(
|
const auto prepared = Images::FromPhotoSize(
|
||||||
|
@ -2813,7 +2821,7 @@ void Session::documentApplyFields(
|
||||||
data.vdate().v,
|
data.vdate().v,
|
||||||
data.vattributes().v,
|
data.vattributes().v,
|
||||||
qs(data.vmime_type()),
|
qs(data.vmime_type()),
|
||||||
inlineThumbnailBytes,
|
inlineThumbnail,
|
||||||
prepared,
|
prepared,
|
||||||
videoThumbnail,
|
videoThumbnail,
|
||||||
data.vdc_id().v,
|
data.vdc_id().v,
|
||||||
|
@ -2827,7 +2835,7 @@ void Session::documentApplyFields(
|
||||||
TimeId date,
|
TimeId date,
|
||||||
const QVector<MTPDocumentAttribute> &attributes,
|
const QVector<MTPDocumentAttribute> &attributes,
|
||||||
const QString &mime,
|
const QString &mime,
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail,
|
const ImageWithLocation &videoThumbnail,
|
||||||
int32 dc,
|
int32 dc,
|
||||||
|
@ -2838,7 +2846,7 @@ void Session::documentApplyFields(
|
||||||
document->date = date;
|
document->date = date;
|
||||||
document->setMimeString(mime);
|
document->setMimeString(mime);
|
||||||
document->updateThumbnails(
|
document->updateThumbnails(
|
||||||
inlineThumbnailBytes,
|
inlineThumbnail,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
videoThumbnail);
|
videoThumbnail);
|
||||||
document->size = size;
|
document->size = size;
|
||||||
|
|
|
@ -476,7 +476,7 @@ public:
|
||||||
TimeId date,
|
TimeId date,
|
||||||
const QVector<MTPDocumentAttribute> &attributes,
|
const QVector<MTPDocumentAttribute> &attributes,
|
||||||
const QString &mime,
|
const QString &mime,
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail,
|
const ImageWithLocation &videoThumbnail,
|
||||||
int32 dc,
|
int32 dc,
|
||||||
|
@ -743,7 +743,7 @@ private:
|
||||||
TimeId date,
|
TimeId date,
|
||||||
const QVector<MTPDocumentAttribute> &attributes,
|
const QVector<MTPDocumentAttribute> &attributes,
|
||||||
const QString &mime,
|
const QString &mime,
|
||||||
const QByteArray &inlineThumbnailBytes,
|
const InlineImageLocation &inlineThumbnail,
|
||||||
const ImageWithLocation &thumbnail,
|
const ImageWithLocation &thumbnail,
|
||||||
const ImageWithLocation &videoThumbnail,
|
const ImageWithLocation &videoThumbnail,
|
||||||
int32 dc,
|
int32 dc,
|
||||||
|
|
|
@ -106,7 +106,7 @@ enum class FilterType {
|
||||||
base::unixtime::now(),
|
base::unixtime::now(),
|
||||||
QVector<MTPDocumentAttribute>(),
|
QVector<MTPDocumentAttribute>(),
|
||||||
QString(),
|
QString(),
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
ImageWithLocation(),
|
ImageWithLocation(),
|
||||||
ImageWithLocation(),
|
ImageWithLocation(),
|
||||||
owner->session().mainDcId(),
|
owner->session().mainDcId(),
|
||||||
|
|
|
@ -150,11 +150,14 @@ QSize Sticker::GetAnimatedEmojiSize(
|
||||||
|
|
||||||
void Sticker::draw(Painter &p, const QRect &r, bool selected) {
|
void Sticker::draw(Painter &p, const QRect &r, bool selected) {
|
||||||
ensureDataMediaCreated();
|
ensureDataMediaCreated();
|
||||||
|
paintPath(p, r, selected);
|
||||||
|
return;
|
||||||
if (readyToDrawLottie()) {
|
if (readyToDrawLottie()) {
|
||||||
paintLottie(p, r, selected);
|
paintLottie(p, r, selected);
|
||||||
} else if (_data->sticker()
|
} else if (!_data->sticker()
|
||||||
&& (!_data->sticker()->animated || !_replacements)) {
|
|| (_data->sticker()->animated && _replacements)
|
||||||
paintPixmap(p, r, selected);
|
|| !paintPixmap(p, r, selected)) {
|
||||||
|
paintPath(p, r, selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,15 +218,25 @@ void Sticker::paintLottie(Painter &p, const QRect &r, bool selected) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sticker::paintPixmap(Painter &p, const QRect &r, bool selected) {
|
bool Sticker::paintPixmap(Painter &p, const QRect &r, bool selected) {
|
||||||
const auto pixmap = paintedPixmap(selected);
|
const auto pixmap = paintedPixmap(selected);
|
||||||
if (!pixmap.isNull()) {
|
if (pixmap.isNull()) {
|
||||||
p.drawPixmap(
|
return false;
|
||||||
QPoint(
|
|
||||||
r.x() + (r.width() - _size.width()) / 2,
|
|
||||||
r.y() + (r.height() - _size.height()) / 2),
|
|
||||||
pixmap);
|
|
||||||
}
|
}
|
||||||
|
p.drawPixmap(
|
||||||
|
QPoint(
|
||||||
|
r.x() + (r.width() - _size.width()) / 2,
|
||||||
|
r.y() + (r.height() - _size.height()) / 2),
|
||||||
|
pixmap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sticker::paintPath(Painter &p, const QRect &r, bool selected) {
|
||||||
|
ChatHelpers::PaintStickerThumbnailPath(
|
||||||
|
p,
|
||||||
|
_dataMedia.get(),
|
||||||
|
r,
|
||||||
|
(selected ? st::msgServiceBgSelected : st::msgServiceBg)->c);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap Sticker::paintedPixmap(bool selected) const {
|
QPixmap Sticker::paintedPixmap(bool selected) const {
|
||||||
|
@ -306,7 +319,9 @@ void Sticker::dataMediaCreated() const {
|
||||||
Expects(_dataMedia != nullptr);
|
Expects(_dataMedia != nullptr);
|
||||||
|
|
||||||
_dataMedia->goodThumbnailWanted();
|
_dataMedia->goodThumbnailWanted();
|
||||||
_dataMedia->thumbnailWanted(_parent->data()->fullId());
|
if (_dataMedia->thumbnailPath().isEmpty()) {
|
||||||
|
_dataMedia->thumbnailWanted(_parent->data()->fullId());
|
||||||
|
}
|
||||||
_parent->history()->owner().registerHeavyViewPart(_parent);
|
_parent->history()->owner().registerHeavyViewPart(_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,8 @@ public:
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] bool isEmojiSticker() const;
|
[[nodiscard]] bool isEmojiSticker() const;
|
||||||
void paintLottie(Painter &p, const QRect &r, bool selected);
|
void paintLottie(Painter &p, const QRect &r, bool selected);
|
||||||
void paintPixmap(Painter &p, const QRect &r, bool selected);
|
bool paintPixmap(Painter &p, const QRect &r, bool selected);
|
||||||
|
void paintPath(Painter &p, const QRect &r, bool selected);
|
||||||
[[nodiscard]] QPixmap paintedPixmap(bool selected) const;
|
[[nodiscard]] QPixmap paintedPixmap(bool selected) const;
|
||||||
|
|
||||||
void ensureDataMediaCreated() const;
|
void ensureDataMediaCreated() const;
|
||||||
|
|
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_photo_media.h"
|
#include "data/data_photo_media.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "chat_helpers/gifs_list_widget.h" // ChatHelpers::AddGifAction
|
#include "chat_helpers/gifs_list_widget.h" // ChatHelpers::AddGifAction.
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_single_player.h"
|
||||||
|
@ -483,6 +483,19 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context)
|
||||||
int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor();
|
int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor();
|
||||||
QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
||||||
p.drawPixmap(pos, _thumb);
|
p.drawPixmap(pos, _thumb);
|
||||||
|
} else {
|
||||||
|
const auto thumbSize = getThumbSize();
|
||||||
|
const auto w = thumbSize.width();
|
||||||
|
const auto h = thumbSize.height();
|
||||||
|
ChatHelpers::PaintStickerThumbnailPath(
|
||||||
|
p,
|
||||||
|
_dataMedia.get(),
|
||||||
|
QRect(
|
||||||
|
(st::stickerPanSize.width() - w) / 2,
|
||||||
|
(st::stickerPanSize.height() - h) / 2,
|
||||||
|
w,
|
||||||
|
h),
|
||||||
|
st::windowBgRipple->c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ DocumentData *Document::readFromStreamHelper(
|
||||||
date,
|
date,
|
||||||
attributes,
|
attributes,
|
||||||
mime,
|
mime,
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
ImageWithLocation{
|
ImageWithLocation{
|
||||||
.location = *thumb,
|
.location = *thumb,
|
||||||
.bytesCount = thumbnailByteSize
|
.bytesCount = thumbnailByteSize
|
||||||
|
|
|
@ -1995,7 +1995,7 @@ void Account::importOldRecentStickers() {
|
||||||
date,
|
date,
|
||||||
attributes,
|
attributes,
|
||||||
mime,
|
mime,
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
ImageWithLocation(),
|
ImageWithLocation(),
|
||||||
ImageWithLocation(),
|
ImageWithLocation(),
|
||||||
dc,
|
dc,
|
||||||
|
|
|
@ -98,7 +98,7 @@ void LoadAndApplyThumbnail(
|
||||||
};
|
};
|
||||||
|
|
||||||
document->updateThumbnails(
|
document->updateThumbnails(
|
||||||
QByteArray(),
|
InlineImageLocation(),
|
||||||
imageWithLocation,
|
imageWithLocation,
|
||||||
ImageWithLocation{ .location = ImageLocation() });
|
ImageWithLocation{ .location = ImageLocation() });
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,206 @@ QImage FromInlineBytes(const QByteArray &bytes) {
|
||||||
return App::readImage(ExpandInlineBytes(bytes));
|
return App::readImage(ExpandInlineBytes(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thanks TDLib for code.
|
||||||
|
QByteArray ExpandPathInlineBytes(const QByteArray &bytes) {
|
||||||
|
auto result = QByteArray();
|
||||||
|
result.reserve(3 * (bytes.size() + 1));
|
||||||
|
result.append('M');
|
||||||
|
for (unsigned char c : bytes) {
|
||||||
|
if (c >= 128 + 64) {
|
||||||
|
result.append("AACAAAAHAAALMAAAQASTAVAAAZ"
|
||||||
|
"aacaaaahaaalmaaaqastava.az0123456789-,"[c - 128 - 64]);
|
||||||
|
} else {
|
||||||
|
if (c >= 128) {
|
||||||
|
result.append(',');
|
||||||
|
} else if (c >= 64) {
|
||||||
|
result.append('-');
|
||||||
|
}
|
||||||
|
char buffer[3] = { 0 };
|
||||||
|
std::to_chars(buffer, buffer + 3, (c & 63));
|
||||||
|
result.append(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.append('z');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath PathFromInlineBytes(const QByteArray &bytes) {
|
||||||
|
if (bytes.isEmpty()) {
|
||||||
|
return QPainterPath();
|
||||||
|
}
|
||||||
|
const auto expanded = ExpandPathInlineBytes(bytes);
|
||||||
|
const auto path = expanded.data(); // Allows checking for '\0' by index.
|
||||||
|
auto position = 0;
|
||||||
|
|
||||||
|
const auto isAlpha = [](char c) {
|
||||||
|
c |= 0x20;
|
||||||
|
return 'a' <= c && c <= 'z';
|
||||||
|
};
|
||||||
|
const auto isDigit = [](char c) {
|
||||||
|
return '0' <= c && c <= '9';
|
||||||
|
};
|
||||||
|
const auto skipCommas = [&] {
|
||||||
|
while (path[position] == ',') {
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto getNumber = [&] {
|
||||||
|
skipCommas();
|
||||||
|
auto sign = 1;
|
||||||
|
if (path[position] == '-') {
|
||||||
|
sign = -1;
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
double res = 0;
|
||||||
|
while (isDigit(path[position])) {
|
||||||
|
res = res * 10 + path[position++] - '0';
|
||||||
|
}
|
||||||
|
if (path[position] == '.') {
|
||||||
|
++position;
|
||||||
|
double mul = 0.1;
|
||||||
|
while (isDigit(path[position])) {
|
||||||
|
res += (path[position] - '0') * mul;
|
||||||
|
mul *= 0.1;
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sign * res;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = QPainterPath();
|
||||||
|
auto x = 0.;
|
||||||
|
auto y = 0.;
|
||||||
|
while (path[position] != '\0') {
|
||||||
|
skipCommas();
|
||||||
|
if (path[position] == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (path[position] == 'm' || path[position] == 'M') {
|
||||||
|
auto command = path[position++];
|
||||||
|
do {
|
||||||
|
if (command == 'm') {
|
||||||
|
x += getNumber();
|
||||||
|
y += getNumber();
|
||||||
|
} else {
|
||||||
|
x = getNumber();
|
||||||
|
y = getNumber();
|
||||||
|
}
|
||||||
|
skipCommas();
|
||||||
|
} while (path[position] != '\0' && !isAlpha(path[position]));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xStart = x;
|
||||||
|
auto yStart = y;
|
||||||
|
result.moveTo(xStart, yStart);
|
||||||
|
auto haveLastEndControlPoint = false;
|
||||||
|
auto xLastEndControlPoint = 0.;
|
||||||
|
auto yLastEndControlPoint = 0.;
|
||||||
|
auto isClosed = false;
|
||||||
|
auto command = '-';
|
||||||
|
while (!isClosed) {
|
||||||
|
skipCommas();
|
||||||
|
if (path[position] == '\0') {
|
||||||
|
LOG(("SVG Error: Receive unclosed path: %1"
|
||||||
|
).arg(QString::fromLatin1(path)));
|
||||||
|
return QPainterPath();
|
||||||
|
}
|
||||||
|
if (isAlpha(path[position])) {
|
||||||
|
command = path[position++];
|
||||||
|
}
|
||||||
|
switch (command) {
|
||||||
|
case 'l':
|
||||||
|
case 'L':
|
||||||
|
case 'h':
|
||||||
|
case 'H':
|
||||||
|
case 'v':
|
||||||
|
case 'V':
|
||||||
|
if (command == 'l' || command == 'h') {
|
||||||
|
x += getNumber();
|
||||||
|
} else if (command == 'L' || command == 'H') {
|
||||||
|
x = getNumber();
|
||||||
|
}
|
||||||
|
if (command == 'l' || command == 'v') {
|
||||||
|
y += getNumber();
|
||||||
|
} else if (command == 'L' || command == 'V') {
|
||||||
|
y = getNumber();
|
||||||
|
}
|
||||||
|
result.lineTo(x, y);
|
||||||
|
haveLastEndControlPoint = false;
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
case 'c':
|
||||||
|
case 'S':
|
||||||
|
case 's': {
|
||||||
|
auto xStartControlPoint = 0.;
|
||||||
|
auto yStartControlPoint = 0.;
|
||||||
|
if (command == 'S' || command == 's') {
|
||||||
|
if (haveLastEndControlPoint) {
|
||||||
|
xStartControlPoint = 2 * x - xLastEndControlPoint;
|
||||||
|
yStartControlPoint = 2 * y - yLastEndControlPoint;
|
||||||
|
} else {
|
||||||
|
xStartControlPoint = x;
|
||||||
|
yStartControlPoint = y;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xStartControlPoint = getNumber();
|
||||||
|
yStartControlPoint = getNumber();
|
||||||
|
if (command == 'c') {
|
||||||
|
xStartControlPoint += x;
|
||||||
|
yStartControlPoint += y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xLastEndControlPoint = getNumber();
|
||||||
|
yLastEndControlPoint = getNumber();
|
||||||
|
if (command == 'c' || command == 's') {
|
||||||
|
xLastEndControlPoint += x;
|
||||||
|
yLastEndControlPoint += y;
|
||||||
|
}
|
||||||
|
haveLastEndControlPoint = true;
|
||||||
|
|
||||||
|
if (command == 'c' || command == 's') {
|
||||||
|
x += getNumber();
|
||||||
|
y += getNumber();
|
||||||
|
} else {
|
||||||
|
x = getNumber();
|
||||||
|
y = getNumber();
|
||||||
|
}
|
||||||
|
result.cubicTo(
|
||||||
|
xStartControlPoint,
|
||||||
|
yStartControlPoint,
|
||||||
|
xLastEndControlPoint,
|
||||||
|
yLastEndControlPoint,
|
||||||
|
x,
|
||||||
|
y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
case 'M':
|
||||||
|
--position;
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'z':
|
||||||
|
case 'Z':
|
||||||
|
if (x != xStart || y != yStart) {
|
||||||
|
x = xStart;
|
||||||
|
y = yStart;
|
||||||
|
result.lineTo(x, y);
|
||||||
|
}
|
||||||
|
isClosed = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(("SVG Error: Receive invalid command %1 at pos %2: %3"
|
||||||
|
).arg(command
|
||||||
|
).arg(position
|
||||||
|
).arg(QString::fromLatin1(path)));
|
||||||
|
return QPainterPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Images
|
} // namespace Images
|
||||||
|
|
||||||
Image::Image(const QString &path) : Image(ReadContent(path)) {
|
Image::Image(const QString &path) : Image(ReadContent(path)) {
|
||||||
|
|
|
@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
|
|
||||||
|
class QPainterPath;
|
||||||
|
|
||||||
namespace Images {
|
namespace Images {
|
||||||
|
|
||||||
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
||||||
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
||||||
|
[[nodiscard]] QPainterPath PathFromInlineBytes(const QByteArray &bytes);
|
||||||
|
|
||||||
} // namespace Images
|
} // namespace Images
|
||||||
|
|
||||||
|
|
|
@ -620,6 +620,11 @@ struct ImageWithLocation {
|
||||||
int progressivePartSize = 0;
|
int progressivePartSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct InlineImageLocation {
|
||||||
|
QByteArray bytes;
|
||||||
|
bool isPath = false;
|
||||||
|
};
|
||||||
|
|
||||||
InMemoryKey inMemoryKey(const StorageFileLocation &location);
|
InMemoryKey inMemoryKey(const StorageFileLocation &location);
|
||||||
|
|
||||||
inline InMemoryKey inMemoryKey(const StorageImageLocation &location) {
|
inline InMemoryKey inMemoryKey(const StorageImageLocation &location) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue