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