Merge pull request #4539 from Matricom/amcodec
[vuplus_xbmc] / xbmc / guilib / TextureManager.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 "TextureManager.h"
22 #include "Texture.h"
23 #include "AnimatedGif.h"
24 #include "GraphicContext.h"
25 #include "threads/SingleLock.h"
26 #include "utils/log.h"
27 #include "utils/URIUtils.h"
28 #include "utils/StringUtils.h"
29 #include "addons/Skin.h"
30 #ifdef _DEBUG
31 #include "utils/TimeUtils.h"
32 #endif
33 #include "threads/SystemClock.h"
34 #include "filesystem/File.h"
35 #include "filesystem/Directory.h"
36 #include "URL.h"
37 #include <assert.h>
38
39 #if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV2)
40 #include "windowing/WindowingFactory.h" // for g_Windowing in CGUITextureManager::FreeUnusedTextures
41 #endif
42
43 using namespace std;
44
45
46 /************************************************************************/
47 /*                                                                      */
48 /************************************************************************/
49 CTextureArray::CTextureArray(int width, int height, int loops,  bool texCoordsArePixels)
50 {
51   m_width = width;
52   m_height = height;
53   m_loops = loops;
54   m_orientation = 0;
55   m_texWidth = 0;
56   m_texHeight = 0;
57   m_texCoordsArePixels = false;
58 }
59
60 CTextureArray::CTextureArray()
61 {
62   Reset();
63 }
64
65 CTextureArray::~CTextureArray()
66 {
67
68 }
69
70 unsigned int CTextureArray::size() const
71 {
72   return m_textures.size();
73 }
74
75
76 void CTextureArray::Reset()
77 {
78   m_textures.clear();
79   m_delays.clear();
80   m_width = 0;
81   m_height = 0;
82   m_loops = 0;
83   m_orientation = 0;
84   m_texWidth = 0;
85   m_texHeight = 0;
86   m_texCoordsArePixels = false;
87 }
88
89 void CTextureArray::Add(CBaseTexture *texture, int delay)
90 {
91   if (!texture)
92     return;
93
94   m_textures.push_back(texture);
95   m_delays.push_back(delay ? delay * 2 : 100);
96
97   m_texWidth = texture->GetTextureWidth();
98   m_texHeight = texture->GetTextureHeight();
99   m_texCoordsArePixels = false;
100 }
101
102 void CTextureArray::Set(CBaseTexture *texture, int width, int height)
103 {
104   assert(!m_textures.size()); // don't try and set a texture if we already have one!
105   m_width = width;
106   m_height = height;
107   m_orientation = texture ? texture->GetOrientation() : 0;
108   Add(texture, 100);
109 }
110
111 void CTextureArray::Free()
112 {
113   CSingleLock lock(g_graphicsContext);
114   for (unsigned int i = 0; i < m_textures.size(); i++)
115   {
116     delete m_textures[i];
117   }
118
119   m_textures.clear();
120   m_delays.clear();
121
122   Reset();
123 }
124
125
126 /************************************************************************/
127 /*                                                                      */
128 /************************************************************************/
129
130 CTextureMap::CTextureMap()
131 {
132   m_textureName = "";
133   m_referenceCount = 0;
134   m_memUsage = 0;
135 }
136
137 CTextureMap::CTextureMap(const CStdString& textureName, int width, int height, int loops)
138 : m_texture(width, height, loops)
139 {
140   m_textureName = textureName;
141   m_referenceCount = 0;
142   m_memUsage = 0;
143 }
144
145 CTextureMap::~CTextureMap()
146 {
147   FreeTexture();
148 }
149
150 bool CTextureMap::Release()
151 {
152   if (!m_texture.m_textures.size())
153     return true;
154   if (!m_referenceCount)
155     return true;
156
157   m_referenceCount--;
158   if (!m_referenceCount)
159   {
160     return true;
161   }
162   return false;
163 }
164
165 const CStdString& CTextureMap::GetName() const
166 {
167   return m_textureName;
168 }
169
170 const CTextureArray& CTextureMap::GetTexture()
171 {
172   m_referenceCount++;
173   return m_texture;
174 }
175
176 void CTextureMap::Dump() const
177 {
178   if (!m_referenceCount)
179     return;   // nothing to see here
180
181   CStdString strLog = StringUtils::Format("  texture:%s has %i frames %i refcount\n", m_textureName.c_str(), m_texture.m_textures.size(), m_referenceCount);
182   OutputDebugString(strLog.c_str());
183 }
184
185 unsigned int CTextureMap::GetMemoryUsage() const
186 {
187   return m_memUsage;
188 }
189
190 void CTextureMap::Flush()
191 {
192   if (!m_referenceCount)
193     FreeTexture();
194 }
195
196
197 void CTextureMap::FreeTexture()
198 {
199   m_texture.Free();
200 }
201
202 bool CTextureMap::IsEmpty() const
203 {
204   return m_texture.m_textures.size() == 0;
205 }
206
207 void CTextureMap::Add(CBaseTexture* texture, int delay)
208 {
209   m_texture.Add(texture, delay);
210
211   if (texture)
212     m_memUsage += sizeof(CTexture) + (texture->GetTextureWidth() * texture->GetTextureHeight() * 4);
213 }
214
215 /************************************************************************/
216 /*                                                                      */
217 /************************************************************************/
218 CGUITextureManager::CGUITextureManager(void)
219 {
220   // we set the theme bundle to be the first bundle (thus prioritizing it)
221   m_TexBundle[0].SetThemeBundle(true);
222 }
223
224 CGUITextureManager::~CGUITextureManager(void)
225 {
226   Cleanup();
227 }
228
229 /************************************************************************/
230 /*                                                                      */
231 /************************************************************************/
232 bool CGUITextureManager::CanLoad(const CStdString &texturePath)
233 {
234   if (texturePath == "-")
235     return false;
236
237   if (!CURL::IsFullPath(texturePath))
238     return true;  // assume we have it
239
240   // we can't (or shouldn't) be loading from remote paths, so check these
241   return URIUtils::IsHD(texturePath);
242 }
243
244 bool CGUITextureManager::HasTexture(const CStdString &textureName, CStdString *path, int *bundle, int *size)
245 {
246   // default values
247   if (bundle) *bundle = -1;
248   if (size) *size = 0;
249   if (path) *path = textureName;
250
251   if (!CanLoad(textureName))
252     return false;
253
254   // Check our loaded and bundled textures - we store in bundles using \\.
255   CStdString bundledName = CTextureBundle::Normalize(textureName);
256   for (int i = 0; i < (int)m_vecTextures.size(); ++i)
257   {
258     CTextureMap *pMap = m_vecTextures[i];
259     if (pMap->GetName() == textureName)
260     {
261       if (size) *size = 1;
262       return true;
263     }
264   }
265
266   for (int i = 0; i < 2; i++)
267   {
268     if (m_TexBundle[i].HasFile(bundledName))
269     {
270       if (bundle) *bundle = i;
271       return true;
272     }
273   }
274
275   CStdString fullPath = GetTexturePath(textureName);
276   if (path)
277     *path = fullPath;
278
279   return !fullPath.empty();
280 }
281
282 const CTextureArray& CGUITextureManager::Load(const CStdString& strTextureName, bool checkBundleOnly /*= false */)
283 {
284   CStdString strPath;
285   static CTextureArray emptyTexture;
286   int bundle = -1;
287   int size = 0;
288   if (!HasTexture(strTextureName, &strPath, &bundle, &size))
289     return emptyTexture;
290
291   if (size) // we found the texture
292   {
293     for (int i = 0; i < (int)m_vecTextures.size(); ++i)
294     {
295       CTextureMap *pMap = m_vecTextures[i];
296       if (pMap->GetName() == strTextureName)
297       {
298         //CLog::Log(LOGDEBUG, "Total memusage %u", GetMemoryUsage());
299         return pMap->GetTexture();
300       }
301     }
302     // Whoops, not there.
303     return emptyTexture;
304   }
305
306   for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i)
307   {
308     CTextureMap* pMap = i->first;
309     if (pMap->GetName() == strTextureName && i->second > 0)
310     {
311       m_vecTextures.push_back(pMap);
312       m_unusedTextures.erase(i);
313       return pMap->GetTexture();
314     }
315   }
316
317   if (checkBundleOnly && bundle == -1)
318     return emptyTexture;
319
320   //Lock here, we will do stuff that could break rendering
321   CSingleLock lock(g_graphicsContext);
322
323 #ifdef _DEBUG
324   int64_t start;
325   start = CurrentHostCounter();
326 #endif
327
328   if (StringUtils::EndsWithNoCase(strPath, ".gif"))
329   {
330     CTextureMap* pMap;
331
332     if (bundle >= 0)
333     {
334       CBaseTexture **pTextures;
335       int nLoops = 0, width = 0, height = 0;
336       int* Delay;
337       int nImages = m_TexBundle[bundle].LoadAnim(strTextureName, &pTextures, width, height, nLoops, &Delay);
338       if (!nImages)
339       {
340         CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
341         delete [] pTextures;
342         delete [] Delay;
343         return emptyTexture;
344       }
345
346       pMap = new CTextureMap(strTextureName, width, height, nLoops);
347       for (int iImage = 0; iImage < nImages; ++iImage)
348       {
349         pMap->Add(pTextures[iImage], Delay[iImage]);
350       }
351
352       delete [] pTextures;
353       delete [] Delay;
354     }
355     else
356     {
357       CAnimatedGifSet AnimatedGifSet;
358       int iImages = AnimatedGifSet.LoadGIF(strPath.c_str());
359       if (iImages == 0)
360       {
361         if (StringUtils::StartsWith(strPath, g_SkinInfo->Path()))
362           CLog::Log(LOGERROR, "Texture manager unable to load file: %s", strPath.c_str());
363         return emptyTexture;
364       }
365       int iWidth = AnimatedGifSet.FrameWidth;
366       int iHeight = AnimatedGifSet.FrameHeight;
367
368       // fixup our palette
369       COLOR *palette = AnimatedGifSet.m_vecimg[0]->Palette;
370       // set the alpha values to fully opaque
371       for (int i = 0; i < 256; i++)
372         palette[i].x = 0xff;
373       // and set the transparent colour
374       if (AnimatedGifSet.m_vecimg[0]->Transparency && AnimatedGifSet.m_vecimg[0]->Transparent >= 0)
375         palette[AnimatedGifSet.m_vecimg[0]->Transparent].x = 0;
376
377       pMap = new CTextureMap(strTextureName, iWidth, iHeight, AnimatedGifSet.nLoops);
378
379       for (int iImage = 0; iImage < iImages; iImage++)
380       {
381         CTexture *glTexture = new CTexture();
382         if (glTexture)
383         {
384           CAnimatedGif* pImage = AnimatedGifSet.m_vecimg[iImage];
385           glTexture->LoadPaletted(pImage->Width, pImage->Height, pImage->BytesPerRow, XB_FMT_A8R8G8B8, (unsigned char *)pImage->Raster, palette);
386           pMap->Add(glTexture, pImage->Delay);
387         }
388       } // of for (int iImage=0; iImage < iImages; iImage++)
389     }
390
391 #ifdef _DEBUG
392     int64_t end, freq;
393     end = CurrentHostCounter();
394     freq = CurrentHostFrequency();
395     char temp[200];
396     sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
397     OutputDebugString(temp);
398 #endif
399
400     m_vecTextures.push_back(pMap);
401     return pMap->GetTexture();
402   } // of if (strPath.Right(4).ToLower()==".gif")
403
404   CBaseTexture *pTexture = NULL;
405   int width = 0, height = 0;
406   if (bundle >= 0)
407   {
408     if (!m_TexBundle[bundle].LoadTexture(strTextureName, &pTexture, width, height))
409     {
410       CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
411       return emptyTexture;
412     }
413   }
414   else
415   {
416     pTexture = CBaseTexture::LoadFromFile(strPath);
417     if (!pTexture)
418       return emptyTexture;
419     width = pTexture->GetWidth();
420     height = pTexture->GetHeight();
421   }
422
423   if (!pTexture) return emptyTexture;
424
425   CTextureMap* pMap = new CTextureMap(strTextureName, width, height, 0);
426   pMap->Add(pTexture, 100);
427   m_vecTextures.push_back(pMap);
428
429 #ifdef _DEBUG_TEXTURES
430   int64_t end, freq;
431   end = CurrentHostCounter();
432   freq = CurrentHostFrequency();
433   char temp[200];
434   sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
435   OutputDebugString(temp);
436 #endif
437
438   return pMap->GetTexture();
439 }
440
441
442 void CGUITextureManager::ReleaseTexture(const CStdString& strTextureName, bool immediately /*= false */)
443 {
444   CSingleLock lock(g_graphicsContext);
445
446   ivecTextures i;
447   i = m_vecTextures.begin();
448   while (i != m_vecTextures.end())
449   {
450     CTextureMap* pMap = *i;
451     if (pMap->GetName() == strTextureName)
452     {
453       if (pMap->Release())
454       {
455         //CLog::Log(LOGINFO, "  cleanup:%s", strTextureName.c_str());
456         // add to our textures to free
457         m_unusedTextures.push_back(make_pair(pMap, immediately ? 0 : XbmcThreads::SystemClockMillis()));
458         i = m_vecTextures.erase(i);
459       }
460       return;
461     }
462     ++i;
463   }
464   CLog::Log(LOGWARNING, "%s: Unable to release texture %s", __FUNCTION__, strTextureName.c_str());
465 }
466
467 void CGUITextureManager::FreeUnusedTextures(unsigned int timeDelay)
468 {
469   unsigned int currFrameTime = XbmcThreads::SystemClockMillis();
470   CSingleLock lock(g_graphicsContext);
471   for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end();)
472   {
473     if (currFrameTime - i->second >= timeDelay)
474     {
475       delete i->first;
476       i = m_unusedTextures.erase(i);
477     }
478     else
479       ++i;
480   }
481
482 #if defined(HAS_GL) || defined(HAS_GLES)
483   for (unsigned int i = 0; i < m_unusedHwTextures.size(); ++i)
484   {
485   // on ios the hw textures might be deleted from the os
486   // when XBMC is backgrounded (e.x. for backgrounded music playback)
487   // sanity check before delete in that case.
488 #if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV2)
489     if (!g_Windowing.IsBackgrounded() || glIsTexture(m_unusedHwTextures[i]))
490 #endif
491       glDeleteTextures(1, (GLuint*) &m_unusedHwTextures[i]);
492   }
493 #endif
494   m_unusedHwTextures.clear();
495 }
496
497 void CGUITextureManager::ReleaseHwTexture(unsigned int texture)
498 {
499   CSingleLock lock(g_graphicsContext);
500   m_unusedHwTextures.push_back(texture);
501 }
502
503 void CGUITextureManager::Cleanup()
504 {
505   CSingleLock lock(g_graphicsContext);
506
507   ivecTextures i;
508   i = m_vecTextures.begin();
509   while (i != m_vecTextures.end())
510   {
511     CTextureMap* pMap = *i;
512     CLog::Log(LOGWARNING, "%s: Having to cleanup texture %s", __FUNCTION__, pMap->GetName().c_str());
513     delete pMap;
514     i = m_vecTextures.erase(i);
515   }
516   for (int i = 0; i < 2; i++)
517     m_TexBundle[i].Cleanup();
518   FreeUnusedTextures();
519 }
520
521 void CGUITextureManager::Dump() const
522 {
523   CStdString strLog = StringUtils::Format("total texturemaps size:%i\n", m_vecTextures.size());
524   OutputDebugString(strLog.c_str());
525
526   for (int i = 0; i < (int)m_vecTextures.size(); ++i)
527   {
528     const CTextureMap* pMap = m_vecTextures[i];
529     if (!pMap->IsEmpty())
530       pMap->Dump();
531   }
532 }
533
534 void CGUITextureManager::Flush()
535 {
536   CSingleLock lock(g_graphicsContext);
537
538   ivecTextures i;
539   i = m_vecTextures.begin();
540   while (i != m_vecTextures.end())
541   {
542     CTextureMap* pMap = *i;
543     pMap->Flush();
544     if (pMap->IsEmpty() )
545     {
546       delete pMap;
547       i = m_vecTextures.erase(i);
548     }
549     else
550     {
551       ++i;
552     }
553   }
554 }
555
556 unsigned int CGUITextureManager::GetMemoryUsage() const
557 {
558   unsigned int memUsage = 0;
559   for (int i = 0; i < (int)m_vecTextures.size(); ++i)
560   {
561     memUsage += m_vecTextures[i]->GetMemoryUsage();
562   }
563   return memUsage;
564 }
565
566 void CGUITextureManager::SetTexturePath(const CStdString &texturePath)
567 {
568   CSingleLock lock(m_section);
569   m_texturePaths.clear();
570   AddTexturePath(texturePath);
571 }
572
573 void CGUITextureManager::AddTexturePath(const CStdString &texturePath)
574 {
575   CSingleLock lock(m_section);
576   if (!texturePath.empty())
577     m_texturePaths.push_back(texturePath);
578 }
579
580 void CGUITextureManager::RemoveTexturePath(const CStdString &texturePath)
581 {
582   CSingleLock lock(m_section);
583   for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it)
584   {
585     if (*it == texturePath)
586     {
587       m_texturePaths.erase(it);
588       return;
589     }
590   }
591 }
592
593 CStdString CGUITextureManager::GetTexturePath(const CStdString &textureName, bool directory /* = false */)
594 {
595   if (CURL::IsFullPath(textureName))
596     return textureName;
597   else
598   { // texture doesn't include the full path, so check all fallbacks
599     CSingleLock lock(m_section);
600     for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it)
601     {
602       CStdString path = URIUtils::AddFileToFolder(it->c_str(), "media");
603       path = URIUtils::AddFileToFolder(path, textureName);
604       if (directory)
605       {
606         if (XFILE::CDirectory::Exists(path))
607           return path;
608       }
609       else
610       {
611         if (XFILE::CFile::Exists(path))
612           return path;
613       }
614     }
615   }
616   return "";
617 }
618
619 void CGUITextureManager::GetBundledTexturesFromPath(const CStdString& texturePath, std::vector<CStdString> &items)
620 {
621   m_TexBundle[0].GetTexturesFromPath(texturePath, items);
622   if (items.empty())
623     m_TexBundle[1].GetTexturesFromPath(texturePath, items);
624 }