media_view_overlay_raster.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "media/view/media_view_overlay_raster.h"
  8. #include "ui/painter.h"
  9. #include "media/stories/media_stories_view.h"
  10. #include "media/view/media_view_pip.h"
  11. #include "platform/platform_overlay_widget.h"
  12. #include "styles/style_media_view.h"
  13. namespace Media::View {
  14. OverlayWidget::RendererSW::RendererSW(not_null<OverlayWidget*> owner)
  15. : _owner(owner)
  16. , _transparentBrush(style::TransparentPlaceholder()) {
  17. }
  18. bool OverlayWidget::RendererSW::handleHideWorkaround() {
  19. // This is needed on Windows or Linux,
  20. // because on reopen it blinks with the last shown content.
  21. return _owner->_hideWorkaround != nullptr;
  22. }
  23. void OverlayWidget::RendererSW::paintFallback(
  24. Painter &&p,
  25. const QRegion &clip,
  26. Ui::GL::Backend backend) {
  27. if (handleHideWorkaround()) {
  28. p.setCompositionMode(QPainter::CompositionMode_Source);
  29. p.fillRect(clip.boundingRect(), Qt::transparent);
  30. return;
  31. }
  32. _p = &p;
  33. _clip = &clip;
  34. _clipOuter = clip.boundingRect();
  35. _owner->paint(this);
  36. _p = nullptr;
  37. _clip = nullptr;
  38. }
  39. void OverlayWidget::RendererSW::paintBackground() {
  40. const auto region = _owner->opaqueContentShown()
  41. ? (*_clip - _owner->finalContentRect())
  42. : *_clip;
  43. const auto m = _p->compositionMode();
  44. _p->setCompositionMode(QPainter::CompositionMode_Source);
  45. const auto &bg = _owner->_fullScreenVideo
  46. ? st::mediaviewVideoBg
  47. : st::mediaviewBg;
  48. for (const auto &rect : region) {
  49. _p->fillRect(rect, bg);
  50. }
  51. if (const auto notch = _owner->topNotchSkip()) {
  52. const auto top = QRect(0, 0, _owner->width(), notch);
  53. if (const auto black = top.intersected(_clipOuter); !black.isEmpty()) {
  54. _p->fillRect(black, Qt::black);
  55. }
  56. }
  57. _p->setCompositionMode(m);
  58. }
  59. QRect OverlayWidget::RendererSW::TransformRect(
  60. QRectF geometry,
  61. int rotation) {
  62. const auto center = geometry.center();
  63. const auto rect = ((rotation % 180) == 90)
  64. ? QRectF(
  65. center.x() - geometry.height() / 2.,
  66. center.y() - geometry.width() / 2.,
  67. geometry.height(),
  68. geometry.width())
  69. : geometry;
  70. return QRect(
  71. int(rect.x()),
  72. int(rect.y()),
  73. int(rect.width()),
  74. int(rect.height()));
  75. }
  76. void OverlayWidget::RendererSW::paintTransformedVideoFrame(
  77. ContentGeometry geometry) {
  78. Expects(_owner->_streamed != nullptr);
  79. const auto rotation = int(geometry.rotation);
  80. const auto rect = TransformRect(geometry.rect, rotation);
  81. if (!rect.intersects(_clipOuter)) {
  82. return;
  83. }
  84. paintTransformedImage(_owner->videoFrame(), rect, rotation);
  85. paintControlsFade(rect, geometry);
  86. }
  87. void OverlayWidget::RendererSW::paintTransformedStaticContent(
  88. const QImage &image,
  89. ContentGeometry geometry,
  90. bool semiTransparent,
  91. bool fillTransparentBackground,
  92. int index) {
  93. const auto rotation = int(geometry.rotation);
  94. const auto rect = TransformRect(geometry.rect, rotation);
  95. if (!rect.intersects(_clipOuter)) {
  96. return;
  97. }
  98. if (fillTransparentBackground) {
  99. _p->fillRect(rect, _transparentBrush);
  100. }
  101. if (!image.isNull()) {
  102. paintTransformedImage(image, rect, rotation);
  103. }
  104. paintControlsFade(rect, geometry);
  105. }
  106. void OverlayWidget::RendererSW::paintControlsFade(
  107. QRect content,
  108. const ContentGeometry &geometry) {
  109. auto opacity = geometry.controlsOpacity;
  110. if (geometry.fade > 0.) {
  111. _p->setOpacity(geometry.fade);
  112. _p->fillRect(content, Qt::black);
  113. opacity *= 1. - geometry.fade;
  114. }
  115. _p->setOpacity(opacity);
  116. _p->setClipRect(content);
  117. const auto width = _owner->width();
  118. const auto stories = (_owner->_stories != nullptr);
  119. if (!stories || geometry.topShadowShown) {
  120. const auto flip = !stories && !_owner->topShadowOnTheRight();
  121. const auto &top = stories
  122. ? st::storiesShadowTop
  123. : st::mediaviewShadowTop;
  124. const auto topShadow = stories
  125. ? QRect(
  126. content.topLeft(),
  127. QSize(content.width(), top.height()))
  128. : QRect(
  129. QPoint(flip ? 0 : (width - top.width()), 0),
  130. top.size());
  131. if (topShadow.intersected(content).intersects(_clipOuter)) {
  132. if (stories) {
  133. top.fill(*_p, topShadow);
  134. } else if (flip) {
  135. if (_topShadowCache.isNull()
  136. || _topShadowColor != st::windowShadowFg->c) {
  137. _topShadowColor = st::windowShadowFg->c;
  138. _topShadowCache = top.instance(
  139. _topShadowColor).mirrored(true, false);
  140. }
  141. _p->drawImage(0, 0, _topShadowCache);
  142. } else {
  143. top.paint(*_p, topShadow.topLeft(), width);
  144. }
  145. }
  146. }
  147. const auto &bottom = stories
  148. ? st::storiesShadowBottom
  149. : st::mediaviewShadowBottom;
  150. const auto bottomStart = _owner->height() - geometry.bottomShadowSkip;
  151. const auto bottomShadow = QRect(
  152. QPoint(0, bottomStart - bottom.height()),
  153. QSize(width, bottom.height()));
  154. if (bottomShadow.intersected(content).intersects(_clipOuter)) {
  155. bottom.fill(*_p, bottomShadow);
  156. }
  157. _p->setClipping(false);
  158. _p->setOpacity(1.);
  159. if (bottomStart < content.y() + content.height()) {
  160. _p->fillRect(
  161. content.x(),
  162. bottomStart,
  163. content.width(),
  164. content.y() + content.height() - bottomStart,
  165. QColor(0, 0, 0, 88));
  166. }
  167. }
  168. void OverlayWidget::RendererSW::paintTransformedImage(
  169. const QImage &image,
  170. QRect rect,
  171. int rotation) {
  172. PainterHighQualityEnabler hq(*_p);
  173. if (UsePainterRotation(rotation)) {
  174. if (rotation) {
  175. _p->save();
  176. _p->rotate(rotation);
  177. }
  178. _p->drawImage(RotatedRect(rect, rotation), image);
  179. if (rotation) {
  180. _p->restore();
  181. }
  182. } else {
  183. _p->drawImage(rect, _owner->transformShownContent(image, rotation));
  184. }
  185. }
  186. void OverlayWidget::RendererSW::paintRadialLoading(
  187. QRect inner,
  188. bool radial,
  189. float64 radialOpacity) {
  190. _owner->paintRadialLoadingContent(*_p, inner, radial, radialOpacity);
  191. }
  192. void OverlayWidget::RendererSW::paintThemePreview(QRect outer) {
  193. _owner->paintThemePreviewContent(*_p, outer, _clipOuter);
  194. }
  195. void OverlayWidget::RendererSW::paintDocumentBubble(
  196. QRect outer,
  197. QRect icon) {
  198. if (outer.intersects(_clipOuter)) {
  199. _owner->paintDocumentBubbleContent(*_p, outer, icon, _clipOuter);
  200. if (icon.intersects(_clipOuter)) {
  201. _owner->paintRadialLoading(this);
  202. }
  203. }
  204. }
  205. void OverlayWidget::RendererSW::paintSaveMsg(QRect outer) {
  206. if (outer.intersects(_clipOuter)) {
  207. _owner->paintSaveMsgContent(*_p, outer, _clipOuter);
  208. }
  209. }
  210. void OverlayWidget::RendererSW::paintControlsStart() {
  211. }
  212. void OverlayWidget::RendererSW::paintControl(
  213. Over control,
  214. QRect over,
  215. float64 overOpacity,
  216. QRect inner,
  217. float64 innerOpacity,
  218. const style::icon &icon) {
  219. if (!over.isEmpty() && !over.intersects(_clipOuter)) {
  220. return;
  221. }
  222. if (!over.isEmpty() && overOpacity > 0) {
  223. if (_overControlImage.isNull()) {
  224. validateOverControlImage();
  225. }
  226. _p->setOpacity(overOpacity);
  227. _p->drawImage(over.topLeft(), _overControlImage);
  228. }
  229. if (inner.intersects(_clipOuter)) {
  230. _p->setOpacity(innerOpacity);
  231. icon.paintInCenter(*_p, inner);
  232. }
  233. }
  234. void OverlayWidget::RendererSW::paintFooter(QRect outer, float64 opacity) {
  235. if (outer.intersects(_clipOuter)) {
  236. _owner->paintFooterContent(*_p, outer, _clipOuter, opacity);
  237. }
  238. }
  239. void OverlayWidget::RendererSW::paintCaption(QRect outer, float64 opacity) {
  240. if (outer.intersects(_clipOuter)) {
  241. _owner->paintCaptionContent(*_p, outer, _clipOuter, opacity);
  242. }
  243. }
  244. void OverlayWidget::RendererSW::paintGroupThumbs(
  245. QRect outer,
  246. float64 opacity) {
  247. if (outer.intersects(_clipOuter)) {
  248. _owner->paintGroupThumbsContent(*_p, outer, _clipOuter, opacity);
  249. }
  250. }
  251. void OverlayWidget::RendererSW::paintRoundedCorners(int radius) {
  252. // The RpWindow rounding overlay will do the job.
  253. }
  254. void OverlayWidget::RendererSW::paintStoriesSiblingPart(
  255. int index,
  256. const QImage &image,
  257. QRect rect,
  258. float64 opacity) {
  259. const auto changeOpacity = (opacity != 1.);
  260. if (changeOpacity) {
  261. _p->setOpacity(opacity);
  262. }
  263. _p->drawImage(rect, image);
  264. if (changeOpacity) {
  265. _p->setOpacity(1.);
  266. }
  267. }
  268. void OverlayWidget::RendererSW::validateOverControlImage() {
  269. const auto size = QSize(st::mediaviewIconOver, st::mediaviewIconOver);
  270. const auto alpha = base::SafeRound(kOverBackgroundOpacity * 255);
  271. _overControlImage = QImage(
  272. size * style::DevicePixelRatio(),
  273. QImage::Format_ARGB32_Premultiplied);
  274. _overControlImage.setDevicePixelRatio(style::DevicePixelRatio());
  275. _overControlImage.fill(Qt::transparent);
  276. Painter p(&_overControlImage);
  277. PainterHighQualityEnabler hq(p);
  278. p.setPen(Qt::NoPen);
  279. auto color = OverBackgroundColor();
  280. color.setAlpha(alpha);
  281. p.setBrush(color);
  282. p.drawEllipse(QRect(QPoint(), size));
  283. }
  284. } // namespace Media::View