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