Rotate video in PiP.

This commit is contained in:
John Preston 2020-02-05 20:04:33 +04:00
parent 4544b091a0
commit d29c3add79
3 changed files with 127 additions and 50 deletions

View File

@ -199,32 +199,6 @@ QPixmap PrepareStaticImage(const QString &path) {
return App::pixmapFromImageInPlace(std::move(image)); return App::pixmapFromImageInPlace(std::move(image));
} }
[[nodiscard]] QRect RotatedRect(QRect rect, int rotation) {
switch (rotation) {
case 0: return rect;
case 90: return QRect(
rect.y(),
-rect.x() - rect.width(),
rect.height(),
rect.width());
case 180: return QRect(
-rect.x() - rect.width(),
-rect.y() - rect.height(),
rect.width(),
rect.height());
case 270: return QRect(
-rect.y() - rect.height(),
rect.x(),
rect.height(),
rect.width());
}
Unexpected("Rotation in RotatedRect.");
}
[[nodiscard]] bool UsePainterRotation(int rotation) {
return Platform::IsMac() || !(rotation % 180);
}
} // namespace } // namespace
struct OverlayWidget::SharedMedia { struct OverlayWidget::SharedMedia {
@ -460,9 +434,7 @@ void OverlayWidget::moveToScreen(bool force) {
} }
QSize OverlayWidget::flipSizeByRotation(QSize size) const { QSize OverlayWidget::flipSizeByRotation(QSize size) const {
return (((_rotation / 90) % 2) == 1) return FlipSizeByRotation(size, _rotation);
? QSize(size.height(), size.width())
: size;
} }
bool OverlayWidget::videoShown() const { bool OverlayWidget::videoShown() const {
@ -2289,9 +2261,7 @@ QImage OverlayWidget::transformVideoFrame(QImage frame) const {
const auto rotation = contentRotation(); const auto rotation = contentRotation();
if (rotation != 0) { if (rotation != 0) {
auto transform = QTransform(); frame = RotateFrameImage(std::move(frame), rotation);
transform.rotate(rotation);
frame = frame.transformed(transform);
} }
const auto requiredSize = videoSize(); const auto requiredSize = videoSize();
if (frame.size() != requiredSize) { if (frame.size() != requiredSize) {
@ -2304,13 +2274,9 @@ QImage OverlayWidget::transformVideoFrame(QImage frame) const {
} }
QImage OverlayWidget::transformStaticContent(QPixmap content) const { QImage OverlayWidget::transformStaticContent(QPixmap content) const {
auto image = content.toImage(); return _rotation
if (!_rotation) { ? RotateFrameImage(content.toImage(), _rotation)
return image; : content.toImage();
}
auto transform = QTransform();
transform.rotate(_rotation);
return image.transformed(transform);
} }
void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) { void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) {

View File

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_session.h"
#include "data/data_media_rotation.h"
#include "core/application.h" #include "core/application.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "ui/platform/ui_platform_utility.h" #include "ui/platform/ui_platform_utility.h"
@ -253,8 +255,94 @@ constexpr auto kMsInSecond = 1000;
return result; return result;
} }
Streaming::FrameRequest UnrotateRequest(
const Streaming::FrameRequest &request,
int rotation) {
if (!rotation) {
return request;
}
const auto unrotatedCorner = [&](RectPart corner) {
if (!(request.corners & corner)) {
return RectPart(0);
}
switch (corner) {
case RectPart::TopLeft:
return (rotation == 90)
? RectPart::BottomLeft
: (rotation == 180)
? RectPart::BottomRight
: RectPart::TopRight;
case RectPart::TopRight:
return (rotation == 90)
? RectPart::TopLeft
: (rotation == 180)
? RectPart::BottomLeft
: RectPart::BottomRight;
case RectPart::BottomRight:
return (rotation == 90)
? RectPart::TopRight
: (rotation == 180)
? RectPart::TopLeft
: RectPart::BottomLeft;
case RectPart::BottomLeft:
return (rotation == 90)
? RectPart::BottomRight
: (rotation == 180)
? RectPart::TopRight
: RectPart::TopLeft;
}
Unexpected("Corner in rotateCorner.");
};
auto result = request;
result.outer = FlipSizeByRotation(request.outer, rotation);
result.resize = FlipSizeByRotation(request.resize, rotation);
result.corners = unrotatedCorner(RectPart::TopLeft)
| unrotatedCorner(RectPart::TopRight)
| unrotatedCorner(RectPart::BottomRight)
| unrotatedCorner(RectPart::BottomLeft);
return result;
}
} // namespace } // namespace
QRect RotatedRect(QRect rect, int rotation) {
switch (rotation) {
case 0: return rect;
case 90: return QRect(
rect.y(),
-rect.x() - rect.width(),
rect.height(),
rect.width());
case 180: return QRect(
-rect.x() - rect.width(),
-rect.y() - rect.height(),
rect.width(),
rect.height());
case 270: return QRect(
-rect.y() - rect.height(),
rect.x(),
rect.height(),
rect.width());
}
Unexpected("Rotation in RotatedRect.");
}
bool UsePainterRotation(int rotation) {
return Platform::IsMac() || !(rotation % 180);
}
QSize FlipSizeByRotation(QSize size, int rotation) {
return (((rotation / 90) % 2) == 1)
? QSize(size.height(), size.width())
: size;
}
QImage RotateFrameImage(QImage image, int rotation) {
auto transform = QTransform();
transform.rotate(rotation);
return image.transformed(transform);
}
PipPanel::PipPanel( PipPanel::PipPanel(
QWidget *parent, QWidget *parent,
Fn<void(QPainter&, FrameRequest)> paint) Fn<void(QPainter&, FrameRequest)> paint)
@ -724,6 +812,7 @@ Pip::Pip(
_delegate->pipParentWidget(), _delegate->pipParentWidget(),
[=](QPainter &p, const FrameRequest &request) { paint(p, request); }) [=](QPainter &p, const FrameRequest &request) { paint(p, request); })
, _playbackProgress(std::make_unique<PlaybackProgress>()) , _playbackProgress(std::make_unique<PlaybackProgress>())
, _rotation(document->owner().mediaRotation().get(document))
, _roundRect(ImageRoundRadius::Large, st::radialBg) , _roundRect(ImageRoundRadius::Large, st::radialBg)
, _closeAndContinue(std::move(closeAndContinue)) , _closeAndContinue(std::move(closeAndContinue))
, _destroy(std::move(destroy)) { , _destroy(std::move(destroy)) {
@ -735,15 +824,16 @@ Pip::Pip(
Pip::~Pip() = default; Pip::~Pip() = default;
void Pip::setupPanel() { void Pip::setupPanel() {
const auto size = style::ConvertScale(_instance.info().video.size); const auto size = [&] {
if (size.isEmpty()) { if (!_instance.info().video.size.isEmpty()) {
return _instance.info().video.size;
}
const auto good = _document->goodThumbnail(); const auto good = _document->goodThumbnail();
const auto useGood = (good && good->loaded()); const auto useGood = (good && good->loaded());
const auto original = useGood ? good->size() : _document->dimensions; const auto original = useGood ? good->size() : _document->dimensions;
_panel.setAspectRatio(original.isEmpty() ? QSize(1, 1) : original); return original.isEmpty() ? QSize(1, 1) : original;
} else { }();
_panel.setAspectRatio(size); _panel.setAspectRatio(FlipSizeByRotation(size, _rotation));
}
_panel.setPosition(Deserialize(_delegate->pipLoadGeometry())); _panel.setPosition(Deserialize(_delegate->pipLoadGeometry()));
_panel.show(); _panel.show();
@ -1022,11 +1112,25 @@ void Pip::setupStreaming() {
} }
void Pip::paint(QPainter &p, FrameRequest request) { void Pip::paint(QPainter &p, FrameRequest request) {
const auto image = videoFrameForDirectPaint(request); const auto image = videoFrameForDirectPaint(
UnrotateRequest(request, _rotation));
const auto inner = _panel.inner(); const auto inner = _panel.inner();
p.drawImage( const auto rect = QRect{
QRect{ inner.topLeft(), request.outer / style::DevicePixelRatio() }, inner.topLeft(),
image); request.outer / style::DevicePixelRatio()
};
if (UsePainterRotation(_rotation)) {
if (_rotation) {
p.save();
p.rotate(_rotation);
}
p.drawImage(RotatedRect(rect, _rotation), image);
if (_rotation) {
p.restore();
}
} else {
p.drawImage(rect, RotateFrameImage(image, _rotation));
}
if (_instance.player().ready()) { if (_instance.player().ready()) {
_instance.markFrameShown(); _instance.markFrameShown();
} }
@ -1149,7 +1253,8 @@ void Pip::handleStreamingUpdate(Streaming::Update &&update) {
using namespace Streaming; using namespace Streaming;
update.data.match([&](Information &update) { update.data.match([&](Information &update) {
_panel.setAspectRatio(update.video.size); _panel.setAspectRatio(
FlipSizeByRotation(update.video.size, _rotation));
}, [&](const PreloadedVideo &update) { }, [&](const PreloadedVideo &update) {
updatePlaybackState(); updatePlaybackState();
}, [&](const UpdateVideo &update) { }, [&](const UpdateVideo &update) {

View File

@ -29,6 +29,11 @@ namespace View {
class PlaybackProgress; class PlaybackProgress;
[[nodiscard]] QRect RotatedRect(QRect rect, int rotation);
[[nodiscard]] bool UsePainterRotation(int rotation);
[[nodiscard]] QSize FlipSizeByRotation(QSize size, int rotation);
[[nodiscard]] QImage RotateFrameImage(QImage image, int rotation);
#if defined Q_OS_MAC && !defined OS_MAC_OLD #if defined Q_OS_MAC && !defined OS_MAC_OLD
#define USE_OPENGL_OVERLAY_WIDGET #define USE_OPENGL_OVERLAY_WIDGET
#endif // Q_OS_MAC && !OS_MAC_OLD #endif // Q_OS_MAC && !OS_MAC_OLD
@ -204,6 +209,7 @@ private:
bool _pausedBySeek = false; bool _pausedBySeek = false;
QString _timeAlready, _timeLeft; QString _timeAlready, _timeLeft;
int _timeLeftWidth = 0; int _timeLeftWidth = 0;
int _rotation = 0;
crl::time _seekPositionMs = -1; crl::time _seekPositionMs = -1;
crl::time _lastDurationMs = 0; crl::time _lastDurationMs = 0;
OverState _over = OverState::None; OverState _over = OverState::None;