Init webm player for sticker set thumbnails.

This commit is contained in:
John Preston 2022-01-24 15:33:31 +03:00
parent 10ff71e8f6
commit 20dbf18106
13 changed files with 251 additions and 117 deletions

View file

@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/cached_round_corners.h"
#include "window/window_session_controller.h"
#include "media/clip/media_clip_reader.h"
#include "main/main_session.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
@ -143,10 +144,7 @@ private:
int32 count,
const QString &title,
int titleWidth,
bool installed,
bool official,
bool unread,
bool archived,
Data::StickersSetFlags flagsOverride,
bool removed,
int32 pixw,
int32 pixh);
@ -154,6 +152,10 @@ private:
bool isRecentSet() const;
bool isMasksSet() const;
bool isWebm() const;
bool isInstalled() const;
bool isUnread() const;
bool isArchived() const;
const not_null<StickersSet*> set;
DocumentData *sticker = nullptr;
@ -162,16 +164,14 @@ private:
int32 count = 0;
QString title;
int titleWidth = 0;
bool installed = false;
bool official = false;
bool unread = false;
bool archived = false;
Data::StickersSetFlags flagsOverride;
bool removed = false;
int32 pixw = 0;
int32 pixh = 0;
anim::value yadd;
std::unique_ptr<Ui::RippleAnimation> ripple;
std::unique_ptr<Lottie::SinglePlayer> lottie;
Media::Clip::ReaderPointer webm;
};
struct MegagroupSet {
inline bool operator==(const MegagroupSet &other) const {
@ -221,6 +221,8 @@ private:
void setActionSel(int32 actionSel);
float64 aboveShadowOpacity() const;
void validateLottieAnimation(not_null<Row*> row);
void validateWebmAnimation(not_null<Row*> row);
void validateAnimation(not_null<Row*> row);
void updateRowThumbnail(not_null<Row*> row);
void readVisibleSets();
@ -229,8 +231,12 @@ private:
void rebuildAppendSet(not_null<StickersSet*> set, int maxNameWidth);
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(not_null<StickersSet*> set) const;
QString fillSetTitle(not_null<StickersSet*> set, int maxNameWidth, int *outTitleWidth) const;
void fillSetFlags(not_null<StickersSet*> set, bool *outInstalled, bool *outOfficial, bool *outUnread, bool *outArchived);
[[nodiscard]] QString fillSetTitle(
not_null<StickersSet*> set,
int maxNameWidth,
int *outTitleWidth) const;
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
not_null<StickersSet*> set) const;
void rebuildMegagroupSet();
void fixupMegagroupSetAddress();
void handleMegagroupSetAddressChange();
@ -1030,10 +1036,7 @@ StickersBox::Inner::Row::Row(
int32 count,
const QString &title,
int titleWidth,
bool installed,
bool official,
bool unread,
bool archived,
Data::StickersSetFlags flagsOverride,
bool removed,
int32 pixw,
int32 pixh)
@ -1042,10 +1045,7 @@ StickersBox::Inner::Row::Row(
, count(count)
, title(title)
, titleWidth(titleWidth)
, installed(installed)
, official(official)
, unread(unread)
, archived(archived)
, flagsOverride(flagsOverride)
, removed(removed)
, pixw(pixw)
, pixh(pixh) {
@ -1062,6 +1062,22 @@ bool StickersBox::Inner::Row::isMasksSet() const {
return (set->flags & SetFlag::Masks);
}
bool StickersBox::Inner::Row::isWebm() const {
return (set->flags & SetFlag::Webm);
}
bool StickersBox::Inner::Row::isInstalled() const {
return (flagsOverride & SetFlag::Installed);
}
bool StickersBox::Inner::Row::isUnread() const {
return (flagsOverride & SetFlag::Unread);
}
bool StickersBox::Inner::Row::isArchived() const {
return (flagsOverride & SetFlag::Archived);
}
StickersBox::Inner::Inner(
QWidget *parent,
not_null<Window::SessionController*> controller,
@ -1302,7 +1318,7 @@ void StickersBox::Inner::paintRow(Painter &p, not_null<Row*> row, int index) {
p.setPen(st::contactsNameFg);
p.drawTextLeft(namex, namey, width(), row->title, row->titleWidth);
if (row->unread) {
if (row->isUnread()) {
p.setPen(Qt::NoPen);
p.setBrush(st::stickersFeaturedUnreadBg);
@ -1344,7 +1360,7 @@ void StickersBox::Inner::paintRowThumbnail(
row->stickerMedia->thumbnailWanted(origin);
}
}
validateLottieAnimation(row);
validateAnimation(row);
if (!row->lottie) {
const auto thumb = row->thumbnailMedia
? row->thumbnailMedia->image()
@ -1380,6 +1396,7 @@ void StickersBox::Inner::paintRowThumbnail(
void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
if (row->lottie
|| !ChatHelpers::HasLottieThumbnail(
row->set->flags,
row->thumbnailMedia.get(),
row->stickerMedia.get())) {
return;
@ -1401,6 +1418,25 @@ void StickersBox::Inner::validateLottieAnimation(not_null<Row*> row) {
}, lifetime());
}
void StickersBox::Inner::validateWebmAnimation(not_null<Row*> row) {
if (row->webm
|| !ChatHelpers::HasWebmThumbnail(
row->set->flags,
row->thumbnailMedia.get(),
row->stickerMedia.get())) {
return;
}
row->webm = ChatHelpers::WebmThumbnail(
row->thumbnailMedia.get(),
row->stickerMedia.get(),
[=](Media::Clip::Notification) { updateRowThumbnail(row); });
}
void StickersBox::Inner::validateAnimation(not_null<Row*> row) {
validateWebmAnimation(row);
validateLottieAnimation(row);
}
void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
const auto rowTop = [&] {
if (row == _megagroupSelectedSet.get()) {
@ -1429,7 +1465,7 @@ void StickersBox::Inner::updateRowThumbnail(not_null<Row*> row) {
void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int index) {
auto removeButton = (_isInstalled && !row->removed);
auto rect = relativeButtonRect(removeButton);
if (!_isInstalled && row->installed && !row->archived && !row->removed) {
if (!_isInstalled && row->isInstalled() && !row->isArchived() && !row->removed) {
// Checkbox after installed from Trending or Archived.
int checkx = width() - (st::contactsPadding.right() + st::contactsCheckPosition.x() + (rect.width() + st::stickersFeaturedInstalled.width()) / 2);
int checky = st::contactsPadding.top() + (st::contactsPhotoSize - st::stickersFeaturedInstalled.height()) / 2;
@ -1516,7 +1552,7 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
auto rippleMask = Ui::RippleAnimation::ellipseMask(QSize(rippleSize, rippleSize));
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton);
}
} else if (!row->installed || row->archived || row->removed) {
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
auto rippleMask = Ui::RippleAnimation::roundRectMask(rippleSize, st::roundRadiusSmall);
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
@ -1649,7 +1685,7 @@ void StickersBox::Inner::updateSelected() {
selected = selectedIndex;
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
const auto row = _rows[selectedIndex].get();
if (!_megagroupSet && (_isInstalled || !row->installed || row->archived || row->removed)) {
if (!_megagroupSet && (_isInstalled || !row->isInstalled() || row->isArchived() || row->removed)) {
auto removeButton = (_isInstalled && !row->removed);
auto rect = myrtlrect(relativeButtonRect(removeButton));
actionSel = rect.contains(local) ? selectedIndex : -1;
@ -1952,8 +1988,10 @@ void StickersBox::Inner::rebuildMegagroupSet() {
auto sticker = (DocumentData*)nullptr;
auto pixw = 0, pixh = 0;
fillSetCover(set, &sticker, &pixw, &pixh);
auto installed = true, official = false, unread = false, archived = false, removed = false;
if (!_megagroupSelectedSet || _megagroupSelectedSet->set->id != set->id) {
auto flagsOverride = SetFlag::Installed;
auto removed = false;
if (!_megagroupSelectedSet
|| _megagroupSelectedSet->set->id != set->id) {
_megagroupSetField->setText(set->shortName);
_megagroupSetField->finishAnimating();
}
@ -1963,10 +2001,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
count,
title,
titleWidth,
installed,
official,
unread,
archived,
flagsOverride,
removed,
pixw,
pixh);
@ -2095,13 +2130,14 @@ void StickersBox::Inner::updateRows() {
}
}
if (!row->isRecentSet()) {
auto wasInstalled = row->installed;
auto wasArchived = row->archived;
fillSetFlags(set, &row->installed, &row->official, &row->unread, &row->archived);
auto wasInstalled = row->isInstalled();
auto wasArchived = row->isArchived();
row->flagsOverride = fillSetFlags(set);
if (_isInstalled) {
row->archived = false;
row->flagsOverride &= ~SetFlag::Archived;
}
if (row->installed != wasInstalled || row->archived != wasArchived) {
if (row->isInstalled() != wasInstalled
|| row->isArchived() != wasArchived) {
row->ripple.reset();
}
}
@ -2143,11 +2179,11 @@ int StickersBox::Inner::countMaxNameWidth() const {
void StickersBox::Inner::rebuildAppendSet(
not_null<StickersSet*> set,
int maxNameWidth) {
bool installed = true, official = true, unread = false, archived = false, removed = false;
if (set->id != Data::Stickers::CloudRecentSetId) {
fillSetFlags(set, &installed, &official, &unread, &archived);
}
if (_isInstalled && archived) {
auto flagsOverride = (set->id != Data::Stickers::CloudRecentSetId)
? fillSetFlags(set)
: SetFlag::Installed;
auto removed = false;
if (_isInstalled && (flagsOverride & SetFlag::Archived)) {
return;
}
@ -2176,10 +2212,7 @@ void StickersBox::Inner::rebuildAppendSet(
raw->count = count;
raw->title = title;
raw->titleWidth = titleWidth;
raw->installed = installed;
raw->official = official;
raw->unread = unread;
raw->archived = archived;
raw->flagsOverride = flagsOverride;
raw->removed = removed;
raw->pixw = pixw;
raw->pixh = pixh;
@ -2200,10 +2233,7 @@ void StickersBox::Inner::rebuildAppendSet(
count,
title,
titleWidth,
installed,
official,
unread,
archived,
flagsOverride,
removed,
pixw,
pixh));
@ -2289,20 +2319,12 @@ QString StickersBox::Inner::fillSetTitle(
return result;
}
void StickersBox::Inner::fillSetFlags(
not_null<StickersSet*> set,
bool *outInstalled,
bool *outOfficial,
bool *outUnread,
bool *outArchived) {
*outInstalled = (set->flags & SetFlag::Installed);
*outOfficial = (set->flags & SetFlag::Official);
*outArchived = (set->flags & SetFlag::Archived);
if (_section == Section::Featured) {
*outUnread = (set->flags & SetFlag::Unread);
} else {
*outUnread = false;
}
Data::StickersSetFlags StickersBox::Inner::fillSetFlags(
not_null<StickersSet*> set) const {
const auto result = set->flags;
return (_section == Section::Featured)
? result
: (result & ~SetFlag::Unread);
}
template <typename Check>
@ -2319,7 +2341,7 @@ StickersSetsOrder StickersBox::Inner::collectSets(Check check) const {
StickersSetsOrder StickersBox::Inner::getOrder() const {
return collectSets([](Row *row) {
return !row->archived && !row->removed && !row->isRecentSet();
return !row->isArchived() && !row->removed && !row->isRecentSet();
});
}
@ -2394,7 +2416,7 @@ void StickersBox::Inner::readVisibleSets() {
int rowTo = ceilclamp(itemsVisibleBottom, _rowHeight, 0, _rows.size());
for (int i = rowFrom; i < rowTo; ++i) {
const auto row = _rows[i].get();
if (!row->unread) {
if (!row->isUnread()) {
continue;
}
if ((i * _rowHeight < itemsVisibleTop)

View file

@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" // GifPauseReason.
#include "main/main_session.h"
#include "main/main_session_settings.h"
#include "media/clip/media_clip_reader.h"
#include "apiwrap.h"
#include "api/api_toggling_media.h" // Api::ToggleFavedSticker
#include "styles/style_chat_helpers.h"
@ -97,6 +98,7 @@ struct StickerIcon {
uint64 setId = 0;
StickersSet *set = nullptr;
mutable std::unique_ptr<Lottie::SinglePlayer> lottie;
mutable Media::Clip::ReaderPointer webm;
mutable QPixmap savedFrame;
DocumentData *sticker = nullptr;
ChannelData *megagroup = nullptr;
@ -157,6 +159,8 @@ private:
int newSelected,
ValidateIconAnimations animations);
void validateIconLottieAnimation(const StickerIcon &icon);
void validateIconWebmAnimation(const StickerIcon &icon);
void validateIconAnimation(const StickerIcon &icon);
void refreshIconsGeometry(ValidateIconAnimations animations);
void updateSelected();
@ -792,6 +796,7 @@ void StickersListWidget::Footer::validateIconLottieAnimation(
if (icon.lottie
|| !icon.sticker
|| !HasLottieThumbnail(
icon.set ? icon.set->flags : Data::StickersSetFlags(),
icon.thumbnailMedia.get(),
icon.stickerMedia.get())) {
return;
@ -817,6 +822,30 @@ void StickersListWidget::Footer::validateIconLottieAnimation(
}, icon.lifetime);
}
void StickersListWidget::Footer::validateIconWebmAnimation(
const StickerIcon &icon) {
icon.ensureMediaCreated();
if (icon.webm
|| !icon.sticker
|| !HasWebmThumbnail(
icon.set ? icon.set->flags : Data::StickersSetFlags(),
icon.thumbnailMedia.get(),
icon.stickerMedia.get())) {
return;
}
const auto id = icon.setId;
icon.webm = WebmThumbnail(
icon.thumbnailMedia.get(),
icon.stickerMedia.get(),
[=](Media::Clip::Notification) { updateSetIcon(id); });
}
void StickersListWidget::Footer::validateIconAnimation(
const StickerIcon &icon) {
validateIconWebmAnimation(icon);
validateIconLottieAnimation(icon);
}
void StickersListWidget::Footer::updateSetIcon(uint64 setId) {
enumerateVisibleIcons([&](const StickerIcon &icon, int x) {
if (icon.setId != setId) {
@ -832,7 +861,7 @@ void StickersListWidget::Footer::paintSetIcon(
int x) const {
if (icon.sticker) {
icon.ensureMediaCreated();
const_cast<Footer*>(this)->validateIconLottieAnimation(icon);
const_cast<Footer*>(this)->validateIconAnimation(icon);
const auto origin = icon.sticker->stickerSetOrigin();
const auto thumb = icon.thumbnailMedia
? icon.thumbnailMedia->image()

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "storage/cache/storage_cache_database.h"
#include "media/clip/media_clip_reader.h"
#include "ui/effects/path_shift_gradient.h"
#include "main/main_session.h"
@ -130,10 +131,12 @@ not_null<Lottie::Animation*> LottieAnimationFromDocument(
}
bool HasLottieThumbnail(
Data::StickersSetFlags flags,
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media) {
if (thumb) {
return !thumb->content().isEmpty();
return !(flags & Data::StickersSetFlag::Webm)
&& !thumb->content().isEmpty();
} else if (!media) {
return false;
}
@ -189,6 +192,44 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
box);
}
bool HasWebmThumbnail(
Data::StickersSetFlags flags,
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media) {
if (thumb) {
return (flags & Data::StickersSetFlag::Webm)
&& !thumb->content().isEmpty();
} else if (!media) {
return false;
}
const auto document = media->owner();
if (const auto info = document->sticker()) {
if (!info->isWebm()) {
return false;
}
media->automaticLoad(document->stickerSetOrigin(), nullptr);
if (!media->loaded()) {
return false;
}
return document->bigFileBaseCacheKey().valid();
}
return false;
}
Media::Clip::ReaderPointer WebmThumbnail(
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media,
Fn<void(Media::Clip::Notification)> callback) {
return thumb
? ::Media::Clip::MakeReader(
thumb->content(),
std::move(callback))
: ::Media::Clip::MakeReader(
media->owner()->location(),
media->bytes(),
std::move(callback));
}
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,

View file

@ -7,12 +7,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace base {
template <typename Enum>
class Flags;
} // namespace base
namespace Storage {
namespace Cache {
struct Key;
} // namespace Cache
} // namespace Storage
namespace Media::Clip {
class ReaderPointer;
enum class Notification;
} // namespace Media::Clip
namespace Lottie {
class SinglePlayer;
class MultiPlayer;
@ -33,6 +43,8 @@ class PathShiftGradient;
namespace Data {
class DocumentMedia;
class StickersSetThumbnailView;
enum class StickersSetFlag;
using StickersSetFlags = base::flags<StickersSetFlag>;
} // namespace Data
namespace ChatHelpers {
@ -70,6 +82,7 @@ enum class StickerLottieSize : uchar {
QSize box);
[[nodiscard]] bool HasLottieThumbnail(
Data::StickersSetFlags flags,
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media);
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
@ -79,6 +92,15 @@ enum class StickerLottieSize : uchar {
QSize box,
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
[[nodiscard]] bool HasWebmThumbnail(
Data::StickersSetFlags flags,
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media);
[[nodiscard]] Media::Clip::ReaderPointer WebmThumbnail(
Data::StickersSetThumbnailView *thumb,
Data::DocumentMedia *media,
Fn<void(Media::Clip::Notification)> callback);
bool PaintStickerThumbnailPath(
QPainter &p,
not_null<Data::DocumentMedia*> media,

View file

@ -14,7 +14,7 @@ class FileLoader;
namespace Media {
namespace Clip {
enum Notification : int;
enum class Notification;
class ReaderPointer;
} // namespace Clip
} // namespace Media

View file

@ -49,7 +49,8 @@ StickersSetFlags ParseStickersSetFlags(const MTPDstickerSet &data) {
return (data.is_archived() ? Flag::Archived : Flag())
| (data.is_official() ? Flag::Official : Flag())
| (data.is_masks() ? Flag::Masks : Flag())
| (data.vinstalled_date() ? Flag::Installed : Flag());
| (data.vinstalled_date() ? Flag::Installed : Flag())
| (data.is_gifs() ? Flag::Webm : Flag());
}
StickersSet::StickersSet(

View file

@ -54,6 +54,7 @@ enum class StickersSetFlag {
Featured = (1 << 5),
Unread = (1 << 6),
Special = (1 << 7),
Webm = (1 << 8),
};
inline constexpr bool is_flag_type(StickersSetFlag) { return true; };
using StickersSetFlags = base::flags<StickersSetFlag>;

View file

@ -393,7 +393,7 @@ void Gif::unloadHeavyPart() {
void Gif::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
case Notification::Reinit: {
if (_gif) {
if (_gif->state() == State::Error) {
_gif.setBad();
@ -416,7 +416,7 @@ void Gif::clipCallback(Media::Clip::Notification notification) {
update();
} break;
case NotificationRepaint: {
case Notification::Repaint: {
if (_gif && !_gif->currentDisplayed()) {
update();
}
@ -1583,7 +1583,7 @@ void Game::unloadHeavyPart() {
void Game::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
case Notification::Reinit: {
if (_gif) {
if (_gif->state() == State::Error) {
_gif.setBad();
@ -1610,7 +1610,7 @@ void Game::clipCallback(Media::Clip::Notification notification) {
update();
} break;
case NotificationRepaint: {
case Notification::Repaint: {
if (_gif && !_gif->currentDisplayed()) {
update();
}

View file

@ -133,12 +133,12 @@ void Reader::init(const Core::FileLocation &location, const QByteArray &data) {
Reader::Frame *Reader::frameToShow(int32 *index) const { // 0 means not ready
int step = _step.loadAcquire(), i;
if (step == WaitingForDimensionsStep) {
if (step == kWaitingForDimensionsStep) {
if (index) *index = 0;
return nullptr;
} else if (step == WaitingForRequestStep) {
} else if (step == kWaitingForRequestStep) {
i = 0;
} else if (step == WaitingForFirstFrameStep) {
} else if (step == kWaitingForFirstFrameStep) {
i = 0;
} else {
i = (step / 2) % 3;
@ -149,12 +149,12 @@ Reader::Frame *Reader::frameToShow(int32 *index) const { // 0 means not ready
Reader::Frame *Reader::frameToWrite(int32 *index) const { // 0 means not ready
int32 step = _step.loadAcquire(), i;
if (step == WaitingForDimensionsStep) {
if (step == kWaitingForDimensionsStep) {
i = 0;
} else if (step == WaitingForRequestStep) {
} else if (step == kWaitingForRequestStep) {
if (index) *index = 0;
return nullptr;
} else if (step == WaitingForFirstFrameStep) {
} else if (step == kWaitingForFirstFrameStep) {
i = 0;
} else {
i = ((step + 2) / 2) % 3;
@ -165,7 +165,9 @@ Reader::Frame *Reader::frameToWrite(int32 *index) const { // 0 means not ready
Reader::Frame *Reader::frameToWriteNext(bool checkNotWriting, int32 *index) const {
int32 step = _step.loadAcquire(), i;
if (step == WaitingForDimensionsStep || step == WaitingForRequestStep || (checkNotWriting && (step % 2))) {
if (step == kWaitingForDimensionsStep
|| step == kWaitingForRequestStep
|| (checkNotWriting && (step % 2))) {
if (index) *index = 0;
return nullptr;
}
@ -176,10 +178,10 @@ Reader::Frame *Reader::frameToWriteNext(bool checkNotWriting, int32 *index) cons
void Reader::moveToNextShow() const {
int32 step = _step.loadAcquire();
if (step == WaitingForDimensionsStep) {
} else if (step == WaitingForRequestStep) {
_step.storeRelease(WaitingForFirstFrameStep);
} else if (step == WaitingForFirstFrameStep) {
if (step == kWaitingForDimensionsStep) {
} else if (step == kWaitingForRequestStep) {
_step.storeRelease(kWaitingForFirstFrameStep);
} else if (step == kWaitingForFirstFrameStep) {
} else if (!(step % 2)) {
_step.storeRelease(step + 1);
}
@ -187,10 +189,10 @@ void Reader::moveToNextShow() const {
void Reader::moveToNextWrite() const {
int32 step = _step.loadAcquire();
if (step == WaitingForDimensionsStep) {
_step.storeRelease(WaitingForRequestStep);
} else if (step == WaitingForRequestStep) {
} else if (step == WaitingForFirstFrameStep) {
if (step == kWaitingForDimensionsStep) {
_step.storeRelease(kWaitingForRequestStep);
} else if (step == kWaitingForRequestStep) {
} else if (step == kWaitingForFirstFrameStep) {
_step.storeRelease(0);
// Force paint the first frame so moveToNextShow() is called.
@ -200,7 +202,10 @@ void Reader::moveToNextWrite() const {
}
}
void Reader::callback(Reader *reader, qint32 threadIndex, qint32 notification) {
void Reader::SafeCallback(
Reader *reader,
int threadIndex,
Notification notification) {
// Check if reader is not deleted already
if (managers.size() > threadIndex && managers.at(threadIndex)->carries(reader) && reader->_callback) {
reader->_callback(Notification(notification));
@ -211,7 +216,7 @@ void Reader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, Image
if (managers.size() <= _threadIndex) error();
if (_state == State::Error) return;
if (_step.loadAcquire() == WaitingForRequestStep) {
if (_step.loadAcquire() == kWaitingForRequestStep) {
int factor = style::DevicePixelRatio();
FrameRequest request;
request.factor = factor;
@ -613,23 +618,29 @@ bool Manager::carries(Reader *reader) const {
return _readerPointers.contains(reader);
}
Manager::ReaderPointers::iterator Manager::unsafeFindReaderPointer(ReaderPrivate *reader) {
ReaderPointers::iterator it = _readerPointers.find(reader->_interface);
auto Manager::unsafeFindReaderPointer(ReaderPrivate *reader)
-> ReaderPointers::iterator {
const auto it = _readerPointers.find(reader->_interface);
// could be a new reader which was realloced in the same address
return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.end();
return (it == _readerPointers.cend() || it.key()->_private == reader)
? it
: _readerPointers.end();
}
Manager::ReaderPointers::const_iterator Manager::constUnsafeFindReaderPointer(ReaderPrivate *reader) const {
ReaderPointers::const_iterator it = _readerPointers.constFind(reader->_interface);
auto Manager::constUnsafeFindReaderPointer(ReaderPrivate *reader) const
-> ReaderPointers::const_iterator {
const auto it = _readerPointers.constFind(reader->_interface);
// could be a new reader which was realloced in the same address
return (it == _readerPointers.cend() || it.key()->_private == reader) ? it : _readerPointers.cend();
return (it == _readerPointers.cend() || it.key()->_private == reader)
? it
: _readerPointers.cend();
}
void Manager::callback(Reader *reader, Notification notification) {
crl::on_main([=, threadIndex = reader->threadIndex()] {
Reader::callback(reader, threadIndex, notification);
Reader::SafeCallback(reader, threadIndex, notification);
});
}
@ -639,14 +650,14 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
if (result == ProcessResult::Error) {
if (it != _readerPointers.cend()) {
it.key()->error();
callback(it.key(), NotificationReinit);
callback(it.key(), Notification::Reinit);
_readerPointers.erase(it);
}
return false;
} else if (result == ProcessResult::Finished) {
if (it != _readerPointers.cend()) {
it.key()->finished();
callback(it.key(), NotificationReinit);
callback(it.key(), Notification::Reinit);
}
return false;
}
@ -682,14 +693,14 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
if (result == ProcessResult::Started) {
reader->startedAt(ms);
it.key()->moveToNextWrite();
callback(it.key(), NotificationReinit);
callback(it.key(), Notification::Reinit);
}
} else if (result == ProcessResult::Paused) {
it.key()->moveToNextWrite();
callback(it.key(), NotificationReinit);
callback(it.key(), Notification::Reinit);
} else if (result == ProcessResult::Repaint) {
it.key()->moveToNextWrite();
callback(it.key(), NotificationRepaint);
callback(it.key(), Notification::Repaint);
}
return true;
}

View file

@ -39,15 +39,19 @@ struct FrameRequest {
RectParts corners = RectPart::AllCorners;
};
enum ReaderSteps : int {
WaitingForDimensionsStep = -3, // before ReaderPrivate read the first image and got the original frame size
WaitingForRequestStep = -2, // before Reader got the original frame size and prepared the frame request
WaitingForFirstFrameStep = -1, // before ReaderPrivate got the frame request and started waiting for the 1-2 delay
};
// Before ReaderPrivate read the first image and got the original frame size.
inline constexpr auto kWaitingForDimensionsStep = -3;
enum Notification : int {
NotificationReinit,
NotificationRepaint,
// Before Reader got the original frame size and prepared the frame request.
inline constexpr auto kWaitingForRequestStep = -2;
// Before ReaderPrivate got the frame request
// and started waiting for the 1-2 delay.
inline constexpr auto kWaitingForFirstFrameStep = -1;
enum class Notification {
Reinit,
Repaint,
};
class ReaderPrivate;
@ -64,7 +68,10 @@ public:
Reader(const QByteArray &data, Callback &&callback);
// Reader can be already deleted.
static void callback(Reader *reader, qint32 threadIndex, qint32 notification);
static void SafeCallback(
Reader *reader,
int threadIndex,
Notification notification);
void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners);
QPixmap current(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners, crl::time ms);
@ -94,7 +101,7 @@ public:
State state() const;
bool started() const {
auto step = _step.loadAcquire();
return (step == WaitingForFirstFrameStep) || (step >= 0);
return (step == kWaitingForFirstFrameStep) || (step >= 0);
}
bool ready() const;
@ -120,7 +127,7 @@ private:
mutable int _height = 0;
// -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
mutable QAtomicInt _step = WaitingForDimensionsStep;
mutable QAtomicInt _step = kWaitingForDimensionsStep;
struct Frame {
void clear() {
pix = QPixmap();

View file

@ -1897,7 +1897,7 @@ QSize Gif::countFrameSize() const {
void Gif::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
case Notification::Reinit: {
if (_gif) {
if (_gif->state() == State::Error) {
_gif.setBad();
@ -1926,7 +1926,7 @@ void Gif::clipCallback(Media::Clip::Notification notification) {
update();
} break;
case NotificationRepaint: {
case Notification::Repaint: {
if (_gif && !_gif->currentDisplayed()) {
update();
}

View file

@ -133,7 +133,7 @@ void SingleMediaPreview::clipCallback(
Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
case Notification::Reinit: {
if (_gifPreview && _gifPreview->state() == State::Error) {
_gifPreview.setBad();
}
@ -152,7 +152,7 @@ void SingleMediaPreview::clipCallback(
update();
} break;
case NotificationRepaint: {
case Notification::Repaint: {
if (_gifPreview && !_gifPreview->currentDisplayed()) {
update();
}

View file

@ -391,7 +391,7 @@ void MediaPreviewWidget::clipCallback(
Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
case Notification::Reinit: {
if (_gifThumbnail && _gifThumbnail->state() == State::Error) {
_gifThumbnail.setBad();
}
@ -413,7 +413,7 @@ void MediaPreviewWidget::clipCallback(
update();
} break;
case NotificationRepaint: {
case Notification::Repaint: {
if ((_gif && _gif->started() && !_gif->currentDisplayed())
|| (_gifThumbnail
&& _gifThumbnail->started()