[guilib] fix labelcontrols with auto width always being marked as dirty if they speci...
[vuplus_xbmc] / xbmc / guilib / GUIImage.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUIImage.h"
22 #include "TextureManager.h"
23 #include "utils/log.h"
24 #include "utils/TimeUtils.h"
25
26 using namespace std;
27
28 CGUIImage::CGUIImage(int parentID, int controlID, float posX, float posY, float width, float height, const CTextureInfo& texture)
29     : CGUIControl(parentID, controlID, 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), 
41   m_image(left.m_image),
42   m_info(left.m_info),
43   m_texture(left.m_texture),
44   m_fadingTextures(),
45   m_currentTexture(),
46   m_currentFallback()
47 {
48   m_crossFadeTime = left.m_crossFadeTime;
49   // defaults
50   m_currentFadeTime = 0;
51   m_lastRenderTime = 0;
52   ControlType = GUICONTROL_IMAGE;
53   m_bDynamicResourceAlloc=false;
54 }
55
56 CGUIImage::~CGUIImage(void)
57 {
58
59 }
60
61 void CGUIImage::UpdateVisibility(const CGUIListItem *item)
62 {
63   CGUIControl::UpdateVisibility(item);
64
65   // now that we've checked for conditional info, we can
66   // check for allocation
67   AllocateOnDemand();
68 }
69
70 void CGUIImage::UpdateInfo(const CGUIListItem *item)
71 {
72   if (m_info.IsConstant())
73     return; // nothing to do
74
75   // don't allow image to change while animating out
76   if (HasProcessed() && IsAnimating(ANIM_TYPE_HIDDEN) && !IsVisibleFromSkin())
77     return;
78
79   if (item)
80     SetFileName(m_info.GetItemLabel(item, true, &m_currentFallback));
81   else
82     SetFileName(m_info.GetLabel(m_parentID, true, &m_currentFallback));
83 }
84
85 void CGUIImage::AllocateOnDemand()
86 {
87   // if we're hidden, we can free our resources and return
88   if (!IsVisible() && m_visible != DELAYED)
89   {
90     if (m_bDynamicResourceAlloc && m_texture.IsAllocated())
91       FreeResourcesButNotAnims();
92     return;
93   }
94
95   // either visible or delayed - we need the resources allocated in either case
96   if (!m_texture.IsAllocated())
97     AllocResources();
98 }
99
100 void CGUIImage::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
101 {
102   // check whether our image failed to allocate, and if so drop back to the fallback image
103   if (m_texture.FailedToAlloc() && !m_texture.GetFileName().Equals(m_info.GetFallback()))
104   {
105     if (!m_currentFallback.empty() && !m_texture.GetFileName().Equals(m_currentFallback))
106       m_texture.SetFileName(m_currentFallback);
107     else
108       m_texture.SetFileName(m_info.GetFallback());
109   }
110
111   if (m_crossFadeTime)
112   {
113     // make sure our texture has started allocating
114     if (m_texture.AllocResources())
115       MarkDirtyRegion();
116
117     // compute the frame time
118     unsigned int frameTime = 0;
119     if (m_lastRenderTime)
120       frameTime = currentTime - m_lastRenderTime;
121     m_lastRenderTime = currentTime;
122
123     if (m_fadingTextures.size())  // have some fading images
124     { // anything other than the last old texture needs to be faded out as per usual
125       for (vector<CFadingTexture *>::iterator i = m_fadingTextures.begin(); i != m_fadingTextures.end() - 1;)
126       {
127         if (!ProcessFading(*i, frameTime, currentTime))
128           i = m_fadingTextures.erase(i);
129         else
130           ++i;
131       }
132
133       if (m_texture.ReadyToRender() || m_texture.GetFileName().empty())
134       { // fade out the last one as well
135         if (!ProcessFading(m_fadingTextures[m_fadingTextures.size() - 1], frameTime, currentTime))
136           m_fadingTextures.erase(m_fadingTextures.end() - 1);
137       }
138       else
139       { // keep the last one fading in
140         CFadingTexture *texture = m_fadingTextures[m_fadingTextures.size() - 1];
141         texture->m_fadeTime += frameTime;
142         if (texture->m_fadeTime > m_crossFadeTime)
143           texture->m_fadeTime = m_crossFadeTime;
144
145         if (texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime)))
146           MarkDirtyRegion();
147         if (texture->m_texture->SetDiffuseColor(m_diffuseColor))
148           MarkDirtyRegion();
149         if (texture->m_texture->Process(currentTime))
150           MarkDirtyRegion();
151       }
152     }
153
154     if (m_texture.ReadyToRender() || m_texture.GetFileName().empty())
155     { // fade the new one in
156       m_currentFadeTime += frameTime;
157       if (m_currentFadeTime > m_crossFadeTime || frameTime == 0) // for if we allocate straight away on creation
158         m_currentFadeTime = m_crossFadeTime;
159     }
160     if (m_texture.SetAlpha(GetFadeLevel(m_currentFadeTime)))
161       MarkDirtyRegion();
162   }
163
164   if (m_texture.SetDiffuseColor(m_diffuseColor))
165     MarkDirtyRegion();
166
167   if (m_texture.Process(currentTime))
168     MarkDirtyRegion();
169
170   CGUIControl::Process(currentTime, dirtyregions);
171 }
172
173 void CGUIImage::Render()
174 {
175   if (!IsVisible()) return;
176
177   for (vector<CFadingTexture *>::iterator itr = m_fadingTextures.begin(); itr != m_fadingTextures.end(); ++itr)
178     (*itr)->m_texture->Render();
179
180   m_texture.Render();
181
182   CGUIControl::Render();
183 }
184
185 bool CGUIImage::ProcessFading(CGUIImage::CFadingTexture *texture, unsigned int frameTime, unsigned int currentTime)
186 {
187   assert(texture);
188   if (texture->m_fadeTime <= frameTime)
189   { // time to kill off the texture
190     MarkDirtyRegion();
191     delete texture;
192     return false;
193   }
194   // render this texture
195   texture->m_fadeTime -= frameTime;
196
197   if (texture->m_texture->SetAlpha(GetFadeLevel(texture->m_fadeTime)))
198     MarkDirtyRegion();
199   if (texture->m_texture->SetDiffuseColor(m_diffuseColor))
200     MarkDirtyRegion();
201   if (texture->m_texture->Process(currentTime))
202     MarkDirtyRegion();
203
204   return true;
205 }
206
207 bool CGUIImage::OnAction(const CAction &action)
208 {
209   return false;
210 }
211
212 bool CGUIImage::OnMessage(CGUIMessage& message)
213 {
214   if (message.GetMessage() == GUI_MSG_REFRESH_THUMBS)
215   {
216     if (!m_info.IsConstant())
217       FreeTextures(true); // true as we want to free the texture immediately
218     return true;
219   }
220   return CGUIControl::OnMessage(message);
221 }
222
223 void CGUIImage::AllocResources()
224 {
225   if (m_texture.GetFileName().empty())
226     return;
227
228   CGUIControl::AllocResources();
229   m_texture.AllocResources();
230 }
231
232 void CGUIImage::FreeTextures(bool immediately /* = false */)
233 {
234   m_texture.FreeResources(immediately);
235   for (unsigned int i = 0; i < m_fadingTextures.size(); i++)
236     delete m_fadingTextures[i];
237   m_fadingTextures.clear();
238   m_currentTexture.clear();
239   if (!m_info.IsConstant()) // constant textures never change
240     m_texture.SetFileName("");
241 }
242
243 void CGUIImage::FreeResources(bool immediately)
244 {
245   FreeTextures(immediately);
246   CGUIControl::FreeResources(immediately);
247 }
248
249 void CGUIImage::SetInvalid()
250 {
251   m_texture.SetInvalid();
252   CGUIControl::SetInvalid();
253 }
254
255 // WORKAROUND - we are currently resetting all animations when this is called, which shouldn't be the case
256 //              see CGUIControl::FreeResources() - this needs remedying.
257 void CGUIImage::FreeResourcesButNotAnims()
258 {
259   FreeTextures();
260   m_bAllocated=false;
261   m_hasProcessed = false;
262 }
263
264 void CGUIImage::DynamicResourceAlloc(bool bOnOff)
265 {
266   m_bDynamicResourceAlloc = bOnOff;
267   m_texture.DynamicResourceAlloc(bOnOff);
268   CGUIControl::DynamicResourceAlloc(bOnOff);
269 }
270
271 bool CGUIImage::CanFocus() const
272 {
273   return false;
274 }
275
276 float CGUIImage::GetTextureWidth() const
277 {
278   return m_texture.GetTextureWidth();
279 }
280
281 float CGUIImage::GetTextureHeight() const
282 {
283   return m_texture.GetTextureHeight();
284 }
285
286 CRect CGUIImage::CalcRenderRegion() const
287 {
288   CRect region = m_texture.GetRenderRect();
289
290   for (vector<CFadingTexture *>::const_iterator itr = m_fadingTextures.begin(); itr != m_fadingTextures.end(); ++itr)
291     region.Union( (*itr)->m_texture->GetRenderRect() );
292
293   return CGUIControl::CalcRenderRegion().Intersect(region);
294 }
295
296 const CStdString &CGUIImage::GetFileName() const
297 {
298   return m_texture.GetFileName();
299 }
300
301 void CGUIImage::SetAspectRatio(const CAspectRatio &aspect)
302 {
303   m_texture.SetAspectRatio(aspect);
304 }
305
306 void CGUIImage::SetCrossFade(unsigned int time)
307 {
308   m_crossFadeTime = time;
309   if (!m_crossFadeTime && m_texture.IsLazyLoaded() && !m_info.GetFallback().empty())
310     m_crossFadeTime = 1;
311 }
312
313 void CGUIImage::SetFileName(const CStdString& strFileName, bool setConstant, const bool useCache)
314 {
315   if (setConstant)
316     m_info.SetLabel(strFileName, "", GetParentID());
317
318   // Set whether or not to use cache
319   m_texture.SetUseCache(useCache);
320
321   if (m_crossFadeTime)
322   {
323     // set filename on the next texture
324     if (m_currentTexture.Equals(strFileName))
325       return; // nothing to do - we already have this image
326
327     if (m_texture.ReadyToRender() || m_texture.GetFileName().empty())
328     { // save the current image
329       m_fadingTextures.push_back(new CFadingTexture(m_texture, m_currentFadeTime));
330       MarkDirtyRegion();
331     }
332     m_currentFadeTime = 0;
333   }
334   if (!m_currentTexture.Equals(strFileName))
335   { // texture is changing - attempt to load it, and save the name in m_currentTexture.
336     // we'll check whether it loaded or not in Render()
337     m_currentTexture = strFileName;
338     if (m_texture.SetFileName(m_currentTexture))
339       MarkDirtyRegion();
340   }
341 }
342
343 #ifdef _DEBUG
344 void CGUIImage::DumpTextureUse()
345 {
346   if (m_texture.IsAllocated())
347   {
348     if (GetID())
349       CLog::Log(LOGDEBUG, "Image control %u using texture %s",
350                 GetID(), m_texture.GetFileName().c_str());
351     else
352       CLog::Log(LOGDEBUG, "Using texture %s", m_texture.GetFileName().c_str());
353   }
354 }
355 #endif
356
357 void CGUIImage::SetWidth(float width)
358 {
359   m_texture.SetWidth(width);
360   CGUIControl::SetWidth(m_texture.GetWidth());
361 }
362
363 void CGUIImage::SetHeight(float height)
364 {
365   m_texture.SetHeight(height);
366   CGUIControl::SetHeight(m_texture.GetHeight());
367 }
368
369 void CGUIImage::SetPosition(float posX, float posY)
370 {
371   m_texture.SetPosition(posX, posY);
372   CGUIControl::SetPosition(posX, posY);
373 }
374
375 void CGUIImage::SetInfo(const CGUIInfoLabel &info)
376 {
377   m_info = info;
378   // a constant image never needs updating
379   if (m_info.IsConstant())
380     m_texture.SetFileName(m_info.GetLabel(0));
381 }
382
383 unsigned char CGUIImage::GetFadeLevel(unsigned int time) const
384 {
385   float amount = (float)time / m_crossFadeTime;
386   // we want a semi-transparent image, so we need to use a more complicated
387   // fade technique.  Assuming a black background (not generally true, but still...)
388   // we have
389   // b(t) = [a - b(1-t)*a] / a*(1-b(1-t)*a),
390   // where a = alpha, and b(t):[0,1] -> [0,1] is the blend function.
391   // solving, we get
392   // b(t) = [1 - (1-a)^t] / a
393   const float alpha = 0.7f;
394   return (unsigned char)(255.0f * (1 - pow(1-alpha, amount))/alpha);
395 }
396
397 CStdString CGUIImage::GetDescription(void) const
398 {
399   return GetFileName();
400 }
401