changed: Delayed animations now allocate on start, rather than after the delay.
[xbmc:xbmc-antiquated.git] / XBMC / guilib / GUIMultiImage.cpp
1 #include "include.h"
2 #include "../xbmc/FileItem.h"
3 #include "GUIMultiImage.h"
4 #include "TextureManager.h"
5 #include "../xbmc/FileSystem/HDDirectory.h"
6 #include "../xbmc/utils/GUIInfoManager.h"
7
8 using namespace DIRECTORY;
9
10 CGUIMultiImage::CGUIMultiImage(DWORD dwParentID, DWORD dwControlId, float posX, float posY, float width, float height, const CStdString& strTexturePath, DWORD timePerImage, DWORD fadeTime, bool randomized, bool loop)
11     : CGUIControl(dwParentID, dwControlId, posX, posY, width, height)
12 {
13   m_currentPath = m_texturePath = strTexturePath;
14   m_currentImage = 0;
15   m_timePerImage = timePerImage;
16   m_fadeTime = fadeTime;
17   m_randomized = randomized;
18   m_loop = loop;
19   m_aspectRatio = CGUIImage::ASPECT_RATIO_STRETCH;
20   ControlType = GUICONTROL_MULTI_IMAGE;
21   m_bDynamicResourceAlloc=false;
22   m_Info = 0;
23   m_directoryLoaded = false;
24 }
25
26 CGUIMultiImage::~CGUIMultiImage(void)
27 {
28 }
29
30 void CGUIMultiImage::Render()
31 {
32   // check if we're hidden, and deallocate + return
33   if (!IsVisible() && m_visible != DELAYED)
34   {
35     if (m_bDynamicResourceAlloc && IsAllocated())
36       FreeResources();
37     return;
38   }
39
40   // check for conditional information before we
41   // alloc as this can free our resources
42   if (m_Info)
43   {
44     CStdString texturePath = g_infoManager.GetImage(m_Info, WINDOW_INVALID);
45     if (texturePath != m_currentPath && !texturePath.IsEmpty())
46     {
47       m_currentPath = texturePath;
48       FreeResources();
49       LoadDirectory();
50     }
51     else if (texturePath.IsEmpty() && m_currentPath != m_texturePath)
52     {
53       m_currentPath = m_texturePath;
54       FreeResources();
55       LoadDirectory();
56     }
57   }
58
59   if (!IsAllocated())
60     AllocResources();
61
62   // if we're delayed, we allocate (above) but there's no need to render.
63   if (m_visible == DELAYED)
64     return CGUIControl::Render();
65
66   if (!m_images.empty())
67   {
68     // Set a viewport so that we don't render outside the defined area
69     g_graphicsContext.SetViewPort(m_posX, m_posY, m_width, m_height);
70     m_images[m_currentImage]->Render();
71
72     unsigned int nextImage = m_currentImage + 1;
73     if (nextImage >= m_images.size())
74       nextImage = m_loop ? 0 : m_currentImage;  // stay on the last image if <loop>no</loop>
75
76     if (nextImage != m_currentImage)
77     {
78       // check if we should be loading a new image yet
79       if (m_imageTimer.IsRunning() && m_imageTimer.GetElapsedMilliseconds() > m_timePerImage)
80       {
81         m_imageTimer.Stop();
82         // grab a new image
83         LoadImage(nextImage);
84         // start the fade timer
85         m_fadeTimer.StartZero();
86       }
87
88       // check if we are still fading
89       if (m_fadeTimer.IsRunning())
90       {
91         // check if the fade timer has run out
92         float timeFading = m_fadeTimer.GetElapsedMilliseconds();
93         if (timeFading > m_fadeTime)
94         {
95           m_fadeTimer.Stop();
96           // swap images
97           m_images[m_currentImage]->FreeResources();
98           m_images[nextImage]->SetAlpha(255);
99           m_currentImage = nextImage;
100           // start the load timer
101           m_imageTimer.StartZero();
102         }
103         else
104         { // perform the fade
105           float fadeAmount = timeFading / m_fadeTime;
106           m_images[nextImage]->SetAlpha((unsigned char)(255 * fadeAmount));
107         }
108         m_images[nextImage]->Render();
109       }
110     }
111     g_graphicsContext.RestoreViewPort();
112   }
113   CGUIControl::Render();
114 }
115
116 bool CGUIMultiImage::OnAction(const CAction &action)
117 {
118   return false;
119 }
120
121 bool CGUIMultiImage::OnMessage(CGUIMessage &message)
122 {
123   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
124   {
125     if (m_Info)
126       FreeResources();
127     return true;
128   }
129   return CGUIControl::OnMessage(message);
130 }
131
132 void CGUIMultiImage::PreAllocResources()
133 {
134   FreeResources();
135 }
136
137 void CGUIMultiImage::AllocResources()
138 {
139   FreeResources();
140   CGUIControl::AllocResources();
141
142   if (!m_directoryLoaded)
143     LoadDirectory();
144
145   // Randomize or sort our images if necessary
146   if (m_randomized)
147     random_shuffle(m_files.begin(), m_files.end());
148
149   for (unsigned int i=0; i < m_files.size(); i++)
150   {
151     CGUIImage *pImage = new CGUIImage(GetParentID(), GetID(), m_posX, m_posY, m_width, m_height, m_files[i]);
152     if (pImage)
153       m_images.push_back(pImage);
154   }
155   // Load in the current image, and reset our timer
156   m_imageTimer.StartZero();
157   m_fadeTimer.Stop();
158   m_currentImage = 0;
159   if (m_images.empty())
160     return;
161
162   LoadImage(m_currentImage);
163 }
164
165 void CGUIMultiImage::LoadImage(int image)
166 {
167   if (image < 0 || image >= (int)m_images.size())
168     return;
169
170   m_images[image]->AllocResources();
171   m_images[image]->SetColorDiffuse(m_diffuseColor);
172
173   // Scale image so that it will fill our render area
174   if (m_aspectRatio != CGUIImage::ASPECT_RATIO_STRETCH)
175   {
176     // image is scaled so that the aspect ratio is maintained (taking into account the TV pixel ratio)
177     // and so that it fills the allocated space (so is zoomed then cropped)
178     float sourceAspectRatio = (float)m_images[image]->GetTextureWidth() / m_images[image]->GetTextureHeight();
179     float aspectRatio = sourceAspectRatio / g_graphicsContext.GetPixelRatio(g_graphicsContext.GetVideoResolution());
180
181     float newWidth = m_width;
182     float newHeight = newWidth / aspectRatio;
183     if ((m_aspectRatio == CGUIImage::ASPECT_RATIO_SCALE && newHeight < m_height) ||
184         (m_aspectRatio == CGUIImage::ASPECT_RATIO_KEEP && newHeight > m_height))
185     {
186       newHeight = m_height;
187       newWidth = newHeight * aspectRatio;
188     }
189     m_images[image]->SetPosition(m_posX - (newWidth - m_width)*0.5f, m_posY - (newHeight - m_height)*0.5f);
190     m_images[image]->SetWidth(newWidth);
191     m_images[image]->SetHeight(newHeight);
192   }
193 }
194
195 void CGUIMultiImage::FreeResources()
196 {
197   for (unsigned int i = 0; i < m_images.size(); ++i)
198   {
199     m_images[i]->FreeResources();
200     delete m_images[i];
201   }
202
203   m_images.clear();
204   m_currentImage = 0;
205   CGUIControl::FreeResources();
206 }
207
208 void CGUIMultiImage::DynamicResourceAlloc(bool bOnOff)
209 {
210   CGUIControl::DynamicResourceAlloc(bOnOff);
211   m_bDynamicResourceAlloc=bOnOff;
212 }
213
214 bool CGUIMultiImage::CanFocus() const
215 {
216   return false;
217 }
218
219 void CGUIMultiImage::SetAspectRatio(CGUIImage::GUIIMAGE_ASPECT_RATIO ratio)
220 {
221   if (m_aspectRatio != ratio)
222   {
223     m_aspectRatio = ratio;
224     m_bInvalidated = true;
225   }
226 }
227
228 void CGUIMultiImage::LoadDirectory()
229 {
230   // Load any images from our texture bundle first
231   m_files.clear();
232
233   // don't load any images if our path is empty
234   if (m_currentPath.IsEmpty()) return;
235
236   // check to see if we have a single image or a folder of images
237   CFileItem item(m_currentPath, true);
238   if (item.IsPicture())
239   {
240     m_files.push_back(g_TextureManager.GetTexturePath(m_currentPath));
241   }
242   else
243   { // folder of images
244     g_TextureManager.GetBundledTexturesFromPath(m_currentPath, m_files);
245
246     // Load in our images from the directory specified
247     // m_currentPath is relative (as are all skin paths)
248     CStdString realPath = g_TextureManager.GetTexturePath(m_currentPath);
249     CHDDirectory dir;
250     CFileItemList items;
251     dir.GetDirectory(realPath, items);
252     for (int i=0; i < items.Size(); i++)
253     {
254       CFileItem *pItem = items[i];
255       if (pItem->IsPicture())
256         m_files.push_back(pItem->m_strPath);
257     }
258   }
259
260   // sort our images - they'll be randomized in AllocResources() if necessary
261   sort(m_files.begin(), m_files.end());
262
263   // flag as loaded - no point in constantly reloading them
264   m_directoryLoaded = true;
265 }