merged: Linuxport revisions 14057-14059
[xbmc:xbmc-antiquated.git] / guilib / GraphicContext.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "include.h"
23 #include "GraphicContext.h"
24 #include "GUIMessage.h"
25 #include "IMsgSenderCallback.h"
26 #include "Settings.h"
27 #include "GUISettings.h"
28 #include "XBVideoConfig.h"
29 #ifdef HAS_XBOX_D3D
30  #include "xgraphics.h"
31  #define D3D_CLEAR_STENCIL D3DCLEAR_STENCIL
32 #else
33  #define D3D_CLEAR_STENCIL 0x0l
34 #endif
35 #include "SkinInfo.h"
36
37 using namespace std;
38
39 CGraphicContext g_graphicsContext;
40
41 /* quick access to a skin setting, fine unless we starts clearing video settings */
42 static CSettingInt* g_guiSkinzoom = NULL;
43
44 CGraphicContext::CGraphicContext(void)
45 {
46   m_iScreenWidth = 720;
47   m_iScreenHeight = 576;
48   m_pd3dDevice = NULL;
49   m_pd3dParams = NULL;
50   m_stateBlock = 0xffffffff;
51   m_dwID = 0;
52   m_strMediaDir = "D:\\media";
53   m_bCalibrating = false;
54   m_Resolution = INVALID;
55   m_pCallback = NULL;
56   m_guiScaleX = m_guiScaleY = 1.0f;
57   m_windowResolution = INVALID;
58 }
59
60 CGraphicContext::~CGraphicContext(void)
61 {
62   if (m_stateBlock != 0xffffffff)
63   {
64     Get3DDevice()->DeleteStateBlock(m_stateBlock);
65   }
66   while (m_viewStack.size())
67   {
68     D3DVIEWPORT8 *viewport = m_viewStack.top();
69     m_viewStack.pop();
70     if (viewport) delete viewport;
71   }
72 }
73
74
75 void CGraphicContext::SetD3DDevice(LPDIRECT3DDEVICE8 p3dDevice)
76 {
77   m_pd3dDevice = p3dDevice;
78 }
79
80 void CGraphicContext::SetD3DParameters(D3DPRESENT_PARAMETERS *p3dParams)
81 {
82   m_pd3dParams = p3dParams;
83 }
84
85 bool CGraphicContext::SendMessage(CGUIMessage& message)
86 {
87   if (!m_pCallback) return false;
88   return m_pCallback->SendMessage(message);
89 }
90
91 void CGraphicContext::setMessageSender(IMsgSenderCallback* pCallback)
92 {
93   m_pCallback = pCallback;
94 }
95
96 DWORD CGraphicContext::GetNewID()
97 {
98   m_dwID++;
99   return m_dwID;
100 }
101
102 void CGraphicContext::SetOrigin(float x, float y)
103 {
104   if (m_origins.size())
105     m_origins.push(CPoint(x,y) + m_origins.top());
106   else
107     m_origins.push(CPoint(x,y));
108   AddTransform(TransformMatrix::CreateTranslation(x, y));
109 }
110
111 void CGraphicContext::RestoreOrigin()
112 {
113   m_origins.pop();
114   RemoveTransform();
115 }
116
117 // add a new clip region, intersecting with the previous clip region.
118 bool CGraphicContext::SetClipRegion(float x, float y, float w, float h)
119 { // transform from our origin
120   CPoint origin;
121   if (m_origins.size())
122     origin = m_origins.top();
123   // ok, now intersect with our old clip region
124   CRect rect(x, y, x + w, y + h);
125   rect += origin;
126   if (m_clipRegions.size())
127   { // intersect with original clip region
128     rect.Intersect(m_clipRegions.top());
129   }
130   if (rect.IsEmpty())
131     return false;
132   m_clipRegions.push(rect);
133
134   // here we could set the hardware clipping, if applicable
135   return true;
136 }
137
138 void CGraphicContext::RestoreClipRegion()
139 {
140   if (m_clipRegions.size())
141     m_clipRegions.pop();
142
143   // here we could reset the hardware clipping, if applicable
144 }
145
146 void CGraphicContext::ClipRect(CRect &vertex, CRect &texture, CRect *texture2)
147 {
148   // this is the software clipping routine.  If the graphics hardware is set to do the clipping
149   // (eg via SetClipPlane in D3D for instance) then this routine is unneeded.
150   if (m_clipRegions.size())
151   {
152     // take a copy of the vertex rectangle and intersect
153     // it with our clip region (moved to the same coordinate system)
154     CRect clipRegion(m_clipRegions.top());
155     if (m_origins.size())
156       clipRegion -= m_origins.top();
157     CRect original(vertex);
158     vertex.Intersect(clipRegion);
159     // and use the original to compute the texture coordinates
160     if (original != vertex)
161     {
162       float scaleX = texture.Width() / original.Width();
163       float scaleY = texture.Height() / original.Height();
164       texture.x1 += (vertex.x1 - original.x1) * scaleX;
165       texture.y1 += (vertex.y1 - original.y1) * scaleY;
166       texture.x2 += (vertex.x2 - original.x2) * scaleX;
167       texture.y2 += (vertex.y2 - original.y2) * scaleY;
168       if (texture2)
169       {
170         scaleX = texture2->Width() / original.Width();
171         scaleY = texture2->Height() / original.Height();
172         texture2->x1 += (vertex.x1 - original.x1) * scaleX;
173         texture2->y1 += (vertex.y1 - original.y1) * scaleY;
174         texture2->x2 += (vertex.x2 - original.x2) * scaleX;
175         texture2->y2 += (vertex.y2 - original.y2) * scaleY;
176       }
177     }
178   }
179 }
180
181 bool CGraphicContext::SetViewPort(float fx, float fy , float fwidth, float fheight, bool intersectPrevious /* = false */)
182 {
183   D3DVIEWPORT8 newviewport;
184   D3DVIEWPORT8 *oldviewport = new D3DVIEWPORT8;
185   Get3DDevice()->GetViewport(oldviewport);
186   // transform coordinates - we may have a rotation which changes the positioning of the
187   // minimal and maximal viewport extents.  We currently go to the maximal extent.
188   float x[4], y[4];
189   x[0] = x[3] = fx;
190   x[1] = x[2] = fx + fwidth;
191   y[0] = y[1] = fy;
192   y[2] = y[3] = fy + fheight;
193   float minX = (float)m_iScreenWidth;
194   float maxX = 0;
195   float minY = (float)m_iScreenHeight;
196   float maxY = 0;
197   for (int i = 0; i < 4; i++)
198   {
199     float z = 0;
200     ScaleFinalCoords(x[i], y[i], z);
201     if (x[i] < minX) minX = x[i];
202     if (x[i] > maxX) maxX = x[i];
203     if (y[i] < minY) minY = y[i];
204     if (y[i] > maxY) maxY = y[i];
205   }
206
207   int newLeft = (int)(minX + 0.5f);
208   int newTop = (int)(minY + 0.5f);
209   int newRight = (int)(maxX + 0.5f);
210   int newBottom = (int)(maxY + 0.5f);
211   if (intersectPrevious)
212   {
213     // do the intersection
214     int oldLeft = (int)oldviewport->X;
215     int oldTop = (int)oldviewport->Y;
216     int oldRight = (int)oldviewport->X + oldviewport->Width;
217     int oldBottom = (int)oldviewport->Y + oldviewport->Height;
218     if (newLeft >= oldRight || newTop >= oldBottom || newRight <= oldLeft || newBottom <= oldTop)
219     { // empty intersection - return false to indicate no rendering should occur
220       delete oldviewport;
221       return false;
222     }
223     // ok, they intersect, do the intersection
224     if (newLeft < oldLeft) newLeft = oldLeft;
225     if (newTop < oldTop) newTop = oldTop;
226     if (newRight > oldRight) newRight = oldRight;
227     if (newBottom > oldBottom) newBottom = oldBottom;
228   }
229   // check range against screen size
230   if (newRight <= 0 || newBottom <= 0 ||
231       newTop >= m_iScreenHeight || newLeft >= m_iScreenWidth ||
232       newLeft >= newRight || newTop >= newBottom)
233   { // no intersection with the screen
234     delete oldviewport;
235     return false;
236   }
237   // intersection with the screen
238   if (newLeft < 0) newLeft = 0;
239   if (newTop < 0) newTop = 0;
240   if (newRight > m_iScreenWidth) newRight = m_iScreenWidth;
241   if (newBottom > m_iScreenHeight) newBottom = m_iScreenHeight;
242
243   ASSERT(newLeft < newRight);
244   ASSERT(newTop < newBottom);
245
246   newviewport.MinZ = 0.0f;
247   newviewport.MaxZ = 1.0f;
248   newviewport.X = newLeft;
249   newviewport.Y = newTop;
250   newviewport.Width = newRight - newLeft;
251   newviewport.Height = newBottom - newTop;
252   m_pd3dDevice->SetViewport(&newviewport);
253   m_viewStack.push(oldviewport);
254  
255   UpdateCameraPosition(m_cameras.top());
256   return true;
257 }
258
259 void CGraphicContext::RestoreViewPort()
260 {
261   if (!m_viewStack.size()) return;
262   D3DVIEWPORT8 *oldviewport = (D3DVIEWPORT8*)m_viewStack.top();
263   m_viewStack.pop();
264   Get3DDevice()->SetViewport(oldviewport);
265
266   if (oldviewport) delete oldviewport;
267
268   UpdateCameraPosition(m_cameras.top());
269 }
270
271 const RECT& CGraphicContext::GetViewWindow() const
272 {
273   return m_videoRect;
274 }
275 void CGraphicContext::SetViewWindow(float left, float top, float right, float bottom)
276 {
277   if (m_bCalibrating)
278   {
279     SetFullScreenViewWindow(m_Resolution);
280   }
281   else
282   {
283     m_videoRect.left = (long)(ScaleFinalXCoord(left, top) + 0.5f);
284     m_videoRect.top = (long)(ScaleFinalYCoord(left, top) + 0.5f);
285     m_videoRect.right = (long)(ScaleFinalXCoord(right, bottom) + 0.5f);
286     m_videoRect.bottom = (long)(ScaleFinalYCoord(right, bottom) + 0.5f);
287   }
288 }
289
290 void CGraphicContext::ClipToViewWindow()
291 {
292   D3DRECT clip = { m_videoRect.left, m_videoRect.top, m_videoRect.right, m_videoRect.bottom };
293   if (m_videoRect.left < 0) clip.x1 = 0;
294   if (m_videoRect.top < 0) clip.y1 = 0;
295   if (m_videoRect.left > m_iScreenWidth - 1) clip.x1 = m_iScreenWidth - 1;
296   if (m_videoRect.top > m_iScreenHeight - 1) clip.y1 = m_iScreenHeight - 1;
297   if (m_videoRect.right > m_iScreenWidth) clip.x2 = m_iScreenWidth;
298   if (m_videoRect.bottom > m_iScreenHeight) clip.y2 = m_iScreenHeight;
299   if (clip.x2 < clip.x1) clip.x2 = clip.x1 + 1;
300   if (clip.y2 < clip.y1) clip.y2 = clip.y1 + 1;
301 #ifdef HAS_XBOX_D3D
302   m_pd3dDevice->SetScissors(1, FALSE, &clip);
303 #endif
304 }
305
306 void CGraphicContext::SetFullScreenViewWindow(RESOLUTION &res)
307 {
308   m_videoRect.left = g_settings.m_ResInfo[res].Overscan.left;
309   m_videoRect.top = g_settings.m_ResInfo[res].Overscan.top;
310   m_videoRect.right = g_settings.m_ResInfo[res].Overscan.right;
311   m_videoRect.bottom = g_settings.m_ResInfo[res].Overscan.bottom;
312 }
313
314 void CGraphicContext::SetFullScreenVideo(bool bOnOff)
315 {
316   Lock();
317   m_bFullScreenVideo = bOnOff;
318   SetFullScreenViewWindow(m_Resolution);
319   Unlock();
320 }
321
322 bool CGraphicContext::IsFullScreenVideo() const
323 {
324   return m_bFullScreenVideo;
325 }
326
327 bool CGraphicContext::IsCalibrating() const
328 {
329   return m_bCalibrating;
330 }
331
332 void CGraphicContext::SetCalibrating(bool bOnOff)
333 {
334   m_bCalibrating = bOnOff;
335 }
336
337 bool CGraphicContext::IsValidResolution(RESOLUTION res)
338 {
339   return g_videoConfig.IsValidResolution(res);
340 }
341
342 void CGraphicContext::GetAllowedResolutions(vector<RESOLUTION> &res, bool bAllowPAL60)
343 {
344   bool bCanDoWidescreen = g_videoConfig.HasWidescreen();
345   res.clear();  
346   if (g_videoConfig.HasPAL())
347   {
348     res.push_back(PAL_4x3);
349     if (bCanDoWidescreen) res.push_back(PAL_16x9);
350     if (bAllowPAL60 && g_videoConfig.HasPAL60())
351     {
352       res.push_back(PAL60_4x3);
353       if (bCanDoWidescreen) res.push_back(PAL60_16x9);
354     }
355   }
356   if (g_videoConfig.HasNTSC())
357   {
358     res.push_back(NTSC_4x3);
359     if (bCanDoWidescreen) res.push_back(NTSC_16x9);
360     if (g_videoConfig.Has480p())
361     {
362       res.push_back(HDTV_480p_4x3);
363       if (bCanDoWidescreen) res.push_back(HDTV_480p_16x9);
364     }
365     if (g_videoConfig.Has720p())
366       res.push_back(HDTV_720p);
367     if (g_videoConfig.Has1080i())
368       res.push_back(HDTV_1080i);
369   }
370 }
371
372 void CGraphicContext::SetVideoResolution(RESOLUTION &res, BOOL NeedZ, bool forceClear /* = false */)
373 {
374   if (res == AUTORES)
375   {
376     res = g_videoConfig.GetBestMode();
377   }
378   if (!IsValidResolution(res))
379   { // Choose a failsafe resolution that we can actually display
380     CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode");
381     res = g_videoConfig.GetSafeMode();
382   }
383   
384   if (!m_pd3dParams)
385   {
386     m_Resolution = res;
387     return ;
388   }
389   bool NeedReset = false;
390
391   UINT interval = D3DPRESENT_INTERVAL_ONE;  
392   //if( m_bFullScreenVideo )
393   //  interval = D3DPRESENT_INTERVAL_IMMEDIATE;
394
395 #ifdef PROFILE
396   interval = D3DPRESENT_INTERVAL_IMMEDIATE;
397 #endif
398
399 #ifndef HAS_XBOX_D3D
400   interval = 0;
401 #endif
402
403   if (interval != m_pd3dParams->FullScreen_PresentationInterval)
404   {
405     m_pd3dParams->FullScreen_PresentationInterval = interval;
406     NeedReset = true;
407   }
408
409
410   if (NeedZ != m_pd3dParams->EnableAutoDepthStencil)
411   {
412     m_pd3dParams->EnableAutoDepthStencil = NeedZ;
413     NeedReset = true;
414   }
415   if (m_Resolution != res)
416   {
417     NeedReset = true;
418     m_pd3dParams->BackBufferWidth = g_settings.m_ResInfo[res].iWidth;
419     m_pd3dParams->BackBufferHeight = g_settings.m_ResInfo[res].iHeight;
420     m_pd3dParams->Flags = g_settings.m_ResInfo[res].dwFlags;
421     m_pd3dParams->Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
422
423     if (res == HDTV_1080i || res == HDTV_720p || m_bFullScreenVideo)
424       m_pd3dParams->BackBufferCount = 1;
425     else
426       m_pd3dParams->BackBufferCount = 2;
427
428     if (res == PAL60_4x3 || res == PAL60_16x9)
429     {
430       if (m_pd3dParams->BackBufferWidth <= 720 && m_pd3dParams->BackBufferHeight <= 480)
431       {
432         m_pd3dParams->FullScreen_RefreshRateInHz = 60;
433       }
434       else
435       {
436         m_pd3dParams->FullScreen_RefreshRateInHz = 0;
437       }
438     }
439     else
440     {
441       m_pd3dParams->FullScreen_RefreshRateInHz = 0;
442     }
443   }
444   Lock();
445   if (m_pd3dDevice)
446   {
447     if (NeedReset)
448     {
449       CLog::Log(LOGDEBUG, "Setting resolution %i", res);
450       m_pd3dDevice->Reset(m_pd3dParams);
451     }
452
453     /* need to clear and preset, otherwise flicker filters won't take effect */
454     if (NeedReset || forceClear)
455     {
456       m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3D_CLEAR_STENCIL, 0x00010001, 1.0f, 0L );
457       m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
458     }
459
460     m_iScreenWidth = m_pd3dParams->BackBufferWidth;
461     m_iScreenHeight = m_pd3dParams->BackBufferHeight;
462     m_bWidescreen = (m_pd3dParams->Flags & D3DPRESENTFLAG_WIDESCREEN) != 0;
463   }
464   if ((g_settings.m_ResInfo[m_Resolution].iWidth != g_settings.m_ResInfo[res].iWidth) || (g_settings.m_ResInfo[m_Resolution].iHeight != g_settings.m_ResInfo[res].iHeight))
465   { // set the mouse resolution
466     g_Mouse.SetResolution(g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight, 1, 1);
467   }
468
469   SetFullScreenViewWindow(res);
470   SetScreenFilters(m_bFullScreenVideo);
471   
472   m_Resolution = res;
473   if(NeedReset)
474     CLog::Log(LOGDEBUG, "We set resolution %i", m_Resolution);
475
476   Unlock();  
477 }
478
479 RESOLUTION CGraphicContext::GetVideoResolution() const
480 {
481   return m_Resolution;
482 }
483
484 void CGraphicContext::SetScreenFilters(bool useFullScreenFilters)
485 {
486   Lock();
487   if (m_pd3dDevice)
488   {
489     // These are only valid here and nowhere else
490     // set soften on/off
491 #ifdef HAS_XBOX_D3D
492     m_pd3dDevice->SetSoftDisplayFilter(useFullScreenFilters ? g_guiSettings.GetBool("videoplayer.soften") : g_guiSettings.GetBool("videoscreen.soften"));
493     m_pd3dDevice->SetFlickerFilter(useFullScreenFilters ? g_guiSettings.GetInt("videoplayer.flicker") : g_guiSettings.GetInt("videoscreen.flickerfilter"));
494 #endif
495   }
496   Unlock();
497 }
498
499 void CGraphicContext::ResetOverscan(RESOLUTION res, OVERSCAN &overscan)
500 {
501   overscan.left = 0;
502   overscan.top = 0;
503   switch (res)
504   {
505   case HDTV_1080i:
506     overscan.right = 1920;
507     overscan.bottom = 1080;
508     break;
509   case HDTV_720p:
510     overscan.right = 1280;
511     overscan.bottom = 720;
512     break;
513   case HDTV_480p_16x9:
514   case HDTV_480p_4x3:
515   case NTSC_16x9:
516   case NTSC_4x3:
517   case PAL60_16x9:
518   case PAL60_4x3:
519     overscan.right = 720;
520     overscan.bottom = 480;
521     break;
522   case PAL_16x9:
523   case PAL_4x3:
524     overscan.right = 720;
525     overscan.bottom = 576;
526     break;
527   default:
528     break;
529   }
530 }
531
532 void CGraphicContext::ResetScreenParameters(RESOLUTION res)
533 {
534   ResetOverscan(res, g_settings.m_ResInfo[res].Overscan);
535   g_settings.m_ResInfo[res].fPixelRatio = GetPixelRatio(res);
536   // 1080i
537   switch (res)
538   {
539   case HDTV_1080i:
540     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 1080);
541     g_settings.m_ResInfo[res].iWidth = 1920;
542     g_settings.m_ResInfo[res].iHeight = 1080;
543     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
544     g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
545     strcpy(g_settings.m_ResInfo[res].strMode, "1080i 16:9");
546     break;
547   case HDTV_720p:
548     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 720);
549     g_settings.m_ResInfo[res].iWidth = 1280;
550     g_settings.m_ResInfo[res].iHeight = 720;
551     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN;
552     g_settings.m_ResInfo[res].fPixelRatio = 1.0f;
553     strcpy(g_settings.m_ResInfo[res].strMode, "720p 16:9");
554     break;
555   case HDTV_480p_4x3:
556     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
557     g_settings.m_ResInfo[res].iWidth = 720;
558     g_settings.m_ResInfo[res].iHeight = 480;
559     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE;
560     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
561     strcpy(g_settings.m_ResInfo[res].strMode, "480p 4:3");
562     break;
563   case HDTV_480p_16x9:
564     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
565     g_settings.m_ResInfo[res].iWidth = 720;
566     g_settings.m_ResInfo[res].iHeight = 480;
567     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN;
568     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
569     strcpy(g_settings.m_ResInfo[res].strMode, "480p 16:9");
570     break;
571   case NTSC_4x3:
572     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
573     g_settings.m_ResInfo[res].iWidth = 720;
574     g_settings.m_ResInfo[res].iHeight = 480;
575     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
576     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
577     strcpy(g_settings.m_ResInfo[res].strMode, "NTSC 4:3");
578     break;
579   case NTSC_16x9:
580     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
581     g_settings.m_ResInfo[res].iWidth = 720;
582     g_settings.m_ResInfo[res].iHeight = 480;
583     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
584     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
585     strcpy(g_settings.m_ResInfo[res].strMode, "NTSC 16:9");
586     break;
587   case PAL_4x3:
588     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 576);
589     g_settings.m_ResInfo[res].iWidth = 720;
590     g_settings.m_ResInfo[res].iHeight = 576;
591     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
592     g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f;
593     strcpy(g_settings.m_ResInfo[res].strMode, "PAL 4:3");
594     break;
595   case PAL_16x9:
596     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 576);
597     g_settings.m_ResInfo[res].iWidth = 720;
598     g_settings.m_ResInfo[res].iHeight = 576;
599     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
600     g_settings.m_ResInfo[res].fPixelRatio = 128.0f / 117.0f*4.0f / 3.0f;
601     strcpy(g_settings.m_ResInfo[res].strMode, "PAL 16:9");
602     break;
603   case PAL60_4x3:
604     g_settings.m_ResInfo[res].iSubtitles = (int)(0.9 * 480);
605     g_settings.m_ResInfo[res].iWidth = 720;
606     g_settings.m_ResInfo[res].iHeight = 480;
607     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED;
608     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f;
609     strcpy(g_settings.m_ResInfo[res].strMode, "PAL60 4:3");
610     break;
611   case PAL60_16x9:
612     g_settings.m_ResInfo[res].iSubtitles = (int)(0.965 * 480);
613     g_settings.m_ResInfo[res].iWidth = 720;
614     g_settings.m_ResInfo[res].iHeight = 480;
615     g_settings.m_ResInfo[res].dwFlags = D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN;
616     g_settings.m_ResInfo[res].fPixelRatio = 4320.0f / 4739.0f*4.0f / 3.0f;
617     strcpy(g_settings.m_ResInfo[res].strMode, "PAL60 16:9");
618     break;
619   default:
620     break;
621   }
622 }
623
624 float CGraphicContext::GetPixelRatio(RESOLUTION iRes) const
625 {
626   return g_settings.m_ResInfo[iRes].fPixelRatio;
627 }
628
629 void CGraphicContext::Clear()
630 {
631   if (!m_pd3dDevice) return;
632   //Not trying to clear the zbuffer when there is none is 7 fps faster (pal resolution)
633   if ((!m_pd3dParams) || (m_pd3dParams->EnableAutoDepthStencil == TRUE))
634     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3D_CLEAR_STENCIL, 0x00010001, 1.0f, 0L );
635   else
636     m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x00010001, 1.0f, 0L );
637 }
638
639 void CGraphicContext::CaptureStateBlock()
640 {
641   if (m_stateBlock != 0xffffffff)
642   {
643     Get3DDevice()->DeleteStateBlock(m_stateBlock);
644   }
645
646   if (D3D_OK != Get3DDevice()->CreateStateBlock(D3DSBT_PIXELSTATE, &m_stateBlock))
647   {
648     // Creation failure
649     m_stateBlock = 0xffffffff;
650   }
651 }
652
653 void CGraphicContext::ApplyStateBlock()
654 {
655   if (m_stateBlock != 0xffffffff)
656   {
657     Get3DDevice()->ApplyStateBlock(m_stateBlock);
658   }
659 }
660
661 void CGraphicContext::SetScalingResolution(RESOLUTION res, float posX, float posY, bool needsScaling)
662 {
663   m_windowResolution = res;
664   if (needsScaling)
665   {
666     // calculate necessary scalings    
667     float fFromWidth;
668     float fFromHeight;
669     float fToPosX;
670     float fToPosY;
671     float fToWidth;
672     float fToHeight;
673     
674     {
675       fFromWidth = (float)g_settings.m_ResInfo[res].iWidth;
676       fFromHeight = (float)g_settings.m_ResInfo[res].iHeight;
677       fToPosX = (float)g_settings.m_ResInfo[m_Resolution].Overscan.left;
678       fToPosY = (float)g_settings.m_ResInfo[m_Resolution].Overscan.top;
679       fToWidth = (float)g_settings.m_ResInfo[m_Resolution].Overscan.right - fToPosX;
680       fToHeight = (float)g_settings.m_ResInfo[m_Resolution].Overscan.bottom - fToPosY;      
681     }
682
683     // add additional zoom to compensate for any overskan built in skin
684     float fZoom = g_SkinInfo.GetSkinZoom();
685
686     if(!g_guiSkinzoom) // lookup gui setting if we didn't have it already
687       g_guiSkinzoom = (CSettingInt*)g_guiSettings.GetSetting("lookandfeel.skinzoom");
688
689     if(g_guiSkinzoom)
690       fZoom *= (100 + g_guiSkinzoom->GetData()) * 0.01f;
691
692     fZoom -= 1.0f;
693     fToPosX -= fToWidth * fZoom * 0.5f;
694     fToWidth *= fZoom + 1.0f;
695
696     // adjust for aspect ratio as zoom is given in the vertical direction and we don't 
697     // do aspect ratio corrections in the gui code 
698     fZoom = fZoom / g_settings.m_ResInfo[m_Resolution].fPixelRatio;
699     fToPosY -= fToHeight * fZoom * 0.5f;
700     fToHeight *= fZoom + 1.0f;
701     
702     m_guiScaleX = fFromWidth / fToWidth;
703     m_guiScaleY = fFromHeight / fToHeight;
704     TransformMatrix windowOffset = TransformMatrix::CreateTranslation(posX, posY);
705     TransformMatrix guiScaler = TransformMatrix::CreateScaler(fToWidth / fFromWidth, fToHeight / fFromHeight, fToHeight / fFromHeight);
706     TransformMatrix guiOffset = TransformMatrix::CreateTranslation(fToPosX, fToPosY);
707     m_guiTransform = guiOffset * guiScaler * windowOffset;
708   }
709   else
710   {
711     m_guiTransform = TransformMatrix::CreateTranslation(posX, posY);
712     m_guiScaleX = 1.0f;
713     m_guiScaleY = 1.0f;
714   }
715   // reset our origin and camera
716   while (m_origins.size())
717     m_origins.pop();
718   m_origins.push(CPoint(posX, posY));
719   while (m_cameras.size())
720     m_cameras.pop();
721   m_cameras.push(CPoint(0.5f*m_iScreenWidth, 0.5f*m_iScreenHeight));
722   UpdateCameraPosition(m_cameras.top());
723
724   // and reset the final transform
725   UpdateFinalTransform(m_guiTransform);
726 }
727
728 void CGraphicContext::UpdateFinalTransform(const TransformMatrix &matrix)
729 {
730   m_finalTransform = matrix;
731   // We could set the world transform here to GPU-ize the animation system.
732   // trouble is that we require the resulting x,y coords to be rounded to
733   // the nearest pixel (vertex shader perhaps?)
734 }
735
736 void CGraphicContext::InvertFinalCoords(float &x, float &y) const
737 {
738   m_finalTransform.InverseTransformPosition(x, y);
739 }
740
741 float CGraphicContext::GetScalingPixelRatio() const
742 {
743   if (m_Resolution == m_windowResolution)
744     return GetPixelRatio(m_windowResolution);
745
746   RESOLUTION checkRes = m_windowResolution;
747   if (checkRes == INVALID)
748     checkRes = m_Resolution;
749   // resolutions are different - we want to return the aspect ratio of the video resolution
750   // but only once it's been corrected for the skin -> screen coordinates scaling
751   float winWidth = (float)g_settings.m_ResInfo[checkRes].iWidth;
752   float winHeight = (float)g_settings.m_ResInfo[checkRes].iHeight;
753   float outWidth = (float)g_settings.m_ResInfo[m_Resolution].iWidth;
754   float outHeight = (float)g_settings.m_ResInfo[m_Resolution].iHeight;
755   float outPR = GetPixelRatio(m_Resolution);
756
757   return outPR * (outWidth / outHeight) / (winWidth / winHeight);
758 }
759
760 void CGraphicContext::SetCameraPosition(const CPoint &camera)
761 {
762   // offset the camera from our current location (this is in XML coordinates) and scale it up to
763   // the screen resolution
764   CPoint cam(camera);
765   if (m_origins.size())
766     cam += m_origins.top();
767
768   RESOLUTION windowRes = (m_windowResolution == INVALID) ? m_Resolution : m_windowResolution;
769   cam.x *= (float)m_iScreenWidth / g_settings.m_ResInfo[windowRes].iWidth;
770   cam.y *= (float)m_iScreenHeight / g_settings.m_ResInfo[windowRes].iHeight;
771
772   m_cameras.push(cam);
773   UpdateCameraPosition(m_cameras.top());
774 }
775
776 void CGraphicContext::RestoreCameraPosition()
777 { // remove the top camera from the stack
778   ASSERT(m_cameras.size());
779   m_cameras.pop();
780   UpdateCameraPosition(m_cameras.top());
781 }
782
783 void CGraphicContext::UpdateCameraPosition(const CPoint &camera)
784 {
785   // NOTE: This routine is currently called (twice) every time there is a <camera>
786   //       tag in the skin.  It actually only has to be called before we render
787   //       something, so another option is to just save the camera coordinates
788   //       and then have a routine called before every draw that checks whether
789   //       the camera has changed, and if so, changes it.  Similarly, it could set
790   //       the world transform at that point as well (or even combine world + view
791   //       to cut down on one setting)
792  
793   // and calculate the offset from the screen center
794   CPoint offset = camera - CPoint(m_iScreenWidth*0.5f, m_iScreenHeight*0.5f);
795
796   // grab the viewport dimensions and location
797   D3DVIEWPORT8 viewport;
798   m_pd3dDevice->GetViewport(&viewport);
799   float w = viewport.Width*0.5f;
800   float h = viewport.Height*0.5f;
801
802   // world view.  Until this is moved onto the GPU (via a vertex shader for instance), we set it to the identity
803   // here.
804   D3DXMATRIX mtxWorld;
805   D3DXMatrixIdentity(&mtxWorld);
806   m_pd3dDevice->SetTransform(D3DTS_WORLD, &mtxWorld);
807
808   // camera view.  Multiply the Y coord by -1 then translate so that everything is relative to the camera
809   // position.
810   D3DXMATRIX flipY, translate, mtxView;
811   D3DXMatrixScaling(&flipY, 1.0f, -1.0f, 1.0f);
812   D3DXMatrixTranslation(&translate, -(viewport.X + w + offset.x), -(viewport.Y + h + offset.y), 2*h);
813   D3DXMatrixMultiply(&mtxView, &translate, &flipY);
814   m_pd3dDevice->SetTransform(D3DTS_VIEW, &mtxView);
815
816   // projection onto screen space
817   D3DXMATRIX mtxProjection;
818   D3DXMatrixPerspectiveOffCenterLH(&mtxProjection, (-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100*h);
819   m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mtxProjection);
820 }
821
822 bool CGraphicContext::RectIsAngled(float x1, float y1, float x2, float y2) const
823 { // need only test 3 points, as they must be co-planer
824   if (m_finalTransform.TransformZCoord(x1, y1, 0)) return true;
825   if (m_finalTransform.TransformZCoord(x2, y2, 0)) return true;
826   if (m_finalTransform.TransformZCoord(x1, y2, 0)) return true;
827   return false;
828 }
829
830 int CGraphicContext::GetFPS() const
831 {
832   if (m_Resolution == PAL_4x3 || m_Resolution == PAL_16x9)
833     return 50;
834   else if (m_Resolution == HDTV_1080i)
835     return 30;
836   return 60;
837 }
838