MythTV  master
videooutwindow.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Daniel Kristjansson, Jens Rehaag 2008
3  *
4  * This class encapsulates some of the video framing information,
5  * so that a VideoOutput class can have multiple concurrent video
6  * windows displayed at any one time.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 // Qt
24 #include <QApplication>
25 
26 // MythtTV
27 #include "mythconfig.h"
28 #include "mythmiscutil.h"
29 #include "osd.h"
30 #include "mythplayer.h"
31 #include "videodisplayprofile.h"
32 #include "decoderbase.h"
33 #include "mythcorecontext.h"
34 #include "videooutwindow.h"
35 
36 // Std
37 #include <cmath>
38 
39 #define LOC QString("VideoWin: ")
40 
41 static float fix_aspect(float raw);
42 static float snap(float value, float snapto, float diff);
43 
49 
51  : m_display(nullptr)
52 {
53  m_dbPipSize = gCoreContext->GetNumSetting("PIPSize", 26);
54 
55  m_dbMove = QPoint(gCoreContext->GetNumSetting("xScanDisplacement", 0),
56  gCoreContext->GetNumSetting("yScanDisplacement", 0));
57  m_dbUseGUISize = gCoreContext->GetBoolSetting("GuiSizeForTV", false);
58 }
59 
60 void VideoOutWindow::ScreenChanged(QScreen */*screen*/)
61 {
63  MoveResize();
64 }
65 
67 {
68  if (!m_display)
69  return;
70 
71  QScreen *screen = m_display->GetCurrentScreen();
72  if (!screen)
73  return;
74 
76  {
77  m_screenGeometry = screen->virtualGeometry();
78  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using all screens %1x%2")
79  .arg(m_screenGeometry.width()).arg(m_screenGeometry.height()));
80  return;
81  }
82 
83  m_screenGeometry = screen->geometry();
84  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using screen %1 %2x%3")
85  .arg(screen->name()).arg(m_screenGeometry.width()).arg(m_screenGeometry.height()));
86 }
87 
99 {
100  // for 'portrait' mode, rotate the 'screen' dimensions
101  float tempdisplayaspect = m_displayAspect;
102  bool rotate = (m_rotation == 90) || (m_rotation == -90);
103  if (rotate)
104  Rotate();
105 
106  // Preset all image placement and sizing variables.
107  m_videoRect = QRect(QPoint(0, 0), m_videoDispDim);
109 
110  // Apply various modifications
114 
115  // Interactive TV (MHEG) embedding
116  if (m_itvResizing)
118 
119  // and switch back
120  if (rotate)
121  Rotate();
122  m_displayAspect = tempdisplayaspect;
123 
125 
126  // TODO fine tune when these are emitted - it is not enough to just check whether the values
127  // have changed
132 }
133 
144 {
145  m_dbMove = QPoint(m_dbMove.y(), m_dbMove.x());
146  float temp = m_dbHorizScale;
148  m_dbVertScale = temp;
149  temp = m_manualHorizScale;
151  m_manualVertScale = temp;
152  m_manualMove = QPoint(m_manualMove.y(), m_manualMove.x());
154 
155  m_displayVisibleRect = QRect(QPoint(m_displayVisibleRect.top(), m_displayVisibleRect.left()),
156  QSize(m_displayVisibleRect.height(), m_displayVisibleRect.width()));
157  m_displayVideoRect = QRect(QPoint(m_displayVideoRect.top(), m_displayVideoRect.left()),
158  QSize(m_displayVideoRect.height(), m_displayVideoRect.width()));
167 }
168 
179 {
180  if (m_dbVertScale > 0)
181  {
182  // Veritcal overscan. Move the Y start point in original image.
183  float tmp = 1.0F - 2.0F * m_dbVertScale;
184  m_videoRect.moveTop(qRound(m_videoRect.height() * m_dbVertScale));
185  m_videoRect.setHeight(qRound(m_videoRect.height() * tmp));
186 
187  // If there is an offset, apply it now that we have a room.
188  int yoff = m_dbMove.y();
189  if (yoff > 0)
190  {
191  // To move the image down, move the start point up.
192  // Don't offset the image more than we have overscanned.
193  yoff = min(m_videoRect.top(), yoff);
194  m_videoRect.moveTop(m_videoRect.top() - yoff);
195  }
196  else if (yoff < 0)
197  {
198  // To move the image up, move the start point down.
199  // Don't offset the image more than we have overscanned.
200  if (abs(yoff) > m_videoRect.top())
201  yoff = 0 - m_videoRect.top();
202  m_videoRect.moveTop(m_videoRect.top() - yoff);
203  }
204  }
205  else if (m_dbVertScale < 0)
206  {
207  // Vertical underscan. Move the starting Y point in the display window.
208  // Use the abolute value of scan factor.
209  float vscanf = fabs(m_dbVertScale);
210  float tmp = 1.0F - 2.0F * vscanf;
211 
212  m_displayVideoRect.moveTop(qRound(m_displayVisibleRect.height() * vscanf) + m_displayVisibleRect.top());
213  m_displayVideoRect.setHeight(qRound(m_displayVisibleRect.height() * tmp));
214 
215  // Now offset the image within the extra blank space created by
216  // underscanning. To move the image down, increase the Y offset
217  // inside the display window.
218  int yoff = m_dbMove.y();
219  if (yoff > 0)
220  {
221  // Don't offset more than we have underscanned.
222  yoff = min(m_displayVideoRect.top(), yoff);
223  m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
224  }
225  else if (yoff < 0)
226  {
227  // Don't offset more than we have underscanned.
228  if (abs(yoff) > m_displayVideoRect.top())
229  yoff = 0 - m_displayVideoRect.top();
230  m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
231  }
232  }
233 
234  // Horizontal.. comments, same as vertical...
235  if (m_dbHorizScale > 0)
236  {
237  float tmp = 1.0F - 2.0F * m_dbHorizScale;
238  m_videoRect.moveLeft(qRound(m_videoDispDim.width() * m_dbHorizScale));
239  m_videoRect.setWidth(qRound(m_videoDispDim.width() * tmp));
240 
241  int xoff = m_dbMove.x();
242  if (xoff > 0)
243  {
244  xoff = min(m_videoRect.left(), xoff);
245  m_videoRect.moveLeft(m_videoRect.left() - xoff);
246  }
247  else if (xoff < 0)
248  {
249  if (abs(xoff) > m_videoRect.left())
250  xoff = 0 - m_videoRect.left();
251  m_videoRect.moveLeft(m_videoRect.left() - xoff);
252  }
253  }
254  else if (m_dbHorizScale < 0)
255  {
256  float hscanf = fabs(m_dbHorizScale);
257  float tmp = 1.0F - 2.0F * hscanf;
258 
259  m_displayVideoRect.moveLeft(qRound(m_displayVisibleRect.width() * hscanf) + m_displayVisibleRect.left());
260  m_displayVideoRect.setWidth(qRound(m_displayVisibleRect.width() * tmp));
261 
262  int xoff = m_dbMove.x();
263  if (xoff > 0)
264  {
265  xoff = min(m_displayVideoRect.left(), xoff);
266  m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
267  }
268  else if (xoff < 0)
269  {
270  if (abs(xoff) > m_displayVideoRect.left())
271  xoff = 0 - m_displayVideoRect.left();
272  m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
273  }
274  }
275 
276 }
277 
282 {
283  if ((m_manualVertScale != 1.0F) || (m_manualHorizScale != 1.0F))
284  {
285  QSize newsz = QSize(qRound(m_displayVideoRect.width() * m_manualHorizScale),
286  qRound(m_displayVideoRect.height() * m_manualVertScale));
287  QSize tmp = (m_displayVideoRect.size() - newsz) / 2;
288  QPoint chgloc = QPoint(tmp.width(), tmp.height());
289  QPoint newloc = m_displayVideoRect.topLeft() + chgloc;
290 
291  m_displayVideoRect = QRect(newloc, newsz);
292  }
293 
294  if (m_manualMove.y())
295  {
296  int move_vert = m_manualMove.y() * m_displayVideoRect.height() / 100;
297  m_displayVideoRect.moveTop(m_displayVideoRect.top() + move_vert);
298  }
299 
300  if (m_manualMove.x())
301  {
302  int move_horiz = m_manualMove.x() * m_displayVideoRect.width() / 100;
303  m_displayVideoRect.moveLeft(m_displayVideoRect.left() + move_horiz);
304  }
305 }
306 
307 // Code should take into account the aspect ratios of both the video as
308 // well as the actual screen to allow proper letterboxing to take place.
310 {
311  float disp_aspect = fix_aspect(GetDisplayAspect());
312  float aspect_diff = disp_aspect - m_videoAspectOverride;
313  bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.02F;
314  bool nomatch_with_fill = !aspects_match && ((kAdjustFill_HorizontalStretch == m_adjustFill) ||
316  bool nomatch_without_fill = (!aspects_match) && !nomatch_with_fill;
317 
318  // Adjust for video/display aspect ratio mismatch
319  if (nomatch_with_fill && (disp_aspect > m_videoAspectOverride))
320  {
321  int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
322  m_displayVideoRect.moveTop(m_displayVideoRect.top() + (m_displayVideoRect.height() - pixNeeded) / 2);
323  m_displayVideoRect.setHeight(pixNeeded);
324  }
325  else if (nomatch_with_fill)
326  {
327  int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
328  m_displayVideoRect.moveLeft(m_displayVideoRect.left() + (m_displayVideoRect.width() - pixNeeded) / 2);
329  m_displayVideoRect.setWidth(pixNeeded);
330  }
331  else if (nomatch_without_fill && (disp_aspect > m_videoAspectOverride))
332  {
333  int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
334  m_displayVideoRect.moveLeft(m_displayVideoRect.left() + (m_displayVideoRect.width() - pixNeeded) / 2);
335  m_displayVideoRect.setWidth(pixNeeded);
336  }
337  else if (nomatch_without_fill)
338  {
339  int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
340  m_displayVideoRect.moveTop(m_displayVideoRect.top() + (m_displayVideoRect.height() - pixNeeded) / 2);
341  m_displayVideoRect.setHeight(pixNeeded);
342  }
343 
344  // Process letterbox zoom modes
346  {
347  // Zoom mode -- Expand by 4/3 and overscan.
348  // 1/6 of original is 1/8 of new
349  m_displayVideoRect = QRect(
350  m_displayVideoRect.left() - (m_displayVideoRect.width() / 6),
351  m_displayVideoRect.top() - (m_displayVideoRect.height() / 6),
352  m_displayVideoRect.width() * 4 / 3,
353  m_displayVideoRect.height() * 4 / 3);
354  }
355  else if (m_adjustFill == kAdjustFill_Half)
356  {
357  // Zoom mode -- Expand by 7/6 and overscan.
358  // Intended for eliminating the top bars on 14:9 material.
359  // Also good compromise for 4:3 material on 16:9 screen.
360  // Expanding by 7/6, so remove 1/6 of original from overscan;
361  // take half from each side, so remove 1/12.
362  m_displayVideoRect = QRect(
363  m_displayVideoRect.left() - (m_displayVideoRect.width() / 12),
364  m_displayVideoRect.top() - (m_displayVideoRect.height() / 12),
365  m_displayVideoRect.width() * 7 / 6,
366  m_displayVideoRect.height() * 7 / 6);
367  }
369  {
370  // Horizontal Stretch mode -- 1/6 of original is 1/8 of new
371  // Intended to be used to eliminate side bars on 4:3 material
372  // encoded to 16:9.
373  m_displayVideoRect.moveLeft(
374  m_displayVideoRect.left() - (m_displayVideoRect.width() / 6));
375 
376  m_displayVideoRect.setWidth(m_displayVideoRect.width() * 4 / 3);
377  }
379  {
380  // Vertical Stretch mode -- 1/6 of original is 1/8 of new
381  // Intended to be used to eliminate top/bottom bars on 16:9
382  // material encoded to 4:3.
383  m_displayVideoRect.moveTop(
384  m_displayVideoRect.top() - (m_displayVideoRect.height() / 6));
385 
386  m_displayVideoRect.setHeight(m_displayVideoRect.height() * 4 / 3);
387  }
388  else if (m_adjustFill == kAdjustFill_VerticalFill && m_displayVideoRect.height() > 0)
389  {
390  // Video fills screen vertically. May be cropped left and right
391  float factor = static_cast<float>(m_displayVisibleRect.height()) / static_cast<float>(m_displayVideoRect.height());
392  QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
393  qRound(m_displayVideoRect.height() * factor));
394  QSize temp = (m_displayVideoRect.size() - newsize) / 2;
395  QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
396  m_displayVideoRect = QRect(newloc, newsize);
397  }
399  {
400  // Video fills screen horizontally. May be cropped top and bottom
401  float factor = static_cast<float>(m_displayVisibleRect.width()) /
402  static_cast<float>(m_displayVideoRect.width());
403  QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
404  qRound(m_displayVideoRect.height() * factor));
405  QSize temp = (m_displayVideoRect.size() - newsize) / 2;
406  QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
407  m_displayVideoRect = QRect(newloc, newsize);
408  }
409 }
410 
411 bool VideoOutWindow::Init(const QSize &VideoDim, const QSize &VideoDispDim,
412  float Aspect, const QRect &WindowRect,
414 {
415  if (!m_display && Display)
416  {
417  m_display = Display;
419  }
420 
421  if (m_display)
422  {
423  QString dummy;
424  m_displayAspect = static_cast<float>(m_display->GetAspectRatio(dummy));
425  }
426 
427  // Refresh the geometry in case the video mode has changed
429 
430  // N.B. we are always confined to the window size so use that for the initial
431  // displayVisibleRect
432  m_windowRect = m_displayVisibleRect = WindowRect;
433 
434  int pbp_width = m_displayVisibleRect.width() / 2;
436  m_displayVisibleRect.setWidth(pbp_width);
437 
438  if (m_pipState == kPBPRight)
439  m_displayVisibleRect.moveLeft(pbp_width);
440 
441  m_videoDispDim = Fix1088(VideoDispDim);
442  m_videoDim = VideoDim;
443  m_videoRect = QRect(m_displayVisibleRect.topLeft(), m_videoDispDim);
444 
445  if (m_pipState > kPIPOff)
446  {
449  }
450  else
451  {
454  }
455  m_embedding = false;
456  SetVideoAspectRatio(Aspect);
457  MoveResize();
458  return true;
459 }
460 
462 {
463  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window Rect: %1x%2+%3+%4")
464  .arg(m_windowRect.width()).arg(m_windowRect.height())
465  .arg(m_windowRect.left()).arg(m_windowRect.top()));
466  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display Rect: %1x%2+%3+%4 Aspect: %5")
467  .arg(m_displayVideoRect.width()).arg(m_displayVideoRect.height())
468  .arg(m_displayVideoRect.left()).arg(m_displayVideoRect.top())
469  .arg(static_cast<qreal>(fix_aspect(GetDisplayAspect()))));
470  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Video Rect: %1x%2+%3+%4 Aspect: %5")
471  .arg(m_videoRect.width()).arg(m_videoRect.height())
472  .arg(m_videoRect.left()).arg(m_videoRect.top())
473  .arg(static_cast<qreal>(m_videoAspectOverride)));
474 }
475 
484 {
485  m_videoAspect = aspect;
487 }
488 
495 {
496  if (!qFuzzyCompare(Aspect, m_videoAspect))
497  {
498  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect ratio: '%1'")
499  .arg(static_cast<double>(Aspect)));
500  SetVideoAspectRatio(Aspect);
501  MoveResize();
502  }
503 }
504 
510 void VideoOutWindow::InputChanged(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect)
511 {
512  if (Aspect < 0.0F)
513  Aspect = m_videoAspect;
514 
515  QSize newvideodispdim = Fix1088(VideoDispDim);
516 
517  if (!((VideoDim == m_videoDim) && (newvideodispdim == m_videoDispDim) &&
518  qFuzzyCompare(Aspect + 100.0F, m_videoAspect + 100.0F)))
519  {
520  m_videoDispDim = newvideodispdim;
521  m_videoDim = VideoDim;
522  SetVideoAspectRatio(Aspect);
523  LOG(VB_PLAYBACK, LOG_INFO, LOC +
524  QString("New video parameters: Size %1x%2 DisplaySize: %3x%4 Aspect: %5")
525  .arg(m_videoDim.width()).arg(m_videoDim.height())
526  .arg(m_videoDispDim.width()).arg(m_videoDispDim.height())
527  .arg(static_cast<double>(m_videoAspect)));
528  MoveResize();
529  }
530 }
531 
532 QSize VideoOutWindow::Fix1088(QSize Dimensions)
533 {
534  QSize result = Dimensions;
535  // 544 represents a 1088 field
536  if (result.width() == 1920 || result.width() == 1440)
537  {
538  if (result.height() == 1088)
539  result.setHeight(1080);
540  else if (result.height() == 544)
541  result.setHeight(540);
542  }
543  return result;
544 }
545 
551 {
552  return { QPoint(0, 0), m_videoDispDim };
553 }
554 
562 {
564  AdjustFill = static_cast<AdjustFillMode>((m_adjustFill + 1) % kAdjustFill_END);
565  if (m_adjustFill != AdjustFill)
566  {
568  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New fill mode: '%1'").arg(toString(m_adjustFill)));
569  MoveResize();
570  }
571 }
572 
577 {
578  float oldvert = m_dbVertScale;
579  float oldhoriz = m_dbHorizScale;
580 
581  if (Change)
582  {
583  m_dbVertScale = gCoreContext->GetNumSetting("VertScanPercentage", 0) * 0.01F;
584  m_dbHorizScale = gCoreContext->GetNumSetting("HorizScanPercentage", 0) * 0.01F;
585  m_dbScalingAllowed = true;
586  }
587  else
588  {
589  m_dbVertScale = 0.0F;
590  m_dbHorizScale = 0.0F;
591  m_dbScalingAllowed = false;
592  }
593 
594  if (!(qFuzzyCompare(oldvert + 100.0F, m_dbVertScale + 100.0F) &&
595  qFuzzyCompare(oldhoriz + 100.0F, m_dbHorizScale + 100.0F)))
596  {
597  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Over/underscan. V: %1, H: %2")
598  .arg(static_cast<double>(m_dbVertScale)).arg(static_cast<double>(m_dbHorizScale)));
599  MoveResize();
600  }
601 }
602 
603 void VideoOutWindow::SetDisplayAspect(float DisplayAspect)
604 {
605  if (!qFuzzyCompare(DisplayAspect + 10.0F, m_displayAspect + 10.0F))
606  {
607  LOG(VB_GENERAL, LOG_INFO, LOC + QString("New display aspect: %1")
608  .arg(static_cast<double>(DisplayAspect)));
609  m_displayAspect = DisplayAspect;
610  MoveResize();
611  }
612 }
613 
615 {
616  if (Size != m_windowRect.size())
617  {
618  QRect rect(m_windowRect.topLeft(), Size);
619  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New window rect: %1x%2+%3+%4")
620  .arg(rect.width()).arg(rect.height()).arg(rect.left()).arg(rect.top()));
622  MoveResize();
623  }
624 }
625 
627 {
628  QRect oldrect = m_itvDisplayVideoRect;
629  if (Rect.isEmpty())
630  {
631  m_itvResizing = false;
632  m_itvDisplayVideoRect = QRect();
633  }
634  else
635  {
636  m_itvResizing = true;
637  m_itvDisplayVideoRect = Rect;
638  }
639  if (m_itvDisplayVideoRect != oldrect)
640  {
641  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New ITV display rect: %1x%2+%3+%4")
642  .arg(m_itvDisplayVideoRect.width()).arg(m_itvDisplayVideoRect.height())
643  .arg(m_itvDisplayVideoRect.left()).arg(m_itvDisplayVideoRect.right()));
644  MoveResize();
645  }
646 }
647 
652 void VideoOutWindow::SetRotation(int Rotation)
653 {
654  if (Rotation == m_rotation)
655  return;
656  if ((Rotation < -180) || (Rotation > 180))
657  return;
658 
659  m_rotation = Rotation;
660  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New rotation: %1").arg(m_rotation));
661  MoveResize();
662 }
663 
667 void VideoOutWindow::ResizeDisplayWindow(const QRect &Rect, bool SaveVisibleRect)
668 {
669  if (SaveVisibleRect)
671  m_displayVisibleRect = Rect;
672  MoveResize();
673 }
674 
680 void VideoOutWindow::EmbedInWidget(const QRect &Rect)
681 {
682  if (m_embedding && (Rect == m_embeddingRect))
683  return;
684  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New embedding rect: %1x%2+%3+%4")
685  .arg(Rect.width()).arg(Rect.height()).arg(Rect.left()).arg(Rect.top()));
686  m_embeddingRect = Rect;
687  bool savevisiblerect = !m_embedding;
688  m_embedding = true;
689  m_displayVideoRect = Rect;
690  ResizeDisplayWindow(m_displayVideoRect, savevisiblerect);
691 }
692 
698 {
699  if (!m_embedding)
700  return;
701  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Stopped embedding");
702  m_embeddingRect = QRect();
704  m_embedding = false;
705  MoveResize();
706 }
707 
714 QRect VideoOutWindow::GetVisibleOSDBounds(float &VisibleAspect,
715  float &FontScaling,
716  float ThemeAspect) const
717 {
718  float dv_w = ((static_cast<float>(m_videoDispDim.width())) / m_displayVideoRect.width());
719  float dv_h = ((static_cast<float>(m_videoDispDim.height())) / m_displayVideoRect.height());
720 
721  int right_overflow = max((m_displayVideoRect.width() + m_displayVideoRect.left()) - m_displayVisibleRect.width(), 0);
722  int lower_overflow = max((m_displayVideoRect.height() + m_displayVideoRect.top()) - m_displayVisibleRect.height(), 0);
723 
724  bool isPBP = (kPBPLeft == m_pipState || kPBPRight == m_pipState);
725  if (isPBP)
726  {
727  right_overflow = 0;
728  lower_overflow = 0;
729  }
730 
731  // top left and bottom right corners respecting letterboxing
732  QPoint tl = QPoint((static_cast<int>(max(-m_displayVideoRect.left(), 0) * dv_w)) & ~1,
733  (static_cast<int>(max(-m_displayVideoRect.top(), 0) * dv_h)) & ~1);
734  QPoint br = QPoint(static_cast<int>(floor(m_videoDispDim.width() - (right_overflow * dv_w))),
735  static_cast<int>(floor(m_videoDispDim.height() - (lower_overflow * dv_h))));
736  // adjust for overscan
737  if ((m_dbVertScale > 0.0F) || (m_dbHorizScale > 0.0F))
738  {
739  QRect v(tl, br);
740  float xs = (m_dbHorizScale > 0.0F) ? m_dbHorizScale : 0.0F;
741  float ys = (m_dbVertScale > 0.0F) ? m_dbVertScale : 0.0F;
742  QPoint s(qRound((v.width() * xs)), qRound((v.height() * ys)));
743  tl += s;
744  br -= s;
745  }
746  // Work around Qt bug, QRect(QPoint(0,0), QPoint(0,0)) has area 1.
747  QRect vb(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y());
748 
749  // The calculation is completely bogus if the video is not centered
750  // which happens in the EPG, where we don't actually care about the OSD.
751  // So we just make sure the width and height are positive numbers
752  vb = QRect(vb.x(), vb.y(), abs(vb.width()), abs(vb.height()));
753 
754  // set the physical aspect ratio of the displayable area
755  const float dispPixelAdj = m_displayVisibleRect.width() ?
757  / m_displayVisibleRect.width() : 1.F;
758 
759  float vs = m_videoRect.height() ? static_cast<float>(m_videoRect.width()) / m_videoRect.height() : 1.0F;
760  VisibleAspect = ThemeAspect / dispPixelAdj * (m_videoAspectOverride > 0.0F ? vs / m_videoAspectOverride : 1.F);
761 
762  if (ThemeAspect > 0.0F)
763  {
764  // now adjust for scaling of the video on the size
765  float tmp = sqrtf(2.0F/(sq(VisibleAspect / ThemeAspect) + 1.0F));
766  if (tmp > 0.0F)
767  FontScaling = 1.0F / tmp;
768  // now adjust for aspect ratio effect on font size
769  // (should be in osd.cpp?)
770  FontScaling *= sqrtf(m_videoAspectOverride / ThemeAspect);
771  }
772 
773  if (isPBP)
774  FontScaling *= 0.65F;
775  return vb;
776 }
777 
785 {
786  if (m_pipState > kPIPOff)
787  return;
788 
789  if (AspectMode == kAspect_Toggle)
790  AspectMode = static_cast<AspectOverrideMode>(((m_videoAspectOverrideMode + 1) % kAspect_END));
791 
792  if (m_videoAspectOverrideMode != AspectMode)
793  {
794  m_videoAspectOverrideMode = AspectMode;
795  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect override: '%1'")
798  MoveResize();
799  }
800 }
801 
807 {
808  if (IsEmbedding())
809  return false;
810 
811  return m_displayVideoRect.contains(m_windowRect);
812 }
813 
818 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
820 {
821  QRegion visible(m_windowRect);
822  QRegion video(m_displayVideoRect);
823  return visible.subtracted(video);
824 }
825 #endif
826 
827 /*
828  * \brief Determines PIP Window size and Position.
829  */
831  PIPLocation Location, MythPlayer *PiPPlayer, bool DoPixelAdjustment) const
832 {
833  QRect position;
834 
835  float pipVideoAspect = PiPPlayer ? PiPPlayer->GetVideoAspect() : (4.0F / 3.0F);
836  int tmph = (m_displayVisibleRect.height() * m_dbPipSize) / 100;
837  float pixel_adj = 1.0F;
838  if (DoPixelAdjustment)
839  {
840  pixel_adj = (static_cast<float>(m_displayVisibleRect.width()) /
841  static_cast<float>(m_displayVisibleRect.height())) / m_displayAspect;
842  }
843  position.setHeight(tmph);
844  position.setWidth(qRound((tmph * pipVideoAspect * pixel_adj)));
845 
846  int xoff = qRound(m_displayVisibleRect.width() * 0.06);
847  int yoff = qRound(m_displayVisibleRect.height() * 0.06);
848  switch (Location)
849  {
850  case kPIP_END:
851  case kPIPTopLeft:
852  break;
853  case kPIPBottomLeft:
854  yoff = m_displayVisibleRect.height() - position.height() - yoff;
855  break;
856  case kPIPTopRight:
857  xoff = m_displayVisibleRect.width() - position.width() - xoff;
858  break;
859  case kPIPBottomRight:
860  xoff = m_displayVisibleRect.width() - position.width() - xoff;
861  yoff = m_displayVisibleRect.height() - position.height() - yoff;
862  break;
863  }
864  position.translate(xoff, yoff);
865  return position;
866 }
867 
873 {
874  float oldvertscale = m_manualVertScale;
875  float oldhorizscale = m_manualHorizScale;
876  QPoint oldmove = m_manualMove;
877 
878  const float zf = 0.02F;
879  if (kZoomHome == Direction)
880  {
881  m_manualVertScale = 1.0F;
882  m_manualHorizScale = 1.0F;
883  m_manualMove = QPoint(0, 0);
884  }
885  else if (kZoomIn == Direction)
886  {
889  {
890  m_manualHorizScale += zf;
891  m_manualVertScale += zf;
892  }
893  }
894  else if (kZoomOut == Direction)
895  {
898  {
899  m_manualHorizScale -= zf;
900  m_manualVertScale -= zf;
901  }
902  }
903  else if (kZoomAspectUp == Direction)
904  {
907  {
908  m_manualHorizScale += zf;
909  m_manualVertScale -= zf;
910  }
911  }
912  else if (kZoomAspectDown == Direction)
913  {
916  {
917  m_manualHorizScale -= zf;
918  m_manualVertScale += zf;
919  }
920  }
921  else if (kZoomVerticalIn == Direction)
922  {
924  m_manualVertScale += zf;
925  }
926  else if (kZoomVerticalOut == Direction)
927  {
929  m_manualVertScale -= zf;
930  }
931  else if (kZoomHorizontalIn == Direction)
932  {
934  m_manualHorizScale += zf;
935  }
936  else if (kZoomHorizontalOut == Direction)
937  {
939  m_manualHorizScale -= zf;
940  }
941  else if (kZoomUp == Direction && (m_manualMove.y() < +kManualZoomMaxMove))
942  m_manualMove.setY(m_manualMove.y() + 1);
943  else if (kZoomDown == Direction && (m_manualMove.y() > -kManualZoomMaxMove))
944  m_manualMove.setY(m_manualMove.y() - 1);
945  else if (kZoomLeft == Direction && (m_manualMove.x() < +kManualZoomMaxMove))
946  m_manualMove.setX(m_manualMove.x() + 2);
947  else if (kZoomRight == Direction && (m_manualMove.x() > -kManualZoomMaxMove))
948  m_manualMove.setX(m_manualMove.x() - 2);
949 
950  m_manualVertScale = snap(m_manualVertScale, 1.0F, zf / 2);
951  m_manualHorizScale = snap(m_manualHorizScale, 1.0F, zf / 2);
952 
953  if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
954  qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
955  {
956  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New zoom: Offset %1x%2 HScale %3 VScale %4")
957  .arg(m_manualMove.x()).arg(m_manualMove.y())
958  .arg(static_cast<double>(m_manualHorizScale))
959  .arg(static_cast<double>(m_manualVertScale)));
960  MoveResize();
961  }
962 }
963 
965 {
966  float oldvertscale = m_manualVertScale;
967  float oldhorizscale = m_manualHorizScale;
968  QPoint oldmove = m_manualMove;
969 
970  if (m_bottomLine)
971  {
972  m_manualMove.setX(0);
973  m_manualMove.setY(0);
974  m_manualHorizScale = 1.0;
975  m_manualVertScale = 1.0;
976  m_bottomLine = false;
977  }
978  else
979  {
980  const float zf = 0.02F;
981  m_manualMove.setX(gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0));
982  m_manualMove.setY(gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5));
983  float h = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) / 100.0F;
984  m_manualHorizScale = snap(h, 1.0F, zf / 2.0F);
985  float v = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) / 100.0F;
986  m_manualVertScale = snap(v, 1.0F, zf / 2.0F);
987  m_bottomLine = true;
988  }
989 
990  if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
991  qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
992  {
993  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New custom zoom: Offset %1x%2 HScale %3 VScale %4")
994  .arg(m_manualMove.x()).arg(m_manualMove.y())
995  .arg(static_cast<double>(m_manualHorizScale))
996  .arg(static_cast<double>(m_manualVertScale)));
997  MoveResize();
998  }
999 }
1000 
1002 {
1003  gCoreContext->SaveSetting("OSDMoveXBottomLine", m_manualMove.x());
1004  gCoreContext->SaveSetting("OSDMoveYBottomLine", m_manualMove.y());
1005  gCoreContext->SaveSetting("OSDScaleHBottomLine", static_cast<int>(m_manualHorizScale * 100.0F));
1006  gCoreContext->SaveSetting("OSDScaleVBottomLine", static_cast<int>(m_manualVertScale * 100.0F));
1007 }
1008 
1010 {
1011  return tr("Zoom %1x%2 @ (%3,%4)")
1012  .arg(static_cast<double>(m_manualHorizScale), 0, 'f', 2)
1013  .arg(static_cast<double>(m_manualVertScale), 0, 'f', 2)
1014  .arg(m_manualMove.x()).arg(m_manualMove.y());
1015 }
1016 
1018 static float fix_aspect(float raw)
1019 {
1020  // Check if close to 4:3
1021  if (fabs(raw - 1.333333F) < 0.05F)
1022  raw = 1.333333F;
1023 
1024  // Check if close to 16:9
1025  if (fabs(raw - 1.777777F) < 0.05F)
1026  raw = 1.777777F;
1027 
1028  return raw;
1029 }
1030 
1032 {
1033  if (m_pipState != Setting)
1034  {
1035  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetPIPState: %1").arg(toString(Setting)));
1036  m_pipState = Setting;
1037  }
1038 }
1039 
1040 static float snap(float value, float snapto, float diff)
1041 {
1042  if ((value + diff > snapto) && (value - diff < snapto))
1043  return snapto;
1044  return value;
1045 }
void ToggleAspectOverride(AspectOverrideMode AspectMode=kAspect_Toggle)
Enforce different aspect ration than detected, then calls VideoAspectRatioChanged(float) to apply the...
QRegion GetBoundingRegion(void) const
Return the region of DisplayVisibleRect that lies outside of DisplayVideoRect.
float GetVideoAspect(void) const
Definition: mythplayer.h:212
QRect m_displayVideoRect
Pixel rectangle in display window into which video_rect maps to.
static float fix_aspect(float raw)
Correct for rounding errors.
void PopulateGeometry(void)
QRect GetTotalOSDBounds(void) const
Returns total OSD bounds.
bool Init(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect, const QRect &WindowRect, AspectOverrideMode AspectOverride, AdjustFillMode AdjustFill, MythDisplay *Display)
QString toString(MarkTypes type)
void VideoRectsChanged(const QRect &DisplayVideoRect, const QRect &VideoRect)
void Zoom(ZoomDirection Direction)
Sets up zooming into to different parts of the video.
void SaveSetting(const QString &key, int newValue)
bool m_dbScalingAllowed
disable this to prevent overscan/underscan
float sq(float a)
Definition: mythmiscutil.h:57
QRect m_tmpDisplayVisibleRect
Used to save the display_visible_rect for restoration after video embedding ends.
void ResizeDisplayWindow(const QRect &Rect, bool SaveVisibleRect)
Resize Display Window.
void Rotate(void)
Adjust various settings to facilitate portrait mode calculations.
bool m_dbUseGUISize
Use the gui size for video window.
float m_videoAspect
Physical aspect ratio of video.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
float get_aspect_override(AspectOverrideMode Aspectmode, float Original)
PIPState m_pipState
float m_videoAspectOverride
Normally this is the same as videoAspect, but may not be if the user has toggled the aspect override ...
QRect GetPIPRect(PIPLocation Location, MythPlayer *PiPPlayer=nullptr, bool DoPixelAdjustment=true) const
void SetVideoAspectRatio(float Aspect)
Sets VideoOutWindow::video_aspect to aspect, and sets VideoOutWindow::overriden_video_aspect if aspec...
static const float kManualZoomMinVerticalZoom
QScreen * GetCurrentScreen(void)
Return a pointer to the screen to use.
static guint32 * tmp
Definition: goom_core.c:35
float GetDisplayAspect(void) const
QRect m_windowRect
Rectangle describing QWidget bounds.
void WindowRectChanged(const QRect &WindowRect)
static QSize Fix1088(QSize Dimensions)
QRect m_embeddingRect
Embedded video rectangle.
void EmbedInWidget(const QRect &Rect)
Tells video output to embed video in an existing window.
double GetAspectRatio(QString &Source, bool IgnoreModeOverride=false)
Returns current screen aspect ratio.
AspectOverrideMode
Definition: videoouttypes.h:44
QRect m_displayVisibleRect
Visible portion of display window in pixels.
QSize m_videoDim
Pixel dimensions of video buffer.
QRect GetVisibleOSDBounds(float &VisibleAspect, float &FontScaling, float ThemeAspect) const
Returns visible portions of total OSD bounds.
float m_dbVertScale
Vertical Overscan/Underscan percentage.
void SaveBottomLine(void)
QRect m_itvDisplayVideoRect
void CurrentScreenChanged(QScreen *qScreen)
float m_manualVertScale
Manually applied vertical scaling.
void VideoAspectRatioChanged(float Aspect)
Calls SetVideoAspectRatio(float aspect), then calls MoveResize() to apply changes.
void SetWindowSize(QSize Size)
void VisibleRectChanged(const QRect &DisplayVisibleRect)
float m_dbHorizScale
Horizontal Overscan/Underscan percentage.
MythDisplay * m_display
bool IsEmbedding(void) const
static const int kManualZoomMaxMove
static HostComboBoxSetting * AspectOverride()
void SetPIPState(PIPState Setting)
static float snap(float value, float snapto, float diff)
void ScreenChanged(QScreen *screen)
void SetDisplayAspect(float DisplayAspect)
bool VideoIsFullScreen(void) const
Check whether the video display rect covers the entire window/framebuffer.
void PrintMoveResizeDebug(void)
PIPLocation
Definition: videoouttypes.h:17
AdjustFillMode
Definition: videoouttypes.h:55
QRect m_videoRect
Pixel rectangle in video frame to display.
int GetNumSetting(const QString &key, int defaultval=0)
QPoint m_dbMove
Percentage move from database.
AspectOverrideMode m_videoAspectOverrideMode
AspectOverrideMode to use to modify overriden_video_aspect.
bool GetBoolSetting(const QString &key, bool defaultval=false)
QSize m_videoDispDim
Pixel dimensions of video display area.
PIPState
Definition: videoouttypes.h:8
static int GetScreenCount(void)
bool m_embedding
State variables.
void ToggleMoveBottomLine(void)
static const float kManualZoomMinHorizontalZoom
static const float kManualZoomMaxVerticalZoom
void MoveResize(void)
performs all the calculations for video framing and any resizing.
void VideoSizeChanged(const QSize &VideoDim, const QSize &VideoDispDim)
void ToggleAdjustFill(AdjustFillMode AdjustFillMode=kAdjustFill_Toggle)
Sets up letterboxing for various standard video frame and monitor dimensions, then calls MoveResize()...
void ApplyLetterboxing(void)
static const float kManualZoomMaxHorizontalZoom
QString GetZoomString(void) const
int m_dbPipSize
percentage of full window to use for PiP
void InputChanged(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect)
Tells video output to discard decoded frames and wait for new ones.
QRect m_screenGeometry
Full screen geometry.
#define LOC
AdjustFillMode m_adjustFill
Zoom mode.
float m_displayAspect
Physical aspect ratio of playback window.
ZoomDirection
Definition: videoouttypes.h:26
void SetRotation(int Rotation)
Set the rotation in degrees.
void StopEmbedding(void)
Tells video output to stop embedding video in an existing window.
static bool SpanAllScreens(void)
Return true if the MythTV windows should span all screens.
void ApplyManualScaleAndMove(void)
Apply scales and moves from "Zoom Mode" settings.
static HostComboBoxSetting * AdjustFill()
void ApplyDBScaleAndMove(void)
Apply scales and moves for "Overscan" and "Underscan" DB settings.
QPoint m_manualMove
Manually applied percentage move.
void SetITVResize(QRect Rect)
float m_manualHorizScale
Manually applied horizontal scaling.
void SetVideoScalingAllowed(bool Change)
Disable or enable underscan/overscan.
QMap< QString, bool > Setting
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:41