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 #include <cmath>
24 
25 #include <QDesktopWidget>
26 #include <QApplication>
27 
28 #include "mythconfig.h"
29 
30 #include "videooutwindow.h"
31 #include "mythmiscutil.h"
32 #include "osd.h"
33 #include "mythplayer.h"
34 #include "videodisplayprofile.h"
35 #include "decoderbase.h"
36 #include "mythcorecontext.h"
37 #include "dithertable.h"
38 
39 extern "C" {
40 #include "libavcodec/avcodec.h"
41 }
42 
43 #include "filtermanager.h"
44 
45 static QSize fix_alignment(QSize raw);
46 static float fix_aspect(float raw);
47 static float snap(float value, float snapto, float diff);
48 
54 
56  // DB settings
57  db_move(0, 0), db_scale_horiz(0.0f), db_scale_vert(0.0f),
58  db_pip_size(26),
59  db_scaling_allowed(true),
60 
61  using_xinerama(false), screen_num(0), screen_geom(0, 0, 1024, 768),
62 
63  // Manual Zoom
64  mz_scale_v(1.0f), mz_scale_h(1.0f), mz_move(0, 0),
65 
66  // Physical dimensions
67  display_dim(400, 300), display_aspect(1.3333f),
68 
69  // Video dimensions
70  video_dim(640, 480), video_disp_dim(640, 480),
71  video_dim_act(640, 480), video_aspect(1.3333f),
72 
73  // Aspect override
74  overriden_video_aspect(1.3333f), aspectoverride(kAspect_Off),
75 
76  // Adjust Fill
77  adjustfill(kAdjustFill_Off),
78 
79  // Screen settings
80  video_rect(0, 0, 0, 0),
81  display_video_rect(0, 0, 0, 0),
82  display_visible_rect(0, 0, 0, 0),
83  tmp_display_visible_rect(0, 0, 0, 0),
84  embedding_rect(QRect()),
85 
86  // Various state variables
87  embedding(false), needrepaint(false),
88  allowpreviewepg(true), bottomline(false),
89  pip_state(kPIPOff)
90 {
91  db_pip_size = gCoreContext->GetNumSetting("PIPSize", 26);
92 
93  db_move = QPoint(gCoreContext->GetNumSetting("xScanDisplacement", 0),
94  gCoreContext->GetNumSetting("yScanDisplacement", 0));
95  db_use_gui_size = gCoreContext->GetBoolSetting("GuiSizeForTV", false);
96 
98 }
99 
101 {
102  qApp->processEvents();
103  QDesktopWidget *desktop = nullptr;
104  if (qobject_cast<QApplication*>(qApp))
105  desktop = QApplication::desktop();
106 
107  if (desktop)
108  {
109  screen_num = desktop->primaryScreen();
111  if (using_xinerama)
112  {
113  screen_num = gCoreContext->GetNumSetting("XineramaScreen", screen_num);
114  if (screen_num >= desktop->screenCount())
115  screen_num = 0;
116  }
117 
118  screen_geom = desktop->geometry();
119  if (screen_num >= 0)
120  screen_geom = desktop->screenGeometry(screen_num);
121  }
122 }
123 
135 {
136  // Preset all image placement and sizing variables.
137  video_rect = QRect(QPoint(0, 0), video_disp_dim);
139 
140  // Avoid too small frames for audio only streams (for OSD).
141  if ((video_rect.width() <= 0) || (video_rect.height() <= 0))
142  {
145  video_rect = QRect(QPoint(0, 0), video_dim);
146  }
147 
148  // Apply various modifications
152  if ((db_scale_vert == 0) && (db_scale_horiz == 0) &&
153  (mz_scale_v == 1.0f) && (mz_scale_h == 1.0f))
154  {
156  }
158  needrepaint = true;
159 }
160 
172 {
173  if (db_scale_vert > 0)
174  {
175  // Veritcal overscan. Move the Y start point in original image.
176  float tmp = 1.0f - 2.0f * db_scale_vert;
177  video_rect.moveTop((int) round(video_rect.height() * db_scale_vert));
178  video_rect.setHeight((int) round(video_rect.height() * tmp));
179 
180  // If there is an offset, apply it now that we have a room.
181  int yoff = db_move.y();
182  if (yoff > 0)
183  {
184  // To move the image down, move the start point up.
185  // Don't offset the image more than we have overscanned.
186  yoff = min(video_rect.top(), yoff);
187  video_rect.moveTop(video_rect.top() - yoff);
188  }
189  else if (yoff < 0)
190  {
191  // To move the image up, move the start point down.
192  // Don't offset the image more than we have overscanned.
193  if (abs(yoff) > video_rect.top())
194  yoff = 0 - video_rect.top();
195  video_rect.moveTop(video_rect.top() - yoff);
196  }
197  }
198  else if (db_scale_vert < 0)
199  {
200  // Vertical underscan. Move the starting Y point in the display window.
201  // Use the abolute value of scan factor.
202  float vscanf = fabs(db_scale_vert);
203  float tmp = 1.0f - 2.0f * vscanf;
204 
205  display_video_rect.moveTop(
206  (int) round(display_visible_rect.height() * vscanf) +
207  display_visible_rect.top());
208 
209  display_video_rect.setHeight(
210  (int) round(display_visible_rect.height() * tmp));
211 
212  // Now offset the image within the extra blank space created by
213  // underscanning. To move the image down, increase the Y offset
214  // inside the display window.
215  int yoff = db_move.y();
216  if (yoff > 0)
217  {
218  // Don't offset more than we have underscanned.
219  yoff = min(display_video_rect.top(), yoff);
220  display_video_rect.moveTop(display_video_rect.top() + yoff);
221  }
222  else if (yoff < 0)
223  {
224  // Don't offset more than we have underscanned.
225  if (abs(yoff) > display_video_rect.top())
226  yoff = 0 - display_video_rect.top();
227  display_video_rect.moveTop(display_video_rect.top() + yoff);
228  }
229  }
230 
231  // Horizontal.. comments, same as vertical...
232  if (db_scale_horiz > 0)
233  {
234  float tmp = 1.0f - 2.0f * db_scale_horiz;
235  video_rect.moveLeft(
236  (int) round(video_disp_dim.width() * db_scale_horiz));
237  video_rect.setWidth((int) round(video_disp_dim.width() * tmp));
238 
239  int xoff = db_move.x();
240  if (xoff > 0)
241  {
242  xoff = min(video_rect.left(), xoff);
243  video_rect.moveLeft(video_rect.left() - xoff);
244  }
245  else if (xoff < 0)
246  {
247  if (abs(xoff) > video_rect.left())
248  xoff = 0 - video_rect.left();
249  video_rect.moveLeft(video_rect.left() - xoff);
250  }
251  }
252  else if (db_scale_horiz < 0)
253  {
254  float hscanf = fabs(db_scale_horiz);
255  float tmp = 1.0f - 2.0f * hscanf;
256 
257  display_video_rect.moveLeft(
258  (int) round(display_visible_rect.width() * hscanf) +
259  display_visible_rect.left());
260 
261  display_video_rect.setWidth(
262  (int) round(display_visible_rect.width() * tmp));
263 
264  int xoff = db_move.x();
265  if (xoff > 0)
266  {
267  xoff = min(display_video_rect.left(), xoff);
268  display_video_rect.moveLeft(display_video_rect.left() + xoff);
269  }
270  else if (xoff < 0)
271  {
272  if (abs(xoff) > display_video_rect.left())
273  xoff = 0 - display_video_rect.left();
274  display_video_rect.moveLeft(display_video_rect.left() + xoff);
275  }
276  }
277 
278 }
279 
284 {
285  if ((mz_scale_v != 1.0f) || (mz_scale_h != 1.0f))
286  {
287  QSize newsz = QSize((int) (display_video_rect.width() * mz_scale_h),
288  (int) (display_video_rect.height() * mz_scale_v));
289  QSize tmp = (display_video_rect.size() - newsz) / 2;
290  QPoint chgloc = QPoint(tmp.width(), tmp.height());
291  QPoint newloc = display_video_rect.topLeft() + chgloc;
292 
293  display_video_rect = QRect(newloc, newsz);
294  }
295 
296  if (mz_move.y())
297  {
298  int move_vert = mz_move.y() * display_video_rect.height() / 100;
299  display_video_rect.moveTop(display_video_rect.top() + move_vert);
300  }
301 
302  if (mz_move.x())
303  {
304  int move_horiz = mz_move.x() * display_video_rect.width() / 100;
305  display_video_rect.moveLeft(display_video_rect.left() + move_horiz);
306  }
307 }
308 
309 // Code should take into account the aspect ratios of both the video as
310 // well as the actual screen to allow proper letterboxing to take place.
312 {
313  float disp_aspect = fix_aspect(GetDisplayAspect());
314  float aspect_diff = disp_aspect - overriden_video_aspect;
315  bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.02f;
316  bool nomatch_with_fill =
317  !aspects_match && ((kAdjustFill_HorizontalStretch == adjustfill) ||
319  bool nomatch_without_fill = (!aspects_match) && !nomatch_with_fill;
320 
321  // Adjust for video/display aspect ratio mismatch
322  if (nomatch_with_fill && (disp_aspect > overriden_video_aspect))
323  {
324  float pixNeeded = ((disp_aspect / overriden_video_aspect)
325  * (float) display_video_rect.height()) + 0.5f;
326 
327  display_video_rect.moveTop(
328  display_video_rect.top() +
329  (display_video_rect.height() - (int) pixNeeded) / 2);
330 
331  display_video_rect.setHeight((int) pixNeeded);
332  }
333  else if (nomatch_with_fill)
334  {
335  float pixNeeded =
336  ((overriden_video_aspect / disp_aspect) *
337  (float) display_video_rect.width()) + 0.5f;
338 
339  display_video_rect.moveLeft(
340  display_video_rect.left() +
341  (display_video_rect.width() - (int) pixNeeded) / 2);
342 
343  display_video_rect.setWidth((int) pixNeeded);
344  }
345  else if (nomatch_without_fill && (disp_aspect > overriden_video_aspect))
346  {
347  float pixNeeded =
348  ((overriden_video_aspect / disp_aspect) *
349  (float) display_video_rect.width()) + 0.5f;
350 
351  display_video_rect.moveLeft(
352  display_video_rect.left() +
353  (display_video_rect.width() - (int) pixNeeded) / 2);
354 
355  display_video_rect.setWidth((int) pixNeeded);
356  }
357  else if (nomatch_without_fill)
358  {
359  float pixNeeded = ((disp_aspect / overriden_video_aspect) *
360  (float) display_video_rect.height()) + 0.5f;
361 
362  display_video_rect.moveTop(
363  display_video_rect.top() +
364  (display_video_rect.height() - (int) pixNeeded) / 2);
365 
366  display_video_rect.setHeight((int) pixNeeded);
367  }
368 
369  // Process letterbox zoom modes
371  {
372  // Zoom mode -- Expand by 4/3 and overscan.
373  // 1/6 of original is 1/8 of new
374  display_video_rect = QRect(
375  display_video_rect.left() - (display_video_rect.width() / 6),
376  display_video_rect.top() - (display_video_rect.height() / 6),
377  display_video_rect.width() * 4 / 3,
378  display_video_rect.height() * 4 / 3);
379  }
380  else if (adjustfill == kAdjustFill_Half)
381  {
382  // Zoom mode -- Expand by 7/6 and overscan.
383  // Intended for eliminating the top bars on 14:9 material.
384  // Also good compromise for 4:3 material on 16:9 screen.
385  // Expanding by 7/6, so remove 1/6 of original from overscan;
386  // take half from each side, so remove 1/12.
387  display_video_rect = QRect(
388  display_video_rect.left() - (display_video_rect.width() / 12),
389  display_video_rect.top() - (display_video_rect.height() / 12),
390  display_video_rect.width() * 7 / 6,
391  display_video_rect.height() * 7 / 6);
392  }
394  {
395  // Horizontal Stretch mode -- 1/6 of original is 1/8 of new
396  // Intended to be used to eliminate side bars on 4:3 material
397  // encoded to 16:9.
398  display_video_rect.moveLeft(
399  display_video_rect.left() - (display_video_rect.width() / 6));
400 
401  display_video_rect.setWidth(display_video_rect.width() * 4 / 3);
402  }
404  {
405  // Vertical Stretch mode -- 1/6 of original is 1/8 of new
406  // Intended to be used to eliminate top/bottom bars on 16:9
407  // material encoded to 4:3.
408  display_video_rect.moveTop(
409  display_video_rect.top() - (display_video_rect.height() / 6));
410 
411  display_video_rect.setHeight(display_video_rect.height() * 4 / 3);
412  }
413  else if (adjustfill == kAdjustFill_VerticalFill &&
414  display_video_rect.height() > 0)
415  {
416  // Video fills screen vertically. May be cropped left and right
417  float factor = (float)display_visible_rect.height() /
418  (float)display_video_rect.height();
419  QSize newsize = QSize((int) (display_video_rect.width() * factor),
420  (int) (display_video_rect.height() * factor));
421  QSize temp = (display_video_rect.size() - newsize) / 2;
422  QPoint newloc = display_video_rect.topLeft() +
423  QPoint(temp.width(), temp.height());
424  display_video_rect = QRect(newloc, newsize);
425  }
427  display_video_rect.width() > 0)
428  {
429  // Video fills screen horizontally. May be cropped top and bottom
430  float factor = (float)display_visible_rect.width() /
431  (float)display_video_rect.width();
432  QSize newsize = QSize((int) (display_video_rect.width() * factor),
433  (int) (display_video_rect.height() * factor));
434  QSize temp = (display_video_rect.size() - newsize) / 2;
435  QPoint newloc = display_video_rect.topLeft() +
436  QPoint(temp.width(), temp.height());
437  display_video_rect = QRect(newloc, newsize);
438  }
439 }
440 
450 {
451  if (pip_state > kPIPOff)
452  return;
453 
454  if (display_video_rect.height() == 0 || display_video_rect.width() == 0)
455  return;
456 
457  float ydiff = abs(display_video_rect.height() - video_rect.height());
458  if ((ydiff / display_video_rect.height()) < 0.05f)
459  {
460  display_video_rect.moveTop(
461  display_video_rect.top() +
462  (display_video_rect.height() - video_rect.height()) / 2);
463 
464  display_video_rect.setHeight(video_rect.height());
465 
466  LOG(VB_PLAYBACK, LOG_INFO,
467  QString("Snapping height to avoid scaling: height: %1, top: %2")
468  .arg(display_video_rect.height())
469  .arg(display_video_rect.top()));
470  }
471 
472  float xdiff = abs(display_video_rect.width() - video_rect.width());
473  if ((xdiff / display_video_rect.width()) < 0.05f)
474  {
475  display_video_rect.moveLeft(
476  display_video_rect.left() +
477  (display_video_rect.width() - video_rect.width()) / 2);
478 
479  display_video_rect.setWidth(video_rect.width());
480 
481  LOG(VB_PLAYBACK, LOG_INFO,
482  QString("Snapping width to avoid scaling: width: %1, left: %2")
483  .arg(display_video_rect.width())
484  .arg(display_video_rect.left()));
485  }
486 }
487 
488 bool VideoOutWindow::Init(const QSize &new_video_dim_buf,
489  const QSize &new_video_dim_disp, float new_video_aspect,
490  const QRect &new_display_visible_rect,
491  AspectOverrideMode new_aspectoverride,
492  AdjustFillMode new_adjustfill)
493 {
494  // Refresh the geometry in case the video mode has changed
496  display_visible_rect = db_use_gui_size ? new_display_visible_rect :
497  screen_geom;
498 
499  int pbp_width = display_visible_rect.width() / 2;
500  if (pip_state == kPBPLeft || pip_state == kPBPRight)
501  display_visible_rect.setWidth(pbp_width);
502 
503  if (pip_state == kPBPRight)
504  display_visible_rect.moveLeft(pbp_width);
505 
506  video_dim_act = new_video_dim_disp;
507  video_disp_dim = new_video_dim_disp;
508  video_dim = new_video_dim_buf;
509  video_rect = QRect(display_visible_rect.topLeft(), video_disp_dim);
510 
511  if (pip_state > kPIPOff)
512  {
515  }
516  else
517  {
518  aspectoverride = new_aspectoverride;
519  adjustfill = new_adjustfill;
520  }
521 
522  // apply aspect ratio and letterbox mode
523  VideoAspectRatioChanged(new_video_aspect);
524 
525  embedding = false;
526 
527  return true;
528 }
529 
531 {
532 #if 0
533  LOG(VB_PLAYBACK, LOG_DEBUG, "VideoOutWindow::MoveResize:");
534  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Img(%1,%2 %3,%4)")
535  .arg(video_rect.left()).arg(video_rect.top())
536  .arg(video_rect.width()).arg(video_rect.height()));
537  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Disp(%1,%2 %3,%4)")
538  .arg(display_video_rect.left()).arg(display_video_rect.top())
539  .arg(display_video_rect.width()).arg(display_video_rect.height()));
540  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Vscan(%1, %2)")
541  .arg(db_scale_vert).arg(db_scale_vert));
542  LOG(VB_PLAYBACK, LOG_DEBUG, QString("DisplayAspect: %1")
543  .arg(GetDisplayAspect()));
544  LOG(VB_PLAYBACK, LOG_DEBUG, QString("VideoAspect(%1)")
545  .arg(video_aspect));
546  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overriden_video_aspect(%1)")
547  .arg(overriden_video_aspect));
548  LOG(VB_PLAYBACK, LOG_DEBUG, QString("CDisplayAspect: %1")
549  .arg(fix_aspect(GetDisplayAspect())));
550  LOG(VB_PLAYBACK, LOG_DEBUG, QString("AspectOverride: %1")
551  .arg(aspectoverride));
552  LOG(VB_PLAYBACK, LOG_DEBUG, QString("AdjustFill: %1") .arg(adjustfill));
553 #endif
554 
555  LOG(VB_PLAYBACK, LOG_INFO,
556  QString("Display Rect left: %1, top: %2, width: %3, "
557  "height: %4, aspect: %5")
558  .arg(display_video_rect.left())
559  .arg(display_video_rect.top())
560  .arg(display_video_rect.width())
561  .arg(display_video_rect.height())
562  .arg(fix_aspect(GetDisplayAspect())));
563 
564  LOG(VB_PLAYBACK, LOG_INFO,
565  QString("Video Rect left: %1, top: %2, width: %3, "
566  "height: %4, aspect: %5")
567  .arg(video_rect.left())
568  .arg(video_rect.top())
569  .arg(video_rect.width())
570  .arg(video_rect.height())
571  .arg(overriden_video_aspect));
572 }
573 
583 {
584  video_aspect = aspect;
586 }
587 
595 {
596  SetVideoAspectRatio(aspect);
597  MoveResize();
598 }
599 
606 bool VideoOutWindow::InputChanged(const QSize &input_size_buf,
607  const QSize &input_size_disp, float aspect,
608  MythCodecID myth_codec_id, void *codec_private)
609 {
610  (void) myth_codec_id;
611  (void) codec_private;
612 
613  video_dim_act = input_size_disp;
614  video_disp_dim = input_size_disp;
615  video_dim = input_size_buf;
616 
617  SetVideoAspectRatio(aspect);
618 
619  return true;
620 }
621 
627 {
628  return QRect(QPoint(0, 0), video_disp_dim);
629 }
630 
639 {
640  if (adjustFill == kAdjustFill_Toggle)
641  adjustFill = (AdjustFillMode) ((adjustfill + 1) % kAdjustFill_END);
642 
643  adjustfill = adjustFill;
644 
645  MoveResize();
646 }
647 
652 {
653  if (change)
654  {
655  db_scale_vert =
656  gCoreContext->GetNumSetting("VertScanPercentage", 0) * 0.01f;
658  gCoreContext->GetNumSetting("HorizScanPercentage", 0) * 0.01f;
659  db_scaling_allowed = true;
660  }
661  else
662  {
663  db_scale_vert = 0.0f;
664  db_scale_horiz = 0.0f;
665  db_scaling_allowed = false;
666  }
667 
668  LOG(VB_PLAYBACK, LOG_INFO, QString("Over/underscan. V: %1, H: %2")
669  .arg(db_scale_vert).arg(db_scale_horiz));
670 
671  MoveResize();
672 }
673 
677 void VideoOutWindow::ResizeDisplayWindow(const QRect &rect,
678  bool save_visible_rect)
679 {
680  if (save_visible_rect)
682  display_visible_rect = rect;
683  MoveResize();
684 }
685 
692 void VideoOutWindow::EmbedInWidget(const QRect &new_video_rect)
693 {
694  if (!allowpreviewepg && pip_state == kPIPOff)
695  return;
696 
697  embedding_rect = new_video_rect;
698  bool save_visible_rect = !embedding;
699 
700  embedding = true;
701 
702  display_video_rect = new_video_rect;
703  ResizeDisplayWindow(display_video_rect, save_visible_rect);
704 }
705 
712 {
713  embedding_rect = QRect();
715 
716  MoveResize();
717 
718  embedding = false;
719 }
720 
728  float &visible_aspect, float &font_scaling, float themeaspect) const
729 {
730  float dv_w = (((float)video_disp_dim.width()) /
731  display_video_rect.width());
732  float dv_h = (((float)video_disp_dim.height()) /
733  display_video_rect.height());
734 
735  uint right_overflow = max(
736  (display_video_rect.width() + display_video_rect.left()) -
737  display_visible_rect.width(), 0);
738  uint lower_overflow = max(
739  (display_video_rect.height() + display_video_rect.top()) -
740  display_visible_rect.height(), 0);
741 
742  bool isPBP = (kPBPLeft == pip_state || kPBPRight == pip_state);
743  if (isPBP)
744  {
745  right_overflow = 0;
746  lower_overflow = 0;
747  }
748 
749  // top left and bottom right corners respecting letterboxing
750  QPoint tl = QPoint(((uint) (max(-display_video_rect.left(),0)*dv_w)) & ~1,
751  ((uint) (max(-display_video_rect.top(),0)*dv_h)) & ~1);
752  QPoint br = QPoint(
753  (uint) floor(video_disp_dim.width() - (right_overflow * dv_w)),
754  (uint) floor(video_disp_dim.height() - (lower_overflow * dv_h)));
755  // adjust for overscan
756  if ((db_scale_vert > 0.0f) || (db_scale_horiz > 0.0f))
757  {
758  QRect v(tl, br);
759  float xs = (db_scale_horiz > 0.0f) ? db_scale_horiz : 0.0f;
760  float ys = (db_scale_vert > 0.0f) ? db_scale_vert : 0.0f;
761  QPoint s((int)(v.width() * xs), (int)(v.height() * ys));
762  tl += s;
763  br -= s;
764  }
765  // Work around Qt bug, QRect(QPoint(0,0), QPoint(0,0)) has area 1.
766  QRect vb(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y());
767 
768  // The calculation is completely bogus if the video is not centered
769  // which happens in the EPG, where we don't actually care about the OSD.
770  // So we just make sure the width and height are positive numbers
771  vb = QRect(vb.x(), vb.y(), abs(vb.width()), abs(vb.height()));
772 
773  // set the physical aspect ratio of the displayable area
774  const float dispPixelAdj = display_visible_rect.width() ?
776  / display_visible_rect.width() : 1.f;
777 
778  float vs = video_rect.height() ? (float)video_rect.width() /
779  video_rect.height() : 1.f;
780  visible_aspect = themeaspect / dispPixelAdj *
782 
783  if (themeaspect > 0.0f)
784  {
785  // now adjust for scaling of the video on the size
786  float tmp = sqrtf(2.0f/(sq(visible_aspect / themeaspect) + 1.0f));
787  if (tmp > 0.0f)
788  font_scaling = 1.0f / tmp;
789  // now adjust for aspect ratio effect on font size
790  // (should be in osd.cpp?)
791  font_scaling *= sqrtf(overriden_video_aspect / themeaspect);
792  }
793 
794  if (isPBP)
795  font_scaling *= 0.65f;
796 
797  return vb;
798 }
799 
808 {
809  if (pip_state > kPIPOff)
810  {
811  aspectMode = kAspect_Off;
812  return;
813  }
814 
815  if (aspectMode == kAspect_Toggle)
816  {
817  aspectMode = (AspectOverrideMode) ((aspectoverride + 1) % kAspect_END);
818  }
819 
820  aspectoverride = aspectMode;
821 
823 }
824 
825 /*
826  * \brief Determines PIP Window size and Position.
827  */
829  PIPLocation location, MythPlayer *pipplayer, bool do_pixel_adj) const
830 {
831  QRect position;
832 
833  float pipVideoAspect = pipplayer ? pipplayer->GetVideoAspect()
834  : (4.0f / 3.0f);
835  int tmph = (display_visible_rect.height() * db_pip_size) / 100;
836  float pixel_adj = 1;
837  if (do_pixel_adj)
838  {
839  pixel_adj = ((float) display_visible_rect.width() /
840  (float) display_visible_rect.height()) / display_aspect;
841  }
842  position.setHeight(tmph);
843  position.setWidth((int) (tmph * pipVideoAspect * pixel_adj));
844 
845  int xoff = (int) (display_visible_rect.width() * 0.06);
846  int yoff = (int) (display_visible_rect.height() * 0.06);
847  switch (location)
848  {
849  case kPIP_END:
850  case kPIPTopLeft:
851  break;
852  case kPIPBottomLeft:
853  yoff = display_visible_rect.height() - position.height() - yoff;
854  break;
855  case kPIPTopRight:
856  xoff = display_visible_rect.width() - position.width() - xoff;
857  break;
858  case kPIPBottomRight:
859  xoff = display_visible_rect.width() - position.width() - xoff;
860  yoff = display_visible_rect.height() - position.height() - yoff;
861  break;
862  }
863  position.translate(xoff, yoff);
864  return position;
865 }
866 
874 {
875  const float zf = 0.02;
876  if (kZoomHome == direction)
877  {
878  mz_scale_v = 1.0f;
879  mz_scale_h = 1.0f;
880  mz_move = QPoint(0, 0);
881  }
882  else if (kZoomIn == direction)
883  {
886  {
887  mz_scale_h += zf;
888  mz_scale_v += zf;
889  }
890  }
891  else if (kZoomOut == direction)
892  {
895  {
896  mz_scale_h -= zf;
897  mz_scale_v -= zf;
898  }
899  }
900  else if (kZoomAspectUp == direction)
901  {
904  {
905  mz_scale_h += zf;
906  mz_scale_v -= zf;
907  }
908  }
909  else if (kZoomAspectDown == direction)
910  {
913  {
914  mz_scale_h -= zf;
915  mz_scale_v += zf;
916  }
917  }
918  else if (kZoomVerticalIn == direction)
919  {
921  mz_scale_v += zf;
922  }
923  else if (kZoomVerticalOut == direction)
924  {
926  mz_scale_v -= zf;
927  }
928  else if (kZoomHorizontalIn == direction)
929  {
931  mz_scale_h += zf;
932  }
933  else if (kZoomHorizontalOut == direction)
934  {
936  mz_scale_h -= zf;
937  }
938  else if (kZoomUp == direction && (mz_move.y() < +kManualZoomMaxMove))
939  mz_move.setY(mz_move.y() + 1);
940  else if (kZoomDown == direction && (mz_move.y() > -kManualZoomMaxMove))
941  mz_move.setY(mz_move.y() - 1);
942  else if (kZoomLeft == direction && (mz_move.x() < +kManualZoomMaxMove))
943  mz_move.setX(mz_move.x() + 2);
944  else if (kZoomRight == direction && (mz_move.x() > -kManualZoomMaxMove))
945  mz_move.setX(mz_move.x() - 2);
946 
947  mz_scale_v = snap(mz_scale_v, 1.0f, zf / 2);
948  mz_scale_h = snap(mz_scale_h, 1.0f, zf / 2);
949 }
950 
952 {
953  if (bottomline)
954  {
955  mz_move.setX(0);
956  mz_move.setY(0);
957  mz_scale_h = 1.0;
958  mz_scale_v = 1.0;
959  bottomline = false;
960  }
961  else
962  {
963  const float zf = 0.02;
964 
965  int x = gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0);
966  mz_move.setX(x);
967 
968  int y = gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5);
969  mz_move.setY(y);
970 
971  double h = static_cast<double>
972  (gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) /
973  100.0;
974  mz_scale_h = snap(h, 1.0f, zf / 2);
975 
976  double v = static_cast<double>
977  (gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) /
978  100.0;
979  mz_scale_v = snap(v, 1.0f, zf / 2);
980 
981  bottomline = true;
982  }
983 
984  MoveResize();
985 }
986 
988 {
989  gCoreContext->SaveSetting("OSDMoveXBottomLine", GetMzMove().x());
990  gCoreContext->SaveSetting("OSDMoveYBottomLine", GetMzMove().y());
991 
992  gCoreContext->SaveSetting("OSDScaleHBottomLine", GetMzScaleH() * 100.0f);
993  gCoreContext->SaveSetting("OSDScaleVBottomLine", GetMzScaleV() * 100.0f);
994 }
995 
996 QString VideoOutWindow::GetZoomString(void) const
997 {
998  float zh = GetMzScaleH();
999  float zv = GetMzScaleV();
1000  QPoint zo = GetMzMove();
1001  return tr("Zoom %1x%2 @ (%3,%4)")
1002  .arg(zh, 0, 'f', 2).arg(zv, 0, 'f', 2).arg(zo.x()).arg(zo.y());
1003 }
1004 
1006 static float fix_aspect(float raw)
1007 {
1008  // Check if close to 4:3
1009  if (fabs(raw - 1.333333f) < 0.05f)
1010  raw = 1.333333f;
1011 
1012  // Check if close to 16:9
1013  if (fabs(raw - 1.777777f) < 0.05f)
1014  raw = 1.777777f;
1015 
1016  return raw;
1017 }
1018 
1020 {
1021  LOG(VB_PLAYBACK, LOG_INFO,
1022  QString("VideoOutWindow::SetPIPState. pip_state: %1]") .arg(setting));
1023 
1024  pip_state = setting;
1025 }
1026 
1028 static QSize fix_alignment(QSize raw)
1029 {
1030  return QSize((raw.width() + 15) & (~0xf), (raw.height() + 15) & (~0xf));
1031 }
1032 
1033 static float snap(float value, float snapto, float diff)
1034 {
1035  if ((value + diff > snapto) && (value - diff < snapto))
1036  return snapto;
1037  return value;
1038 }
void ToggleAspectOverride(AspectOverrideMode aspectOverrideMode=kAspect_Toggle)
Enforce different aspect ration than detected, then calls VideoAspectRatioChanged(float) to apply the...
QSize video_disp_dim
Pixel dimensions of video display area.
float GetVideoAspect(void) const
Definition: mythplayer.h:175
QPoint db_move
Percentage move from database.
static float fix_aspect(float raw)
Correct for rounding errors.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
QRect GetTotalOSDBounds(void) const
Returns total OSD bounds.
MythCodecID
Definition: mythcodecid.h:10
#define round(x)
Definition: mythplayer.cpp:65
void SaveSetting(const QString &key, int newValue)
int db_pip_size
percentage of full window to use for PiP
float sq(float a)
Definition: mythmiscutil.h:57
QRect GetPIPRect(PIPLocation location, MythPlayer *pipplayer=nullptr, bool do_pixel_adj=true) const
bool embedding
State variables.
float mz_scale_h
Manually applied horizontal scaling.
QRect embedding_rect
Embedded video rectangle.
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
float mz_scale_v
Manually applied vertical scaling.
QPoint mz_move
Manually applied percentage move.
QRect tmp_display_visible_rect
Used to save the display_visible_rect for restoration after video embedding ends.
static const float kManualZoomMinVerticalZoom
static guint32 * tmp
Definition: goom_core.c:35
void SetPIPState(PIPState setting)
void populateGeometry(void)
void SetVideoScalingAllowed(bool change)
Disable or enable underscan/overscan.
float GetDisplayAspect(void) const
Returns current display aspect ratio.
QSize video_dim
Pixel dimensions of video buffer.
bool db_scaling_allowed
disable this to prevent overscan/underscan
bool using_xinerama
Display is using multiple screens.
AspectOverrideMode
Definition: videoouttypes.h:46
PIPState pip_state
void ApplySnapToVideoRect(void)
Snap displayed rectagle to video rectange if they are close.
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
QRect video_rect
Pixel rectangle in video frame to display.
void SaveBottomLine(void)
float db_scale_horiz
Horizontal Overscan/Underscan percentage.
static int GetNumberXineramaScreens(void)
QRect GetVisibleOSDBounds(float &, float &, float) const
Returns visible portions of total OSD bounds.
AdjustFillMode adjustfill
Zoom mode.
static const int kManualZoomMaxMove
AspectOverrideMode aspectoverride
AspectOverrideMode to use to modify overriden_video_aspect.
float GetMzScaleH(void) const
float get_aspect_override(AspectOverrideMode aspectmode, float orig)
void EmbedInWidget(const QRect &)
Tells video output to embed video in an existing window.
static float snap(float value, float snapto, float diff)
QSize video_dim_act
Pixel dimensions of the raw video stream.
int screen_num
Screen that contains playback window.
void PrintMoveResizeDebug(void)
QRect display_visible_rect
Visible portion of display window in pixels.
QRect display_video_rect
Pixel rectangle in display window into which video_rect maps to.
PIPLocation
Definition: videoouttypes.h:19
AdjustFillMode
Definition: videoouttypes.h:57
static QSize fix_alignment(QSize raw)
Correct for underalignment.
int GetNumSetting(const QString &key, int defaultval=0)
QPoint GetMzMove(void) const
float overriden_video_aspect
Normally this is the same as videoAspect, but may not be if the user has toggled the aspect override ...
void ResizeDisplayWindow(const QRect &, bool)
Resize Display Window.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
float display_aspect
Physical aspect ratio of playback window.
bool GetBoolSetting(const QString &key, bool defaultval=false)
PIPState
Definition: videoouttypes.h:10
bool InputChanged(const QSize &input_size_buf, const QSize &input_size_disp, float aspect, MythCodecID myth_codec_id, void *codec_private)
Tells video output to discard decoded frames and wait for new ones.
float db_scale_vert
Vertical Overscan/Underscan percentage.
void ToggleMoveBottomLine(void)
static const float kManualZoomMinHorizontalZoom
void VideoAspectRatioChanged(float aspect)
Calls SetVideoAspectRatio(float aspect), then calls MoveResize() to apply changes.
static const float kManualZoomMaxVerticalZoom
void MoveResize(void)
performs all the calculations for video framing and any resizing.
void SetVideoAspectRatio(float aspect)
Sets VideoOutWindow::video_aspect to aspect, and sets VideoOutWindow::overriden_video_aspect if aspec...
bool Init(const QSize &new_video_dim_buf, const QSize &new_video_dim_disp, float aspect, const QRect &new_display_visible_rect, AspectOverrideMode aspectoverride, AdjustFillMode adjustfill)
QRect screen_geom
Full screen geometry.
void ApplyLetterboxing(void)
bool db_use_gui_size
Use the gui size for video window.
static const float kManualZoomMaxHorizontalZoom
QString GetZoomString(void) const
float video_aspect
Physical aspect ratio of video.
void ToggleAdjustFill(AdjustFillMode adjustFillMode=kAdjustFill_Toggle)
Sets up letterboxing for various standard video frame and monitor dimensions, then calls MoveResize()...
ZoomDirection
Definition: videoouttypes.h:28
float GetMzScaleV(void) const
void StopEmbedding(void)
Tells video output to stop embedding video in an existing window.
void ApplyManualScaleAndMove(void)
Apply scales and moves from "Zoom Mode" settings.
void Zoom(ZoomDirection direction)
Sets up zooming into to different parts of the video, the zoom is actually applied in MoveResize().
void ApplyDBScaleAndMove(void)
Apply scales and moves for "Overscan" and "Underscan" DB settings.