2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GraphicContext.h"
11 #include "ServiceBroker.h"
12 #include "WinSystem.h"
13 #include "application/ApplicationComponents.h"
14 #include "application/ApplicationPlayer.h"
15 #include "guilib/GUIComponent.h"
16 #include "guilib/GUIWindowManager.h"
17 #include "guilib/TextureManager.h"
18 #include "guilib/gui3d.h"
19 #include "input/InputManager.h"
20 #include "messaging/ApplicationMessenger.h"
21 #include "rendering/RenderSystem.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/DisplaySettings.h"
24 #include "settings/Settings.h"
25 #include "settings/SettingsComponent.h"
26 #include "settings/lib/Setting.h"
27 #include "utils/log.h"
32 using KODI::UTILS::COLOR::Color
;
34 CGraphicContext::CGraphicContext() = default;
35 CGraphicContext::~CGraphicContext() = default;
37 void CGraphicContext::SetOrigin(float x
, float y
)
39 if (!m_origins
.empty())
40 m_origins
.push(CPoint(x
,y
) + m_origins
.top());
42 m_origins
.emplace(x
, y
);
44 AddTransform(TransformMatrix::CreateTranslation(x
, y
));
47 void CGraphicContext::RestoreOrigin()
49 if (!m_origins
.empty())
54 // add a new clip region, intersecting with the previous clip region.
55 bool CGraphicContext::SetClipRegion(float x
, float y
, float w
, float h
)
56 { // transform from our origin
58 if (!m_origins
.empty())
59 origin
= m_origins
.top();
61 // ok, now intersect with our old clip region
62 CRect
rect(x
, y
, x
+ w
, y
+ h
);
64 if (!m_clipRegions
.empty())
66 // intersect with original clip region
67 rect
.Intersect(m_clipRegions
.top());
73 m_clipRegions
.push(rect
);
75 // here we could set the hardware clipping, if applicable
79 void CGraphicContext::RestoreClipRegion()
81 if (!m_clipRegions
.empty())
84 // here we could reset the hardware clipping, if applicable
87 void CGraphicContext::ClipRect(CRect
&vertex
, CRect
&texture
, CRect
*texture2
)
89 // this is the software clipping routine. If the graphics hardware is set to do the clipping
90 // (eg via SetClipPlane in D3D for instance) then this routine is unneeded.
91 if (!m_clipRegions
.empty())
93 // take a copy of the vertex rectangle and intersect
94 // it with our clip region (moved to the same coordinate system)
95 CRect
clipRegion(m_clipRegions
.top());
96 if (!m_origins
.empty())
97 clipRegion
-= m_origins
.top();
98 CRect
original(vertex
);
99 vertex
.Intersect(clipRegion
);
100 // and use the original to compute the texture coordinates
101 if (original
!= vertex
)
103 float scaleX
= texture
.Width() / original
.Width();
104 float scaleY
= texture
.Height() / original
.Height();
105 texture
.x1
+= (vertex
.x1
- original
.x1
) * scaleX
;
106 texture
.y1
+= (vertex
.y1
- original
.y1
) * scaleY
;
107 texture
.x2
+= (vertex
.x2
- original
.x2
) * scaleX
;
108 texture
.y2
+= (vertex
.y2
- original
.y2
) * scaleY
;
111 scaleX
= texture2
->Width() / original
.Width();
112 scaleY
= texture2
->Height() / original
.Height();
113 texture2
->x1
+= (vertex
.x1
- original
.x1
) * scaleX
;
114 texture2
->y1
+= (vertex
.y1
- original
.y1
) * scaleY
;
115 texture2
->x2
+= (vertex
.x2
- original
.x2
) * scaleX
;
116 texture2
->y2
+= (vertex
.y2
- original
.y2
) * scaleY
;
122 CRect
CGraphicContext::GetClipRegion()
124 if (m_clipRegions
.empty())
125 return CRect(0, 0, m_iScreenWidth
, m_iScreenHeight
);
126 CRect
clipRegion(m_clipRegions
.top());
127 if (!m_origins
.empty())
128 clipRegion
-= m_origins
.top();
132 void CGraphicContext::AddGUITransform()
134 m_transforms
.push(m_finalTransform
);
135 m_finalTransform
= m_guiTransform
;
138 TransformMatrix
CGraphicContext::AddTransform(const TransformMatrix
&matrix
)
140 m_transforms
.push(m_finalTransform
);
141 m_finalTransform
.matrix
*= matrix
;
142 return m_finalTransform
.matrix
;
145 void CGraphicContext::SetTransform(const TransformMatrix
&matrix
)
147 m_transforms
.push(m_finalTransform
);
148 m_finalTransform
.matrix
= matrix
;
151 void CGraphicContext::SetTransform(const TransformMatrix
&matrix
, float scaleX
, float scaleY
)
153 m_transforms
.push(m_finalTransform
);
154 m_finalTransform
.matrix
= matrix
;
155 m_finalTransform
.scaleX
= scaleX
;
156 m_finalTransform
.scaleY
= scaleY
;
159 void CGraphicContext::RemoveTransform()
161 if (!m_transforms
.empty())
163 m_finalTransform
= m_transforms
.top();
168 bool CGraphicContext::SetViewPort(float fx
, float fy
, float fwidth
, float fheight
, bool intersectPrevious
/* = false */)
170 // transform coordinates - we may have a rotation which changes the positioning of the
171 // minimal and maximal viewport extents. We currently go to the maximal extent.
174 x
[1] = x
[2] = fx
+ fwidth
;
176 y
[2] = y
[3] = fy
+ fheight
;
177 float minX
= (float)m_iScreenWidth
;
179 float minY
= (float)m_iScreenHeight
;
181 for (int i
= 0; i
< 4; i
++)
184 ScaleFinalCoords(x
[i
], y
[i
], z
);
185 if (x
[i
] < minX
) minX
= x
[i
];
186 if (x
[i
] > maxX
) maxX
= x
[i
];
187 if (y
[i
] < minY
) minY
= y
[i
];
188 if (y
[i
] > maxY
) maxY
= y
[i
];
191 int newLeft
= (int)(minX
+ 0.5f
);
192 int newTop
= (int)(minY
+ 0.5f
);
193 int newRight
= (int)(maxX
+ 0.5f
);
194 int newBottom
= (int)(maxY
+ 0.5f
);
195 if (intersectPrevious
)
197 CRect oldviewport
= m_viewStack
.top();
198 // do the intersection
199 int oldLeft
= (int)oldviewport
.x1
;
200 int oldTop
= (int)oldviewport
.y1
;
201 int oldRight
= (int)oldviewport
.x2
;
202 int oldBottom
= (int)oldviewport
.y2
;
203 if (newLeft
>= oldRight
|| newTop
>= oldBottom
|| newRight
<= oldLeft
|| newBottom
<= oldTop
)
204 { // empty intersection - return false to indicate no rendering should occur
207 // ok, they intersect, do the intersection
208 if (newLeft
< oldLeft
) newLeft
= oldLeft
;
209 if (newTop
< oldTop
) newTop
= oldTop
;
210 if (newRight
> oldRight
) newRight
= oldRight
;
211 if (newBottom
> oldBottom
) newBottom
= oldBottom
;
213 // check range against screen size
214 if (newRight
<= 0 || newBottom
<= 0 ||
215 newTop
>= m_iScreenHeight
|| newLeft
>= m_iScreenWidth
||
216 newLeft
>= newRight
|| newTop
>= newBottom
)
217 { // no intersection with the screen
220 // intersection with the screen
221 if (newLeft
< 0) newLeft
= 0;
222 if (newTop
< 0) newTop
= 0;
223 if (newRight
> m_iScreenWidth
) newRight
= m_iScreenWidth
;
224 if (newBottom
> m_iScreenHeight
) newBottom
= m_iScreenHeight
;
226 assert(newLeft
< newRight
);
227 assert(newTop
< newBottom
);
229 CRect
newviewport((float)newLeft
, (float)newTop
, (float)newRight
, (float)newBottom
);
231 m_viewStack
.push(newviewport
);
233 newviewport
= StereoCorrection(newviewport
);
234 CServiceBroker::GetRenderSystem()->SetViewPort(newviewport
);
237 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
241 void CGraphicContext::RestoreViewPort()
243 if (m_viewStack
.size() <= 1) return;
246 CRect viewport
= StereoCorrection(m_viewStack
.top());
247 CServiceBroker::GetRenderSystem()->SetViewPort(viewport
);
249 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
252 CPoint
CGraphicContext::StereoCorrection(const CPoint
&point
) const
256 if(m_stereoMode
== RENDER_STEREO_MODE_SPLIT_HORIZONTAL
)
258 const RESOLUTION_INFO info
= GetResInfo();
260 if(m_stereoView
== RENDER_STEREO_VIEW_RIGHT
)
261 res
.y
+= info
.iHeight
+ info
.iBlanking
;
263 if(m_stereoMode
== RENDER_STEREO_MODE_SPLIT_VERTICAL
)
265 const RESOLUTION_INFO info
= GetResInfo();
267 if(m_stereoView
== RENDER_STEREO_VIEW_RIGHT
)
268 res
.x
+= info
.iWidth
+ info
.iBlanking
;
273 CRect
CGraphicContext::StereoCorrection(const CRect
&rect
) const
275 CRect
res(StereoCorrection(rect
.P1())
276 , StereoCorrection(rect
.P2()));
280 void CGraphicContext::SetScissors(const CRect
&rect
)
283 m_scissors
.Intersect(CRect(0,0,(float)m_iScreenWidth
, (float)m_iScreenHeight
));
284 CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors
));
287 const CRect
&CGraphicContext::GetScissors() const
292 void CGraphicContext::ResetScissors()
294 m_scissors
.SetRect(0, 0, (float)m_iScreenWidth
, (float)m_iScreenHeight
);
295 CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors
));
298 const CRect
CGraphicContext::GetViewWindow() const
300 if (m_bCalibrating
|| m_bFullScreenVideo
)
303 RESOLUTION_INFO info
= GetResInfo();
304 rect
.x1
= (float)info
.Overscan
.left
;
305 rect
.y1
= (float)info
.Overscan
.top
;
306 rect
.x2
= (float)info
.Overscan
.right
;
307 rect
.y2
= (float)info
.Overscan
.bottom
;
313 void CGraphicContext::SetViewWindow(float left
, float top
, float right
, float bottom
)
315 m_videoRect
.x1
= ScaleFinalXCoord(left
, top
);
316 m_videoRect
.y1
= ScaleFinalYCoord(left
, top
);
317 m_videoRect
.x2
= ScaleFinalXCoord(right
, bottom
);
318 m_videoRect
.y2
= ScaleFinalYCoord(right
, bottom
);
321 void CGraphicContext::SetFullScreenVideo(bool bOnOff
)
323 std::unique_lock
<CCriticalSection
> lock(*this);
325 m_bFullScreenVideo
= bOnOff
;
327 if (m_bFullScreenRoot
)
329 bool bTriggerUpdateRes
= false;
330 auto& components
= CServiceBroker::GetAppComponents();
331 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
332 if (m_bFullScreenVideo
)
333 bTriggerUpdateRes
= true;
336 bool allowDesktopRes
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE
) == ADJUST_REFRESHRATE_ALWAYS
;
337 if (!allowDesktopRes
)
339 if (appPlayer
->IsPlayingVideo())
340 bTriggerUpdateRes
= true;
344 bool allowResolutionChangeOnStop
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE
) != ADJUST_REFRESHRATE_ON_START
;
345 RESOLUTION targetResolutionOnStop
= RES_DESKTOP
;
346 if (bTriggerUpdateRes
)
347 appPlayer
->TriggerUpdateResolution();
348 else if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP
)
350 targetResolutionOnStop
= CDisplaySettings::GetInstance().GetCurrentResolution();
353 if (allowResolutionChangeOnStop
&& !bTriggerUpdateRes
)
355 SetVideoResolution(targetResolutionOnStop
, false);
359 SetVideoResolution(RES_WINDOW
, false);
362 bool CGraphicContext::IsFullScreenVideo() const
364 return m_bFullScreenVideo
;
367 bool CGraphicContext::IsCalibrating() const
369 return m_bCalibrating
;
372 void CGraphicContext::SetCalibrating(bool bOnOff
)
374 m_bCalibrating
= bOnOff
;
377 bool CGraphicContext::IsValidResolution(RESOLUTION res
)
379 if (res
>= RES_WINDOW
&& (size_t) res
< CDisplaySettings::GetInstance().ResolutionInfoSize())
387 // call SetVideoResolutionInternal and ensure its done from mainthread
388 void CGraphicContext::SetVideoResolution(RESOLUTION res
, bool forceUpdate
)
390 if (CServiceBroker::GetAppMessenger()->IsProcessThread())
392 SetVideoResolutionInternal(res
, forceUpdate
);
396 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_SETVIDEORESOLUTION
, res
, forceUpdate
? 1 : 0);
400 void CGraphicContext::SetVideoResolutionInternal(RESOLUTION res
, bool forceUpdate
)
402 RESOLUTION lastRes
= m_Resolution
;
404 // If the user asked us to guess, go with desktop
405 if (!IsValidResolution(res
))
410 // If we are switching to the same resolution and same window/full-screen, no need to do anything
411 if (!forceUpdate
&& res
== lastRes
&& m_bFullScreenRoot
== CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
)
416 if (res
>= RES_DESKTOP
)
418 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
= true;
419 m_bFullScreenRoot
= true;
423 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
= false;
424 m_bFullScreenRoot
= false;
427 std::unique_lock
<CCriticalSection
> lock(*this);
429 // FIXME Wayland windowing needs some way to "deny" resolution updates since what Kodi
430 // requests might not get actually set by the compositor.
431 // So in theory, m_iScreenWidth etc. would not need to be updated at all before the
432 // change is confirmed.
433 // But other windowing code expects these variables to be already set when
434 // SetFullScreen() is called, so set them anyway and remember the old values.
435 int origScreenWidth
= m_iScreenWidth
;
436 int origScreenHeight
= m_iScreenHeight
;
437 float origFPSOverride
= m_fFPSOverride
;
439 UpdateInternalStateWithResolution(res
);
440 RESOLUTION_INFO info_org
= CDisplaySettings::GetInstance().GetResolutionInfo(res
);
442 bool switched
= false;
443 if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
)
445 #if defined (TARGET_DARWIN) || defined (TARGET_WINDOWS)
446 bool blankOtherDisplays
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS
);
447 switched
= CServiceBroker::GetWinSystem()->SetFullScreen(true, info_org
, blankOtherDisplays
);
449 switched
= CServiceBroker::GetWinSystem()->SetFullScreen(true, info_org
, false);
452 else if (lastRes
>= RES_DESKTOP
)
453 switched
= CServiceBroker::GetWinSystem()->SetFullScreen(false, info_org
, false);
455 switched
= CServiceBroker::GetWinSystem()->ResizeWindow(info_org
.iWidth
, info_org
.iHeight
, -1, -1);
459 m_scissors
.SetRect(0, 0, (float)m_iScreenWidth
, (float)m_iScreenHeight
);
461 // make sure all stereo stuff are correctly setup
462 SetStereoView(RENDER_STEREO_VIEW_OFF
);
464 // update anyone that relies on sizing information
465 CServiceBroker::GetInputManager().SetMouseResolution(info_org
.iWidth
, info_org
.iHeight
, 1, 1);
467 CGUIComponent
*gui
= CServiceBroker::GetGUI();
469 gui
->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_WINDOW_RESIZE
);
474 m_iScreenWidth
= origScreenWidth
;
475 m_iScreenHeight
= origScreenHeight
;
476 m_fFPSOverride
= origFPSOverride
;
477 if (IsValidResolution(lastRes
))
479 m_Resolution
= lastRes
;
483 // FIXME Resolution has become invalid
484 // This happens e.g. when switching monitors and the new monitor has fewer
485 // resolutions than the old one. Fall back to RES_DESKTOP and hope that
486 // the real resolution is set soon.
487 // Again, must be fixed as part of a greater refactor.
488 m_Resolution
= RES_DESKTOP
;
493 void CGraphicContext::ApplyVideoResolution(RESOLUTION res
)
495 if (!IsValidResolution(res
))
497 CLog::LogF(LOGWARNING
, "Asked to apply invalid resolution {}, falling back to RES_DESKTOP",
502 if (res
>= RES_DESKTOP
)
504 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
= true;
505 m_bFullScreenRoot
= true;
509 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen
= false;
510 m_bFullScreenRoot
= false;
513 std::unique_lock
<CCriticalSection
> lock(*this);
515 UpdateInternalStateWithResolution(res
);
517 m_scissors
.SetRect(0, 0, (float)m_iScreenWidth
, (float)m_iScreenHeight
);
519 // make sure all stereo stuff are correctly setup
520 SetStereoView(RENDER_STEREO_VIEW_OFF
);
522 // update anyone that relies on sizing information
523 RESOLUTION_INFO info_org
= CDisplaySettings::GetInstance().GetResolutionInfo(res
);
524 CServiceBroker::GetInputManager().SetMouseResolution(info_org
.iWidth
, info_org
.iHeight
, 1, 1);
525 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_WINDOW_RESIZE
);
528 void CGraphicContext::UpdateInternalStateWithResolution(RESOLUTION res
)
530 RESOLUTION_INFO info_mod
= GetResInfo(res
);
532 m_iScreenWidth
= info_mod
.iWidth
;
533 m_iScreenHeight
= info_mod
.iHeight
;
538 void CGraphicContext::ApplyModeChange(RESOLUTION res
)
540 ApplyVideoResolution(res
);
541 CServiceBroker::GetWinSystem()->FinishModeChange(res
);
544 void CGraphicContext::ApplyWindowResize(int newWidth
, int newHeight
)
546 CServiceBroker::GetWinSystem()->SetWindowResolution(newWidth
, newHeight
);
547 ApplyVideoResolution(RES_WINDOW
);
548 CServiceBroker::GetWinSystem()->FinishWindowResize(newWidth
, newHeight
);
551 RESOLUTION
CGraphicContext::GetVideoResolution() const
556 void CGraphicContext::ResetOverscan(RESOLUTION_INFO
&res
)
558 res
.Overscan
.left
= 0;
559 res
.Overscan
.top
= 0;
560 res
.Overscan
.right
= res
.iWidth
;
561 res
.Overscan
.bottom
= res
.iHeight
;
564 void CGraphicContext::ResetOverscan(RESOLUTION res
, OVERSCAN
&overscan
)
569 RESOLUTION_INFO info
= GetResInfo(res
);
570 overscan
.right
= info
.iWidth
;
571 overscan
.bottom
= info
.iHeight
;
574 void CGraphicContext::ResetScreenParameters(RESOLUTION res
)
576 RESOLUTION_INFO
& info
= CDisplaySettings::GetInstance().GetResolutionInfo(res
);
578 info
.iSubtitles
= info
.iHeight
;
579 info
.fPixelRatio
= 1.0f
;
580 info
.iScreenWidth
= info
.iWidth
;
581 info
.iScreenHeight
= info
.iHeight
;
582 ResetOverscan(res
, info
.Overscan
);
585 void CGraphicContext::Clear()
587 CServiceBroker::GetRenderSystem()->InvalidateColorBuffer();
590 void CGraphicContext::Clear(Color color
)
592 CServiceBroker::GetRenderSystem()->ClearBuffers(color
);
595 void CGraphicContext::CaptureStateBlock()
597 CServiceBroker::GetRenderSystem()->CaptureStateBlock();
600 void CGraphicContext::ApplyStateBlock()
602 CServiceBroker::GetRenderSystem()->ApplyStateBlock();
605 const RESOLUTION_INFO
CGraphicContext::GetResInfo(RESOLUTION res
) const
607 RESOLUTION_INFO info
= CDisplaySettings::GetInstance().GetResolutionInfo(res
);
609 if(m_stereoMode
== RENDER_STEREO_MODE_SPLIT_HORIZONTAL
)
611 if((info
.dwFlags
& D3DPRESENTFLAG_MODE3DTB
) == 0)
613 info
.fPixelRatio
/= 2;
615 info
.dwFlags
|= D3DPRESENTFLAG_MODE3DTB
;
617 info
.iHeight
= (info
.iHeight
- info
.iBlanking
) / 2;
618 info
.Overscan
.top
/= 2;
619 info
.Overscan
.bottom
= (info
.Overscan
.bottom
- info
.iBlanking
) / 2;
620 info
.iSubtitles
= (info
.iSubtitles
- info
.iBlanking
) / 2;
623 if(m_stereoMode
== RENDER_STEREO_MODE_SPLIT_VERTICAL
)
625 if((info
.dwFlags
& D3DPRESENTFLAG_MODE3DSBS
) == 0)
627 info
.fPixelRatio
*= 2;
629 info
.dwFlags
|= D3DPRESENTFLAG_MODE3DSBS
;
631 info
.iWidth
= (info
.iWidth
- info
.iBlanking
) / 2;
632 info
.Overscan
.left
/= 2;
633 info
.Overscan
.right
= (info
.Overscan
.right
- info
.iBlanking
) / 2;
636 if (res
== m_Resolution
&& m_fFPSOverride
!= 0)
638 info
.fRefreshRate
= m_fFPSOverride
;
644 void CGraphicContext::SetResInfo(RESOLUTION res
, const RESOLUTION_INFO
& info
)
646 RESOLUTION_INFO
& curr
= CDisplaySettings::GetInstance().GetResolutionInfo(res
);
647 curr
.Overscan
= info
.Overscan
;
648 curr
.iSubtitles
= info
.iSubtitles
;
649 curr
.fPixelRatio
= info
.fPixelRatio
;
650 curr
.guiInsets
= info
.guiInsets
;
652 if(info
.dwFlags
& D3DPRESENTFLAG_MODE3DSBS
)
654 curr
.Overscan
.right
= info
.Overscan
.right
* 2 + info
.iBlanking
;
655 if((curr
.dwFlags
& D3DPRESENTFLAG_MODE3DSBS
) == 0)
656 curr
.fPixelRatio
/= 2.0f
;
659 if(info
.dwFlags
& D3DPRESENTFLAG_MODE3DTB
)
661 curr
.Overscan
.bottom
= info
.Overscan
.bottom
* 2 + info
.iBlanking
;
662 curr
.iSubtitles
= info
.iSubtitles
* 2 + info
.iBlanking
;
663 if((curr
.dwFlags
& D3DPRESENTFLAG_MODE3DTB
) == 0)
664 curr
.fPixelRatio
*= 2.0f
;
668 const RESOLUTION_INFO
CGraphicContext::GetResInfo() const
670 return GetResInfo(m_Resolution
);
673 void CGraphicContext::GetGUIScaling(const RESOLUTION_INFO
&res
, float &scaleX
, float &scaleY
, TransformMatrix
*matrix
/* = NULL */)
675 if (m_Resolution
!= RES_INVALID
)
677 // calculate necessary scalings
678 RESOLUTION_INFO info
= GetResInfo();
679 float fFromWidth
= (float)res
.iWidth
;
680 float fFromHeight
= (float)res
.iHeight
;
681 auto fToPosX
= info
.Overscan
.left
+ info
.guiInsets
.left
;
682 auto fToPosY
= info
.Overscan
.top
+ info
.guiInsets
.top
;
683 auto fToWidth
= info
.Overscan
.right
- info
.guiInsets
.right
- fToPosX
;
684 auto fToHeight
= info
.Overscan
.bottom
- info
.guiInsets
.bottom
- fToPosY
;
686 float fZoom
= (100 + CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_SKINZOOM
)) * 0.01f
;
689 fToPosX
-= fToWidth
* fZoom
* 0.5f
;
690 fToWidth
*= fZoom
+ 1.0f
;
692 // adjust for aspect ratio as zoom is given in the vertical direction and we don't
693 // do aspect ratio corrections in the gui code
694 fZoom
= fZoom
/ info
.fPixelRatio
;
695 fToPosY
-= fToHeight
* fZoom
* 0.5f
;
696 fToHeight
*= fZoom
+ 1.0f
;
698 scaleX
= fFromWidth
/ fToWidth
;
699 scaleY
= fFromHeight
/ fToHeight
;
702 TransformMatrix guiScaler
= TransformMatrix::CreateScaler(fToWidth
/ fFromWidth
, fToHeight
/ fFromHeight
, fToHeight
/ fFromHeight
);
703 TransformMatrix guiOffset
= TransformMatrix::CreateTranslation(fToPosX
, fToPosY
);
704 *matrix
= guiOffset
* guiScaler
;
709 scaleX
= scaleY
= 1.0f
;
715 void CGraphicContext::SetScalingResolution(const RESOLUTION_INFO
&res
, bool needsScaling
)
717 m_windowResolution
= res
;
718 if (needsScaling
&& m_Resolution
!= RES_INVALID
)
719 GetGUIScaling(res
, m_guiTransform
.scaleX
, m_guiTransform
.scaleY
, &m_guiTransform
.matrix
);
722 m_guiTransform
.Reset();
725 // reset our origin and camera
726 while (!m_origins
.empty())
728 m_origins
.emplace(.0f
, .0f
);
729 while (!m_cameras
.empty())
731 m_cameras
.emplace(0.5f
* m_iScreenWidth
, 0.5f
* m_iScreenHeight
);
732 while (!m_stereoFactors
.empty())
733 m_stereoFactors
.pop();
734 m_stereoFactors
.push(0.0f
);
736 // and reset the final transform
737 m_finalTransform
= m_guiTransform
;
740 void CGraphicContext::SetRenderingResolution(const RESOLUTION_INFO
&res
, bool needsScaling
)
742 std::unique_lock
<CCriticalSection
> lock(*this);
744 SetScalingResolution(res
, needsScaling
);
745 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
748 void CGraphicContext::SetStereoView(RENDER_STEREO_VIEW view
)
752 while(!m_viewStack
.empty())
755 CRect
viewport(0.0f
, 0.0f
, (float)m_iScreenWidth
, (float)m_iScreenHeight
);
757 m_viewStack
.push(viewport
);
759 viewport
= StereoCorrection(viewport
);
760 CServiceBroker::GetRenderSystem()->SetStereoMode(m_stereoMode
, m_stereoView
);
761 CServiceBroker::GetRenderSystem()->SetViewPort(viewport
);
762 CServiceBroker::GetRenderSystem()->SetScissors(viewport
);
765 void CGraphicContext::InvertFinalCoords(float &x
, float &y
) const
767 m_finalTransform
.matrix
.InverseTransformPosition(x
, y
);
770 float CGraphicContext::ScaleFinalXCoord(float x
, float y
) const
772 return m_finalTransform
.matrix
.TransformXCoord(x
, y
, 0);
775 float CGraphicContext::ScaleFinalYCoord(float x
, float y
) const
777 return m_finalTransform
.matrix
.TransformYCoord(x
, y
, 0);
780 float CGraphicContext::ScaleFinalZCoord(float x
, float y
) const
782 return m_finalTransform
.matrix
.TransformZCoord(x
, y
, 0);
785 void CGraphicContext::ScaleFinalCoords(float &x
, float &y
, float &z
) const
787 m_finalTransform
.matrix
.TransformPosition(x
, y
, z
);
790 float CGraphicContext::GetScalingPixelRatio() const
792 // assume the resolutions are different - we want to return the aspect ratio of the video resolution
793 // but only once it's been corrected for the skin -> screen coordinates scaling
794 return GetResInfo().fPixelRatio
* (m_finalTransform
.scaleY
/ m_finalTransform
.scaleX
);
797 void CGraphicContext::SetCameraPosition(const CPoint
&camera
)
799 // offset the camera from our current location (this is in XML coordinates) and scale it up to
800 // the screen resolution
802 if (!m_origins
.empty())
803 cam
+= m_origins
.top();
805 cam
.x
*= (float)m_iScreenWidth
/ m_windowResolution
.iWidth
;
806 cam
.y
*= (float)m_iScreenHeight
/ m_windowResolution
.iHeight
;
809 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
812 void CGraphicContext::RestoreCameraPosition()
813 { // remove the top camera from the stack
814 assert(m_cameras
.size());
816 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
819 void CGraphicContext::SetStereoFactor(float factor
)
821 m_stereoFactors
.push(factor
);
822 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
825 void CGraphicContext::RestoreStereoFactor()
826 { // remove the top factor from the stack
827 assert(m_stereoFactors
.size());
828 m_stereoFactors
.pop();
829 UpdateCameraPosition(m_cameras
.top(), m_stereoFactors
.top());
832 float CGraphicContext::GetNormalizedDepth(uint32_t depth
)
834 float normalizedDepth
= static_cast<float>(depth
);
835 normalizedDepth
/= m_layer
;
836 normalizedDepth
= normalizedDepth
* 2 - 1;
837 return normalizedDepth
;
840 float CGraphicContext::GetTransformDepth(int32_t depthOffset
)
842 float depth
= static_cast<float>(m_finalTransform
.matrix
.depth
+ depthOffset
);
844 depth
= depth
* 2 - 1;
848 CRect
CGraphicContext::GenerateAABB(const CRect
&rect
) const
850 // ------------------------
851 // |(x1, y1) (x2, y2)|
853 // |(x3, y3) (x4, y4)|
854 // ------------------------
856 float x1
= rect
.x1
, x2
= rect
.x2
, x3
= rect
.x1
, x4
= rect
.x2
;
857 float y1
= rect
.y1
, y2
= rect
.y1
, y3
= rect
.y2
, y4
= rect
.y2
;
860 ScaleFinalCoords(x1
, y1
, z
);
861 CServiceBroker::GetRenderSystem()->Project(x1
, y1
, z
);
864 ScaleFinalCoords(x2
, y2
, z
);
865 CServiceBroker::GetRenderSystem()->Project(x2
, y2
, z
);
868 ScaleFinalCoords(x3
, y3
, z
);
869 CServiceBroker::GetRenderSystem()->Project(x3
, y3
, z
);
872 ScaleFinalCoords(x4
, y4
, z
);
873 CServiceBroker::GetRenderSystem()->Project(x4
, y4
, z
);
875 return CRect( std::min(std::min(std::min(x1
, x2
), x3
), x4
),
876 std::min(std::min(std::min(y1
, y2
), y3
), y4
),
877 std::max(std::max(std::max(x1
, x2
), x3
), x4
),
878 std::max(std::max(std::max(y1
, y2
), y3
), y4
));
881 // NOTE: This routine is currently called (twice) every time there is a <camera>
882 // tag in the skin. It actually only has to be called before we render
883 // something, so another option is to just save the camera coordinates
884 // and then have a routine called before every draw that checks whether
885 // the camera has changed, and if so, changes it. Similarly, it could set
886 // the world transform at that point as well (or even combine world + view
887 // to cut down on one setting)
888 void CGraphicContext::UpdateCameraPosition(const CPoint
&camera
, const float &factor
)
890 float stereoFactor
= 0.f
;
891 if ( m_stereoMode
!= RENDER_STEREO_MODE_OFF
892 && m_stereoMode
!= RENDER_STEREO_MODE_MONO
893 && m_stereoView
!= RENDER_STEREO_VIEW_OFF
)
895 RESOLUTION_INFO res
= GetResInfo();
896 RESOLUTION_INFO desktop
= GetResInfo(RES_DESKTOP
);
897 float scaleRes
= (static_cast<float>(res
.iWidth
) / static_cast<float>(desktop
.iWidth
));
898 float scaleX
= static_cast<float>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_STEREOSTRENGTH
)) * scaleRes
;
899 stereoFactor
= factor
* (m_stereoView
== RENDER_STEREO_VIEW_LEFT
? scaleX
: -scaleX
);
901 CServiceBroker::GetRenderSystem()->SetCameraPosition(camera
, m_iScreenWidth
, m_iScreenHeight
, stereoFactor
);
904 bool CGraphicContext::RectIsAngled(float x1
, float y1
, float x2
, float y2
) const
905 { // need only test 3 points, as they must be co-planer
906 if (m_finalTransform
.matrix
.TransformZCoord(x1
, y1
, 0)) return true;
907 if (m_finalTransform
.matrix
.TransformZCoord(x2
, y2
, 0)) return true;
908 if (m_finalTransform
.matrix
.TransformZCoord(x1
, y2
, 0)) return true;
912 const TransformMatrix
&CGraphicContext::GetGUIMatrix() const
914 return m_finalTransform
.matrix
;
917 float CGraphicContext::GetGUIScaleX() const
919 return m_finalTransform
.scaleX
;
922 float CGraphicContext::GetGUIScaleY() const
924 return m_finalTransform
.scaleY
;
927 Color
CGraphicContext::MergeAlpha(Color color
) const
929 Color alpha
= m_finalTransform
.matrix
.TransformAlpha((color
>> 24) & 0xff);
930 if (alpha
> 255) alpha
= 255;
931 return ((alpha
<< 24) & 0xff000000) | (color
& 0xffffff);
934 Color
CGraphicContext::MergeColor(Color color
) const
936 return m_finalTransform
.matrix
.TransformColor(color
);
939 int CGraphicContext::GetWidth() const
941 return m_iScreenWidth
;
944 int CGraphicContext::GetHeight() const
946 return m_iScreenHeight
;
949 float CGraphicContext::GetFPS() const
951 if (m_Resolution
!= RES_INVALID
)
953 RESOLUTION_INFO info
= GetResInfo();
954 if (info
.fRefreshRate
> 0)
955 return info
.fRefreshRate
;
960 float CGraphicContext::GetDisplayLatency() const
962 float latency
= CServiceBroker::GetWinSystem()->GetDisplayLatency();
966 latency
= (CServiceBroker::GetWinSystem()->NoOfBuffers() + 1) / GetFPS() * 1000.0f
;
972 bool CGraphicContext::IsFullScreenRoot () const
974 return m_bFullScreenRoot
;
977 void CGraphicContext::ToggleFullScreen()
981 if (m_bFullScreenRoot
)
987 if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP
)
988 uiRes
= CDisplaySettings::GetInstance().GetCurrentResolution();
993 CDisplaySettings::GetInstance().SetCurrentResolution(uiRes
, true);
996 void CGraphicContext::SetMediaDir(const std::string
&strMediaDir
)
998 CServiceBroker::GetGUI()->GetTextureManager().SetTexturePath(strMediaDir
);
999 m_strMediaDir
= strMediaDir
;
1002 const std::string
& CGraphicContext::GetMediaDir() const
1004 return m_strMediaDir
;
1008 void CGraphicContext::Flip(bool rendered
, bool videoLayer
)
1010 CServiceBroker::GetRenderSystem()->PresentRender(rendered
, videoLayer
);
1012 if(m_stereoMode
!= m_nextStereoMode
)
1014 m_stereoMode
= m_nextStereoMode
;
1015 SetVideoResolution(GetVideoResolution(), true);
1016 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_RENDERER_RESET
);
1020 void CGraphicContext::GetAllowedResolutions(std::vector
<RESOLUTION
> &res
)
1024 res
.push_back(RES_WINDOW
);
1025 res
.push_back(RES_DESKTOP
);
1026 for (size_t r
= (size_t) RES_CUSTOM
; r
< CDisplaySettings::GetInstance().ResolutionInfoSize(); r
++)
1028 res
.push_back((RESOLUTION
) r
);
1032 void CGraphicContext::SetRenderOrder(RENDER_ORDER renderOrder
)
1034 m_renderOrder
= renderOrder
;
1035 if (renderOrder
== RENDER_ORDER_ALL_BACK_TO_FRONT
)
1036 CServiceBroker::GetRenderSystem()->SetDepthCulling(DEPTH_CULLING_OFF
);
1037 else if (renderOrder
== RENDER_ORDER_BACK_TO_FRONT
)
1038 CServiceBroker::GetRenderSystem()->SetDepthCulling(DEPTH_CULLING_BACK_TO_FRONT
);
1039 else if (renderOrder
== RENDER_ORDER_FRONT_TO_BACK
)
1040 CServiceBroker::GetRenderSystem()->SetDepthCulling(DEPTH_CULLING_FRONT_TO_BACK
);
1043 uint32_t CGraphicContext::GetDepth(uint32_t addLayers
)
1045 uint32_t layer
= m_layer
;
1046 m_layer
+= addLayers
;
1050 void CGraphicContext::SetFPS(float fps
)
1052 m_fFPSOverride
= fps
;