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"
39 #if defined(TARGET_DARWIN_IOS) && !defined(TARGET_DARWIN_IOS_ATV2)
40 #include "windowing/WindowingFactory.h" // for g_Windowing in CGUITextureManager::FreeUnusedTextures
46 /************************************************************************/
48 /************************************************************************/
49 CTextureArray::CTextureArray(int width, int height, int loops, bool texCoordsArePixels)
57 m_texCoordsArePixels = false;
60 CTextureArray::CTextureArray()
65 CTextureArray::~CTextureArray()
70 unsigned int CTextureArray::size() const
72 return m_textures.size();
76 void CTextureArray::Reset()
86 m_texCoordsArePixels = false;
89 void CTextureArray::Add(CBaseTexture *texture, int delay)
94 m_textures.push_back(texture);
95 m_delays.push_back(delay ? delay * 2 : 100);
97 m_texWidth = texture->GetTextureWidth();
98 m_texHeight = texture->GetTextureHeight();
99 m_texCoordsArePixels = false;
102 void CTextureArray::Set(CBaseTexture *texture, int width, int height)
104 assert(!m_textures.size()); // don't try and set a texture if we already have one!
107 m_orientation = texture ? texture->GetOrientation() : 0;
111 void CTextureArray::Free()
113 CSingleLock lock(g_graphicsContext);
114 for (unsigned int i = 0; i < m_textures.size(); i++)
116 delete m_textures[i];
126 /************************************************************************/
128 /************************************************************************/
130 CTextureMap::CTextureMap()
133 m_referenceCount = 0;
137 CTextureMap::CTextureMap(const CStdString& textureName, int width, int height, int loops)
138 : m_texture(width, height, loops)
140 m_textureName = textureName;
141 m_referenceCount = 0;
145 CTextureMap::~CTextureMap()
150 bool CTextureMap::Release()
152 if (!m_texture.m_textures.size())
154 if (!m_referenceCount)
158 if (!m_referenceCount)
165 const CStdString& CTextureMap::GetName() const
167 return m_textureName;
170 const CTextureArray& CTextureMap::GetTexture()
176 void CTextureMap::Dump() const
178 if (!m_referenceCount)
179 return; // nothing to see here
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());
185 unsigned int CTextureMap::GetMemoryUsage() const
190 void CTextureMap::Flush()
192 if (!m_referenceCount)
197 void CTextureMap::FreeTexture()
202 bool CTextureMap::IsEmpty() const
204 return m_texture.m_textures.size() == 0;
207 void CTextureMap::Add(CBaseTexture* texture, int delay)
209 m_texture.Add(texture, delay);
212 m_memUsage += sizeof(CTexture) + (texture->GetTextureWidth() * texture->GetTextureHeight() * 4);
215 /************************************************************************/
217 /************************************************************************/
218 CGUITextureManager::CGUITextureManager(void)
220 // we set the theme bundle to be the first bundle (thus prioritizing it)
221 m_TexBundle[0].SetThemeBundle(true);
224 CGUITextureManager::~CGUITextureManager(void)
229 /************************************************************************/
231 /************************************************************************/
232 bool CGUITextureManager::CanLoad(const CStdString &texturePath)
234 if (texturePath == "-")
237 if (!CURL::IsFullPath(texturePath))
238 return true; // assume we have it
240 // we can't (or shouldn't) be loading from remote paths, so check these
241 return URIUtils::IsHD(texturePath);
244 bool CGUITextureManager::HasTexture(const CStdString &textureName, CStdString *path, int *bundle, int *size)
247 if (bundle) *bundle = -1;
249 if (path) *path = textureName;
251 if (!CanLoad(textureName))
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)
258 CTextureMap *pMap = m_vecTextures[i];
259 if (pMap->GetName() == textureName)
266 for (int i = 0; i < 2; i++)
268 if (m_TexBundle[i].HasFile(bundledName))
270 if (bundle) *bundle = i;
275 CStdString fullPath = GetTexturePath(textureName);
279 return !fullPath.empty();
282 const CTextureArray& CGUITextureManager::Load(const CStdString& strTextureName, bool checkBundleOnly /*= false */)
285 static CTextureArray emptyTexture;
288 if (!HasTexture(strTextureName, &strPath, &bundle, &size))
291 if (size) // we found the texture
293 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
295 CTextureMap *pMap = m_vecTextures[i];
296 if (pMap->GetName() == strTextureName)
298 //CLog::Log(LOGDEBUG, "Total memusage %u", GetMemoryUsage());
299 return pMap->GetTexture();
302 // Whoops, not there.
306 for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end(); ++i)
308 CTextureMap* pMap = i->first;
309 if (pMap->GetName() == strTextureName && i->second > 0)
311 m_vecTextures.push_back(pMap);
312 m_unusedTextures.erase(i);
313 return pMap->GetTexture();
317 if (checkBundleOnly && bundle == -1)
320 //Lock here, we will do stuff that could break rendering
321 CSingleLock lock(g_graphicsContext);
325 start = CurrentHostCounter();
328 if (StringUtils::EndsWithNoCase(strPath, ".gif"))
334 CBaseTexture **pTextures;
335 int nLoops = 0, width = 0, height = 0;
337 int nImages = m_TexBundle[bundle].LoadAnim(strTextureName, &pTextures, width, height, nLoops, &Delay);
340 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
346 pMap = new CTextureMap(strTextureName, width, height, nLoops);
347 for (int iImage = 0; iImage < nImages; ++iImage)
349 pMap->Add(pTextures[iImage], Delay[iImage]);
357 CAnimatedGifSet AnimatedGifSet;
358 int iImages = AnimatedGifSet.LoadGIF(strPath.c_str());
361 if (StringUtils::StartsWith(strPath, g_SkinInfo->Path()))
362 CLog::Log(LOGERROR, "Texture manager unable to load file: %s", strPath.c_str());
365 int iWidth = AnimatedGifSet.FrameWidth;
366 int iHeight = AnimatedGifSet.FrameHeight;
369 COLOR *palette = AnimatedGifSet.m_vecimg[0]->Palette;
370 // set the alpha values to fully opaque
371 for (int i = 0; i < 256; i++)
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;
377 pMap = new CTextureMap(strTextureName, iWidth, iHeight, AnimatedGifSet.nLoops);
379 for (int iImage = 0; iImage < iImages; iImage++)
381 CTexture *glTexture = new CTexture();
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);
388 } // of for (int iImage=0; iImage < iImages; iImage++)
393 end = CurrentHostCounter();
394 freq = CurrentHostFrequency();
396 sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
397 OutputDebugString(temp);
400 m_vecTextures.push_back(pMap);
401 return pMap->GetTexture();
402 } // of if (strPath.Right(4).ToLower()==".gif")
404 CBaseTexture *pTexture = NULL;
405 int width = 0, height = 0;
408 if (!m_TexBundle[bundle].LoadTexture(strTextureName, &pTexture, width, height))
410 CLog::Log(LOGERROR, "Texture manager unable to load bundled file: %s", strTextureName.c_str());
416 pTexture = CBaseTexture::LoadFromFile(strPath);
419 width = pTexture->GetWidth();
420 height = pTexture->GetHeight();
423 if (!pTexture) return emptyTexture;
425 CTextureMap* pMap = new CTextureMap(strTextureName, width, height, 0);
426 pMap->Add(pTexture, 100);
427 m_vecTextures.push_back(pMap);
429 #ifdef _DEBUG_TEXTURES
431 end = CurrentHostCounter();
432 freq = CurrentHostFrequency();
434 sprintf(temp, "Load %s: %.1fms%s\n", strPath.c_str(), 1000.f * (end - start) / freq, (bundle >= 0) ? " (bundled)" : "");
435 OutputDebugString(temp);
438 return pMap->GetTexture();
442 void CGUITextureManager::ReleaseTexture(const CStdString& strTextureName, bool immediately /*= false */)
444 CSingleLock lock(g_graphicsContext);
447 i = m_vecTextures.begin();
448 while (i != m_vecTextures.end())
450 CTextureMap* pMap = *i;
451 if (pMap->GetName() == strTextureName)
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);
464 CLog::Log(LOGWARNING, "%s: Unable to release texture %s", __FUNCTION__, strTextureName.c_str());
467 void CGUITextureManager::FreeUnusedTextures(unsigned int timeDelay)
469 unsigned int currFrameTime = XbmcThreads::SystemClockMillis();
470 CSingleLock lock(g_graphicsContext);
471 for (ilistUnused i = m_unusedTextures.begin(); i != m_unusedTextures.end();)
473 if (currFrameTime - i->second >= timeDelay)
476 i = m_unusedTextures.erase(i);
482 #if defined(HAS_GL) || defined(HAS_GLES)
483 for (unsigned int i = 0; i < m_unusedHwTextures.size(); ++i)
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]))
491 glDeleteTextures(1, (GLuint*) &m_unusedHwTextures[i]);
494 m_unusedHwTextures.clear();
497 void CGUITextureManager::ReleaseHwTexture(unsigned int texture)
499 CSingleLock lock(g_graphicsContext);
500 m_unusedHwTextures.push_back(texture);
503 void CGUITextureManager::Cleanup()
505 CSingleLock lock(g_graphicsContext);
508 i = m_vecTextures.begin();
509 while (i != m_vecTextures.end())
511 CTextureMap* pMap = *i;
512 CLog::Log(LOGWARNING, "%s: Having to cleanup texture %s", __FUNCTION__, pMap->GetName().c_str());
514 i = m_vecTextures.erase(i);
516 for (int i = 0; i < 2; i++)
517 m_TexBundle[i].Cleanup();
518 FreeUnusedTextures();
521 void CGUITextureManager::Dump() const
523 CStdString strLog = StringUtils::Format("total texturemaps size:%i\n", m_vecTextures.size());
524 OutputDebugString(strLog.c_str());
526 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
528 const CTextureMap* pMap = m_vecTextures[i];
529 if (!pMap->IsEmpty())
534 void CGUITextureManager::Flush()
536 CSingleLock lock(g_graphicsContext);
539 i = m_vecTextures.begin();
540 while (i != m_vecTextures.end())
542 CTextureMap* pMap = *i;
544 if (pMap->IsEmpty() )
547 i = m_vecTextures.erase(i);
556 unsigned int CGUITextureManager::GetMemoryUsage() const
558 unsigned int memUsage = 0;
559 for (int i = 0; i < (int)m_vecTextures.size(); ++i)
561 memUsage += m_vecTextures[i]->GetMemoryUsage();
566 void CGUITextureManager::SetTexturePath(const CStdString &texturePath)
568 CSingleLock lock(m_section);
569 m_texturePaths.clear();
570 AddTexturePath(texturePath);
573 void CGUITextureManager::AddTexturePath(const CStdString &texturePath)
575 CSingleLock lock(m_section);
576 if (!texturePath.empty())
577 m_texturePaths.push_back(texturePath);
580 void CGUITextureManager::RemoveTexturePath(const CStdString &texturePath)
582 CSingleLock lock(m_section);
583 for (vector<CStdString>::iterator it = m_texturePaths.begin(); it != m_texturePaths.end(); ++it)
585 if (*it == texturePath)
587 m_texturePaths.erase(it);
593 CStdString CGUITextureManager::GetTexturePath(const CStdString &textureName, bool directory /* = false */)
595 if (CURL::IsFullPath(textureName))
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)
602 CStdString path = URIUtils::AddFileToFolder(it->c_str(), "media");
603 path = URIUtils::AddFileToFolder(path, textureName);
606 if (XFILE::CDirectory::Exists(path))
611 if (XFILE::CFile::Exists(path))
619 void CGUITextureManager::GetBundledTexturesFromPath(const CStdString& texturePath, std::vector<CStdString> &items)
621 m_TexBundle[0].GetTexturesFromPath(texturePath, items);
623 m_TexBundle[1].GetTexturesFromPath(texturePath, items);