2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "TextureManager.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"
31 #include "utils/TimeUtils.h"
33 #include "threads/SystemClock.h"
34 #include "filesystem/File.h"
35 #include "filesystem/Directory.h"
42 /************************************************************************/
44 /************************************************************************/
45 CTextureArray::CTextureArray(int width, int height, int loops, bool texCoordsArePixels)
53 m_texCoordsArePixels = false;
56 CTextureArray::CTextureArray()
61 CTextureArray::~CTextureArray()
66 unsigned int CTextureArray::size() const
68 return m_textures.size();
72 void CTextureArray::Reset()
82 m_texCoordsArePixels = false;
85 void CTextureArray::Add(CBaseTexture *texture, int delay)
90 m_textures.push_back(texture);
91 m_delays.push_back(delay ? delay * 2 : 100);
93 m_texWidth = texture->GetTextureWidth();
94 m_texHeight = texture->GetTextureHeight();
95 m_texCoordsArePixels = false;
98 void CTextureArray::Set(CBaseTexture *texture, int width, int height)
100 assert(!m_textures.size()); // don't try and set a texture if we already have one!
103 m_orientation = texture ? texture->GetOrientation() : 0;
107 void CTextureArray::Free()
109 CSingleLock lock(g_graphicsContext);
110 for (unsigned int i = 0; i < m_textures.size(); i++)
112 delete m_textures[i];
122 /************************************************************************/
124 /************************************************************************/
126 CTextureMap::CTextureMap()
129 m_referenceCount = 0;
133 CTextureMap::CTextureMap(const CStdString& textureName, int width, int height, int loops)
134 : m_texture(width, height, loops)
136 m_textureName = textureName;
137 m_referenceCount = 0;
141 CTextureMap::~CTextureMap()
146 bool CTextureMap::Release()
148 if (!m_texture.m_textures.size())
150 if (!m_referenceCount)
154 if (!m_referenceCount)
161 const CStdString& CTextureMap::GetName() const
163 return m_textureName;
166 const CTextureArray& CTextureMap::GetTexture()
172 void CTextureMap::Dump() const
174 if (!m_referenceCount)
175 return; // nothing to see here
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());
181 unsigned int CTextureMap::GetMemoryUsage() const
186 void CTextureMap::Flush()
188 if (!m_referenceCount)
193 void CTextureMap::FreeTexture()
198 bool CTextureMap::IsEmpty() const
200 return m_texture.m_textures.size() == 0;
203 void CTextureMap::Add(CBaseTexture* texture, int delay)
205 m_texture.Add(texture, delay);
208 m_memUsage += sizeof(CTexture) + (texture->GetTextureWidth() * texture->GetTextureHeight() * 4);
211 /************************************************************************/
213 /************************************************************************/
214 CGUITextureManager::CGUITextureManager(void)
216 // we set the theme bundle to be the first bundle (thus prioritizing it)
217 m_TexBundle[0].SetThemeBundle(true);
220 CGUITextureManager::~CGUITextureManager(void)
225 /************************************************************************/
227 /************************************************************************/
228 bool CGUITextureManager::CanLoad(const CStdString &texturePath)
230 if (texturePath == "-")
233 if (!CURL::IsFullPath(texturePath))
234 return true; // assume we have it
236 // we can't (or shouldn't) be loading from remote paths, so check these
237 return URIUtils::IsHD(texturePath);
240 bool CGUITextureManager::HasTexture(const CStdString &textureName, CStdString *path, int *bundle, int *size)
243 if (bundle) *bundle = -1;
245 if (path) *path = textureName;
247 if (!CanLoad(textureName))
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)
254 CTextureMap *pMap = m_vecTextures[i];
255 if (pMap->GetName() == textureName)
262 for (int i = 0; i < 2; i++)
264 if (m_TexBundle[i].HasFile(bundledName))
266 if (bundle) *bundle = i;
271 CStdString fullPath = GetTexturePath(textureName);
275 return !fullPath.IsEmpty();
278 const CTextureArray& CGUITextureManager::Load(const CStdString& strTextureName, bool checkBundleOnly /*= false */)
281 static CTextureArray emptyTexture;
284 if (!HasTexture(strTextureName, &strPath, &bundle, &size))
287 if (size) // we found the texture
289 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
291 CTextureMap *pMap = m_vecTextures[i];
292 if (pMap->GetName() == strTextureName)
294 //CLog::Log(LOGDEBUG, "Total memusage %u", GetMemoryUsage());
295 return pMap->GetTexture();
298 // Whoops, not there.
302 for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i)
304 CTextureMap* pMap = i->first;
305 if (pMap->GetName() == strTextureName)
307 m_vecTextures.push_back(pMap);
308 m_unusedTextures.erase(i);
309 return pMap->GetTexture();
313 if (checkBundleOnly && bundle == -1)
316 //Lock here, we will do stuff that could break rendering
317 CSingleLock lock(g_graphicsContext);
321 start = CurrentHostCounter();
324 if (strPath.Right(4).ToLower() == ".gif")
330 CBaseTexture **pTextures;
331 int nLoops = 0, width = 0, height = 0;
333 int nImages = m_TexBundle[bundle].LoadAnim(strTextureName, &pTextures, width, height, nLoops, &Delay);
336 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
342 pMap = new CTextureMap(strTextureName, width, height, nLoops);
343 for (int iImage = 0; iImage < nImages; ++iImage)
345 pMap->Add(pTextures[iImage], Delay[iImage]);
353 CAnimatedGifSet AnimatedGifSet;
354 int iImages = AnimatedGifSet.LoadGIF(strPath.c_str());
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());
362 int iWidth = AnimatedGifSet.FrameWidth;
363 int iHeight = AnimatedGifSet.FrameHeight;
366 COLOR *palette = AnimatedGifSet.m_vecimg[0]->Palette;
367 // set the alpha values to fully opaque
368 for (int i = 0; i < 256; i++)
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;
374 pMap = new CTextureMap(strTextureName, iWidth, iHeight, AnimatedGifSet.nLoops);
376 for (int iImage = 0; iImage < iImages; iImage++)
378 CTexture *glTexture = new CTexture();
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);
385 } // of for (int iImage=0; iImage < iImages; iImage++)
390 end = CurrentHostCounter();
391 freq = CurrentHostFrequency();
393 sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
394 OutputDebugString(temp);
397 m_vecTextures.push_back(pMap);
398 return pMap->GetTexture();
399 } // of if (strPath.Right(4).ToLower()==".gif")
401 CBaseTexture *pTexture = NULL;
402 int width = 0, height = 0;
405 if (!m_TexBundle[bundle].LoadTexture(strTextureName, &pTexture, width, height))
407 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
413 pTexture = CBaseTexture::LoadFromFile(strPath);
416 width = pTexture->GetWidth();
417 height = pTexture->GetHeight();
420 if (!pTexture) return emptyTexture;
422 CTextureMap* pMap = new CTextureMap(strTextureName, width, height, 0);
423 pMap->Add(pTexture, 100);
424 m_vecTextures.push_back(pMap);
426 #ifdef _DEBUG_TEXTURES
428 end = CurrentHostCounter();
429 freq = CurrentHostFrequency();
431 sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
432 OutputDebugString(temp);
435 return pMap->GetTexture();
439 void CGUITextureManager::ReleaseTexture(const CStdString& strTextureName)
441 CSingleLock lock(g_graphicsContext);
444 i = m_vecTextures.begin();
445 while (i != m_vecTextures.end())
447 CTextureMap* pMap = *i;
448 if (pMap->GetName() == strTextureName)
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);
461 CLog::Log(LOGWARNING, "%s: Unable to release texture %s", __FUNCTION__, strTextureName.c_str());
464 void CGUITextureManager::FreeUnusedTextures(unsigned int timeDelay)
466 unsigned int currFrameTime = XbmcThreads::SystemClockMillis();
467 CSingleLock lock(g_graphicsContext);
468 for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end();)
470 if (currFrameTime - i->second >= timeDelay)
473 i = m_unusedTextures.erase(i);
479 #if defined(HAS_GL) || defined(HAS_GLES)
480 for (unsigned int i = 0; i < m_unusedHwTextures.size(); ++i)
482 glDeleteTextures(1, (GLuint*) &m_unusedHwTextures[i]);
485 m_unusedHwTextures.clear();
488 void CGUITextureManager::ReleaseHwTexture(unsigned int texture)
490 CSingleLock lock(g_graphicsContext);
491 m_unusedHwTextures.push_back(texture);
494 void CGUITextureManager::Cleanup()
496 CSingleLock lock(g_graphicsContext);
499 i = m_vecTextures.begin();
500 while (i != m_vecTextures.end())
502 CTextureMap* pMap = *i;
503 CLog::Log(LOGWARNING, "%s: Having to cleanup texture %s", __FUNCTION__, pMap->GetName().c_str());
505 i = m_vecTextures.erase(i);
507 for (int i = 0; i < 2; i++)
508 m_TexBundle[i].Cleanup();
509 FreeUnusedTextures();
512 void CGUITextureManager::Dump() const
514 CStdString strLog = StringUtils::Format("total texturemaps size:%i\n", m_vecTextures.size());
515 OutputDebugString(strLog.c_str());
517 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
519 const CTextureMap* pMap = m_vecTextures[i];
520 if (!pMap->IsEmpty())
525 void CGUITextureManager::Flush()
527 CSingleLock lock(g_graphicsContext);
530 i = m_vecTextures.begin();
531 while (i != m_vecTextures.end())
533 CTextureMap* pMap = *i;
535 if (pMap->IsEmpty() )
538 i = m_vecTextures.erase(i);
547 unsigned int CGUITextureManager::GetMemoryUsage() const
549 unsigned int memUsage = 0;
550 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
552 memUsage += m_vecTextures[i]->GetMemoryUsage();
557 void CGUITextureManager::SetTexturePath(const CStdString &texturePath)
559 CSingleLock lock(m_section);
560 m_texturePaths.clear();
561 AddTexturePath(texturePath);
564 void CGUITextureManager::AddTexturePath(const CStdString &texturePath)
566 CSingleLock lock(m_section);
567 if (!texturePath.IsEmpty())
568 m_texturePaths.push_back(texturePath);
571 void CGUITextureManager::RemoveTexturePath(const CStdString &texturePath)
573 CSingleLock lock(m_section);
574 for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it)
576 if (*it == texturePath)
578 m_texturePaths.erase(it);
584 CStdString CGUITextureManager::GetTexturePath(const CStdString &textureName, bool directory /* = false */)
586 if (CURL::IsFullPath(textureName))
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)
593 CStdString path = URIUtils::AddFileToFolder(it->c_str(), "media");
594 path = URIUtils::AddFileToFolder(path, textureName);
597 if (XFILE::CDirectory::Exists(path))
602 if (XFILE::CFile::Exists(path))
610 void CGUITextureManager::GetBundledTexturesFromPath(const CStdString& texturePath, std::vector<CStdString> &items)
612 m_TexBundle[0].GetTexturesFromPath(texturePath, items);
614 m_TexBundle[1].GetTexturesFromPath(texturePath, items);