MythTV master
mythvideobounds.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// Std
24#include <cmath>
25
26// Qt
27#include <QApplication>
28
29// MythtTV
34#include "mythplayer.h"
35#include "mythvideobounds.h"
36
37#define LOC QString("VideoBounds: ")
38
39static inline QRect SCALED_RECT(QRect src, qreal scale)
40{ return {static_cast<int>(src.left() * scale),
41 static_cast<int>(src.top() * scale),
42 static_cast<int>(src.width() * scale),
43 static_cast<int>(src.height() * scale) }; }
44
45static float fix_aspect(float raw);
46static float snap(float value, float snapto, float diff);
47
53
55 : m_dbMove( { gCoreContext->GetNumSetting("xScanDisplacement", 0),
56 gCoreContext->GetNumSetting("yScanDisplacement", 0) }),
57 m_dbUseGUISize(gCoreContext->GetBoolSetting("GuiSizeForTV", false)),
58 m_dbAspectOverride(static_cast<AspectOverrideMode>(gCoreContext->GetNumSetting("AspectOverride", 0))),
59 m_dbAdjustFill(static_cast<AdjustFillMode>(gCoreContext->GetNumSetting("AdjustFill", 0)))
60{
61}
62
69{
72}
73
75{
76 if (m_display)
77 {
78 LOG(VB_GENERAL, LOG_WARNING, LOC + "Already have a display");
79 return;
80 }
81
82 m_display = mDisplay;
84}
85
87{
89#ifdef Q_OS_MACOS
90 // PopulateGeometry will update m_devicePixelRatio
92#endif
93 MoveResize();
94}
95
97{
98 if (!m_display)
99 return;
100
101 QScreen *screen = m_display->GetCurrentScreen();
102 if (!screen)
103 return;
104
105#ifdef Q_OS_MACOS
106 m_devicePixelRatio = screen->devicePixelRatio();
107#endif
108
110 {
111 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using all screens %1x%2")
112 .arg(screen->virtualGeometry().width()).arg(screen->virtualGeometry().height()));
113 return;
114 }
115
116 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using screen '%1' %2x%3")
117 .arg(screen->name()).arg(screen->geometry().width()).arg(screen->geometry().height()));
118}
119
131{
132 // for 'portrait' mode, rotate the 'screen' dimensions
133 float tempdisplayaspect = m_displayAspect;
134 bool rotate = (m_rotation == 90) || (m_rotation == -90);
135 if (rotate)
136 Rotate();
137
138 // Preset all image placement and sizing variables.
139 m_videoRect = QRect(QPoint(0, 0), m_videoDispDim);
141
142 // Apply various modifications
146
147 // Interactive TV (MHEG) embedding
148 if (m_itvResizing)
150
151 // and switch back
152 if (rotate)
153 Rotate();
154 m_displayAspect = tempdisplayaspect;
155
157
158 // TODO fine tune when these are emitted - it is not enough to just check whether the values
159 // have changed
164
166}
167
178{
179 m_dbMove = QPoint(m_dbMove.y(), m_dbMove.x());
180 float temp = m_dbHorizScale;
182 m_dbVertScale = temp;
183 temp = m_manualHorizScale;
185 m_manualVertScale = temp;
186 m_manualMove = QPoint(m_manualMove.y(), m_manualMove.x());
188
190 QSize(m_displayVisibleRect.height(), m_displayVisibleRect.width()));
191 m_displayVideoRect = QRect(QPoint(m_displayVideoRect.top(), m_displayVideoRect.left()),
192 QSize(m_displayVideoRect.height(), m_displayVideoRect.width()));
201}
202
213{
214 if (m_dbVertScale > 0)
215 {
216 // Veritcal overscan. Move the Y start point in original image.
217 float tmp = 1.0F - (2.0F * m_dbVertScale);
218 m_videoRect.moveTop(qRound(m_videoRect.height() * m_dbVertScale));
219 m_videoRect.setHeight(qRound(m_videoRect.height() * tmp));
220
221 // If there is an offset, apply it now that we have a room.
222 int yoff = m_dbMove.y();
223 if (yoff > 0)
224 {
225 // To move the image down, move the start point up.
226 // Don't offset the image more than we have overscanned.
227 yoff = std::min(m_videoRect.top(), yoff);
228 m_videoRect.moveTop(m_videoRect.top() - yoff);
229 }
230 else if (yoff < 0)
231 {
232 // To move the image up, move the start point down.
233 // Don't offset the image more than we have overscanned.
234 if (abs(yoff) > m_videoRect.top())
235 yoff = 0 - m_videoRect.top();
236 m_videoRect.moveTop(m_videoRect.top() - yoff);
237 }
238 }
239 else if (m_dbVertScale < 0)
240 {
241 // Vertical underscan. Move the starting Y point in the display window.
242 // Use the abolute value of scan factor.
243 float vscanf = fabs(m_dbVertScale);
244 float tmp = 1.0F - (2.0F * vscanf);
245
246 m_displayVideoRect.moveTop(qRound(m_displayVisibleRect.height() * vscanf) + m_displayVisibleRect.top());
247 m_displayVideoRect.setHeight(qRound(m_displayVisibleRect.height() * tmp));
248
249 // Now offset the image within the extra blank space created by
250 // underscanning. To move the image down, increase the Y offset
251 // inside the display window.
252 int yoff = m_dbMove.y();
253 if (yoff > 0)
254 {
255 // Don't offset more than we have underscanned.
256 yoff = std::min(m_displayVideoRect.top(), yoff);
257 m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
258 }
259 else if (yoff < 0)
260 {
261 // Don't offset more than we have underscanned.
262 if (abs(yoff) > m_displayVideoRect.top())
263 yoff = 0 - m_displayVideoRect.top();
264 m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
265 }
266 }
267
268 // Horizontal.. comments, same as vertical...
269 if (m_dbHorizScale > 0)
270 {
271 float tmp = 1.0F - (2.0F * m_dbHorizScale);
272 m_videoRect.moveLeft(qRound(m_videoDispDim.width() * m_dbHorizScale));
273 m_videoRect.setWidth(qRound(m_videoDispDim.width() * tmp));
274
275 int xoff = m_dbMove.x();
276 if (xoff > 0)
277 {
278 xoff = std::min(m_videoRect.left(), xoff);
279 m_videoRect.moveLeft(m_videoRect.left() - xoff);
280 }
281 else if (xoff < 0)
282 {
283 if (abs(xoff) > m_videoRect.left())
284 xoff = 0 - m_videoRect.left();
285 m_videoRect.moveLeft(m_videoRect.left() - xoff);
286 }
287 }
288 else if (m_dbHorizScale < 0)
289 {
290 float hscanf = fabs(m_dbHorizScale);
291 float tmp = 1.0F - (2.0F * hscanf);
292
293 m_displayVideoRect.moveLeft(qRound(m_displayVisibleRect.width() * hscanf) + m_displayVisibleRect.left());
294 m_displayVideoRect.setWidth(qRound(m_displayVisibleRect.width() * tmp));
295
296 int xoff = m_dbMove.x();
297 if (xoff > 0)
298 {
299 xoff = std::min(m_displayVideoRect.left(), xoff);
300 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
301 }
302 else if (xoff < 0)
303 {
304 if (abs(xoff) > m_displayVideoRect.left())
305 xoff = 0 - m_displayVideoRect.left();
306 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
307 }
308 }
309
310}
311
316{
317 if ((m_manualVertScale != 1.0F) || (m_manualHorizScale != 1.0F))
318 {
319 QSize newsz = QSize(qRound(m_displayVideoRect.width() * m_manualHorizScale),
320 qRound(m_displayVideoRect.height() * m_manualVertScale));
321 QSize tmp = (m_displayVideoRect.size() - newsz) / 2;
322 QPoint chgloc = QPoint(tmp.width(), tmp.height());
323 QPoint newloc = m_displayVideoRect.topLeft() + chgloc;
324
325 m_displayVideoRect = QRect(newloc, newsz);
326 }
327
328 if (m_manualMove.y())
329 {
330 int move_vert = m_manualMove.y() * m_displayVideoRect.height() / 100;
331 m_displayVideoRect.moveTop(m_displayVideoRect.top() + move_vert);
332 }
333
334 if (m_manualMove.x())
335 {
336 int move_horiz = m_manualMove.x() * m_displayVideoRect.width() / 100;
337 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + move_horiz);
338 }
339}
340
341// Code should take into account the aspect ratios of both the video as
342// well as the actual screen to allow proper letterboxing to take place.
344{
345 float disp_aspect = fix_aspect(GetDisplayAspect());
346 float aspect_diff = disp_aspect - m_videoAspectOverride;
347 bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.02F;
348 bool nomatch_with_fill = !aspects_match && ((kAdjustFill_HorizontalStretch == m_adjustFill) ||
350 bool nomatch_without_fill = (!aspects_match) && !nomatch_with_fill;
351
352 // Adjust for video/display aspect ratio mismatch
353 if (nomatch_with_fill && (disp_aspect > m_videoAspectOverride))
354 {
355 int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
356 m_displayVideoRect.moveTop(m_displayVideoRect.top() + ((m_displayVideoRect.height() - pixNeeded) / 2));
357 m_displayVideoRect.setHeight(pixNeeded);
358 }
359 else if (nomatch_with_fill)
360 {
361 int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
362 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + ((m_displayVideoRect.width() - pixNeeded) / 2));
363 m_displayVideoRect.setWidth(pixNeeded);
364 }
365 else if (nomatch_without_fill && (disp_aspect > m_videoAspectOverride))
366 {
367 int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
368 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + ((m_displayVideoRect.width() - pixNeeded) / 2));
369 m_displayVideoRect.setWidth(pixNeeded);
370 }
371 else if (nomatch_without_fill)
372 {
373 int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
374 m_displayVideoRect.moveTop(m_displayVideoRect.top() + ((m_displayVideoRect.height() - pixNeeded) / 2));
375 m_displayVideoRect.setHeight(pixNeeded);
376 }
377
378 // Process letterbox zoom modes
380 {
381 // Zoom mode -- Expand by 4/3 and overscan.
382 // 1/6 of original is 1/8 of new
383 m_displayVideoRect = QRect(
384 m_displayVideoRect.left() - (m_displayVideoRect.width() / 6),
385 m_displayVideoRect.top() - (m_displayVideoRect.height() / 6),
386 m_displayVideoRect.width() * 4 / 3,
387 m_displayVideoRect.height() * 4 / 3);
388 }
389 else if (m_adjustFill == kAdjustFill_Half)
390 {
391 // Zoom mode -- Expand by 7/6 and overscan.
392 // Intended for eliminating the top bars on 14:9 material.
393 // Also good compromise for 4:3 material on 16:9 screen.
394 // Expanding by 7/6, so remove 1/6 of original from overscan;
395 // take half from each side, so remove 1/12.
396 m_displayVideoRect = QRect(
397 m_displayVideoRect.left() - (m_displayVideoRect.width() / 12),
398 m_displayVideoRect.top() - (m_displayVideoRect.height() / 12),
399 m_displayVideoRect.width() * 7 / 6,
400 m_displayVideoRect.height() * 7 / 6);
401 }
403 {
404 // Horizontal Stretch mode -- 1/6 of original is 1/8 of new
405 // Intended to be used to eliminate side bars on 4:3 material
406 // encoded to 16:9.
407 m_displayVideoRect.moveLeft(
408 m_displayVideoRect.left() - (m_displayVideoRect.width() / 6));
409
410 m_displayVideoRect.setWidth(m_displayVideoRect.width() * 4 / 3);
411 }
413 {
414 // Vertical Stretch mode -- 1/6 of original is 1/8 of new
415 // Intended to be used to eliminate top/bottom bars on 16:9
416 // material encoded to 4:3.
417 m_displayVideoRect.moveTop(
418 m_displayVideoRect.top() - (m_displayVideoRect.height() / 6));
419
420 m_displayVideoRect.setHeight(m_displayVideoRect.height() * 4 / 3);
421 }
422 else if (m_adjustFill == kAdjustFill_VerticalFill && m_displayVideoRect.height() > 0)
423 {
424 // Video fills screen vertically. May be cropped left and right
425 float factor = static_cast<float>(m_displayVisibleRect.height()) / static_cast<float>(m_displayVideoRect.height());
426 QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
427 qRound(m_displayVideoRect.height() * factor));
428 QSize temp = (m_displayVideoRect.size() - newsize) / 2;
429 QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
430 m_displayVideoRect = QRect(newloc, newsize);
431 }
433 {
434 // Video fills screen horizontally. May be cropped top and bottom
435 float factor = static_cast<float>(m_displayVisibleRect.width()) /
436 static_cast<float>(m_displayVideoRect.width());
437 QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
438 qRound(m_displayVideoRect.height() * factor));
439 QSize temp = (m_displayVideoRect.size() - newsize) / 2;
440 QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
441 m_displayVideoRect = QRect(newloc, newsize);
442 }
443}
444
445bool MythVideoBounds::InitBounds(QSize VideoDim, QSize VideoDispDim,
446 float Aspect, QRect WindowRect)
447{
448 if (m_display)
449 {
450 QString dummy;
451 m_displayAspect = static_cast<float>(m_display->GetAspectRatio(dummy));
452 }
453
454 // Refresh the geometry in case the video mode has changed
456
457 // N.B. we are always confined to the window size so use that for the initial
458 // displayVisibleRect
459 m_rawWindowRect = WindowRect;
461 m_videoDispDim = Fix1088(VideoDispDim);
462 m_videoDim = VideoDim;
464
467 m_embedding = false;
468 SetVideoAspectRatio(Aspect);
469 MoveResize();
470 return true;
471}
472
474{
475 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window Rect: %1x%2+%3+%4")
476 .arg(m_windowRect.width()).arg(m_windowRect.height())
477 .arg(m_windowRect.left()).arg(m_windowRect.top()));
478 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display Rect: %1x%2+%3+%4 Aspect: %5")
479 .arg(m_displayVideoRect.width()).arg(m_displayVideoRect.height())
480 .arg(m_displayVideoRect.left()).arg(m_displayVideoRect.top())
481 .arg(static_cast<qreal>(fix_aspect(GetDisplayAspect()))));
482 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Video Rect: %1x%2+%3+%4 Aspect: %5")
483 .arg(m_videoRect.width()).arg(m_videoRect.height())
484 .arg(m_videoRect.left()).arg(m_videoRect.top())
485 .arg(static_cast<qreal>(m_videoAspectOverride)));
486}
487
496{
497 m_videoAspect = aspect;
499}
500
507{
508 if (!qFuzzyCompare(Aspect, m_videoAspect))
509 {
510 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect ratio: '%1'")
511 .arg(static_cast<double>(Aspect)));
512 SetVideoAspectRatio(Aspect);
513 MoveResize();
514 }
515}
516
518void MythVideoBounds::SourceChanged(QSize VideoDim, QSize VideoDispDim, float Aspect)
519{
520 if (Aspect < 0.0F)
521 Aspect = m_videoAspect;
522
523 QSize newvideodispdim = Fix1088(VideoDispDim);
524
525 if (!((VideoDim == m_videoDim) && (newvideodispdim == m_videoDispDim) &&
526 qFuzzyCompare(Aspect + 100.0F, m_videoAspect + 100.0F)))
527 {
528 m_videoDispDim = newvideodispdim;
529 m_videoDim = VideoDim;
530 SetVideoAspectRatio(Aspect);
531 LOG(VB_PLAYBACK, LOG_INFO, LOC +
532 QString("New video parameters: Size %1x%2 DisplaySize: %3x%4 Aspect: %5")
533 .arg(m_videoDim.width()).arg(m_videoDim.height())
534 .arg(m_videoDispDim.width()).arg(m_videoDispDim.height())
535 .arg(static_cast<double>(m_videoAspect)));
536 MoveResize();
537 }
538}
539
540QSize MythVideoBounds::Fix1088(QSize Dimensions)
541{
542 QSize result = Dimensions;
543 // 544 represents a 1088 field
544 if (result.width() == 1920 || result.width() == 1440)
545 {
546 if (result.height() == 1088)
547 result.setHeight(1080);
548 else if (result.height() == 544)
549 result.setHeight(540);
550 }
551 return result;
552}
553
560{
564 {
566 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New fill mode: '%1'").arg(toString(m_adjustFill)));
567 MoveResize();
568 }
569}
570
575{
576 float oldvert = m_dbVertScale;
577 float oldhoriz = m_dbHorizScale;
578
579 if (Change)
580 {
581 m_dbVertScale = gCoreContext->GetNumSetting("VertScanPercentage", 0) * 0.01F;
582 m_dbHorizScale = gCoreContext->GetNumSetting("HorizScanPercentage", 0) * 0.01F;
583 m_dbScalingAllowed = true;
584 }
585 else
586 {
587 m_dbVertScale = 0.0F;
588 m_dbHorizScale = 0.0F;
589 m_dbScalingAllowed = false;
590 }
591
592 if (!(qFuzzyCompare(oldvert + 100.0F, m_dbVertScale + 100.0F) &&
593 qFuzzyCompare(oldhoriz + 100.0F, m_dbHorizScale + 100.0F)))
594 {
595 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Over/underscan. V: %1, H: %2")
596 .arg(static_cast<double>(m_dbVertScale)).arg(static_cast<double>(m_dbHorizScale)));
597 MoveResize();
598 }
599}
600
601void MythVideoBounds::SetDisplayAspect(float DisplayAspect)
602{
603 if (!qFuzzyCompare(DisplayAspect + 10.0F, m_displayAspect + 10.0F))
604 {
605 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New display aspect: %1")
606 .arg(static_cast<double>(DisplayAspect)));
607 m_displayAspect = DisplayAspect;
608 MoveResize();
609 }
610}
611
613{
614 if (Size != m_rawWindowRect.size())
615 {
616 QRect rect(m_rawWindowRect.topLeft(), Size);
617 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New window rect: %1x%2+%3+%4")
618 .arg(rect.width()).arg(rect.height()).arg(rect.left()).arg(rect.top()));
619 m_rawWindowRect = rect;
621 MoveResize();
622 }
623}
624
626{
627 QRect oldrect = m_rawItvDisplayVideoRect;
628 if (Rect.isEmpty())
629 {
630 m_itvResizing = false;
631 m_itvDisplayVideoRect = QRect();
632 m_rawItvDisplayVideoRect = QRect();
633 }
634 else
635 {
636 m_itvResizing = true;
639 }
640 if (m_rawItvDisplayVideoRect != oldrect)
641 {
642 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New ITV display rect: %1x%2+%3+%4 (Scale: %5)")
643 .arg(m_itvDisplayVideoRect.width()).arg(m_itvDisplayVideoRect.height())
644 .arg(m_itvDisplayVideoRect.left()).arg(m_itvDisplayVideoRect.right())
645 .arg(m_devicePixelRatio));
646 MoveResize();
647 }
648}
649
655{
656 if (Rotation == m_rotation)
657 return;
658 if ((Rotation < -180) || (Rotation > 180))
659 return;
660
661 m_rotation = Rotation;
662 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New rotation: %1").arg(m_rotation));
663 MoveResize();
664}
665
669void MythVideoBounds::ResizeDisplayWindow(QRect Rect, bool SaveVisibleRect)
670{
671 if (SaveVisibleRect)
674 MoveResize();
675}
676
677void MythVideoBounds::EmbedPlayback(bool Embed, QRect Rect)
678{
679 if (Embed)
680 {
681 if (m_embedding && (Rect == m_rawEmbeddingRect))
682 return;
683
684 m_rawEmbeddingRect = Rect;
686 bool savevisiblerect = !m_embedding;
687 m_embedding = true;
688 m_embeddingHidden = Rect.isEmpty();
690 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New embedding rect: %1x%2+%3+%4 (Scale: %5)")
691 .arg(m_embeddingRect.width()).arg(m_embeddingRect.height())
692 .arg(m_embeddingRect.left()).arg(m_embeddingRect.top())
693 .arg(m_devicePixelRatio));
694 ResizeDisplayWindow(m_displayVideoRect, savevisiblerect);
695 return;
696 }
697
698 if (!m_embedding)
699 return;
700 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Stopped embedding");
701 m_rawEmbeddingRect = QRect();
702 m_embeddingRect = QRect();
704 m_embedding = false;
705 m_embeddingHidden = false;
706 MoveResize();
707}
708
716{
717 if (AspectMode == kAspect_Toggle)
718 AspectMode = static_cast<AspectOverrideMode>(((m_videoAspectOverrideMode + 1) % kAspect_END));
719
720 if (m_videoAspectOverrideMode != AspectMode)
721 {
722 m_videoAspectOverrideMode = AspectMode;
723 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect override: '%1'")
726 MoveResize();
727 }
728
730}
731
737{
738 if (IsEmbedding())
739 return false;
740
741 return m_displayVideoRect.contains(m_windowRect);
742}
743
749{
750 QRegion visible(m_windowRect);
751 QRegion video(m_displayVideoRect);
752 return visible.subtracted(video);
753}
754
760{
761 float oldvertscale = m_manualVertScale;
762 float oldhorizscale = m_manualHorizScale;
763 QPoint oldmove = m_manualMove;
764
765 const float zf = 0.02F;
766 if (kZoomHome == Direction)
767 {
768 m_manualVertScale = 1.0F;
769 m_manualHorizScale = 1.0F;
770 m_manualMove = QPoint(0, 0);
771 }
772 else if (kZoomIn == Direction)
773 {
776 {
777 m_manualHorizScale += zf;
778 m_manualVertScale += zf;
779 }
780 }
781 else if (kZoomOut == Direction)
782 {
785 {
786 m_manualHorizScale -= zf;
787 m_manualVertScale -= zf;
788 }
789 }
790 else if (kZoomAspectUp == Direction)
791 {
794 {
795 m_manualHorizScale += zf;
796 m_manualVertScale -= zf;
797 }
798 }
799 else if (kZoomAspectDown == Direction)
800 {
803 {
804 m_manualHorizScale -= zf;
805 m_manualVertScale += zf;
806 }
807 }
808 else if (kZoomVerticalIn == Direction)
809 {
811 m_manualVertScale += zf;
812 }
813 else if (kZoomVerticalOut == Direction)
814 {
816 m_manualVertScale -= zf;
817 }
818 else if (kZoomHorizontalIn == Direction)
819 {
821 m_manualHorizScale += zf;
822 }
823 else if (kZoomHorizontalOut == Direction)
824 {
826 m_manualHorizScale -= zf;
827 }
828 else if (kZoomUp == Direction && (m_manualMove.y() < +kManualZoomMaxMove))
829 {
830 m_manualMove.setY(m_manualMove.y() + 1);
831 }
832 else if (kZoomDown == Direction && (m_manualMove.y() > -kManualZoomMaxMove))
833 {
834 m_manualMove.setY(m_manualMove.y() - 1);
835 }
836 else if (kZoomLeft == Direction && (m_manualMove.x() < +kManualZoomMaxMove))
837 {
838 m_manualMove.setX(m_manualMove.x() + 2);
839 }
840 else if (kZoomRight == Direction && (m_manualMove.x() > -kManualZoomMaxMove))
841 {
842 m_manualMove.setX(m_manualMove.x() - 2);
843 }
844
847
848 if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
849 qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
850 {
851 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New zoom: Offset %1x%2 HScale %3 VScale %4")
852 .arg(m_manualMove.x()).arg(m_manualMove.y())
853 .arg(static_cast<double>(m_manualHorizScale))
854 .arg(static_cast<double>(m_manualVertScale)));
855 MoveResize();
856 }
857}
858
860{
861 float oldvertscale = m_manualVertScale;
862 float oldhorizscale = m_manualHorizScale;
863 QPoint oldmove = m_manualMove;
864
865 if (m_bottomLine)
866 {
867 m_manualMove.setX(0);
868 m_manualMove.setY(0);
869 m_manualHorizScale = 1.0;
870 m_manualVertScale = 1.0;
871 m_bottomLine = false;
872 }
873 else
874 {
875 const float zf = 0.02F;
876 m_manualMove.setX(gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0));
877 m_manualMove.setY(gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5));
878 float h = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) / 100.0F;
879 m_manualHorizScale = snap(h, 1.0F, zf / 2.0F);
880 float v = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) / 100.0F;
881 m_manualVertScale = snap(v, 1.0F, zf / 2.0F);
882 m_bottomLine = true;
883 }
884
885 if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
886 qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
887 {
888 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New custom zoom: Offset %1x%2 HScale %3 VScale %4")
889 .arg(m_manualMove.x()).arg(m_manualMove.y())
890 .arg(static_cast<double>(m_manualHorizScale))
891 .arg(static_cast<double>(m_manualVertScale)));
892 MoveResize();
893 }
894
896}
897
899{
900 gCoreContext->SaveSetting("OSDMoveXBottomLine", m_manualMove.x());
901 gCoreContext->SaveSetting("OSDMoveYBottomLine", m_manualMove.y());
902 gCoreContext->SaveSetting("OSDScaleHBottomLine", static_cast<int>(m_manualHorizScale * 100.0F));
903 gCoreContext->SaveSetting("OSDScaleVBottomLine", static_cast<int>(m_manualVertScale * 100.0F));
904
905 emit UpdateOSDMessage("Current 'Manual Zoom' saved for 'BottomLine'.");
906}
907
909static float fix_aspect(float raw)
910{
911 // Check if close to 4:3
912 if (fabs(raw - 1.333333F) < 0.05F)
913 raw = 1.333333F;
914
915 // Check if close to 16:9
916 if (fabs(raw - 1.777777F) < 0.05F)
917 raw = 1.777777F;
918
919 return raw;
920}
921
922static float snap(float value, float snapto, float diff)
923{
924 if ((value + diff > snapto) && (value - diff < snapto))
925 return snapto;
926 return value;
927}
928
930{
931 if (Mode != m_stereoOverride)
932 {
936 }
937}
void SaveSetting(const QString &key, int newValue)
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
double GetAspectRatio(QString &Source, bool IgnoreModeOverride=false)
Returns current screen aspect ratio.
QScreen * GetCurrentScreen()
Return a pointer to the screen to use.
void DisplayChanged()
static bool SpanAllScreens()
Return true if the MythTV windows should span all screens.
static int GetScreenCount()
void RefreshVideoBoundsState()
Send out latest state to listeners.
void SetITVResize(QRect Rect)
void SetRotation(int Rotation)
Set the rotation in degrees.
void Rotate(void)
Adjust various settings to facilitate portrait mode calculations.
bool IsEmbedding(void) const
QSize m_videoDispDim
Pixel dimensions of video display area.
static const float kManualZoomMaxHorizontalZoom
virtual void EmbedPlayback(bool Embed, QRect Rect)
float m_manualHorizScale
Manually applied horizontal scaling.
float m_dbVertScale
Vertical Overscan/Underscan percentage.
QSize m_videoDim
Pixel dimensions of video buffer.
float m_dbHorizScale
Horizontal Overscan/Underscan percentage.
QRect m_displayVideoRect
Pixel rectangle in display window into which video_rect maps to.
void VideoRectsChanged(const QRect &DisplayVideoRect, const QRect &VideoRect)
void SaveBottomLine(void)
static const float kManualZoomMinVerticalZoom
void MoveResize(void)
performs all the calculations for video framing and any resizing.
static QSize Fix1088(QSize Dimensions)
static const float kManualZoomMaxVerticalZoom
QRect m_windowRect
Rectangle describing QWidget bounds.
AspectOverrideMode m_dbAspectOverride
void UpdateOSDMessage(const QString &Message)
void VideoBoundsStateChanged(MythVideoBoundsState VideoState)
QRect m_videoRect
Pixel rectangle in video frame to display.
void SourceChanged(QSize VideoDim, QSize VideoDispDim, float Aspect)
Update for new source video dimensions and aspect ratio.
bool m_embedding
State variables.
void WindowRectChanged(const QRect &WindowRect)
void VisibleRectChanged(const QRect &DisplayVisibleRect)
void ToggleAdjustFill(AdjustFillMode AdjustFillMode=kAdjustFill_Toggle)
Sets up letterboxing for various standard video frame and monitor dimensions, then calls MoveResize()...
float m_videoAspectOverride
Normally this is the same as videoAspect, but may not be if the user has toggled the aspect override ...
MythDisplay * m_display
void SetDisplayAspect(float DisplayAspect)
bool VideoIsFullScreen(void) const
Check whether the video display rect covers the entire window/framebuffer.
void SetVideoScalingAllowed(bool Change)
Disable or enable underscan/overscan.
QRect m_tmpDisplayVisibleRect
Used to save the display_visible_rect for restoration after video embedding ends.
void ToggleAspectOverride(AspectOverrideMode AspectMode=kAspect_Toggle)
Enforce different aspect ratio than detected, then calls VideoAspectRatioChanged(float) to apply them...
void ApplyLetterboxing(void)
AspectOverrideMode m_videoAspectOverrideMode
AspectOverrideMode to use to modify overriden_video_aspect.
void SetVideoAspectRatio(float Aspect)
Sets MythVideoBounds::video_aspect to aspect, and sets MythVideoBounds::overriden_video_aspect if asp...
float m_videoAspect
Physical aspect ratio of video.
void ApplyManualScaleAndMove(void)
Apply scales and moves from "Zoom Mode" settings.
void ToggleMoveBottomLine(void)
void PopulateGeometry(void)
QRegion GetBoundingRegion(void) const
Return the region of DisplayVisibleRect that lies outside of DisplayVideoRect.
float m_displayAspect
Physical aspect ratio of playback window.
AdjustFillMode m_adjustFill
Zoom mode.
float m_manualVertScale
Manually applied vertical scaling.
bool InitBounds(QSize VideoDim, QSize VideoDispDim, float Aspect, QRect WindowRect)
QRect m_rawWindowRect
Rectangle describing QWidget bounds - not adjusted for high DPI scaling (macos)
static const int kManualZoomMaxMove
QRect m_rawItvDisplayVideoRect
void SetDisplay(MythDisplay *mDisplay)
QPoint m_manualMove
Manually applied percentage move.
void SetWindowSize(QSize Size)
void VideoAspectRatioChanged(float Aspect)
Calls SetVideoAspectRatio(float aspect), then calls MoveResize() to apply changes.
void ResizeDisplayWindow(QRect Rect, bool SaveVisibleRect)
Resize Display Window.
void Zoom(ZoomDirection Direction)
Sets up zooming into to different parts of the video.
StereoscopicMode m_stereoOverride
void PrintMoveResizeDebug(void)
bool m_dbScalingAllowed
disable this to prevent overscan/underscan
void SetStereoOverride(StereoscopicMode Mode)
void ApplyDBScaleAndMove(void)
Apply scales and moves for "Overscan" and "Underscan" DB settings.
void VideoSizeChanged(const QSize &VideoDim, const QSize &VideoDispDim)
float GetDisplayAspect(void) const
QRect m_displayVisibleRect
Visible portion of display window in pixels.
QRect m_embeddingRect
Embedded video rectangle.
static const float kManualZoomMinHorizontalZoom
AdjustFillMode m_dbAdjustFill
QPoint m_dbMove
Percentage move from database.
static HostComboBoxSetting * AdjustFill()
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
static float snap(float value, float snapto, float diff)
static QRect SCALED_RECT(QRect src, qreal scale)
static float fix_aspect(float raw)
Correct for rounding errors.
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
Mode
Definition: synaesthesia.h:20
QString GetZoomString(float HorizScale, float VertScale, QPoint Move)
AspectOverrideMode
Definition: videoouttypes.h:61
@ kAspect_Toggle
Definition: videoouttypes.h:62
@ kAspect_END
Definition: videoouttypes.h:68
float get_aspect_override(AspectOverrideMode Aspectmode, float Original)
AdjustFillMode
Definition: videoouttypes.h:72
@ kAdjustFill_Full
Definition: videoouttypes.h:76
@ kAdjustFill_HorizontalFill
Definition: videoouttypes.h:79
@ kAdjustFill_VerticalFill
Definition: videoouttypes.h:80
@ kAdjustFill_Half
Definition: videoouttypes.h:75
@ kAdjustFill_VerticalStretch
Definition: videoouttypes.h:78
@ kAdjustFill_HorizontalStretch
Definition: videoouttypes.h:77
@ kAdjustFill_Toggle
Definition: videoouttypes.h:73
@ kAdjustFill_END
Definition: videoouttypes.h:81
QString StereoscopictoString(StereoscopicMode Mode)
ZoomDirection
Definition: videoouttypes.h:43
@ kZoomVerticalOut
Definition: videoouttypes.h:48
@ kZoomRight
Definition: videoouttypes.h:54
@ kZoomAspectUp
Definition: videoouttypes.h:55
@ kZoomVerticalIn
Definition: videoouttypes.h:47
@ kZoomHome
Definition: videoouttypes.h:44
@ kZoomUp
Definition: videoouttypes.h:51
@ kZoomDown
Definition: videoouttypes.h:52
@ kZoomLeft
Definition: videoouttypes.h:53
@ kZoomOut
Definition: videoouttypes.h:46
@ kZoomIn
Definition: videoouttypes.h:45
@ kZoomAspectDown
Definition: videoouttypes.h:56
@ kZoomHorizontalIn
Definition: videoouttypes.h:49
@ kZoomHorizontalOut
Definition: videoouttypes.h:50
StereoscopicMode