changed: Move pulled control updating via UpdateInfo() to UpdateVisibility() rather...
[xbmc:xbmc-antiquated.git] / XBMC / guilib / GUIImage.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 "GUIImage.h"
24 #include "TextureManager.h"
25
26 using namespace std;
27
28 CGUIImage::CGUIImage(DWORD dwParentID, DWORD dwControlId, float posX, float posY, float width, float height, const CTextureInfo& texture)
29     : CGUIControl(dwParentID, dwControlId, posX, posY, width, height)
30     , m_texture(posX, posY, width, height, texture)
31 {
32   m_crossFadeTime = 0;
33   m_currentFadeTime = 0;
34   m_lastRenderTime = 0;
35   ControlType = GUICONTROL_IMAGE;
36   m_bDynamicResourceAlloc=false;
37 }
38
39 CGUIImage::CGUIImage(const CGUIImage &left)
40     : CGUIControl(left), m_texture(left.m_texture)
41 {
42   m_info = left.m_info;
43   m_crossFadeTime = left.m_crossFadeTime;
44   // defaults
45   m_currentFadeTime = 0;
46   m_lastRenderTime = 0;
47   ControlType = GUICONTROL_IMAGE;
48   m_bDynamicResourceAlloc=false;
49 }
50
51 CGUIImage::~CGUIImage(void)
52 {
53
54 }
55
56 void CGUIImage::UpdateVisibility(const CGUIListItem *item)
57 {
58   CGUIControl::UpdateVisibility(item);
59
60   // now that we've checked for conditional info, we can
61   // check for allocation
62   AllocateOnDemand();
63 }
64
65 void CGUIImage::UpdateInfo(const CGUIListItem *item)
66 {
67   if (m_info.IsConstant())
68     return; // nothing to do
69
70   // don't allow image to change while animating out
71   if (HasRendered() && IsAnimating(ANIM_TYPE_HIDDEN) && !IsVisibleFromSkin())
72     return;
73
74   if (item)
75     SetFileName(m_info.GetItemLabel(item, true));
76   else
77     SetFileName(m_info.GetLabel(m_dwParentID, true));
78 }
79
80 void CGUIImage::AllocateOnDemand()
81 {
82   // if we're hidden, we can free our resources and return
83   if (!IsVisible() && m_visible != DELAYED)
84   {
85     if (m_bDynamicResourceAlloc && m_texture.IsAllocated())
86       FreeResourcesButNotAnims();
87     return;
88   }
89
90   // either visible or delayed - we need the resources allocated in either case
91   if (!m_texture.IsAllocated())
92     AllocResources();
93 }
94
95 void CGUIImage::Render()
96 {
97   if (!IsVisible()) return;
98
99   // check whether our image failed to allocate, and if so drop back to the fallback image
100   if (m_texture.FailedToAlloc() && !m_texture.GetFileName().Equals(m_info.GetFallback()))
101     m_texture.SetFileName(m_info.GetFallback());
102   
103   if (m_crossFadeTime)
104   {
105     // make sure our texture has started allocating
106     m_texture.AllocResources();
107
108     // compute the frame time
109     unsigned int frameTime = 0;
110     unsigned int currentTime = timeGetTime();
111     if (m_lastRenderTime)
112       frameTime = currentTime - m_lastRenderTime;
113     m_lastRenderTime = currentTime;
114
115     if (m_fadingTextures.size())  // have some fading images
116     { // anything other than the last old texture needs to be faded out as per usual
117       for (vector<CFadingTexture *>::iterator i = m_fadingTextures.begin(); i != m_fadingTextures.end() - 1;)
118       {
119         if (!RenderFading(*i, frameTime))
120           i = m_fadingTextures.erase(i);
121         else
122           i++;
123       }
124
125       if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty())
126       { // fade out the last one as well
127         if (!RenderFading(m_fadingTextures[m_fadingTextures.size() - 1], frameTime))
128           m_fadingTextures.erase(m_fadingTextures.end() - 1);
129       }
130       else
131       { // keep the last one fading in
132         CFadingTexture *texture = m_fadingTextures[m_fadingTextures.size() - 1];
133         texture->m_fadeTime += frameTime;
134         if (texture->m_fadeTime > m_crossFadeTime)
135           texture->m_fadeTime = m_crossFadeTime;
136         texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime));
137         texture->m_texture->SetDiffuseColor(m_diffuseColor);
138         texture->m_texture->Render();
139       }
140     }
141
142     if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty())
143     { // fade the new one in
144       m_currentFadeTime += frameTime;
145       if (m_currentFadeTime > m_crossFadeTime || frameTime == 0) // for if we allocate straight away on creation
146         m_currentFadeTime = m_crossFadeTime;
147     }
148     m_texture.SetAlpha(GetFadeLevel(m_currentFadeTime));
149   }
150
151   m_texture.SetDiffuseColor(m_diffuseColor);
152   m_texture.Render();
153
154   CGUIControl::Render();
155 }
156
157 bool CGUIImage::RenderFading(CGUIImage::CFadingTexture *texture, unsigned int frameTime)
158 {
159   assert(texture);
160   if (texture->m_fadeTime <= frameTime)
161   { // time to kill off the texture
162     delete texture;
163     return false;
164   }
165   // render this texture
166   texture->m_fadeTime -= frameTime;
167   texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime));
168   texture->m_texture->SetDiffuseColor(m_diffuseColor);
169   texture->m_texture->Render();
170   return true;
171 }
172
173 bool CGUIImage::OnAction(const CAction &action)
174 {
175   return false;
176 }
177
178 bool CGUIImage::OnMessage(CGUIMessage& message)
179 {
180   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
181   {
182     if (!m_info.IsConstant())
183       FreeTextures(true); // true as we want to free the texture immediately
184     return true;
185   }
186   return CGUIControl::OnMessage(message);
187 }
188
189 void CGUIImage::AllocResources()
190 {
191   if (m_texture.GetFileName().IsEmpty())
192     return;
193
194   CGUIControl::AllocResources();
195   m_texture.AllocResources();
196 }
197
198 void CGUIImage::FreeTextures(bool immediately /* = false */)
199 {
200   m_texture.FreeResources(immediately);
201   for (unsigned int i = 0; i < m_fadingTextures.size(); i++)
202     delete m_fadingTextures[i];
203   m_fadingTextures.clear();
204 }
205
206 void CGUIImage::FreeResources()
207 {
208   FreeTextures();
209   CGUIControl::FreeResources();
210 }
211
212 // WORKAROUND - we are currently resetting all animations when this is called, which shouldn't be the case
213 //              see CGUIControl::FreeResources() - this needs remedying.
214 void CGUIImage::FreeResourcesButNotAnims()
215 {
216   FreeTextures();
217   m_bAllocated=false;
218   m_hasRendered = false;
219 }
220
221 void CGUIImage::DynamicResourceAlloc(bool bOnOff)
222 {
223   m_bDynamicResourceAlloc = bOnOff;
224   m_texture.DynamicResourceAlloc(bOnOff);
225   CGUIControl::DynamicResourceAlloc(bOnOff);
226 }
227
228 bool CGUIImage::CanFocus() const
229 {
230   return false;
231 }
232
233 float CGUIImage::GetTextureWidth() const
234 {
235   return m_texture.GetTextureWidth();
236 }
237
238 float CGUIImage::GetTextureHeight() const
239 {
240   return m_texture.GetTextureHeight();
241 }
242
243 const CStdString &CGUIImage::GetFileName() const
244 {
245   return m_texture.GetFileName();
246 }
247
248 void CGUIImage::SetAspectRatio(const CAspectRatio &aspect)
249 {
250   m_texture.SetAspectRatio(aspect);
251 }
252
253 void CGUIImage::SetCrossFade(unsigned int time)
254 {
255   m_crossFadeTime = time;
256   if (!m_crossFadeTime && m_texture.IsLazyLoaded() && !m_info.GetFallback().IsEmpty())
257     m_crossFadeTime = 1;
258 }
259
260 void CGUIImage::SetFileName(const CStdString& strFileName, bool setConstant)
261 {
262   if (setConstant)
263     m_info.SetLabel(strFileName, "");
264
265   if (m_crossFadeTime)
266   {
267     // set filename on the next texture
268     if (m_currentTexture.Equals(strFileName))
269       return; // nothing to do - we already have this image
270
271     if (m_texture.ReadyToRender() || m_texture.GetFileName().IsEmpty())
272     { // save the current image
273       m_fadingTextures.push_back(new CFadingTexture(m_texture, m_currentFadeTime));
274     }
275     m_currentFadeTime = 0;
276   }
277   if (!m_currentTexture.Equals(strFileName))
278   { // texture is changing - attempt to load it, and save the name in m_currentTexture.
279     // we'll check whether it loaded or not in Render()
280     m_currentTexture = strFileName;
281     m_texture.SetFileName(m_currentTexture);
282   }
283 }
284
285 #ifdef _DEBUG
286 void CGUIImage::DumpTextureUse()
287 {
288   if (m_texture.IsAllocated())
289   {
290     if (GetID())
291       CLog::Log(LOGDEBUG, "Image control %u using texture %s",
292                 GetID(), m_texture.GetFileName().c_str());
293     else
294       CLog::Log(LOGDEBUG, "Using texture %s", m_texture.GetFileName().c_str());
295   }
296 }
297 #endif
298
299 void CGUIImage::SetWidth(float width)
300 {
301   m_texture.SetWidth(width);
302   CGUIControl::SetWidth(m_texture.GetWidth());
303 }
304
305 void CGUIImage::SetHeight(float height)
306 {
307   m_texture.SetHeight(height);
308   CGUIControl::SetHeight(m_texture.GetHeight());
309 }
310
311 void CGUIImage::SetPosition(float posX, float posY)
312 {
313   m_texture.SetPosition(posX, posY);
314   CGUIControl::SetPosition(posX, posY);
315 }
316
317 void CGUIImage::SetInfo(const CGUIInfoLabel &info)
318 {
319   m_info = info;
320   // a constant image never needs updating
321   if (m_info.IsConstant())
322     m_texture.SetFileName(m_info.GetLabel(0));
323 }
324
325 unsigned char CGUIImage::GetFadeLevel(unsigned int time) const
326 {
327   float amount = (float)time / m_crossFadeTime;
328   // we want a semi-transparent image, so we need to use a more complicated
329   // fade technique.  Assuming a black background (not generally true, but still...)
330   // we have
331   // b(t) = [a - b(1-t)*a] / a*(1-b(1-t)*a),
332   // where a = alpha, and b(t):[0,1] -> [0,1] is the blend function.
333   // solving, we get
334   // b(t) = [1 - (1-a)^t] / a
335   const float alpha = 0.7f;
336   return (unsigned char)(255.0f * (1 - pow(1-alpha, amount))/alpha);
337 }