summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/GraphicContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/windowing/GraphicContext.cpp')
-rw-r--r--xbmc/windowing/GraphicContext.cpp1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/xbmc/windowing/GraphicContext.cpp b/xbmc/windowing/GraphicContext.cpp
new file mode 100644
index 0000000..423148c
--- /dev/null
+++ b/xbmc/windowing/GraphicContext.cpp
@@ -0,0 +1,1011 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GraphicContext.h"
+
+#include "ServiceBroker.h"
+#include "WinSystem.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/TextureManager.h"
+#include "guilib/gui3d.h"
+#include "input/InputManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "rendering/RenderSystem.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "utils/log.h"
+
+#include <cassert>
+#include <mutex>
+
+CGraphicContext::CGraphicContext() = default;
+CGraphicContext::~CGraphicContext() = default;
+
+void CGraphicContext::SetOrigin(float x, float y)
+{
+ if (!m_origins.empty())
+ m_origins.push(CPoint(x,y) + m_origins.top());
+ else
+ m_origins.push(CPoint(x,y));
+
+ AddTransform(TransformMatrix::CreateTranslation(x, y));
+}
+
+void CGraphicContext::RestoreOrigin()
+{
+ if (!m_origins.empty())
+ m_origins.pop();
+ RemoveTransform();
+}
+
+// add a new clip region, intersecting with the previous clip region.
+bool CGraphicContext::SetClipRegion(float x, float y, float w, float h)
+{ // transform from our origin
+ CPoint origin;
+ if (!m_origins.empty())
+ origin = m_origins.top();
+
+ // ok, now intersect with our old clip region
+ CRect rect(x, y, x + w, y + h);
+ rect += origin;
+ if (!m_clipRegions.empty())
+ {
+ // intersect with original clip region
+ rect.Intersect(m_clipRegions.top());
+ }
+
+ if (rect.IsEmpty())
+ return false;
+
+ m_clipRegions.push(rect);
+
+ // here we could set the hardware clipping, if applicable
+ return true;
+}
+
+void CGraphicContext::RestoreClipRegion()
+{
+ if (!m_clipRegions.empty())
+ m_clipRegions.pop();
+
+ // here we could reset the hardware clipping, if applicable
+}
+
+void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
+{
+ // this is the software clipping routine. If the graphics hardware is set to do the clipping
+ // (eg via SetClipPlane in D3D for instance) then this routine is unneeded.
+ if (!m_clipRegions.empty())
+ {
+ // take a copy of the vertex rectangle and intersect
+ // it with our clip region (moved to the same coordinate system)
+ CRect clipRegion(m_clipRegions.top());
+ if (!m_origins.empty())
+ clipRegion -= m_origins.top();
+ CRect original(vertex);
+ vertex.Intersect(clipRegion);
+ // and use the original to compute the texture coordinates
+ if (original != vertex)
+ {
+ float scaleX = texture.Width() / original.Width();
+ float scaleY = texture.Height() / original.Height();
+ texture.x1 += (vertex.x1 - original.x1) * scaleX;
+ texture.y1 += (vertex.y1 - original.y1) * scaleY;
+ texture.x2 += (vertex.x2 - original.x2) * scaleX;
+ texture.y2 += (vertex.y2 - original.y2) * scaleY;
+ if (texture2)
+ {
+ scaleX = texture2->Width() / original.Width();
+ scaleY = texture2->Height() / original.Height();
+ texture2->x1 += (vertex.x1 - original.x1) * scaleX;
+ texture2->y1 += (vertex.y1 - original.y1) * scaleY;
+ texture2->x2 += (vertex.x2 - original.x2) * scaleX;
+ texture2->y2 += (vertex.y2 - original.y2) * scaleY;
+ }
+ }
+ }
+}
+
+CRect CGraphicContext::GetClipRegion()
+{
+ if (m_clipRegions.empty())
+ return CRect(0, 0, m_iScreenWidth, m_iScreenHeight);
+ CRect clipRegion(m_clipRegions.top());
+ if (!m_origins.empty())
+ clipRegion -= m_origins.top();
+ return clipRegion;
+}
+
+void CGraphicContext::AddGUITransform()
+{
+ m_transforms.push(m_finalTransform);
+ m_finalTransform = m_guiTransform;
+}
+
+TransformMatrix CGraphicContext::AddTransform(const TransformMatrix &matrix)
+{
+ m_transforms.push(m_finalTransform);
+ m_finalTransform.matrix *= matrix;
+ return m_finalTransform.matrix;
+}
+
+void CGraphicContext::SetTransform(const TransformMatrix &matrix)
+{
+ m_transforms.push(m_finalTransform);
+ m_finalTransform.matrix = matrix;
+}
+
+void CGraphicContext::SetTransform(const TransformMatrix &matrix, float scaleX, float scaleY)
+{
+ m_transforms.push(m_finalTransform);
+ m_finalTransform.matrix = matrix;
+ m_finalTransform.scaleX = scaleX;
+ m_finalTransform.scaleY = scaleY;
+}
+
+void CGraphicContext::RemoveTransform()
+{
+ if (!m_transforms.empty())
+ {
+ m_finalTransform = m_transforms.top();
+ m_transforms.pop();
+ }
+}
+
+bool CGraphicContext::SetViewPort(float fx, float fy, float fwidth, float fheight, bool intersectPrevious /* = false */)
+{
+ // transform coordinates - we may have a rotation which changes the positioning of the
+ // minimal and maximal viewport extents. We currently go to the maximal extent.
+ float x[4], y[4];
+ x[0] = x[3] = fx;
+ x[1] = x[2] = fx + fwidth;
+ y[0] = y[1] = fy;
+ y[2] = y[3] = fy + fheight;
+ float minX = (float)m_iScreenWidth;
+ float maxX = 0;
+ float minY = (float)m_iScreenHeight;
+ float maxY = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ float z = 0;
+ ScaleFinalCoords(x[i], y[i], z);
+ if (x[i] < minX) minX = x[i];
+ if (x[i] > maxX) maxX = x[i];
+ if (y[i] < minY) minY = y[i];
+ if (y[i] > maxY) maxY = y[i];
+ }
+
+ int newLeft = (int)(minX + 0.5f);
+ int newTop = (int)(minY + 0.5f);
+ int newRight = (int)(maxX + 0.5f);
+ int newBottom = (int)(maxY + 0.5f);
+ if (intersectPrevious)
+ {
+ CRect oldviewport = m_viewStack.top();
+ // do the intersection
+ int oldLeft = (int)oldviewport.x1;
+ int oldTop = (int)oldviewport.y1;
+ int oldRight = (int)oldviewport.x2;
+ int oldBottom = (int)oldviewport.y2;
+ if (newLeft >= oldRight || newTop >= oldBottom || newRight <= oldLeft || newBottom <= oldTop)
+ { // empty intersection - return false to indicate no rendering should occur
+ return false;
+ }
+ // ok, they intersect, do the intersection
+ if (newLeft < oldLeft) newLeft = oldLeft;
+ if (newTop < oldTop) newTop = oldTop;
+ if (newRight > oldRight) newRight = oldRight;
+ if (newBottom > oldBottom) newBottom = oldBottom;
+ }
+ // check range against screen size
+ if (newRight <= 0 || newBottom <= 0 ||
+ newTop >= m_iScreenHeight || newLeft >= m_iScreenWidth ||
+ newLeft >= newRight || newTop >= newBottom)
+ { // no intersection with the screen
+ return false;
+ }
+ // intersection with the screen
+ if (newLeft < 0) newLeft = 0;
+ if (newTop < 0) newTop = 0;
+ if (newRight > m_iScreenWidth) newRight = m_iScreenWidth;
+ if (newBottom > m_iScreenHeight) newBottom = m_iScreenHeight;
+
+ assert(newLeft < newRight);
+ assert(newTop < newBottom);
+
+ CRect newviewport((float)newLeft, (float)newTop, (float)newRight, (float)newBottom);
+
+ m_viewStack.push(newviewport);
+
+ newviewport = StereoCorrection(newviewport);
+ CServiceBroker::GetRenderSystem()->SetViewPort(newviewport);
+
+
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+ return true;
+}
+
+void CGraphicContext::RestoreViewPort()
+{
+ if (m_viewStack.size() <= 1) return;
+
+ m_viewStack.pop();
+ CRect viewport = StereoCorrection(m_viewStack.top());
+ CServiceBroker::GetRenderSystem()->SetViewPort(viewport);
+
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+CPoint CGraphicContext::StereoCorrection(const CPoint &point) const
+{
+ CPoint res(point);
+
+ if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+ {
+ const RESOLUTION_INFO info = GetResInfo();
+
+ if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ res.y += info.iHeight + info.iBlanking;
+ }
+ if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+ {
+ const RESOLUTION_INFO info = GetResInfo();
+
+ if(m_stereoView == RENDER_STEREO_VIEW_RIGHT)
+ res.x += info.iWidth + info.iBlanking;
+ }
+ return res;
+}
+
+CRect CGraphicContext::StereoCorrection(const CRect &rect) const
+{
+ CRect res(StereoCorrection(rect.P1())
+ , StereoCorrection(rect.P2()));
+ return res;
+}
+
+void CGraphicContext::SetScissors(const CRect &rect)
+{
+ m_scissors = rect;
+ m_scissors.Intersect(CRect(0,0,(float)m_iScreenWidth, (float)m_iScreenHeight));
+ CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors));
+}
+
+const CRect &CGraphicContext::GetScissors() const
+{
+ return m_scissors;
+}
+
+void CGraphicContext::ResetScissors()
+{
+ m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
+ CServiceBroker::GetRenderSystem()->SetScissors(StereoCorrection(m_scissors));
+}
+
+const CRect CGraphicContext::GetViewWindow() const
+{
+ if (m_bCalibrating || m_bFullScreenVideo)
+ {
+ CRect rect;
+ RESOLUTION_INFO info = GetResInfo();
+ rect.x1 = (float)info.Overscan.left;
+ rect.y1 = (float)info.Overscan.top;
+ rect.x2 = (float)info.Overscan.right;
+ rect.y2 = (float)info.Overscan.bottom;
+ return rect;
+ }
+ return m_videoRect;
+}
+
+void CGraphicContext::SetViewWindow(float left, float top, float right, float bottom)
+{
+ m_videoRect.x1 = ScaleFinalXCoord(left, top);
+ m_videoRect.y1 = ScaleFinalYCoord(left, top);
+ m_videoRect.x2 = ScaleFinalXCoord(right, bottom);
+ m_videoRect.y2 = ScaleFinalYCoord(right, bottom);
+}
+
+void CGraphicContext::SetFullScreenVideo(bool bOnOff)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ m_bFullScreenVideo = bOnOff;
+
+ if (m_bFullScreenRoot)
+ {
+ bool bTriggerUpdateRes = false;
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (m_bFullScreenVideo)
+ bTriggerUpdateRes = true;
+ else
+ {
+ bool allowDesktopRes = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) == ADJUST_REFRESHRATE_ALWAYS;
+ if (!allowDesktopRes)
+ {
+ if (appPlayer->IsPlayingVideo())
+ bTriggerUpdateRes = true;
+ }
+ }
+
+ bool allowResolutionChangeOnStop = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE) != ADJUST_REFRESHRATE_ON_START;
+ RESOLUTION targetResolutionOnStop = RES_DESKTOP;
+ if (bTriggerUpdateRes)
+ appPlayer->TriggerUpdateResolution();
+ else if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP)
+ {
+ targetResolutionOnStop = CDisplaySettings::GetInstance().GetCurrentResolution();
+ }
+
+ if (allowResolutionChangeOnStop && !bTriggerUpdateRes)
+ {
+ SetVideoResolution(targetResolutionOnStop, false);
+ }
+ }
+ else
+ SetVideoResolution(RES_WINDOW, false);
+}
+
+bool CGraphicContext::IsFullScreenVideo() const
+{
+ return m_bFullScreenVideo;
+}
+
+bool CGraphicContext::IsCalibrating() const
+{
+ return m_bCalibrating;
+}
+
+void CGraphicContext::SetCalibrating(bool bOnOff)
+{
+ m_bCalibrating = bOnOff;
+}
+
+bool CGraphicContext::IsValidResolution(RESOLUTION res)
+{
+ if (res >= RES_WINDOW && (size_t) res < CDisplaySettings::GetInstance().ResolutionInfoSize())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// call SetVideoResolutionInternal and ensure its done from mainthread
+void CGraphicContext::SetVideoResolution(RESOLUTION res, bool forceUpdate)
+{
+ if (CServiceBroker::GetAppMessenger()->IsProcessThread())
+ {
+ SetVideoResolutionInternal(res, forceUpdate);
+ }
+ else
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_SETVIDEORESOLUTION, res, forceUpdate ? 1 : 0);
+ }
+}
+
+void CGraphicContext::SetVideoResolutionInternal(RESOLUTION res, bool forceUpdate)
+{
+ RESOLUTION lastRes = m_Resolution;
+
+ // If the user asked us to guess, go with desktop
+ if (!IsValidResolution(res))
+ {
+ res = RES_DESKTOP;
+ }
+
+ // If we are switching to the same resolution and same window/full-screen, no need to do anything
+ if (!forceUpdate && res == lastRes && m_bFullScreenRoot == CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
+ {
+ return;
+ }
+
+ if (res >= RES_DESKTOP)
+ {
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = true;
+ m_bFullScreenRoot = true;
+ }
+ else
+ {
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = false;
+ m_bFullScreenRoot = false;
+ }
+
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ // FIXME Wayland windowing needs some way to "deny" resolution updates since what Kodi
+ // requests might not get actually set by the compositor.
+ // So in theory, m_iScreenWidth etc. would not need to be updated at all before the
+ // change is confirmed.
+ // But other windowing code expects these variables to be already set when
+ // SetFullScreen() is called, so set them anyway and remember the old values.
+ int origScreenWidth = m_iScreenWidth;
+ int origScreenHeight = m_iScreenHeight;
+ float origFPSOverride = m_fFPSOverride;
+
+ UpdateInternalStateWithResolution(res);
+ RESOLUTION_INFO info_org = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+
+ bool switched = false;
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen)
+ {
+#if defined (TARGET_DARWIN) || defined (TARGET_WINDOWS)
+ bool blankOtherDisplays = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS);
+ switched = CServiceBroker::GetWinSystem()->SetFullScreen(true, info_org, blankOtherDisplays);
+#else
+ switched = CServiceBroker::GetWinSystem()->SetFullScreen(true, info_org, false);
+#endif
+ }
+ else if (lastRes >= RES_DESKTOP )
+ switched = CServiceBroker::GetWinSystem()->SetFullScreen(false, info_org, false);
+ else
+ switched = CServiceBroker::GetWinSystem()->ResizeWindow(info_org.iWidth, info_org.iHeight, -1, -1);
+
+ if (switched)
+ {
+ m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
+
+ // make sure all stereo stuff are correctly setup
+ SetStereoView(RENDER_STEREO_VIEW_OFF);
+
+ // update anyone that relies on sizing information
+ CServiceBroker::GetInputManager().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1);
+
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui)
+ gui->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
+ }
+ else
+ {
+ // Reset old state
+ m_iScreenWidth = origScreenWidth;
+ m_iScreenHeight = origScreenHeight;
+ m_fFPSOverride = origFPSOverride;
+ if (IsValidResolution(lastRes))
+ {
+ m_Resolution = lastRes;
+ }
+ else
+ {
+ // FIXME Resolution has become invalid
+ // This happens e.g. when switching monitors and the new monitor has fewer
+ // resolutions than the old one. Fall back to RES_DESKTOP and hope that
+ // the real resolution is set soon.
+ // Again, must be fixed as part of a greater refactor.
+ m_Resolution = RES_DESKTOP;
+ }
+ }
+}
+
+void CGraphicContext::ApplyVideoResolution(RESOLUTION res)
+{
+ if (!IsValidResolution(res))
+ {
+ CLog::LogF(LOGWARNING, "Asked to apply invalid resolution {}, falling back to RES_DESKTOP",
+ res);
+ res = RES_DESKTOP;
+ }
+
+ if (res >= RES_DESKTOP)
+ {
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = true;
+ m_bFullScreenRoot = true;
+ }
+ else
+ {
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = false;
+ m_bFullScreenRoot = false;
+ }
+
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ UpdateInternalStateWithResolution(res);
+
+ m_scissors.SetRect(0, 0, (float)m_iScreenWidth, (float)m_iScreenHeight);
+
+ // make sure all stereo stuff are correctly setup
+ SetStereoView(RENDER_STEREO_VIEW_OFF);
+
+ // update anyone that relies on sizing information
+ RESOLUTION_INFO info_org = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ CServiceBroker::GetInputManager().SetMouseResolution(info_org.iWidth, info_org.iHeight, 1, 1);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE);
+}
+
+void CGraphicContext::UpdateInternalStateWithResolution(RESOLUTION res)
+{
+ RESOLUTION_INFO info_mod = GetResInfo(res);
+
+ m_iScreenWidth = info_mod.iWidth;
+ m_iScreenHeight = info_mod.iHeight;
+ m_Resolution = res;
+ m_fFPSOverride = 0;
+}
+
+void CGraphicContext::ApplyModeChange(RESOLUTION res)
+{
+ ApplyVideoResolution(res);
+ CServiceBroker::GetWinSystem()->FinishModeChange(res);
+}
+
+void CGraphicContext::ApplyWindowResize(int newWidth, int newHeight)
+{
+ CServiceBroker::GetWinSystem()->SetWindowResolution(newWidth, newHeight);
+ ApplyVideoResolution(RES_WINDOW);
+ CServiceBroker::GetWinSystem()->FinishWindowResize(newWidth, newHeight);
+}
+
+RESOLUTION CGraphicContext::GetVideoResolution() const
+{
+ return m_Resolution;
+}
+
+void CGraphicContext::ResetOverscan(RESOLUTION_INFO &res)
+{
+ res.Overscan.left = 0;
+ res.Overscan.top = 0;
+ res.Overscan.right = res.iWidth;
+ res.Overscan.bottom = res.iHeight;
+}
+
+void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan)
+{
+ overscan.left = 0;
+ overscan.top = 0;
+
+ RESOLUTION_INFO info = GetResInfo(res);
+ overscan.right = info.iWidth;
+ overscan.bottom = info.iHeight;
+}
+
+void CGraphicContext::ResetScreenParameters(RESOLUTION res)
+{
+ RESOLUTION_INFO& info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+
+ info.iSubtitles = info.iHeight;
+ info.fPixelRatio = 1.0f;
+ info.iScreenWidth = info.iWidth;
+ info.iScreenHeight = info.iHeight;
+ ResetOverscan(res, info.Overscan);
+}
+
+void CGraphicContext::Clear(UTILS::COLOR::Color color)
+{
+ CServiceBroker::GetRenderSystem()->ClearBuffers(color);
+}
+
+void CGraphicContext::CaptureStateBlock()
+{
+ CServiceBroker::GetRenderSystem()->CaptureStateBlock();
+}
+
+void CGraphicContext::ApplyStateBlock()
+{
+ CServiceBroker::GetRenderSystem()->ApplyStateBlock();
+}
+
+const RESOLUTION_INFO CGraphicContext::GetResInfo(RESOLUTION res) const
+{
+ RESOLUTION_INFO info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+
+ if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_HORIZONTAL)
+ {
+ if((info.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
+ {
+ info.fPixelRatio /= 2;
+ info.iBlanking = 0;
+ info.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+ }
+ info.iHeight = (info.iHeight - info.iBlanking) / 2;
+ info.Overscan.top /= 2;
+ info.Overscan.bottom = (info.Overscan.bottom - info.iBlanking) / 2;
+ info.iSubtitles = (info.iSubtitles - info.iBlanking) / 2;
+ }
+
+ if(m_stereoMode == RENDER_STEREO_MODE_SPLIT_VERTICAL)
+ {
+ if((info.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
+ {
+ info.fPixelRatio *= 2;
+ info.iBlanking = 0;
+ info.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
+ }
+ info.iWidth = (info.iWidth - info.iBlanking) / 2;
+ info.Overscan.left /= 2;
+ info.Overscan.right = (info.Overscan.right - info.iBlanking) / 2;
+ }
+
+ if (res == m_Resolution && m_fFPSOverride != 0)
+ {
+ info.fRefreshRate = m_fFPSOverride;
+ }
+
+ return info;
+}
+
+void CGraphicContext::SetResInfo(RESOLUTION res, const RESOLUTION_INFO& info)
+{
+ RESOLUTION_INFO& curr = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ curr.Overscan = info.Overscan;
+ curr.iSubtitles = info.iSubtitles;
+ curr.fPixelRatio = info.fPixelRatio;
+
+ if(info.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+ {
+ curr.Overscan.right = info.Overscan.right * 2 + info.iBlanking;
+ if((curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS) == 0)
+ curr.fPixelRatio /= 2.0f;
+ }
+
+ if(info.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+ {
+ curr.Overscan.bottom = info.Overscan.bottom * 2 + info.iBlanking;
+ curr.iSubtitles = info.iSubtitles * 2 + info.iBlanking;
+ if((curr.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
+ curr.fPixelRatio *= 2.0f;
+ }
+}
+
+const RESOLUTION_INFO CGraphicContext::GetResInfo() const
+{
+ return GetResInfo(m_Resolution);
+}
+
+void CGraphicContext::GetGUIScaling(const RESOLUTION_INFO &res, float &scaleX, float &scaleY, TransformMatrix *matrix /* = NULL */)
+{
+ if (m_Resolution != RES_INVALID)
+ {
+ // calculate necessary scalings
+ RESOLUTION_INFO info = GetResInfo();
+ float fFromWidth = (float)res.iWidth;
+ float fFromHeight = (float)res.iHeight;
+ auto fToPosX = info.Overscan.left + info.guiInsets.left;
+ auto fToPosY = info.Overscan.top + info.guiInsets.top;
+ auto fToWidth = info.Overscan.right - info.guiInsets.right - fToPosX;
+ auto fToHeight = info.Overscan.bottom - info.guiInsets.bottom - fToPosY;
+
+ float fZoom = (100 + CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_SKINZOOM)) * 0.01f;
+
+ fZoom -= 1.0f;
+ fToPosX -= fToWidth * fZoom * 0.5f;
+ fToWidth *= fZoom + 1.0f;
+
+ // adjust for aspect ratio as zoom is given in the vertical direction and we don't
+ // do aspect ratio corrections in the gui code
+ fZoom = fZoom / info.fPixelRatio;
+ fToPosY -= fToHeight * fZoom * 0.5f;
+ fToHeight *= fZoom + 1.0f;
+
+ scaleX = fFromWidth / fToWidth;
+ scaleY = fFromHeight / fToHeight;
+ if (matrix)
+ {
+ TransformMatrix guiScaler = TransformMatrix::CreateScaler(fToWidth / fFromWidth, fToHeight / fFromHeight, fToHeight / fFromHeight);
+ TransformMatrix guiOffset = TransformMatrix::CreateTranslation(fToPosX, fToPosY);
+ *matrix = guiOffset * guiScaler;
+ }
+ }
+ else
+ {
+ scaleX = scaleY = 1.0f;
+ if (matrix)
+ matrix->Reset();
+ }
+}
+
+void CGraphicContext::SetScalingResolution(const RESOLUTION_INFO &res, bool needsScaling)
+{
+ m_windowResolution = res;
+ if (needsScaling && m_Resolution != RES_INVALID)
+ GetGUIScaling(res, m_guiTransform.scaleX, m_guiTransform.scaleY, &m_guiTransform.matrix);
+ else
+ {
+ m_guiTransform.Reset();
+ }
+
+ // reset our origin and camera
+ while (!m_origins.empty())
+ m_origins.pop();
+ m_origins.push(CPoint(0, 0));
+ while (!m_cameras.empty())
+ m_cameras.pop();
+ m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight));
+ while (!m_stereoFactors.empty())
+ m_stereoFactors.pop();
+ m_stereoFactors.push(0.0f);
+
+ // and reset the final transform
+ m_finalTransform = m_guiTransform;
+}
+
+void CGraphicContext::SetRenderingResolution(const RESOLUTION_INFO &res, bool needsScaling)
+{
+ std::unique_lock<CCriticalSection> lock(*this);
+
+ SetScalingResolution(res, needsScaling);
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+void CGraphicContext::SetStereoView(RENDER_STEREO_VIEW view)
+{
+ m_stereoView = view;
+
+ while(!m_viewStack.empty())
+ m_viewStack.pop();
+
+ CRect viewport(0.0f, 0.0f, (float)m_iScreenWidth, (float)m_iScreenHeight);
+
+ m_viewStack.push(viewport);
+
+ viewport = StereoCorrection(viewport);
+ CServiceBroker::GetRenderSystem()->SetStereoMode(m_stereoMode, m_stereoView);
+ CServiceBroker::GetRenderSystem()->SetViewPort(viewport);
+ CServiceBroker::GetRenderSystem()->SetScissors(viewport);
+}
+
+void CGraphicContext::InvertFinalCoords(float &x, float &y) const
+{
+ m_finalTransform.matrix.InverseTransformPosition(x, y);
+}
+
+float CGraphicContext::ScaleFinalXCoord(float x, float y) const
+{
+ return m_finalTransform.matrix.TransformXCoord(x, y, 0);
+}
+
+float CGraphicContext::ScaleFinalYCoord(float x, float y) const
+{
+ return m_finalTransform.matrix.TransformYCoord(x, y, 0);
+}
+
+float CGraphicContext::ScaleFinalZCoord(float x, float y) const
+{
+ return m_finalTransform.matrix.TransformZCoord(x, y, 0);
+}
+
+void CGraphicContext::ScaleFinalCoords(float &x, float &y, float &z) const
+{
+ m_finalTransform.matrix.TransformPosition(x, y, z);
+}
+
+float CGraphicContext::GetScalingPixelRatio() const
+{
+ // assume the resolutions are different - we want to return the aspect ratio of the video resolution
+ // but only once it's been corrected for the skin -> screen coordinates scaling
+ return GetResInfo().fPixelRatio * (m_finalTransform.scaleY / m_finalTransform.scaleX);
+}
+
+void CGraphicContext::SetCameraPosition(const CPoint &camera)
+{
+ // offset the camera from our current location (this is in XML coordinates) and scale it up to
+ // the screen resolution
+ CPoint cam(camera);
+ if (!m_origins.empty())
+ cam += m_origins.top();
+
+ cam.x *= (float)m_iScreenWidth / m_windowResolution.iWidth;
+ cam.y *= (float)m_iScreenHeight / m_windowResolution.iHeight;
+
+ m_cameras.push(cam);
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+void CGraphicContext::RestoreCameraPosition()
+{ // remove the top camera from the stack
+ assert(m_cameras.size());
+ m_cameras.pop();
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+void CGraphicContext::SetStereoFactor(float factor)
+{
+ m_stereoFactors.push(factor);
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+void CGraphicContext::RestoreStereoFactor()
+{ // remove the top factor from the stack
+ assert(m_stereoFactors.size());
+ m_stereoFactors.pop();
+ UpdateCameraPosition(m_cameras.top(), m_stereoFactors.top());
+}
+
+CRect CGraphicContext::GenerateAABB(const CRect &rect) const
+{
+// ------------------------
+// |(x1, y1) (x2, y2)|
+// | |
+// |(x3, y3) (x4, y4)|
+// ------------------------
+
+ float x1 = rect.x1, x2 = rect.x2, x3 = rect.x1, x4 = rect.x2;
+ float y1 = rect.y1, y2 = rect.y1, y3 = rect.y2, y4 = rect.y2;
+
+ float z = 0.0f;
+ ScaleFinalCoords(x1, y1, z);
+ CServiceBroker::GetRenderSystem()->Project(x1, y1, z);
+
+ z = 0.0f;
+ ScaleFinalCoords(x2, y2, z);
+ CServiceBroker::GetRenderSystem()->Project(x2, y2, z);
+
+ z = 0.0f;
+ ScaleFinalCoords(x3, y3, z);
+ CServiceBroker::GetRenderSystem()->Project(x3, y3, z);
+
+ z = 0.0f;
+ ScaleFinalCoords(x4, y4, z);
+ CServiceBroker::GetRenderSystem()->Project(x4, y4, z);
+
+ return CRect( std::min(std::min(std::min(x1, x2), x3), x4),
+ std::min(std::min(std::min(y1, y2), y3), y4),
+ std::max(std::max(std::max(x1, x2), x3), x4),
+ std::max(std::max(std::max(y1, y2), y3), y4));
+}
+
+// NOTE: This routine is currently called (twice) every time there is a <camera>
+// tag in the skin. It actually only has to be called before we render
+// something, so another option is to just save the camera coordinates
+// and then have a routine called before every draw that checks whether
+// the camera has changed, and if so, changes it. Similarly, it could set
+// the world transform at that point as well (or even combine world + view
+// to cut down on one setting)
+void CGraphicContext::UpdateCameraPosition(const CPoint &camera, const float &factor)
+{
+ float stereoFactor = 0.f;
+ if ( m_stereoMode != RENDER_STEREO_MODE_OFF
+ && m_stereoMode != RENDER_STEREO_MODE_MONO
+ && m_stereoView != RENDER_STEREO_VIEW_OFF)
+ {
+ RESOLUTION_INFO res = GetResInfo();
+ RESOLUTION_INFO desktop = GetResInfo(RES_DESKTOP);
+ float scaleRes = (static_cast<float>(res.iWidth) / static_cast<float>(desktop.iWidth));
+ float scaleX = static_cast<float>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_STEREOSTRENGTH)) * scaleRes;
+ stereoFactor = factor * (m_stereoView == RENDER_STEREO_VIEW_LEFT ? scaleX : -scaleX);
+ }
+ CServiceBroker::GetRenderSystem()->SetCameraPosition(camera, m_iScreenWidth, m_iScreenHeight, stereoFactor);
+}
+
+bool CGraphicContext::RectIsAngled(float x1, float y1, float x2, float y2) const
+{ // need only test 3 points, as they must be co-planer
+ if (m_finalTransform.matrix.TransformZCoord(x1, y1, 0)) return true;
+ if (m_finalTransform.matrix.TransformZCoord(x2, y2, 0)) return true;
+ if (m_finalTransform.matrix.TransformZCoord(x1, y2, 0)) return true;
+ return false;
+}
+
+const TransformMatrix &CGraphicContext::GetGUIMatrix() const
+{
+ return m_finalTransform.matrix;
+}
+
+float CGraphicContext::GetGUIScaleX() const
+{
+ return m_finalTransform.scaleX;
+}
+
+float CGraphicContext::GetGUIScaleY() const
+{
+ return m_finalTransform.scaleY;
+}
+
+UTILS::COLOR::Color CGraphicContext::MergeAlpha(UTILS::COLOR::Color color) const
+{
+ UTILS::COLOR::Color alpha = m_finalTransform.matrix.TransformAlpha((color >> 24) & 0xff);
+ if (alpha > 255) alpha = 255;
+ return ((alpha << 24) & 0xff000000) | (color & 0xffffff);
+}
+
+UTILS::COLOR::Color CGraphicContext::MergeColor(UTILS::COLOR::Color color) const
+{
+ return m_finalTransform.matrix.TransformColor(color);
+}
+
+int CGraphicContext::GetWidth() const
+{
+ return m_iScreenWidth;
+}
+
+int CGraphicContext::GetHeight() const
+{
+ return m_iScreenHeight;
+}
+
+float CGraphicContext::GetFPS() const
+{
+ if (m_Resolution != RES_INVALID)
+ {
+ RESOLUTION_INFO info = GetResInfo();
+ if (info.fRefreshRate > 0)
+ return info.fRefreshRate;
+ }
+ return 60.0f;
+}
+
+float CGraphicContext::GetDisplayLatency() const
+{
+ float latency = CServiceBroker::GetWinSystem()->GetDisplayLatency();
+ if (latency < 0.0f)
+ {
+ // fallback
+ latency = (CServiceBroker::GetWinSystem()->NoOfBuffers() + 1) / GetFPS() * 1000.0f;
+ }
+
+ return latency;
+}
+
+bool CGraphicContext::IsFullScreenRoot () const
+{
+ return m_bFullScreenRoot;
+}
+
+void CGraphicContext::ToggleFullScreen()
+{
+ RESOLUTION uiRes;
+
+ if (m_bFullScreenRoot)
+ {
+ uiRes = RES_WINDOW;
+ }
+ else
+ {
+ if (CDisplaySettings::GetInstance().GetCurrentResolution() > RES_DESKTOP)
+ uiRes = CDisplaySettings::GetInstance().GetCurrentResolution();
+ else
+ uiRes = RES_DESKTOP;
+ }
+
+ CDisplaySettings::GetInstance().SetCurrentResolution(uiRes, true);
+}
+
+void CGraphicContext::SetMediaDir(const std::string &strMediaDir)
+{
+ CServiceBroker::GetGUI()->GetTextureManager().SetTexturePath(strMediaDir);
+ m_strMediaDir = strMediaDir;
+}
+
+const std::string& CGraphicContext::GetMediaDir() const
+{
+ return m_strMediaDir;
+
+}
+
+void CGraphicContext::Flip(bool rendered, bool videoLayer)
+{
+ CServiceBroker::GetRenderSystem()->PresentRender(rendered, videoLayer);
+
+ if(m_stereoMode != m_nextStereoMode)
+ {
+ m_stereoMode = m_nextStereoMode;
+ SetVideoResolution(GetVideoResolution(), true);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_RENDERER_RESET);
+ }
+}
+
+void CGraphicContext::GetAllowedResolutions(std::vector<RESOLUTION> &res)
+{
+ res.clear();
+
+ res.push_back(RES_WINDOW);
+ res.push_back(RES_DESKTOP);
+ for (size_t r = (size_t) RES_CUSTOM; r < CDisplaySettings::GetInstance().ResolutionInfoSize(); r++)
+ {
+ res.push_back((RESOLUTION) r);
+ }
+}
+
+void CGraphicContext::SetFPS(float fps)
+{
+ m_fFPSOverride = fps;
+}