From 3a38497c4c5690ffea4133845dced7ecb3fbe840 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 13 Dec 2022 10:29:48 +0400 Subject: [PATCH] Support displaying of video spoilers. --- .../history/view/media/history_view_gif.cpp | 170 ++++++++++++------ .../history/view/media/history_view_gif.h | 3 + 2 files changed, 117 insertions(+), 56 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 632a9aa88..7db685895 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -92,6 +92,10 @@ Gif::Gif( setDocumentLinks(_data, realParent); setStatusSize(Ui::FileStatusSizeReady); + if (_spoiler) { + createSpoilerLink(_spoiler.get()); + } + refreshCaption(); if ((_dataMedia = _data->activeMediaView())) { dataMediaCreated(); @@ -328,9 +332,59 @@ void Gif::draw(Painter &p, const PaintContext &context) const { && canBePlayed && CanPlayInline(_data); const auto activeRoundPlaying = activeRoundStreamed(); + + auto paintx = 0, painty = 0, paintw = width(), painth = height(); + auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right(); + const bool bubble = _parent->hasBubble(); + const auto outbg = context.outbg; + const auto inWebPage = (_parent->media() != this); + const auto isRound = _data->isVideoMessage(); + + const auto rounding = inWebPage + ? std::optional() + : adjustedBubbleRoundingWithCaption(_caption); + if (bubble) { + if (!_caption.isEmpty()) { + painth -= st::mediaCaptionSkip + _caption.countHeight(captionw); + if (isBubbleBottom()) { + painth -= st::msgPadding.bottom(); + } + } + } + + auto usex = 0, usew = paintw; + const auto unwrapped = isUnwrapped(); + const auto via = unwrapped ? item->Get() : nullptr; + const auto reply = unwrapped ? _parent->displayedReply() : nullptr; + const auto forwarded = unwrapped ? item->Get() : nullptr; + const auto rightAligned = unwrapped + && outbg + && !_parent->delegate()->elementIsChatWide(); + if (via || reply || forwarded) { + usew = maxWidth() - additionalWidth(via, reply, forwarded); + if (rightAligned) { + usex = width() - usew; + } + } + if (isRound) { + accumulate_min(usew, painth); + } + if (rtl()) usex = width() - usex - usew; + + QRect rthumb(style::rtlrect(usex + paintx, painty, usew, painth, width())); + + const auto revealed = (!isRound && _spoiler) + ? _spoiler->revealAnimation.value(_spoiler->revealed ? 1. : 0.) + : 1.; + const auto fullHiddenBySpoiler = (revealed == 0.); + if (revealed < 1.) { + validateSpoilerImageCache(rthumb.size(), rounding); + } + const auto startPlay = autoplay && !_streamed - && !activeRoundPlaying; + && !activeRoundPlaying + && !fullHiddenBySpoiler; if (startPlay) { const_cast(this)->playAnimation(true); } else { @@ -339,13 +393,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const { const auto streamingMode = _streamed || activeRoundPlaying || autoplay; const auto activeOwnPlaying = activeOwnStreamed(); - auto paintx = 0, painty = 0, paintw = width(), painth = height(); - auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right(); - const bool bubble = _parent->hasBubble(); - const auto outbg = context.outbg; - const auto inWebPage = (_parent->media() != this); - const auto isRound = _data->isVideoMessage(); - const auto unwrapped = isUnwrapped(); auto displayMute = false; const auto streamed = activeRoundPlaying ? activeRoundPlaying @@ -372,38 +419,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const { const auto radial = isRadialAnimation() || (streamedForWaiting && streamedForWaiting->waitingShown()); - const auto rounding = inWebPage - ? std::optional() - : adjustedBubbleRoundingWithCaption(_caption); - if (bubble) { - if (!_caption.isEmpty()) { - painth -= st::mediaCaptionSkip + _caption.countHeight(captionw); - if (isBubbleBottom()) { - painth -= st::msgPadding.bottom(); - } - } - } - - auto usex = 0, usew = paintw; - const auto via = unwrapped ? item->Get() : nullptr; - const auto reply = unwrapped ? _parent->displayedReply() : nullptr; - const auto forwarded = unwrapped ? item->Get() : nullptr; - const auto rightAligned = unwrapped - && outbg - && !_parent->delegate()->elementIsChatWide(); - if (via || reply || forwarded) { - usew = maxWidth() - additionalWidth(via, reply, forwarded); - if (rightAligned) { - usex = width() - usew; - } - } - if (isRound) { - accumulate_min(usew, painth); - } - if (rtl()) usex = width() - usex - usew; - - QRect rthumb(style::rtlrect(usex + paintx, painty, usew, painth, width())); - if (!bubble && !unwrapped) { Assert(rounding.has_value()); fillImageShadow(p, rthumb, *rounding, context); @@ -411,7 +426,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { const auto skipDrawingContent = context.skipDrawingParts == PaintContext::SkipDrawingParts::Content; - if (streamed && !skipDrawingContent) { + if (streamed && !skipDrawingContent && !fullHiddenBySpoiler) { auto paused = context.paused; auto request = ::Media::Streaming::FrameRequest{ .outer = QSize(usew, painth) * cIntRetinaFactor(), @@ -474,18 +489,17 @@ void Gif::draw(Painter &p, const PaintContext &context) const { p.setOpacity(1.); } } - } else if (!skipDrawingContent) { + } else if (!skipDrawingContent && !fullHiddenBySpoiler) { ensureDataMediaCreated(); validateThumbCache({ usew, painth }, isRound, rounding); p.drawImage(rthumb, _thumbCache); } - if (!isRound && _spoiler) { - fillImageSpoiler( - p, - _spoiler.get(), - rthumb, - context); + if (!isRound && revealed < 1.) { + p.setOpacity(1. - revealed); + p.drawImage(rthumb.topLeft(), _spoiler->background); + fillImageSpoiler(p, _spoiler.get(), rthumb, context); + p.setOpacity(1.); } if (context.selected()) { if (isRound) { @@ -498,13 +512,14 @@ void Gif::draw(Painter &p, const PaintContext &context) const { if (radial || (!streamingMode && ((!loaded && !_data->loading()) || !autoplay))) { - const auto radialOpacity = (item->isSending() || _data->uploading()) + const auto opacity = (item->isSending() || _data->uploading()) ? 1. : streamedForWaiting ? streamedForWaiting->waitingOpacity() : (radial && loaded) ? _animation->radial.opacity() : 1.; + const auto radialOpacity = opacity * revealed; const auto innerSize = st::msgFileLayout.thumbSize; auto inner = QRect(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize); p.setPen(Qt::NoPen); @@ -542,7 +557,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { if (icon) { icon->paintInCenter(p, inner); } - p.setOpacity(1); + p.setOpacity(revealed); if (radial) { QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine))); if (streamedForWaiting && !_data->uploading()) { @@ -562,6 +577,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { sti->historyFileThumbRadialFg); } } + p.setOpacity(1.); } if (displayMute) { auto muteRect = style::rtlrect(rthumb.x() + (rthumb.width() - st::historyVideoMessageMuteSize) / 2, rthumb.y() + st::msgDateImgDelta, st::historyVideoMessageMuteSize, st::historyVideoMessageMuteSize, width()); @@ -808,6 +824,26 @@ QImage Gif::prepareThumbCache(QSize outer) const { blurFromLarge ? large : blurred); } +void Gif::validateSpoilerImageCache( + QSize outer, + std::optional rounding) const { + Expects(_spoiler != nullptr); + + const auto ratio = style::DevicePixelRatio(); + if (_spoiler->background.size() == (outer * ratio) + && _spoiler->backgroundRounding == rounding) { + return; + } + _spoiler->background = Images::Round( + PrepareWithBlurredBackground( + outer, + ::Media::Streaming::ExpandDecision(), + nullptr, + _dataMedia->thumbnailInline()), + MediaRoundingMask(rounding)); + _spoiler->backgroundRounding = rounding; +} + void Gif::drawCornerStatus( Painter &p, const PaintContext &context, @@ -984,7 +1020,9 @@ TextState Gif::textState(QPoint point, StateRequest request) const { } if (QRect(usex + paintx, painty, usew, painth).contains(point)) { ensureDataMediaCreated(); - result.link = _data->uploading() + result.link = (_spoiler && !_spoiler->revealed) + ? _spoiler->link + : _data->uploading() ? _cancell : _realParent->isSending() ? nullptr @@ -1105,6 +1143,15 @@ void Gif::drawGrouped( const auto fullFeatured = fullFeaturedGrouped(sides); const auto cornerDownload = fullFeatured && downloadInCorner(); const auto canBePlayed = _dataMedia->canBePlayed(_realParent); + + const auto revealed = _spoiler + ? _spoiler->revealAnimation.value(_spoiler->revealed ? 1. : 0.) + : 1.; + const auto fullHiddenBySpoiler = (revealed == 0.); + if (revealed < 1.) { + validateSpoilerImageCache(geometry.size(), rounding); + } + const auto autoplay = fullFeatured && autoplayEnabled() && canBePlayed @@ -1139,7 +1186,7 @@ void Gif::drawGrouped( const auto radial = isRadialAnimation() || (streamedForWaiting && streamedForWaiting->waitingShown()); - if (streamed) { + if (streamed && !fullHiddenBySpoiler) { const auto original = sizeForAspectRatio(); const auto originalWidth = style::ConvertScale(original.width()); const auto originalHeight = style::ConvertScale(original.height()); @@ -1171,11 +1218,18 @@ void Gif::drawGrouped( streamed->markFrameShown(); } } - } else { + } else if (!fullHiddenBySpoiler) { validateGroupedCache(geometry, rounding, cacheKey, cache); p.drawPixmap(geometry, *cache); } + if (revealed < 1.) { + p.setOpacity(1. - revealed); + p.drawImage(geometry.topLeft(), _spoiler->background); + fillImageSpoiler(p, _spoiler.get(), geometry, context); + p.setOpacity(1.); + } + const auto overlayOpacity = context.selected() ? (1. - highlightOpacity) : highlightOpacity; @@ -1191,13 +1245,14 @@ void Gif::drawGrouped( if (radial || (!streamingMode && ((!loaded && !_data->loading()) || !autoplay))) { - const auto radialOpacity = (item->isSending() || _data->uploading()) + const auto opacity = (item->isSending() || _data->uploading()) ? 1. : streamedForWaiting ? streamedForWaiting->waitingOpacity() : (radial && loaded) ? _animation->radial.opacity() : 1.; + const auto radialOpacity = opacity * revealed; const auto radialSize = st::historyGroupRadialSize; const auto inner = QRect( geometry.x() + (geometry.width() - radialSize) / 2, @@ -1248,7 +1303,7 @@ void Gif::drawGrouped( icon->paintInCenter(p, inner); } } - p.setOpacity(1); + p.setOpacity(revealed); if (radial) { const auto line = st::historyGroupRadialLine; const auto rinner = inner.marginsRemoved({ line, line, line, line }); @@ -1269,6 +1324,7 @@ void Gif::drawGrouped( sti->historyFileThumbRadialFg); } } + p.setOpacity(1.); } if (fullFeatured) { drawCornerStatus(p, context, geometry.topLeft()); @@ -1289,7 +1345,9 @@ TextState Gif::getStateGrouped( } } ensureDataMediaCreated(); - return TextState(_parent, _data->uploading() + return TextState(_parent, (_spoiler && !_spoiler->revealed) + ? _spoiler->link + : _data->uploading() ? _cancell : _realParent->isSending() ? nullptr diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index 54b30456a..7900f9e46 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -183,6 +183,9 @@ private: bool isEllipse, std::optional rounding) const; [[nodiscard]] QImage prepareThumbCache(QSize outer) const; + void validateSpoilerImageCache( + QSize outer, + std::optional rounding) const; void validateGroupedCache( const QRect &geometry,