191 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop application for the Telegram messaging service.
 | |
| 
 | |
| For license and copyright information please follow this link:
 | |
| https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | |
| */
 | |
| #include "chat_helpers/stickers_lottie.h"
 | |
| 
 | |
| #include "lottie/lottie_single_player.h"
 | |
| #include "lottie/lottie_multi_player.h"
 | |
| #include "data/stickers/data_stickers_set.h"
 | |
| #include "data/data_document.h"
 | |
| #include "data/data_document_media.h"
 | |
| #include "data/data_session.h"
 | |
| #include "data/data_file_origin.h"
 | |
| #include "storage/cache/storage_cache_database.h"
 | |
| #include "main/main_session.h"
 | |
| 
 | |
| namespace ChatHelpers {
 | |
| namespace {
 | |
| 
 | |
| constexpr auto kDontCacheLottieAfterArea = 512 * 512;
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| template <typename Method>
 | |
| auto LottieCachedFromContent(
 | |
| 		Method &&method,
 | |
| 		Storage::Cache::Key baseKey,
 | |
| 		uint8 keyShift,
 | |
| 		not_null<Main::Session*> session,
 | |
| 		const QByteArray &content,
 | |
| 		QSize box) {
 | |
| 	const auto key = Storage::Cache::Key{
 | |
| 		baseKey.high,
 | |
| 		baseKey.low + keyShift
 | |
| 	};
 | |
| 	const auto get = [=](FnMut<void(QByteArray &&cached)> handler) {
 | |
| 		session->data().cacheBigFile().get(
 | |
| 			key,
 | |
| 			std::move(handler));
 | |
| 	};
 | |
| 	const auto weak = base::make_weak(session.get());
 | |
| 	const auto put = [=](QByteArray &&cached) {
 | |
| 		crl::on_main(weak, [=, data = std::move(cached)]() mutable {
 | |
| 			weak->data().cacheBigFile().put(key, std::move(data));
 | |
| 		});
 | |
| 	};
 | |
| 	return method(
 | |
| 		get,
 | |
| 		put,
 | |
| 		content,
 | |
| 		Lottie::FrameRequest{ box });
 | |
| }
 | |
| 
 | |
| template <typename Method>
 | |
| auto LottieFromDocument(
 | |
| 		Method &&method,
 | |
| 		not_null<Data::DocumentMedia*> media,
 | |
| 		uint8 keyShift,
 | |
| 		QSize box) {
 | |
| 	const auto document = media->owner();
 | |
| 	const auto data = media->bytes();
 | |
| 	const auto filepath = document->filepath();
 | |
| 	if (box.width() * box.height() > kDontCacheLottieAfterArea) {
 | |
| 		// Don't use frame caching for large stickers.
 | |
| 		return method(
 | |
| 			Lottie::ReadContent(data, filepath),
 | |
| 			Lottie::FrameRequest{ box });
 | |
| 	}
 | |
| 	if (const auto baseKey = document->bigFileBaseCacheKey()) {
 | |
| 		return LottieCachedFromContent(
 | |
| 			std::forward<Method>(method),
 | |
| 			baseKey,
 | |
| 			keyShift,
 | |
| 			&document->session(),
 | |
| 			Lottie::ReadContent(data, filepath),
 | |
| 			box);
 | |
| 	}
 | |
| 	return method(
 | |
| 		Lottie::ReadContent(data, filepath),
 | |
| 		Lottie::FrameRequest{ box });
 | |
| }
 | |
| 
 | |
| std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
 | |
| 		not_null<Data::DocumentMedia*> media,
 | |
| 		StickerLottieSize sizeTag,
 | |
| 		QSize box,
 | |
| 		Lottie::Quality quality,
 | |
| 		std::shared_ptr<Lottie::FrameRenderer> renderer) {
 | |
| 	return LottiePlayerFromDocument(
 | |
| 		media,
 | |
| 		nullptr,
 | |
| 		sizeTag,
 | |
| 		box,
 | |
| 		quality,
 | |
| 		std::move(renderer));
 | |
| }
 | |
| 
 | |
| std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
 | |
| 		not_null<Data::DocumentMedia*> media,
 | |
| 		const Lottie::ColorReplacements *replacements,
 | |
| 		StickerLottieSize sizeTag,
 | |
| 		QSize box,
 | |
| 		Lottie::Quality quality,
 | |
| 		std::shared_ptr<Lottie::FrameRenderer> renderer) {
 | |
| 	const auto method = [&](auto &&...args) {
 | |
| 		return std::make_unique<Lottie::SinglePlayer>(
 | |
| 			std::forward<decltype(args)>(args)...,
 | |
| 			quality,
 | |
| 			replacements,
 | |
| 			std::move(renderer));
 | |
| 	};
 | |
| 	const auto tag = replacements ? replacements->tag : uint8(0);
 | |
| 	const auto keyShift = ((tag << 4) & 0xF0) | (uint8(sizeTag) & 0x0F);
 | |
| 	return LottieFromDocument(method, media, uint8(keyShift), box);
 | |
| }
 | |
