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