[XAudio2] avoid leak + fix voice creation for closest match
[xbmc.git] / xbmc / pictures / GUIWindowSlideShow.cpp
blobfbe77a374fdc9dd27bb35348f6820fb603fd708a
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIWindowSlideShow.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIDialogPictureInfo.h"
14 #include "GUIInfoManager.h"
15 #include "GUIUserMessages.h"
16 #include "ServiceBroker.h"
17 #include "URL.h"
18 #include "application/Application.h"
19 #include "application/ApplicationComponents.h"
20 #include "application/ApplicationPlayer.h"
21 #include "application/ApplicationPowerHandling.h"
22 #include "filesystem/Directory.h"
23 #include "guilib/GUIComponent.h"
24 #include "guilib/GUILabelControl.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "guilib/Texture.h"
28 #include "imagefiles/ImageFileURL.h"
29 #include "input/actions/Action.h"
30 #include "input/actions/ActionIDs.h"
31 #include "input/mouse/MouseEvent.h"
32 #include "interfaces/AnnouncementManager.h"
33 #include "pictures/GUIViewStatePictures.h"
34 #include "pictures/PictureThumbLoader.h"
35 #include "pictures/SlideShowDelegator.h"
36 #include "playlists/PlayListTypes.h"
37 #include "rendering/RenderSystem.h"
38 #include "settings/DisplaySettings.h"
39 #include "settings/Settings.h"
40 #include "settings/SettingsComponent.h"
41 #include "utils/Random.h"
42 #include "utils/URIUtils.h"
43 #include "utils/Variant.h"
44 #include "utils/XTimeUtils.h"
45 #include "utils/log.h"
46 #include "video/VideoFileItemClassify.h"
48 #include <memory>
49 #include <random>
51 using namespace KODI;
52 using namespace KODI::VIDEO;
53 using namespace MESSAGING;
54 using namespace XFILE;
55 using namespace std::chrono_literals;
57 #define MAX_ZOOM_FACTOR 10
58 #define MAX_PICTURE_SIZE 2048*2048
60 #define IMMEDIATE_TRANSITION_TIME 1
62 #define PICTURE_MOVE_AMOUNT 0.02f
63 #define PICTURE_MOVE_AMOUNT_ANALOG 0.01f
64 #define PICTURE_MOVE_AMOUNT_TOUCH 0.002f
65 #define PICTURE_VIEW_BOX_COLOR 0xffffff00 // YELLOW
66 #define PICTURE_VIEW_BOX_BACKGROUND 0xff000000 // BLACK
68 #define ROTATION_SNAP_RANGE 10.0f
70 #define LABEL_ROW1 10
71 #define CONTROL_PAUSE 13
73 static float zoomamount[10] = { 1.0f, 1.2f, 1.5f, 2.0f, 2.8f, 4.0f, 6.0f, 9.0f, 13.5f, 20.0f };
75 CBackgroundPicLoader::CBackgroundPicLoader() : CThread("BgPicLoader")
79 CBackgroundPicLoader::~CBackgroundPicLoader()
81 StopThread();
84 void CBackgroundPicLoader::Create(CGUIWindowSlideShow *pCallback)
86 m_pCallback = pCallback;
87 m_isLoading = false;
88 CThread::Create(false);
91 void CBackgroundPicLoader::Process()
93 auto totalTime = std::chrono::milliseconds(0);
94 unsigned int count = 0;
95 while (!m_bStop)
96 { // loop around forever, waiting for the app to call LoadPic
97 if (AbortableWait(m_loadPic, 10ms) == WAIT_SIGNALED)
99 if (m_pCallback)
101 auto start = std::chrono::steady_clock::now();
102 std::unique_ptr<CTexture> texture =
103 CTexture::LoadFromFile(m_strFileName, m_maxWidth, m_maxHeight);
105 auto end = std::chrono::steady_clock::now();
106 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
108 totalTime += duration;
109 count++;
110 // tell our parent
111 bool bFullSize = false;
112 if (texture)
114 bFullSize = ((int)texture->GetWidth() < m_maxWidth) && ((int)texture->GetHeight() < m_maxHeight);
115 if (!bFullSize)
117 int iSize = texture->GetWidth() * texture->GetHeight() - MAX_PICTURE_SIZE;
118 if ((iSize + (int)texture->GetWidth() > 0) || (iSize + (int)texture->GetHeight() > 0))
119 bFullSize = true;
120 if (!bFullSize && texture->GetWidth() == CServiceBroker::GetRenderSystem()->GetMaxTextureSize())
121 bFullSize = true;
122 if (!bFullSize && texture->GetHeight() == CServiceBroker::GetRenderSystem()->GetMaxTextureSize())
123 bFullSize = true;
126 m_pCallback->OnLoadPic(m_iPic, m_iSlideNumber, m_strFileName, std::move(texture),
127 bFullSize);
128 m_isLoading = false;
132 if (count > 0)
133 CLog::Log(LOGDEBUG, "Time for loading {} images: {} ms, average {} ms", count,
134 totalTime.count(), totalTime.count() / count);
137 void CBackgroundPicLoader::LoadPic(int iPic, int iSlideNumber, const std::string &strFileName, const int maxWidth, const int maxHeight)
139 m_iPic = iPic;
140 m_iSlideNumber = iSlideNumber;
141 m_strFileName = strFileName;
142 m_maxWidth = maxWidth;
143 m_maxHeight = maxHeight;
144 m_isLoading = true;
145 m_loadPic.Set();
148 CGUIWindowSlideShow::CGUIWindowSlideShow(void)
149 : CGUIDialog(WINDOW_SLIDESHOW, "SlideShow.xml")
151 m_Resolution = RES_INVALID;
152 m_loadType = KEEP_IN_MEMORY;
153 m_bLoadNextPic = false;
154 CServiceBroker::GetSlideShowDelegator().SetDelegate(this);
155 CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this);
156 Reset();
159 CGUIWindowSlideShow::~CGUIWindowSlideShow()
161 CServiceBroker::GetSlideShowDelegator().ResetDelegate();
162 CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this);
165 void CGUIWindowSlideShow::Announce(ANNOUNCEMENT::AnnouncementFlag flag,
166 const std::string& sender,
167 const std::string& message,
168 const CVariant& data)
170 if (flag & ANNOUNCEMENT::Player)
172 if (message == "OnPlay" || message == "OnResume")
174 if (data.isMember("player") && data["player"].isMember("playerid") &&
175 data["player"]["playerid"] == static_cast<int>(PLAYLIST::Id::TYPE_VIDEO))
176 Close();
181 void CGUIWindowSlideShow::AnnouncePlayerPlay(const CFileItemPtr& item)
183 CVariant param;
184 param["player"]["speed"] = m_bSlideShow && !m_bPause ? 1 : 0;
185 param["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
186 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPlay", item, param);
189 void CGUIWindowSlideShow::AnnouncePlayerPause(const CFileItemPtr& item)
191 CVariant param;
192 param["player"]["speed"] = 0;
193 param["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
194 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPause", item, param);
197 void CGUIWindowSlideShow::AnnouncePlayerStop(const CFileItemPtr& item)
199 CVariant param;
200 param["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
201 param["end"] = true;
202 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnStop", item, param);
205 void CGUIWindowSlideShow::AnnouncePlaylistClear()
207 CVariant data;
208 data["playlistid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
209 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist, "OnClear", data);
212 void CGUIWindowSlideShow::AnnouncePlaylistAdd(const CFileItemPtr& item, int pos)
214 CVariant data;
215 data["playlistid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
216 data["position"] = pos;
217 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist, "OnAdd", item, data);
220 void CGUIWindowSlideShow::AnnouncePropertyChanged(const std::string &strProperty, const CVariant &value)
222 if (strProperty.empty() || value.isNull())
223 return;
225 CVariant data;
226 data["player"]["playerid"] = static_cast<int>(PLAYLIST::Id::TYPE_PICTURE);
227 data["property"][strProperty] = value;
228 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Player, "OnPropertyChanged",
229 data);
232 bool CGUIWindowSlideShow::IsPlaying() const
234 return m_Image[m_iCurrentPic]->IsLoaded();
237 void CGUIWindowSlideShow::Reset()
239 m_bSlideShow = false;
240 m_bShuffled = false;
241 m_bPause = false;
242 m_bPlayingVideo = false;
243 m_bErrorMessage = false;
245 if (m_Image[0])
247 m_Image[0]->UnLoad();
248 m_Image[0]->Close();
251 m_Image[0] = CSlideShowPic::CreateSlideShowPicture();
253 if (m_Image[1])
255 m_Image[1]->UnLoad();
256 m_Image[1]->Close();
259 m_Image[1] = CSlideShowPic::CreateSlideShowPicture();
261 m_fRotate = 0.0f;
262 m_fInitialRotate = 0.0f;
263 m_iZoomFactor = 1;
264 m_fZoom = 1.0f;
265 m_fInitialZoom = 0.0f;
266 m_iCurrentSlide = 0;
267 m_iNextSlide = 1;
268 m_iCurrentPic = 0;
269 m_iDirection = 1;
270 m_iLastFailedNextSlide = -1;
271 m_slides.clear();
272 AnnouncePlaylistClear();
273 m_Resolution = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
276 void CGUIWindowSlideShow::OnDeinitWindow(int nextWindowID)
278 if (m_Resolution != CDisplaySettings::GetInstance().GetCurrentResolution())
280 //FIXME: Use GUI resolution for now
281 //CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(CDisplaySettings::GetInstance().GetCurrentResolution(), true);
284 if (nextWindowID != WINDOW_FULLSCREEN_VIDEO &&
285 nextWindowID != WINDOW_FULLSCREEN_GAME)
287 // wait for any outstanding picture loads
288 if (m_pBackgroundLoader)
290 // sleep until the loader finishes loading the current pic
291 CLog::Log(LOGDEBUG,"Waiting for BackgroundLoader thread to close");
292 while (m_pBackgroundLoader->IsLoading())
293 KODI::TIME::Sleep(10ms);
294 // stop the thread
295 CLog::Log(LOGDEBUG,"Stopping BackgroundLoader thread");
296 m_pBackgroundLoader->StopThread();
297 m_pBackgroundLoader.reset();
299 // and close the images.
300 m_Image[0]->Close();
301 m_Image[1]->Close();
303 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPicturesInfoProvider().SetCurrentSlide(nullptr);
304 m_bSlideShow = false;
306 CGUIDialog::OnDeinitWindow(nextWindowID);
309 void CGUIWindowSlideShow::Add(const CFileItem *picture)
311 CFileItemPtr item(new CFileItem(*picture));
312 if (!item->HasVideoInfoTag() && !item->HasPictureInfoTag())
314 // item without tag; get mimetype then we can tell whether it's video item
315 item->FillInMimeType();
317 if (!IsVideo(*item))
318 // then it is a picture and force tag generation
319 item->GetPictureInfoTag();
321 AnnouncePlaylistAdd(item, m_slides.size());
323 m_slides.emplace_back(std::move(item));
326 void CGUIWindowSlideShow::ShowNext()
328 if (m_slides.size() == 1)
329 return;
331 m_iDirection = 1;
332 m_iNextSlide = GetNextSlide();
333 m_iZoomFactor = 1;
334 m_fZoom = 1.0f;
335 m_fRotate = 0.0f;
336 m_bLoadNextPic = true;
339 void CGUIWindowSlideShow::ShowPrevious()
341 if (m_slides.size() == 1)
342 return;
344 m_iDirection = -1;
345 m_iNextSlide = GetNextSlide();
346 m_iZoomFactor = 1;
347 m_fZoom = 1.0f;
348 m_fRotate = 0.0f;
349 m_bLoadNextPic = true;
352 void CGUIWindowSlideShow::Select(const std::string& strPicture)
354 for (size_t i = 0; i < m_slides.size(); ++i)
356 const CFileItemPtr item = m_slides.at(i);
357 if (item->GetPath() == strPicture)
359 m_iDirection = 1;
360 if (!m_Image[m_iCurrentPic]->IsLoaded() &&
361 (!m_pBackgroundLoader || !m_pBackgroundLoader->IsLoading()))
363 // will trigger loading current slide when next Process call.
364 m_iCurrentSlide = i;
365 m_iNextSlide = GetNextSlide();
367 else
369 m_iNextSlide = i;
370 m_bLoadNextPic = true;
372 return ;
377 void CGUIWindowSlideShow::GetSlideShowContents(CFileItemList &list)
379 for (size_t index = 0; index < m_slides.size(); index++)
380 list.Add(std::make_shared<CFileItem>(*m_slides.at(index)));
383 std::shared_ptr<const CFileItem> CGUIWindowSlideShow::GetCurrentSlide()
385 if (m_iCurrentSlide >= 0 && m_iCurrentSlide < static_cast<int>(m_slides.size()))
386 return m_slides.at(m_iCurrentSlide);
387 return CFileItemPtr();
390 bool CGUIWindowSlideShow::InSlideShow() const
392 return m_bSlideShow;
395 void CGUIWindowSlideShow::StartSlideShow()
397 m_bSlideShow = true;
398 m_iDirection = 1;
399 if (m_slides.size())
400 AnnouncePlayerPlay(m_slides.at(m_iCurrentSlide));
403 void CGUIWindowSlideShow::SetDirection(int direction)
405 direction = direction >= 0 ? 1 : -1;
406 if (m_iDirection != direction)
408 m_iDirection = direction;
409 m_iNextSlide = GetNextSlide();
413 void CGUIWindowSlideShow::Process(unsigned int currentTime, CDirtyRegionList &regions)
415 const RESOLUTION_INFO res = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
417 // reset the screensaver if we're in a slideshow
418 // (unless we are the screensaver!)
419 auto& components = CServiceBroker::GetAppComponents();
420 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
421 if (m_bSlideShow && !m_bPause && !appPower->IsInScreenSaver())
422 appPower->ResetScreenSaver();
423 int iSlides = m_slides.size();
424 if (!iSlides)
425 return;
427 // if we haven't processed yet, we should mark the whole screen
428 if (!HasProcessed())
430 regions.emplace_back(CRect(
431 0.0f, 0.0f, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
432 static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
433 MarkDirtyRegion();
436 if (m_iCurrentSlide < 0 || m_iCurrentSlide >= static_cast<int>(m_slides.size()))
437 m_iCurrentSlide = 0;
438 if (m_iNextSlide < 0 || m_iNextSlide >= static_cast<int>(m_slides.size()))
439 m_iNextSlide = GetNextSlide();
441 // Create our background loader if necessary
442 if (!m_pBackgroundLoader)
444 m_pBackgroundLoader = std::make_unique<CBackgroundPicLoader>();
445 m_pBackgroundLoader->Create(this);
448 bool bSlideShow = m_bSlideShow && !m_bPause && !m_bPlayingVideo;
449 if (bSlideShow && m_slides.at(m_iCurrentSlide)->HasProperty("unplayable"))
451 m_iNextSlide = GetNextSlide();
452 if (m_iCurrentSlide == m_iNextSlide)
453 return;
454 m_iCurrentSlide = m_iNextSlide;
455 m_iNextSlide = GetNextSlide();
458 if (m_bErrorMessage)
459 { // we have an error when loading either the current or next picture
460 // check to see if we have a picture loaded
461 CLog::Log(LOGDEBUG, "We have an error loading picture {}!", m_pBackgroundLoader->SlideNumber());
462 if (m_iCurrentSlide == m_pBackgroundLoader->SlideNumber())
464 if (m_Image[m_iCurrentPic]->IsLoaded())
466 // current image was already loaded, so we can ignore this error.
467 m_bErrorMessage = false;
469 else
471 CLog::Log(LOGERROR, "Error loading the current image {}: {}", m_iCurrentSlide,
472 m_slides.at(m_iCurrentSlide)->GetPath());
473 if (!IsVideo(*m_slides.at(m_iCurrentPic)))
475 // try next if we are in slideshow
476 CLog::Log(LOGINFO, "set image {} unplayable", m_slides.at(m_iCurrentSlide)->GetPath());
477 m_slides.at(m_iCurrentSlide)->SetProperty("unplayable", true);
479 if (m_bLoadNextPic || (bSlideShow && !m_bPause && !IsVideo(*m_slides.at(m_iCurrentPic))))
481 // change to next item, wait loading.
482 m_iCurrentSlide = m_iNextSlide;
483 m_iNextSlide = GetNextSlide();
484 m_bErrorMessage = false;
486 // else just drop through - there's nothing we can do (error message will be displayed)
489 else if (m_iNextSlide == m_pBackgroundLoader->SlideNumber())
491 CLog::Log(LOGERROR, "Error loading the next image {}: {}", m_iNextSlide,
492 m_slides.at(m_iNextSlide)->GetPath());
493 // load next image failed, then skip to load next of next if next is not video.
494 if (!IsVideo(*m_slides.at(m_iNextSlide)))
496 CLog::Log(LOGINFO, "set image {} unplayable", m_slides.at(m_iNextSlide)->GetPath());
497 m_slides.at(m_iNextSlide)->SetProperty("unplayable", true);
498 // change to next item, wait loading.
499 m_iNextSlide = GetNextSlide();
501 else
502 { // prevent reload the next pic and repeat fail.
503 m_iLastFailedNextSlide = m_iNextSlide;
505 m_bErrorMessage = false;
507 else
508 { // Non-current and non-next slide, just ignore error.
509 CLog::Log(LOGERROR, "Error loading the non-current non-next image {}/{}: {}", m_iNextSlide,
510 m_pBackgroundLoader->SlideNumber(), m_slides.at(m_iNextSlide)->GetPath());
511 m_bErrorMessage = false;
515 if (m_bErrorMessage)
516 { // hack, just mark it all
517 regions.emplace_back(CRect(
518 0.0f, 0.0f, static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()),
519 static_cast<float>(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight())));
520 MarkDirtyRegion();
521 return;
524 if (!m_Image[m_iCurrentPic]->IsLoaded() && !m_pBackgroundLoader->IsLoading())
525 { // load first image
526 CFileItemPtr item = m_slides.at(m_iCurrentSlide);
527 std::string picturePath = GetPicturePath(item.get());
528 if (!picturePath.empty())
530 if (IsVideo(*item))
531 CLog::Log(LOGDEBUG, "Loading the thumb {} for current video {}: {}", picturePath,
532 m_iCurrentSlide, item->GetPath());
533 else
534 CLog::Log(LOGDEBUG, "Loading the current image {}: {}", m_iCurrentSlide, item->GetPath());
536 // load using the background loader
537 int maxWidth, maxHeight;
539 GetCheckedSize((float)res.iWidth * m_fZoom,
540 (float)res.iHeight * m_fZoom,
541 maxWidth, maxHeight);
542 m_pBackgroundLoader->LoadPic(m_iCurrentPic, m_iCurrentSlide, picturePath, maxWidth, maxHeight);
543 m_iLastFailedNextSlide = -1;
544 m_bLoadNextPic = false;
548 // check if we should discard an already loaded next slide
549 if (m_Image[1 - m_iCurrentPic]->IsLoaded() &&
550 m_Image[1 - m_iCurrentPic]->SlideNumber() != m_iNextSlide)
551 m_Image[1 - m_iCurrentPic]->Close();
553 if (m_iNextSlide != m_iCurrentSlide && m_Image[m_iCurrentPic]->IsLoaded() &&
554 !m_Image[1 - m_iCurrentPic]->IsLoaded() && !m_pBackgroundLoader->IsLoading() &&
555 m_iLastFailedNextSlide != m_iNextSlide)
556 { // load the next image
557 m_iLastFailedNextSlide = -1;
558 CFileItemPtr item = m_slides.at(m_iNextSlide);
559 std::string picturePath = GetPicturePath(item.get());
560 if (!picturePath.empty() && (!IsVideo(*item) || !m_bSlideShow || m_bPause))
562 if (IsVideo(*item))
563 CLog::Log(LOGDEBUG, "Loading the thumb {} for next video {}: {}", picturePath, m_iNextSlide,
564 item->GetPath());
565 else
566 CLog::Log(LOGDEBUG, "Loading the next image {}: {}", m_iNextSlide, item->GetPath());
568 int maxWidth, maxHeight;
569 GetCheckedSize((float)res.iWidth * m_fZoom,
570 (float)res.iHeight * m_fZoom,
571 maxWidth, maxHeight);
572 m_pBackgroundLoader->LoadPic(1 - m_iCurrentPic, m_iNextSlide, picturePath, maxWidth, maxHeight);
576 bool bPlayVideo = IsVideo(*m_slides.at(m_iCurrentSlide)) && m_iVideoSlide != m_iCurrentSlide;
577 if (bPlayVideo)
578 bSlideShow = false;
580 // render the current image
581 if (m_Image[m_iCurrentPic]->IsLoaded())
583 if (m_Image[m_iCurrentPic]->IsAnimating())
584 MarkDirtyRegion();
586 m_Image[m_iCurrentPic]->SetInSlideshow(bSlideShow);
587 m_Image[m_iCurrentPic]->Pause(!bSlideShow);
588 m_Image[m_iCurrentPic]->Process(currentTime, regions);
591 // Check if we should be transitioning immediately
592 if (m_bLoadNextPic && m_Image[m_iCurrentPic]->IsLoaded())
594 CLog::Log(LOGDEBUG, "Starting immediate transition due to user wanting slide {}",
595 m_slides.at(m_iNextSlide)->GetPath());
596 if (m_Image[m_iCurrentPic]->StartTransition())
598 m_Image[m_iCurrentPic]->SetTransitionTime(1, IMMEDIATE_TRANSITION_TIME);
599 m_bLoadNextPic = false;
600 MarkDirtyRegion();
604 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
606 // render the next image
607 if (m_Image[m_iCurrentPic]->DrawNextImage())
609 if (m_bSlideShow && !m_bPause && IsVideo(*m_slides.at(m_iNextSlide)))
611 // do not show thumb of video when playing slideshow
613 else if (m_Image[1 - m_iCurrentPic]->IsLoaded())
615 if (appPlayer->IsPlayingVideo())
616 appPlayer->ClosePlayer();
617 m_bPlayingVideo = false;
618 m_iVideoSlide = -1;
620 // first time render the next image, make sure using current display effect.
621 if (!m_Image[1 - m_iCurrentPic]->IsStarted())
623 CSlideShowPic::DISPLAY_EFFECT effect = GetDisplayEffect(m_iNextSlide);
624 if (m_Image[1 - m_iCurrentPic]->DisplayEffectNeedChange(effect))
625 m_Image[1 - m_iCurrentPic]->Reset(effect);
628 if (m_Image[1 - m_iCurrentPic]->IsAnimating())
629 MarkDirtyRegion();
631 // set the appropriate transition time
632 m_Image[1 - m_iCurrentPic]->SetTransitionTime(0,
633 m_Image[m_iCurrentPic]->GetTransitionTime(1));
634 m_Image[1 - m_iCurrentPic]->Pause(!m_bSlideShow || m_bPause ||
635 IsVideo(*m_slides.at(m_iNextSlide)));
636 m_Image[1 - m_iCurrentPic]->Process(currentTime, regions);
638 else // next pic isn't loaded. We should hang around if it is in progress
640 if (m_pBackgroundLoader->IsLoading())
642 // CLog::Log(LOGDEBUG, "Having to hold the current image ({}) while we load {}", m_vecSlides[m_iCurrentSlide], m_vecSlides[m_iNextSlide]);
643 m_Image[m_iCurrentPic]->Keep();
648 // check if we should swap images now
649 if (m_Image[m_iCurrentPic]->IsFinished() ||
650 (m_bLoadNextPic && !m_Image[m_iCurrentPic]->IsLoaded()))
652 m_bLoadNextPic = false;
653 if (m_Image[m_iCurrentPic]->IsFinished())
654 CLog::Log(LOGDEBUG, "Image {} is finished rendering, switching to {}",
655 m_slides.at(m_iCurrentSlide)->GetPath(), m_slides.at(m_iNextSlide)->GetPath());
656 else
657 // what if it's bg loading?
658 CLog::Log(LOGDEBUG, "Image {} is not loaded, switching to {}",
659 m_slides.at(m_iCurrentSlide)->GetPath(), m_slides.at(m_iNextSlide)->GetPath());
661 if (m_Image[m_iCurrentPic]->IsFinished() && m_iCurrentSlide == m_iNextSlide &&
662 m_Image[m_iCurrentPic]->SlideNumber() == m_iNextSlide)
663 m_Image[m_iCurrentPic]->Reset(GetDisplayEffect(m_iCurrentSlide));
664 else
666 if (m_Image[m_iCurrentPic]->IsLoaded())
667 m_Image[m_iCurrentPic]->Reset(GetDisplayEffect(m_iCurrentSlide));
668 else
669 m_Image[m_iCurrentPic]->Close();
671 if ((m_Image[1 - m_iCurrentPic]->IsLoaded() &&
672 m_Image[1 - m_iCurrentPic]->SlideNumber() == m_iNextSlide) ||
673 (m_pBackgroundLoader->IsLoading() && m_pBackgroundLoader->SlideNumber() == m_iNextSlide &&
674 m_pBackgroundLoader->Pic() == 1 - m_iCurrentPic))
676 m_iCurrentPic = 1 - m_iCurrentPic;
678 else
680 m_Image[1 - m_iCurrentPic]->Close();
681 m_iCurrentPic = 1 - m_iCurrentPic;
683 m_iCurrentSlide = m_iNextSlide;
684 m_iNextSlide = GetNextSlide();
686 bPlayVideo = IsVideo(*m_slides.at(m_iCurrentSlide)) && m_iVideoSlide != m_iCurrentSlide;
688 AnnouncePlayerPlay(m_slides.at(m_iCurrentSlide));
690 m_iZoomFactor = 1;
691 m_fZoom = 1.0f;
692 m_fRotate = 0.0f;
695 if (bPlayVideo && !PlayVideo())
696 return;
698 if (m_Image[m_iCurrentPic]->IsLoaded())
699 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPicturesInfoProvider().SetCurrentSlide(m_slides.at(m_iCurrentSlide).get());
701 RenderPause();
702 if (IsVideo(*m_slides.at(m_iCurrentSlide)) && appPlayer && appPlayer->IsRenderingGuiLayer())
704 MarkDirtyRegion();
706 CGUIWindow::Process(currentTime, regions);
707 m_renderRegion.SetRect(0, 0, (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), (float)CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight());
710 void CGUIWindowSlideShow::Render()
712 if (m_slides.empty())
713 return;
715 CGraphicContext& gfxCtx = CServiceBroker::GetWinSystem()->GetGfxContext();
716 gfxCtx.Clear(0xff000000);
718 if (IsVideo(*m_slides.at(m_iCurrentSlide)))
720 gfxCtx.SetViewWindow(0, 0, m_coordsRes.iWidth, m_coordsRes.iHeight);
721 gfxCtx.SetRenderingResolution(gfxCtx.GetVideoResolution(), false);
723 auto& components = CServiceBroker::GetAppComponents();
724 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
726 if (appPlayer->IsRenderingVideoLayer())
728 const CRect old = gfxCtx.GetScissors();
729 CRect region = GetRenderRegion();
730 region.Intersect(old);
731 gfxCtx.SetScissors(region);
732 gfxCtx.Clear(0);
733 gfxCtx.SetScissors(old);
735 else if (appPlayer)
737 const ::UTILS::COLOR::Color alpha = gfxCtx.MergeAlpha(0xff000000) >> 24;
738 appPlayer->Render(false, alpha);
741 gfxCtx.SetRenderingResolution(m_coordsRes, m_needsScaling);
743 else
745 if (m_Image[m_iCurrentPic]->IsLoaded())
746 m_Image[m_iCurrentPic]->Render();
748 if (m_Image[m_iCurrentPic]->DrawNextImage() && m_Image[1 - m_iCurrentPic]->IsLoaded())
749 m_Image[1 - m_iCurrentPic]->Render();
752 RenderErrorMessage();
753 CGUIWindow::Render();
756 void CGUIWindowSlideShow::RenderEx()
758 if (IsVideo(*m_slides.at(m_iCurrentSlide)))
760 auto& components = CServiceBroker::GetAppComponents();
761 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
762 appPlayer->Render(false, 255, false);
765 CGUIWindow::RenderEx();
768 int CGUIWindowSlideShow::GetNextSlide()
770 if (m_slides.size() <= 1)
771 return m_iCurrentSlide;
772 int step = m_iDirection >= 0 ? 1 : -1;
773 int nextSlide = (m_iCurrentSlide + step + m_slides.size()) % m_slides.size();
774 while (nextSlide != m_iCurrentSlide)
776 if (!m_slides.at(nextSlide)->HasProperty("unplayable"))
777 return nextSlide;
778 nextSlide = (nextSlide + step + m_slides.size()) % m_slides.size();
780 return m_iCurrentSlide;
783 EVENT_RESULT CGUIWindowSlideShow::OnMouseEvent(const CPoint& point, const MOUSE::CMouseEvent& event)
785 if (event.m_id == ACTION_GESTURE_NOTIFY)
787 int result = EVENT_RESULT_ROTATE | EVENT_RESULT_ZOOM;
788 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic]->m_bCanMoveHorizontally)
789 result |= EVENT_RESULT_SWIPE;
790 else
791 result |= EVENT_RESULT_PAN_HORIZONTAL;
793 if (m_Image[m_iCurrentPic]->m_bCanMoveVertically)
794 result |= EVENT_RESULT_PAN_VERTICAL;
796 return (EVENT_RESULT)result;
798 else if (event.m_id == ACTION_GESTURE_BEGIN)
800 m_firstGesturePoint = point;
801 m_fInitialZoom = m_fZoom;
802 m_fInitialRotate = m_fRotate;
803 return EVENT_RESULT_HANDLED;
805 else if (event.m_id == ACTION_GESTURE_PAN)
807 // zoomed in - free move mode
808 if (m_iZoomFactor != 1 && (m_Image[m_iCurrentPic]->m_bCanMoveHorizontally ||
809 m_Image[m_iCurrentPic]->m_bCanMoveVertically))
811 Move(PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.x - point.x), PICTURE_MOVE_AMOUNT_TOUCH / m_iZoomFactor * (m_firstGesturePoint.y - point.y));
812 m_firstGesturePoint = point;
814 return EVENT_RESULT_HANDLED;
816 else if (event.m_id == ACTION_GESTURE_SWIPE_LEFT || event.m_id == ACTION_GESTURE_SWIPE_RIGHT)
818 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic]->m_bCanMoveHorizontally)
820 // on zoomlevel 1 just detect swipe left and right
821 if (event.m_id == ACTION_GESTURE_SWIPE_LEFT)
822 OnAction(CAction(ACTION_NEXT_PICTURE));
823 else
824 OnAction(CAction(ACTION_PREV_PICTURE));
827 else if (event.m_id == ACTION_GESTURE_END || event.m_id == ACTION_GESTURE_ABORT)
829 if (m_fRotate != 0.0f)
831 // "snap" to nearest of 0, 90, 180 and 270 if the
832 // difference in angle is +/-10 degrees
833 float reminder = fmodf(m_fRotate, 90.0f);
834 if (fabs(reminder) < ROTATION_SNAP_RANGE)
835 Rotate(-reminder);
836 else if (reminder > 90.0f - ROTATION_SNAP_RANGE)
837 Rotate(90.0f - reminder);
838 else if (-reminder > 90.0f - ROTATION_SNAP_RANGE)
839 Rotate(-90.0f - reminder);
842 m_fInitialZoom = 0.0f;
843 m_fInitialRotate = 0.0f;
844 return EVENT_RESULT_HANDLED;
846 else if (event.m_id == ACTION_GESTURE_ZOOM)
848 ZoomRelative(m_fInitialZoom * event.m_offsetX, true);
849 return EVENT_RESULT_HANDLED;
851 else if (event.m_id == ACTION_GESTURE_ROTATE)
853 Rotate(m_fInitialRotate + event.m_offsetX - m_fRotate, true);
854 return EVENT_RESULT_HANDLED;
856 return EVENT_RESULT_UNHANDLED;
859 bool CGUIWindowSlideShow::OnAction(const CAction &action)
861 switch (action.GetID())
863 case ACTION_SHOW_INFO:
865 CGUIDialogPictureInfo *pictureInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPictureInfo>(WINDOW_DIALOG_PICTURE_INFO);
866 if (pictureInfo)
868 // no need to set the picture here, it's done in Render()
869 pictureInfo->Open();
872 break;
873 case ACTION_STOP:
875 if (m_slides.size())
876 AnnouncePlayerStop(m_slides.at(m_iCurrentSlide));
877 auto& components = CServiceBroker::GetAppComponents();
878 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
879 if (appPlayer->IsPlayingVideo())
880 appPlayer->ClosePlayer();
881 Close();
882 break;
885 case ACTION_NEXT_PICTURE:
886 ShowNext();
887 break;
889 case ACTION_PREV_PICTURE:
890 ShowPrevious();
891 break;
893 case ACTION_MOVE_RIGHT:
894 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic]->m_bCanMoveHorizontally)
895 ShowNext();
896 else
897 Move(PICTURE_MOVE_AMOUNT, 0);
898 break;
900 case ACTION_MOVE_LEFT:
901 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic]->m_bCanMoveHorizontally)
902 ShowPrevious();
903 else
904 Move( -PICTURE_MOVE_AMOUNT, 0);
905 break;
907 case ACTION_MOVE_DOWN:
908 Move(0, PICTURE_MOVE_AMOUNT);
909 break;
911 case ACTION_MOVE_UP:
912 Move(0, -PICTURE_MOVE_AMOUNT);
913 break;
915 case ACTION_PAUSE:
916 case ACTION_PLAYER_PLAY:
917 if (m_slides.size() == 0)
918 break;
919 if (IsVideo(*m_slides.at(m_iCurrentSlide)))
921 if (!m_bPlayingVideo)
923 if (m_bSlideShow)
925 SetDirection(1);
926 m_bPause = false;
928 PlayVideo();
931 else if (!m_bSlideShow || m_bPause)
933 m_bSlideShow = true;
934 m_bPause = false;
935 SetDirection(1);
936 if (m_Image[m_iCurrentPic]->IsLoaded())
938 CSlideShowPic::DISPLAY_EFFECT effect = GetDisplayEffect(m_iCurrentSlide);
939 if (m_Image[m_iCurrentPic]->DisplayEffectNeedChange(effect))
940 m_Image[m_iCurrentPic]->Reset(effect);
942 AnnouncePlayerPlay(m_slides.at(m_iCurrentSlide));
944 else if (action.GetID() == ACTION_PAUSE)
946 m_bPause = true;
947 AnnouncePlayerPause(m_slides.at(m_iCurrentSlide));
949 break;
951 case ACTION_ZOOM_OUT:
952 Zoom(m_iZoomFactor - 1);
953 break;
955 case ACTION_ZOOM_IN:
956 Zoom(m_iZoomFactor + 1);
957 break;
959 case ACTION_GESTURE_SWIPE_UP:
960 case ACTION_GESTURE_SWIPE_DOWN:
961 if (m_iZoomFactor == 1 || !m_Image[m_iCurrentPic]->m_bCanMoveVertically)
963 bool swipeOnLeft = action.GetAmount() < CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth() / 2.0f;
964 bool swipeUp = action.GetID() == ACTION_GESTURE_SWIPE_UP;
965 if (swipeUp == swipeOnLeft)
966 Rotate(90.0f);
967 else
968 Rotate(-90.0f);
970 break;
972 case ACTION_ROTATE_PICTURE_CW:
973 Rotate(90.0f);
974 break;
976 case ACTION_ROTATE_PICTURE_CCW:
977 Rotate(-90.0f);
978 break;
980 case ACTION_ZOOM_LEVEL_NORMAL:
981 case ACTION_ZOOM_LEVEL_1:
982 case ACTION_ZOOM_LEVEL_2:
983 case ACTION_ZOOM_LEVEL_3:
984 case ACTION_ZOOM_LEVEL_4:
985 case ACTION_ZOOM_LEVEL_5:
986 case ACTION_ZOOM_LEVEL_6:
987 case ACTION_ZOOM_LEVEL_7:
988 case ACTION_ZOOM_LEVEL_8:
989 case ACTION_ZOOM_LEVEL_9:
990 Zoom((action.GetID() - ACTION_ZOOM_LEVEL_NORMAL) + 1);
991 break;
993 case ACTION_ANALOG_MOVE:
994 // this action is used and works, when CAction object provides both x and y coordinates
995 Move(action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, -action.GetAmount(1)*PICTURE_MOVE_AMOUNT_ANALOG);
996 break;
997 case ACTION_ANALOG_MOVE_X_LEFT:
998 Move(-action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, 0.0f);
999 break;
1000 case ACTION_ANALOG_MOVE_X_RIGHT:
1001 Move(action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG, 0.0f);
1002 break;
1003 case ACTION_ANALOG_MOVE_Y_UP:
1004 Move(0.0f, -action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG);
1005 break;
1006 case ACTION_ANALOG_MOVE_Y_DOWN:
1007 Move(0.0f, action.GetAmount()*PICTURE_MOVE_AMOUNT_ANALOG);
1008 break;
1010 default:
1011 return CGUIDialog::OnAction(action);
1013 MarkDirtyRegion();
1014 return true;
1017 void CGUIWindowSlideShow::RenderErrorMessage()
1019 if (!m_bErrorMessage)
1020 return ;
1022 const CGUIControl *control = GetControl(LABEL_ROW1);
1023 if (NULL == control || control->GetControlType() != CGUIControl::GUICONTROL_LABEL)
1025 return;
1028 CGUIFont *pFont = static_cast<const CGUILabelControl*>(control)->GetLabelInfo().font;
1029 CGUITextLayout::DrawText(pFont, 0.5f*CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(), 0.5f*CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(), 0xffffffff, 0, g_localizeStrings.Get(747), XBFONT_CENTER_X | XBFONT_CENTER_Y);
1032 bool CGUIWindowSlideShow::OnMessage(CGUIMessage& message)
1034 switch ( message.GetMessage() )
1036 case GUI_MSG_WINDOW_INIT:
1038 m_Resolution = (RESOLUTION) CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PICTURES_DISPLAYRESOLUTION);
1040 //FIXME: Use GUI resolution for now
1041 if (false /*m_Resolution != CDisplaySettings::GetInstance().GetCurrentResolution() && m_Resolution != INVALID && m_Resolution!=AUTORES*/)
1042 CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(m_Resolution, false);
1043 else
1044 m_Resolution = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
1046 CGUIDialog::OnMessage(message);
1048 // turn off slideshow if we only have 1 image
1049 if (m_slides.size() <= 1)
1050 m_bSlideShow = false;
1052 return true;
1054 break;
1056 case GUI_MSG_SHOW_PICTURE:
1058 const std::string& strFile = message.GetStringParam();
1059 Reset();
1060 CFileItem item(strFile, false);
1061 Add(&item);
1062 RunSlideShow("", false, false, true, "", false);
1064 break;
1066 case GUI_MSG_START_SLIDESHOW:
1068 const std::string& strFolder = message.GetStringParam();
1069 unsigned int iParams = message.GetParam1();
1070 const std::string& beginSlidePath = message.GetStringParam(1);
1071 //decode params
1072 bool bRecursive = false;
1073 bool bRandom = false;
1074 bool bNotRandom = false;
1075 bool bPause = false;
1076 if (iParams > 0)
1078 if ((iParams & 1) == 1)
1079 bRecursive = true;
1080 if ((iParams & 2) == 2)
1081 bRandom = true;
1082 if ((iParams & 4) == 4)
1083 bNotRandom = true;
1084 if ((iParams & 8) == 8)
1085 bPause = true;
1087 RunSlideShow(strFolder, bRecursive, bRandom, bNotRandom, beginSlidePath, !bPause);
1089 break;
1091 case GUI_MSG_PLAYLISTPLAYER_STOPPED:
1094 break;
1096 case GUI_MSG_PLAYBACK_STOPPED:
1098 if (m_bPlayingVideo)
1100 m_bPlayingVideo = false;
1101 m_iVideoSlide = -1;
1102 if (m_bSlideShow)
1103 m_bPause = true;
1106 break;
1108 case GUI_MSG_PLAYBACK_ENDED:
1110 if (m_bPlayingVideo)
1112 m_bPlayingVideo = false;
1113 m_iVideoSlide = -1;
1114 if (m_bSlideShow)
1116 m_bPause = false;
1117 if (m_iCurrentSlide == m_iNextSlide)
1118 break;
1119 m_Image[m_iCurrentPic]->Close();
1120 m_iCurrentPic = 1 - m_iCurrentPic;
1121 m_iCurrentSlide = m_iNextSlide;
1122 m_iNextSlide = GetNextSlide();
1123 AnnouncePlayerPlay(m_slides.at(m_iCurrentSlide));
1124 m_iZoomFactor = 1;
1125 m_fZoom = 1.0f;
1126 m_fRotate = 0.0f;
1130 break;
1132 return CGUIDialog::OnMessage(message);
1135 void CGUIWindowSlideShow::RenderPause()
1136 { // display the pause icon
1137 if (m_bPause)
1139 SET_CONTROL_VISIBLE(CONTROL_PAUSE);
1141 else
1143 SET_CONTROL_HIDDEN(CONTROL_PAUSE);
1147 void CGUIWindowSlideShow::Rotate(float fAngle, bool immediate /* = false */)
1149 if (m_Image[m_iCurrentPic]->DrawNextImage())
1150 return;
1152 m_fRotate += fAngle;
1154 m_Image[m_iCurrentPic]->Rotate(fAngle, immediate);
1157 void CGUIWindowSlideShow::Zoom(int iZoom)
1159 if (iZoom > MAX_ZOOM_FACTOR || iZoom < 1)
1160 return;
1162 ZoomRelative(zoomamount[iZoom - 1]);
1165 void CGUIWindowSlideShow::ZoomRelative(float fZoom, bool immediate /* = false */)
1167 if (fZoom < zoomamount[0])
1168 fZoom = zoomamount[0];
1169 else if (fZoom > zoomamount[MAX_ZOOM_FACTOR - 1])
1170 fZoom = zoomamount[MAX_ZOOM_FACTOR - 1];
1172 if (m_Image[m_iCurrentPic]->DrawNextImage())
1173 return;
1175 m_fZoom = fZoom;
1177 // find the nearest zoom factor
1178 for (unsigned int i = 1; i < MAX_ZOOM_FACTOR; i++)
1180 if (m_fZoom > zoomamount[i])
1181 continue;
1183 if (fabs(m_fZoom - zoomamount[i - 1]) < fabs(m_fZoom - zoomamount[i]))
1184 m_iZoomFactor = i;
1185 else
1186 m_iZoomFactor = i + 1;
1188 break;
1191 m_Image[m_iCurrentPic]->Zoom(m_fZoom, immediate);
1194 void CGUIWindowSlideShow::Move(float fX, float fY)
1196 if (m_Image[m_iCurrentPic]->IsLoaded() && m_Image[m_iCurrentPic]->GetZoom() > 1)
1197 { // we move in the opposite direction, due to the fact we are moving
1198 // the viewing window, not the picture.
1199 m_Image[m_iCurrentPic]->Move(-fX, -fY);
1203 bool CGUIWindowSlideShow::PlayVideo()
1205 CFileItemPtr item = m_slides.at(m_iCurrentSlide);
1206 if (!item || !IsVideo(*item))
1207 return false;
1208 CLog::Log(LOGDEBUG, "Playing current video slide {}", item->GetPath());
1209 m_bPlayingVideo = true;
1210 m_iVideoSlide = m_iCurrentSlide;
1211 bool ret = g_application.PlayFile(*item, "");
1212 if (ret == true)
1213 return true;
1214 else
1216 CLog::Log(LOGINFO, "set video {} unplayable", item->GetPath());
1217 item->SetProperty("unplayable", true);
1219 m_bPlayingVideo = false;
1220 m_iVideoSlide = -1;
1221 return false;
1224 CSlideShowPic::DISPLAY_EFFECT CGUIWindowSlideShow::GetDisplayEffect(int iSlideNumber) const
1226 if (m_bSlideShow && !m_bPause && !IsVideo(*m_slides.at(iSlideNumber)))
1227 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_DISPLAYEFFECTS) ? CSlideShowPic::EFFECT_RANDOM : CSlideShowPic::EFFECT_NONE;
1228 else
1229 return CSlideShowPic::EFFECT_NO_TIMEOUT;
1232 void CGUIWindowSlideShow::OnLoadPic(int iPic,
1233 int iSlideNumber,
1234 const std::string& strFileName,
1235 std::unique_ptr<CTexture> pTexture,
1236 bool bFullSize)
1238 if (pTexture)
1240 // set the pic's texture + size etc.
1241 if (iSlideNumber >= static_cast<int>(m_slides.size()) || GetPicturePath(m_slides.at(iSlideNumber).get()) != strFileName)
1242 { // throw this away - we must have cleared the slideshow while we were still loading
1243 return;
1245 CLog::Log(LOGDEBUG, "Finished background loading slot {}, {}: {}", iPic, iSlideNumber,
1246 m_slides.at(iSlideNumber)->GetPath());
1247 m_Image[iPic]->SetOriginalSize(pTexture->GetOriginalWidth(), pTexture->GetOriginalHeight(),
1248 bFullSize);
1249 m_Image[iPic]->SetTexture(iSlideNumber, std::move(pTexture), GetDisplayEffect(iSlideNumber));
1251 m_Image[iPic]->m_bIsComic = false;
1252 if (URIUtils::IsInRAR(m_slides.at(m_iCurrentSlide)->GetPath()) || URIUtils::IsInZIP(m_slides.at(m_iCurrentSlide)->GetPath())) // move to top for cbr/cbz
1254 CURL url(m_slides.at(m_iCurrentSlide)->GetPath());
1255 const std::string& strHostName = url.GetHostName();
1256 if (URIUtils::HasExtension(strHostName, ".cbr|.cbz"))
1258 m_Image[iPic]->m_bIsComic = true;
1259 m_Image[iPic]->Move((float)m_Image[iPic]->GetOriginalWidth(),
1260 (float)m_Image[iPic]->GetOriginalHeight());
1264 else if (iSlideNumber >= static_cast<int>(m_slides.size()) || GetPicturePath(m_slides.at(iSlideNumber).get()) != strFileName)
1265 { // Failed to load image. and not match values calling LoadPic, then something is changed, ignore.
1266 CLog::Log(LOGDEBUG,
1267 "CGUIWindowSlideShow::OnLoadPic({}, {}, {}) on failure not match current state (cur "
1268 "{}, next {}, curpic {}, pic[0, 1].slidenumber={}, {}, {})",
1269 iPic, iSlideNumber, strFileName, m_iCurrentSlide, m_iNextSlide, m_iCurrentPic,
1270 m_Image[0]->SlideNumber(), m_Image[1]->SlideNumber(),
1271 iSlideNumber >= static_cast<int>(m_slides.size())
1272 ? ""
1273 : m_slides.at(iSlideNumber)->GetPath());
1275 else
1276 { // Failed to load image. What should be done??
1277 // We should wait for the current pic to finish rendering, then transition it out,
1278 // release the texture, and try and reload this pic from scratch
1279 m_bErrorMessage = true;
1281 MarkDirtyRegion();
1284 void CGUIWindowSlideShow::Shuffle()
1286 KODI::UTILS::RandomShuffle(m_slides.begin(), m_slides.end());
1287 m_iCurrentSlide = 0;
1288 m_iNextSlide = GetNextSlide();
1289 m_bShuffled = true;
1291 AnnouncePropertyChanged("shuffled", true);
1294 int CGUIWindowSlideShow::NumSlides() const
1296 return m_slides.size();
1299 int CGUIWindowSlideShow::CurrentSlide() const
1301 return m_iCurrentSlide + 1;
1304 void CGUIWindowSlideShow::AddFromPath(const std::string &strPath,
1305 bool bRecursive,
1306 SortBy method, SortOrder order, SortAttribute sortAttributes,
1307 const std::string &strExtensions)
1309 if (strPath!="")
1311 // reset the slideshow
1312 Reset();
1313 if (bRecursive)
1315 path_set recursivePaths;
1316 AddItems(strPath, &recursivePaths, method, order, sortAttributes);
1318 else
1319 AddItems(strPath, NULL, method, order, sortAttributes);
1323 void CGUIWindowSlideShow::RunSlideShow(const std::string &strPath,
1324 bool bRecursive /* = false */, bool bRandom /* = false */,
1325 bool bNotRandom /* = false */, const std::string &beginSlidePath /* = "" */,
1326 bool startSlideShow /* = true */, SortBy method /* = SortByLabel */,
1327 SortOrder order /* = SortOrderAscending */, SortAttribute sortAttributes /* = SortAttributeNone */,
1328 const std::string &strExtensions)
1330 // stop any video
1331 const auto& components = CServiceBroker::GetAppComponents();
1332 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
1333 if (appPlayer->IsPlayingVideo())
1334 g_application.StopPlaying();
1336 AddFromPath(strPath, bRecursive, method, order, sortAttributes, strExtensions);
1338 if (!NumSlides())
1339 return;
1341 // mutually exclusive options
1342 // if both are set, clear both and use the gui setting
1343 if (bRandom && bNotRandom)
1344 bRandom = bNotRandom = false;
1346 // NotRandom overrides the window setting
1347 if ((!bNotRandom && CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE)) || bRandom)
1348 Shuffle();
1350 if (!beginSlidePath.empty())
1351 Select(beginSlidePath);
1353 if (startSlideShow)
1354 StartSlideShow();
1355 else
1357 PlayPicture();
1360 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
1363 void CGUIWindowSlideShow::PlayPicture()
1365 if (m_iCurrentSlide >= 0 && m_iCurrentSlide < static_cast<int>(m_slides.size()))
1366 AnnouncePlayerPlay(m_slides.at(m_iCurrentSlide));
1369 void CGUIWindowSlideShow::AddItems(const std::string &strPath, path_set *recursivePaths, SortBy method, SortOrder order, SortAttribute sortAttributes)
1371 // check whether we've already added this path
1372 if (recursivePaths)
1374 std::string path(strPath);
1375 URIUtils::RemoveSlashAtEnd(path);
1376 if (recursivePaths->find(path) != recursivePaths->end())
1377 return;
1378 recursivePaths->insert(path);
1381 CFileItemList items;
1382 CGUIViewStateWindowPictures viewState(items);
1384 // fetch directory and sort accordingly
1385 if (!CDirectory::GetDirectory(strPath, items, viewState.GetExtensions(), DIR_FLAG_NO_FILE_DIRS))
1386 return;
1388 items.Sort(method, order, sortAttributes);
1390 // need to go into all subdirs
1391 for (int i = 0; i < items.Size(); i++)
1393 CFileItemPtr item = items[i];
1394 if (item->m_bIsFolder && recursivePaths)
1396 AddItems(item->GetPath(), recursivePaths);
1398 else if (!item->m_bIsFolder && !URIUtils::IsRAR(item->GetPath()) && !URIUtils::IsZIP(item->GetPath()))
1399 { // add to the slideshow
1400 Add(item.get());
1405 void CGUIWindowSlideShow::GetCheckedSize(float width, float height, int &maxWidth, int &maxHeight)
1407 maxWidth = CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
1408 maxHeight = CServiceBroker::GetRenderSystem()->GetMaxTextureSize();
1411 std::string CGUIWindowSlideShow::GetPicturePath(CFileItem *item)
1413 bool isVideo = IsVideo(*item);
1414 std::string picturePath = item->GetDynPath();
1415 if (isVideo)
1417 picturePath = item->GetArt("thumb");
1418 if (picturePath.empty() && !item->HasProperty("nothumb"))
1420 CPictureThumbLoader thumbLoader;
1421 thumbLoader.LoadItem(item);
1422 picturePath = item->GetArt("thumb");
1423 if (picturePath.empty())
1424 item->SetProperty("nothumb", true);
1427 return picturePath;
1431 void CGUIWindowSlideShow::RunSlideShow(const std::vector<std::string>& paths, int start /* = 0*/)
1433 auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowSlideShow>(WINDOW_SLIDESHOW);
1434 if (dialog)
1436 std::vector<CFileItemPtr> items;
1437 items.reserve(paths.size());
1438 for (const auto& path : paths)
1439 items.push_back(std::make_shared<CFileItem>(IMAGE_FILES::URLFromFile(path), false));
1441 dialog->Reset();
1442 dialog->m_bPause = true;
1443 dialog->m_bSlideShow = false;
1444 dialog->m_iDirection = 1;
1445 dialog->m_iCurrentSlide = start;
1446 dialog->m_iNextSlide = (start + 1) % items.size();
1447 dialog->m_slides = std::move(items);
1448 dialog->Open();