fixed: Race condition in slideshow could cause double attempt at loading images ...
[xbmc:xbmc-antiquated.git] / xbmc / GUIWindowSlideShow.cpp
1 /*
2  *      Copyright (C) 2005-2007 Team XboxMediaCenter
3  *      http://www.xboxmediacenter.com
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 GNU Make; 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 "stdafx.h"
23 #include "GUIWindowSlideShow.h"
24 #include "Application.h"
25 #include "Picture.h"
26 #include "Util.h"
27 #include "TextureManager.h"
28 #include "GUILabelControl.h"
29 #include "utils/GUIInfoManager.h"
30 #include "FileSystem/FactoryDirectory.h"
31 #include "GUIDialogPictureInfo.h"
32
33 using namespace DIRECTORY;
34
35 #define MAX_RENDER_METHODS 9
36 #define MAX_ZOOM_FACTOR    10
37 #define MAX_PICTURE_WIDTH  4096
38 #define MAX_PICTURE_HEIGHT 4096
39 #define MAX_PICTURE_SIZE 2048*2048
40
41 #define IMMEDIATE_TRANSISTION_TIME 20
42
43 #define PICTURE_MOVE_AMOUNT     0.02f
44 #define PICTURE_MOVE_AMOUNT_ANALOG 0.01f
45 #define PICTURE_VIEW_BOX_COLOR   0xffffff00 // YELLOW
46 #define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK
47
48 #define FPS        25
49
50 #define BAR_IMAGE     1
51 #define LABEL_ROW1    10
52 #define LABEL_ROW2    11
53 #define LABEL_ROW2_EXTRA 12
54 #define CONTROL_PAUSE   13
55
56 static float zoomamount[10] = { 1.0f, 1.2f, 1.5f, 2.0f, 2.8f, 4.0f, 6.0f, 9.0f, 13.5f, 20.0f };
57
58 CBackgroundPicLoader::CBackgroundPicLoader()
59 {
60   m_pCallback = NULL;
61   m_loadPic = CreateEvent(NULL,false,false,NULL);
62   m_isLoading = false;
63 }
64
65 CBackgroundPicLoader::~CBackgroundPicLoader()
66 {}
67
68 void CBackgroundPicLoader::Create(CGUIWindowSlideShow *pCallback)
69 {
70   m_pCallback = pCallback;
71   m_isLoading = false;
72   CThread::Create(false);
73 }
74
75 void CBackgroundPicLoader::Process()
76 {
77   DWORD totalTime = 0;
78   DWORD count = 0;
79   while (!m_bStop)
80   { // loop around forever, waiting for the app to call LoadPic
81     if (WaitForSingleObject(m_loadPic, 10) == WAIT_OBJECT_0)
82     {
83       if (m_pCallback)
84       {
85         CPicture pic;
86         DWORD start = timeGetTime();
87         IDirect3DTexture8 *pTexture = pic.Load(m_strFileName, m_maxWidth, m_maxHeight);
88         totalTime += timeGetTime() - start;
89         count++;
90         // tell our parent
91         bool bFullSize = ((int)pic.GetWidth() < m_maxWidth) && ((int)pic.GetHeight() < m_maxHeight);
92         if (!bFullSize)
93         {
94           int iSize = pic.GetWidth() * pic.GetHeight() - MAX_PICTURE_SIZE;
95           if ((iSize + (int)pic.GetWidth() > 0) || (iSize + (int)pic.GetHeight() > 0))
96             bFullSize = true;
97           if (!bFullSize && pic.GetWidth() == MAX_PICTURE_WIDTH)
98             bFullSize = true;
99           if (!bFullSize && pic.GetHeight() == MAX_PICTURE_HEIGHT)
100             bFullSize = true;
101         }
102         m_pCallback->OnLoadPic(m_iPic, m_iSlideNumber, pTexture, pic.GetWidth(), pic.GetHeight(), pic.GetOriginalWidth(), pic.GetOriginalHeight(), pic.GetExifInfo()->Orientation, bFullSize);
103         m_isLoading = false;
104       }
105     }
106   }
107   CLog::Log(LOGDEBUG, "Time for loading %i images: %i ms, average %i ms", count, totalTime, totalTime / count);
108 }
109
110 void CBackgroundPicLoader::LoadPic(int iPic, int iSlideNumber, const CStdString &strFileName, const int maxWidth, const int maxHeight)
111 {
112   m_iPic = iPic;
113   m_iSlideNumber = iSlideNumber;
114   m_strFileName = strFileName;
115   m_maxWidth = maxWidth;
116   m_maxHeight = maxHeight;
117   m_isLoading = true;
118   SetEvent(m_loadPic);
119 }
120
121 CGUIWindowSlideShow::CGUIWindowSlideShow(void)
122     : CGUIWindow(WINDOW_SLIDESHOW, "Slideshow.xml")
123 {
124   m_pBackgroundLoader = NULL;
125   Reset();
126 }
127
128 CGUIWindowSlideShow::~CGUIWindowSlideShow(void)
129 {
130   Reset();
131 }
132
133
134 bool CGUIWindowSlideShow::IsPlaying() const
135 {
136   return m_Image[m_iCurrentPic].IsLoaded();
137 }
138
139 void CGUIWindowSlideShow::Reset()
140 {
141   g_infoManager.SetShowCodec(false);
142   m_bSlideShow = false;
143   m_bPause = false;
144   m_bErrorMessage = false;
145   m_bReloadImage = false;
146   m_Image[0].UnLoad();
147
148   m_iRotate = 0;
149   m_iZoomFactor = 1;
150   m_iCurrentSlide = 0;
151   m_iNextSlide = 1;
152   m_iCurrentPic = 0;
153   m_slides.Clear();
154 }
155
156 void CGUIWindowSlideShow::FreeResources()
157 { // wait for any outstanding picture loads
158   if (m_pBackgroundLoader)
159   {
160     // sleep until the loader finishes loading the current pic
161     CLog::DebugLog("Waiting for BackgroundLoader thread to close");
162     while (m_pBackgroundLoader->IsLoading())
163       Sleep(10);
164     // stop the thread
165     CLog::DebugLog("Stopping BackgroundLoader thread");
166     m_pBackgroundLoader->StopThread();
167     delete m_pBackgroundLoader;
168     m_pBackgroundLoader = NULL;
169   }
170   // and close the images.
171   m_Image[0].Close();
172   m_Image[1].Close();
173   g_infoManager.ResetCurrentSlide();
174 }
175
176 void CGUIWindowSlideShow::Add(const CFileItem *picture)
177 {
178   m_slides.Add(new CFileItem(*picture));
179 }
180
181 void CGUIWindowSlideShow::ShowNext()
182 {
183   if (m_slides.Size() == 1)
184     return;
185
186   m_iNextSlide = m_iCurrentSlide + 1;
187   if (m_iNextSlide >= m_slides.Size())
188     m_iNextSlide = 0;
189
190   m_bLoadNextPic = true;
191 }
192
193 void CGUIWindowSlideShow::ShowPrevious()
194 {
195   if (m_slides.Size() == 1)
196     return;
197
198   m_iNextSlide = m_iCurrentSlide - 1;
199   if (m_iNextSlide < 0)
200     m_iNextSlide = m_slides.Size() - 1;
201   m_bLoadNextPic = true;
202 }
203
204
205 void CGUIWindowSlideShow::Select(const CStdString& strPicture)
206 {
207   for (int i = 0; i < m_slides.Size(); ++i)
208   {
209     const CFileItem *item = m_slides[i];
210     if (item->m_strPath == strPicture)
211     {
212       m_iCurrentSlide = i;
213       m_iNextSlide = m_iCurrentSlide + 1;
214       if (m_iNextSlide >= m_slides.Size())
215         m_iNextSlide = 0;
216       return ;
217     }
218   }
219 }
220
221 const CFileItemList &CGUIWindowSlideShow::GetSlideShowContents()
222 {
223   return m_slides;
224 }
225
226 const CFileItem *CGUIWindowSlideShow::GetCurrentSlide()
227 {
228   if (m_iCurrentSlide >= 0 && m_iCurrentSlide < m_slides.Size())
229     return m_slides[m_iCurrentSlide];
230   return NULL;
231 }
232
233 bool CGUIWindowSlideShow::InSlideShow() const
234 {
235   return m_bSlideShow;
236 }
237
238 void CGUIWindowSlideShow::StartSlideShow()
239 {
240   m_bSlideShow = true;
241 }
242
243 void CGUIWindowSlideShow::Render()
244 {
245   // reset the screensaver if we're in a slideshow
246   if (m_bSlideShow) g_application.ResetScreenSaver();
247   int iSlides = m_slides.Size();
248   if (!iSlides) return ;
249
250   // Create our background loader if necessary
251   if (!m_pBackgroundLoader)
252   {
253     m_pBackgroundLoader = new CBackgroundPicLoader();
254
255     if (!m_pBackgroundLoader)
256     {
257       throw 1;
258     }
259     m_pBackgroundLoader->Create(this);
260   }
261
262   bool bSlideShow = m_bSlideShow && !m_bPause;
263
264   if (m_bErrorMessage)
265   { // we have an error when loading either the current or next picture
266     // check to see if we have a picture loaded
267     CLog::Log(LOGDEBUG, "We have an error loading a picture!");
268     if (m_Image[m_iCurrentPic].IsLoaded())
269     { // Yes.  Let's let it transistion out, wait for it to be released, then try loading again.
270       CLog::Log(LOGERROR, "Error loading the next image %s", m_slides[m_iNextSlide]->m_strPath.c_str());
271       if (!bSlideShow)
272       { // tell the pic to start transistioning out now
273         m_Image[m_iCurrentPic].StartTransistion();
274         m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
275       }
276       m_bWaitForNextPic = true;
277       m_bErrorMessage = false;
278     }
279     else
280     { // No.  Not much we can do here.  If we're in a slideshow, we mayaswell move on to the next picture
281       // change to next image
282       if (bSlideShow)
283       {
284         CLog::Log(LOGERROR, "Error loading the current image %s", m_slides[m_iCurrentSlide]->m_strPath.c_str());
285         m_iCurrentSlide = m_iNextSlide;
286         ShowNext();
287         m_bErrorMessage = false;
288       }
289       else if (m_bLoadNextPic)
290       {
291         m_iCurrentSlide = m_iNextSlide;
292         m_bErrorMessage = false;
293       }
294       // else just drop through - there's nothing we can do (error message will be displayed)
295     }
296   }
297   if (m_bErrorMessage)
298   {
299     RenderErrorMessage();
300     return ;
301   }
302
303   if (!m_Image[m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading())
304   { // load first image
305     CLog::Log(LOGDEBUG, "Loading the current image %s", m_slides[m_iCurrentSlide]->m_strPath.c_str());
306     m_bWaitForNextPic = false;
307     m_bLoadNextPic = false;
308     // load using the background loader
309     int maxWidth, maxHeight;
310     GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * zoomamount[m_iZoomFactor - 1], 
311                     (float)g_settings.m_ResInfo[m_Resolution].iHeight * zoomamount[m_iZoomFactor - 1],
312                     maxWidth, maxHeight);
313     m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides[m_iCurrentSlide]->m_strPath, maxWidth, maxHeight);
314   }
315
316   // check if we should discard an already loaded next slide
317   if (m_bLoadNextPic && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iNextSlide)
318   {
319     m_Image[1 - m_iCurrentPic].Close();
320   }
321   // if we're reloading an image (for better res on zooming we need to close any open ones as well)
322   if (m_bReloadImage && m_Image[1 - m_iCurrentPic].IsLoaded() && m_Image[1 - m_iCurrentPic].SlideNumber() != m_iCurrentSlide)
323   {
324     m_Image[1 - m_iCurrentPic].Close();
325   }
326
327   if (m_bReloadImage)
328   {
329     if (m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
330     { // reload the image if we need to
331       CLog::Log(LOGDEBUG, "Reloading the current image %s at zoom level %i", m_slides[m_iCurrentSlide]->m_strPath.c_str(), m_iZoomFactor);
332       // first, our maximal size for this zoom level
333       int maxWidth = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * zoomamount[m_iZoomFactor - 1]);
334       int maxHeight = (int)((float)g_settings.m_ResInfo[m_Resolution].iWidth * zoomamount[m_iZoomFactor - 1]);
335
336       // the actual maximal size of the image to optimize the sizing based on the known sizing (aspect ratio)
337       int width, height;
338       GetCheckedSize((float)m_Image[m_iCurrentPic].GetOriginalWidth(), (float)m_Image[m_iCurrentPic].GetOriginalHeight(), width, height);
339
340       // use the smaller of the two (no point zooming in more than we have to)
341       if (maxWidth < width) width = maxWidth;
342       if (maxHeight < height) height = maxHeight;
343
344       m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, m_slides[m_iCurrentSlide]->m_strPath, maxWidth, maxHeight);
345     }
346   }
347   else
348   {
349     if ((bSlideShow || m_bLoadNextPic) && m_Image[m_iCurrentPic].IsLoaded() && !m_Image[1 - m_iCurrentPic].IsLoaded() && !m_pBackgroundLoader->IsLoading() && !m_bWaitForNextPic)
350     { // load the next image
351       CLog::Log(LOGDEBUG, "Loading the next image %s", m_slides[m_iNextSlide]->m_strPath.c_str());
352       int maxWidth, maxHeight;
353       GetCheckedSize((float)g_settings.m_ResInfo[m_Resolution].iWidth * zoomamount[m_iZoomFactor - 1], 
354                      (float)g_settings.m_ResInfo[m_Resolution].iHeight * zoomamount[m_iZoomFactor - 1],
355                      maxWidth, maxHeight);
356       m_pBackgroundLoader->LoadPic(1 - m_iCurrentPic, m_iNextSlide, m_slides[m_iNextSlide]->m_strPath, maxWidth, maxHeight);
357     }
358   }
359
360   // render the current image
361   if (m_Image[m_iCurrentPic].IsLoaded())
362   {
363     m_Image[m_iCurrentPic].Pause(m_bPause);
364     m_Image[m_iCurrentPic].Render();
365   }
366
367   // Check if we should be transistioning immediately
368   if (m_bLoadNextPic)
369   {
370     CLog::Log(LOGDEBUG, "Starting immediate transistion due to user wanting slide %s", m_slides[m_iNextSlide]->m_strPath.c_str());
371     if (m_Image[m_iCurrentPic].StartTransistion())
372     {
373       m_Image[m_iCurrentPic].SetTransistionTime(1, IMMEDIATE_TRANSISTION_TIME); // only 20 frames for the transistion
374       m_bLoadNextPic = false;
375     }
376   }
377
378   // render the next image
379   if (m_Image[m_iCurrentPic].DrawNextImage())
380   {
381     if (m_Image[1 - m_iCurrentPic].IsLoaded())
382     {
383       // set the appropriate transistion time
384       m_Image[1 - m_iCurrentPic].SetTransistionTime(0, m_Image[m_iCurrentPic].GetTransistionTime(1));
385       m_Image[1 - m_iCurrentPic].Pause(m_bPause);
386       m_Image[1 - m_iCurrentPic].Render();
387     }
388     else // next pic isn't loaded.  We should hang around if it is in progress
389     {
390       if (m_pBackgroundLoader->IsLoading())
391       {
392 //        CLog::Log(LOGDEBUG, "Having to hold the current image (%s) while we load %s", m_vecSlides[m_iCurrentSlide].c_str(), m_vecSlides[m_iNextSlide].c_str());
393         m_Image[m_iCurrentPic].Keep();
394       }
395     }
396   }
397
398   // check if we should swap images now
399   if (m_Image[m_iCurrentPic].IsFinished())
400   {
401     CLog::Log(LOGDEBUG, "Image %s is finished rendering, switching to %s", m_slides[m_iCurrentSlide]->m_strPath.c_str(), m_slides[m_iNextSlide]->m_strPath.c_str());
402     m_Image[m_iCurrentPic].Close();
403     if (m_Image[1 - m_iCurrentPic].IsLoaded())
404       m_iCurrentPic = 1 - m_iCurrentPic;
405     m_iCurrentSlide = m_iNextSlide;
406     if (bSlideShow)
407     {
408       m_iNextSlide++;
409       if (m_iNextSlide >= m_slides.Size())
410         m_iNextSlide = 0;
411     }
412 //    m_iZoomFactor = 1;
413     m_iRotate = 0;
414   }
415
416   RenderPause();
417
418   if (m_Image[m_iCurrentPic].IsLoaded())
419     g_infoManager.SetCurrentSlide(*m_slides[m_iCurrentSlide]);
420
421   RenderErrorMessage();
422
423   CGUIWindow::Render();
424 }
425
426 bool CGUIWindowSlideShow::OnAction(const CAction &action)
427 {
428   switch (action.wID)
429   {
430   case ACTION_SHOW_CODEC:
431     {
432       CGUIDialogPictureInfo *pictureInfo = (CGUIDialogPictureInfo *)m_gWindowManager.GetWindow(WINDOW_DIALOG_PICTURE_INFO);
433       if (pictureInfo)
434       {
435         // no need to set the picture here, it's done in Render()
436         pictureInfo->DoModal();
437       }
438     }
439     break;
440   case ACTION_PREVIOUS_MENU:
441   case ACTION_STOP:
442     m_gWindowManager.PreviousWindow();
443     break;
444   case ACTION_NEXT_PICTURE:
445 //    if (m_iZoomFactor == 1)
446       ShowNext();
447     break;
448   case ACTION_PREV_PICTURE:
449 //    if (m_iZoomFactor == 1)
450       ShowPrevious();
451     break;
452   case ACTION_MOVE_RIGHT:
453     if (m_iZoomFactor == 1)
454       ShowNext();
455     else
456       Move(PICTURE_MOVE_AMOUNT, 0);
457     break;
458
459   case ACTION_MOVE_LEFT:
460     if (m_iZoomFactor == 1)
461       ShowPrevious();
462     else
463       Move( -PICTURE_MOVE_AMOUNT, 0);
464     break;
465
466   case ACTION_MOVE_DOWN:
467     Move(0, PICTURE_MOVE_AMOUNT);
468     break;
469
470   case ACTION_MOVE_UP:
471     Move(0, -PICTURE_MOVE_AMOUNT);
472     break;
473
474   case ACTION_PAUSE:
475     if (m_bSlideShow)
476       m_bPause = !m_bPause;
477     break;
478
479   case ACTION_ZOOM_OUT:
480     Zoom(m_iZoomFactor - 1);
481     break;
482
483   case ACTION_ZOOM_IN:
484     Zoom(m_iZoomFactor + 1);
485     break;
486
487   case ACTION_ROTATE_PICTURE:
488     Rotate();
489     break;
490
491   case ACTION_ZOOM_LEVEL_NORMAL:
492   case ACTION_ZOOM_LEVEL_1:
493   case ACTION_ZOOM_LEVEL_2:
494   case ACTION_ZOOM_LEVEL_3:
495   case ACTION_ZOOM_LEVEL_4:
496   case ACTION_ZOOM_LEVEL_5:
497   case ACTION_ZOOM_LEVEL_6:
498   case ACTION_ZOOM_LEVEL_7:
499   case ACTION_ZOOM_LEVEL_8:
500   case ACTION_ZOOM_LEVEL_9:
501     Zoom((action.wID - ACTION_ZOOM_LEVEL_NORMAL) + 1);
502     break;
503   case ACTION_ANALOG_MOVE:
504     Move(action.fAmount1*PICTURE_MOVE_AMOUNT_ANALOG, -action.fAmount2*PICTURE_MOVE_AMOUNT_ANALOG);
505     break;
506   default:
507     return CGUIWindow::OnAction(action);
508   }
509   return true;
510 }
511
512 void CGUIWindowSlideShow::RenderErrorMessage()
513 {
514   if (!m_bErrorMessage)
515     return ;
516   CGUIFont *pFont = ((CGUILabelControl *)GetControl(LABEL_ROW1))->GetLabelInfo().font;
517   if (pFont)
518   {
519     CStdStringW utf16ErrorMessage;
520     g_charsetConverter.utf8ToUTF16(g_localizeStrings.Get(747), utf16ErrorMessage);
521     pFont->DrawText((float)g_graphicsContext.GetWidth() / 2, (float)g_graphicsContext.GetHeight() / 2, 0xffffffff, 0, utf16ErrorMessage.c_str(), XBFONT_CENTER_X | XBFONT_CENTER_Y);
522   }
523 }
524
525 bool CGUIWindowSlideShow::OnMessage(CGUIMessage& message)
526 {
527   switch ( message.GetMessage() )
528   {
529   case GUI_MSG_WINDOW_DEINIT:
530     {
531       if (m_Resolution != g_guiSettings.m_LookAndFeelResolution)
532       {
533         g_graphicsContext.SetVideoResolution(g_guiSettings.m_LookAndFeelResolution, TRUE);
534       }
535
536       //   Reset();
537       if (message.GetParam1() != WINDOW_PICTURES)
538       {
539         m_ImageLib.Unload();
540       }
541       m_gWindowManager.ShowOverlay(OVERLAY_STATE_SHOWN);
542       // set screen filters to video filters so that we
543       // get sharper images
544       g_graphicsContext.SetScreenFilters(false);
545       FreeResources();
546     }
547     break;
548
549   case GUI_MSG_WINDOW_INIT:
550     {
551       m_Resolution = (RESOLUTION) g_guiSettings.GetInt("pictures.displayresolution");
552       if (m_Resolution != g_guiSettings.m_LookAndFeelResolution && m_Resolution != INVALID)
553       {
554         g_graphicsContext.SetVideoResolution(m_Resolution, TRUE);
555       }
556
557       CGUIWindow::OnMessage(message);
558       if (g_application.IsPlayingVideo())
559         g_application.StopPlaying();
560       // clear as much memory as possible
561       g_TextureManager.Flush();
562       if (message.GetParam1() != WINDOW_PICTURES)
563       {
564         m_ImageLib.Load();
565       }
566       m_gWindowManager.ShowOverlay(OVERLAY_STATE_HIDDEN);
567       // set screen filters to video filters so that we
568       // get sharper images
569       g_graphicsContext.SetScreenFilters(true);
570
571       // turn off slideshow if we only have 1 image
572       if (m_slides.Size() <= 1)
573         m_bSlideShow = false;
574
575       return true;
576     }
577     break;
578   case GUI_MSG_START_SLIDESHOW:
579     {
580       CStdString strFolder = message.GetStringParam();
581       bool bRecursive = message.GetParam1() != 0;
582         RunSlideShow(strFolder, bRecursive);
583     }
584     break;
585   }
586   return CGUIWindow::OnMessage(message);
587 }
588
589 void CGUIWindowSlideShow::RenderPause()
590 { // display the pause icon
591   if (m_bPause)
592   {
593     SET_CONTROL_VISIBLE(CONTROL_PAUSE);
594   }
595   else
596   {
597     SET_CONTROL_HIDDEN(CONTROL_PAUSE);
598   }
599   /*
600    static DWORD dwCounter=0;
601    dwCounter++;
602    if (dwCounter > 25)
603    {
604     dwCounter=0;
605    }
606    if (!m_bPause) return;
607    if (dwCounter <13) return;*/
608
609 }
610
611 void CGUIWindowSlideShow::Rotate()
612 {
613   if (!m_Image[m_iCurrentPic].DrawNextImage() && m_iZoomFactor == 1)
614   {
615     m_Image[m_iCurrentPic].Rotate(++m_iRotate);
616   }
617 }
618
619 void CGUIWindowSlideShow::Zoom(int iZoom)
620 {
621   if (iZoom > MAX_ZOOM_FACTOR || iZoom < 1)
622     return ;
623   // set the zoom amount and then set so that the image is reloaded at the higher (or lower)
624   // resolution as necessary
625   if (!m_Image[m_iCurrentPic].DrawNextImage())
626   {
627     m_Image[m_iCurrentPic].Zoom(iZoom);
628     // check if we need to reload the image for better resolution
629     if (iZoom > m_iZoomFactor && !m_Image[m_iCurrentPic].FullSize())
630       m_bReloadImage = true;
631     if (iZoom == 1)
632       m_bReloadImage = true;
633     m_iZoomFactor = iZoom;
634   }
635 }
636
637 void CGUIWindowSlideShow::Move(float fX, float fY)
638 {
639   if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].GetZoom() > 1)
640   { // we move in the opposite direction, due to the fact we are moving
641     // the viewing window, not the picture.
642     m_Image[m_iCurrentPic].Move( -fX, -fY);
643   }
644 }
645
646 void CGUIWindowSlideShow::OnLoadPic(int iPic, int iSlideNumber, LPDIRECT3DTEXTURE8 pTexture, int iWidth, int iHeight, int iOriginalWidth, int iOriginalHeight, int iRotate, bool bFullSize)
647 {
648   if (!g_guiSettings.GetBool("pictures.useexifrotation"))
649     iRotate = 1;
650   if (pTexture)
651   {
652     // set the pic's texture + size etc.
653     CLog::Log(LOGDEBUG, "Finished background loading %s", m_slides[iSlideNumber]->m_strPath.c_str());
654     if (m_bReloadImage)
655     {
656       if (m_Image[m_iCurrentPic].IsLoaded() && m_Image[m_iCurrentPic].SlideNumber() != iSlideNumber)
657       { // wrong image (ie we finished loading the next image, not the current image)
658         pTexture->Release();
659         return;
660       }
661       m_Image[m_iCurrentPic].UpdateTexture(pTexture, iWidth, iHeight);
662       m_Image[m_iCurrentPic].SetOriginalSize(iOriginalWidth, iOriginalHeight, bFullSize);
663       m_bReloadImage = false;
664     }
665     else
666     {
667       if (m_bSlideShow)
668         m_Image[iPic].SetTexture(iSlideNumber, pTexture, iWidth, iHeight, iRotate, g_guiSettings.GetBool("slideshow.displayeffects") ? CSlideShowPic::EFFECT_RANDOM : CSlideShowPic::EFFECT_NONE);
669       else
670         m_Image[iPic].SetTexture(iSlideNumber, pTexture, iWidth, iHeight, iRotate, CSlideShowPic::EFFECT_NO_TIMEOUT);
671       m_Image[iPic].SetOriginalSize(iOriginalWidth, iOriginalHeight, bFullSize);
672       m_Image[iPic].Zoom(m_iZoomFactor, true);
673
674       m_Image[iPic].m_bIsComic = false;
675       if (CUtil::IsInRAR(m_slides[m_iCurrentSlide]->m_strPath) || CUtil::IsInZIP(m_slides[m_iCurrentSlide]->m_strPath)) // move to top for cbr/cbz
676       {
677         CURL url(m_slides[m_iCurrentSlide]->m_strPath);
678         CStdString strHostName = url.GetHostName();
679         if (CUtil::GetExtension(strHostName).Equals(".cbr", false) || CUtil::GetExtension(strHostName).Equals(".cbz", false))
680         {
681           m_Image[iPic].m_bIsComic = true;
682           m_Image[iPic].Move((float)m_Image[iPic].GetOriginalWidth(),(float)m_Image[iPic].GetOriginalHeight());
683         }
684       }
685     }
686   }
687   else
688   { // Failed to load image.  What should be done??
689     // We should wait for the current pic to finish rendering, then transistion it out,
690     // release the texture, and try and reload this pic from scratch
691     m_bErrorMessage = true;
692   }
693 }
694
695 void CGUIWindowSlideShow::Shuffle()
696 {
697   m_slides.Randomize();
698   m_iCurrentSlide = 0;
699   m_iNextSlide = 1;
700 }
701
702 int CGUIWindowSlideShow::NumSlides() const
703 {
704   return m_slides.Size();
705 }
706
707 int CGUIWindowSlideShow::CurrentSlide() const
708 {
709   return m_iCurrentSlide + 1;
710 }
711
712 void CGUIWindowSlideShow::RunSlideShow(const CStdString &strPath, bool bRecursive)
713 {
714   // stop any video
715   if (g_application.IsPlayingVideo())
716     g_application.StopPlaying();
717   if (strPath!="")
718   {
719     // reset the slideshow
720     Reset();
721     AddItems(strPath, bRecursive);
722     // ok, now run the slideshow
723   }
724   if (g_guiSettings.GetBool("slideshow.shuffle"))
725     Shuffle();
726
727   StartSlideShow();
728   if (NumSlides())
729     m_gWindowManager.ActivateWindow(WINDOW_SLIDESHOW);
730 }
731
732 void CGUIWindowSlideShow::AddItems(const CStdString &strPath, bool bRecursive)
733 {
734   // read the directory in
735   IDirectory *pDir = CFactoryDirectory::Create(strPath);
736   if (!pDir) return;
737   CFileItemList items;
738   pDir->SetMask(g_stSettings.m_pictureExtensions);
739   bool bResult = pDir->GetDirectory(strPath, items);
740   delete pDir;
741   if (!bResult) return;
742   // now sort it as necessary
743   items.Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC);
744   // need to go into all subdirs
745   for (int i = 0; i < items.Size(); i++)
746   {
747     if (items[i]->m_bIsFolder && bRecursive)
748     {
749       AddItems(items[i]->m_strPath, bRecursive);
750     }
751     else
752     { // add to the slideshow
753       Add(items[i]);
754     }
755   }
756 }
757
758 void CGUIWindowSlideShow::GetCheckedSize(float width, float height, int &maxWidth, int &maxHeight)
759 {
760   if (width * height > MAX_PICTURE_SIZE)
761   {
762     float fScale = sqrt((float)MAX_PICTURE_SIZE / (width * height));
763     width = fScale * width;
764     height = fScale * height;
765   }
766   maxWidth = (int)width;
767   maxHeight = (int)height;
768   if (maxWidth > MAX_PICTURE_WIDTH) maxWidth = MAX_PICTURE_WIDTH;
769   if (maxHeight > MAX_PICTURE_HEIGHT) maxHeight = MAX_PICTURE_HEIGHT;
770 }