merged: Revisions 9635 -> 9702 changes from trunk.
[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 #include "../xbmc/Util.h"
8
9 using namespace DIRECTORY;
10
11 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, DWORD timeToPauseAtEnd)
12     : CGUIControl(dwParentID, dwControlId, posX, posY, width, height)
13 {
14   m_currentPath = m_texturePath = strTexturePath;
15   CUtil::AddSlashAtEnd(m_currentPath);
16   CUtil::AddSlashAtEnd(m_texturePath);
17   m_currentImage = 0;
18   m_timePerImage = timePerImage;
19   m_timeToPauseAtEnd = timeToPauseAtEnd;
20   m_fadeTime = fadeTime;
21   m_randomized = randomized;
22   m_loop = loop;
23   m_aspectRatio = CGUIImage::ASPECT_RATIO_STRETCH;
24   ControlType = GUICONTROL_MULTI_IMAGE;
25   m_bDynamicResourceAlloc=false;
26   m_Info = 0;
27   m_directoryLoaded = false;
28 }
29
30 CGUIMultiImage::~CGUIMultiImage(void)
31 {
32 }
33
34 void CGUIMultiImage::UpdateVisibility()
35 {
36   CGUIControl::UpdateVisibility();
37
38   // check if we're hidden, and deallocate if so
39   if (!IsVisible() && m_visible != DELAYED)
40   {
41     if (m_bDynamicResourceAlloc && IsAllocated())
42       FreeResources();
43     return;
44   }
45
46   // we are either delayed or visible, so we can allocate our resources
47
48   // check for conditional information before we
49   // alloc as this can free our resources
50   if (m_Info)
51   {
52     CStdString texturePath = g_infoManager.GetImage(m_Info, WINDOW_INVALID);
53     if (texturePath != m_currentPath && !texturePath.IsEmpty())
54     {
55       m_currentPath = texturePath;
56       FreeResources();
57       LoadDirectory();
58     }
59     else if (texturePath.IsEmpty() && m_currentPath != m_texturePath)
60     {
61       m_currentPath = m_texturePath;
62       FreeResources();
63       LoadDirectory();
64     }
65   }
66
67   // and allocate our resources
68   if (!IsAllocated())
69     AllocResources();
70 }
71
72 void CGUIMultiImage::Render()
73 {
74   if (!m_images.empty())
75   {
76     // Set a viewport so that we don't render outside the defined area
77     g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height);
78
79     unsigned int nextImage = m_currentImage + 1;
80     if (nextImage >= m_images.size())
81       nextImage = m_loop ? 0 : m_currentImage;  // stay on the last image if <loop>no</loop>
82
83     if (nextImage != m_currentImage)
84     {
85       // check if we should be loading a new image yet
86       DWORD timeToShow = m_timePerImage;
87       if (0 == nextImage) // last image should be paused for a bit longer if that's what the skinner wishes.
88         timeToShow += m_timeToPauseAtEnd;
89       if (m_imageTimer.IsRunning() && m_imageTimer.GetElapsedMilliseconds() > timeToShow)
90       {
91         m_imageTimer.Stop();
92         // grab a new image
93         LoadImage(nextImage);
94         // start the fade timer
95         m_fadeTimer.StartZero();
96       }
97
98       // check if we are still fading
99       if (m_fadeTimer.IsRunning())
100       {
101         // check if the fade timer has run out
102         float timeFading = m_fadeTimer.GetElapsedMilliseconds();
103         if (timeFading >= m_fadeTime)
104         {
105           m_fadeTimer.Stop();
106           // swap images
107           m_images[m_currentImage]->FreeResources();
108           m_images[nextImage]->SetAlpha(255);
109           m_currentImage = nextImage;
110           // start the load timer
111           m_imageTimer.StartZero();
112         }
113         else
114         { // perform the fade in of next image
115           float fadeAmount = timeFading / m_fadeTime;
116           float alpha = (float)(m_diffuseColor >> 24) / 255.0f;
117           if (alpha < 1 && alpha > 0)
118           { // we have a semi-transparent image, so we need to use a more complicated
119             // fade technique.  Assuming a black background (not generally true, but still...)
120             // we have
121             // b(t) = [a - b(1-t)*a] / a*(1-b(1-t)*a),
122             // where a = alpha, and b(t):[0,1] -> [0,1] is the blend function.
123             // solving, we get
124             // b(t) = [1 - (1-a)^t] / a
125             float blendIn = (1 - pow(1-alpha, fadeAmount)) / alpha;
126             m_images[nextImage]->SetAlpha((unsigned char)(255 * blendIn));
127             float blendOut = (1 - blendIn) / (1 - blendIn*alpha); // no need to use pow() again here
128             m_images[m_currentImage]->SetAlpha((unsigned char)(255 * blendOut));
129           }
130           else
131           { // simple case, just fade in the second image
132             m_images[m_currentImage]->SetAlpha(255);
133             m_images[nextImage]->SetAlpha((unsigned char)(255*fadeAmount));
134           }
135           m_images[m_currentImage]->Render();
136         }
137         m_images[nextImage]->Render();
138       }
139       else
140       { // only one image - render it.
141         m_images[m_currentImage]->Render();
142       }
143     }
144     else
145     { // only one image - render it.
146       m_images[m_currentImage]->Render();
147     }
148     g_graphicsContext.RestoreClipRegion();
149   }
150   CGUIControl::Render();
151 }
152
153 bool CGUIMultiImage::OnAction(const CAction &action)
154 {
155   return false;
156 }
157
158 bool CGUIMultiImage::OnMessage(CGUIMessage &message)
159 {
160   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
161   {
162     if (m_Info)
163       FreeResources();
164     return true;
165   }
166   return CGUIControl::OnMessage(message);
167 }
168
169 void CGUIMultiImage::PreAllocResources()
170 {
171   FreeResources();
172 }
173
174 void CGUIMultiImage::AllocResources()
175 {
176   FreeResources();
177   CGUIControl::AllocResources();
178
179   if (!m_directoryLoaded)
180     LoadDirectory();
181
182   // Randomize or sort our images if necessary
183   if (m_randomized)
184     random_shuffle(m_files.begin(), m_files.end());
185
186   for (unsigned int i=0; i < m_files.size(); i++)
187   {
188     CGUIImage *pImage = new CGUIImage(GetParentID(), GetID(), m_posX, m_posY, m_width, m_height, m_files[i]);
189     if (pImage)
190       m_images.push_back(pImage);
191   }
192   // Load in the current image, and reset our timer
193   m_imageTimer.StartZero();
194   m_fadeTimer.Stop();
195   m_currentImage = 0;
196   if (m_images.empty())
197     return;
198
199   LoadImage(m_currentImage);
200 }
201
202 void CGUIMultiImage::LoadImage(int image)
203 {
204   if (image < 0 || image >= (int)m_images.size())
205     return;
206
207   m_images[image]->AllocResources();
208   m_images[image]->SetColorDiffuse(m_diffuseColor);
209
210   // Scale image so that it will fill our render area
211   if (m_aspectRatio != CGUIImage::ASPECT_RATIO_STRETCH)
212   {
213     // to get the pixel ratio, we must use the SCALED output sizes
214     float pixelRatio = g_graphicsContext.GetScalingPixelRatio();
215
216     float sourceAspectRatio = (float)m_images[image]->GetTextureWidth() / m_images[image]->GetTextureHeight();
217     float aspectRatio = sourceAspectRatio / pixelRatio;
218
219     float newWidth = m_width;
220     float newHeight = newWidth / aspectRatio;
221     if ((m_aspectRatio == CGUIImage::ASPECT_RATIO_SCALE && newHeight < m_height) ||
222         (m_aspectRatio == CGUIImage::ASPECT_RATIO_KEEP && newHeight > m_height))
223     {
224       newHeight = m_height;
225       newWidth = newHeight * aspectRatio;
226     }
227     m_images[image]->SetPosition(m_posX - (newWidth - m_width)*0.5f, m_posY - (newHeight - m_height)*0.5f);
228     m_images[image]->SetWidth(newWidth);
229     m_images[image]->SetHeight(newHeight);
230   }
231 }
232
233 void CGUIMultiImage::FreeResources()
234 {
235   for (unsigned int i = 0; i < m_images.size(); ++i)
236   {
237     m_images[i]->FreeResources();
238     delete m_images[i];
239   }
240
241   m_images.clear();
242   m_currentImage = 0;
243   CGUIControl::FreeResources();
244 }
245
246 void CGUIMultiImage::DynamicResourceAlloc(bool bOnOff)
247 {
248   CGUIControl::DynamicResourceAlloc(bOnOff);
249   m_bDynamicResourceAlloc=bOnOff;
250 }
251
252 bool CGUIMultiImage::CanFocus() const
253 {
254   return false;
255 }
256
257 void CGUIMultiImage::SetAspectRatio(CGUIImage::GUIIMAGE_ASPECT_RATIO ratio)
258 {
259   if (m_aspectRatio != ratio)
260   {
261     m_aspectRatio = ratio;
262     m_bInvalidated = true;
263   }
264 }
265
266 void CGUIMultiImage::LoadDirectory()
267 {
268   // Load any images from our texture bundle first
269   m_files.clear();
270
271   // don't load any images if our path is empty
272   if (m_currentPath.IsEmpty()) return;
273
274   // check to see if we have a single image or a folder of images
275   CFileItem item(m_currentPath, false);
276   if (item.IsPicture())
277   {
278     m_files.push_back(g_TextureManager.GetTexturePath(m_currentPath));
279   }
280   else
281   { // folder of images
282     g_TextureManager.GetBundledTexturesFromPath(m_currentPath, m_files);
283
284     // Load in our images from the directory specified
285     // m_currentPath is relative (as are all skin paths)
286     CStdString realPath = g_TextureManager.GetTexturePath(m_currentPath);
287     CHDDirectory dir;
288     CFileItemList items;
289     dir.GetDirectory(realPath, items);
290     for (int i=0; i < items.Size(); i++)
291     {
292       CFileItem *pItem = items[i];
293       if (pItem->IsPicture())
294         m_files.push_back(pItem->m_strPath);
295     }
296   }
297
298   // sort our images - they'll be randomized in AllocResources() if necessary
299   sort(m_files.begin(), m_files.end());
300
301   // flag as loaded - no point in constantly reloading them
302   m_directoryLoaded = true;
303 }
304