diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 348a1f503b..288610e654 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -465,7 +465,8 @@ callTitle: WindowTitle(defaultWindowTitle) { closeIconActive: callTitleCloseIcon; closeIconActiveOver: callTitleCloseIconOver; } -callTitleShadow: icon {{ "calls/calls_shadow_controls", windowShadowFg }}; +callTitleShadowRight: icon {{ "calls/calls_shadow_controls", windowShadowFg }}; +callTitleShadowLeft: icon {{ "calls/calls_shadow_controls-flip_horizontal", windowShadowFg }}; callErrorToast: Toast(defaultToast) { minWidth: 240px; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index fdb7211ca2..ab8819b70c 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_signal_bars.h" #include "calls/calls_userpic.h" #include "calls/calls_video_bubble.h" +#include "calls/calls_video_incoming.h" #include "ui/platform/ui_platform_window_title.h" #include "ui/widgets/call_button.h" #include "ui/widgets/buttons.h" @@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/padding_wrap.h" #include "ui/platform/ui_platform_utility.h" #include "ui/gl/gl_surface.h" +#include "ui/gl/gl_shader.h" #include "ui/toast/toast.h" #include "ui/empty_userpic.h" #include "ui/emoji_config.h" @@ -52,176 +54,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Calls { -class Panel::Incoming final { -public: - Incoming( - not_null parent, - not_null track, - Ui::GL::Backend backend); - - [[nodiscard]] not_null widget() const; - [[nodiscard]] not_null rp() const; - -private: - void paint(QPainter &p, const QRegion &clip, bool opengl); - - void initBottomShadow(); - void fillTopShadow(QPainter &p); - void fillBottomShadow(QPainter &p); - - [[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer( - Ui::GL::Backend backend); - - const std::unique_ptr _surface; - const not_null _track; - QPixmap _bottomShadow; - -}; - -Panel::Incoming::Incoming( - not_null parent, - not_null track, - Ui::GL::Backend backend) -: _surface(Ui::GL::CreateSurface(parent, chooseRenderer(backend))) -, _track(track) { - initBottomShadow(); - widget()->setAttribute(Qt::WA_OpaquePaintEvent); - widget()->setAttribute(Qt::WA_TransparentForMouseEvents); -} - -not_null Panel::Incoming::widget() const { - return _surface->rpWidget(); -} - -not_null Panel::Incoming::rp() const { - return _surface.get(); -} - -Ui::GL::ChosenRenderer Panel::Incoming::chooseRenderer( - Ui::GL::Backend backend) { - 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.boundingRect(), - backend == Ui::GL::Backend::OpenGL); - } - - private: - const not_null _owner; - - }; - - return { - .renderer = std::make_unique(this), - .backend = backend, - }; -} - -void Panel::Incoming::paint(QPainter &p, const QRegion &clip, bool opengl) { - const auto data = _track->frameWithInfo(true); - const auto &image = data.original; - const auto rotation = data.rotation; - if (image.isNull()) { - p.fillRect(clip.boundingRect(), Qt::black); - } else { - const auto rect = widget()->rect(); - using namespace Media::View; - auto hq = PainterHighQualityEnabler(p); - if (UsePainterRotation(rotation, opengl)) { - if (rotation) { - p.save(); - p.rotate(rotation); - } - p.drawImage(RotatedRect(rect, rotation), image); - if (rotation) { - p.restore(); - } - } else if (rotation) { - p.drawImage(rect, RotateFrameImage(image, rotation)); - } else { - p.drawImage(rect, image); - } - fillBottomShadow(p); - fillTopShadow(p); - } - _track->markFrameShown(); -} - -void Panel::Incoming::initBottomShadow() { - auto image = QImage( - QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(), - QImage::Format_ARGB32_Premultiplied); - const auto colorFrom = uint32(0); - const auto colorTill = uint32(74); - const auto rows = image.height(); - const auto step = (uint64(colorTill - colorFrom) << 32) / rows; - auto accumulated = uint64(); - auto bytes = image.bits(); - for (auto y = 0; y != rows; ++y) { - accumulated += step; - const auto color = (colorFrom + uint32(accumulated >> 32)) << 24; - for (auto x = 0; x != image.width(); ++x) { - *(reinterpret_cast(bytes) + x) = color; - } - bytes += image.bytesPerLine(); - } - _bottomShadow = Images::PixmapFast(std::move(image)); -} - -void Panel::Incoming::fillTopShadow(QPainter &p) { -#ifdef Q_OS_WIN - const auto width = widget()->parentWidget()->width(); - const auto position = QPoint(width - st::callTitleShadow.width(), 0); - const auto shadowArea = QRect( - position, - st::callTitleShadow.size()); - const auto fill = shadowArea.intersected( - widget()->geometry()).translated(-widget()->pos()); - if (fill.isEmpty()) { - return; - } - p.save(); - p.setClipRect(fill); - st::callTitleShadow.paint(p, position - widget()->pos(), width); - p.restore(); -#endif // Q_OS_WIN -} - -void Panel::Incoming::fillBottomShadow(QPainter &p) { - const auto shadowArea = QRect( - 0, - widget()->parentWidget()->height() - st::callBottomShadowSize, - widget()->parentWidget()->width(), - st::callBottomShadowSize); - const auto fill = shadowArea.intersected( - widget()->geometry()).translated(-widget()->pos()); - if (fill.isEmpty()) { - return; - } - const auto factor = cIntRetinaFactor(); - p.drawPixmap( - fill, - _bottomShadow, - QRect( - 0, - (factor - * (fill.y() - shadowArea.translated(-widget()->pos()).y())), - factor, - factor * fill.height())); -} - Panel::Panel(not_null call) : _call(call) , _user(call->user()) -, _window(std::make_unique()) +, _window(createWindow()) #ifndef Q_OS_MAC , _controls(std::make_unique( _window->body(), @@ -531,7 +367,9 @@ void Panel::reinitWithCall(Call *call) { _call->videoIncoming()->renderNextFrame( ) | rpl::start_with_next([=] { const auto track = _call->videoIncoming(); - setIncomingSize(track->frameSize()); + setIncomingSize(track->state() == Webrtc::VideoState::Active + ? track->frameSize() + : QSize()); if (_incoming->widget()->isHidden()) { return; } @@ -543,6 +381,13 @@ void Panel::reinitWithCall(Call *call) { } }, _callLifetime); + _call->videoIncoming()->stateValue( + ) | rpl::start_with_next([=](Webrtc::VideoState state) { + setIncomingSize((state == Webrtc::VideoState::Active) + ? _call->videoIncoming()->frameSize() + : QSize()); + }, _callLifetime); + _call->videoOutgoing()->renderNextFrame( ) | rpl::start_with_next([=] { const auto incoming = incomingFrameGeometry(); @@ -593,6 +438,12 @@ void Panel::reinitWithCall(Call *call) { _name->setText(_user->name); updateStatusText(_call->state()); + _answerHangupRedial->raise(); + _decline->raise(); + _cancel->raise(); + _camera->raise(); + _mute->raise(); + _incoming->widget()->lower(); } @@ -720,15 +571,31 @@ void Panel::updateControlsGeometry() { } if (_fingerprint) { #ifndef Q_OS_MAC - const auto minRight = _controls->geometry().width() - + st::callFingerprintTop; + const auto controlsGeometry = _controls->geometry(); + const auto halfWidth = widget()->width() / 2; + const auto minLeft = (controlsGeometry.center().x() < halfWidth) + ? (controlsGeometry.width() + st::callFingerprintTop) + : 0; + const auto minRight = (controlsGeometry.center().x() >= halfWidth) + ? (controlsGeometry.width() + st::callFingerprintTop) + : 0; + _incoming->setControlsAlignment(minLeft + ? style::al_left + : style::al_right); #else // !Q_OS_MAC + const auto minLeft = 0; const auto minRight = 0; #endif // _controls const auto desired = (widget()->width() - _fingerprint->width()) / 2; - _fingerprint->moveToRight( - std::max(desired, minRight), - st::callFingerprintTop); + if (minLeft) { + _fingerprint->moveToLeft( + std::max(desired, minLeft), + st::callFingerprintTop); + } else { + _fingerprint->moveToRight( + std::max(desired, minRight), + st::callFingerprintTop); + } } const auto innerHeight = std::max(widget()->height(), st::callHeightMin); const auto innerWidth = widget()->width() - 2 * st::callInnerPadding; diff --git a/Telegram/SourceFiles/calls/calls_video_incoming.cpp b/Telegram/SourceFiles/calls/calls_video_incoming.cpp new file mode 100644 index 0000000000..b4dc4014ba --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_video_incoming.cpp @@ -0,0 +1,589 @@ +/* +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 "calls/calls_video_incoming.h" + +#include "ui/gl/gl_surface.h" +#include "ui/gl/gl_shader.h" +#include "ui/gl/gl_image.h" +#include "ui/gl/gl_primitives.h" +#include "media/view/media_view_pip.h" +#include "webrtc/webrtc_video_track.h" +#include "styles/style_calls.h" + +#include +#include + +namespace Calls { +namespace { + +constexpr auto kBottomShadowAlphaMax = 74; + +using namespace Ui::GL; + +[[nodiscard]] ShaderPart FragmentBottomShadow() { + return { + .header = R"( +uniform vec3 shadow; // fullHeight, shadowTop, maxOpacity +)", + .body = R"( + float shadowCoord = shadow.y - gl_FragCoord.y; + float shadowValue = clamp(shadowCoord / shadow.x, 0., 1.); + float shadowShown = shadowValue * shadow.z; + result = vec4(min(result.rgb, vec3(1.)) * (1. - shadowShown), result.a); +)", + }; +} + +} // namespace + +class Panel::Incoming::RendererGL final : public Ui::GL::Renderer { +public: + explicit RendererGL(not_null 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) override; + + void paint( + not_null widget, + QOpenGLFunctions &f) override; + +private: + void uploadTexture( + QOpenGLFunctions &f, + GLint internalformat, + GLint format, + QSize size, + QSize hasSize, + int stride, + const void *data) const; + void validateShadowImage(); + + const not_null _owner; + + QSize _viewport; + float _factor = 1.; + QVector2D _uniformViewport; + + std::optional _contentBuffer; + std::optional _argb32Program; + QOpenGLShader *_texturedVertexShader = nullptr; + std::optional _yuv420Program; + std::optional _imageProgram; + Ui::GL::Textures<4> _textures; + QSize _rgbaSize; + QSize _lumaSize; + QSize _chromaSize; + int _trackFrameIndex = 0; + + Ui::GL::Image _controlsShadowImage; + QRect _controlsShadowLeft; + QRect _controlsShadowRight; + + rpl::lifetime _lifetime; + +}; + +class Panel::Incoming::RendererSW final : public Ui::GL::Renderer { +public: + explicit RendererSW(not_null owner); + + void paintFallback( + Painter &&p, + const QRegion &clip, + Ui::GL::Backend backend) override; + +private: + void initBottomShadow(); + void fillTopShadow(QPainter &p); + void fillBottomShadow(QPainter &p); + + const not_null _owner; + + QImage _bottomShadow; + +}; + +Panel::Incoming::RendererGL::RendererGL(not_null owner) +: _owner(owner) { + style::PaletteChanged( + ) | rpl::start_with_next([=] { + _controlsShadowImage.invalidate(); + }, _lifetime); +} + +void Panel::Incoming::RendererGL::init( + not_null widget, + QOpenGLFunctions &f) { + constexpr auto kQuads = 2; + constexpr auto kQuadVertices = kQuads * 4; + constexpr auto kQuadValues = kQuadVertices * 4; + + _contentBuffer.emplace(); + _contentBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); + _contentBuffer->create(); + _contentBuffer->bind(); + _contentBuffer->allocate(kQuadValues * sizeof(GLfloat)); + + _textures.ensureCreated(f); + + _imageProgram.emplace(); + _texturedVertexShader = LinkProgram( + &*_imageProgram, + VertexShader({ + VertexViewportTransform(), + VertexPassTextureCoord(), + }), + FragmentShader({ + FragmentSampleARGB32Texture(), + })).vertex; + + _argb32Program.emplace(); + LinkProgram( + &*_argb32Program, + _texturedVertexShader, + FragmentShader({ + FragmentSampleARGB32Texture(), + FragmentBottomShadow(), + })); + + _yuv420Program.emplace(); + LinkProgram( + &*_yuv420Program, + _texturedVertexShader, + FragmentShader({ + FragmentSampleYUV420Texture(), + FragmentBottomShadow(), + })); +} + +void Panel::Incoming::RendererGL::deinit( + not_null widget, + QOpenGLFunctions &f) { + _textures.destroy(f); + _imageProgram = std::nullopt; + _texturedVertexShader = nullptr; + _argb32Program = std::nullopt; + _yuv420Program = std::nullopt; + _contentBuffer = std::nullopt; +} + +void Panel::Incoming::RendererGL::resize( + not_null widget, + QOpenGLFunctions &f, + int w, + int h) { + const auto factor = widget->devicePixelRatio(); + if (_factor != factor) { + _factor = factor; + _controlsShadowImage.invalidate(); + } + _viewport = QSize{ w, h }; + _uniformViewport = QVector2D( + _viewport.width() * _factor, + _viewport.height() * _factor); + const auto size = _viewport * _factor; + f.glViewport(0, 0, size.width(), size.height()); +} + +void Panel::Incoming::RendererGL::paint( + not_null widget, + QOpenGLFunctions &f) { + const auto markGuard = gsl::finally([&] { + _owner->_track->markFrameShown(); + }); + const auto data = _owner->_track->frameWithInfo(false); + if (data.format == Webrtc::FrameFormat::None) { + return; + } + const auto rgbaFrame = (data.format == Webrtc::FrameFormat::ARGB32); + const auto upload = (_trackFrameIndex != data.index); + _trackFrameIndex = data.index; + auto &program = rgbaFrame ? _argb32Program : _yuv420Program; + program->bind(); + if (rgbaFrame) { + Assert(!data.original.isNull()); + f.glActiveTexture(GL_TEXTURE0); + _textures.bind(f, 0); + if (upload) { + uploadTexture( + f, + GL_RGBA, + GL_RGBA, + data.original.size(), + _rgbaSize, + data.original.bytesPerLine() / 4, + data.original.constBits()); + _rgbaSize = data.original.size(); + } + program->setUniformValue("s_texture", GLint(0)); + } else { + Assert(data.format == Webrtc::FrameFormat::YUV420); + Assert(!data.yuv420->size.isEmpty()); + const auto yuv = data.yuv420; + + f.glActiveTexture(GL_TEXTURE0); + _textures.bind(f, 1); + if (upload) { + f.glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + uploadTexture( + f, + GL_RED, + GL_RED, + yuv->size, + _lumaSize, + yuv->y.stride, + yuv->y.data); + _lumaSize = yuv->size; + } + f.glActiveTexture(GL_TEXTURE1); + _textures.bind(f, 2); + if (upload) { + uploadTexture( + f, + GL_RED, + GL_RED, + yuv->chromaSize, + _chromaSize, + yuv->u.stride, + yuv->u.data); + } + f.glActiveTexture(GL_TEXTURE2); + _textures.bind(f, 3); + if (upload) { + uploadTexture( + f, + GL_RED, + GL_RED, + yuv->chromaSize, + _chromaSize, + yuv->v.stride, + yuv->v.data); + _chromaSize = yuv->chromaSize; + f.glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } + program->setUniformValue("y_texture", GLint(0)); + program->setUniformValue("u_texture", GLint(1)); + program->setUniformValue("v_texture", GLint(2)); + } + const auto rect = TransformRect( + widget->rect(), + _viewport, + _factor); + std::array, 4> texcoords = { { + { { 0.f, 1.f } }, + { { 1.f, 1.f } }, + { { 1.f, 0.f } }, + { { 0.f, 0.f } }, + } }; + if (const auto shift = (data.rotation / 90); shift != 0) { + std::rotate( + begin(texcoords), + begin(texcoords) + shift, + end(texcoords)); + } + + const auto width = widget->parentWidget()->width(); + const auto left = (_owner->_topControlsAlignment == style::al_left); + validateShadowImage(); + const auto position = left + ? QPoint() + : QPoint(width - st::callTitleShadowRight.width(), 0); + const auto translated = position - widget->pos(); + const auto shadowArea = QRect(translated, st::callTitleShadowLeft.size()); + const auto shadow = _controlsShadowImage.texturedRect( + shadowArea, + (left ? _controlsShadowLeft : _controlsShadowRight), + widget->rect()); + const auto shadowRect = TransformRect( + shadow.geometry, + _viewport, + _factor); + + const GLfloat coords[] = { + rect.left(), rect.top(), + texcoords[0][0], texcoords[0][1], + + rect.right(), rect.top(), + texcoords[1][0], texcoords[1][1], + + rect.right(), rect.bottom(), + texcoords[2][0], texcoords[2][1], + + rect.left(), rect.bottom(), + texcoords[3][0], texcoords[3][1], + + shadowRect.left(), shadowRect.top(), + shadow.texture.left(), shadow.texture.bottom(), + + shadowRect.right(), shadowRect.top(), + shadow.texture.right(), shadow.texture.bottom(), + + shadowRect.right(), shadowRect.bottom(), + shadow.texture.right(), shadow.texture.top(), + + shadowRect.left(), shadowRect.bottom(), + shadow.texture.left(), shadow.texture.top(), + }; + + _contentBuffer->write(0, coords, sizeof(coords)); + + const auto bottomShadowArea = QRect( + 0, + widget->parentWidget()->height() - st::callBottomShadowSize, + widget->parentWidget()->width(), + st::callBottomShadowSize); + const auto bottomShadowFill = bottomShadowArea.intersected( + widget->geometry()).translated(-widget->pos()); + const auto shadowHeight = bottomShadowFill.height(); + const auto shadowAlpha = (shadowHeight * kBottomShadowAlphaMax) + / (st::callBottomShadowSize * 255.); + + program->setUniformValue("viewport", _uniformViewport); + program->setUniformValue("shadow", QVector3D( + shadowHeight * _factor, + TransformRect(bottomShadowFill, _viewport, _factor).bottom(), + shadowAlpha)); + + FillTexturedRectangle(f, &*program); + +#ifndef Q_OS_MAC + if (!shadowRect.empty()) { + f.glEnable(GL_BLEND); + f.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + const auto guard = gsl::finally([&] { + f.glDisable(GL_BLEND); + }); + + _imageProgram->bind(); + _imageProgram->setUniformValue("viewport", _uniformViewport); + _imageProgram->setUniformValue("s_texture", GLint(0)); + + f.glActiveTexture(GL_TEXTURE0); + _controlsShadowImage.bind(f); + + FillTexturedRectangle(f, &*_imageProgram, 4); + } +#endif // Q_OS_MAC +} + +void Panel::Incoming::RendererGL::validateShadowImage() { + if (_controlsShadowImage) { + return; + } + const auto size = st::callTitleShadowLeft.size(); + const auto full = QSize(size.width(), 2 * size.height()) * int(_factor); + auto image = QImage(full, QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(_factor); + image.fill(Qt::transparent); + { + auto p = QPainter(&image); + st::callTitleShadowLeft.paint(p, 0, 0, size.width()); + _controlsShadowLeft = QRect(0, 0, full.width(), full.height() / 2); + st::callTitleShadowRight.paint(p, 0, size.height(), size.width()); + _controlsShadowRight = QRect( + 0, + full.height() / 2, + full.width(), + full.height() / 2); + } + _controlsShadowImage.setImage(std::move(image)); +} + +void Panel::Incoming::RendererGL::uploadTexture( + QOpenGLFunctions &f, + GLint internalformat, + GLint format, + QSize size, + QSize hasSize, + int stride, + const void *data) const { + f.glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + if (hasSize != size) { + f.glTexImage2D( + GL_TEXTURE_2D, + 0, + internalformat, + size.width(), + size.height(), + 0, + format, + GL_UNSIGNED_BYTE, + data); + } else { + f.glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + size.width(), + size.height(), + format, + GL_UNSIGNED_BYTE, + data); + } + f.glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} + +Panel::Incoming::RendererSW::RendererSW(not_null owner) +: _owner(owner) { + initBottomShadow(); +} + +void Panel::Incoming::RendererSW::paintFallback( + Painter &&p, + const QRegion &clip, + Ui::GL::Backend backend) { + const auto markGuard = gsl::finally([&] { + _owner->_track->markFrameShown(); + }); + const auto data = _owner->_track->frameWithInfo(true); + const auto &image = data.original; + const auto rotation = data.rotation; + if (image.isNull()) { + p.fillRect(clip.boundingRect(), Qt::black); + } else { + const auto rect = _owner->widget()->rect(); + using namespace Media::View; + auto hq = PainterHighQualityEnabler(p); + if (UsePainterRotation(rotation)) { + if (rotation) { + p.save(); + p.rotate(rotation); + } + p.drawImage(RotatedRect(rect, rotation), image); + if (rotation) { + p.restore(); + } + } else if (rotation) { + p.drawImage(rect, RotateFrameImage(image, rotation)); + } else { + p.drawImage(rect, image); + } + fillBottomShadow(p); + fillTopShadow(p); + } +} + +void Panel::Incoming::RendererSW::initBottomShadow() { + auto image = QImage( + QSize(1, st::callBottomShadowSize) * cIntRetinaFactor(), + QImage::Format_ARGB32_Premultiplied); + const auto colorFrom = uint32(0); + const auto colorTill = uint32(kBottomShadowAlphaMax); + const auto rows = image.height(); + const auto step = (uint64(colorTill - colorFrom) << 32) / rows; + auto accumulated = uint64(); + auto bytes = image.bits(); + for (auto y = 0; y != rows; ++y) { + accumulated += step; + const auto color = (colorFrom + uint32(accumulated >> 32)) << 24; + for (auto x = 0; x != image.width(); ++x) { + *(reinterpret_cast(bytes) + x) = color; + } + bytes += image.bytesPerLine(); + } + _bottomShadow = std::move(image); +} + +void Panel::Incoming::RendererSW::fillTopShadow(QPainter &p) { +#ifndef Q_OS_MAC + const auto widget = _owner->widget(); + const auto width = widget->parentWidget()->width(); + const auto left = (_owner->_topControlsAlignment == style::al_left); + const auto &icon = left + ? st::callTitleShadowLeft + : st::callTitleShadowRight; + const auto position = left + ? QPoint() + : QPoint(width - icon.width(), 0); + const auto shadowArea = QRect(position, icon.size()); + const auto fill = shadowArea.intersected( + widget->geometry()).translated(-widget->pos()); + if (fill.isEmpty()) { + return; + } + p.save(); + p.setClipRect(fill); + icon.paint(p, position - widget->pos(), width); + p.restore(); +#endif // Q_OS_MAC +} + +void Panel::Incoming::RendererSW::fillBottomShadow(QPainter &p) { + const auto widget = _owner->widget(); + const auto shadowArea = QRect( + 0, + widget->parentWidget()->height() - st::callBottomShadowSize, + widget->parentWidget()->width(), + st::callBottomShadowSize); + const auto fill = shadowArea.intersected( + widget->geometry()).translated(-widget->pos()); + if (fill.isEmpty()) { + return; + } + const auto factor = cIntRetinaFactor(); + p.drawImage( + fill, + _bottomShadow, + QRect( + 0, + (factor + * (fill.y() - shadowArea.translated(-widget->pos()).y())), + factor, + factor * fill.height())); +} + +Panel::Incoming::Incoming( + not_null parent, + not_null track, + Ui::GL::Backend backend) +: _surface(Ui::GL::CreateSurface(parent, chooseRenderer(backend))) +, _track(track) { + widget()->setAttribute(Qt::WA_OpaquePaintEvent); + widget()->setAttribute(Qt::WA_TransparentForMouseEvents); +} + +not_null Panel::Incoming::widget() const { + return _surface->rpWidget(); +} + +not_null Panel::Incoming::rp() const { + return _surface.get(); +} + +void Panel::Incoming::setControlsAlignment(style::align align) { + if (_topControlsAlignment != align) { + _topControlsAlignment = align; + widget()->update(); + } +} + +Ui::GL::ChosenRenderer Panel::Incoming::chooseRenderer( + Ui::GL::Backend backend) { + _opengl = (backend == Ui::GL::Backend::OpenGL); + return { + .renderer = (_opengl + ? std::unique_ptr( + std::make_unique(this)) + : std::make_unique(this)), + .backend = backend, + }; +} + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/calls_video_incoming.h b/Telegram/SourceFiles/calls/calls_video_incoming.h new file mode 100644 index 0000000000..65da54620e --- /dev/null +++ b/Telegram/SourceFiles/calls/calls_video_incoming.h @@ -0,0 +1,45 @@ +/* +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 "calls/calls_panel.h" + +namespace Ui::GL { +enum class Backend; +struct ChosenRenderer; +} // namespace Ui::GL + +namespace Calls { + +class Panel::Incoming final { +public: + Incoming( + not_null parent, + not_null track, + Ui::GL::Backend backend); + + [[nodiscard]] not_null widget() const; + [[nodiscard]] not_null rp() const; + + void setControlsAlignment(style::align align); + +private: + class RendererGL; + class RendererSW; + + [[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer( + Ui::GL::Backend backend); + + const std::unique_ptr _surface; + const not_null _track; + style::align _topControlsAlignment = style::al_left; + bool _opengl = false; + +}; + +} // namespace Calls diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp index 7f559c1234..d334ad790f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp @@ -502,6 +502,9 @@ void Viewport::RendererGL::paintTile( not_null tile, TileData &tileData) { const auto track = tile->track(); + const auto markGuard = gsl::finally([&] { + tile->track()->markFrameShown(); + }); const auto data = track->frameWithInfo(false); _userpicFrame = (data.format == Webrtc::FrameFormat::None); validateUserpicFrame(tile, tileData); @@ -745,7 +748,6 @@ void Viewport::RendererGL::paintTile( f.glViewport(0, 0, blurSize.width(), blurSize.height()); bindFrame(f, data, tileData, _downscaleProgram); - tile->track()->markFrameShown(); drawDownscalePass(f, tileData); drawFirstBlurPass(f, tileData, blurSize); diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp index 5c6a366568..6d3a74dd62 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp @@ -95,6 +95,9 @@ void Viewport::RendererSW::paintTile( const QRect &clip, QRegion &bg) { const auto track = tile->track(); + const auto markGuard = gsl::finally([&] { + tile->track()->markFrameShown(); + }); const auto data = track->frameWithInfo(true); auto &tileData = _tileData[tile]; tileData.stale = false; @@ -139,7 +142,7 @@ void Viewport::RendererSW::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(frameRotation, false)) { + if (UsePainterRotation(frameRotation)) { if (frameRotation) { p.save(); p.rotate(frameRotation); @@ -154,7 +157,6 @@ void Viewport::RendererSW::paintTile( p.drawImage(target, image); } bg -= target; - track->markFrameShown(); if (left > 0) { fill({ x, y, left, height }); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index cec4b6ec62..e82eaacedf 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -455,7 +455,7 @@ void OverlayWidget::RendererGL::paintControl( 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 bgRect = transformRect(outer); const auto iconRect = _controlsImage.texturedRect( inner, _controlsTextures[meta.index]); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp index 57d49d68ff..cce6d7831a 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp @@ -98,7 +98,7 @@ void OverlayWidget::RendererSW::paintTransformedImage( QRect rect, int rotation) { PainterHighQualityEnabler hq(*_p); - if (UsePainterRotation(rotation, false)) { + if (UsePainterRotation(rotation)) { if (rotation) { _p->save(); _p->rotate(rotation); diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 02141796eb..84c6f08a88 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -309,8 +309,8 @@ QRect RotatedRect(QRect rect, int rotation) { Unexpected("Rotation in RotatedRect."); } -bool UsePainterRotation(int rotation, bool opengl) { - return opengl || !(rotation % 180); +bool UsePainterRotation(int rotation) { + return !(rotation % 180); } QSize FlipSizeByRotation(QSize size, int rotation) { diff --git a/Telegram/SourceFiles/media/view/media_view_pip.h b/Telegram/SourceFiles/media/view/media_view_pip.h index 5a0cf6c8ba..1554815492 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.h +++ b/Telegram/SourceFiles/media/view/media_view_pip.h @@ -38,7 +38,7 @@ namespace View { class PlaybackProgress; [[nodiscard]] QRect RotatedRect(QRect rect, int rotation); -[[nodiscard]] bool UsePainterRotation(int rotation, bool opengl); +[[nodiscard]] bool UsePainterRotation(int rotation); [[nodiscard]] QSize FlipSizeByRotation(QSize size, int rotation); [[nodiscard]] QImage RotateFrameImage(QImage image, int rotation); diff --git a/Telegram/SourceFiles/media/view/media_view_pip_raster.cpp b/Telegram/SourceFiles/media/view/media_view_pip_raster.cpp index 20150bd5c1..685850180f 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip_raster.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip_raster.cpp @@ -209,7 +209,7 @@ void Pip::RendererSW::paintTransformedImage( Ui::Shadow::paint(*_p, rect, geometry.outer.width(), st::callShadow); } - if (UsePainterRotation(rotation, false)) { + if (UsePainterRotation(rotation)) { if (rotation) { _p->save(); _p->rotate(rotation);