1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "GraphicContext.h"
10 
11 #include "Application.h"
12 #include "ServiceBroker.h"
13 #include "WinSystem.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/GUIWindowManager.h"
16 #include "guilib/TextureManager.h"
17 #include "guilib/gui3d.h"
18 #include "input/InputManager.h"
19 #include "messaging/ApplicationMessenger.h"
20 #include "rendering/RenderSystem.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/DisplaySettings.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "settings/lib/Setting.h"
26 #include "utils/log.h"
27 
28 #include <cassert>
29 
30 using namespace KODI::MESSAGING;
31 
32 CGraphicContext::CGraphicContext(void) = default;
33 CGraphicContext::~CGraphicContext(void) = default;
34 
SetOrigin(float x,float y)35 void CGraphicContext::SetOrigin(float x, float y)
36 {
37   if (!m_origins.empty())
38     m_origins.push(CPoint(x,y) + m_origins.top());
39   else
40     m_origins.push(CPoint(x,y));
41 
42   AddTransform(TransformMatrix::CreateTranslation(x, y));
43 }
44 
RestoreOrigin()45 void CGraphicContext::RestoreOrigin()
46 {
47   if (!m_origins.empty())
48     m_origins.pop();
49   RemoveTransform();
50 }
51 
52 // add a new clip region, intersecting with the previous clip region.
SetClipRegion(float x,float y,float w,float h)53 bool CGraphicContext::SetClipRegion(float x, float y, float w, float h)
54 { // transform from our origin
55   CPoint origin;
56   if (!m_origins.empty())
57     origin = m_origins.top();
58 
59   // ok, now intersect with our old clip region
60   CRect rect(x, y, x + w, y + h);
61   rect += origin;
62   if (!m_clipRegions.empty())
63   {
64     // intersect with original clip region
65     rect.Intersect(m_clipRegions.top());
66   }
67 
68   if (rect.IsEmpty())
69     return false;
70 
71   m_clipRegions.push(rect);
72 
73   // here we could set the hardware clipping, if applicable
74   return true;
75 }
76 
RestoreClipRegion()77 void CGraphicContext::RestoreClipRegion()
78 {
79   if (!m_clipRegions.empty())
80     m_clipRegions.pop();
81 
82   // here we could reset the hardware clipping, if applicable
83 }
84 
ClipRect(CRect & vertex,CRect & texture,CRect * texture2)85 void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
86 {
87   // this is the software clipping routine.  If the graphics hardware is set to do the clipping
88   // (eg via SetClipPlane in D3D for instance) then this routine is unneeded.
89   if (!m_clipRegions.empty())
90   {
91     // take a copy of the vertex rectangle and intersect
92     // it with our clip region (moved to the same coordinate system)
93     CRect clipRegion(m_clipRegions.top());
94     if (!m_origins.empty())
95       clipRegion -= m_origins.top();
96     CRect original(vertex);
97     vertex.Intersect(clipRegion);
98     // and use the original to compute the texture coordinates
99     if (original != vertex)
100     {
101       float scaleX = texture.Width() / original.Width();
102       float scaleY = texture.Height() / original.Height();
103       texture.x1 += (vertex.x1 - original.x1) * scaleX;
104       texture.y1 += (vertex.y1 - original.y1) * scaleY;
105       texture.x2 += (vertex.x2 - original.x2) * scaleX;
106       texture.y2 += (vertex.y2 - original.y2) * scaleY;
107       if (texture2)
108       {
109         scaleX = texture2->Width() / original.Width();
110         scaleY = texture2->Height() / original.Height();
111         texture2->x1 += (vertex.x1 - original.x1) * scaleX;
112         texture2->y1 += (vertex.y1 - original.y1) * scaleY;
113         texture2->x2 += (vertex.x2 - original.x2) * scaleX;
114         texture2->y2 += (vertex.y2 - original.y2) * scaleY;
115       }
116     }
117   }
118 }
119 
GetClipRegion()120 CRect CGraphicContext::GetClipRegion()
121 {
122   if (m_clipRegions.empty())
123     return CRect(0, 0, m_iScreenWidth, m_iScreenHeight);
124   CRect clipRegion(m_clipRegions.top());
125   if (!m_origins.empty())
126     clipRegion -= m_origins.top();
127   return clipRegion;
128 }
129 
AddGUITransform()130 void CGraphicContext::AddGUITransform()
131 {
132   m_transforms.push(m_finalTransform);
133   m_finalTransform = m_guiTransform;
134 }
135 
AddTransform(const TransformMatrix & matrix)136 TransformMatrix CGraphicContext::AddTransform(const TransformMatrix &matrix)
137 {
138   m_transforms.push(m_finalTransform);
139   m_finalTransform.matrix *= matrix;
140   return m_finalTransform.matrix;
141 }
142 
SetTransform(const TransformMatrix & matrix)143 void CGraphicContext::SetTransform(const TransformMatrix &matrix)
144 {
145   m_transforms.push(m_finalTransform);
146   m_finalTransform.matrix = matrix;
147 }
148 
SetTransform(const TransformMatrix & matrix,float scaleX,float scaleY)149 void CGraphicContext::SetTransform(const TransformMatrix &matrix, float scaleX, float scaleY)
150 {
151   m_transforms.push(m_finalTransform);
152   m_finalTransform.matrix = matrix;
153   m_finalTransform.scaleX = scaleX;
154   m_finalTransform.scaleY = scaleY;
155 }
156 
RemoveTransform()157 void CGraphicContext::RemoveTransform()
158 {
159   if (!m_transforms.empty())
160   {
161     m_finalTransform = m_transforms.top();
162     m_transforms.pop();
163   }
164 }
165 
SetViewPort(float fx,float fy,float fwidth,float fheight,bool intersectPrevious)166 bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */)
167 {
168   // transform coordinates - we may have a rotation which changes the positioning of the
169   // minimal and maximal viewport extents.  We currently go to the maximal extent.
170   float x[4], y[4];
171   x[0] = x[3] = fx;
172   x[1] = x[2] = fx + fwidth;
173   y[0] = y[1] = fy;
174   y[2] = y[3] = fy + fheight;
175   float minX = (float)m_iScreenWidth;
176   float maxX = 0;
177   float minY = (float)m_iScreenHeight;
178   float maxY = 0;
179   for (int i = 0; i < 4; i++)
180   {
181     float z = 0;
182     ScaleFinalCoords(x[i], y[i], z);
183     if (x[i] < minX) minX = x[i];
184     if (x[i] > maxX) maxX = x[i];
185     if (y[i] < minY) minY = y[i];
186     if (y[i] > maxY) maxY = y[i];
187   }
188 
189   int newLeft = (int)(minX + 0.5f);
190   int newTop = (int)(minY + 0.5f);
191   int newRight = (int)(maxX + 0.5f);
192   int newBottom = (int)(maxY + 0.5f);
193   if (intersectPrevious)
194   {
195     CRect oldviewport = m_viewStack.top();
196     // do the intersection
197     int oldLeft = (int)oldviewport.x1;
198     int oldTop = (int)oldviewport.y1;
199     int oldRight = (int)oldviewport.x2;
200     int oldBottom = (int)oldviewport.y2;
201     if (newLeft >= oldRight || newTop >= oldBottom || newRight <= oldLeft || newBottom <= oldTop)
202     { // empty intersection - return false to indicate no rendering should occur
203       return false;
204     }
205     // ok, they intersect, do the intersection
206     if (newLeft < oldLeft) newLeft = oldLeft;
207     if (newTop < oldTop) newTop = oldTop;
208     if (newRight > oldRight) newRight = oldRight;
209     if (newBottom > oldBottom) newBottom = oldBottom;
210   }
211   // check range against screen size
212   if (newRight <= 0 || newBottom <= 0 ||
213       newTop >= m_iScreenHeight || newLeft >= m_iScreenWidth ||
214       newLeft >= newRight || newTop >= newBottom)
215   { // no intersection with the screen
216     return false;
217   }
218   // intersection with the screen
219   if (newLeft < 0) newLeft = 0;
220   if (newTop < 0) newTop = 0;
221   if (newRight > m_iScreenWidth) newRight = m_iScreenWidth;
222   if (newBottom > m_iScreenHeight) newBottom = m_iScreenHeight;
223 
224   assert(newLeft < newRight);
225   assert(newTop < newBottom);
226 
227   CRect newviewport((float)newLeft, (float)newTop, (float)newRight, (float)newBottom);
228 
229   m_viewStack.push(newviewport);
230 
231   newviewport = StereoCorrection(newviewport);
232   CServiceBroker::GetRenderSystem()->SetViewPort(newviewport);
233 
234 
235   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
236   return true;
237 }
238 
RestoreViewPort()239 void CGraphicContext::RestoreViewPort()
240 {
241   if (m_viewStack.size() <= 1) return;
242 
243   m_viewStack.pop();
244   CRect viewport = StereoCorrection(m_viewStack.top());
245   CServiceBroker::GetRenderSystem()->SetViewPort(viewport);
246 
247   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
248 }
249 
StereoCorrection(const CPoint & point) const250 CPoint CGraphicContext::StereoCorrection(const CPoint &point) const
251 {
252   CPoint res(point);
253 
254   if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
255   {
256     const RESOLUTION_INFO info = GetResInfo();
257 
258     if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
259       res.y += info.iHeight + info.iBlanking;
260   }
261   if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
262   {
263     const RESOLUTION_INFO info = GetResInfo();
264 
265     if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
266       res.x += info.iWidth  + info.iBlanking;
267   }
268   return res;
269 }
270 
StereoCorrection(const CRect & rect) const271 CRect CGraphicContext::StereoCorrection(const CRect &rect) const
272 {
273   CRect res(StereoCorrection(rect.P1())
274           , StereoCorrection(rect.P2()));
275   return res;
276 }
277 
SetScissors(const CRect & rect)278 void CGraphicContext::SetScissors(const CRect &rect)
279 {
280   m_scissors = rect;
281   m_scissors.Intersect(CRect(0,0,(float)m_iScreenWidth, (float)m_iScreenHeight));
282   CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors));
283 }
284 
GetScissors() const285 const CRect &CGraphicContext::GetScissors() const
286 {
287   return m_scissors;
288 }
289 
ResetScissors()290 void CGraphicContext::ResetScissors()
291 {
292   m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
293   CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors));
294 }
295 
GetViewWindow() const296 const CRect CGraphicContext::GetViewWindow() const
297 {
298   if (m_bCalibrating || m_bFullScreenVideo)
299   {
300     CRect rect;
301     RESOLUTION_INFO info = GetResInfo();
302     rect.x1 = (float)info.Overscan.left;
303     rect.y1 = (float)info.Overscan.top;
304     rect.x2 = (float)info.Overscan.right;
305     rect.y2 = (float)info.Overscan.bottom;
306     return rect;
307   }
308   return m_videoRect;
309 }
310 
SetViewWindow(float left,float top,float right,float bottom)311 void CGraphicContext::SetViewWindow(float left, float top, float right, float bottom)
312 {
313   m_videoRect.x1 = ScaleFinalXCoord(left, top);
314   m_videoRect.y1 = ScaleFinalYCoord(left, top);
315   m_videoRect.x2 = ScaleFinalXCoord(right, bottom);
316   m_videoRect.y2 = ScaleFinalYCoord(right, bottom);
317 }
318 
SetFullScreenVideo(bool bOnOff)319 void CGraphicContext::SetFullScreenVideo(bool bOnOff)
320 {
321   CSingleLock lock(*this);
322 
323   m_bFullScreenVideo = bOnOff;
324 
325   if (m_bFullScreenRoot)
326   {
327     bool bTriggerUpdateRes = false;
328     if (m_bFullScreenVideo)
329       bTriggerUpdateRes = true;
330     else
331     {
332       bool allowDesktopRes = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) == ADJUST_REFRESHRATE_ALWAYS;
333       if (!allowDesktopRes)
334       {
335         if (g_application.GetAppPlayer().IsPlayingVideo())
336           bTriggerUpdateRes = true;
337       }
338     }
339 
340     bool allowResolutionChangeOnStop = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_ON_START;
341     RESOLUTION targetResolutionOnStop = RES_DESKTOP;
342     if (bTriggerUpdateRes)
343       g_application.GetAppPlayer().TriggerUpdateResolution();
344     else if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP)
345     {
346       targetResolutionOnStop = CDisplaySettings::GetInstance().GetCurrentResolution();
347     }
348 
349     if (allowResolutionChangeOnStop && !bTriggerUpdateRes)
350     {
351       SetVideoResolution(targetResolutionOnStop, false);
352     }
353   }
354   else
355     SetVideoResolution(RES_WINDOW, false);
356 }
357 
IsFullScreenVideo() const358 bool CGraphicContext::IsFullScreenVideo() const
359 {
360   return m_bFullScreenVideo;
361 }
362 
IsCalibrating() const363 bool CGraphicContext::IsCalibrating() const
364 {
365   return m_bCalibrating;
366 }
367 
SetCalibrating(bool bOnOff)368 void CGraphicContext::SetCalibrating(bool bOnOff)
369 {
370   m_bCalibrating = bOnOff;
371 }
372 
IsValidResolution(RESOLUTION res)373 bool CGraphicContext::IsValidResolution(RESOLUTION res)
374 {
375   if (res >= RES_WINDOW && (size_t) res < CDisplaySettings::GetInstance().ResolutionInfoSize())
376   {
377     return true;
378   }
379 
380   return false;
381 }
382 
383 // call SetVideoResolutionInternal and ensure its done from mainthread
SetVideoResolution(RESOLUTION res,bool forceUpdate)384 void CGraphicContext::SetVideoResolution(RESOLUTION res, bool forceUpdate)
385 {
386   if (g_application.IsCurrentThread())
387   {
388     SetVideoResolutionInternal(res, forceUpdate);
389   }
390   else
391   {
392     CApplicationMessenger::GetInstance().SendMsg(TMSG_SETVIDEORESOLUTION, res, forceUpdate ? 1 : 0);
393   }
394 }
395 
SetVideoResolutionInternal(RESOLUTION res,bool forceUpdate)396 void CGraphicContext::SetVideoResolutionInternal(RESOLUTION res, bool forceUpdate)
397 {
398   RESOLUTION lastRes = m_Resolution;
399 
400   // If the user asked us to guess, go with desktop
401   if (!IsValidResolution(res))
402   {
403     res = RES_DESKTOP;
404   }
405 
406   // If we are switching to the same resolution and same window/full-screen, no need to do anything
407   if (!forceUpdate && res == lastRes && m_bFullScreenRoot == CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
408   {
409     return;
410   }
411 
412   if (res >= RES_DESKTOP)
413   {
414     CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = true;
415     m_bFullScreenRoot = true;
416   }
417   else
418   {
419     CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = false;
420     m_bFullScreenRoot = false;
421   }
422 
423   CSingleLock lock(*this);
424 
425   // FIXME Wayland windowing needs some way to "deny" resolution updates since what Kodi
426   // requests might not get actually set by the compositor.
427   // So in theory, m_iScreenWidth etc. would not need to be updated at all before the
428   // change is confirmed.
429   // But other windowing code expects these variables to be already set when
430   // SetFullScreen() is called, so set them anyway and remember the old values.
431   int origScreenWidth = m_iScreenWidth;
432   int origScreenHeight = m_iScreenHeight;
433   float origFPSOverride = m_fFPSOverride;
434 
435   UpdateInternalStateWithResolution(res);
436   RESOLUTION_INFO info_org  = CDisplaySettings::GetInstance().GetResolutionInfo(res);
437 
438   bool switched = false;
439   if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
440   {
441 #if defined (TARGET_DARWIN) || defined (TARGET_WINDOWS)
442     bool blankOtherDisplays = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS);
443     switched = CServiceBroker::GetWinSystem()->SetFullScreen(true,  info_org, blankOtherDisplays);
444 #else
445     switched = CServiceBroker::GetWinSystem()->SetFullScreen(true,  info_org, false);
446 #endif
447   }
448   else if (lastRes >= RES_DESKTOP )
449     switched = CServiceBroker::GetWinSystem()->SetFullScreen(false, info_org, false);
450   else
451     switched = CServiceBroker::GetWinSystem()->ResizeWindow(info_org.iWidth, info_org.iHeight, -1, -1);
452 
453   if (switched)
454   {
455     m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
456 
457     // make sure all stereo stuff are correctly setup
458     SetStereoView(RENDER_STEREO_VIEW_OFF);
459 
460     // update anyone that relies on sizing information
461     CServiceBroker::GetInputManager().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1);
462 
463     CGUIComponent *gui = CServiceBroker::GetGUI();
464     if (gui)
465       gui->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
466   }
467   else
468   {
469     // Reset old state
470     m_iScreenWidth = origScreenWidth;
471     m_iScreenHeight = origScreenHeight;
472     m_fFPSOverride = origFPSOverride;
473     if (IsValidResolution(lastRes))
474     {
475       m_Resolution = lastRes;
476     }
477     else
478     {
479       // FIXME Resolution has become invalid
480       // This happens e.g. when switching monitors and the new monitor has fewer
481       // resolutions than the old one. Fall back to RES_DESKTOP and hope that
482       // the real resolution is set soon.
483       // Again, must be fixed as part of a greater refactor.
484       m_Resolution = RES_DESKTOP;
485     }
486   }
487 }
488 
ApplyVideoResolution(RESOLUTION res)489 void CGraphicContext::ApplyVideoResolution(RESOLUTION res)
490 {
491   if (!IsValidResolution(res))
492   {
493     CLog::LogF(LOGWARNING, "Asked to apply invalid resolution %d, falling back to RES_DESKTOP", res);
494     res = RES_DESKTOP;
495   }
496 
497   if (res >= RES_DESKTOP)
498   {
499     CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = true;
500     m_bFullScreenRoot = true;
501   }
502   else
503   {
504     CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = false;
505     m_bFullScreenRoot = false;
506   }
507 
508   CSingleLock lock(*this);
509 
510   UpdateInternalStateWithResolution(res);
511 
512   m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
513 
514   // make sure all stereo stuff are correctly setup
515   SetStereoView(RENDER_STEREO_VIEW_OFF);
516 
517   // update anyone that relies on sizing information
518   RESOLUTION_INFO info_org  = CDisplaySettings::GetInstance().GetResolutionInfo(res);
519   CServiceBroker::GetInputManager().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1);
520   CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
521 }
522 
UpdateInternalStateWithResolution(RESOLUTION res)523 void CGraphicContext::UpdateInternalStateWithResolution(RESOLUTION res)
524 {
525   RESOLUTION_INFO info_mod = GetResInfo(res);
526 
527   m_iScreenWidth = info_mod.iWidth;
528   m_iScreenHeight = info_mod.iHeight;
529   m_Resolution = res;
530   m_fFPSOverride = 0;
531 }
532 
ApplyModeChange(RESOLUTION res)533 void CGraphicContext::ApplyModeChange(RESOLUTION res)
534 {
535   ApplyVideoResolution(res);
536   CServiceBroker::GetWinSystem()->FinishModeChange(res);
537 }
538 
ApplyWindowResize(int newWidth,int newHeight)539 void CGraphicContext::ApplyWindowResize(int newWidth, int newHeight)
540 {
541   CServiceBroker::GetWinSystem()->SetWindowResolution(newWidth, newHeight);
542   ApplyVideoResolution(RES_WINDOW);
543   CServiceBroker::GetWinSystem()->FinishWindowResize(newWidth, newHeight);
544 }
545 
GetVideoResolution() const546 RESOLUTION CGraphicContext::GetVideoResolution() const
547 {
548   return m_Resolution;
549 }
550 
ResetOverscan(RESOLUTION_INFO & res)551 void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res)
552 {
553   res.Overscan.left = 0;
554   res.Overscan.top = 0;
555   res.Overscan.right = res.iWidth;
556   res.Overscan.bottom = res.iHeight;
557 }
558 
ResetOverscan(RESOLUTION res,OVERSCAN & overscan)559 void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan)
560 {
561   overscan.left = 0;
562   overscan.top = 0;
563 
564   RESOLUTION_INFO info = GetResInfo(res);
565   overscan.right  = info.iWidth;
566   overscan.bottom = info.iHeight;
567 }
568 
ResetScreenParameters(RESOLUTION res)569 void CGraphicContext::ResetScreenParameters(RESOLUTION res)
570 {
571   RESOLUTION_INFO& info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
572 
573   switch (res)
574   {
575   case RES_WINDOW:
576     info.iSubtitles = (int)(0.965 * info.iHeight);
577     info.fPixelRatio = 1.0;
578     break;
579   default:
580     break;
581   }
582 
583   info.iScreenWidth  = info.iWidth;
584   info.iScreenHeight = info.iHeight;
585   ResetOverscan(res, info.Overscan);
586 }
587 
Clear(UTILS::Color color)588 void CGraphicContext::Clear(UTILS::Color color)
589 {
590   CServiceBroker::GetRenderSystem()->ClearBuffers(color);
591 }
592 
CaptureStateBlock()593 void CGraphicContext::CaptureStateBlock()
594 {
595   CServiceBroker::GetRenderSystem()->CaptureStateBlock();
596 }
597 
ApplyStateBlock()598 void CGraphicContext::ApplyStateBlock()
599 {
600   CServiceBroker::GetRenderSystem()->ApplyStateBlock();
601 }
602 
GetResInfo(RESOLUTION res) const603 const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res) const
604 {
605   RESOLUTION_INFO info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
606 
607   if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
608   {
609     if((info.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
610     {
611       info.fPixelRatio     /= 2;
612       info.iBlanking        = 0;
613       info.dwFlags         |= D3DPRESENTFLAG_MODE3DTB;
614     }
615     info.iHeight          = (info.iHeight         - info.iBlanking) / 2;
616     info.Overscan.top    /= 2;
617     info.Overscan.bottom  = (info.Overscan.bottom - info.iBlanking) / 2;
618     info.iSubtitles       = (info.iSubtitles      - info.iBlanking) / 2;
619   }
620 
621   if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
622   {
623     if((info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
624     {
625       info.fPixelRatio     *= 2;
626       info.iBlanking        = 0;
627       info.dwFlags         |= D3DPRESENTFLAG_MODE3DSBS;
628     }
629     info.iWidth           = (info.iWidth         - info.iBlanking) / 2;
630     info.Overscan.left   /= 2;
631     info.Overscan.right   = (info.Overscan.right - info.iBlanking) / 2;
632   }
633 
634   if (res == m_Resolution && m_fFPSOverride != 0)
635   {
636     info.fRefreshRate = m_fFPSOverride;
637   }
638 
639   return info;
640 }
641 
SetResInfo(RESOLUTION res,const RESOLUTION_INFO & info)642 void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info)
643 {
644   RESOLUTION_INFO& curr = CDisplaySettings::GetInstance().GetResolutionInfo(res);
645   curr.Overscan   = info.Overscan;
646   curr.iSubtitles = info.iSubtitles;
647   curr.fPixelRatio = info.fPixelRatio;
648 
649   if(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
650   {
651     curr.Overscan.right  = info.Overscan.right  * 2 + info.iBlanking;
652     if((curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
653       curr.fPixelRatio  /= 2.0;
654   }
655 
656   if(info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
657   {
658     curr.Overscan.bottom = info.Overscan.bottom * 2 + info.iBlanking;
659     curr.iSubtitles      = info.iSubtitles      * 2 + info.iBlanking;
660     if((curr.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
661       curr.fPixelRatio  *= 2.0;
662   }
663 }
664 
GetResInfo() const665 const RESOLUTION_INFO CGraphicContext::GetResInfo() const
666 {
667   return GetResInfo(m_Resolution);
668 }
669 
GetGUIScaling(const RESOLUTION_INFO & res,float & scaleX,float & scaleY,TransformMatrix * matrix)670 void CGraphicContext::GetGUIScaling(const RESOLUTION_INFO &res, float &scaleX, float &scaleY, TransformMatrix *matrix /* = NULL */)
671 {
672   if (m_Resolution != RES_INVALID)
673   {
674     // calculate necessary scalings
675     RESOLUTION_INFO info = GetResInfo();
676     float fFromWidth  = (float)res.iWidth;
677     float fFromHeight = (float)res.iHeight;
678     auto fToPosX = info.Overscan.left + info.guiInsets.left;
679     auto fToPosY = info.Overscan.top + info.guiInsets.top;
680     auto fToWidth = info.Overscan.right - info.guiInsets.right - fToPosX;
681     auto fToHeight = info.Overscan.bottom - info.guiInsets.bottom - fToPosY;
682 
683     float fZoom = (100 + CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_SKINZOOM)) * 0.01f;
684 
685     fZoom -= 1.0f;
686     fToPosX -= fToWidth * fZoom * 0.5f;
687     fToWidth *= fZoom + 1.0f;
688 
689     // adjust for aspect ratio as zoom is given in the vertical direction and we don't
690     // do aspect ratio corrections in the gui code
691     fZoom = fZoom / info.fPixelRatio;
692     fToPosY -= fToHeight * fZoom * 0.5f;
693     fToHeight *= fZoom + 1.0f;
694 
695     scaleX = fFromWidth / fToWidth;
696     scaleY = fFromHeight / fToHeight;
697     if (matrix)
698     {
699       TransformMatrix guiScaler = TransformMatrix::CreateScaler(fToWidth / fFromWidth, fToHeight / fFromHeight, fToHeight / fFromHeight);
700       TransformMatrix guiOffset = TransformMatrix::CreateTranslation(fToPosX, fToPosY);
701       *matrix = guiOffset * guiScaler;
702     }
703   }
704   else
705   {
706     scaleX = scaleY = 1.0f;
707     if (matrix)
708       matrix->Reset();
709   }
710 }
711 
SetScalingResolution(const RESOLUTION_INFO & res,bool needsScaling)712 void CGraphicContext::SetScalingResolution(const RESOLUTION_INFO &res, bool needsScaling)
713 {
714   m_windowResolution = res;
715   if (needsScaling && m_Resolution != RES_INVALID)
716     GetGUIScaling(res, m_guiTransform.scaleX, m_guiTransform.scaleY, &m_guiTransform.matrix);
717   else
718   {
719     m_guiTransform.Reset();
720   }
721 
722   // reset our origin and camera
723   while (!m_origins.empty())
724     m_origins.pop();
725   m_origins.push(CPoint(0, 0));
726   while (!m_cameras.empty())
727     m_cameras.pop();
728   m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight));
729   while (!m_stereoFactors.empty())
730     m_stereoFactors.pop();
731   m_stereoFactors.push(0.0f);
732 
733   // and reset the final transform
734   m_finalTransform = m_guiTransform;
735 }
736 
SetRenderingResolution(const RESOLUTION_INFO & res,bool needsScaling)737 void CGraphicContext::SetRenderingResolution(const RESOLUTION_INFO &res, bool needsScaling)
738 {
739   CSingleLock lock(*this);
740 
741   SetScalingResolution(res, needsScaling);
742   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
743 }
744 
SetStereoView(RENDER_STEREO_VIEW view)745 void CGraphicContext::SetStereoView(RENDER_STEREO_VIEW view)
746 {
747   m_stereoView = view;
748 
749   while(!m_viewStack.empty())
750     m_viewStack.pop();
751 
752   CRect viewport(0.0f, 0.0f, (float)m_iScreenWidth, (float)m_iScreenHeight);
753 
754   m_viewStack.push(viewport);
755 
756   viewport = StereoCorrection(viewport);
757   CServiceBroker::GetRenderSystem()->SetStereoMode(m_stereoMode, m_stereoView);
758   CServiceBroker::GetRenderSystem()->SetViewPort(viewport);
759   CServiceBroker::GetRenderSystem()->SetScissors(viewport);
760 }
761 
InvertFinalCoords(float & x,float & y) const762 void CGraphicContext::InvertFinalCoords(float &x, float &y) const
763 {
764   m_finalTransform.matrix.InverseTransformPosition(x, y);
765 }
766 
ScaleFinalXCoord(float x,float y) const767 float CGraphicContext::ScaleFinalXCoord(float x, float y) const
768 {
769   return m_finalTransform.matrix.TransformXCoord(x, y, 0);
770 }
771 
ScaleFinalYCoord(float x,float y) const772 float CGraphicContext::ScaleFinalYCoord(float x, float y) const
773 {
774   return m_finalTransform.matrix.TransformYCoord(x, y, 0);
775 }
776 
ScaleFinalZCoord(float x,float y) const777 float CGraphicContext::ScaleFinalZCoord(float x, float y) const
778 {
779   return m_finalTransform.matrix.TransformZCoord(x, y, 0);
780 }
781 
ScaleFinalCoords(float & x,float & y,float & z) const782 void CGraphicContext::ScaleFinalCoords(float &x, float &y, float &z) const
783 {
784   m_finalTransform.matrix.TransformPosition(x, y, z);
785 }
786 
GetScalingPixelRatio() const787 float CGraphicContext::GetScalingPixelRatio() const
788 {
789   // assume the resolutions are different - we want to return the aspect ratio of the video resolution
790   // but only once it's been corrected for the skin -> screen coordinates scaling
791   return GetResInfo().fPixelRatio * (m_finalTransform.scaleY / m_finalTransform.scaleX);
792 }
793 
SetCameraPosition(const CPoint & camera)794 void CGraphicContext::SetCameraPosition(const CPoint &camera)
795 {
796   // offset the camera from our current location (this is in XML coordinates) and scale it up to
797   // the screen resolution
798   CPoint cam(camera);
799   if (!m_origins.empty())
800     cam += m_origins.top();
801 
802   cam.x *= (float)m_iScreenWidth / m_windowResolution.iWidth;
803   cam.y *= (float)m_iScreenHeight / m_windowResolution.iHeight;
804 
805   m_cameras.push(cam);
806   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
807 }
808 
RestoreCameraPosition()809 void CGraphicContext::RestoreCameraPosition()
810 { // remove the top camera from the stack
811   assert(m_cameras.size());
812   m_cameras.pop();
813   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
814 }
815 
SetStereoFactor(float factor)816 void CGraphicContext::SetStereoFactor(float factor)
817 {
818   m_stereoFactors.push(factor);
819   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
820 }
821 
RestoreStereoFactor()822 void CGraphicContext::RestoreStereoFactor()
823 { // remove the top factor from the stack
824   assert(m_stereoFactors.size());
825   m_stereoFactors.pop();
826   UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
827 }
828 
GenerateAABB(const CRect & rect) const829 CRect CGraphicContext::GenerateAABB(const CRect &rect) const
830 {
831 // ------------------------
832 // |(x1, y1)      (x2, y2)|
833 // |                      |
834 // |(x3, y3)      (x4, y4)|
835 // ------------------------
836 
837   float x1 = rect.x1, x2 = rect.x2, x3 = rect.x1, x4 = rect.x2;
838   float y1 = rect.y1, y2 = rect.y1, y3 = rect.y2, y4 = rect.y2;
839 
840   float z = 0.0f;
841   ScaleFinalCoords(x1, y1, z);
842   CServiceBroker::GetRenderSystem()->Project(x1, y1, z);
843 
844   z = 0.0f;
845   ScaleFinalCoords(x2, y2, z);
846   CServiceBroker::GetRenderSystem()->Project(x2, y2, z);
847 
848   z = 0.0f;
849   ScaleFinalCoords(x3, y3, z);
850   CServiceBroker::GetRenderSystem()->Project(x3, y3, z);
851 
852   z = 0.0f;
853   ScaleFinalCoords(x4, y4, z);
854   CServiceBroker::GetRenderSystem()->Project(x4, y4, z);
855 
856   return CRect( std::min(std::min(std::min(x1, x2), x3), x4),
857                 std::min(std::min(std::min(y1, y2), y3), y4),
858                 std::max(std::max(std::max(x1, x2), x3), x4),
859                 std::max(std::max(std::max(y1, y2), y3), y4));
860 }
861 
862 // NOTE: This routine is currently called (twice) every time there is a <camera>
863 //       tag in the skin.  It actually only has to be called before we render
864 //       something, so another option is to just save the camera coordinates
865 //       and then have a routine called before every draw that checks whether
866 //       the camera has changed, and if so, changes it.  Similarly, it could set
867 //       the world transform at that point as well (or even combine world + view
868 //       to cut down on one setting)
UpdateCameraPosition(const CPoint & camera,const float & factor)869 void CGraphicContext::UpdateCameraPosition(const CPoint &camera, const float &factor)
870 {
871   float stereoFactor = 0.f;
872   if ( m_stereoMode != RENDER_STEREO_MODE_OFF
873     && m_stereoMode != RENDER_STEREO_MODE_MONO
874     && m_stereoView != RENDER_STEREO_VIEW_OFF)
875   {
876     RESOLUTION_INFO res = GetResInfo();
877     RESOLUTION_INFO desktop = GetResInfo(RES_DESKTOP);
878     float scaleRes = (static_cast<float>(res.iWidth) / static_cast<float>(desktop.iWidth));
879     float scaleX = static_cast<float>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_STEREOSTRENGTH)) * scaleRes;
880     stereoFactor = factor * (m_stereoView == RENDER_STEREO_VIEW_LEFT ? scaleX : -scaleX);
881   }
882   CServiceBroker::GetRenderSystem()->SetCameraPosition(camera, m_iScreenWidth, m_iScreenHeight, stereoFactor);
883 }
884 
RectIsAngled(float x1,float y1,float x2,float y2) const885 bool CGraphicContext::RectIsAngled(float x1, float y1, float x2, float y2) const
886 { // need only test 3 points, as they must be co-planer
887   if (m_finalTransform.matrix.TransformZCoord(x1, y1, 0)) return true;
888   if (m_finalTransform.matrix.TransformZCoord(x2, y2, 0)) return true;
889   if (m_finalTransform.matrix.TransformZCoord(x1, y2, 0)) return true;
890   return false;
891 }
892 
GetGUIMatrix() const893 const TransformMatrix &CGraphicContext::GetGUIMatrix() const
894 {
895   return m_finalTransform.matrix;
896 }
897 
GetGUIScaleX() const898 float CGraphicContext::GetGUIScaleX() const
899 {
900   return m_finalTransform.scaleX;
901 }
902 
GetGUIScaleY() const903 float CGraphicContext::GetGUIScaleY() const
904 {
905   return m_finalTransform.scaleY;
906 }
907 
MergeAlpha(UTILS::Color color) const908 UTILS::Color CGraphicContext::MergeAlpha(UTILS::Color color) const
909 {
910   UTILS::Color alpha = m_finalTransform.matrix.TransformAlpha((color >> 24) & 0xff);
911   if (alpha > 255) alpha = 255;
912   return ((alpha << 24) & 0xff000000) | (color & 0xffffff);
913 }
914 
GetWidth() const915 int CGraphicContext::GetWidth() const
916 {
917   return m_iScreenWidth;
918 }
919 
GetHeight() const920 int CGraphicContext::GetHeight() const
921 {
922   return m_iScreenHeight;
923 }
924 
GetFPS() const925 float CGraphicContext::GetFPS() const
926 {
927   if (m_Resolution != RES_INVALID)
928   {
929     RESOLUTION_INFO info = GetResInfo();
930     if (info.fRefreshRate > 0)
931       return info.fRefreshRate;
932   }
933   return 60.0f;
934 }
935 
GetDisplayLatency() const936 float CGraphicContext::GetDisplayLatency() const
937 {
938   float latency = CServiceBroker::GetWinSystem()->GetDisplayLatency();
939   if (latency < 0.0f)
940   {
941     // fallback
942     latency = (CServiceBroker::GetWinSystem()->NoOfBuffers() + 1) / GetFPS() * 1000.0f;
943   }
944 
945   return latency;
946 }
947 
IsFullScreenRoot() const948 bool CGraphicContext::IsFullScreenRoot () const
949 {
950   return m_bFullScreenRoot;
951 }
952 
ToggleFullScreen()953 void CGraphicContext::ToggleFullScreen()
954 {
955   RESOLUTION uiRes;
956 
957   if (m_bFullScreenRoot)
958   {
959     uiRes = RES_WINDOW;
960   }
961   else
962   {
963     if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP)
964       uiRes = CDisplaySettings::GetInstance().GetCurrentResolution();
965     else
966       uiRes = RES_DESKTOP;
967   }
968 
969   CDisplaySettings::GetInstance().SetCurrentResolution(uiRes, true);
970 }
971 
SetMediaDir(const std::string & strMediaDir)972 void CGraphicContext::SetMediaDir(const std::string &strMediaDir)
973 {
974   CServiceBroker::GetGUI()->GetTextureManager().SetTexturePath(strMediaDir);
975   m_strMediaDir = strMediaDir;
976 }
977 
GetMediaDir() const978 const std::string& CGraphicContext::GetMediaDir() const
979 {
980   return m_strMediaDir;
981 
982 }
983 
Flip(bool rendered,bool videoLayer)984 void CGraphicContext::Flip(bool rendered, bool videoLayer)
985 {
986   CServiceBroker::GetRenderSystem()->PresentRender(rendered, videoLayer);
987 
988   if(m_stereoMode != m_nextStereoMode)
989   {
990     m_stereoMode = m_nextStereoMode;
991     SetVideoResolution(GetVideoResolution(), true);
992     CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
993   }
994 }
995 
GetAllowedResolutions(std::vector<RESOLUTION> & res)996 void CGraphicContext::GetAllowedResolutions(std::vector<RESOLUTION> &res)
997 {
998   res.clear();
999 
1000   res.push_back(RES_WINDOW);
1001   res.push_back(RES_DESKTOP);
1002   for (size_t r = (size_t) RES_CUSTOM; r < CDisplaySettings::GetInstance().ResolutionInfoSize(); r++)
1003   {
1004     res.push_back((RESOLUTION) r);
1005   }
1006 }
1007 
SetFPS(float fps)1008 void CGraphicContext::SetFPS(float fps)
1009 {
1010   m_fFPSOverride = fps;
1011 }
1012