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 <QApplication>
26 
27 #include "mythconfig.h"
28 
29 #include "videooutwindow.h"
30 #include "mythmiscutil.h"
31 #include "osd.h"
32 #include "mythplayer.h"
33 #include "videodisplayprofile.h"
34 #include "decoderbase.h"
35 #include "mythcorecontext.h"
36 #include "dithertable.h"
37 
38 extern "C" {
39 #include "libavcodec/avcodec.h"
40 }
41 
42 #include "filtermanager.h"
43 
44 static QSize fix_alignment(QSize raw);
45 static float fix_aspect(float raw);
46 static float snap(float value, float snapto, float diff);
47 
53 
55  // DB settings
56  db_move(0, 0), db_scale_horiz(0.0F), db_scale_vert(0.0F),
57  db_pip_size(26),
58  db_scaling_allowed(true),
59 
60  using_xinerama(false), screen_geom(0, 0, 1024, 768),
61 
62  // Manual Zoom
63  mz_scale_v(1.0F), mz_scale_h(1.0F), mz_move(0, 0),
64 
65  // Physical dimensions
66  display_dim(400, 300), display_aspect(1.3333F),
67 
68  // Video dimensions
69  video_dim(640, 480), video_disp_dim(640, 480),
70  video_dim_act(640, 480), video_aspect(1.3333F),
71 
72  // Aspect override
73  overriden_video_aspect(1.3333F), aspectoverride(kAspect_Off),
74 
75  // Adjust Fill
76  adjustfill(kAdjustFill_Off),
77 
78  // Screen settings
79  video_rect(0, 0, 0, 0),
80  display_video_rect(0, 0, 0, 0),
81  display_visible_rect(0, 0, 0, 0),
82  tmp_display_visible_rect(0, 0, 0, 0),
83  embedding_rect(QRect()),
84 
85  // Various state variables
86  embedding(false), needrepaint(false),
87  allowpreviewepg(true), bottomline(false),
88  pip_state(kPIPOff)
89 {
90  db_pip_size = gCoreContext->GetNumSetting("PIPSize", 26);
91 
92  db_move = QPoint(gCoreContext->GetNumSetting("xScanDisplacement", 0),
93  gCoreContext->GetNumSetting("yScanDisplacement", 0));
94  db_use_gui_size = gCoreContext->GetBoolSetting("GuiSizeForTV", false);
95 
97 }
98 
100 {
101  qApp->processEvents();
102  if (not qobject_cast<QApplication*>(qApp))
103  return;
104 
105  QScreen *screen = MythDisplay::GetScreen();
107  {
108  using_xinerama = true;
109  screen_geom = screen->virtualGeometry();
110  LOG(VB_PLAYBACK, LOG_INFO, QString("Window using all screens %2x%3")
111  .arg(screen_geom.width()).arg(screen_geom.height()));
112  return;
113  }
114 
115  screen_geom = screen->geometry();
116  LOG(VB_PLAYBACK, LOG_INFO, QString("Window using screen %2 %3x%4")
117  .arg(screen->name())
118  .arg(screen_geom.width()).arg(screen_geom.height()));
119 }
120 
132 {
133  // Preset all image placement and sizing variables.
134  video_rect = QRect(QPoint(0, 0), video_disp_dim);
136 
137  // Avoid too small frames for audio only streams (for OSD).
138  if ((video_rect.width() <= 0) || (video_rect.height() <= 0))
139  {
142  video_rect = QRect(QPoint(0, 0), video_dim);
143  }
144 
145  // Apply various modifications
149  if ((db_scale_vert == 0) && (db_scale_horiz == 0) &&
150  (mz_scale_v == 1.0F) && (mz_scale_h == 1.0F))
151  {
153  }
155  needrepaint = true;
156 }
157 
169 {
170  if (db_scale_vert > 0)
171  {
172  // Veritcal overscan. Move the Y start point in original image.
173  float tmp = 1.0F - 2.0F * db_scale_vert;
174  video_rect.moveTop((int) round(video_rect.height() * db_scale_vert));
175  video_rect.setHeight((int) round(video_rect.height() * tmp));
176 
177  // If there is an offset, apply it now that we have a room.
178  int yoff = db_move.y();
179  if (yoff > 0)
180  {
181  // To move the image down, move the start point up.
182  // Don't offset the image more than we have overscanned.
183  yoff = min(video_rect.top(), yoff);
184  video_rect.moveTop(video_rect.top() - yoff);
185  }
186  else if (yoff < 0)
187  {
188  // To move the image up, move the start point down.
189  // Don't offset the image more than we have overscanned.
190  if (abs(yoff) > video_rect.top())
191  yoff = 0 - video_rect.top();
192  video_rect.moveTop(video_rect.top() - yoff);
193  }
194  }
195  else if (db_scale_vert < 0)
196  {
197  // Vertical underscan. Move the starting Y point in the display window.
198  // Use the abolute value of scan factor.
199  float vscanf = fabs(db_scale_vert);
200  float tmp = 1.0F - 2.0F * vscanf;
201 
202  display_video_rect.moveTop(
203  (int) round(display_visible_rect.height() * vscanf) +
204  display_visible_rect.top());
205 
206  display_video_rect.setHeight(
207  (int) round(display_visible_rect.height() * tmp));
208 
209  // Now offset the image within the extra blank space created by
210  // underscanning. To move the image down, increase the Y offset
211  // inside the display window.
212  int yoff = db_move.y();
213  if (yoff > 0)
214  {
215  // Don't offset more than we have underscanned.
216  yoff = min(display_video_rect.top(), yoff);
217  display_video_rect.moveTop(display_video_rect.top() + yoff);
218  }
219  else if (yoff < 0)
220  {
221  // Don't offset more than we have underscanned.
222  if (abs(yoff) > display_video_rect.top())
223  yoff = 0 - display_video_rect.top();
224  display_video_rect.moveTop(display_video_rect.top() + yoff);
225  }
226  }
227 
228  // Horizontal.. comments, same as vertical...
229  if (db_scale_horiz > 0)
230  {
231  float tmp = 1.0F - 2.0F * db_scale_horiz;
232  video_rect.moveLeft(
233  (int) round(video_disp_dim.width() * db_scale_horiz));
234  video_rect.setWidth((int) round(video_disp_dim.width() * tmp));
235 
236  int xoff = db_move.x();
237  if (xoff > 0)
238  {
239  xoff = min(video_rect.left(), xoff);
240  video_rect.moveLeft(video_rect.left() - xoff);
241  }
242  else if (xoff < 0)
243  {
244  if (abs(xoff) > video_rect.left())
245  xoff = 0 - video_rect.left();
246  video_rect.moveLeft(video_rect.left() - xoff);
247  }
248  }
249  else if (db_scale_horiz < 0)
250  {
251  float hscanf = fabs(db_scale_horiz);
252  float tmp = 1.0F - 2.0F * hscanf;
253 
254  display_video_rect.moveLeft(
255  (int) round(display_visible_rect.width() * hscanf) +
256  display_visible_rect.left());
257 
258  display_video_rect.setWidth(
259  (int) round(display_visible_rect.width() * tmp));
260 
261  int xoff = db_move.x();
262  if (xoff > 0)
263  {
264  xoff = min(display_video_rect.left(), xoff);
265  display_video_rect.moveLeft(display_video_rect.left() + xoff);
266  }
267  else if (xoff < 0)
268  {
269  if (abs(xoff) > display_video_rect.left())
270  xoff = 0 - display_video_rect.left();
271  display_video_rect.moveLeft(display_video_rect.left() + xoff);
272  }
273  }
274 
275 }
276 
281 {
282  if ((mz_scale_v != 1.0F) || (mz_scale_h != 1.0F))
283  {
284  QSize newsz = QSize((int) (display_video_rect.width() * mz_scale_h),
285  (int) (display_video_rect.height() * mz_scale_v));
286  QSize tmp = (display_video_rect.size() - newsz) / 2;
287  QPoint chgloc = QPoint(tmp.width(), tmp.height());
288  QPoint newloc = display_video_rect.topLeft() + chgloc;
289 
290  display_video_rect = QRect(newloc, newsz);
291  }
292 
293  if (mz_move.y())
294  {
295  int move_vert = mz_move.y() * display_video_rect.height() / 100;
296  display_video_rect.moveTop(display_video_rect.top() + move_vert);
297  }
298 
299  if (mz_move.x())
300  {
301  int move_horiz = mz_move.x() * display_video_rect.width() / 100;
302  display_video_rect.moveLeft(display_video_rect.left() + move_horiz);
303  }
304 }
305 
306 // Code should take into account the aspect ratios of both the video as
307 // well as the actual screen to allow proper letterboxing to take place.
309 {
310  float disp_aspect = fix_aspect(GetDisplayAspect());
311  float aspect_diff = disp_aspect - overriden_video_aspect;
312  bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.02F;
313  bool nomatch_with_fill =
314  !aspects_match && ((kAdjustFill_HorizontalStretch == 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 > overriden_video_aspect))
320  {
321  float pixNeeded = ((disp_aspect / overriden_video_aspect)
322  * (float) display_video_rect.height()) + 0.5F;
323 
324  display_video_rect.moveTop(
325  display_video_rect.top() +
326  (display_video_rect.height() - (int) pixNeeded) / 2);
327 
328  display_video_rect.setHeight((int) pixNeeded);
329  }
330  else if (nomatch_with_fill)
331  {
332  float pixNeeded =
333  ((overriden_video_aspect / disp_aspect) *
334  (float) display_video_rect.width()) + 0.5F;
335 
336  display_video_rect.moveLeft(
337  display_video_rect.left() +
338  (display_video_rect.width() - (int) pixNeeded) / 2);
339 
340  display_video_rect.setWidth((int) pixNeeded);
341  }
342  else if (nomatch_without_fill && (disp_aspect > overriden_video_aspect))
343  {
344  float pixNeeded =
345  ((overriden_video_aspect / disp_aspect) *
346  (float) display_video_rect.width()) + 0.5F;
347 
348  display_video_rect.moveLeft(
349  display_video_rect.left() +
350  (display_video_rect.width() - (int) pixNeeded) / 2);
351 
352  display_video_rect.setWidth((int) pixNeeded);
353  }
354  else if (nomatch_without_fill)
355  {
356  float pixNeeded = ((disp_aspect / overriden_video_aspect) *
357  (float) display_video_rect.height()) + 0.5F;
358 
359  display_video_rect.moveTop(
360  display_video_rect.top() +
361  (display_video_rect.height() - (int) pixNeeded) / 2);
362 
363  display_video_rect.setHeight((int) pixNeeded);
364  }
365 
366  // Process letterbox zoom modes
368  {
369  // Zoom mode -- Expand by 4/3 and overscan.
370  // 1/6 of original is 1/8 of new
371  display_video_rect = QRect(
372  display_video_rect.left() - (display_video_rect.width() / 6),
373  display_video_rect.top() - (display_video_rect.height() / 6),
374  display_video_rect.width() * 4 / 3,
375  display_video_rect.height() * 4 / 3);
376  }
377  else if (adjustfill == kAdjustFill_Half)
378  {
379  // Zoom mode -- Expand by 7/6 and overscan.
380  // Intended for eliminating the top bars on 14:9 material.
381  // Also good compromise for 4:3 material on 16:9 screen.
382  // Expanding by 7/6, so remove 1/6 of original from overscan;
383  // take half from each side, so remove 1/12.
384  display_video_rect = QRect(
385  display_video_rect.left() - (display_video_rect.width() / 12),
386  display_video_rect.top() - (display_video_rect.height() / 12),
387  display_video_rect.width() * 7 / 6,
388  display_video_rect.height() * 7 / 6);
389  }
391  {
392  // Horizontal Stretch mode -- 1/6 of original is 1/8 of new
393  // Intended to be used to eliminate side bars on 4:3 material
394  // encoded to 16:9.
395  display_video_rect.moveLeft(
396  display_video_rect.left() - (display_video_rect.width() / 6));
397 
398  display_video_rect.setWidth(display_video_rect.width() * 4 / 3);
399  }
401  {
402  // Vertical Stretch mode -- 1/6 of original is 1/8 of new
403  // Intended to be used to eliminate top/bottom bars on 16:9
404  // material encoded to 4:3.
405  display_video_rect.moveTop(
406  display_video_rect.top() - (display_video_rect.height() / 6));
407 
408  display_video_rect.setHeight(display_video_rect.height() * 4 / 3);
409  }
410  else if (adjustfill == kAdjustFill_VerticalFill &&
411  display_video_rect.height() > 0)
412  {
413  // Video fills screen vertically. May be cropped left and right
414  float factor = (float)display_visible_rect.height() /
415  (float)display_video_rect.height();
416  QSize newsize = QSize((int) (display_video_rect.width() * factor),
417  (int) (display_video_rect.height() * factor));
418  QSize temp = (display_video_rect.size() - newsize) / 2;
419  QPoint newloc = display_video_rect.topLeft() +
420  QPoint(temp.width(), temp.height());
421  display_video_rect = QRect(newloc, newsize);
422  }
424  display_video_rect.width() > 0)
425  {
426  // Video fills screen horizontally. May be cropped top and bottom
427  float factor = (float)display_visible_rect.width() /
428  (float)display_video_rect.width();
429  QSize newsize = QSize((int) (display_video_rect.width() * factor),
430  (int) (display_video_rect.height() * factor));
431  QSize temp = (display_video_rect.size() - newsize) / 2;
432  QPoint newloc = display_video_rect.topLeft() +
433  QPoint(temp.width(), temp.height());
434  display_video_rect = QRect(newloc, newsize);
435  }
436 }
437 
447 {
448  if (pip_state > kPIPOff)
449  return;
450 
451  if (display_video_rect.height() == 0 || display_video_rect.width() == 0)
452  return;
453 
454  float ydiff = abs(display_video_rect.height() - video_rect.height());
455  if ((ydiff / display_video_rect.height()) < 0.05F)
456  {
457  display_video_rect.moveTop(
458  display_video_rect.top() +
459  (display_video_rect.height() - video_rect.height()) / 2);
460 
461  display_video_rect.setHeight(video_rect.height());
462 
463  LOG(VB_PLAYBACK, LOG_INFO,
464  QString("Snapping height to avoid scaling: height: %1, top: %2")
465  .arg(display_video_rect.height())
466  .arg(display_video_rect.top()));
467  }
468 
469  float xdiff = abs(display_video_rect.width() - video_rect.width());
470  if ((xdiff / display_video_rect.width()) < 0.05F)
471  {
472  display_video_rect.moveLeft(
473  display_video_rect.left() +
474  (display_video_rect.width() - video_rect.width()) / 2);
475 
476  display_video_rect.setWidth(video_rect.width());
477 
478  LOG(VB_PLAYBACK, LOG_INFO,
479  QString("Snapping width to avoid scaling: width: %1, left: %2")
480  .arg(display_video_rect.width())
481  .arg(display_video_rect.left()));
482  }
483 }
484 
485 bool VideoOutWindow::Init(const QSize &new_video_dim_buf,
486  const QSize &new_video_dim_disp, float new_video_aspect,
487  const QRect &new_display_visible_rect,
488  AspectOverrideMode new_aspectoverride,
489  AdjustFillMode new_adjustfill)
490 {
491  // Refresh the geometry in case the video mode has changed
493  display_visible_rect = db_use_gui_size ? new_display_visible_rect :
494  screen_geom;
495 
496  int pbp_width = display_visible_rect.width() / 2;
497  if (pip_state == kPBPLeft || pip_state == kPBPRight)
498  display_visible_rect.setWidth(pbp_width);
499 
500  if (pip_state == kPBPRight)
501  display_visible_rect.moveLeft(pbp_width);
502 
503  video_dim_act = new_video_dim_disp;
504  video_disp_dim = new_video_dim_disp;
505  video_dim = new_video_dim_buf;
506  video_rect = QRect(display_visible_rect.topLeft(), video_disp_dim);
507 
508  if (pip_state > kPIPOff)
509  {
512  }
513  else
514  {
515  aspectoverride = new_aspectoverride;
516  adjustfill = new_adjustfill;
517  }
518 
519  // apply aspect ratio and letterbox mode
520  VideoAspectRatioChanged(new_video_aspect);
521 
522  embedding = false;
523 
524  return true;
525 }
526 
528 {
529 #if 0
530  LOG(VB_PLAYBACK, LOG_DEBUG, "VideoOutWindow::MoveResize:");
531  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Img(%1,%2 %3,%4)")
532  .arg(video_rect.left()).arg(video_rect.top())
533  .arg(video_rect.width()).arg(video_rect.height()));
534  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Disp(%1,%2 %3,%4)")
535  .arg(display_video_rect.left()).arg(display_video_rect.top())
536  .arg(display_video_rect.width()).arg(display_video_rect.height()));
537  LOG(VB_PLAYBACK, LOG_DEBUG, QString("Vscan(%1, %2)")
538  .arg(db_scale_vert).arg(db_scale_vert));
539  LOG(VB_PLAYBACK, LOG_DEBUG, QString("DisplayAspect: %1")
540  .arg(GetDisplayAspect()));
541  LOG(VB_PLAYBACK, LOG_DEBUG, QString("VideoAspect(%1)")
542  .arg(video_aspect));
543  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overriden_video_aspect(%1)")
544  .arg(overriden_video_aspect));
545  LOG(VB_PLAYBACK, LOG_DEBUG, QString("CDisplayAspect: %1")
546  .arg(fix_aspect(GetDisplayAspect())));
547  LOG(VB_PLAYBACK, LOG_DEBUG, QString("AspectOverride: %1")
548  .arg(aspectoverride));
549  LOG(VB_PLAYBACK, LOG_DEBUG, QString("AdjustFill: %1") .arg(adjustfill));
550 #endif
551 
552  LOG(VB_PLAYBACK, LOG_INFO,
553  QString("Display Rect left: %1, top: %2, width: %3, "
554  "height: %4, aspect: %5")
555  .arg(display_video_rect.left())
556  .arg(display_video_rect.top())
557  .arg(display_video_rect.width())
558  .arg(display_video_rect.height())
559  .arg(fix_aspect(GetDisplayAspect())));
560 
561  LOG(VB_PLAYBACK, LOG_INFO,
562  QString("Video Rect left: %1, top: %2, width: %3, "
563  "height: %4, aspect: %5")
564  .arg(video_rect.left())
565  .arg(video_rect.top())
566  .arg(video_rect.width())
567  .arg(video_rect.height())
568  .arg(overriden_video_aspect));
569 }
570 
580 {
581  video_aspect = aspect;
583 }
584 
592 {
593  SetVideoAspectRatio(aspect);
594  MoveResize();
595 }
596 
603 bool VideoOutWindow::InputChanged(const QSize &input_size_buf,
604  const QSize &input_size_disp, float aspect,
605  MythCodecID myth_codec_id, void *codec_private)
606 {
607  (void) myth_codec_id;
608  (void) codec_private;
609 
610  video_dim_act = input_size_disp;
611  video_disp_dim = input_size_disp;
612  video_dim = input_size_buf;
613 
614  SetVideoAspectRatio(aspect);
615 
616  return true;
617 }
618 
624 {
625  return {QPoint(0, 0), video_disp_dim};
626 }
627 
636 {
637  if (adjustFill == kAdjustFill_Toggle)
638  adjustFill = (AdjustFillMode) ((adjustfill + 1) % kAdjustFill_END);
639 
640  adjustfill = adjustFill;
641 
642  MoveResize();
643 }
644 
649 {
650  if (change)
651  {
652  db_scale_vert =
653  gCoreContext->GetNumSetting("VertScanPercentage", 0) * 0.01F;
655  gCoreContext->GetNumSetting("HorizScanPercentage", 0) * 0.01F;
656  db_scaling_allowed = true;
657  }
658  else
659  {
660  db_scale_vert = 0.0F;
661  db_scale_horiz = 0.0F;
662  db_scaling_allowed = false;
663  }
664 
665  LOG(VB_PLAYBACK, LOG_INFO, QString("Over/underscan. V: %1, H: %2")
666  .arg(db_scale_vert).arg(db_scale_horiz));
667 
668  MoveResize();
669 }
670 
674 void VideoOutWindow::ResizeDisplayWindow(const QRect &rect,
675  bool save_visible_rect)
676 {
677  if (save_visible_rect)
679  display_visible_rect = rect;
680  MoveResize();
681 }
682 
689 void VideoOutWindow::EmbedInWidget(const QRect &new_video_rect)
690 {
691  if (!allowpreviewepg && pip_state == kPIPOff)
692  return;
693 
694  embedding_rect = new_video_rect;
695  bool save_visible_rect = !embedding;
696 
697  embedding = true;
698 
699  display_video_rect = new_video_rect;
700  ResizeDisplayWindow(display_video_rect, save_visible_rect);
701 }
702 
709 {
710  embedding_rect = QRect();
712 
713  MoveResize();
714 
715  embedding = false;
716 }
717 
725  float &visible_aspect, float &font_scaling, float themeaspect) const
726 {
727  float dv_w = (((float)video_disp_dim.width()) /
728  display_video_rect.width());
729  float dv_h = (((float)video_disp_dim.height()) /
730  display_video_rect.height());
731 
732  uint right_overflow = max(
733  (display_video_rect.width() + display_video_rect.left()) -
734  display_visible_rect.width(), 0);
735  uint lower_overflow = max(
736  (display_video_rect.height() + display_video_rect.top()) -
737  display_visible_rect.height(), 0);
738 
739  bool isPBP = (kPBPLeft == pip_state || kPBPRight == pip_state);
740  if (isPBP)
741  {
742  right_overflow = 0;
743  lower_overflow = 0;
744  }
745 
746  // top left and bottom right corners respecting letterboxing
747  QPoint tl = QPoint(((uint) (max(-display_video_rect.left(),0)*dv_w)) & ~1,
748  ((uint) (max(-display_video_rect.top(),0)*dv_h)) & ~1);
749  QPoint br = QPoint(
750  (uint) floor(video_disp_dim.width() - (right_overflow * dv_w)),
751  (uint) floor(video_disp_dim.height() - (lower_overflow * dv_h)));
752  // adjust for overscan
753  if ((db_scale_vert > 0.0F) || (db_scale_horiz > 0.0F))
754  {
755  QRect v(tl, br);
756  float xs = (db_scale_horiz > 0.0F) ? db_scale_horiz : 0.0F;
757  float ys = (db_scale_vert > 0.0F) ? db_scale_vert : 0.0F;
758  QPoint s((int)(v.width() * xs), (int)(v.height() * ys));
759  tl += s;
760  br -= s;
761  }
762  // Work around Qt bug, QRect(QPoint(0,0), QPoint(0,0)) has area 1.
763  QRect vb(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y());
764 
765  // The calculation is completely bogus if the video is not centered
766  // which happens in the EPG, where we don't actually care about the OSD.
767  // So we just make sure the width and height are positive numbers
768  vb = QRect(vb.x(), vb.y(), abs(vb.width()), abs(vb.height()));
769 
770  // set the physical aspect ratio of the displayable area
771  const float dispPixelAdj = display_visible_rect.width() ?
773  / display_visible_rect.width() : 1.F;
774 
775  float vs = video_rect.height() ? (float)video_rect.width() /
776  video_rect.height() : 1.F;
777  visible_aspect = themeaspect / dispPixelAdj *
779 
780  if (themeaspect > 0.0F)
781  {
782  // now adjust for scaling of the video on the size
783  float tmp = sqrtf(2.0F/(sq(visible_aspect / themeaspect) + 1.0F));
784  if (tmp > 0.0F)
785  font_scaling = 1.0F / tmp;
786  // now adjust for aspect ratio effect on font size
787  // (should be in osd.cpp?)
788  font_scaling *= sqrtf(overriden_video_aspect / themeaspect);
789  }
790 
791  if (isPBP)
792  font_scaling *= 0.65F;
793 
794  return vb;
795 }
796 
805 {
806  if (pip_state > kPIPOff)
807  {
808  return;
809  }
810 
811  if (aspectMode == kAspect_Toggle)
812  {
813  aspectMode = (AspectOverrideMode) ((aspectoverride + 1) % kAspect_END);
814  }
815 
816  aspectoverride = aspectMode;
817 
819 }
820 
821 /*
822  * \brief Determines PIP Window size and Position.
823  */
825  PIPLocation location, MythPlayer *pipplayer, bool do_pixel_adj) const
826 {
827  QRect position;
828 
829  float pipVideoAspect = pipplayer ? pipplayer->GetVideoAspect()
830  : (4.0F / 3.0F);
831  int tmph = (display_visible_rect.height() * db_pip_size) / 100;
832  float pixel_adj = 1;
833  if (do_pixel_adj)
834  {
835  pixel_adj = ((float) display_visible_rect.width() /
836  (float) display_visible_rect.height()) / display_aspect;
837  }
838  position.setHeight(tmph);
839  position.setWidth((int) (tmph * pipVideoAspect * pixel_adj));
840 
841  int xoff = (int) (display_visible_rect.width() * 0.06);
842  int yoff = (int) (display_visible_rect.height() * 0.06);
843  switch (location)
844  {
845  case kPIP_END:
846  case kPIPTopLeft:
847  break;
848  case kPIPBottomLeft:
849  yoff = display_visible_rect.height() - position.height() - yoff;
850  break;
851  case kPIPTopRight:
852  xoff = display_visible_rect.width() - position.width() - xoff;
853  break;
854  case kPIPBottomRight:
855  xoff = display_visible_rect.width() - position.width() - xoff;
856  yoff = display_visible_rect.height() - position.height() - yoff;
857  break;
858  }
859  position.translate(xoff, yoff);
860  return position;
861 }
862 
870 {
871  const float zf = 0.02;
872  if (kZoomHome == direction)
873  {
874  mz_scale_v = 1.0F;
875  mz_scale_h = 1.0F;
876  mz_move = QPoint(0, 0);
877  }
878  else if (kZoomIn == direction)
879  {
882  {
883  mz_scale_h += zf;
884  mz_scale_v += zf;
885  }
886  }
887  else if (kZoomOut == direction)
888  {
891  {
892  mz_scale_h -= zf;
893  mz_scale_v -= zf;
894  }
895  }
896  else if (kZoomAspectUp == direction)
897  {
900  {
901  mz_scale_h += zf;
902  mz_scale_v -= zf;
903  }
904  }
905  else if (kZoomAspectDown == direction)
906  {
909  {
910  mz_scale_h -= zf;
911  mz_scale_v += zf;
912  }
913  }
914  else if (kZoomVerticalIn == direction)
915  {
917  mz_scale_v += zf;
918  }
919  else if (kZoomVerticalOut == direction)
920  {
922  mz_scale_v -= zf;
923  }
924  else if (kZoomHorizontalIn == direction)
925  {
927  mz_scale_h += zf;
928  }
929  else if (kZoomHorizontalOut == direction)
930  {
932  mz_scale_h -= zf;
933  }
934  else if (kZoomUp == direction && (mz_move.y() < +kManualZoomMaxMove))
935  mz_move.setY(mz_move.y() + 1);
936  else if (kZoomDown == direction && (mz_move.y() > -kManualZoomMaxMove))
937  mz_move.setY(mz_move.y() - 1);
938  else if (kZoomLeft == direction && (mz_move.x() < +kManualZoomMaxMove))
939  mz_move.setX(mz_move.x() + 2);
940  else if (kZoomRight == direction && (mz_move.x() > -kManualZoomMaxMove))
941  mz_move.setX(mz_move.x() - 2);
942 
943  mz_scale_v = snap(mz_scale_v, 1.0F, zf / 2);
944  mz_scale_h = snap(mz_scale_h, 1.0F, zf / 2);
945 }
946 
948 {
949  if (bottomline)
950  {
951  mz_move.setX(0);
952  mz_move.setY(0);
953  mz_scale_h = 1.0;
954  mz_scale_v = 1.0;
955  bottomline = false;
956  }
957  else
958  {
959  const float zf = 0.02;
960 
961  int x = gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0);
962  mz_move.setX(x);
963 
964  int y = gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5);
965  mz_move.setY(y);
966 
967  double h = static_cast<double>
968  (gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) /
969  100.0;
970  mz_scale_h = snap(h, 1.0F, zf / 2);
971 
972  double v = static_cast<double>
973  (gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) /
974  100.0;
975  mz_scale_v = snap(v, 1.0F, zf / 2);
976 
977  bottomline = true;
978  }
979 
980  MoveResize();
981 }
982 
984 {
985  gCoreContext->SaveSetting("OSDMoveXBottomLine", GetMzMove().x());
986  gCoreContext->SaveSetting("OSDMoveYBottomLine", GetMzMove().y());
987 
988  gCoreContext->SaveSetting("OSDScaleHBottomLine", GetMzScaleH() * 100.0F);
989  gCoreContext->SaveSetting("OSDScaleVBottomLine", GetMzScaleV() * 100.0F);
990 }
991 
992 QString VideoOutWindow::GetZoomString(void) const
993 {
994  float zh = GetMzScaleH();
995  float zv = GetMzScaleV();
996  QPoint zo = GetMzMove();
997  return tr("Zoom %1x%2 @ (%3,%4)")
998  .arg(zh, 0, 'f', 2).arg(zv, 0, 'f', 2).arg(zo.x()).arg(zo.y());
999 }
1000 
1002 static float fix_aspect(float raw)
1003 {
1004  // Check if close to 4:3
1005  if (fabs(raw - 1.333333F) < 0.05F)
1006  raw = 1.333333F;
1007 
1008  // Check if close to 16:9
1009  if (fabs(raw - 1.777777F) < 0.05F)
1010  raw = 1.777777F;
1011 
1012  return raw;
1013 }
1014 
1016 {
1017  LOG(VB_PLAYBACK, LOG_INFO,
1018  QString("VideoOutWindow::SetPIPState. pip_state: %1]") .arg(setting));
1019 
1020  pip_state = setting;
1021 }
1022 
1024 static QSize fix_alignment(QSize raw)
1025 {
1026  return {(raw.width() + 15) & (~0xf), (raw.height() + 15) & (~0xf)};
1027 }
1028 
1029 static float snap(float value, float snapto, float diff)
1030 {
1031  if ((value + diff > snapto) && (value - diff < snapto))
1032  return snapto;
1033  return value;
1034 }
QSize video_disp_dim
Pixel dimensions of video display area.
void ToggleAspectOverride(AspectOverrideMode aspectMode=kAspect_Toggle)
Enforce different aspect ration than detected, then calls VideoAspectRatioChanged(float) to apply the...
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:66
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.
static QScreen * GetScreen(void)
Return a pointer to the screen to use.
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.
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.
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.
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.
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.