| 
 | |
| not_null<Lottie::Animation*> LottieAnimationFromDocument(
 | |
| 		not_null<Lottie::MultiPlayer*> player,
 | |
| 		not_null<Data::DocumentMedia*> media,
 | |
| 		StickerLottieSize sizeTag,
 | |
| 		QSize box) {
 | |
| 	const auto method = [&](auto &&...args) {
 | |
| 		return player->append(std::forward<decltype(args)>(args)...);
 | |
| 	};
 | |
| 	return LottieFromDocument(method, media, uint8(sizeTag), box);
 | |
| }
 | |
| 
 | |
| bool HasLottieThumbnail(
 | |
| 		Data::StickersSetThumbnailView *thumb,
 | |
| 		Data::DocumentMedia *media) {
 | |
| 	if (thumb) {
 | |
| 		return !thumb->content().isEmpty();
 | |
| 	} else if (!media) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	const auto document = media->owner();
 | |
| 	if (const auto info = document->sticker()) {
 | |
| 		if (!info->animated) {
 | |
| 			return false;
 | |
| 		}
 | |
| 		media->automaticLoad(document->stickerSetOrigin(), nullptr);
 | |
| 		if (!media->loaded()) {
 | |
| 			return false;
 | |
| 		}
 | |
| 		return document->bigFileBaseCacheKey().valid();
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
 | |
| 		Data::StickersSetThumbnailView *thumb,
 | |
| 		Data::DocumentMedia *media,
 | |
| 		StickerLottieSize sizeTag,
 | |
| 		QSize box,
 | |
| 		std::shared_ptr<Lottie::FrameRenderer> renderer) {
 | |
| 	const auto baseKey = thumb
 | |
| 		? thumb->owner()->thumbnailLocation().file().bigFileBaseCacheKey()
 | |
| 		: media
 | |
| 		? media->owner()->bigFileBaseCacheKey()
 | |
| 		: Storage::Cache::Key();
 | |
| 	if (!baseKey) {
 | |
| 		return nullptr;
 | |
| 	}
 | |
| 	const auto content = thumb
 | |
| 		? thumb->content()
 | |
| 		: Lottie::ReadContent(media->bytes(), media->owner()->filepath());
 | |
| 	if (content.isEmpty()) {
 | |
| 		return nullptr;
 | |
| 	}
 | |
| 	const auto method = [](auto &&...args) {
 | |
| 		return std::make_unique<Lottie::SinglePlayer>(
 | |
| 			std::forward<decltype(args)>(args)...);
 | |
| 	};
 | |
| 	const auto session = thumb
 | |
| 		? &thumb->owner()->session()
 | |
| 		: media
 | |
| 		? &media->owner()->session()
 | |
| 		: nullptr;
 | |
| 	return LottieCachedFromContent(
 | |
| 		method,
 | |
| 		baseKey,
 | |
| 		uint8(sizeTag),
 | |
| 		session,
 | |
| 		content,
 | |
| 		box);
 | |
| }
 | |
| 
 | |
| } // namespace ChatHelpers
 | 
