mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-06 23:24:01 +02:00
Move incoming video to a separate widget.
This commit is contained in:
parent
f801cb822e
commit
8ce798db12
2 changed files with 177 additions and 106 deletions
|
@ -52,6 +52,123 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtGui/QWindow>
|
#include <QtGui/QWindow>
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined Q_OS_MAC && !defined OS_MAC_OLD
|
||||||
|
#define USE_OPENGL_OVERLAY_WIDGET
|
||||||
|
#endif // Q_OS_MAC && !OS_MAC_OLD
|
||||||
|
|
||||||
|
#ifdef USE_OPENGL_OVERLAY_WIDGET
|
||||||
|
using IncomingParent = Ui::RpWidgetWrap<QOpenGLWidget>;
|
||||||
|
#else // USE_OPENGL_OVERLAY_WIDGET
|
||||||
|
using IncomingParent = Ui::RpWidget;
|
||||||
|
#endif // USE_OPENGL_OVERLAY_WIDGET
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class Panel::Incoming final : public IncomingParent {
|
||||||
|
public:
|
||||||
|
Incoming(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Webrtc::VideoTrack*> track);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
void initBottomShadow();
|
||||||
|
void fillTopShadow(QPainter &p);
|
||||||
|
void fillBottomShadow(QPainter &p);
|
||||||
|
|
||||||
|
const not_null<Webrtc::VideoTrack*> _track;
|
||||||
|
QPixmap _bottomShadow;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Panel::Incoming::Incoming(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Webrtc::VideoTrack*> track)
|
||||||
|
: IncomingParent(parent)
|
||||||
|
, _track(track) {
|
||||||
|
initBottomShadow();
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::Incoming::paintEvent(QPaintEvent *e) {
|
||||||
|
QPainter p(this);
|
||||||
|
|
||||||
|
const auto frame = _track->frame(Webrtc::FrameRequest());
|
||||||
|
if (frame.isNull()) {
|
||||||
|
p.fillRect(e->rect(), Qt::black);
|
||||||
|
fillBottomShadow(p);
|
||||||
|
fillTopShadow(p);
|
||||||
|
} else {
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.drawImage(rect(), frame);
|
||||||
|
}
|
||||||
|
_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<uint32*>(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 = parentWidget()->width();
|
||||||
|
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
|
||||||
|
const auto shadowArea = QRect(
|
||||||
|
position,
|
||||||
|
st::callTitleShadow.size());
|
||||||
|
const auto fill = shadowArea.intersected(geometry()).translated(-x(), -y());
|
||||||
|
if (fill.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.save();
|
||||||
|
p.setClipRect(fill);
|
||||||
|
st::callTitleShadow.paint(p, position, width);
|
||||||
|
p.restore();
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::Incoming::fillBottomShadow(QPainter &p) {
|
||||||
|
const auto shadowArea = QRect(
|
||||||
|
0,
|
||||||
|
parentWidget()->height() - st::callBottomShadowSize,
|
||||||
|
parentWidget()->width(),
|
||||||
|
st::callBottomShadowSize);
|
||||||
|
const auto fill = shadowArea.intersected(geometry()).translated(-x(), -y());
|
||||||
|
if (fill.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto factor = cIntRetinaFactor();
|
||||||
|
p.drawPixmap(
|
||||||
|
fill,
|
||||||
|
_bottomShadow,
|
||||||
|
QRect(
|
||||||
|
0,
|
||||||
|
factor * (fill.y() - shadowArea.y()),
|
||||||
|
factor,
|
||||||
|
factor * fill.height()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Panel::Button final : public Ui::RippleButton {
|
class Panel::Button final : public Ui::RippleButton {
|
||||||
public:
|
public:
|
||||||
|
@ -299,7 +416,6 @@ Panel::Panel(not_null<Call*> call)
|
||||||
initWidget();
|
initWidget();
|
||||||
initControls();
|
initControls();
|
||||||
initLayout();
|
initLayout();
|
||||||
initBottomShadow();
|
|
||||||
showAndActivate();
|
showAndActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,6 +434,8 @@ void Panel::replaceCall(not_null<Call*> call) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::initWindow() {
|
void Panel::initWindow() {
|
||||||
|
_window->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
_window->setAttribute(Qt::WA_NoSystemBackground);
|
||||||
_window->setWindowIcon(
|
_window->setWindowIcon(
|
||||||
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
|
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
|
||||||
_window->setTitle(u" "_q);
|
_window->setTitle(u" "_q);
|
||||||
|
@ -459,16 +577,42 @@ void Panel::setIncomingSize(QSize size) {
|
||||||
if (_incomingFrameSize == size) {
|
if (_incomingFrameSize == size) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
widget()->update(incomingFrameGeometry());
|
|
||||||
_incomingFrameSize = size;
|
_incomingFrameSize = size;
|
||||||
widget()->update(incomingFrameGeometry());
|
refreshIncomingGeometry();
|
||||||
showControls();
|
showControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::refreshIncomingGeometry() {
|
||||||
|
Expects(_call != nullptr);
|
||||||
|
Expects(_incoming != nullptr);
|
||||||
|
|
||||||
|
if (_incomingFrameSize.isEmpty()) {
|
||||||
|
_incoming->hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto to = widget()->size();
|
||||||
|
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
|
||||||
|
const auto big = _incomingFrameSize.scaled(
|
||||||
|
to,
|
||||||
|
Qt::KeepAspectRatioByExpanding);
|
||||||
|
|
||||||
|
// If we cut out no more than 0.33 of the original, let's use expanding.
|
||||||
|
const auto use = ((big.width() * 3 <= to.width() * 4)
|
||||||
|
&& (big.height() * 3 <= to.height() * 4))
|
||||||
|
? big
|
||||||
|
: small;
|
||||||
|
const auto pos = QPoint(
|
||||||
|
(to.width() - use.width()) / 2,
|
||||||
|
(to.height() - use.height()) / 2);
|
||||||
|
_incoming->setGeometry(QRect(pos, use));
|
||||||
|
_incoming->show();
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::reinitWithCall(Call *call) {
|
void Panel::reinitWithCall(Call *call) {
|
||||||
_callLifetime.destroy();
|
_callLifetime.destroy();
|
||||||
_call = call;
|
_call = call;
|
||||||
if (!_call) {
|
if (!_call) {
|
||||||
|
_incoming = nullptr;
|
||||||
_outgoingVideoBubble = nullptr;
|
_outgoingVideoBubble = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -495,6 +639,10 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
_outgoingVideoBubble = std::make_unique<VideoBubble>(
|
_outgoingVideoBubble = std::make_unique<VideoBubble>(
|
||||||
widget(),
|
widget(),
|
||||||
_call->videoOutgoing());
|
_call->videoOutgoing());
|
||||||
|
_incoming = std::make_unique<Incoming>(
|
||||||
|
widget(),
|
||||||
|
_call->videoIncoming());
|
||||||
|
_incoming->hide();
|
||||||
|
|
||||||
_call->mutedValue(
|
_call->mutedValue(
|
||||||
) | rpl::start_with_next([=](bool mute) {
|
) | rpl::start_with_next([=](bool mute) {
|
||||||
|
@ -521,15 +669,14 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
_call->videoIncoming()->renderNextFrame(
|
_call->videoIncoming()->renderNextFrame(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
setIncomingSize(_call->videoIncoming()->frame({}).size());
|
setIncomingSize(_call->videoIncoming()->frame({}).size());
|
||||||
if (_incomingFrameSize.isEmpty()) {
|
if (_incoming->isHidden()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto incoming = incomingFrameGeometry();
|
const auto incoming = incomingFrameGeometry();
|
||||||
const auto outgoing = outgoingFrameGeometry();
|
const auto outgoing = outgoingFrameGeometry();
|
||||||
|
_incoming->update();
|
||||||
if (incoming.intersects(outgoing)) {
|
if (incoming.intersects(outgoing)) {
|
||||||
widget()->update(incoming.united(outgoing));
|
widget()->update(outgoing);
|
||||||
} else {
|
|
||||||
widget()->update(incoming);
|
|
||||||
}
|
}
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
|
@ -537,10 +684,9 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
const auto incoming = incomingFrameGeometry();
|
const auto incoming = incomingFrameGeometry();
|
||||||
const auto outgoing = outgoingFrameGeometry();
|
const auto outgoing = outgoingFrameGeometry();
|
||||||
|
widget()->update(outgoing);
|
||||||
if (incoming.intersects(outgoing)) {
|
if (incoming.intersects(outgoing)) {
|
||||||
widget()->update(incoming.united(outgoing));
|
_incoming->update();
|
||||||
} else {
|
|
||||||
widget()->update(outgoing);
|
|
||||||
}
|
}
|
||||||
}, _callLifetime);
|
}, _callLifetime);
|
||||||
|
|
||||||
|
@ -559,6 +705,8 @@ void Panel::reinitWithCall(Call *call) {
|
||||||
|
|
||||||
_name->setText(_user->name);
|
_name->setText(_user->name);
|
||||||
updateStatusText(_call->state());
|
updateStatusText(_call->state());
|
||||||
|
|
||||||
|
_incoming->lower();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::createRemoteAudioMute() {
|
void Panel::createRemoteAudioMute() {
|
||||||
|
@ -615,66 +763,6 @@ void Panel::initLayout() {
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::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<uint32*>(bytes) + x) = color;
|
|
||||||
}
|
|
||||||
bytes += image.bytesPerLine();
|
|
||||||
}
|
|
||||||
_bottomShadow = Images::PixmapFast(std::move(image));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::fillTopShadow(QPainter &p, QRect incoming) {
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
const auto width = widget()->width();
|
|
||||||
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
|
|
||||||
const auto shadowArea = QRect(
|
|
||||||
position,
|
|
||||||
st::callTitleShadow.size());
|
|
||||||
const auto fill = shadowArea.intersected(incoming);
|
|
||||||
if (fill.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
p.save();
|
|
||||||
p.setClipRect(fill);
|
|
||||||
st::callTitleShadow.paint(p, position, width);
|
|
||||||
p.restore();
|
|
||||||
#endif // Q_OS_WIN
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::fillBottomShadow(QPainter &p, QRect incoming) {
|
|
||||||
const auto shadowArea = QRect(
|
|
||||||
0,
|
|
||||||
widget()->height() - st::callBottomShadowSize,
|
|
||||||
widget()->width(),
|
|
||||||
st::callBottomShadowSize);
|
|
||||||
const auto fill = shadowArea.intersected(incoming);
|
|
||||||
if (fill.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto factor = cIntRetinaFactor();
|
|
||||||
p.drawPixmap(
|
|
||||||
fill,
|
|
||||||
_bottomShadow,
|
|
||||||
QRect(
|
|
||||||
0,
|
|
||||||
factor * (fill.y() - shadowArea.y()),
|
|
||||||
factor,
|
|
||||||
factor * fill.height()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Panel::showControls() {
|
void Panel::showControls() {
|
||||||
Expects(_call != nullptr);
|
Expects(_call != nullptr);
|
||||||
|
|
||||||
|
@ -683,6 +771,7 @@ void Panel::showControls() {
|
||||||
_cancel->setVisible(_cancel->toggled());
|
_cancel->setVisible(_cancel->toggled());
|
||||||
|
|
||||||
const auto shown = !_incomingFrameSize.isEmpty();
|
const auto shown = !_incomingFrameSize.isEmpty();
|
||||||
|
_incoming->setVisible(shown);
|
||||||
_name->setVisible(!shown);
|
_name->setVisible(!shown);
|
||||||
_status->setVisible(!shown);
|
_status->setVisible(!shown);
|
||||||
_userpic->setVisible(!shown);
|
_userpic->setVisible(!shown);
|
||||||
|
@ -726,24 +815,9 @@ void Panel::toggleFullScreen(bool fullscreen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect Panel::incomingFrameGeometry() const {
|
QRect Panel::incomingFrameGeometry() const {
|
||||||
if (!_call || _incomingFrameSize.isEmpty()) {
|
return (!_incoming || _incoming->isHidden())
|
||||||
return QRect();
|
? QRect()
|
||||||
}
|
: _incoming->geometry();
|
||||||
const auto to = widget()->size();
|
|
||||||
const auto small = _incomingFrameSize.scaled(to, Qt::KeepAspectRatio);
|
|
||||||
const auto big = _incomingFrameSize.scaled(
|
|
||||||
to,
|
|
||||||
Qt::KeepAspectRatioByExpanding);
|
|
||||||
|
|
||||||
// If we cut out no more than 0.33 of the original, let's use expanding.
|
|
||||||
const auto use = ((big.width() * 3 <= to.width() * 4)
|
|
||||||
&& (big.height() * 3 <= to.height() * 4))
|
|
||||||
? big
|
|
||||||
: small;
|
|
||||||
const auto pos = QPoint(
|
|
||||||
(to.width() - use.width()) / 2,
|
|
||||||
(to.height() - use.height()) / 2);
|
|
||||||
return QRect(pos, use);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect Panel::outgoingFrameGeometry() const {
|
QRect Panel::outgoingFrameGeometry() const {
|
||||||
|
@ -754,6 +828,9 @@ void Panel::updateControlsGeometry() {
|
||||||
if (widget()->size().isEmpty()) {
|
if (widget()->size().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (_incoming) {
|
||||||
|
refreshIncomingGeometry();
|
||||||
|
}
|
||||||
if (_fingerprint) {
|
if (_fingerprint) {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
const auto minRight = _controls->geometry().width()
|
const auto minRight = _controls->geometry().width()
|
||||||
|
@ -875,21 +952,16 @@ void Panel::updateStatusGeometry() {
|
||||||
void Panel::paint(QRect clip) {
|
void Panel::paint(QRect clip) {
|
||||||
Painter p(widget());
|
Painter p(widget());
|
||||||
|
|
||||||
p.fillRect(clip, st::callBgOpaque);
|
auto region = QRegion(clip);
|
||||||
|
if (!_incoming->isHidden()) {
|
||||||
const auto incoming = incomingFrameGeometry();
|
region = region.subtracted(QRegion(_incoming->geometry()));
|
||||||
if (!incoming.isEmpty()) {
|
}
|
||||||
Assert(_call != nullptr);
|
for (const auto rect : region.rects()) {
|
||||||
const auto frame = _call->videoIncoming()->frame(
|
p.fillRect(rect, st::callBgOpaque);
|
||||||
Webrtc::FrameRequest());
|
}
|
||||||
if (!frame.isNull()) {
|
if (_incoming && _incoming->isHidden()) {
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
_call->videoIncoming()->markFrameShown();
|
||||||
p.drawImage(incoming, frame);
|
|
||||||
}
|
|
||||||
fillBottomShadow(p, incoming);
|
|
||||||
fillTopShadow(p, incoming);
|
|
||||||
}
|
}
|
||||||
_call->videoIncoming()->markFrameShown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::handleClose() {
|
void Panel::handleClose() {
|
||||||
|
|
|
@ -55,7 +55,7 @@ public:
|
||||||
void closeBeforeDestroy();
|
void closeBeforeDestroy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Content;
|
class Incoming;
|
||||||
class Button;
|
class Button;
|
||||||
using State = Call::State;
|
using State = Call::State;
|
||||||
using Type = Call::Type;
|
using Type = Call::Type;
|
||||||
|
@ -75,7 +75,6 @@ private:
|
||||||
void reinitWithCall(Call *call);
|
void reinitWithCall(Call *call);
|
||||||
void initLayout();
|
void initLayout();
|
||||||
void initGeometry();
|
void initGeometry();
|
||||||
void initBottomShadow();
|
|
||||||
|
|
||||||
void handleClose();
|
void handleClose();
|
||||||
|
|
||||||
|
@ -91,8 +90,7 @@ private:
|
||||||
void updateStatusText(State state);
|
void updateStatusText(State state);
|
||||||
void startDurationUpdateTimer(crl::time currentDuration);
|
void startDurationUpdateTimer(crl::time currentDuration);
|
||||||
void setIncomingSize(QSize size);
|
void setIncomingSize(QSize size);
|
||||||
void fillTopShadow(QPainter &p, QRect incoming);
|
void refreshIncomingGeometry();
|
||||||
void fillBottomShadow(QPainter &p, QRect incoming);
|
|
||||||
|
|
||||||
void refreshOutgoingPreviewInBody(State state);
|
void refreshOutgoingPreviewInBody(State state);
|
||||||
void toggleFullScreen(bool fullscreen);
|
void toggleFullScreen(bool fullscreen);
|
||||||
|
@ -106,6 +104,7 @@ private:
|
||||||
not_null<UserData*> _user;
|
not_null<UserData*> _user;
|
||||||
|
|
||||||
const std::unique_ptr<Ui::Window> _window;
|
const std::unique_ptr<Ui::Window> _window;
|
||||||
|
std::unique_ptr<Incoming> _incoming;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
std::unique_ptr<Ui::Platform::TitleControls> _controls;
|
||||||
|
|
Loading…
Add table
Reference in a new issue