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