From a2b6e05cdf54a45b4bcf350bb48845d1feb31a3a Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 18 Dec 2019 20:15:42 +0300
Subject: [PATCH] Fix inline player for rotated videos.

---
 .../streaming/media_streaming_utility.cpp     | 110 ++++++++++++++++--
 .../media/streaming/media_streaming_utility.h |   2 +
 .../streaming/media_streaming_video_track.cpp |  14 ++-
 .../streaming/media_streaming_video_track.h   |   4 +-
 4 files changed, 112 insertions(+), 18 deletions(-)

diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp
index 48f9246b0..297475c95 100644
--- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp
@@ -85,9 +85,14 @@ FFmpeg::AvErrorWrap ReadNextFrame(Stream &stream) {
 	return error;
 }
 
-bool GoodForRequest(const QImage &image, const FrameRequest &request) {
+bool GoodForRequest(
+		const QImage &image,
+		int rotation,
+		const FrameRequest &request) {
 	if (request.resize.isEmpty()) {
 		return true;
+	} else if (rotation != 0) {
+		return false;
 	} else if ((request.radius != ImageRoundRadius::None)
 		&& ((request.corners & RectPart::AllCorners) != 0)) {
 		return false;
@@ -174,8 +179,95 @@ QImage ConvertFrame(
 	return storage;
 }
 
+void PaintFrameOuter(QPainter &p, const QRect &inner, QSize outer) {
+	const auto left = inner.x();
+	const auto right = outer.width() - inner.width() - left;
+	const auto top = inner.y();
+	const auto bottom = outer.height() - inner.height() - top;
+	if (left > 0) {
+		p.fillRect(0, 0, left, outer.height(), st::imageBg);
+	}
+	if (right > 0) {
+		p.fillRect(
+			outer.width() - right,
+			0,
+			right,
+			outer.height(),
+			st::imageBg);
+	}
+	if (top > 0) {
+		p.fillRect(left, 0, inner.width(), top, st::imageBg);
+	}
+	if (bottom > 0) {
+		p.fillRect(
+			left,
+			outer.height() - bottom,
+			inner.width(),
+			bottom,
+			st::imageBg);
+	}
+}
+
+void PaintFrameInner(
+		QPainter &p,
+		QRect to,
+		const QImage &original,
+		int rotation) {
+	const auto rotated = [](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 PaintFrameInner.");
+	};
+
+	PainterHighQualityEnabler hq(p);
+	if (rotation) {
+		p.rotate(rotation);
+	}
+	p.drawImage(rotated(to, rotation), original);
+}
+
+void PaintFrameContent(
+		QPainter &p,
+		const QImage &original,
+		int rotation,
+		const FrameRequest &request) {
+	const auto full = request.outer;
+	const auto to = QRect(
+		(full.width() - request.resize.width()) / 2,
+		(full.height() - request.resize.height()) / 2,
+		request.resize.width(),
+		request.resize.height());
+	PaintFrameOuter(p, to, full);
+	PaintFrameInner(p, to, original, rotation);
+}
+
+void ApplyFrameRounding(QImage &storage, const FrameRequest &request) {
+	if (!(request.corners & RectPart::AllCorners)
+		|| (request.radius == ImageRoundRadius::None)) {
+		return;
+	}
+	Images::prepareRound(storage, request.radius, request.corners);
+}
+
 QImage PrepareByRequest(
 		const QImage &original,
+		int rotation,
 		const FrameRequest &request,
 		QImage storage) {
 	Expects(!request.outer.isEmpty());
@@ -183,16 +275,12 @@ QImage PrepareByRequest(
 	if (!FFmpeg::GoodStorageForFrame(storage, request.outer)) {
 		storage = FFmpeg::CreateFrameStorage(request.outer);
 	}
-	{
-		Painter p(&storage);
-		PainterHighQualityEnabler hq(p);
-		p.drawImage(QRect(QPoint(), request.outer), original);
-	}
-	if ((request.corners & RectPart::AllCorners)
-		&& (request.radius != ImageRoundRadius::None)) {
-		Images::prepareRound(storage, request.radius, request.corners);
-	}
-	// #TODO streaming later full prepare support.
+
+	QPainter p(&storage);
+	PaintFrameContent(p, original, rotation, request);
+	p.end();
+
+	ApplyFrameRounding(storage, request);
 	return storage;
 }
 
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.h b/Telegram/SourceFiles/media/streaming/media_streaming_utility.h
index b2b2a48a2..0b28584e2 100644
--- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.h
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.h
@@ -51,6 +51,7 @@ struct Stream {
 
 [[nodiscard]] bool GoodForRequest(
 	const QImage &image,
+	int rotation,
 	const FrameRequest &request);
 [[nodiscard]] QImage ConvertFrame(
 	Stream &stream,
@@ -59,6 +60,7 @@ struct Stream {
 	QImage storage);
 [[nodiscard]] QImage PrepareByRequest(
 	const QImage &original,
+	int rotation,
 	const FrameRequest &request,
 	QImage storage);
 
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp
index 0c79caa4f..d726dcda7 100644
--- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp
@@ -351,7 +351,7 @@ void VideoTrackObject::presentFrameIfNeeded() {
 			return;
 		}
 
-		VideoTrack::PrepareFrameByRequests(frame);
+		VideoTrack::PrepareFrameByRequests(frame, _stream.rotation);
 
 		Ensures(VideoTrack::IsRasterized(frame));
 	};
@@ -855,7 +855,7 @@ VideoTrack::VideoTrack(
 : _streamIndex(stream.index)
 , _streamTimeBase(stream.timeBase)
 , _streamDuration(stream.duration)
-//, _streamRotation(stream.rotation)
+, _streamRotation(stream.rotation)
 //, _streamAspect(stream.aspect)
 , _shared(std::make_unique<Shared>())
 , _wrapped(
@@ -955,7 +955,7 @@ QImage VideoTrack::frame(
 			unwrapped.updateFrameRequest(instance, request);
 		});
 	}
-	if (GoodForRequest(frame->original, useRequest)) {
+	if (GoodForRequest(frame->original, _streamRotation, useRequest)) {
 		return frame->original;
 	} else if (changed || none || i->second.image.isNull()) {
 		const auto j = none
@@ -975,6 +975,7 @@ QImage VideoTrack::frame(
 		}
 		j->second.image = PrepareByRequest(
 			frame->original,
+			_streamRotation,
 			useRequest,
 			std::move(j->second.image));
 		return j->second.image;
@@ -988,14 +989,16 @@ void VideoTrack::unregisterInstance(not_null<const Instance*> instance) {
 	});
 }
 
-void VideoTrack::PrepareFrameByRequests(not_null<Frame*> frame) {
+void VideoTrack::PrepareFrameByRequests(
+		not_null<Frame*> frame,
+		int rotation) {
 	Expects(!frame->original.isNull());
 
 	const auto begin = frame->prepared.begin();
 	const auto end = frame->prepared.end();
 	for (auto i = begin; i != end; ++i) {
 		auto &prepared = i->second;
-		if (!GoodForRequest(frame->original, prepared.request)) {
+		if (!GoodForRequest(frame->original, rotation, prepared.request)) {
 			auto j = begin;
 			for (; j != i; ++j) {
 				if (j->second.request == prepared.request) {
@@ -1006,6 +1009,7 @@ void VideoTrack::PrepareFrameByRequests(not_null<Frame*> frame) {
 			if (j == i) {
 				prepared.image = PrepareByRequest(
 					frame->original,
+					rotation,
 					prepared.request,
 					std::move(prepared.image));
 			}
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h
index aea570a57..c152adfec 100644
--- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h
@@ -139,7 +139,7 @@ private:
 
 	};
 
-	static void PrepareFrameByRequests(not_null<Frame*> frame);
+	static void PrepareFrameByRequests(not_null<Frame*> frame, int rotation);
 	[[nodiscard]] static bool IsDecoded(not_null<const Frame*> frame);
 	[[nodiscard]] static bool IsRasterized(not_null<const Frame*> frame);
 	[[nodiscard]] static bool IsStale(
@@ -149,7 +149,7 @@ private:
 	const int _streamIndex = 0;
 	const AVRational _streamTimeBase;
 	const crl::time _streamDuration = 0;
-	//const int _streamRotation = 0;
+	const int _streamRotation = 0;
 	//AVRational _streamAspect = kNormalAspect;
 	std::unique_ptr<Shared> _shared;