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