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
30#include "libmythbase/mythconfig.h"
35#include "mythplayer.h"
36#include "mythvideobounds.h"
37
38#define LOC QString("VideoBounds: ")
39
40static inline QRect SCALED_RECT(QRect src, qreal scale)
41{ return {static_cast<int>(src.left() * scale),
42 static_cast<int>(src.top() * scale),
43 static_cast<int>(src.width() * scale),
44 static_cast<int>(src.height() * scale) }; }
45
46static float fix_aspect(float raw);
47static float snap(float value, float snapto, float diff);
48
54
56 : m_dbMove( { gCoreContext->GetNumSetting("xScanDisplacement", 0),
57 gCoreContext->GetNumSetting("yScanDisplacement", 0) }),
58 m_dbUseGUISize(gCoreContext->GetBoolSetting("GuiSizeForTV", false)),
59 m_dbAspectOverride(static_cast<AspectOverrideMode>(gCoreContext->GetNumSetting("AspectOverride", 0))),
60 m_dbAdjustFill(static_cast<AdjustFillMode>(gCoreContext->GetNumSetting("AdjustFill", 0)))
61{
62}
63
70{
73}
74
76{
77 if (m_display)
78 {
79 LOG(VB_GENERAL, LOG_WARNING, LOC + "Already have a display");
80 return;
81 }
82
83 m_display = mDisplay;
85#ifdef Q_OS_MACOS
87#endif
88}
89
90void MythVideoBounds::ScreenChanged(QScreen */*screen*/)
91{
93 MoveResize();
94}
95
97{
98 // PopulateGeometry will update m_devicePixelRatio
101 MoveResize();
102}
103
105{
106 if (!m_display)
107 return;
108
109 QScreen *screen = m_display->GetCurrentScreen();
110 if (!screen)
111 return;
112
113#ifdef Q_OS_MACOS
114 m_devicePixelRatio = screen->devicePixelRatio();
115#endif
116
118 {
119 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using all screens %1x%2")
120 .arg(screen->virtualGeometry().width()).arg(screen->virtualGeometry().height()));
121 return;
122 }
123
124 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window using screen '%1' %2x%3")
125 .arg(screen->name()).arg(screen->geometry().width()).arg(screen->geometry().height()));
126}
127
139{
140 // for 'portrait' mode, rotate the 'screen' dimensions
141 float tempdisplayaspect = m_displayAspect;
142 bool rotate = (m_rotation == 90) || (m_rotation == -90);
143 if (rotate)
144 Rotate();
145
146 // Preset all image placement and sizing variables.
147 m_videoRect = QRect(QPoint(0, 0), m_videoDispDim);
149
150 // Apply various modifications
154
155 // Interactive TV (MHEG) embedding
156 if (m_itvResizing)
158
159 // and switch back
160 if (rotate)
161 Rotate();
162 m_displayAspect = tempdisplayaspect;
163
165
166 // TODO fine tune when these are emitted - it is not enough to just check whether the values
167 // have changed
172
174}
175
186{
187 m_dbMove = QPoint(m_dbMove.y(), m_dbMove.x());
188 float temp = m_dbHorizScale;
190 m_dbVertScale = temp;
191 temp = m_manualHorizScale;
193 m_manualVertScale = temp;
194 m_manualMove = QPoint(m_manualMove.y(), m_manualMove.x());
196
198 QSize(m_displayVisibleRect.height(), m_displayVisibleRect.width()));
199 m_displayVideoRect = QRect(QPoint(m_displayVideoRect.top(), m_displayVideoRect.left()),
200 QSize(m_displayVideoRect.height(), m_displayVideoRect.width()));
209}
210
221{
222 if (m_dbVertScale > 0)
223 {
224 // Veritcal overscan. Move the Y start point in original image.
225 float tmp = 1.0F - (2.0F * m_dbVertScale);
226 m_videoRect.moveTop(qRound(m_videoRect.height() * m_dbVertScale));
227 m_videoRect.setHeight(qRound(m_videoRect.height() * tmp));
228
229 // If there is an offset, apply it now that we have a room.
230 int yoff = m_dbMove.y();
231 if (yoff > 0)
232 {
233 // To move the image down, move the start point up.
234 // Don't offset the image more than we have overscanned.
235 yoff = std::min(m_videoRect.top(), yoff);
236 m_videoRect.moveTop(m_videoRect.top() - yoff);
237 }
238 else if (yoff < 0)
239 {
240 // To move the image up, move the start point down.
241 // Don't offset the image more than we have overscanned.
242 if (abs(yoff) > m_videoRect.top())
243 yoff = 0 - m_videoRect.top();
244 m_videoRect.moveTop(m_videoRect.top() - yoff);
245 }
246 }
247 else if (m_dbVertScale < 0)
248 {
249 // Vertical underscan. Move the starting Y point in the display window.
250 // Use the abolute value of scan factor.
251 float vscanf = fabs(m_dbVertScale);
252 float tmp = 1.0F - (2.0F * vscanf);
253
254 m_displayVideoRect.moveTop(qRound(m_displayVisibleRect.height() * vscanf) + m_displayVisibleRect.top());
255 m_displayVideoRect.setHeight(qRound(m_displayVisibleRect.height() * tmp));
256
257 // Now offset the image within the extra blank space created by
258 // underscanning. To move the image down, increase the Y offset
259 // inside the display window.
260 int yoff = m_dbMove.y();
261 if (yoff > 0)
262 {
263 // Don't offset more than we have underscanned.
264 yoff = std::min(m_displayVideoRect.top(), yoff);
265 m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
266 }
267 else if (yoff < 0)
268 {
269 // Don't offset more than we have underscanned.
270 if (abs(yoff) > m_displayVideoRect.top())
271 yoff = 0 - m_displayVideoRect.top();
272 m_displayVideoRect.moveTop(m_displayVideoRect.top() + yoff);
273 }
274 }
275
276 // Horizontal.. comments, same as vertical...
277 if (m_dbHorizScale > 0)
278 {
279 float tmp = 1.0F - (2.0F * m_dbHorizScale);
280 m_videoRect.moveLeft(qRound(m_videoDispDim.width() * m_dbHorizScale));
281 m_videoRect.setWidth(qRound(m_videoDispDim.width() * tmp));
282
283 int xoff = m_dbMove.x();
284 if (xoff > 0)
285 {
286 xoff = std::min(m_videoRect.left(), xoff);
287 m_videoRect.moveLeft(m_videoRect.left() - xoff);
288 }
289 else if (xoff < 0)
290 {
291 if (abs(xoff) > m_videoRect.left())
292 xoff = 0 - m_videoRect.left();
293 m_videoRect.moveLeft(m_videoRect.left() - xoff);
294 }
295 }
296 else if (m_dbHorizScale < 0)
297 {
298 float hscanf = fabs(m_dbHorizScale);
299 float tmp = 1.0F - (2.0F * hscanf);
300
301 m_displayVideoRect.moveLeft(qRound(m_displayVisibleRect.width() * hscanf) + m_displayVisibleRect.left());
302 m_displayVideoRect.setWidth(qRound(m_displayVisibleRect.width() * tmp));
303
304 int xoff = m_dbMove.x();
305 if (xoff > 0)
306 {
307 xoff = std::min(m_displayVideoRect.left(), xoff);
308 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
309 }
310 else if (xoff < 0)
311 {
312 if (abs(xoff) > m_displayVideoRect.left())
313 xoff = 0 - m_displayVideoRect.left();
314 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + xoff);
315 }
316 }
317
318}
319
324{
325 if ((m_manualVertScale != 1.0F) || (m_manualHorizScale != 1.0F))
326 {
327 QSize newsz = QSize(qRound(m_displayVideoRect.width() * m_manualHorizScale),
328 qRound(m_displayVideoRect.height() * m_manualVertScale));
329 QSize tmp = (m_displayVideoRect.size() - newsz) / 2;
330 QPoint chgloc = QPoint(tmp.width(), tmp.height());
331 QPoint newloc = m_displayVideoRect.topLeft() + chgloc;
332
333 m_displayVideoRect = QRect(newloc, newsz);
334 }
335
336 if (m_manualMove.y())
337 {
338 int move_vert = m_manualMove.y() * m_displayVideoRect.height() / 100;
339 m_displayVideoRect.moveTop(m_displayVideoRect.top() + move_vert);
340 }
341
342 if (m_manualMove.x())
343 {
344 int move_horiz = m_manualMove.x() * m_displayVideoRect.width() / 100;
345 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + move_horiz);
346 }
347}
348
349// Code should take into account the aspect ratios of both the video as
350// well as the actual screen to allow proper letterboxing to take place.
352{
353 float disp_aspect = fix_aspect(GetDisplayAspect());
354 float aspect_diff = disp_aspect - m_videoAspectOverride;
355 bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.02F;
356 bool nomatch_with_fill = !aspects_match && ((kAdjustFill_HorizontalStretch == m_adjustFill) ||
358 bool nomatch_without_fill = (!aspects_match) && !nomatch_with_fill;
359
360 // Adjust for video/display aspect ratio mismatch
361 if (nomatch_with_fill && (disp_aspect > m_videoAspectOverride))
362 {
363 int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
364 m_displayVideoRect.moveTop(m_displayVideoRect.top() + ((m_displayVideoRect.height() - pixNeeded) / 2));
365 m_displayVideoRect.setHeight(pixNeeded);
366 }
367 else if (nomatch_with_fill)
368 {
369 int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
370 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + ((m_displayVideoRect.width() - pixNeeded) / 2));
371 m_displayVideoRect.setWidth(pixNeeded);
372 }
373 else if (nomatch_without_fill && (disp_aspect > m_videoAspectOverride))
374 {
375 int pixNeeded = qRound(((m_videoAspectOverride / disp_aspect) * static_cast<float>(m_displayVideoRect.width())));
376 m_displayVideoRect.moveLeft(m_displayVideoRect.left() + ((m_displayVideoRect.width() - pixNeeded) / 2));
377 m_displayVideoRect.setWidth(pixNeeded);
378 }
379 else if (nomatch_without_fill)
380 {
381 int pixNeeded = qRound(((disp_aspect / m_videoAspectOverride) * static_cast<float>(m_displayVideoRect.height())));
382 m_displayVideoRect.moveTop(m_displayVideoRect.top() + ((m_displayVideoRect.height() - pixNeeded) / 2));
383 m_displayVideoRect.setHeight(pixNeeded);
384 }
385
386 // Process letterbox zoom modes
388 {
389 // Zoom mode -- Expand by 4/3 and overscan.
390 // 1/6 of original is 1/8 of new
391 m_displayVideoRect = QRect(
392 m_displayVideoRect.left() - (m_displayVideoRect.width() / 6),
393 m_displayVideoRect.top() - (m_displayVideoRect.height() / 6),
394 m_displayVideoRect.width() * 4 / 3,
395 m_displayVideoRect.height() * 4 / 3);
396 }
397 else if (m_adjustFill == kAdjustFill_Half)
398 {
399 // Zoom mode -- Expand by 7/6 and overscan.
400 // Intended for eliminating the top bars on 14:9 material.
401 // Also good compromise for 4:3 material on 16:9 screen.
402 // Expanding by 7/6, so remove 1/6 of original from overscan;
403 // take half from each side, so remove 1/12.
404 m_displayVideoRect = QRect(
405 m_displayVideoRect.left() - (m_displayVideoRect.width() / 12),
406 m_displayVideoRect.top() - (m_displayVideoRect.height() / 12),
407 m_displayVideoRect.width() * 7 / 6,
408 m_displayVideoRect.height() * 7 / 6);
409 }
411 {
412 // Horizontal Stretch mode -- 1/6 of original is 1/8 of new
413 // Intended to be used to eliminate side bars on 4:3 material
414 // encoded to 16:9.
415 m_displayVideoRect.moveLeft(
416 m_displayVideoRect.left() - (m_displayVideoRect.width() / 6));
417
418 m_displayVideoRect.setWidth(m_displayVideoRect.width() * 4 / 3);
419 }
421 {
422 // Vertical Stretch mode -- 1/6 of original is 1/8 of new
423 // Intended to be used to eliminate top/bottom bars on 16:9
424 // material encoded to 4:3.
425 m_displayVideoRect.moveTop(
426 m_displayVideoRect.top() - (m_displayVideoRect.height() / 6));
427
428 m_displayVideoRect.setHeight(m_displayVideoRect.height() * 4 / 3);
429 }
430 else if (m_adjustFill == kAdjustFill_VerticalFill && m_displayVideoRect.height() > 0)
431 {
432 // Video fills screen vertically. May be cropped left and right
433 float factor = static_cast<float>(m_displayVisibleRect.height()) / static_cast<float>(m_displayVideoRect.height());
434 QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
435 qRound(m_displayVideoRect.height() * factor));
436 QSize temp = (m_displayVideoRect.size() - newsize) / 2;
437 QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
438 m_displayVideoRect = QRect(newloc, newsize);
439 }
441 {
442 // Video fills screen horizontally. May be cropped top and bottom
443 float factor = static_cast<float>(m_displayVisibleRect.width()) /
444 static_cast<float>(m_displayVideoRect.width());
445 QSize newsize = QSize(qRound(m_displayVideoRect.width() * factor),
446 qRound(m_displayVideoRect.height() * factor));
447 QSize temp = (m_displayVideoRect.size() - newsize) / 2;
448 QPoint newloc = m_displayVideoRect.topLeft() + QPoint(temp.width(), temp.height());
449 m_displayVideoRect = QRect(newloc, newsize);
450 }
451}
452
453bool MythVideoBounds::InitBounds(QSize VideoDim, QSize VideoDispDim,
454 float Aspect, QRect WindowRect)
455{
456 if (m_display)
457 {
458 QString dummy;
459 m_displayAspect = static_cast<float>(m_display->GetAspectRatio(dummy));
460 }
461
462 // Refresh the geometry in case the video mode has changed
464
465 // N.B. we are always confined to the window size so use that for the initial
466 // displayVisibleRect
467 m_rawWindowRect = WindowRect;
469 m_videoDispDim = Fix1088(VideoDispDim);
470 m_videoDim = VideoDim;
472
475 m_embedding = false;
476 SetVideoAspectRatio(Aspect);
477 MoveResize();
478 return true;
479}
480
482{
483 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Window Rect: %1x%2+%3+%4")
484 .arg(m_windowRect.width()).arg(m_windowRect.height())
485 .arg(m_windowRect.left()).arg(m_windowRect.top()));
486 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display Rect: %1x%2+%3+%4 Aspect: %5")
487 .arg(m_displayVideoRect.width()).arg(m_displayVideoRect.height())
488 .arg(m_displayVideoRect.left()).arg(m_displayVideoRect.top())
489 .arg(static_cast<qreal>(fix_aspect(GetDisplayAspect()))));
490 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Video Rect: %1x%2+%3+%4 Aspect: %5")
491 .arg(m_videoRect.width()).arg(m_videoRect.height())
492 .arg(m_videoRect.left()).arg(m_videoRect.top())
493 .arg(static_cast<qreal>(m_videoAspectOverride)));
494}
495
504{
505 m_videoAspect = aspect;
507}
508
515{
516 if (!qFuzzyCompare(Aspect, m_videoAspect))
517 {
518 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect ratio: '%1'")
519 .arg(static_cast<double>(Aspect)));
520 SetVideoAspectRatio(Aspect);
521 MoveResize();
522 }
523}
524
526void MythVideoBounds::SourceChanged(QSize VideoDim, QSize VideoDispDim, float Aspect)
527{
528 if (Aspect < 0.0F)
529 Aspect = m_videoAspect;
530
531 QSize newvideodispdim = Fix1088(VideoDispDim);
532
533 if (!((VideoDim == m_videoDim) && (newvideodispdim == m_videoDispDim) &&
534 qFuzzyCompare(Aspect + 100.0F, m_videoAspect + 100.0F)))
535 {
536 m_videoDispDim = newvideodispdim;
537 m_videoDim = VideoDim;
538 SetVideoAspectRatio(Aspect);
539 LOG(VB_PLAYBACK, LOG_INFO, LOC +
540 QString("New video parameters: Size %1x%2 DisplaySize: %3x%4 Aspect: %5")
541 .arg(m_videoDim.width()).arg(m_videoDim.height())
542 .arg(m_videoDispDim.width()).arg(m_videoDispDim.height())
543 .arg(static_cast<double>(m_videoAspect)));
544 MoveResize();
545 }
546}
547
548QSize MythVideoBounds::Fix1088(QSize Dimensions)
549{
550 QSize result = Dimensions;
551 // 544 represents a 1088 field
552 if (result.width() == 1920 || result.width() == 1440)
553 {
554 if (result.height() == 1088)
555 result.setHeight(1080);
556 else if (result.height() == 544)
557 result.setHeight(540);
558 }
559 return result;
560}
561
568{
572 {
574 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New fill mode: '%1'").arg(toString(m_adjustFill)));
575 MoveResize();
576 }
577}
578
583{
584 float oldvert = m_dbVertScale;
585 float oldhoriz = m_dbHorizScale;
586
587 if (Change)
588 {
589 m_dbVertScale = gCoreContext->GetNumSetting("VertScanPercentage", 0) * 0.01F;
590 m_dbHorizScale = gCoreContext->GetNumSetting("HorizScanPercentage", 0) * 0.01F;
591 m_dbScalingAllowed = true;
592 }
593 else
594 {
595 m_dbVertScale = 0.0F;
596 m_dbHorizScale = 0.0F;
597 m_dbScalingAllowed = false;
598 }
599
600 if (!(qFuzzyCompare(oldvert + 100.0F, m_dbVertScale + 100.0F) &&
601 qFuzzyCompare(oldhoriz + 100.0F, m_dbHorizScale + 100.0F)))
602 {
603 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Over/underscan. V: %1, H: %2")
604 .arg(static_cast<double>(m_dbVertScale)).arg(static_cast<double>(m_dbHorizScale)));
605 MoveResize();
606 }
607}
608
609void MythVideoBounds::SetDisplayAspect(float DisplayAspect)
610{
611 if (!qFuzzyCompare(DisplayAspect + 10.0F, m_displayAspect + 10.0F))
612 {
613 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New display aspect: %1")
614 .arg(static_cast<double>(DisplayAspect)));
615 m_displayAspect = DisplayAspect;
616 MoveResize();
617 }
618}
619
621{
622 if (Size != m_rawWindowRect.size())
623 {
624 QRect rect(m_rawWindowRect.topLeft(), Size);
625 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New window rect: %1x%2+%3+%4")
626 .arg(rect.width()).arg(rect.height()).arg(rect.left()).arg(rect.top()));
627 m_rawWindowRect = rect;
629 MoveResize();
630 }
631}
632
634{
635 QRect oldrect = m_rawItvDisplayVideoRect;
636 if (Rect.isEmpty())
637 {
638 m_itvResizing = false;
639 m_itvDisplayVideoRect = QRect();
640 m_rawItvDisplayVideoRect = QRect();
641 }
642 else
643 {
644 m_itvResizing = true;
647 }
648 if (m_rawItvDisplayVideoRect != oldrect)
649 {
650 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New ITV display rect: %1x%2+%3+%4 (Scale: %5)")
651 .arg(m_itvDisplayVideoRect.width()).arg(m_itvDisplayVideoRect.height())
652 .arg(m_itvDisplayVideoRect.left()).arg(m_itvDisplayVideoRect.right())
653 .arg(m_devicePixelRatio));
654 MoveResize();
655 }
656}
657
663{
664 if (Rotation == m_rotation)
665 return;
666 if ((Rotation < -180) || (Rotation > 180))
667 return;
668
669 m_rotation = Rotation;
670 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New rotation: %1").arg(m_rotation));
671 MoveResize();
672}
673
677void MythVideoBounds::ResizeDisplayWindow(QRect Rect, bool SaveVisibleRect)
678{
679 if (SaveVisibleRect)
682 MoveResize();
683}
684
685void MythVideoBounds::EmbedPlayback(bool Embed, QRect Rect)
686{
687 if (Embed)
688 {
689 if (m_embedding && (Rect == m_rawEmbeddingRect))
690 return;
691
692 m_rawEmbeddingRect = Rect;
694 bool savevisiblerect = !m_embedding;
695 m_embedding = true;
696 m_embeddingHidden = Rect.isEmpty();
698 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New embedding rect: %1x%2+%3+%4 (Scale: %5)")
699 .arg(m_embeddingRect.width()).arg(m_embeddingRect.height())
700 .arg(m_embeddingRect.left()).arg(m_embeddingRect.top())
701 .arg(m_devicePixelRatio));
702 ResizeDisplayWindow(m_displayVideoRect, savevisiblerect);
703 return;
704 }
705
706 if (!m_embedding)
707 return;
708 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Stopped embedding");
709 m_rawEmbeddingRect = QRect();
710 m_embeddingRect = QRect();
712 m_embedding = false;
713 m_embeddingHidden = false;
714 MoveResize();
715}
716
724{
725 if (AspectMode == kAspect_Toggle)
726 AspectMode = static_cast<AspectOverrideMode>(((m_videoAspectOverrideMode + 1) % kAspect_END));
727
728 if (m_videoAspectOverrideMode != AspectMode)
729 {
730 m_videoAspectOverrideMode = AspectMode;
731 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New video aspect override: '%1'")
734 MoveResize();
735 }
736
738}
739
745{
746 if (IsEmbedding())
747 return false;
748
749 return m_displayVideoRect.contains(m_windowRect);
750}
751
757{
758 QRegion visible(m_windowRect);
759 QRegion video(m_displayVideoRect);
760 return visible.subtracted(video);
761}
762
768{
769 float oldvertscale = m_manualVertScale;
770 float oldhorizscale = m_manualHorizScale;
771 QPoint oldmove = m_manualMove;
772
773 const float zf = 0.02F;
774 if (kZoomHome == Direction)
775 {
776 m_manualVertScale = 1.0F;
777 m_manualHorizScale = 1.0F;
778 m_manualMove = QPoint(0, 0);
779 }
780 else if (kZoomIn == Direction)
781 {
784 {
785 m_manualHorizScale += zf;
786 m_manualVertScale += zf;
787 }
788 }
789 else if (kZoomOut == Direction)
790 {
793 {
794 m_manualHorizScale -= zf;
795 m_manualVertScale -= zf;
796 }
797 }
798 else if (kZoomAspectUp == Direction)
799 {
802 {
803 m_manualHorizScale += zf;
804 m_manualVertScale -= zf;
805 }
806 }
807 else if (kZoomAspectDown == Direction)
808 {
811 {
812 m_manualHorizScale -= zf;
813 m_manualVertScale += zf;
814 }
815 }
816 else if (kZoomVerticalIn == Direction)
817 {
819 m_manualVertScale += zf;
820 }
821 else if (kZoomVerticalOut == Direction)
822 {
824 m_manualVertScale -= zf;
825 }
826 else if (kZoomHorizontalIn == Direction)
827 {
829 m_manualHorizScale += zf;
830 }
831 else if (kZoomHorizontalOut == Direction)
832 {
834 m_manualHorizScale -= zf;
835 }
836 else if (kZoomUp == Direction && (m_manualMove.y() < +kManualZoomMaxMove))
837 {
838 m_manualMove.setY(m_manualMove.y() + 1);
839 }
840 else if (kZoomDown == Direction && (m_manualMove.y() > -kManualZoomMaxMove))
841 {
842 m_manualMove.setY(m_manualMove.y() - 1);
843 }
844 else if (kZoomLeft == Direction && (m_manualMove.x() < +kManualZoomMaxMove))
845 {
846 m_manualMove.setX(m_manualMove.x() + 2);
847 }
848 else if (kZoomRight == Direction && (m_manualMove.x() > -kManualZoomMaxMove))
849 {
850 m_manualMove.setX(m_manualMove.x() - 2);
851 }
852
855
856 if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
857 qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
858 {
859 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New zoom: Offset %1x%2 HScale %3 VScale %4")
860 .arg(m_manualMove.x()).arg(m_manualMove.y())
861 .arg(static_cast<double>(m_manualHorizScale))
862 .arg(static_cast<double>(m_manualVertScale)));
863 MoveResize();
864 }
865}
866
868{
869 float oldvertscale = m_manualVertScale;
870 float oldhorizscale = m_manualHorizScale;
871 QPoint oldmove = m_manualMove;
872
873 if (m_bottomLine)
874 {
875 m_manualMove.setX(0);
876 m_manualMove.setY(0);
877 m_manualHorizScale = 1.0;
878 m_manualVertScale = 1.0;
879 m_bottomLine = false;
880 }
881 else
882 {
883 const float zf = 0.02F;
884 m_manualMove.setX(gCoreContext->GetNumSetting("OSDMoveXBottomLine", 0));
885 m_manualMove.setY(gCoreContext->GetNumSetting("OSDMoveYBottomLine", 5));
886 float h = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleHBottomLine", 100)) / 100.0F;
887 m_manualHorizScale = snap(h, 1.0F, zf / 2.0F);
888 float v = static_cast<float>(gCoreContext->GetNumSetting("OSDScaleVBottomLine", 112)) / 100.0F;
889 m_manualVertScale = snap(v, 1.0F, zf / 2.0F);
890 m_bottomLine = true;
891 }
892
893 if (!((oldmove == m_manualMove) && qFuzzyCompare(m_manualVertScale + 100.0F, oldvertscale + 100.0F) &&
894 qFuzzyCompare(m_manualHorizScale + 100.0F, oldhorizscale + 100.0F)))
895 {
896 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New custom zoom: Offset %1x%2 HScale %3 VScale %4")
897 .arg(m_manualMove.x()).arg(m_manualMove.y())
898 .arg(static_cast<double>(m_manualHorizScale))
899 .arg(static_cast<double>(m_manualVertScale)));
900 MoveResize();
901 }
902
904}
905
907{
908 gCoreContext->SaveSetting("OSDMoveXBottomLine", m_manualMove.x());
909 gCoreContext->SaveSetting("OSDMoveYBottomLine", m_manualMove.y());
910 gCoreContext->SaveSetting("OSDScaleHBottomLine", static_cast<int>(m_manualHorizScale * 100.0F));
911 gCoreContext->SaveSetting("OSDScaleVBottomLine", static_cast<int>(m_manualVertScale * 100.0F));
912
913 emit UpdateOSDMessage("Current 'Manual Zoom' saved for 'BottomLine'.");
914}
915
917static float fix_aspect(float raw)
918{
919 // Check if close to 4:3
920 if (fabs(raw - 1.333333F) < 0.05F)
921 raw = 1.333333F;
922
923 // Check if close to 16:9
924 if (fabs(raw - 1.777777F) < 0.05F)
925 raw = 1.777777F;
926
927 return raw;
928}
929
930static float snap(float value, float snapto, float diff)
931{
932 if ((value + diff > snapto) && (value - diff < snapto))
933 return snapto;
934 return value;
935}
936
938{
939 if (Mode != m_stereoOverride)
940 {
944 }
945}
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.
void PhysicalDPIChanged(qreal DPI)
QScreen * GetCurrentScreen()
Return a pointer to the screen to use.
void CurrentScreenChanged(QScreen *qScreen)
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 ScreenChanged(QScreen *screen)
void Zoom(ZoomDirection Direction)
Sets up zooming into to different parts of the video.
StereoscopicMode m_stereoOverride
void PhysicalDPIChanged(qreal)
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()
static guint32 * tmp
Definition: goom_core.cpp:26
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:23
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