mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +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,
|
||||
width(),
|
||||
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()) {
|
||||
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -71,4 +71,10 @@ enum class StickerLottieSize : uchar {
|
|||
QSize box,
|
||||
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
|
||||
|
||||
bool PaintStickerThumbnailPath(
|
||||
QPainter &p,
|
||||
not_null<Data::DocumentMedia*> media,
|
||||
QRect target,
|
||||
QColor fg);
|
||||
|
||||
} // namespace ChatHelpers
|
||||
|
|
|
@ -442,12 +442,17 @@ bool DocumentData::checkWallPaperProperties() {
|
|||
}
|
||||
|
||||
void DocumentData::updateThumbnails(
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail) {
|
||||
if (!inlineThumbnailBytes.isEmpty()
|
||||
if (!inlineThumbnail.bytes.isEmpty()
|
||||
&& _inlineThumbnailBytes.isEmpty()) {
|
||||
_inlineThumbnailBytes = inlineThumbnailBytes;
|
||||
_inlineThumbnailBytes = inlineThumbnail.bytes;
|
||||
if (inlineThumbnail.isPath) {
|
||||
_flags |= Flag::InlineThumbnailIsPath;
|
||||
} else {
|
||||
_flags &= ~Flag::InlineThumbnailIsPath;
|
||||
}
|
||||
}
|
||||
Data::UpdateCloudFile(
|
||||
_thumbnail,
|
||||
|
|
|
@ -171,13 +171,16 @@ public:
|
|||
[[nodiscard]] int videoThumbnailByteSize() const;
|
||||
|
||||
void updateThumbnails(
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail);
|
||||
|
||||
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
|
||||
return _inlineThumbnailBytes;
|
||||
}
|
||||
[[nodiscard]] bool inlineThumbnailIsPath() const {
|
||||
return (_flags & Flag::InlineThumbnailIsPath);
|
||||
}
|
||||
void clearInlineThumbnailBytes() {
|
||||
_inlineThumbnailBytes = QByteArray();
|
||||
}
|
||||
|
@ -257,6 +260,7 @@ private:
|
|||
DownloadCancelled = 0x10,
|
||||
LoadedInMediaCache = 0x20,
|
||||
HasAttachedStickers = 0x40,
|
||||
InlineThumbnailIsPath = 0x80,
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend constexpr bool is_flag_type(Flag) { return true; };
|
||||
|
|
|
@ -172,7 +172,7 @@ void DocumentMedia::setGoodThumbnail(QImage thumbnail) {
|
|||
}
|
||||
|
||||
Image *DocumentMedia::thumbnailInline() const {
|
||||
if (!_inlineThumbnail) {
|
||||
if (!_inlineThumbnail && !_owner->inlineThumbnailIsPath()) {
|
||||
const auto bytes = _owner->inlineThumbnailBytes();
|
||||
if (!bytes.isEmpty()) {
|
||||
auto image = Images::FromInlineBytes(bytes);
|
||||
|
@ -186,6 +186,19 @@ Image *DocumentMedia::thumbnailInline() const {
|
|||
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 {
|
||||
return _thumbnail.get();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public:
|
|||
void setGoodThumbnail(QImage thumbnail);
|
||||
|
||||
[[nodiscard]] Image *thumbnailInline() const;
|
||||
[[nodiscard]] const QPainterPath &thumbnailPath() const;
|
||||
|
||||
[[nodiscard]] Image *thumbnail() const;
|
||||
[[nodiscard]] QSize thumbnailSize() const;
|
||||
|
@ -102,6 +103,7 @@ private:
|
|||
const not_null<DocumentData*> _owner;
|
||||
std::unique_ptr<Image> _goodThumbnail;
|
||||
mutable std::unique_ptr<Image> _inlineThumbnail;
|
||||
mutable QPainterPath _pathThumbnail;
|
||||
std::unique_ptr<Image> _thumbnail;
|
||||
std::unique_ptr<Image> _sticker;
|
||||
QByteArray _bytes;
|
||||
|
|
|
@ -134,18 +134,25 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
|
|||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
[[nodiscard]] QByteArray FindInlineThumbnail(
|
||||
[[nodiscard]] InlineImageLocation FindInlineThumbnail(
|
||||
const QVector<MTPPhotoSize> &sizes) {
|
||||
const auto i = ranges::find(
|
||||
sizes,
|
||||
mtpc_photoStrippedSize,
|
||||
&MTPPhotoSize::type);
|
||||
const auto j = ranges::find(
|
||||
sizes,
|
||||
mtpc_photoPathSize,
|
||||
&MTPPhotoSize::type);
|
||||
return (i != sizes.end())
|
||||
? i->c_photoStrippedSize().vbytes().v
|
||||
: QByteArray();
|
||||
? InlineImageLocation{ i->c_photoStrippedSize().vbytes().v, false }
|
||||
: (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());
|
||||
}
|
||||
|
||||
|
@ -193,7 +200,8 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
|
|||
}
|
||||
|
||||
[[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) {
|
||||
|
@ -2656,7 +2664,7 @@ not_null<DocumentData*> Session::processDocument(
|
|||
data.vdate().v,
|
||||
data.vattributes().v,
|
||||
qs(data.vmime_type()),
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
thumbnail,
|
||||
ImageWithLocation(),
|
||||
data.vdc_id().v,
|
||||
|
@ -2673,7 +2681,7 @@ not_null<DocumentData*> Session::document(
|
|||
TimeId date,
|
||||
const QVector<MTPDocumentAttribute> &attributes,
|
||||
const QString &mime,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail,
|
||||
int32 dc,
|
||||
|
@ -2686,7 +2694,7 @@ not_null<DocumentData*> Session::document(
|
|||
date,
|
||||
attributes,
|
||||
mime,
|
||||
inlineThumbnailBytes,
|
||||
inlineThumbnail,
|
||||
thumbnail,
|
||||
videoThumbnail,
|
||||
dc,
|
||||
|
@ -2754,7 +2762,7 @@ DocumentData *Session::documentFromWeb(
|
|||
base::unixtime::now(),
|
||||
data.vattributes().v,
|
||||
data.vmime_type().v,
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
ImageWithLocation{ .location = thumbnailLocation },
|
||||
ImageWithLocation{ .location = videoThumbnailLocation },
|
||||
session().mainDcId(),
|
||||
|
@ -2776,7 +2784,7 @@ DocumentData *Session::documentFromWeb(
|
|||
base::unixtime::now(),
|
||||
data.vattributes().v,
|
||||
data.vmime_type().v,
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
ImageWithLocation{ .location = thumbnailLocation },
|
||||
ImageWithLocation{ .location = videoThumbnailLocation },
|
||||
session().mainDcId(),
|
||||
|
@ -2796,7 +2804,7 @@ void Session::documentApplyFields(
|
|||
void Session::documentApplyFields(
|
||||
not_null<DocumentData*> document,
|
||||
const MTPDdocument &data) {
|
||||
const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
|
||||
const auto inlineThumbnail = FindDocumentInlineThumbnail(data);
|
||||
const auto thumbnailSize = FindDocumentThumbnail(data);
|
||||
const auto videoThumbnailSize = FindDocumentVideoThumbnail(data);
|
||||
const auto prepared = Images::FromPhotoSize(
|
||||
|
@ -2813,7 +2821,7 @@ void Session::documentApplyFields(
|
|||
data.vdate().v,
|
||||
data.vattributes().v,
|
||||
qs(data.vmime_type()),
|
||||
inlineThumbnailBytes,
|
||||
inlineThumbnail,
|
||||
prepared,
|
||||
videoThumbnail,
|
||||
data.vdc_id().v,
|
||||
|
@ -2827,7 +2835,7 @@ void Session::documentApplyFields(
|
|||
TimeId date,
|
||||
const QVector<MTPDocumentAttribute> &attributes,
|
||||
const QString &mime,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail,
|
||||
int32 dc,
|
||||
|
@ -2838,7 +2846,7 @@ void Session::documentApplyFields(
|
|||
document->date = date;
|
||||
document->setMimeString(mime);
|
||||
document->updateThumbnails(
|
||||
inlineThumbnailBytes,
|
||||
inlineThumbnail,
|
||||
thumbnail,
|
||||
videoThumbnail);
|
||||
document->size = size;
|
||||
|
|
|
@ -476,7 +476,7 @@ public:
|
|||
TimeId date,
|
||||
const QVector<MTPDocumentAttribute> &attributes,
|
||||
const QString &mime,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail,
|
||||
int32 dc,
|
||||
|
@ -743,7 +743,7 @@ private:
|
|||
TimeId date,
|
||||
const QVector<MTPDocumentAttribute> &attributes,
|
||||
const QString &mime,
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const InlineImageLocation &inlineThumbnail,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &videoThumbnail,
|
||||
int32 dc,
|
||||
|
|
|
@ -106,7 +106,7 @@ enum class FilterType {
|
|||
base::unixtime::now(),
|
||||
QVector<MTPDocumentAttribute>(),
|
||||
QString(),
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
ImageWithLocation(),
|
||||
ImageWithLocation(),
|
||||
owner->session().mainDcId(),
|
||||
|
|
|
@ -150,11 +150,14 @@ QSize Sticker::GetAnimatedEmojiSize(
|
|||
|
||||
void Sticker::draw(Painter &p, const QRect &r, bool selected) {
|
||||
ensureDataMediaCreated();
|
||||
paintPath(p, r, selected);
|
||||
return;
|
||||
if (readyToDrawLottie()) {
|
||||
paintLottie(p, r, selected);
|
||||
} else if (_data->sticker()
|
||||
&& (!_data->sticker()->animated || !_replacements)) {
|
||||
paintPixmap(p, r, selected);
|
||||
} else if (!_data->sticker()
|
||||
|| (_data->sticker()->animated && _replacements)
|
||||
|| !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);
|
||||
if (!pixmap.isNull()) {
|
||||
p.drawPixmap(
|
||||
QPoint(
|
||||
r.x() + (r.width() - _size.width()) / 2,
|
||||
r.y() + (r.height() - _size.height()) / 2),
|
||||
pixmap);
|
||||
if (pixmap.isNull()) {
|
||||
return false;
|
||||
}
|
||||
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 {
|
||||
|
@ -306,7 +319,9 @@ void Sticker::dataMediaCreated() const {
|
|||
Expects(_dataMedia != nullptr);
|
||||
|
||||
_dataMedia->goodThumbnailWanted();
|
||||
_dataMedia->thumbnailWanted(_parent->data()->fullId());
|
||||
if (_dataMedia->thumbnailPath().isEmpty()) {
|
||||
_dataMedia->thumbnailWanted(_parent->data()->fullId());
|
||||
}
|
||||
_parent->history()->owner().registerHeavyViewPart(_parent);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,8 @@ public:
|
|||
private:
|
||||
[[nodiscard]] bool isEmojiSticker() const;
|
||||
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;
|
||||
|
||||
void ensureDataMediaCreated() const;
|
||||
|
|
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_photo_media.h"
|
||||
#include "data/data_document_media.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 "inline_bots/inline_bot_result.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();
|
||||
QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
|
||||
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,
|
||||
attributes,
|
||||
mime,
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
ImageWithLocation{
|
||||
.location = *thumb,
|
||||
.bytesCount = thumbnailByteSize
|
||||
|
|
|
@ -1995,7 +1995,7 @@ void Account::importOldRecentStickers() {
|
|||
date,
|
||||
attributes,
|
||||
mime,
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
ImageWithLocation(),
|
||||
ImageWithLocation(),
|
||||
dc,
|
||||
|
|
|
@ -98,7 +98,7 @@ void LoadAndApplyThumbnail(
|
|||
};
|
||||
|
||||
document->updateThumbnails(
|
||||
QByteArray(),
|
||||
InlineImageLocation(),
|
||||
imageWithLocation,
|
||||
ImageWithLocation{ .location = ImageLocation() });
|
||||
|
||||
|
|
|
@ -93,6 +93,206 @@ QImage FromInlineBytes(const QByteArray &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
|
||||
|
||||
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"
|
||||
|
||||
class QPainterPath;
|
||||
|
||||
namespace Images {
|
||||
|
||||
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
||||
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
||||
[[nodiscard]] QPainterPath PathFromInlineBytes(const QByteArray &bytes);
|
||||
|
||||
} // namespace Images
|
||||
|
||||
|
|
|
@ -620,6 +620,11 @@ struct ImageWithLocation {
|
|||
int progressivePartSize = 0;
|
||||
};
|
||||
|
||||
struct InlineImageLocation {
|
||||
QByteArray bytes;
|
||||
bool isPath = false;
|
||||
};
|
||||
|
||||
InMemoryKey inMemoryKey(const StorageFileLocation &location);
|
||||
|
||||
inline InMemoryKey inMemoryKey(const StorageImageLocation &location) {
|
||||
|
|
Loading…
Add table
Reference in a new issue