mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Allow sending animated stickers.
This commit is contained in:
parent
af85aec33b
commit
8b804d1995
3 changed files with 75 additions and 36 deletions
|
@ -21,8 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Lottie {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxSize = 1024 * 1024;
|
||||
|
||||
QByteArray UnpackGzip(const QByteArray &bytes) {
|
||||
z_stream stream;
|
||||
stream.zalloc = nullptr;
|
||||
|
@ -36,7 +34,7 @@ QByteArray UnpackGzip(const QByteArray &bytes) {
|
|||
}
|
||||
const auto guard = gsl::finally([&] { inflateEnd(&stream); });
|
||||
|
||||
auto result = QByteArray(kMaxSize + 1, Qt::Uninitialized);
|
||||
auto result = QByteArray(kMaxFileSize + 1, Qt::Uninitialized);
|
||||
stream.avail_in = bytes.size();
|
||||
stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(bytes.data()));
|
||||
stream.avail_out = 0;
|
||||
|
@ -84,34 +82,58 @@ std::unique_ptr<Animation> FromData(const QByteArray &data) {
|
|||
return std::make_unique<Animation>(base::duplicate(data));
|
||||
}
|
||||
|
||||
Animation::Animation(QByteArray &&content)
|
||||
: _timer([=] { checkNextFrameRender(); }) {
|
||||
const auto weak = base::make_weak(this);
|
||||
crl::async([=, content = base::take(content)]() mutable {
|
||||
content = UnpackGzip(content);
|
||||
if (content.size() > kMaxSize) {
|
||||
auto Init(QByteArray &&content)
|
||||
-> base::variant<std::unique_ptr<SharedState>, Error> {
|
||||
if (content.size() > kMaxFileSize) {
|
||||
qWarning()
|
||||
<< "Lottie Error: Too large file: "
|
||||
<< content.size();
|
||||
crl::on_main(weak, [=] {
|
||||
parseFailed();
|
||||
});
|
||||
return;
|
||||
return Error::ParseFailed;
|
||||
}
|
||||
content = UnpackGzip(content);
|
||||
if (content.size() > kMaxFileSize) {
|
||||
qWarning()
|
||||
<< "Lottie Error: Too large file: "
|
||||
<< content.size();
|
||||
return Error::ParseFailed;
|
||||
}
|
||||
const auto document = JsonDocument(std::move(content));
|
||||
if (const auto error = document.error()) {
|
||||
qWarning()
|
||||
<< "Lottie Error: Parse failed with code: "
|
||||
<< error;
|
||||
crl::on_main(weak, [=] {
|
||||
parseFailed();
|
||||
});
|
||||
} else {
|
||||
auto state = std::make_unique<SharedState>(document.root());
|
||||
crl::on_main(weak, [this, result = std::move(state)]() mutable {
|
||||
parseDone(std::move(result));
|
||||
return Error::ParseFailed;
|
||||
}
|
||||
auto result = std::make_unique<SharedState>(document.root());
|
||||
auto information = result->information();
|
||||
if (!information.frameRate
|
||||
|| information.framesCount <= 0
|
||||
|| information.size.isEmpty()) {
|
||||
return Error::NotSupported;
|
||||
}
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
QImage ReadThumbnail(QByteArray &&content) {
|
||||
return Init(std::move(content)).match([](
|
||||
const std::unique_ptr<SharedState> &state) {
|
||||
return state->frameForPaint()->original;
|
||||
}, [](Error) {
|
||||
return QImage();
|
||||
});
|
||||
}
|
||||
|
||||
Animation::Animation(QByteArray &&content)
|
||||
: _timer([=] { checkNextFrameRender(); }) {
|
||||
const auto weak = base::make_weak(this);
|
||||
crl::async([=, content = base::take(content)]() mutable {
|
||||
crl::on_main(weak, [this, result = Init(std::move(content))]() mutable {
|
||||
result.match([&](std::unique_ptr<SharedState> &state) {
|
||||
parseDone(std::move(state));
|
||||
}, [&](Error error) {
|
||||
parseFailed(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -126,12 +148,6 @@ void Animation::parseDone(std::unique_ptr<SharedState> state) {
|
|||
Expects(state != nullptr);
|
||||
|
||||
auto information = state->information();
|
||||
if (!information.frameRate
|
||||
|| information.framesCount <= 0
|
||||
|| information.size.isEmpty()) {
|
||||
_updates.fire_error(Error::NotSupported);
|
||||
return;
|
||||
}
|
||||
_state = state.get();
|
||||
_state->start(this, crl::now());
|
||||
_renderer = FrameRenderer::Instance();
|
||||
|
@ -144,8 +160,8 @@ void Animation::parseDone(std::unique_ptr<SharedState> state) {
|
|||
}, _lifetime);
|
||||
}
|
||||
|
||||
void Animation::parseFailed() {
|
||||
_updates.fire_error(Error::ParseFailed);
|
||||
void Animation::parseFailed(Error error) {
|
||||
_updates.fire_error(std::move(error));
|
||||
}
|
||||
|
||||
QImage Animation::frame(const FrameRequest &request) const {
|
||||
|
|
|
@ -25,6 +25,8 @@ class QByteArray;
|
|||
|
||||
namespace Lottie {
|
||||
|
||||
constexpr auto kMaxFileSize = 1024 * 1024;
|
||||
|
||||
class Animation;
|
||||
class SharedState;
|
||||
class FrameRenderer;
|
||||
|
@ -33,6 +35,8 @@ bool ValidateFile(const QString &path);
|
|||
std::unique_ptr<Animation> FromFile(const QString &path);
|
||||
std::unique_ptr<Animation> FromData(const QByteArray &data);
|
||||
|
||||
QImage ReadThumbnail(QByteArray &&content);
|
||||
|
||||
class Animation final : public base::has_weak_ptr {
|
||||
public:
|
||||
explicit Animation(QByteArray &&content);
|
||||
|
@ -54,7 +58,7 @@ public:
|
|||
|
||||
private:
|
||||
void parseDone(std::unique_ptr<SharedState> state);
|
||||
void parseFailed();
|
||||
void parseFailed(Error error);
|
||||
|
||||
void checkNextFrameAvailability();
|
||||
void checkNextFrameRender();
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "core/mime_type.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
#include "lottie/lottie_animation.h"
|
||||
#include "history/history_item.h"
|
||||
#include "boxes/send_files_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
|
@ -74,6 +75,18 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) {
|
|||
return result;
|
||||
}
|
||||
|
||||
PreparedFileThumbnail PrepareAnimatedStickerThumbnail(
|
||||
const QString &file,
|
||||
const QByteArray &bytes) {
|
||||
return PrepareFileThumbnail(Lottie::ReadThumbnail([&] {
|
||||
if (!bytes.isEmpty()) {
|
||||
return bytes;
|
||||
}
|
||||
auto f = QFile(file);
|
||||
return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray();
|
||||
}()));
|
||||
}
|
||||
|
||||
bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) {
|
||||
constexpr auto kThumbnailUploadBySize = 5 * 1024 * 1024;
|
||||
const auto kThumbnailKnownMimes = {
|
||||
|
@ -817,6 +830,10 @@ void FileLoadTask::process() {
|
|||
QByteArray goodThumbnailBytes;
|
||||
|
||||
QVector<MTPDocumentAttribute> attributes(1, MTP_documentAttributeFilename(MTP_string(filename)));
|
||||
const auto checkAnimatedSticker = filename.endsWith(qstr(".tgs"), Qt::CaseInsensitive);
|
||||
if (checkAnimatedSticker) {
|
||||
filemime = "application/x-tgsticker";
|
||||
}
|
||||
|
||||
auto thumbnail = PreparedFileThumbnail();
|
||||
|
||||
|
@ -855,6 +872,8 @@ void FileLoadTask::process() {
|
|||
}
|
||||
|
||||
thumbnail = PrepareFileThumbnail(std::move(video->thumbnail));
|
||||
} else if (checkAnimatedSticker) {
|
||||
thumbnail = PrepareAnimatedStickerThumbnail(_filepath, _content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,7 +937,7 @@ void FileLoadTask::process() {
|
|||
std::move(thumbnail),
|
||||
filemime,
|
||||
filesize,
|
||||
isSticker);
|
||||
isSticker || checkAnimatedSticker);
|
||||
|
||||
if (_type == SendMediaType::Photo && photo.type() == mtpc_photoEmpty) {
|
||||
_type = SendMediaType::File;
|
||||
|
|
Loading…
Add table
Reference in a new issue