From 71ddfacfaa0a080b15aad9c3be375f08d46fb370 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 7 Jun 2021 10:25:44 +0400 Subject: [PATCH] Fix showing static content in OpenGL media viewer. --- .../media/view/media_view_overlay_opengl.cpp | 43 ++++-- .../media/view/media_view_overlay_opengl.h | 2 +- .../media/view/media_view_overlay_widget.cpp | 126 +++++++++++------- .../media/view/media_view_overlay_widget.h | 2 + 4 files changed, 113 insertions(+), 60 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index 8ba007cf1..b7d35379f 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -264,6 +264,14 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent( const QImage &image, ContentGeometry geometry, bool fillTransparentBackground) { + Expects(image.isNull() + || image.format() == QImage::Format_RGB32 + || image.format() == QImage::Format_ARGB32_Premultiplied); + + if (geometry.rect.isEmpty()) { + return; + } + auto &program = fillTransparentBackground ? _withTransparencyProgram : _imageProgram; @@ -282,20 +290,33 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent( _f->glActiveTexture(GL_TEXTURE0); _textures.bind(*_f, 0); - const auto cacheKey = image.cacheKey(); + const auto cacheKey = image.isNull() ? qint64(-1) : image.cacheKey(); const auto upload = (_cacheKey != cacheKey); if (upload) { _cacheKey = cacheKey; - const auto stride = image.bytesPerLine() / 4; - const auto data = image.constBits(); - uploadTexture( - GL_RGBA, - GL_RGBA, - image.size(), - _rgbaSize, - stride, - data); - _rgbaSize = image.size(); + if (image.isNull()) { + // Upload transparent 2x2 texture. + const auto stride = 2; + const uint32_t data[4] = { 0 }; + uploadTexture( + GL_RGBA, + GL_RGBA, + QSize(2, 2), + _rgbaSize, + stride, + data); + } else { + const auto stride = image.bytesPerLine() / 4; + const auto data = image.constBits(); + uploadTexture( + GL_RGBA, + GL_RGBA, + image.size(), + _rgbaSize, + stride, + data); + _rgbaSize = image.size(); + } } paintTransformedContent(&*program, geometry); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index a04253622..aa731a626 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -119,7 +119,7 @@ private: QSize _rgbaSize; QSize _lumaSize; QSize _chromaSize; - quint64 _cacheKey = 0; + qint64 _cacheKey = 0; int _trackFrameIndex = 0; int _streamedIndex = 0; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 1df3cec68..bd210f507 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -139,14 +139,14 @@ QWidget *PipDelegate::pipParentWidget() { return _parent; } -Images::Options VideoThumbOptions(DocumentData *document) { +[[nodiscard]] Images::Options VideoThumbOptions(DocumentData *document) { const auto result = Images::Option::Smooth | Images::Option::Blurred; return (document && document->isVideoMessage()) ? (result | Images::Option::Circled) : result; } -QImage PrepareStaticImage(QImage image) { +[[nodiscard]] QImage PrepareStaticImage(QImage image) { if (image.width() > kMaxDisplayImageSize || image.height() > kMaxDisplayImageSize) { image = image.scaled( @@ -158,14 +158,35 @@ QImage PrepareStaticImage(QImage image) { return image; } -QImage PrepareStaticImage(const QString &path) { +[[nodiscard]] QImage PrepareStaticImage(const QString &path) { return PrepareStaticImage(App::readImage(path, nullptr, false)); } -QImage PrepareStaticImage(const QByteArray &bytes) { +[[nodiscard]] QImage PrepareStaticImage(const QByteArray &bytes) { return PrepareStaticImage(App::readImage(bytes, nullptr, false)); } +[[nodiscard]] bool IsSemitransparent(const QImage &image) { + if (image.isNull()) { + return true; + } else if (!image.hasAlphaChannel()) { + return false; + } + Assert(image.format() == QImage::Format_ARGB32_Premultiplied); + constexpr auto kAlphaMask = 0xFF000000; + auto ints = reinterpret_cast(image.bits()); + const auto add = (image.bytesPerLine() / 4) - image.width(); + for (auto y = 0; y != image.height(); ++y) { + for (auto till = ints + image.width(); ints != till; ++ints) { + if ((*ints & kAlphaMask) != kAlphaMask) { + return true; + } + } + ints += add; + } + return false; +} + } // namespace struct OverlayWidget::SharedMedia { @@ -589,6 +610,18 @@ bool OverlayWidget::documentBubbleShown() const { && _staticContent.isNull()); } +void OverlayWidget::setStaticContent(QImage image) { + constexpr auto kGood = QImage::Format_ARGB32_Premultiplied; + if (!image.isNull() + && image.format() != kGood + && image.format() != QImage::Format_RGB32) { + image = std::move(image).convertToFormat(kGood); + } + image.setDevicePixelRatio(cRetinaFactor()); + _staticContent = std::move(image); + _staticContentTransparent = IsSemitransparent(_staticContent); +} + bool OverlayWidget::contentShown() const { return _photo || documentContentShown(); } @@ -2362,43 +2395,44 @@ void OverlayWidget::displayDocument( refreshMediaViewer(); if (_document) { - if (_document->sticker()) { - if (const auto image = _documentMedia->getStickerLarge()) { - _staticContent = image->original(); - } else if (const auto thumbnail = _documentMedia->thumbnail()) { - _staticContent = thumbnail->pixBlurred( - _document->dimensions.width(), - _document->dimensions.height() - ).toImage(); +if (_document->sticker()) { + if (const auto image = _documentMedia->getStickerLarge()) { + setStaticContent(image->original()); + } else if (const auto thumbnail = _documentMedia->thumbnail()) { + setStaticContent(thumbnail->pixBlurred( + _document->dimensions.width(), + _document->dimensions.height() + ).toImage()); + } +} else { + if (_documentMedia->canBePlayed() + && initStreaming(continueStreaming)) { + } else if (_document->isVideoFile()) { + _documentMedia->automaticLoad(fileOrigin(), item); + initStreamingThumbnail(); + } else if (_document->isTheme()) { + _documentMedia->automaticLoad(fileOrigin(), item); + initThemePreview(); + } else { + _documentMedia->automaticLoad(fileOrigin(), item); + _document->saveFromDataSilent(); + auto &location = _document->location(true); + if (location.accessEnable()) { + const auto &path = location.name(); + if (QImageReader(path).canRead()) { + setStaticContent(PrepareStaticImage(path)); + _touchbarDisplay.fire(TouchBarItemType::Photo); } - } else { - if (_documentMedia->canBePlayed() - && initStreaming(continueStreaming)) { - } else if (_document->isVideoFile()) { - _documentMedia->automaticLoad(fileOrigin(), item); - initStreamingThumbnail(); - } else if (_document->isTheme()) { - _documentMedia->automaticLoad(fileOrigin(), item); - initThemePreview(); - } else { - _documentMedia->automaticLoad(fileOrigin(), item); - _document->saveFromDataSilent(); - auto &location = _document->location(true); - if (location.accessEnable()) { - const auto &path = location.name(); - if (QImageReader(path).canRead()) { - _staticContent = PrepareStaticImage(path); - _touchbarDisplay.fire(TouchBarItemType::Photo); - } - } else if (!_documentMedia->bytes().isEmpty()) { - _staticContent = PrepareStaticImage(_documentMedia->bytes()); - if (!_staticContent.isNull()) { - _touchbarDisplay.fire(TouchBarItemType::Photo); - } - } - location.accessDisable(); + } else if (!_documentMedia->bytes().isEmpty()) { + setStaticContent( + PrepareStaticImage(_documentMedia->bytes())); + if (!_staticContent.isNull()) { + _touchbarDisplay.fire(TouchBarItemType::Photo); } } + location.accessDisable(); + } +} } refreshCaption(item); @@ -2459,7 +2493,6 @@ void OverlayWidget::displayDocument( } else if (_themePreviewShown) { updateThemePreviewGeometry(); } else if (!_staticContent.isNull()) { - _staticContent.setDevicePixelRatio(cRetinaFactor()); const auto size = style::ConvertScale( flipSizeByRotation(_staticContent.size())); _w = size.width(); @@ -2631,7 +2664,7 @@ void OverlayWidget::initStreamingThumbnail() { const auto h = size.height(); const auto options = VideoThumbOptions(_document); const auto goodOptions = (options & ~Images::Option::Blurred); - _staticContent = (good + setStaticContent((good ? good : thumbnail ? thumbnail @@ -2643,8 +2676,7 @@ void OverlayWidget::initStreamingThumbnail() { good ? goodOptions : options, w / cIntRetinaFactor(), h / cIntRetinaFactor() - ).toImage(); - _staticContent.setDevicePixelRatio(cRetinaFactor()); + ).toImage()); } void OverlayWidget::streamingReady(Streaming::Information &&info) { @@ -2970,7 +3002,7 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { if (videoShown()) { _streamed->instance.saveFrameToCover(); const auto saved = base::take(_rotation); - _staticContent = transformedShownContent(); + setStaticContent(transformedShownContent()); _rotation = saved; updateContentRect(); } @@ -3163,13 +3195,12 @@ void OverlayWidget::validatePhotoImage(Image *image, bool blurred) { } const auto use = flipSizeByRotation({ _width, _height }) * cIntRetinaFactor(); - _staticContent = image->pixNoCache( + setStaticContent(image->pixNoCache( use.width(), use.height(), Images::Option::Smooth | (blurred ? Images::Option::Blurred : Images::Option(0)) - ).toImage(); - _staticContent.setDevicePixelRatio(cRetinaFactor()); + ).toImage()); _blurred = blurred; } @@ -3226,8 +3257,7 @@ void OverlayWidget::paint(not_null renderer) { validatePhotoCurrentImage(); const auto fillTransparentBackground = (!_document || (!_document->sticker() && !_document->isVideoMessage())) - && (_staticContent.isNull() - || _staticContent.hasAlphaChannel()); + && _staticContentTransparent; renderer->paintTransformedStaticContent( _staticContent, contentGeometry(), diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 974b43643..f1c641c6a 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -413,6 +413,7 @@ private: int rotation) const; [[nodiscard]] bool documentContentShown() const; [[nodiscard]] bool documentBubbleShown() const; + void setStaticContent(QImage image); [[nodiscard]] bool contentShown() const; [[nodiscard]] bool opaqueContentShown() const; void clearStreaming(bool savePosition = true); @@ -477,6 +478,7 @@ private: bool _pressed = false; int32 _dragging = 0; QImage _staticContent; + bool _staticContentTransparent = false; bool _blurred = true; ContentGeometry _oldGeometry;