diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2383d41eb..c7e467237 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -777,6 +777,11 @@ PRIVATE media/streaming/media_streaming_video_track.h media/view/media_view_group_thumbs.cpp media/view/media_view_group_thumbs.h + media/view/media_view_overlay_opengl.cpp + media/view/media_view_overlay_opengl.h + media/view/media_view_overlay_raster.cpp + media/view/media_view_overlay_raster.h + media/view/media_view_overlay_renderer.h media/view/media_view_overlay_widget.cpp media/view/media_view_overlay_widget.h media/view/media_view_pip.cpp diff --git a/Telegram/SourceFiles/boxes/edit_color_box.cpp b/Telegram/SourceFiles/boxes/edit_color_box.cpp index 8889db904..19a2fc3e8 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_color_box.cpp @@ -349,7 +349,9 @@ EditColorBox::Slider::Slider( , _type(type) , _color(color.red(), color.green(), color.blue()) , _value(valueFromColor(color)) -, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) { +, _transparent((_type == Type::Opacity) + ? style::TransparentPlaceholder() + : QBrush()) { prepareMinSize(); } @@ -758,7 +760,7 @@ EditColorBox::EditColorBox( , _greenField(this, st::colorValueInput, "G", 255) , _blueField(this, st::colorValueInput, "B", 255) , _result(this, st::colorResultInput) -, _transparent(style::transparentPlaceholderBrush()) +, _transparent(style::TransparentPlaceholder()) , _current(current) , _new(current) { if (_mode == Mode::RGBA) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp index 0ead4b863..5240e3c75 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp @@ -798,7 +798,7 @@ Ui::GL::ChosenRenderer Viewport::chooseRenderer( }; } return { - .renderer = std::make_unique(this), + .renderer = std::make_unique(this), .backend = Ui::GL::Backend::Raster, }; } diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport.h b/Telegram/SourceFiles/calls/group/calls_group_viewport.h index dac2fcdee..d0c1f9048 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport.h +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport.h @@ -90,7 +90,7 @@ public: private: struct Textures; class VideoTile; - class Renderer; + class RendererSW; class RendererGL; using TileId = quintptr; diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp index 06a01ce1b..957d6a623 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp @@ -174,90 +174,6 @@ vec4 background() { } }; } -void FillRectVertices(GLfloat *coords, Rect rect) { - coords[0] = coords[10] = rect.left(); - coords[1] = coords[11] = rect.top(); - coords[2] = rect.right(); - coords[3] = rect.top(); - coords[4] = coords[6] = rect.right(); - coords[5] = coords[7] = rect.bottom(); - coords[8] = rect.left(); - coords[9] = rect.bottom(); -} - -void FillTriangles( - QOpenGLFunctions &f, - gsl::span coords, - not_null buffer, - not_null program, - QSize viewportWithFactor, - const QColor &color, - Fn additional = nullptr) { - Expects(coords.size() % 6 == 0); - - if (coords.empty()) { - return; - } - buffer->bind(); - buffer->allocate(coords.data(), coords.size() * sizeof(GLfloat)); - - f.glUseProgram(program->programId()); - program->setUniformValue("viewport", QSizeF(viewportWithFactor)); - program->setUniformValue("s_color", Uniform(color)); - - GLint position = program->attributeLocation("position"); - f.glVertexAttribPointer( - position, - 2, - GL_FLOAT, - GL_FALSE, - 2 * sizeof(GLfloat), - nullptr); - f.glEnableVertexAttribArray(position); - - if (additional) { - additional(); - } - - f.glDrawArrays(GL_TRIANGLES, 0, coords.size() / 2); - - f.glDisableVertexAttribArray(position); -} - -void FillTexturedRectangle( - QOpenGLFunctions &f, - not_null program, - int skipVertices = 0) { - const auto shift = [&](int elements) { - return reinterpret_cast( - (skipVertices * 4 + elements) * sizeof(GLfloat)); - }; - GLint position = program->attributeLocation("position"); - f.glVertexAttribPointer( - position, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(GLfloat), - shift(0)); - f.glEnableVertexAttribArray(position); - - GLint texcoord = program->attributeLocation("v_texcoordIn"); - f.glVertexAttribPointer( - texcoord, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(GLfloat), - shift(2)); - f.glEnableVertexAttribArray(texcoord); - - f.glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - f.glDisableVertexAttribArray(position); - f.glDisableVertexAttribArray(texcoord); -} - } // namespace Viewport::RendererGL::RendererGL(not_null owner) @@ -319,14 +235,7 @@ void Viewport::RendererGL::init( FragmentRoundCorners(), })).vertex; - _bgBuffer.emplace(); - _bgBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); - _bgBuffer->create(); - _bgProgram.emplace(); - LinkProgram( - &*_bgProgram, - VertexShader({ VertexViewportTransform() }), - FragmentShader({ FragmentStaticColor() })); + _background.init(f); _imageProgram.emplace(); LinkProgram( @@ -366,10 +275,8 @@ void Viewport::RendererGL::ensureARGB32Program() { void Viewport::RendererGL::deinit( not_null widget, QOpenGLFunctions &f) { - _bgBuffer = std::nullopt; _frameBuffer = std::nullopt; _frameVertexShader = nullptr; - _bgProgram = std::nullopt; _imageProgram = std::nullopt; _downscaleProgram.argb32 = std::nullopt; _downscaleProgram.yuv420 = std::nullopt; @@ -381,6 +288,7 @@ void Viewport::RendererGL::deinit( } _tileData.clear(); _tileDataIndices.clear(); + _background.deinit(f); _buttons.destroy(f); } @@ -422,28 +330,13 @@ void Viewport::RendererGL::paint( void Viewport::RendererGL::fillBackground(QOpenGLFunctions &f) { const auto radius = st::roundRadiusLarge; const auto radiuses = QMargins{ radius, radius, radius, radius }; - auto bg = QRegion(QRect(QPoint(), _viewport)); + auto region = QRegion(QRect(QPoint(), _viewport)); for (const auto &tile : _owner->_tiles) { if (tile->shown()) { - bg -= tile->geometry().marginsRemoved(radiuses); + region -= tile->geometry().marginsRemoved(radiuses); } } - if (bg.isEmpty()) { - return; - } - _bgTriangles.resize((bg.end() - bg.begin()) * 12); - auto coords = _bgTriangles.data(); - for (const auto rect : bg) { - FillRectVertices(coords, transformRect(rect)); - coords += 12; - } - FillTriangles( - f, - _bgTriangles, - &*_bgBuffer, - &*_bgProgram, - _viewport * _factor, - st::groupCallBg->c); + _background.fill(f, region, _viewport, _factor, st::groupCallBg); } void Viewport::RendererGL::paintTile( @@ -957,12 +850,7 @@ void Viewport::RendererGL::drawFirstBlurPass( } Rect Viewport::RendererGL::transformRect(const Rect &raster) const { - return { - raster.left() * _factor, - float(_viewport.height() - raster.bottom()) * _factor, - raster.width() * _factor, - raster.height() * _factor, - }; + return TransformRect(raster, _viewport, _factor); } Rect Viewport::RendererGL::transformRect(const QRect &raster) const { diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.h b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.h index 2db68b805..244420c65 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.h +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/round_rect.h" #include "ui/effects/animations.h" #include "ui/effects/cross_line.h" +#include "ui/gl/gl_primitives.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_image.h" @@ -116,13 +117,12 @@ private: GLfloat _factor = 1.; QSize _viewport; bool _rgbaFrame = false; + Ui::GL::BackgroundFiller _background; std::optional _frameBuffer; - std::optional _bgBuffer; Program _downscaleProgram; std::optional _blurProgram; Program _frameProgram; std::optional _imageProgram; - std::optional _bgProgram; QOpenGLShader *_downscaleVertexShader = nullptr; QOpenGLShader *_frameVertexShader = nullptr; @@ -137,7 +137,6 @@ private: std::vector _tileData; std::vector _tileDataIndices; - std::vector _bgTriangles; Ui::CrossLineAnimation _pinIcon; Ui::CrossLineAnimation _muteIcon; Ui::RoundRect _pinBackground; diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp index 05e2f82c0..946da64bf 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp @@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Calls::Group { -Viewport::Renderer::Renderer(not_null owner) +Viewport::RendererSW::RendererSW(not_null owner) : _owner(owner) , _pinIcon(st::groupCallVideoTile.pin) , _pinBackground( @@ -28,27 +28,25 @@ Viewport::Renderer::Renderer(not_null owner) st::radialBg) { } -void Viewport::Renderer::paintFallback( +void Viewport::RendererSW::paintFallback( Painter &&p, const QRegion &clip, Ui::GL::Backend backend) { auto bg = clip; auto hq = PainterHighQualityEnabler(p); const auto bounding = clip.boundingRect(); - const auto opengl = (backend == Ui::GL::Backend::OpenGL); for (const auto &tile : _owner->_tiles) { - paintTile(p, tile.get(), bounding, opengl, bg); + paintTile(p, tile.get(), bounding, bg); } for (const auto rect : bg) { p.fillRect(rect, st::groupCallBg); } } -void Viewport::Renderer::paintTile( +void Viewport::RendererSW::paintTile( Painter &p, not_null tile, const QRect &clip, - bool opengl, QRegion &bg) { const auto track = tile->track(); const auto data = track->frameWithInfo(true); @@ -79,7 +77,7 @@ void Viewport::Renderer::paintTile( const auto left = (width - scaled.width()) / 2; const auto top = (height - scaled.height()) / 2; const auto target = QRect(QPoint(x + left, y + top), scaled); - if (UsePainterRotation(rotation, opengl)) { + if (UsePainterRotation(rotation, false)) { if (rotation) { p.save(); p.rotate(rotation); @@ -113,7 +111,7 @@ void Viewport::Renderer::paintTile( paintTileOutline(p, x, y, width, height, tile); } -void Viewport::Renderer::paintTileOutline( +void Viewport::RendererSW::paintTileOutline( Painter &p, int x, int y, @@ -137,7 +135,7 @@ void Viewport::Renderer::paintTileOutline( p.fillRect(x, y + height - outline, width - outline, outline, color); } -void Viewport::Renderer::paintTileControls( +void Viewport::RendererSW::paintTileControls( Painter &p, int x, int y, diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.h b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.h index 269a85f8f..deae5a88a 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.h +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.h @@ -15,9 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Calls::Group { -class Viewport::Renderer final : public Ui::GL::Renderer { +class Viewport::RendererSW final : public Ui::GL::Renderer { public: - explicit Renderer(not_null owner); + explicit RendererSW(not_null owner); void paintFallback( Painter &&p, @@ -29,7 +29,6 @@ private: Painter &p, not_null tile, const QRect &clip, - bool opengl, QRegion &bg); void paintTileOutline( Painter &p, diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 40efafa80..95053d1ed 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -237,7 +237,7 @@ mediaviewTextOpacity: 0.5; mediaviewTextOverOpacity: 1; mediaviewIconOpacity: 0.45; -mediaviewIconOverOpacity: 1; +mediaviewIconOverOpacity: 1.; mediaviewControlBgOpacity: 0.3; mediaviewControlMargin: 0px; mediaviewControlSize: 90px; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp new file mode 100644 index 000000000..ffadd28be --- /dev/null +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -0,0 +1,183 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "media/view/media_view_overlay_opengl.h" + +#include "base/platform/base_platform_info.h" + +namespace Media::View { + +void OverlayWidget::RendererGL::init( + not_null widget, + QOpenGLFunctions &f) { + _background.init(f); +} + +void OverlayWidget::RendererGL::deinit( + not_null widget, + QOpenGLFunctions &f) { + _background.deinit(f); +} + +void OverlayWidget::RendererGL::resize( + not_null widget, + QOpenGLFunctions &f, + int w, + int h) { + _factor = widget->devicePixelRatio(); + _viewport = QSize(w, h); + setDefaultViewport(f); +} + +void OverlayWidget::RendererGL::setDefaultViewport(QOpenGLFunctions &f) { + const auto size = _viewport * _factor; + f.glViewport(0, 0, size.width(), size.height()); +} + +void OverlayWidget::RendererGL::paint( + not_null widget, + QOpenGLFunctions &f) { + if (handleHideWorkaround(f)) { + return; + } + _f = &f; + _owner->paint(this); +} + +bool OverlayWidget::RendererGL::handleHideWorkaround(QOpenGLFunctions &f) { + if (!Platform::IsWindows() || !_owner->_hideWorkaround) { + return false; + } + // This is needed on Windows, + // because on reopen it blinks with the last shown content. + f.glClearColor(0., 0., 0., 0.); + f.glClear(GL_COLOR_BUFFER_BIT); + return true; +} + +void OverlayWidget::RendererGL::paintBackground() { + const auto &bg = _owner->_fullScreenVideo + ? st::mediaviewVideoBg + : st::mediaviewBg; + auto fill = QRegion(QRect(QPoint(), _viewport)); + if (_owner->opaqueContentShown()) { + fill -= _owner->contentRect(); + } + _background.fill( + *_f, + fill, + _viewport, + _factor, + bg); +} + +void OverlayWidget::RendererGL::paintTransformedVideoFrame( + QRect rect, + int rotation) { +} + +void OverlayWidget::RendererGL::paintTransformedStaticContent( + const QImage &image, + QRect rect, + int rotation, + bool fillTransparentBackground) { +} + +void OverlayWidget::RendererGL::paintRadialLoading( + QRect inner, + bool radial, + float64 radialOpacity) { + paintToCache(_radialCache, inner.size(), [&](Painter &&p) { + const auto newInner = QRect(QPoint(), inner.size()); + _owner->paintRadialLoadingContent(p, newInner, radial, radialOpacity); + }, true); + //p.drawImage(inner.topLeft(), _radialCache); +} + +void OverlayWidget::RendererGL::paintThemePreview(QRect outer) { + paintToCache(_themePreviewCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + _owner->paintThemePreviewContent(p, newOuter, newOuter); + }); +} + +void OverlayWidget::RendererGL::paintDocumentBubble( + QRect outer, + QRect icon) { + paintToCache(_documentBubbleCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + const auto newIcon = icon.translated(-outer.topLeft()); + _owner->paintDocumentBubbleContent(p, newOuter, newIcon, newOuter); + }); + //p.drawImage(outer.topLeft(), _documentBubbleCache); + _owner->paintRadialLoading(this); +} + +void OverlayWidget::RendererGL::paintSaveMsg(QRect outer) { + paintToCache(_saveMsgCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + _owner->paintSaveMsgContent(p, newOuter, newOuter); + }, true); + //p.drawImage(outer.topLeft(), _saveMsgCache); +} + +void OverlayWidget::RendererGL::paintControl( + OverState control, + QRect outer, + float64 outerOpacity, + QRect inner, + float64 innerOpacity, + const style::icon &icon) { + +} + +void OverlayWidget::RendererGL::paintFooter(QRect outer, float64 opacity) { + paintToCache(_footerCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + _owner->paintFooterContent(p, newOuter, newOuter, opacity); + }, true); + //p.drawImage(outer, _footerCache, QRect(QPoint(), outer.size()) * factor); +} + +void OverlayWidget::RendererGL::paintCaption(QRect outer, float64 opacity) { + paintToCache(_captionCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + _owner->paintCaptionContent(p, newOuter, newOuter, opacity); + }); + //p.drawImage(outer, _captionCache, ...); +} + +void OverlayWidget::RendererGL::paintGroupThumbs( + QRect outer, + float64 opacity) { + paintToCache(_groupThumbsCache, outer.size(), [&](Painter &&p) { + const auto newOuter = QRect(QPoint(), outer.size()); + _owner->paintGroupThumbsContent(p, newOuter, newOuter, opacity); + }); +} + +void OverlayWidget::RendererGL::paintToCache( + QImage &cache, + QSize size, + Fn method, + bool clear) { + if (cache.width() < size.width() * _factor + || cache.height() < size.height() * _factor) { + cache = QImage( + size * _factor, + QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(_factor); + } else if (cache.devicePixelRatio() != _factor) { + cache.setDevicePixelRatio(_factor); + } + if (clear) { + cache.fill(Qt::transparent); + } + method(Painter(&cache)); +} + +} // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h new file mode 100644 index 000000000..3db11a636 --- /dev/null +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -0,0 +1,91 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "media/view/media_view_overlay_renderer.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/gl_primitives.h" + +namespace Media::View { + +class OverlayWidget::RendererGL final : public OverlayWidget::Renderer { +public: + RendererGL(not_null owner) : _owner(owner) { + } + + void init( + not_null widget, + QOpenGLFunctions &f) override; + + void deinit( + not_null widget, + QOpenGLFunctions &f) override; + + void resize( + not_null widget, + QOpenGLFunctions &f, + int w, + int h); + + void paint( + not_null widget, + QOpenGLFunctions &f) override; + +private: + bool handleHideWorkaround(QOpenGLFunctions &f); + void setDefaultViewport(QOpenGLFunctions &f); + + void paintBackground(); + void paintTransformedVideoFrame(QRect rect, int rotation) override; + void paintTransformedStaticContent( + const QImage &image, + QRect rect, + int rotation, + bool fillTransparentBackground) override; + void paintRadialLoading( + QRect inner, + bool radial, + float64 radialOpacity) override; + void paintThemePreview(QRect outer) override; + void paintDocumentBubble(QRect outer, QRect icon) override; + void paintSaveMsg(QRect outer) override; + void paintControl( + OverState control, + QRect outer, + float64 outerOpacity, + QRect inner, + float64 innerOpacity, + const style::icon &icon) override; + void paintFooter(QRect outer, float64 opacity) override; + void paintCaption(QRect outer, float64 opacity) override; + void paintGroupThumbs(QRect outer, float64 opacity) override; + + void paintToCache( + QImage &cache, + QSize size, + Fn method, + bool clear = false); + + const not_null _owner; + + QOpenGLFunctions *_f = nullptr; + Ui::GL::BackgroundFiller _background; + QSize _viewport; + float _factor = 1.; + + QImage _radialCache; + QImage _documentBubbleCache; + QImage _themePreviewCache; + QImage _saveMsgCache; + QImage _footerCache; + QImage _captionCache; + QImage _groupThumbsCache; + +}; + +} // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp new file mode 100644 index 000000000..a8675c9c4 --- /dev/null +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp @@ -0,0 +1,166 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "media/view/media_view_overlay_raster.h" + +#include "media/view/media_view_pip.h" + +namespace Media::View { + +OverlayWidget::RendererSW::RendererSW(not_null owner) +: _owner(owner) +, _transparentBrush(style::TransparentPlaceholder()) { +} + +void OverlayWidget::RendererSW::paintFallback( + Painter &&p, + const QRegion &clip, + Ui::GL::Backend backend) { + _p = &p; + _clip = &clip; + _clipOuter = clip.boundingRect(); + _owner->paint(this); +} + +void OverlayWidget::RendererSW::paintBackground() { + const auto region = _owner->opaqueContentShown() + ? (*_clip - _owner->contentRect()) + : *_clip; + + const auto m = _p->compositionMode(); + _p->setCompositionMode(QPainter::CompositionMode_Source); + const auto &bg = _owner->_fullScreenVideo + ? st::mediaviewVideoBg + : st::mediaviewBg; + for (const auto rect : region) { + _p->fillRect(rect, bg); + } + _p->setCompositionMode(m); +} + +void OverlayWidget::RendererSW::paintTransformedVideoFrame( + QRect rect, + int rotation) { + Expects(_owner->_streamed != nullptr); + + if (!rect.intersects(_clipOuter)) { + return; + } + paintTransformedImage(_owner->videoFrame(), rect, rotation); +} + +void OverlayWidget::RendererSW::paintTransformedStaticContent( + const QImage &image, + QRect rect, + int rotation, + bool fillTransparentBackground) { + if (!rect.intersects(_clipOuter)) { + return; + } + + if (fillTransparentBackground) { + _p->fillRect(rect, _transparentBrush); + } + if (image.isNull()) { + return; + } + paintTransformedImage(image, rect, rotation); +} + +void OverlayWidget::RendererSW::paintTransformedImage( + const QImage &image, + QRect rect, + int rotation) { + PainterHighQualityEnabler hq(*_p); + if (UsePainterRotation(rotation, false)) { + if (rotation) { + _p->save(); + _p->rotate(rotation); + } + _p->drawImage(RotatedRect(rect, rotation), image); + if (rotation) { + _p->restore(); + } + } else { + _p->drawImage(rect, _owner->transformShownContent(image, rotation)); + } +} + +void OverlayWidget::RendererSW::paintRadialLoading( + QRect inner, + bool radial, + float64 radialOpacity) { + _owner->paintRadialLoadingContent(*_p, inner, radial, radialOpacity); +} + +void OverlayWidget::RendererSW::paintThemePreview(QRect outer) { + _owner->paintThemePreviewContent(*_p, outer, _clipOuter); +} + +void OverlayWidget::RendererSW::paintDocumentBubble( + QRect outer, + QRect icon) { + if (outer.intersects(_clipOuter)) { + _owner->paintDocumentBubbleContent(*_p, outer, icon, _clipOuter); + if (icon.intersects(_clipOuter)) { + _owner->paintRadialLoading(this); + } + } +} + +void OverlayWidget::RendererSW::paintSaveMsg(QRect outer) { + if (outer.intersects(_clipOuter)) { + _owner->paintSaveMsgContent(*_p, outer, _clipOuter); + } +} + +void OverlayWidget::RendererSW::paintControl( + OverState control, + QRect outer, + float64 outerOpacity, + QRect inner, + float64 innerOpacity, + const style::icon &icon) { + if (!outer.isEmpty() && !outer.intersects(_clipOuter)) { + return; + } + if (!outer.isEmpty() && outerOpacity > 0) { + _p->setOpacity(outerOpacity); + for (const auto &rect : *_clip) { + const auto fill = outer.intersected(rect); + if (!fill.isEmpty()) { + _p->fillRect(fill, st::mediaviewControlBg); + } + } + } + if (inner.intersects(_clipOuter)) { + _p->setOpacity(innerOpacity); + icon.paintInCenter(*_p, inner); + } +} + +void OverlayWidget::RendererSW::paintFooter(QRect outer, float64 opacity) { + if (outer.intersects(_clipOuter)) { + _owner->paintFooterContent(*_p, outer, _clipOuter, opacity); + } +} + +void OverlayWidget::RendererSW::paintCaption(QRect outer, float64 opacity) { + if (outer.intersects(_clipOuter)) { + _owner->paintCaptionContent(*_p, outer, _clipOuter, opacity); + } +} + +void OverlayWidget::RendererSW::paintGroupThumbs( + QRect outer, + float64 opacity) { + if (outer.intersects(_clipOuter)) { + _owner->paintGroupThumbsContent(*_p, outer, _clipOuter, opacity); + } +} + +} // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h new file mode 100644 index 000000000..052b776d2 --- /dev/null +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h @@ -0,0 +1,63 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "media/view/media_view_overlay_renderer.h" +#include "ui/gl/gl_surface.h" + +namespace Media::View { + +class OverlayWidget::RendererSW final : public OverlayWidget::Renderer { +public: + RendererSW(not_null owner); + + void paintFallback( + Painter &&p, + const QRegion &clip, + Ui::GL::Backend backend) override; + +private: + void paintBackground() override; + void paintTransformedVideoFrame(QRect rect, int rotation) override; + void paintTransformedStaticContent( + const QImage &image, + QRect rect, + int rotation, + bool fillTransparentBackground) override; + void paintTransformedImage( + const QImage &image, + QRect rect, + int rotation); + void paintRadialLoading( + QRect inner, + bool radial, + float64 radialOpacity) override; + void paintThemePreview(QRect outer) override; + void paintDocumentBubble(QRect outer, QRect icon) override; + void paintSaveMsg(QRect outer) override; + void paintControl( + OverState control, + QRect outer, + float64 outerOpacity, + QRect inner, + float64 innerOpacity, + const style::icon &icon) override; + void paintFooter(QRect outer, float64 opacity) override; + void paintCaption(QRect outer, float64 opacity) override; + void paintGroupThumbs(QRect outer, float64 opacity) override; + + const not_null _owner; + QBrush _transparentBrush; + + Painter *_p = nullptr; + const QRegion *_clip = nullptr; + QRect _clipOuter; + +}; + +} // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h new file mode 100644 index 000000000..189e65445 --- /dev/null +++ b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h @@ -0,0 +1,43 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "media/view/media_view_overlay_widget.h" + +namespace Media::View { + +class OverlayWidget::Renderer : public Ui::GL::Renderer { +public: + virtual void paintBackground() = 0; + virtual void paintTransformedVideoFrame(QRect rect, int rotation) = 0; + virtual void paintTransformedStaticContent( + const QImage &image, + QRect rect, + int rotation, + bool fillTransparentBackground) = 0; + virtual void paintRadialLoading( + QRect inner, + bool radial, + float64 radialOpacity) = 0; + virtual void paintThemePreview(QRect outer) = 0; + virtual void paintDocumentBubble(QRect outer, QRect icon) = 0; + virtual void paintSaveMsg(QRect outer) = 0; + virtual void paintControl( + OverState control, + QRect outer, + float64 outerOpacity, + QRect inner, + float64 innerOpacity, + const style::icon &icon) = 0; + virtual void paintFooter(QRect outer, float64 opacity) = 0; + virtual void paintCaption(QRect outer, float64 opacity) = 0; + virtual void paintGroupThumbs(QRect outer, float64 opacity) = 0; + +}; + +} // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 794507b75..e1a18991d 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -32,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_controls.h" #include "media/view/media_view_group_thumbs.h" #include "media/view/media_view_pip.h" +#include "media/view/media_view_overlay_raster.h" +#include "media/view/media_view_overlay_opengl.h" #include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_player.h" #include "media/player/media_player_instance.h" @@ -144,7 +146,7 @@ Images::Options VideoThumbOptions(DocumentData *document) { : result; } -QPixmap PrepareStaticImage(QImage image) { +QImage PrepareStaticImage(QImage image) { if (image.width() > kMaxDisplayImageSize || image.height() > kMaxDisplayImageSize) { image = image.scaled( @@ -153,14 +155,14 @@ QPixmap PrepareStaticImage(QImage image) { Qt::KeepAspectRatio, Qt::SmoothTransformation); } - return App::pixmapFromImageInPlace(std::move(image)); + return image; } -QPixmap PrepareStaticImage(const QString &path) { +QImage PrepareStaticImage(const QString &path) { return PrepareStaticImage(App::readImage(path, nullptr, false)); } -QPixmap PrepareStaticImage(const QByteArray &bytes) { +QImage PrepareStaticImage(const QByteArray &bytes) { return PrepareStaticImage(App::readImage(bytes, nullptr, false)); } @@ -206,8 +208,6 @@ struct OverlayWidget::Streamed { Streaming::Instance instance; PlaybackControls controls; - QImage frameForDirectPaint; - bool withSound = false; bool pausedBySeek = false; bool resumeOnCallEnd = false; @@ -273,7 +273,6 @@ OverlayWidget::OverlayWidget() return chooseRenderer(capabilities); })) , _widget(_surface->rpWidget()) -, _transparentBrush(style::transparentPlaceholderBrush()) , _docDownload(_widget, tr::lng_media_download(tr::now), st::mediaviewFileLink) , _docSaveAs(_widget, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink) , _docCancel(_widget, tr::lng_cancel(tr::now), st::mediaviewFileLink) @@ -301,6 +300,14 @@ OverlayWidget::OverlayWidget() Ui::Text::WithEntities); _saveMsgText.setMarkedText(st::mediaviewSaveMsgStyle, text, Ui::DialogTextOptions()); _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::mediaviewSaveMsgPadding.left() + st::mediaviewSaveMsgPadding.right(), st::mediaviewSaveMsgStyle.font->height + st::mediaviewSaveMsgPadding.top() + st::mediaviewSaveMsgPadding.bottom()); + _saveMsgImage = QImage( + _saveMsg.size() * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + + _docRectImage = QImage( + st::mediaviewFileSize * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + _docRectImage.setDevicePixelRatio(cIntRetinaFactor()); _surface->shownValue( ) | rpl::start_with_next([=](bool shown) { @@ -548,42 +555,6 @@ QImage OverlayWidget::videoFrame() const { : _streamed->instance.info().video.cover; } -QImage OverlayWidget::videoFrameForDirectPaint() const { - Expects(_streamed != nullptr); - - const auto result = videoFrame(); - if (!_opengl) { - return result; - } - const auto bytesPerLine = result.bytesPerLine(); - if (bytesPerLine == result.width() * 4) { - return result; - } - - // The OpenGL painter can't paint textures where byte data is with strides. - // So in that case we prepare a compact copy of the frame to render. - // - // See Qt commit ed557c037847e343caa010562952b398f806adcd - // - auto &cache = _streamed->frameForDirectPaint; - if (cache.size() != result.size()) { - cache = QImage(result.size(), result.format()); - } - const auto height = result.height(); - const auto line = cache.bytesPerLine(); - Assert(line == result.width() * 4); - Assert(line < bytesPerLine); - - auto from = result.bits(); - auto to = cache.bits(); - for (auto y = 0; y != height; ++y) { - memcpy(to, from, line); - to += line; - from += bytesPerLine; - } - return cache; -} - bool OverlayWidget::documentContentShown() const { return _document && (!_staticContent.isNull() || videoShown()); } @@ -596,6 +567,16 @@ bool OverlayWidget::documentBubbleShown() const { && _staticContent.isNull()); } +bool OverlayWidget::contentShown() const { + return _photo || documentContentShown(); +} + +bool OverlayWidget::opaqueContentShown() const { + return contentShown() + && (!_document + || (!_document->isVideoMessage() && !_document->sticker())); +} + void OverlayWidget::clearStreaming(bool savePosition) { if (_streamed && _document && savePosition) { Media::Player::SaveLastPlaybackPosition( @@ -1658,9 +1639,7 @@ void OverlayWidget::showMediaOverview() { void OverlayWidget::copyMedia() { _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); if (_document) { - QGuiApplication::clipboard()->setImage(videoShown() - ? transformVideoFrame(videoFrame()) - : transformStaticContent(_staticContent)); + QGuiApplication::clipboard()->setImage(transformedShownContent()); } else if (_photo && _photoMedia->loaded()) { const auto image = _photoMedia->image( Data::PhotoSize::Large)->original(); @@ -2247,7 +2226,7 @@ void OverlayWidget::displayPhoto(not_null photo, HistoryItem *item) refreshMediaViewer(); - _staticContent = QPixmap(); + _staticContent = QImage(); if (_photo->videoCanBePlayed()) { initStreaming(); } @@ -2302,7 +2281,7 @@ void OverlayWidget::displayDocument( const Data::CloudTheme &cloud, bool continueStreaming) { _fullScreenVideo = false; - _staticContent = QPixmap(); + _staticContent = QImage(); clearStreaming(_document != doc); destroyThemePreview(); assignMediaPointer(doc); @@ -2317,11 +2296,12 @@ void OverlayWidget::displayDocument( if (_document) { if (_document->sticker()) { if (const auto image = _documentMedia->getStickerLarge()) { - _staticContent = image->pix(); + _staticContent = image->original(); } else if (const auto thumbnail = _documentMedia->thumbnail()) { _staticContent = thumbnail->pixBlurred( _document->dimensions.width(), - _document->dimensions.height()); + _document->dimensions.height() + ).toImage(); } } else { if (_documentMedia->canBePlayed() @@ -2594,7 +2574,8 @@ void OverlayWidget::initStreamingThumbnail() { h, good ? goodOptions : options, w / cIntRetinaFactor(), - h / cIntRetinaFactor()); + h / cIntRetinaFactor() + ).toImage(); _staticContent.setDevicePixelRatio(cRetinaFactor()); } @@ -2654,27 +2635,28 @@ bool OverlayWidget::createStreamingObjects() { return true; } -QImage OverlayWidget::transformVideoFrame(QImage frame) const { - Expects(videoShown()); - - const auto rotation = contentRotation(); - if (rotation != 0) { - frame = RotateFrameImage(std::move(frame), rotation); - } - const auto requiredSize = videoSize(); - if (frame.size() != requiredSize) { - frame = frame.scaled( - requiredSize, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - } - return frame; +QImage OverlayWidget::transformedShownContent() const { + return transformShownContent( + videoShown() ? videoFrame() : _staticContent, + contentRotation()); } -QImage OverlayWidget::transformStaticContent(QPixmap content) const { - return _rotation - ? RotateFrameImage(content.toImage(), _rotation) - : content.toImage(); +QImage OverlayWidget::transformShownContent( + QImage content, + int rotation) const { + if (rotation) { + content = RotateFrameImage(std::move(content), rotation); + } + if (videoShown()) { + const auto requiredSize = videoSize(); + if (content.size() != requiredSize) { + content = content.scaled( + requiredSize, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } + } + return content; } void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) { @@ -2909,7 +2891,7 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { if (videoShown()) { _streamed->instance.saveFrameToCover(); const auto saved = base::take(_rotation); - _staticContent = Images::PixmapFast(transformVideoFrame(videoFrame())); + _staticContent = transformedShownContent(); _rotation = saved; update(contentRect()); } @@ -3106,12 +3088,16 @@ void OverlayWidget::validatePhotoImage(Image *image, bool blurred) { use.width(), use.height(), Images::Option::Smooth - | (blurred ? Images::Option::Blurred : Images::Option(0))); + | (blurred ? Images::Option::Blurred : Images::Option(0)) + ).toImage(); _staticContent.setDevicePixelRatio(cRetinaFactor()); _blurred = blurred; } void OverlayWidget::validatePhotoCurrentImage() { + if (!_photo) { + return; + } validatePhotoImage(_photoMedia->image(Data::PhotoSize::Large), false); validatePhotoImage(_photoMedia->image(Data::PhotoSize::Thumbnail), true); validatePhotoImage(_photoMedia->image(Data::PhotoSize::Small), true); @@ -3131,283 +3117,72 @@ void OverlayWidget::validatePhotoCurrentImage() { Ui::GL::ChosenRenderer OverlayWidget::chooseRenderer( Ui::GL::Capabilities capabilities) { - class Renderer : public Ui::GL::Renderer { - public: - Renderer(not_null owner) : _owner(owner) { - } - - void paintFallback( - Painter &&p, - const QRegion &clip, - Ui::GL::Backend backend) override { - _owner->paint(p, clip); - } - - private: - const not_null _owner; - - }; - const auto use = Platform::IsMac() ? true : capabilities.transparency; LOG(("OpenGL: %1 (OverlayWidget)").arg(Logs::b(use))); - _opengl = use; + if (use && false) { + auto renderer = std::make_unique(this); + _opengl = true; + return { + .renderer = std::move(renderer), + .backend = Ui::GL::Backend::OpenGL, + }; + } return { - .renderer = std::make_unique(this), - .backend = (use ? Ui::GL::Backend::OpenGL : Ui::GL::Backend::Raster), + .renderer = std::make_unique(this), + .backend = Ui::GL::Backend::Raster, }; } -void OverlayWidget::paint(Painter &p, const QRegion &clip) { - if (_hideWorkaround && Platform::IsWindows()) { - // This glitches on macOS, it shows old content while animating hide. - p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(_widget->rect(), st::transparent); - return; - } - const auto r = clip.boundingRect(); - const auto contentShown = _photo || documentContentShown(); - const auto opaqueContentShown = contentShown - && (!_document - || (!_document->isVideoMessage() && !_document->sticker())); - const auto bgRegion = opaqueContentShown - ? (clip - contentRect()) - : clip; - - // main bg - const auto m = p.compositionMode(); - p.setCompositionMode(QPainter::CompositionMode_Source); - const auto bgColor = _fullScreenVideo ? st::mediaviewVideoBg : st::mediaviewBg; - for (const auto rect : bgRegion) { - p.fillRect(rect, bgColor); - } - p.setCompositionMode(m); - - // photo - if (_photo) { - validatePhotoCurrentImage(); - } - p.setOpacity(1); - if (contentShown) { +void OverlayWidget::paint(not_null renderer) { + renderer->paintBackground(); + if (contentShown()) { const auto rect = contentRect(); - if (rect.intersects(r)) { - if (videoShown()) { - paintTransformedVideoFrame(p); - } else { - paintTransformedStaticContent(p); - } - - const auto radial = _radial.animating(); - const auto radialOpacity = radial ? _radial.opacity() : 0.; - paintRadialLoading(p, radial, radialOpacity); - } - if (_saveMsgStarted && _saveMsg.intersects(r)) { - float64 dt = float64(crl::now()) - _saveMsgStarted; - float64 hidingDt = dt - st::mediaviewSaveMsgShowing - st::mediaviewSaveMsgShown; - if (dt < st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + st::mediaviewSaveMsgHiding) { - if (hidingDt >= 0 && _saveMsgOpacity.to() > 0.5) { - _saveMsgOpacity.start(0); - } - float64 progress = (hidingDt >= 0) ? (hidingDt / st::mediaviewSaveMsgHiding) : (dt / st::mediaviewSaveMsgShowing); - _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); - if (_saveMsgOpacity.current() > 0) { - p.setOpacity(_saveMsgOpacity.current()); - Ui::FillRoundRect(p, _saveMsg, st::mediaviewSaveMsgBg, Ui::MediaviewSaveCorners); - st::mediaviewSaveMsgCheck.paint(p, _saveMsg.topLeft() + st::mediaviewSaveMsgCheckPos, width()); - - p.setPen(st::mediaviewSaveMsgFg); - p.setTextPalette(st::mediaviewTextPalette); - _saveMsgText.draw(p, _saveMsg.x() + st::mediaviewSaveMsgPadding.left(), _saveMsg.y() + st::mediaviewSaveMsgPadding.top(), _saveMsg.width() - st::mediaviewSaveMsgPadding.left() - st::mediaviewSaveMsgPadding.right()); - p.restoreTextPalette(); - p.setOpacity(1); - } - if (!_blurred) { - auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); - _saveMsgUpdater.callOnce(nextFrame); - } - } else { - _saveMsgStarted = 0; + const auto rotation = contentRotation(); + if (videoShown()) { + renderer->paintTransformedVideoFrame(rect, rotation); + if (_streamed->instance.player().ready()) { + _streamed->instance.markFrameShown(); } + } else { + validatePhotoCurrentImage(); + const auto fillTransparentBackground = (!_document + || (!_document->sticker() && !_document->isVideoMessage())) + && (_staticContent.isNull() + || _staticContent.hasAlphaChannel()); + renderer->paintTransformedStaticContent( + _staticContent, + rect, + rotation, + fillTransparentBackground); } + paintRadialLoading(renderer); } else if (_themePreviewShown) { - paintThemePreview(p, r); + renderer->paintThemePreview(_themePreviewRect); } else if (documentBubbleShown()) { - if (_docRect.intersects(r)) { - p.fillRect(_docRect, st::mediaviewFileBg); - if (_docIconRect.intersects(r)) { - const auto radial = _radial.animating(); - const auto radialOpacity = radial ? _radial.opacity() : 0.; - if (!_document || !_document->hasThumbnail()) { - p.fillRect(_docIconRect, _docIconColor); - if ((!_document || _documentMedia->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { - _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); - p.setPen(st::mediaviewFileExtFg); - p.setFont(st::mediaviewFileExtFont); - if (!_docExt.isEmpty()) { - p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mediaviewFileExtTop + st::mediaviewFileExtFont->ascent, _docExt); - } - } - } else if (const auto thumbnail = _documentMedia->thumbnail()) { - int32 rf(cIntRetinaFactor()); - p.drawPixmap(_docIconRect.topLeft(), thumbnail->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); - } - - paintRadialLoading(p, radial, radialOpacity); - } - - if (!_docIconRect.contains(r)) { - p.setPen(st::mediaviewFileNameFg); - p.setFont(st::mediaviewFileNameFont); - p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileNameTop, width(), _docName, _docNameWidth); - - p.setPen(st::mediaviewFileSizeFg); - p.setFont(st::mediaviewFont); - p.drawTextLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileSizeTop, width(), _docSize, _docSizeWidth); - } - } + renderer->paintDocumentBubble(_docRect, _docIconRect); + } + updateSaveMsgState(); + if (_saveMsgStarted && _saveMsgOpacity.current() > 0.) { + renderer->paintSaveMsg(_saveMsg); } - float64 co = _fullScreenVideo ? 0. : _controlsOpacity.current(); - if (co > 0) { - // left nav bar - if (_leftNav.intersects(r) && _leftNavVisible) { - auto o = overLevel(OverLeftNav); - if (o > 0) { - p.setOpacity(o * co); - for (const auto &rect : clip) { - const auto fill = _leftNav.intersected(rect); - if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); - } - } - if (_leftNavIcon.intersects(r)) { - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewLeft.paintInCenter(p, _leftNavIcon); - } - } - - // right nav bar - if (_rightNav.intersects(r) && _rightNavVisible) { - auto o = overLevel(OverRightNav); - if (o > 0) { - p.setOpacity(o * co); - for (const auto &rect : clip) { - const auto fill = _rightNav.intersected(rect); - if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); - } - } - if (_rightNavIcon.intersects(r)) { - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewRight.paintInCenter(p, _rightNavIcon); - } - } - - // close button - if (_closeNav.intersects(r)) { - auto o = overLevel(OverClose); - if (o > 0) { - p.setOpacity(o * co); - for (const auto &rect : clip) { - const auto fill = _closeNav.intersected(rect); - if (!fill.isEmpty()) p.fillRect(fill, st::mediaviewControlBg); - } - } - if (_closeNavIcon.intersects(r)) { - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewClose.paintInCenter(p, _closeNavIcon); - } - } - - // save button - if (_saveVisible && _saveNavIcon.intersects(r)) { - auto o = overLevel(OverSave); - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewSave.paintInCenter(p, _saveNavIcon); - } - - // rotate button - if (_rotateVisible && _rotateNavIcon.intersects(r)) { - auto o = overLevel(OverRotate); - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewRotate.paintInCenter(p, _rotateNavIcon); - } - - // more area - if (_moreNavIcon.intersects(r)) { - auto o = overLevel(OverMore); - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - st::mediaviewMore.paintInCenter(p, _moreNavIcon); - } - - p.setPen(st::mediaviewControlFg); - p.setFont(st::mediaviewThickFont); - - // header - if (_headerNav.intersects(r)) { - auto o = _headerHasLink ? overLevel(OverHeader) : 0; - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - p.drawText(_headerNav.left(), _headerNav.top() + st::mediaviewThickFont->ascent, _headerText); - - if (o > 0) { - p.setOpacity(o * co); - p.drawLine(_headerNav.left(), _headerNav.top() + st::mediaviewThickFont->ascent + 1, _headerNav.right(), _headerNav.top() + st::mediaviewThickFont->ascent + 1); - } - } - - p.setFont(st::mediaviewFont); - - // name - if (_nameNav.isValid() && _nameNav.intersects(r)) { - float64 o = _from ? overLevel(OverName) : 0.; - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - _fromNameLabel.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width()); - - if (o > 0) { - p.setOpacity(o * co); - p.drawLine(_nameNav.left(), _nameNav.top() + st::mediaviewFont->ascent + 1, _nameNav.right(), _nameNav.top() + st::mediaviewFont->ascent + 1); - } - } - - // date - if (_dateNav.intersects(r)) { - float64 o = overLevel(OverDate); - p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * co); - p.drawText(_dateNav.left(), _dateNav.top() + st::mediaviewFont->ascent, _dateText); - - if (o > 0) { - p.setOpacity(o * co); - p.drawLine(_dateNav.left(), _dateNav.top() + st::mediaviewFont->ascent + 1, _dateNav.right(), _dateNav.top() + st::mediaviewFont->ascent + 1); - } - } - - // caption + const auto opacity = _fullScreenVideo ? 0. : _controlsOpacity.current(); + if (opacity > 0) { + paintControls(renderer, opacity); + renderer->paintFooter(footerGeometry(), opacity); if (!_caption.isEmpty()) { - QRect outer(_captionRect.marginsAdded(st::mediaviewCaptionPadding)); - if (outer.intersects(r)) { - p.setOpacity(co); - p.setBrush(st::mediaviewCaptionBg); - p.setPen(Qt::NoPen); - p.drawRoundedRect(outer, st::mediaviewCaptionRadius, st::mediaviewCaptionRadius); - if (_captionRect.intersects(r)) { - p.setTextPalette(st::mediaviewTextPalette); - p.setPen(st::mediaviewCaptionFg); - _caption.drawElided(p, _captionRect.x(), _captionRect.y(), _captionRect.width(), _captionRect.height() / st::mediaviewCaptionStyle.font->height); - p.restoreTextPalette(); - } - } + renderer->paintCaption(captionGeometry(), opacity); } - - if (_groupThumbs && _groupThumbsRect.intersects(r)) { - p.setOpacity(co); - _groupThumbs->paint( - p, - _groupThumbsLeft, - _groupThumbsTop, - width()); - if (_groupThumbs->hidden()) { - _groupThumbs = nullptr; - _groupThumbsRect = QRect(); - } + if (_groupThumbs) { + renderer->paintGroupThumbs( + QRect( + _groupThumbsLeft, + _groupThumbsTop, + width() - 2 * _groupThumbsLeft, + _groupThumbs->height()), + opacity); } } checkGroupThumbsAnimation(); @@ -3420,63 +3195,8 @@ void OverlayWidget::checkGroupThumbsAnimation() { } } -void OverlayWidget::paintTransformedVideoFrame(Painter &p) { - Expects(_streamed != nullptr); - - const auto rect = contentRect(); - const auto image = videoFrameForDirectPaint(); - - PainterHighQualityEnabler hq(p); - - const auto rotation = contentRotation(); - if (UsePainterRotation(rotation, _opengl)) { - if (rotation) { - p.save(); - p.rotate(rotation); - } - p.drawImage(RotatedRect(rect, rotation), image); - if (rotation) { - p.restore(); - } - } else { - p.drawImage(rect, transformVideoFrame(image)); - } - if (_streamed->instance.player().ready()) { - _streamed->instance.markFrameShown(); - } -} - -void OverlayWidget::paintTransformedStaticContent(Painter &p) { - const auto rect = contentRect(); - - PainterHighQualityEnabler hq(p); - if ((!_document - || (!_document->sticker() && !_document->isVideoMessage())) - && (_staticContent.isNull() || _staticContent.hasAlpha())) { - p.fillRect(rect, _transparentBrush); - } - if (_staticContent.isNull()) { - return; - } - const auto rotation = contentRotation(); - if (UsePainterRotation(rotation, _opengl)) { - if (rotation) { - p.save(); - p.rotate(rotation); - } - p.drawPixmap(RotatedRect(rect, rotation), _staticContent); - if (rotation) { - p.restore(); - } - } else { - p.drawImage(rect, transformStaticContent(_staticContent)); - } -} - -void OverlayWidget::paintRadialLoading( - Painter &p, - bool radial, - float64 radialOpacity) { +void OverlayWidget::paintRadialLoading(not_null renderer) { + const auto radial = _radial.animating(); if (_streamed) { if (!_streamed->instance.waitingShown()) { return; @@ -3485,26 +3205,11 @@ void OverlayWidget::paintRadialLoading( return; } + const auto radialOpacity = radial ? _radial.opacity() : 0.; const auto inner = radialRect(); Assert(!inner.isEmpty()); - if (_opengl) { - if (_radialCache.size() != inner.size() * cIntRetinaFactor()) { - _radialCache = QImage( - inner.size() * cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied); - _radialCache.setDevicePixelRatio(cRetinaFactor()); - } - _radialCache.fill(Qt::transparent); - { - Painter q(&_radialCache); - const auto moved = inner.translated(-inner.topLeft()); - paintRadialLoadingContent(q, moved, radial, radialOpacity); - } - p.drawImage(inner.topLeft(), _radialCache); - } else { - paintRadialLoadingContent(p, inner, radial, radialOpacity); - } + renderer->paintRadialLoading(inner, radial, radialOpacity); } void OverlayWidget::paintRadialLoadingContent( @@ -3566,19 +3271,22 @@ void OverlayWidget::paintRadialLoadingContent( } } -void OverlayWidget::paintThemePreview(Painter &p, QRect clip) { - auto fill = _themePreviewRect.intersected(clip); +void OverlayWidget::paintThemePreviewContent( + Painter &p, + QRect outer, + QRect clip) { + const auto fill = outer.intersected(clip); if (!fill.isEmpty()) { if (_themePreview) { p.drawImage( - _themePreviewRect.topLeft(), + outer.topLeft(), _themePreview->preview); } else { p.fillRect(fill, st::themePreviewBg); p.setFont(st::themePreviewLoadingFont); p.setPen(st::themePreviewLoadingFg); p.drawText( - _themePreviewRect, + outer, (_themePreviewId ? tr::lng_theme_preview_generating(tr::now) : tr::lng_theme_preview_invalid(tr::now)), @@ -3586,44 +3294,310 @@ void OverlayWidget::paintThemePreview(Painter &p, QRect clip) { } } - auto fillOverlay = [&](QRect fill) { - auto clipped = fill.intersected(clip); + const auto fillOverlay = [&](QRect fill) { + const auto clipped = fill.intersected(clip); if (!clipped.isEmpty()) { p.setOpacity(st::themePreviewOverlayOpacity); p.fillRect(clipped, st::themePreviewBg); p.setOpacity(1.); } }; - auto titleRect = QRect(_themePreviewRect.x(), _themePreviewRect.y(), _themePreviewRect.width(), st::themePreviewMargin.top()); + auto titleRect = QRect( + outer.x(), + outer.y(), + outer.width(), + st::themePreviewMargin.top()); if (titleRect.x() < 0) { - titleRect = QRect(0, _themePreviewRect.y(), width(), st::themePreviewMargin.top()); + titleRect = QRect( + 0, + outer.y(), + width(), + st::themePreviewMargin.top()); } - if (auto fillTitleRect = (titleRect.y() < 0)) { + if (const auto fillTitleRect = (titleRect.y() < 0)) { titleRect.moveTop(0); fillOverlay(titleRect); } - titleRect = titleRect.marginsRemoved(QMargins(st::themePreviewMargin.left(), st::themePreviewTitleTop, st::themePreviewMargin.right(), titleRect.height() - st::themePreviewTitleTop - st::themePreviewTitleFont->height)); + titleRect = titleRect.marginsRemoved(QMargins( + st::themePreviewMargin.left(), + st::themePreviewTitleTop, + st::themePreviewMargin.right(), + (titleRect.height() + - st::themePreviewTitleTop + - st::themePreviewTitleFont->height))); if (titleRect.intersects(clip)) { p.setFont(st::themePreviewTitleFont); p.setPen(st::themePreviewTitleFg); const auto title = _themeCloudData.title.isEmpty() ? tr::lng_theme_preview_title(tr::now) : _themeCloudData.title; - const auto elided = st::themePreviewTitleFont->elided(title, titleRect.width()); + const auto elided = st::themePreviewTitleFont->elided( + title, + titleRect.width()); p.drawTextLeft(titleRect.x(), titleRect.y(), width(), elided); } - auto buttonsRect = QRect(_themePreviewRect.x(), _themePreviewRect.y() + _themePreviewRect.height() - st::themePreviewMargin.bottom(), _themePreviewRect.width(), st::themePreviewMargin.bottom()); - if (auto fillButtonsRect = (buttonsRect.y() + buttonsRect.height() > height())) { + auto buttonsRect = QRect( + outer.x(), + outer.y() + outer.height() - st::themePreviewMargin.bottom(), + outer.width(), + st::themePreviewMargin.bottom()); + if (const auto fillButtonsRect + = (buttonsRect.y() + buttonsRect.height() > height())) { buttonsRect.moveTop(height() - buttonsRect.height()); fillOverlay(buttonsRect); } if (_themeShare && _themeCloudData.usersCount > 0) { p.setFont(st::boxTextFont); p.setPen(st::windowSubTextFg); - const auto left = _themeShare->x() + _themeShare->width() - (st::themePreviewCancelButton.width / 2); - const auto baseline = _themeShare->y() + st::themePreviewCancelButton.padding.top() + +st::themePreviewCancelButton.textTop + st::themePreviewCancelButton.font->ascent; - p.drawText(left, baseline, tr::lng_theme_preview_users(tr::now, lt_count, _themeCloudData.usersCount)); + const auto left = outer.x() + + (_themeShare->x() - _themePreviewRect.x()) + + _themeShare->width() + - (st::themePreviewCancelButton.width / 2); + const auto baseline = outer.y() + + (_themeShare->y() - _themePreviewRect.y()) + + st::themePreviewCancelButton.padding.top() + + st::themePreviewCancelButton.textTop + + st::themePreviewCancelButton.font->ascent; + p.drawText( + left, + baseline, + tr::lng_theme_preview_users( + tr::now, + lt_count, + _themeCloudData.usersCount)); + } +} + +void OverlayWidget::paintDocumentBubbleContent( + Painter &p, + QRect outer, + QRect icon, + QRect clip) const { + p.fillRect(outer, st::mediaviewFileBg); + if (icon.intersects(clip)) { + if (!_document || !_document->hasThumbnail()) { + p.fillRect(icon, _docIconColor); + const auto radial = _radial.animating(); + const auto radialOpacity = radial ? _radial.opacity() : 0.; + if ((!_document || _documentMedia->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { + _docIcon->paint(p, icon.x() + (icon.width() - _docIcon->width()), icon.y(), width()); + p.setPen(st::mediaviewFileExtFg); + p.setFont(st::mediaviewFileExtFont); + if (!_docExt.isEmpty()) { + p.drawText(icon.x() + (icon.width() - _docExtWidth) / 2, icon.y() + st::mediaviewFileExtTop + st::mediaviewFileExtFont->ascent, _docExt); + } + } + } else if (const auto thumbnail = _documentMedia->thumbnail()) { + int32 rf(cIntRetinaFactor()); + p.drawPixmap(icon.topLeft(), thumbnail->pix(_docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); + } + } + if (!icon.contains(clip)) { + p.setPen(st::mediaviewFileNameFg); + p.setFont(st::mediaviewFileNameFont); + p.drawTextLeft(outer.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, outer.y() + st::mediaviewFilePadding + st::mediaviewFileNameTop, width(), _docName, _docNameWidth); + + p.setPen(st::mediaviewFileSizeFg); + p.setFont(st::mediaviewFont); + p.drawTextLeft(outer.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, outer.y() + st::mediaviewFilePadding + st::mediaviewFileSizeTop, width(), _docSize, _docSizeWidth); + } +} + +void OverlayWidget::paintSaveMsgContent( + Painter &p, + QRect outer, + QRect clip) { + p.setOpacity(_saveMsgOpacity.current()); + Ui::FillRoundRect(p, outer, st::mediaviewSaveMsgBg, Ui::MediaviewSaveCorners); + st::mediaviewSaveMsgCheck.paint(p, outer.topLeft() + st::mediaviewSaveMsgCheckPos, width()); + + p.setPen(st::mediaviewSaveMsgFg); + p.setTextPalette(st::mediaviewTextPalette); + _saveMsgText.draw(p, outer.x() + st::mediaviewSaveMsgPadding.left(), outer.y() + st::mediaviewSaveMsgPadding.top(), outer.width() - st::mediaviewSaveMsgPadding.left() - st::mediaviewSaveMsgPadding.right()); + p.restoreTextPalette(); + p.setOpacity(1); +} + +void OverlayWidget::paintControls( + not_null renderer, + float64 opacity) { + struct Control { + OverState state = OverNone; + bool visible = false; + const QRect &outer; + const QRect &inner; + const style::icon &icon; + }; + const QRect kEmpty; + const Control controls[] = { + { + OverLeftNav, + _leftNavVisible, + _leftNav, + _leftNavIcon, + st::mediaviewLeft }, + { + OverRightNav, + _rightNavVisible, + _rightNav, + _rightNavIcon, + st::mediaviewRight }, + { + OverClose, + true, + _closeNav, + _closeNavIcon, + st::mediaviewClose }, + { + OverSave, + _saveVisible, + kEmpty, + _saveNavIcon, + st::mediaviewSave }, + { + OverRotate, + _rotateVisible, + kEmpty, + _rotateNavIcon, + st::mediaviewRotate }, + { + OverMore, + true, + kEmpty, + _moreNavIcon, + st::mediaviewMore }, + }; + + for (const auto &control : controls) { + if (!control.visible) { + continue; + } + const auto bg = overLevel(control.state); + const auto icon = bg * st::mediaviewIconOverOpacity + + (1 - bg) * st::mediaviewIconOpacity; + renderer->paintControl( + control.state, + control.outer, + bg * opacity, + control.inner, + icon * opacity, + control.icon); + } +} + +void OverlayWidget::paintFooterContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity) { + p.setPen(st::mediaviewControlFg); + p.setFont(st::mediaviewThickFont); + + // header + const auto shift = outer.topLeft() - _headerNav.topLeft(); + const auto header = _headerNav.translated(shift); + const auto name = _nameNav.translated(shift); + const auto date = _dateNav.translated(shift); + if (header.intersects(clip)) { + auto o = _headerHasLink ? overLevel(OverHeader) : 0; + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity); + p.drawText(header.left(), header.top() + st::mediaviewThickFont->ascent, _headerText); + + if (o > 0) { + p.setOpacity(o * opacity); + p.drawLine(header.left(), header.top() + st::mediaviewThickFont->ascent + 1, header.right(), header.top() + st::mediaviewThickFont->ascent + 1); + } + } + + p.setFont(st::mediaviewFont); + + // name + if (_nameNav.isValid() && name.intersects(clip)) { + float64 o = _from ? overLevel(OverName) : 0.; + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity); + _fromNameLabel.drawElided(p, name.left(), name.top(), name.width()); + + if (o > 0) { + p.setOpacity(o * opacity); + p.drawLine(name.left(), name.top() + st::mediaviewFont->ascent + 1, name.right(), name.top() + st::mediaviewFont->ascent + 1); + } + } + + // date + if (date.intersects(clip)) { + float64 o = overLevel(OverDate); + p.setOpacity((o * st::mediaviewIconOverOpacity + (1 - o) * st::mediaviewIconOpacity) * opacity); + p.drawText(date.left(), date.top() + st::mediaviewFont->ascent, _dateText); + + if (o > 0) { + p.setOpacity(o * opacity); + p.drawLine(date.left(), date.top() + st::mediaviewFont->ascent + 1, date.right(), date.top() + st::mediaviewFont->ascent + 1); + } + } +} + +QRect OverlayWidget::footerGeometry() const { + return _headerNav.united(_nameNav).united(_dateNav); +} + +void OverlayWidget::paintCaptionContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity) { + const auto inner = outer.marginsRemoved(st::mediaviewCaptionPadding); + p.setOpacity(opacity); + p.setBrush(st::mediaviewCaptionBg); + p.setPen(Qt::NoPen); + p.drawRoundedRect(outer, st::mediaviewCaptionRadius, st::mediaviewCaptionRadius); + if (inner.intersects(clip)) { + p.setTextPalette(st::mediaviewTextPalette); + p.setPen(st::mediaviewCaptionFg); + _caption.drawElided(p, inner.x(), inner.y(), inner.width(), inner.height() / st::mediaviewCaptionStyle.font->height); + p.restoreTextPalette(); + } +} + +QRect OverlayWidget::captionGeometry() const { + return _captionRect.marginsAdded(st::mediaviewCaptionPadding); +} + +void OverlayWidget::paintGroupThumbsContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity) { + p.setOpacity(opacity); + _groupThumbs->paint(p, outer.x(), outer.y(), width()); + if (_groupThumbs->hidden()) { + _groupThumbs = nullptr; + _groupThumbsRect = QRect(); + } +} + +void OverlayWidget::updateSaveMsgState() { + if (!_saveMsgStarted) { + return; + } + float64 dt = float64(crl::now()) - _saveMsgStarted; + float64 hidingDt = dt - st::mediaviewSaveMsgShowing - st::mediaviewSaveMsgShown; + if (dt >= st::mediaviewSaveMsgShowing + + st::mediaviewSaveMsgShown + + st::mediaviewSaveMsgHiding) { + _saveMsgStarted = 0; + return; + } + if (hidingDt >= 0 && _saveMsgOpacity.to() > 0.5) { + _saveMsgOpacity.start(0); + } + float64 progress = (hidingDt >= 0) ? (hidingDt / st::mediaviewSaveMsgHiding) : (dt / st::mediaviewSaveMsgShowing); + _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); + if (!_blurred) { + const auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) + ? int(AnimationTimerDelta) + : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); + _saveMsgUpdater.callOnce(nextFrame); } } @@ -4464,7 +4438,7 @@ void OverlayWidget::clearAfterHide() { clearStreaming(); destroyThemePreview(); _radial.stop(); - _staticContent = QPixmap(); + _staticContent = QImage(); _themePreview = nullptr; _themeApply.destroyDelayed(); _themeCancel.destroyDelayed(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 6ba283a90..f8e7056ec 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "ui/rp_widget.h" +#include "ui/gl/gl_surface.h" #include "ui/widgets/dropdown_menu.h" #include "ui/effects/animations.h" #include "ui/effects/radial_animation.h" @@ -50,8 +51,7 @@ enum class Error; } // namespace Streaming } // namespace Media -namespace Media { -namespace View { +namespace Media::View { class GroupThumbs; class Pip; @@ -111,7 +111,11 @@ public: private: struct Streamed; struct PipWrap; + class Renderer; + class RendererSW; + class RendererGL; + // If changing, see paintControls()! enum OverState { OverNone, OverLeftNav, @@ -147,7 +151,7 @@ private: [[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer( Ui::GL::Capabilities capabilities); - void paint(Painter &p, const QRegion &clip); + void paint(not_null renderer); void handleMousePress(QPoint position, Qt::MouseButton button); void handleMouseRelease(QPoint position, Qt::MouseButton button); @@ -342,13 +346,39 @@ private: void zoomReset(); void zoomUpdate(int32 &newZoom); - void paintRadialLoading(Painter &p, bool radial, float64 radialOpacity); + void paintRadialLoading(not_null renderer); void paintRadialLoadingContent( Painter &p, QRect inner, bool radial, float64 radialOpacity) const; - void paintThemePreview(Painter &p, QRect clip); + void paintThemePreviewContent(Painter &p, QRect outer, QRect clip); + void paintDocumentBubbleContent( + Painter &p, + QRect outer, + QRect icon, + QRect clip) const; + void paintSaveMsgContent(Painter &p, QRect outer, QRect clip); + void paintControls(not_null renderer, float64 opacity); + void paintFooterContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity); + [[nodiscard]] QRect footerGeometry() const; + void paintCaptionContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity); + [[nodiscard]] QRect captionGeometry() const; + void paintGroupThumbsContent( + Painter &p, + QRect outer, + QRect clip, + float64 opacity); + + void updateSaveMsgState(); void updateOverRect(OverState state); bool updateOverState(OverState newState); @@ -367,13 +397,14 @@ private: [[nodiscard]] QSize videoSize() const; [[nodiscard]] bool videoIsGifOrUserpic() const; [[nodiscard]] QImage videoFrame() const; - [[nodiscard]] QImage videoFrameForDirectPaint() const; - [[nodiscard]] QImage transformVideoFrame(QImage frame) const; - [[nodiscard]] QImage transformStaticContent(QPixmap content) const; + [[nodiscard]] QImage transformedShownContent() const; + [[nodiscard]] QImage transformShownContent( + QImage content, + int rotation) const; [[nodiscard]] bool documentContentShown() const; [[nodiscard]] bool documentBubbleShown() const; - void paintTransformedVideoFrame(Painter &p); - void paintTransformedStaticContent(Painter &p); + [[nodiscard]] bool contentShown() const; + [[nodiscard]] bool opaqueContentShown() const; void clearStreaming(bool savePosition = true); bool canInitStreaming() const; @@ -382,7 +413,6 @@ private: bool _opengl = false; const std::unique_ptr _surface; const not_null _widget; - QBrush _transparentBrush; Main::Session *_session = nullptr; rpl::lifetime _sessionLifetime; @@ -436,7 +466,7 @@ private: QPoint _mStart; bool _pressed = false; int32 _dragging = 0; - QPixmap _staticContent; + QImage _staticContent; bool _blurred = true; rpl::lifetime _screenGeometryLifetime; @@ -451,6 +481,7 @@ private: QString _docName, _docSize, _docExt; int _docNameWidth = 0, _docSizeWidth = 0, _docExtWidth = 0; QRect _docRect, _docIconRect; + QImage _docRectImage; int _docThumbx = 0, _docThumby = 0, _docThumbw = 0; object_ptr _docDownload; object_ptr _docSaveAs; @@ -458,7 +489,6 @@ private: QRect _photoRadialRect; Ui::RadialAnimation _radial; - QImage _radialCache; History *_migrated = nullptr; History *_history = nullptr; // if conversation photos or files overview @@ -519,6 +549,7 @@ private: crl::time _saveMsgStarted = 0; anim::value _saveMsgOpacity; QRect _saveMsg; + QImage _saveMsgImage; base::Timer _saveMsgUpdater; Ui::Text::String _saveMsgText; SavePhotoVideo _savePhotoVideoWhenLoaded = SavePhotoVideo::None; @@ -545,5 +576,4 @@ private: }; -} // namespace View -} // namespace Media +} // namespace Media::View diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index bb5918222..ea0812698 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -165,7 +165,7 @@ void EditorBlock::Row::fillSearchIndex() { EditorBlock::EditorBlock(QWidget *parent, Type type, Context *context) : TWidget(parent) , _type(type) , _context(context) -, _transparent(style::transparentPlaceholderBrush()) { +, _transparent(style::TransparentPlaceholder()) { setMouseTracking(true); subscribe(_context->updated, [this] { if (_mouseSelection) { diff --git a/Telegram/lib_ui b/Telegram/lib_ui index e6b736e71..8b7aa4422 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit e6b736e718abd783e0ca32b471846f0ec38d0750 +Subproject commit 8b7aa442268a4928f7582d97b33cb624b29d0cde