From 2801bd99b835a140a987ddf50c2fdab640f1edb2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 4 Jun 2021 19:14:42 +0400 Subject: [PATCH] Render media viewer icons in RendererGL. --- .../media/view/media_view_overlay_opengl.cpp | 175 ++++++++++++++++-- .../media/view/media_view_overlay_opengl.h | 16 ++ .../media/view/media_view_overlay_raster.cpp | 3 + .../media/view/media_view_overlay_raster.h | 1 + .../media/view/media_view_overlay_renderer.h | 1 + .../media/view/media_view_overlay_widget.cpp | 15 +- Telegram/lib_ui | 2 +- 7 files changed, 190 insertions(+), 23 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index cf379cf61..4fd05b967 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -10,20 +10,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/gl/gl_shader.h" #include "media/streaming/media_streaming_common.h" #include "base/platform/base_platform_info.h" +#include "styles/style_media_view.h" namespace Media::View { namespace { using namespace Ui::GL; -constexpr auto kQuads = 8; -constexpr auto kQuadVertices = kQuads * 4; -constexpr auto kQuadValues = kQuadVertices * 4; -constexpr auto kControls = 6; -constexpr auto kControlValues = 2 * 4 + 4 * 4; -constexpr auto kControlsValues = kControls * kControlValues; -constexpr auto kValues = kQuadValues + kControlsValues; - constexpr auto kRadialLoadingOffset = 4; constexpr auto kThemePreviewOffset = kRadialLoadingOffset + 4; constexpr auto kDocumentBubbleOffset = kThemePreviewOffset + 4; @@ -31,6 +24,8 @@ constexpr auto kSaveMsgOffset = kDocumentBubbleOffset + 4; constexpr auto kFooterOffset = kSaveMsgOffset + 4; constexpr auto kCaptionOffset = kFooterOffset + 4; constexpr auto kGroupThumbsOffset = kCaptionOffset + 4; +constexpr auto kControlsOffset = kGroupThumbsOffset + 4; +constexpr auto kControlValues = 2 * 4 + 4 * 4; [[nodiscard]] ShaderPart FragmentPlaceOnTransparentBackground() { return { @@ -61,13 +56,19 @@ OverlayWidget::RendererGL::RendererGL(not_null owner) _saveMsgImage.invalidate(); _footerImage.invalidate(); _captionImage.invalidate(); + invalidateControls(); }, _lifetime); } void OverlayWidget::RendererGL::init( not_null widget, QOpenGLFunctions &f) { - _factor = widget->devicePixelRatio(); + constexpr auto kQuads = 8; + constexpr auto kQuadVertices = kQuads * 4; + constexpr auto kQuadValues = kQuadVertices * 4; + constexpr auto kControlsValues = kControlsCount * kControlValues; + constexpr auto kValues = kQuadValues + kControlsValues; + _contentBuffer.emplace(); _contentBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); _contentBuffer->create(); @@ -104,6 +105,21 @@ void OverlayWidget::RendererGL::init( FragmentSampleYUV420Texture(), })); + _fillProgram.emplace(); + LinkProgram( + &*_fillProgram, + VertexShader({ VertexViewportTransform() }), + FragmentShader({ FragmentStaticColor() })); + + _controlsProgram.emplace(); + LinkProgram( + &*_controlsProgram, + _texturedVertexShader, + FragmentShader({ + FragmentSampleARGB32Texture(), + FragmentGlobalOpacity(), + })); + _background.init(f); } @@ -116,6 +132,8 @@ void OverlayWidget::RendererGL::deinit( _texturedVertexShader = nullptr; _withTransparencyProgram = std::nullopt; _yuv420Program = std::nullopt; + _fillProgram = std::nullopt; + _controlsProgram = std::nullopt; _contentBuffer = std::nullopt; } @@ -124,14 +142,20 @@ void OverlayWidget::RendererGL::resize( QOpenGLFunctions &f, int w, int h) { - _factor = widget->devicePixelRatio(); - _viewport = QSize(w, h); + const auto factor = widget->devicePixelRatio(); + if (_factor != factor) { + _factor = factor; + _controlsImage.invalidate(); + } + _viewport = QSize{ w, h }; + _uniformViewport = QVector2D( + _viewport.width() * _factor, + _viewport.height() * _factor); setDefaultViewport(f); } void OverlayWidget::RendererGL::setDefaultViewport(QOpenGLFunctions &f) { - const auto size = _viewport * _factor; - f.glViewport(0, 0, size.width(), size.height()); + f.glViewport(0, 0, _uniformViewport.x(), _uniformViewport.y()); } void OverlayWidget::RendererGL::paint( @@ -171,6 +195,7 @@ void OverlayWidget::RendererGL::paintBackground() { _viewport, _factor, bg); + _contentBuffer->bind(); } void OverlayWidget::RendererGL::paintTransformedVideoFrame( @@ -269,6 +294,7 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent( const auto cacheKey = image.cacheKey(); const auto upload = (_cacheKey != cacheKey); if (upload) { + _cacheKey = cacheKey; const auto stride = image.bytesPerLine() / 4; const auto data = image.constBits(); uploadTexture( @@ -316,10 +342,9 @@ void OverlayWidget::RendererGL::paintTransformedContent( texCoords[3][0], texCoords[3][1], }; - _contentBuffer->bind(); _contentBuffer->write(0, coords, sizeof(coords)); - program->setUniformValue("viewport", QSizeF(_viewport * _factor)); + program->setUniformValue("viewport", _uniformViewport); program->setUniformValue("s_texture", GLint(0)); toggleBlending(false); @@ -395,6 +420,13 @@ void OverlayWidget::RendererGL::paintSaveMsg(QRect outer) { }, kSaveMsgOffset, true); } +void OverlayWidget::RendererGL::paintControlsStart() { + validateControls(); + _f->glActiveTexture(GL_TEXTURE0); + _controlsImage.bind(*_f); + toggleBlending(true); +} + void OverlayWidget::RendererGL::paintControl( OverState control, QRect outer, @@ -402,7 +434,114 @@ void OverlayWidget::RendererGL::paintControl( QRect inner, float64 innerOpacity, const style::icon &icon) { - AssertIsDebug(controls); + const auto meta = ControlMeta(control); + Assert(meta.icon == &icon); + + const auto &bg = st::mediaviewControlBg->c; + const auto bgAlpha = int(std::round(bg.alpha() * outerOpacity)); + const auto offset = kControlsOffset + (meta.index * kControlValues) / 4; + const auto fgOffset = offset + 2; + const auto bgRect = TransformRect(outer, _viewport, _factor); + const auto iconRect = _controlsImage.texturedRect( + inner, + _controlsTextures[meta.index]); + const auto iconGeometry = transformRect(iconRect.geometry); + const GLfloat coords[] = { + bgRect.left(), bgRect.top(), + bgRect.right(), bgRect.top(), + bgRect.right(), bgRect.bottom(), + bgRect.left(), bgRect.bottom(), + + iconGeometry.left(), iconGeometry.top(), + iconRect.texture.left(), iconRect.texture.bottom(), + + iconGeometry.right(), iconGeometry.top(), + iconRect.texture.right(), iconRect.texture.bottom(), + + iconGeometry.right(), iconGeometry.bottom(), + iconRect.texture.right(), iconRect.texture.top(), + + iconGeometry.left(), iconGeometry.bottom(), + iconRect.texture.left(), iconRect.texture.top(), + }; + if (!outer.isEmpty() && bgAlpha > 0) { + _contentBuffer->write( + offset * 4 * sizeof(GLfloat), + coords, + sizeof(coords)); + _f->glUseProgram(_fillProgram->programId()); + _fillProgram->setUniformValue("viewport", _uniformViewport); + FillRectangle( + *_f, + &*_fillProgram, + offset, + QColor(bg.red(), bg.green(), bg.blue(), bgAlpha)); + } else { + _contentBuffer->write( + fgOffset * 4 * sizeof(GLfloat), + coords + (fgOffset - offset) * 4, + sizeof(coords) - (fgOffset - offset) * 4 * sizeof(GLfloat)); + } + _f->glUseProgram(_controlsProgram->programId()); + _controlsProgram->setUniformValue("g_opacity", GLfloat(innerOpacity)); + _controlsProgram->setUniformValue("viewport", _uniformViewport); + FillTexturedRectangle(*_f, &*_controlsProgram, fgOffset); +} + +auto OverlayWidget::RendererGL::ControlMeta(OverState control) +-> Control { + switch (control) { + case OverLeftNav: return { 0, &st::mediaviewLeft }; + case OverRightNav: return { 1, &st::mediaviewRight }; + case OverClose: return { 2, &st::mediaviewClose }; + case OverSave: return { 3, &st::mediaviewSave }; + case OverRotate: return { 4, &st::mediaviewRotate }; + case OverMore: return { 5, &st::mediaviewMore }; + } + Unexpected("Control value in OverlayWidget::RendererGL::ControlIndex."); +} + +void OverlayWidget::RendererGL::validateControls() { + if (!_controlsImage.image().isNull()) { + return; + } + const auto metas = { + ControlMeta(OverLeftNav), + ControlMeta(OverRightNav), + ControlMeta(OverClose), + ControlMeta(OverSave), + ControlMeta(OverRotate), + ControlMeta(OverMore), + }; + auto maxWidth = 0; + auto fullHeight = 0; + for (const auto meta : metas) { + maxWidth = std::max(meta.icon->width(), maxWidth); + fullHeight += meta.icon->height(); + } + auto image = QImage( + QSize(maxWidth, fullHeight) * _factor, + QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + image.setDevicePixelRatio(_factor); + { + auto p = QPainter(&image); + auto index = 0; + auto height = 0; + for (const auto meta : metas) { + meta.icon->paint(p, 0, height, maxWidth); + _controlsTextures[index++] = QRect( + QPoint(0, height) * _factor, + meta.icon->size() * _factor); + height += meta.icon->height(); + } + } + _controlsImage.setImage(std::move(image)); +} + +void OverlayWidget::RendererGL::invalidateControls() { + _controlsImage.invalidate(); + ranges::fill(_controlsTextures, QRect()); } void OverlayWidget::RendererGL::paintFooter(QRect outer, float64 opacity) { @@ -439,10 +578,12 @@ void OverlayWidget::RendererGL::invalidate() { &_footerImage, &_captionImage, &_groupThumbsImage, + &_controlsImage, }; for (const auto image : images) { image->setImage(QImage()); } + invalidateControls(); } void OverlayWidget::RendererGL::paintUsingRaster( @@ -494,7 +635,7 @@ void OverlayWidget::RendererGL::paintUsingRaster( sizeof(coords)); _f->glUseProgram(_imageProgram->programId()); - _imageProgram->setUniformValue("viewport", QSizeF(_viewport * _factor)); + _imageProgram->setUniformValue("viewport", _uniformViewport); _imageProgram->setUniformValue("s_texture", GLint(0)); _f->glActiveTexture(GL_TEXTURE0); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index 6b8f383f4..e1abb7248 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -39,6 +39,10 @@ public: QOpenGLFunctions &f) override; private: + struct Control { + int index = -1; + not_null icon; + }; bool handleHideWorkaround(QOpenGLFunctions &f); void setDefaultViewport(QOpenGLFunctions &f); @@ -60,6 +64,7 @@ private: void paintThemePreview(QRect outer) override; void paintDocumentBubble(QRect outer, QRect icon) override; void paintSaveMsg(QRect outer) override; + void paintControlsStart() override; void paintControl( OverState control, QRect outer, @@ -80,6 +85,8 @@ private: int bufferOffset, bool transparent = false); + void validateControls(); + void invalidateControls(); void toggleBlending(bool enabled); [[nodiscard]] Ui::GL::Rect transformRect(const QRect &raster) const; @@ -100,12 +107,15 @@ private: Ui::GL::BackgroundFiller _background; QSize _viewport; float _factor = 1.; + QVector2D _uniformViewport; std::optional _contentBuffer; std::optional _imageProgram; QOpenGLShader *_texturedVertexShader = nullptr; std::optional _withTransparencyProgram; std::optional _yuv420Program; + std::optional _fillProgram; + std::optional _controlsProgram; Ui::GL::Textures<4> _textures; QSize _rgbaSize; QSize _lumaSize; @@ -121,6 +131,12 @@ private: Ui::GL::Image _footerImage; Ui::GL::Image _captionImage; Ui::GL::Image _groupThumbsImage; + Ui::GL::Image _controlsImage; + + static constexpr auto kControlsCount = 6; + [[nodiscard]] static Control ControlMeta(OverState control); + std::array _controlsTextures; + bool _blendingEnabled = false; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp index 70d9d8d95..039f10882 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp @@ -120,6 +120,9 @@ void OverlayWidget::RendererSW::paintSaveMsg(QRect outer) { } } +void OverlayWidget::RendererSW::paintControlsStart() { +} + void OverlayWidget::RendererSW::paintControl( OverState control, QRect outer, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h index 4ca8d0dfc..b31a47425 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h @@ -40,6 +40,7 @@ private: void paintThemePreview(QRect outer) override; void paintDocumentBubble(QRect outer, QRect icon) override; void paintSaveMsg(QRect outer) override; + void paintControlsStart() override; void paintControl( OverState control, QRect outer, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h index e10f12253..d258772c6 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h @@ -27,6 +27,7 @@ public: virtual void paintThemePreview(QRect outer) = 0; virtual void paintDocumentBubble(QRect outer, QRect icon) = 0; virtual void paintSaveMsg(QRect outer) = 0; + virtual void paintControlsStart() = 0; virtual void paintControl( OverState control, QRect outer, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 99f276e70..e4f6a6db1 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3181,10 +3181,13 @@ void OverlayWidget::paint(not_null renderer) { fillTransparentBackground); } paintRadialLoading(renderer); - } else if (_themePreviewShown) { - renderer->paintThemePreview(_themePreviewRect); - } else if (documentBubbleShown() && !_docRect.isEmpty()) { - renderer->paintDocumentBubble(_docRect, _docIconRect); + } else { + int a = 0; + if (_themePreviewShown) { + renderer->paintThemePreview(_themePreviewRect); + } else if (documentBubbleShown() && !_docRect.isEmpty()) { + renderer->paintDocumentBubble(_docRect, _docIconRect); + } } updateSaveMsgState(); if (_saveMsgStarted && _saveMsgOpacity.current() > 0.) { @@ -3453,6 +3456,7 @@ void OverlayWidget::paintControls( const style::icon &icon; }; const QRect kEmpty; + // When adding / removing controls please update RendererGL. const Control controls[] = { { OverLeftNav, @@ -3492,6 +3496,7 @@ void OverlayWidget::paintControls( st::mediaviewMore }, }; + renderer->paintControlsStart(); for (const auto &control : controls) { if (!control.visible) { continue; @@ -3938,7 +3943,7 @@ void OverlayWidget::preloadData(int delta) { if (!_index) { return; } - auto from = *_index + (delta ? delta : -1); + auto from = *_index + (delta ? -delta : -1); auto till = *_index + (delta ? delta * kPreloadCount : 1); if (from > till) std::swap(from, till); diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 06f7b1d4e..02049aeaa 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 06f7b1d4ec414a631010a9a3e2ef20a335523bc6 +Subproject commit 02049aeaa8806ef5d23fbf050be7e341e2d5bbde