diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp index 7c582784a..1e2910e60 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" #include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_round_preview.h" +#include "storage/storage_account.h" #include "ui/controls/round_video_recorder.h" #include "ui/controls/send_button.h" #include "ui/effects/animation_value.h" @@ -2362,7 +2363,13 @@ bool VoiceRecordBar::createVideoRecorder() { .hidden = hidden, .capturer = std::move(capturer), .track = std::move(track), + .placeholder = _show->session().local().readRoundPlaceholder(), }); + _videoRecorder->placeholderUpdates( + ) | rpl::start_with_next([=](QImage &&placeholder) { + _show->session().local().writeRoundPlaceholder(placeholder); + }, _videoCapturerLifetime); + return true; } diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index a24a6e6fc..5af0677c5 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -94,6 +94,7 @@ enum { // Local Storage Keys lskCustomEmojiKeys = 0x17, // no data lskSearchSuggestions = 0x18, // no data lskWebviewTokens = 0x19, // data: QByteArray bots, QByteArray other + lskRoundPlaceholder = 0x1a, // no data }; auto EmptyMessageDraftSources() @@ -220,6 +221,7 @@ base::flat_set Account::collectGoodNames() const { _featuredCustomEmojiKey, _archivedCustomEmojiKey, _searchSuggestionsKey, + _roundPlaceholderKey, }; auto result = base::flat_set{ "map0", @@ -306,6 +308,7 @@ Account::ReadMapResult Account::readMapWith( quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0; quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0; quint64 searchSuggestionsKey = 0; + quint64 roundPlaceholderKey = 0; QByteArray webviewStorageTokenBots, webviewStorageTokenOther; while (!map.stream.atEnd()) { quint32 keyType; @@ -415,6 +418,9 @@ Account::ReadMapResult Account::readMapWith( case lskSearchSuggestions: { map.stream >> searchSuggestionsKey; } break; + case lskRoundPlaceholder: { + map.stream >> roundPlaceholderKey; + } break; case lskWebviewTokens: { map.stream >> webviewStorageTokenBots @@ -456,6 +462,7 @@ Account::ReadMapResult Account::readMapWith( _recentHashtagsAndBotsKey = recentHashtagsAndBotsKey; _exportSettingsKey = exportSettingsKey; _searchSuggestionsKey = searchSuggestionsKey; + _roundPlaceholderKey = roundPlaceholderKey; _oldMapVersion = mapData.version; _webviewStorageIdBots.token = webviewStorageTokenBots; _webviewStorageIdOther.token = webviewStorageTokenOther; @@ -570,6 +577,7 @@ void Account::writeMap() { + Serialize::bytearraySize(_webviewStorageIdBots.token) + Serialize::bytearraySize(_webviewStorageIdOther.token); } + if (_roundPlaceholderKey) mapSize += sizeof(quint32) + sizeof(quint64); EncryptedDescriptor mapData(mapSize); if (!self.isEmpty()) { @@ -640,6 +648,10 @@ void Account::writeMap() { << _webviewStorageIdBots.token << _webviewStorageIdOther.token; } + if (_roundPlaceholderKey) { + mapData.stream << quint32(lskRoundPlaceholder); + mapData.stream << quint64(_roundPlaceholderKey); + } map.writeEncrypted(mapData, _localKey); _mapChanged = false; @@ -669,6 +681,7 @@ void Account::reset() { _legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0; _settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0; _searchSuggestionsKey = 0; + _roundPlaceholderKey = 0; _oldMapVersion = 0; _fileLocations.clear(); _fileLocationPairs.clear(); @@ -3178,6 +3191,52 @@ Webview::StorageId Account::resolveStorageIdOther() { return _webviewStorageIdOther; } +QImage Account::readRoundPlaceholder() { + if (!_roundPlaceholder.isNull()) { + return _roundPlaceholder; + } else if (!_roundPlaceholderKey) { + return QImage(); + } + + FileReadDescriptor placeholder; + if (!ReadEncryptedFile( + placeholder, + _roundPlaceholderKey, + _basePath, + _localKey)) { + ClearKey(_roundPlaceholderKey, _basePath); + _roundPlaceholderKey = 0; + writeMapDelayed(); + return QImage(); + } + + auto bytes = QByteArray(); + placeholder.stream >> bytes; + _roundPlaceholder = Images::Read({ .content = bytes }).image; + return _roundPlaceholder; +} + +void Account::writeRoundPlaceholder(const QImage &placeholder) { + if (placeholder.isNull()) { + return; + } + _roundPlaceholder = placeholder; + + auto bytes = QByteArray(); + auto buffer = QBuffer(&bytes); + placeholder.save(&buffer, "JPG", 87); + + quint32 size = Serialize::bytearraySize(bytes); + if (!_roundPlaceholderKey) { + _roundPlaceholderKey = GenerateKey(_basePath); + writeMapQueued(); + } + EncryptedDescriptor data(size); + data.stream << bytes; + FileWriteDescriptor file(_roundPlaceholderKey, _basePath); + file.writeEncrypted(data, _localKey); +} + bool Account::encrypt( const void *src, void *dst, diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index 84533f93d..c0efdefdc 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -174,6 +174,9 @@ public: [[nodiscard]] Webview::StorageId resolveStorageIdBots(); [[nodiscard]] Webview::StorageId resolveStorageIdOther(); + [[nodiscard]] QImage readRoundPlaceholder(); + void writeRoundPlaceholder(const QImage &placeholder); + [[nodiscard]] bool encrypt( const void *src, void *dst, @@ -302,6 +305,7 @@ private: FileKey _featuredCustomEmojiKey = 0; FileKey _archivedCustomEmojiKey = 0; FileKey _searchSuggestionsKey = 0; + FileKey _roundPlaceholderKey = 0; qint64 _cacheTotalSizeLimit = 0; qint64 _cacheBigFileTotalSizeLimit = 0; @@ -325,6 +329,8 @@ private: bool _mapChanged = false; bool _locationsChanged = false; + QImage _roundPlaceholder; + }; [[nodiscard]] Webview::StorageId TonSiteStorageId(); diff --git a/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp b/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp index c7383614a..5c29cc65e 100644 --- a/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp +++ b/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp @@ -1052,6 +1052,10 @@ Fn RoundVideoRecorder::audioChunkProcessor() { }; } +rpl::producer RoundVideoRecorder::placeholderUpdates() const { + return _placeholderUpdates.events(); +} + int RoundVideoRecorder::previewSize() const { return _side; } @@ -1096,6 +1100,31 @@ void RoundVideoRecorder::progressTo(float64 progress) { _preview->update(); } +void RoundVideoRecorder::preparePlaceholder(const QImage &placeholder) { + const auto ratio = style::DevicePixelRatio(); + const auto full = QSize(_side, _side) * ratio; + if (!placeholder.isNull()) { + _framePlaceholder = Images::Circle( + placeholder.scaled( + full, + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); + _framePlaceholder.setDevicePixelRatio(ratio); + } else { + _framePlaceholder = QImage( + full, + QImage::Format_ARGB32_Premultiplied); + _framePlaceholder.fill(Qt::transparent); + _framePlaceholder.setDevicePixelRatio(ratio); + + auto p = QPainter(&_framePlaceholder); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(Qt::black); + p.drawEllipse(0, 0, _side, _side); + } +} + void RoundVideoRecorder::prepareFrame(bool blurred) { if (_frameOriginal.isNull()) { return; @@ -1124,18 +1153,14 @@ void RoundVideoRecorder::prepareFrame(bool blurred) { const auto ratio = style::DevicePixelRatio(); if (blurred) { static constexpr auto kRadius = 16; - _framePlaceholder = Images::Circle( - Images::BlurLargeImage( - copy.scaled( - QSize(kBlurredSize, kBlurredSize), - Qt::KeepAspectRatio, - Qt::FastTransformation), - kRadius - ).scaled( - QSize(_side, _side) * ratio, + auto image = Images::BlurLargeImage( + copy.scaled( + QSize(kBlurredSize, kBlurredSize), Qt::KeepAspectRatio, - Qt::SmoothTransformation)); - _framePlaceholder.setDevicePixelRatio(ratio); + Qt::FastTransformation), + kRadius); + preparePlaceholder(image); + _placeholderUpdates.fire(std::move(image)); } else { _framePrepared = Images::Circle(copy.scaled( QSize(_side, _side) * ratio, @@ -1147,17 +1172,7 @@ void RoundVideoRecorder::prepareFrame(bool blurred) { void RoundVideoRecorder::createImages() { const auto ratio = style::DevicePixelRatio(); - _framePlaceholder = QImage( - QSize(_side, _side) * ratio, - QImage::Format_ARGB32_Premultiplied); - _framePlaceholder.fill(Qt::transparent); - _framePlaceholder.setDevicePixelRatio(ratio); - auto p = QPainter(&_framePlaceholder); - auto hq = PainterHighQualityEnabler(p); - - p.setPen(Qt::NoPen); - p.setBrush(Qt::black); - p.drawEllipse(0, 0, _side, _side); + preparePlaceholder(_descriptor.placeholder); const auto side = _side + 2 * _extent; _shadow = QImage( diff --git a/Telegram/SourceFiles/ui/controls/round_video_recorder.h b/Telegram/SourceFiles/ui/controls/round_video_recorder.h index e8cd5379f..57f47b8ca 100644 --- a/Telegram/SourceFiles/ui/controls/round_video_recorder.h +++ b/Telegram/SourceFiles/ui/controls/round_video_recorder.h @@ -38,6 +38,7 @@ struct RoundVideoRecorderDescriptor { Fn)> hidden; std::shared_ptr capturer; std::shared_ptr track; + QImage placeholder; }; struct RoundVideoResult { @@ -62,6 +63,7 @@ public: [[nodiscard]] int previewSize() const; [[nodiscard]] Fn audioChunkProcessor(); + [[nodiscard]] rpl::producer placeholderUpdates() const; void pause(Fn done = nullptr); void resume(RoundVideoPartial partial); @@ -84,6 +86,7 @@ private: void setup(); void prepareFrame(bool blurred = false); + void preparePlaceholder(const QImage &placeholder); void createImages(); void progressTo(float64 progress); void fade(bool visible); @@ -100,6 +103,7 @@ private: Ui::Animations::Simple _progressAnimation; Ui::Animations::Simple _fadeAnimation; Ui::Animations::Simple _fadeContentAnimation; + rpl::event_stream _placeholderUpdates; std::shared_ptr _silentPreview; std::shared_ptr _soundedPreview;