mirror of https://github.com/procxx/kepka.git
				
				
				
			Fix caching for large sticker area.
This commit is contained in:
		
							parent
							
								
									808583c5ae
								
							
						
					
					
						commit
						4a7b5a8e01
					
				|  | @ -23,6 +23,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | ||||||
| #include "styles/style_chat_helpers.h" | #include "styles/style_chat_helpers.h" | ||||||
| 
 | 
 | ||||||
| namespace Stickers { | namespace Stickers { | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | constexpr auto kDontCacheLottieAfterArea = 512 * 512; | ||||||
|  | 
 | ||||||
|  | } // namespace
 | ||||||
| 
 | 
 | ||||||
| void ApplyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { | void ApplyArchivedResult(const MTPDmessages_stickerSetInstallResultArchive &d) { | ||||||
| 	auto &v = d.vsets.v; | 	auto &v = d.vsets.v; | ||||||
|  | @ -1093,6 +1098,13 @@ std::unique_ptr<Lottie::Animation> LottieFromDocument( | ||||||
| 		QSize box) { | 		QSize box) { | ||||||
| 	const auto data = document->data(); | 	const auto data = document->data(); | ||||||
| 	const auto filepath = document->filepath(); | 	const auto filepath = document->filepath(); | ||||||
|  | 	if (box.width() & box.height() > kDontCacheLottieAfterArea) { | ||||||
|  | 		// Don't use frame caching for large stickers.
 | ||||||
|  | 		return Lottie::FromContent( | ||||||
|  | 			data, | ||||||
|  | 			filepath, | ||||||
|  | 			Lottie::FrameRequest{ box }); | ||||||
|  | 	} | ||||||
| 	if (const auto baseKey = document->bigFileBaseCacheKey()) { | 	if (const auto baseKey = document->bigFileBaseCacheKey()) { | ||||||
| 		const auto key = Storage::Cache::Key{ | 		const auto key = Storage::Cache::Key{ | ||||||
| 			baseKey->high, | 			baseKey->high, | ||||||
|  | @ -1116,7 +1128,7 @@ std::unique_ptr<Lottie::Animation> LottieFromDocument( | ||||||
| 			filepath, | 			filepath, | ||||||
| 			Lottie::FrameRequest{ box }); | 			Lottie::FrameRequest{ box }); | ||||||
| 	} | 	} | ||||||
| 	return Lottie::FromContent(data, filepath); | 	return Lottie::FromContent(data, filepath, Lottie::FrameRequest{ box }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Stickers
 | } // namespace Stickers
 | ||||||
|  |  | ||||||
|  | @ -185,8 +185,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, c | ||||||
| 			pixmap); | 			pixmap); | ||||||
| 	} else if (lottieReady) { | 	} else if (lottieReady) { | ||||||
| 		auto request = Lottie::FrameRequest(); | 		auto request = Lottie::FrameRequest(); | ||||||
| 		request.box = QSize(st::maxStickerSize, st::maxStickerSize) | 		request.box = QSize(_pixw, _pixh) * cIntRetinaFactor(); | ||||||
| 			* cIntRetinaFactor(); |  | ||||||
| 		if (selected) { | 		if (selected) { | ||||||
| 			request.colored = st::msgStickerOverlay->c; | 			request.colored = st::msgStickerOverlay->c; | ||||||
| 		} | 		} | ||||||
|  | @ -194,9 +193,15 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, c | ||||||
| 		if (!paused) { | 		if (!paused) { | ||||||
| 			_lottie->markFrameShown(); | 			_lottie->markFrameShown(); | ||||||
| 		} | 		} | ||||||
|  | 		const auto frame = _lottie->frame(request); | ||||||
|  | 		const auto size = frame.size() / cIntRetinaFactor(); | ||||||
| 		p.drawImage( | 		p.drawImage( | ||||||
| 			QRect(usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2, _pixw, _pixh), | 			QRect( | ||||||
| 			_lottie->frame(request)); | 				QPoint( | ||||||
|  | 					usex + (usew - size.width()) / 2, | ||||||
|  | 					(minHeight() - size.height()) / 2), | ||||||
|  | 				size), | ||||||
|  | 			frame); | ||||||
| 	} | 	} | ||||||
| 	if (!inWebPage) { | 	if (!inWebPage) { | ||||||
| 		auto fullRight = usex + usew; | 		auto fullRight = usex + usew; | ||||||
|  |  | ||||||
|  | @ -87,14 +87,17 @@ details::InitData CheckSharedState(std::unique_ptr<SharedState> state) { | ||||||
| 	return state; | 	return state; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| details::InitData Init(const QByteArray &content) { | details::InitData Init( | ||||||
|  | 		const QByteArray &content, | ||||||
|  | 		const FrameRequest &request) { | ||||||
| 	if (const auto error = ContentError(content)) { | 	if (const auto error = ContentError(content)) { | ||||||
| 		return *error; | 		return *error; | ||||||
| 	} | 	} | ||||||
| 	auto animation = details::CreateFromContent(content); | 	auto animation = details::CreateFromContent(content); | ||||||
| 	return animation | 	return animation | ||||||
| 		? CheckSharedState(std::make_unique<SharedState>( | 		? CheckSharedState(std::make_unique<SharedState>( | ||||||
| 			std::move(animation))) | 			std::move(animation), | ||||||
|  | 			request)) | ||||||
| 		: Error::ParseFailed; | 		: Error::ParseFailed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -139,8 +142,9 @@ std::unique_ptr<rlottie::Animation> CreateFromContent( | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Animation> FromContent( | std::unique_ptr<Animation> FromContent( | ||||||
| 		const QByteArray &data, | 		const QByteArray &data, | ||||||
| 		const QString &filepath) { | 		const QString &filepath, | ||||||
| 	return std::make_unique<Animation>(ReadContent(data, filepath)); | 		const FrameRequest &request) { | ||||||
|  | 	return std::make_unique<Animation>(ReadContent(data, filepath), request); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Animation> FromCached( | std::unique_ptr<Animation> FromCached( | ||||||
|  | @ -157,7 +161,7 @@ std::unique_ptr<Animation> FromCached( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| QImage ReadThumbnail(const QByteArray &content) { | QImage ReadThumbnail(const QByteArray &content) { | ||||||
| 	return Init(std::move(content)).match([]( | 	return Init(content, FrameRequest()).match([]( | ||||||
| 		const std::unique_ptr<SharedState> &state) { | 		const std::unique_ptr<SharedState> &state) { | ||||||
| 		return state->frameForPaint()->original; | 		return state->frameForPaint()->original; | ||||||
| 	}, [](Error) { | 	}, [](Error) { | ||||||
|  | @ -165,11 +169,11 @@ QImage ReadThumbnail(const QByteArray &content) { | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Animation::Animation(const QByteArray &content) | Animation::Animation(const QByteArray &content, const FrameRequest &request) | ||||||
| : _timer([=] { checkNextFrameRender(); }) { | : _timer([=] { checkNextFrameRender(); }) { | ||||||
| 	const auto weak = base::make_weak(this); | 	const auto weak = base::make_weak(this); | ||||||
| 	crl::async([=] { | 	crl::async([=] { | ||||||
| 		crl::on_main(weak, [=, data = Init(content)]() mutable { | 		crl::on_main(weak, [=, data = Init(content, request)]() mutable { | ||||||
| 			initDone(std::move(data)); | 			initDone(std::move(data)); | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
|  | @ -37,7 +37,8 @@ class FrameRenderer; | ||||||
| 
 | 
 | ||||||
| std::unique_ptr<Animation> FromContent( | std::unique_ptr<Animation> FromContent( | ||||||
| 	const QByteArray &data, | 	const QByteArray &data, | ||||||
| 		const QString &filepath); | 	const QString &filepath, | ||||||
|  | 	const FrameRequest &request); | ||||||
| std::unique_ptr<Animation> FromCached( | std::unique_ptr<Animation> FromCached( | ||||||
| 	FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
 | 	FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
 | ||||||
| 	FnMut<void(QByteArray &&cached)> put, // Unknown thread.
 | 	FnMut<void(QByteArray &&cached)> put, // Unknown thread.
 | ||||||
|  | @ -58,7 +59,9 @@ std::unique_ptr<rlottie::Animation> CreateFromContent( | ||||||
| 
 | 
 | ||||||
| class Animation final : public base::has_weak_ptr { | class Animation final : public base::has_weak_ptr { | ||||||
| public: | public: | ||||||
| 	explicit Animation(const QByteArray &content); | 	explicit Animation( | ||||||
|  | 		const QByteArray &content, | ||||||
|  | 		const FrameRequest &request); | ||||||
| 	Animation( | 	Animation( | ||||||
| 		FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
 | 		FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
 | ||||||
| 		FnMut<void(QByteArray &&cached)> put, // Unknown thread.
 | 		FnMut<void(QByteArray &&cached)> put, // Unknown thread.
 | ||||||
|  |  | ||||||
|  | @ -23,6 +23,9 @@ namespace { | ||||||
| 
 | 
 | ||||||
| constexpr auto kAlignStorage = 16; | constexpr auto kAlignStorage = 16; | ||||||
| 
 | 
 | ||||||
|  | // Must not exceed max database allowed entry size.
 | ||||||
|  | constexpr auto kMaxCacheSize = 10 * 1024 * 1024; | ||||||
|  | 
 | ||||||
| void Xor(EncodedStorage &to, const EncodedStorage &from) { | void Xor(EncodedStorage &to, const EncodedStorage &from) { | ||||||
| 	Expects(to.size() == from.size()); | 	Expects(to.size() == from.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -451,7 +454,7 @@ bool Cache::readHeader(const FrameRequest &request) { | ||||||
| 	} | 	} | ||||||
| 	QDataStream stream(&_data, QIODevice::ReadOnly); | 	QDataStream stream(&_data, QIODevice::ReadOnly); | ||||||
| 
 | 
 | ||||||
| 	auto encoder = quint32(0); | 	auto encoder = qint32(0); | ||||||
| 	stream >> encoder; | 	stream >> encoder; | ||||||
| 	if (static_cast<Encoder>(encoder) != Encoder::YUV420A4_LZ4) { | 	if (static_cast<Encoder>(encoder) != Encoder::YUV420A4_LZ4) { | ||||||
| 		return false; | 		return false; | ||||||
|  | @ -542,13 +545,26 @@ void Cache::appendFrame( | ||||||
| 		prepareBuffers(); | 		prepareBuffers(); | ||||||
| 	} | 	} | ||||||
| 	Assert(frame.size() == _size); | 	Assert(frame.size() == _size); | ||||||
|  | 	const auto now = crl::profile(); | ||||||
| 	Encode(_uncompressed, frame, _encode.cache, _encode.context); | 	Encode(_uncompressed, frame, _encode.cache, _encode.context); | ||||||
|  | 	const auto enc = crl::profile(); | ||||||
| 	CompressAndSwapFrame( | 	CompressAndSwapFrame( | ||||||
| 		_encode.compressBuffer, | 		_encode.compressBuffer, | ||||||
| 		(index != 0) ? &_encode.xorCompressBuffer : nullptr, | 		(index != 0) ? &_encode.xorCompressBuffer : nullptr, | ||||||
| 		_uncompressed, | 		_uncompressed, | ||||||
| 		_previous); | 		_previous); | ||||||
| 	_encode.compressedFrames.push_back(_encode.compressBuffer); | 	_encode.compress += crl::profile() - enc; | ||||||
|  | 	_encode.encode += enc - now; | ||||||
|  | 	const auto compressed = _encode.compressBuffer; | ||||||
|  | 	const auto nowSize = (_data.isEmpty() ? headerSize() : _data.size()) | ||||||
|  | 		+ _encode.totalSize; | ||||||
|  | 	const auto totalSize = nowSize + compressed.size(); | ||||||
|  | 	if (nowSize <= kMaxCacheSize && totalSize > kMaxCacheSize) { | ||||||
|  | 		// Write to cache while we still can.
 | ||||||
|  | 		finalizeEncoding(); | ||||||
|  | 	} | ||||||
|  | 	_encode.totalSize += compressed.size(); | ||||||
|  | 	_encode.compressedFrames.push_back(compressed); | ||||||
| 	_encode.compressedFrames.back().detach(); | 	_encode.compressedFrames.back().detach(); | ||||||
| 	if (++_framesReady == _framesCount) { | 	if (++_framesReady == _framesCount) { | ||||||
| 		finalizeEncoding(); | 		finalizeEncoding(); | ||||||
|  | @ -560,31 +576,26 @@ void Cache::finalizeEncoding() { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	const auto size = (_data.isEmpty() ? headerSize() : _data.size()) | 	const auto size = (_data.isEmpty() ? headerSize() : _data.size()) | ||||||
| 		+ ranges::accumulate( | 		+ _encode.totalSize; | ||||||
| 			_encode.compressedFrames, |  | ||||||
| 			0, |  | ||||||
| 			std::plus(), |  | ||||||
| 			&QByteArray::size); |  | ||||||
| 	if (_data.isEmpty()) { | 	if (_data.isEmpty()) { | ||||||
| 		_data.reserve(size); | 		_data.reserve(size); | ||||||
| 		writeHeader(); | 		writeHeader(); | ||||||
| 	} | 	} | ||||||
| 	auto xored = 0; |  | ||||||
| 	const auto offset = _data.size(); | 	const auto offset = _data.size(); | ||||||
| 	_data.resize(size); | 	_data.resize(size); | ||||||
| 	auto to = _data.data() + offset; | 	auto to = _data.data() + offset; | ||||||
| 	for (const auto &block : _encode.compressedFrames) { | 	for (const auto &block : _encode.compressedFrames) { | ||||||
| 		const auto amount = qint32(block.size()); | 		const auto amount = qint32(block.size()); | ||||||
| 		memcpy(to, block.data(), amount); | 		memcpy(to, block.data(), amount); | ||||||
| 		if (*reinterpret_cast<const qint32*>(block.data()) < 0) { |  | ||||||
| 			++xored; |  | ||||||
| 		} |  | ||||||
| 		to += amount; | 		to += amount; | ||||||
| 	} | 	} | ||||||
|  | 	if (_data.size() <= kMaxCacheSize) { | ||||||
|  | 		_put(QByteArray(_data)); | ||||||
|  | 		LOG(("SIZE: %1 (%2x%3, %4 frames, %5 encode, %6 compress)").arg(_data.size()).arg(_size.width()).arg(_size.height()).arg(_framesCount).arg(_encode.encode / float64(_encode.compressedFrames.size())).arg(_encode.compress / float64(_encode.compressedFrames.size()))); | ||||||
|  | 	} else { | ||||||
|  | 		LOG(("WARNING: %1 (%2x%3, %4 frames, %5 encode, %6 compress)").arg(_data.size()).arg(_size.width()).arg(_size.height()).arg(_framesCount).arg(_encode.encode / float64(_encode.compressedFrames.size())).arg(_encode.compress / float64(_encode.compressedFrames.size()))); | ||||||
|  | 	} | ||||||
| 	_encode = EncodeFields(); | 	_encode = EncodeFields(); | ||||||
| 	LOG(("SIZE: %1 (%2x%3, %4 frames, %5 xored)").arg(_data.size()).arg(_size.width()).arg(_size.height()).arg(_framesCount).arg(xored)); |  | ||||||
| 
 |  | ||||||
| 	_put(QByteArray(_data)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int Cache::headerSize() const { | int Cache::headerSize() const { | ||||||
|  |  | ||||||
|  | @ -94,6 +94,9 @@ private: | ||||||
| 		QByteArray xorCompressBuffer; | 		QByteArray xorCompressBuffer; | ||||||
| 		QImage cache; | 		QImage cache; | ||||||
| 		FFmpeg::SwscalePointer context; | 		FFmpeg::SwscalePointer context; | ||||||
|  | 		int totalSize = 0; | ||||||
|  | 		crl::profile_time encode = 0; | ||||||
|  | 		crl::profile_time compress = 0; | ||||||
| 	}; | 	}; | ||||||
| 	int headerSize() const; | 	int headerSize() const; | ||||||
| 	void prepareBuffers(); | 	void prepareBuffers(); | ||||||
|  |  | ||||||
|  | @ -177,9 +177,11 @@ void FrameRendererObject::queueGenerateFrames() { | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SharedState::SharedState(std::unique_ptr<rlottie::Animation> animation) | SharedState::SharedState( | ||||||
|  | 	std::unique_ptr<rlottie::Animation> animation, | ||||||
|  | 	const FrameRequest &request) | ||||||
| : _animation(std::move(animation)) { | : _animation(std::move(animation)) { | ||||||
| 	construct(FrameRequest()); | 	construct(request); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SharedState::SharedState( | SharedState::SharedState( | ||||||
|  |  | ||||||
|  | @ -46,7 +46,9 @@ QImage PrepareFrameByRequest( | ||||||
| 
 | 
 | ||||||
| class SharedState { | class SharedState { | ||||||
| public: | public: | ||||||
| 	explicit SharedState(std::unique_ptr<rlottie::Animation> animation); | 	SharedState( | ||||||
|  | 		std::unique_ptr<rlottie::Animation> animation, | ||||||
|  | 		const FrameRequest &request); | ||||||
| 	SharedState( | 	SharedState( | ||||||
| 		const QByteArray &content, | 		const QByteArray &content, | ||||||
| 		std::unique_ptr<rlottie::Animation> animation, | 		std::unique_ptr<rlottie::Animation> animation, | ||||||
|  |  | ||||||
|  | @ -20,7 +20,9 @@ namespace Cache { | ||||||
| struct Key; | struct Key; | ||||||
| } // namespace Cache
 | } // namespace Cache
 | ||||||
| 
 | 
 | ||||||
|  | // This value is used in local cache database settings!
 | ||||||
| constexpr auto kMaxFileInMemory = 10 * 1024 * 1024; // 10 MB max file could be hold in memory
 | constexpr auto kMaxFileInMemory = 10 * 1024 * 1024; // 10 MB max file could be hold in memory
 | ||||||
|  | 
 | ||||||
| constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024; // 2 MB audio is hold in memory and auto loaded
 | constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024; // 2 MB audio is hold in memory and auto loaded
 | ||||||
| constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024; // 2 MB stickers hold in memory, auto loaded and displayed inline
 | constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024; // 2 MB stickers hold in memory, auto loaded and displayed inline
 | ||||||
| constexpr auto kMaxWallPaperInMemory = kMaxFileInMemory; | constexpr auto kMaxWallPaperInMemory = kMaxFileInMemory; | ||||||
|  |  | ||||||
|  | @ -1049,7 +1049,10 @@ QSize MediaPreviewWidget::currentDimensions() const { | ||||||
| void MediaPreviewWidget::setupLottie() { | void MediaPreviewWidget::setupLottie() { | ||||||
| 	Expects(_document != nullptr); | 	Expects(_document != nullptr); | ||||||
| 
 | 
 | ||||||
| 	_lottie = Lottie::FromContent(_document->data(), _document->filepath()); | 	_lottie = Lottie::FromContent( | ||||||
|  | 		_document->data(), | ||||||
|  | 		_document->filepath(), | ||||||
|  | 		Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }); | ||||||
| 
 | 
 | ||||||
| 	_lottie->updates( | 	_lottie->updates( | ||||||
| 	) | rpl::start_with_next_error([=](Lottie::Update update) { | 	) | rpl::start_with_next_error([=](Lottie::Update update) { | ||||||
|  |  | ||||||
|  | @ -1 +1 @@ | ||||||
| Subproject commit d259aebc11df52cb6ff8c738580dc4d8f245d681 | Subproject commit 1f0d4470b1234e31c75a4186abd59759d8142414 | ||||||
		Loading…
	
		Reference in New Issue