mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Save last camera image as a placeholder.
This commit is contained in:
parent
9ce6636c6a
commit
ccc6c6daa5
5 changed files with 113 additions and 22 deletions
|
@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "media/streaming/media_streaming_instance.h"
|
#include "media/streaming/media_streaming_instance.h"
|
||||||
#include "media/streaming/media_streaming_round_preview.h"
|
#include "media/streaming/media_streaming_round_preview.h"
|
||||||
|
#include "storage/storage_account.h"
|
||||||
#include "ui/controls/round_video_recorder.h"
|
#include "ui/controls/round_video_recorder.h"
|
||||||
#include "ui/controls/send_button.h"
|
#include "ui/controls/send_button.h"
|
||||||
#include "ui/effects/animation_value.h"
|
#include "ui/effects/animation_value.h"
|
||||||
|
@ -2362,7 +2363,13 @@ bool VoiceRecordBar::createVideoRecorder() {
|
||||||
.hidden = hidden,
|
.hidden = hidden,
|
||||||
.capturer = std::move(capturer),
|
.capturer = std::move(capturer),
|
||||||
.track = std::move(track),
|
.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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,7 @@ enum { // Local Storage Keys
|
||||||
lskCustomEmojiKeys = 0x17, // no data
|
lskCustomEmojiKeys = 0x17, // no data
|
||||||
lskSearchSuggestions = 0x18, // no data
|
lskSearchSuggestions = 0x18, // no data
|
||||||
lskWebviewTokens = 0x19, // data: QByteArray bots, QByteArray other
|
lskWebviewTokens = 0x19, // data: QByteArray bots, QByteArray other
|
||||||
|
lskRoundPlaceholder = 0x1a, // no data
|
||||||
};
|
};
|
||||||
|
|
||||||
auto EmptyMessageDraftSources()
|
auto EmptyMessageDraftSources()
|
||||||
|
@ -220,6 +221,7 @@ base::flat_set<QString> Account::collectGoodNames() const {
|
||||||
_featuredCustomEmojiKey,
|
_featuredCustomEmojiKey,
|
||||||
_archivedCustomEmojiKey,
|
_archivedCustomEmojiKey,
|
||||||
_searchSuggestionsKey,
|
_searchSuggestionsKey,
|
||||||
|
_roundPlaceholderKey,
|
||||||
};
|
};
|
||||||
auto result = base::flat_set<QString>{
|
auto result = base::flat_set<QString>{
|
||||||
"map0",
|
"map0",
|
||||||
|
@ -306,6 +308,7 @@ Account::ReadMapResult Account::readMapWith(
|
||||||
quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0;
|
quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0;
|
||||||
quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0;
|
quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0;
|
||||||
quint64 searchSuggestionsKey = 0;
|
quint64 searchSuggestionsKey = 0;
|
||||||
|
quint64 roundPlaceholderKey = 0;
|
||||||
QByteArray webviewStorageTokenBots, webviewStorageTokenOther;
|
QByteArray webviewStorageTokenBots, webviewStorageTokenOther;
|
||||||
while (!map.stream.atEnd()) {
|
while (!map.stream.atEnd()) {
|
||||||
quint32 keyType;
|
quint32 keyType;
|
||||||
|
@ -415,6 +418,9 @@ Account::ReadMapResult Account::readMapWith(
|
||||||
case lskSearchSuggestions: {
|
case lskSearchSuggestions: {
|
||||||
map.stream >> searchSuggestionsKey;
|
map.stream >> searchSuggestionsKey;
|
||||||
} break;
|
} break;
|
||||||
|
case lskRoundPlaceholder: {
|
||||||
|
map.stream >> roundPlaceholderKey;
|
||||||
|
} break;
|
||||||
case lskWebviewTokens: {
|
case lskWebviewTokens: {
|
||||||
map.stream
|
map.stream
|
||||||
>> webviewStorageTokenBots
|
>> webviewStorageTokenBots
|
||||||
|
@ -456,6 +462,7 @@ Account::ReadMapResult Account::readMapWith(
|
||||||
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
|
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey;
|
||||||
_exportSettingsKey = exportSettingsKey;
|
_exportSettingsKey = exportSettingsKey;
|
||||||
_searchSuggestionsKey = searchSuggestionsKey;
|
_searchSuggestionsKey = searchSuggestionsKey;
|
||||||
|
_roundPlaceholderKey = roundPlaceholderKey;
|
||||||
_oldMapVersion = mapData.version;
|
_oldMapVersion = mapData.version;
|
||||||
_webviewStorageIdBots.token = webviewStorageTokenBots;
|
_webviewStorageIdBots.token = webviewStorageTokenBots;
|
||||||
_webviewStorageIdOther.token = webviewStorageTokenOther;
|
_webviewStorageIdOther.token = webviewStorageTokenOther;
|
||||||
|
@ -570,6 +577,7 @@ void Account::writeMap() {
|
||||||
+ Serialize::bytearraySize(_webviewStorageIdBots.token)
|
+ Serialize::bytearraySize(_webviewStorageIdBots.token)
|
||||||
+ Serialize::bytearraySize(_webviewStorageIdOther.token);
|
+ Serialize::bytearraySize(_webviewStorageIdOther.token);
|
||||||
}
|
}
|
||||||
|
if (_roundPlaceholderKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
|
|
||||||
EncryptedDescriptor mapData(mapSize);
|
EncryptedDescriptor mapData(mapSize);
|
||||||
if (!self.isEmpty()) {
|
if (!self.isEmpty()) {
|
||||||
|
@ -640,6 +648,10 @@ void Account::writeMap() {
|
||||||
<< _webviewStorageIdBots.token
|
<< _webviewStorageIdBots.token
|
||||||
<< _webviewStorageIdOther.token;
|
<< _webviewStorageIdOther.token;
|
||||||
}
|
}
|
||||||
|
if (_roundPlaceholderKey) {
|
||||||
|
mapData.stream << quint32(lskRoundPlaceholder);
|
||||||
|
mapData.stream << quint64(_roundPlaceholderKey);
|
||||||
|
}
|
||||||
map.writeEncrypted(mapData, _localKey);
|
map.writeEncrypted(mapData, _localKey);
|
||||||
|
|
||||||
_mapChanged = false;
|
_mapChanged = false;
|
||||||
|
@ -669,6 +681,7 @@ void Account::reset() {
|
||||||
_legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0;
|
_legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0;
|
||||||
_settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0;
|
_settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0;
|
||||||
_searchSuggestionsKey = 0;
|
_searchSuggestionsKey = 0;
|
||||||
|
_roundPlaceholderKey = 0;
|
||||||
_oldMapVersion = 0;
|
_oldMapVersion = 0;
|
||||||
_fileLocations.clear();
|
_fileLocations.clear();
|
||||||
_fileLocationPairs.clear();
|
_fileLocationPairs.clear();
|
||||||
|
@ -3178,6 +3191,52 @@ Webview::StorageId Account::resolveStorageIdOther() {
|
||||||
return _webviewStorageIdOther;
|
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(
|
bool Account::encrypt(
|
||||||
const void *src,
|
const void *src,
|
||||||
void *dst,
|
void *dst,
|
||||||
|
|
|
@ -174,6 +174,9 @@ public:
|
||||||
[[nodiscard]] Webview::StorageId resolveStorageIdBots();
|
[[nodiscard]] Webview::StorageId resolveStorageIdBots();
|
||||||
[[nodiscard]] Webview::StorageId resolveStorageIdOther();
|
[[nodiscard]] Webview::StorageId resolveStorageIdOther();
|
||||||
|
|
||||||
|
[[nodiscard]] QImage readRoundPlaceholder();
|
||||||
|
void writeRoundPlaceholder(const QImage &placeholder);
|
||||||
|
|
||||||
[[nodiscard]] bool encrypt(
|
[[nodiscard]] bool encrypt(
|
||||||
const void *src,
|
const void *src,
|
||||||
void *dst,
|
void *dst,
|
||||||
|
@ -302,6 +305,7 @@ private:
|
||||||
FileKey _featuredCustomEmojiKey = 0;
|
FileKey _featuredCustomEmojiKey = 0;
|
||||||
FileKey _archivedCustomEmojiKey = 0;
|
FileKey _archivedCustomEmojiKey = 0;
|
||||||
FileKey _searchSuggestionsKey = 0;
|
FileKey _searchSuggestionsKey = 0;
|
||||||
|
FileKey _roundPlaceholderKey = 0;
|
||||||
|
|
||||||
qint64 _cacheTotalSizeLimit = 0;
|
qint64 _cacheTotalSizeLimit = 0;
|
||||||
qint64 _cacheBigFileTotalSizeLimit = 0;
|
qint64 _cacheBigFileTotalSizeLimit = 0;
|
||||||
|
@ -325,6 +329,8 @@ private:
|
||||||
bool _mapChanged = false;
|
bool _mapChanged = false;
|
||||||
bool _locationsChanged = false;
|
bool _locationsChanged = false;
|
||||||
|
|
||||||
|
QImage _roundPlaceholder;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] Webview::StorageId TonSiteStorageId();
|
[[nodiscard]] Webview::StorageId TonSiteStorageId();
|
||||||
|
|
|
@ -1052,6 +1052,10 @@ Fn<void(Media::Capture::Chunk)> RoundVideoRecorder::audioChunkProcessor() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<QImage> RoundVideoRecorder::placeholderUpdates() const {
|
||||||
|
return _placeholderUpdates.events();
|
||||||
|
}
|
||||||
|
|
||||||
int RoundVideoRecorder::previewSize() const {
|
int RoundVideoRecorder::previewSize() const {
|
||||||
return _side;
|
return _side;
|
||||||
}
|
}
|
||||||
|
@ -1096,6 +1100,31 @@ void RoundVideoRecorder::progressTo(float64 progress) {
|
||||||
_preview->update();
|
_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) {
|
void RoundVideoRecorder::prepareFrame(bool blurred) {
|
||||||
if (_frameOriginal.isNull()) {
|
if (_frameOriginal.isNull()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1124,18 +1153,14 @@ void RoundVideoRecorder::prepareFrame(bool blurred) {
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
if (blurred) {
|
if (blurred) {
|
||||||
static constexpr auto kRadius = 16;
|
static constexpr auto kRadius = 16;
|
||||||
_framePlaceholder = Images::Circle(
|
auto image = Images::BlurLargeImage(
|
||||||
Images::BlurLargeImage(
|
copy.scaled(
|
||||||
copy.scaled(
|
QSize(kBlurredSize, kBlurredSize),
|
||||||
QSize(kBlurredSize, kBlurredSize),
|
|
||||||
Qt::KeepAspectRatio,
|
|
||||||
Qt::FastTransformation),
|
|
||||||
kRadius
|
|
||||||
).scaled(
|
|
||||||
QSize(_side, _side) * ratio,
|
|
||||||
Qt::KeepAspectRatio,
|
Qt::KeepAspectRatio,
|
||||||
Qt::SmoothTransformation));
|
Qt::FastTransformation),
|
||||||
_framePlaceholder.setDevicePixelRatio(ratio);
|
kRadius);
|
||||||
|
preparePlaceholder(image);
|
||||||
|
_placeholderUpdates.fire(std::move(image));
|
||||||
} else {
|
} else {
|
||||||
_framePrepared = Images::Circle(copy.scaled(
|
_framePrepared = Images::Circle(copy.scaled(
|
||||||
QSize(_side, _side) * ratio,
|
QSize(_side, _side) * ratio,
|
||||||
|
@ -1147,17 +1172,7 @@ void RoundVideoRecorder::prepareFrame(bool blurred) {
|
||||||
|
|
||||||
void RoundVideoRecorder::createImages() {
|
void RoundVideoRecorder::createImages() {
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
_framePlaceholder = QImage(
|
preparePlaceholder(_descriptor.placeholder);
|
||||||
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);
|
|
||||||
|
|
||||||
const auto side = _side + 2 * _extent;
|
const auto side = _side + 2 * _extent;
|
||||||
_shadow = QImage(
|
_shadow = QImage(
|
||||||
|
|
|
@ -38,6 +38,7 @@ struct RoundVideoRecorderDescriptor {
|
||||||
Fn<void(not_null<RoundVideoRecorder*>)> hidden;
|
Fn<void(not_null<RoundVideoRecorder*>)> hidden;
|
||||||
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer;
|
std::shared_ptr<tgcalls::VideoCaptureInterface> capturer;
|
||||||
std::shared_ptr<Webrtc::VideoTrack> track;
|
std::shared_ptr<Webrtc::VideoTrack> track;
|
||||||
|
QImage placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RoundVideoResult {
|
struct RoundVideoResult {
|
||||||
|
@ -62,6 +63,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] int previewSize() const;
|
[[nodiscard]] int previewSize() const;
|
||||||
[[nodiscard]] Fn<void(Media::Capture::Chunk)> audioChunkProcessor();
|
[[nodiscard]] Fn<void(Media::Capture::Chunk)> audioChunkProcessor();
|
||||||
|
[[nodiscard]] rpl::producer<QImage> placeholderUpdates() const;
|
||||||
|
|
||||||
void pause(Fn<void(RoundVideoResult)> done = nullptr);
|
void pause(Fn<void(RoundVideoResult)> done = nullptr);
|
||||||
void resume(RoundVideoPartial partial);
|
void resume(RoundVideoPartial partial);
|
||||||
|
@ -84,6 +86,7 @@ private:
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
void prepareFrame(bool blurred = false);
|
void prepareFrame(bool blurred = false);
|
||||||
|
void preparePlaceholder(const QImage &placeholder);
|
||||||
void createImages();
|
void createImages();
|
||||||
void progressTo(float64 progress);
|
void progressTo(float64 progress);
|
||||||
void fade(bool visible);
|
void fade(bool visible);
|
||||||
|
@ -100,6 +103,7 @@ private:
|
||||||
Ui::Animations::Simple _progressAnimation;
|
Ui::Animations::Simple _progressAnimation;
|
||||||
Ui::Animations::Simple _fadeAnimation;
|
Ui::Animations::Simple _fadeAnimation;
|
||||||
Ui::Animations::Simple _fadeContentAnimation;
|
Ui::Animations::Simple _fadeContentAnimation;
|
||||||
|
rpl::event_stream<QImage> _placeholderUpdates;
|
||||||
|
|
||||||
std::shared_ptr<Ui::DynamicImage> _silentPreview;
|
std::shared_ptr<Ui::DynamicImage> _silentPreview;
|
||||||
std::shared_ptr<Ui::DynamicImage> _soundedPreview;
|
std::shared_ptr<Ui::DynamicImage> _soundedPreview;
|
||||||
|
|
Loading…
Add table
Reference in a new issue