PageRenderTime 821ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/TextureCache.cpp

https://gitlab.com/nghia-n-v2007/xbmc
C++ | 331 lines | 270 code | 37 blank | 24 comment | 49 complexity | 2202ab3e19bb5568b02bfaaa47b35054 MD5 | raw file
  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. #include "TextureCache.h"
  21. #include "TextureCacheJob.h"
  22. #include "filesystem/File.h"
  23. #include "profiles/ProfilesManager.h"
  24. #include "threads/SingleLock.h"
  25. #include "utils/Crc32.h"
  26. #include "settings/AdvancedSettings.h"
  27. #include "utils/log.h"
  28. #include "utils/URIUtils.h"
  29. #include "utils/StringUtils.h"
  30. #include "URL.h"
  31. using namespace XFILE;
  32. CTextureCache &CTextureCache::GetInstance()
  33. {
  34. static CTextureCache s_cache;
  35. return s_cache;
  36. }
  37. CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE)
  38. {
  39. }
  40. CTextureCache::~CTextureCache()
  41. {
  42. }
  43. void CTextureCache::Initialize()
  44. {
  45. CSingleLock lock(m_databaseSection);
  46. if (!m_database.IsOpen())
  47. m_database.Open();
  48. }
  49. void CTextureCache::Deinitialize()
  50. {
  51. CancelJobs();
  52. CSingleLock lock(m_databaseSection);
  53. m_database.Close();
  54. }
  55. bool CTextureCache::IsCachedImage(const std::string &url) const
  56. {
  57. if (url != "-" && !CURL::IsFullPath(url))
  58. return true;
  59. if (URIUtils::PathHasParent(url, "special://skin", true) ||
  60. URIUtils::PathHasParent(url, "special://temp", true) ||
  61. URIUtils::PathHasParent(url, "resource://", true) ||
  62. URIUtils::PathHasParent(url, "androidapp://", true) ||
  63. URIUtils::PathHasParent(url, CProfilesManager::GetInstance().GetThumbnailsFolder(), true))
  64. return true;
  65. return false;
  66. }
  67. bool CTextureCache::HasCachedImage(const std::string &url)
  68. {
  69. CTextureDetails details;
  70. std::string cachedImage(GetCachedImage(url, details));
  71. return (!cachedImage.empty() && cachedImage != url);
  72. }
  73. std::string CTextureCache::GetCachedImage(const std::string &image, CTextureDetails &details, bool trackUsage)
  74. {
  75. std::string url = CTextureUtils::UnwrapImageURL(image);
  76. if (IsCachedImage(url))
  77. return url;
  78. // lookup the item in the database
  79. if (GetCachedTexture(url, details))
  80. {
  81. if (trackUsage)
  82. IncrementUseCount(details);
  83. return GetCachedPath(details.file);
  84. }
  85. return "";
  86. }
  87. bool CTextureCache::CanCacheImageURL(const CURL &url)
  88. {
  89. return (url.GetUserName().empty() || url.GetUserName() == "music");
  90. }
  91. std::string CTextureCache::CheckCachedImage(const std::string &url, bool &needsRecaching)
  92. {
  93. CTextureDetails details;
  94. std::string path(GetCachedImage(url, details, true));
  95. needsRecaching = !details.hash.empty();
  96. if (!path.empty())
  97. return path;
  98. return "";
  99. }
  100. void CTextureCache::BackgroundCacheImage(const std::string &url)
  101. {
  102. CTextureDetails details;
  103. std::string path(GetCachedImage(url, details));
  104. if (!path.empty() && details.hash.empty())
  105. return; // image is already cached and doesn't need to be checked further
  106. // needs (re)caching
  107. AddJob(new CTextureCacheJob(CTextureUtils::UnwrapImageURL(url), details.hash));
  108. }
  109. std::string CTextureCache::CacheImage(const std::string &image, CBaseTexture **texture /* = NULL */, CTextureDetails *details /* = NULL */)
  110. {
  111. std::string url = CTextureUtils::UnwrapImageURL(image);
  112. CSingleLock lock(m_processingSection);
  113. if (m_processinglist.find(url) == m_processinglist.end())
  114. {
  115. m_processinglist.insert(url);
  116. lock.Leave();
  117. // cache the texture directly
  118. CTextureCacheJob job(url);
  119. bool success = job.CacheTexture(texture);
  120. OnCachingComplete(success, &job);
  121. if (success && details)
  122. *details = job.m_details;
  123. return success ? GetCachedPath(job.m_details.file) : "";
  124. }
  125. lock.Leave();
  126. // wait for currently processing job to end.
  127. while (true)
  128. {
  129. m_completeEvent.WaitMSec(1000);
  130. {
  131. CSingleLock lock(m_processingSection);
  132. if (m_processinglist.find(url) == m_processinglist.end())
  133. break;
  134. }
  135. }
  136. CTextureDetails tempDetails;
  137. if (!details)
  138. details = &tempDetails;
  139. return GetCachedImage(url, *details, true);
  140. }
  141. bool CTextureCache::CacheImage(const std::string &image, CTextureDetails &details)
  142. {
  143. std::string path = GetCachedImage(image, details);
  144. if (path.empty()) // not cached
  145. path = CacheImage(image, NULL, &details);
  146. return !path.empty();
  147. }
  148. void CTextureCache::ClearCachedImage(const std::string &url, bool deleteSource /*= false */)
  149. {
  150. // TODO: This can be removed when the texture cache covers everything.
  151. std::string path = deleteSource ? url : "";
  152. std::string cachedFile;
  153. if (ClearCachedTexture(url, cachedFile))
  154. path = GetCachedPath(cachedFile);
  155. if (CFile::Exists(path))
  156. CFile::Delete(path);
  157. path = URIUtils::ReplaceExtension(path, ".dds");
  158. if (CFile::Exists(path))
  159. CFile::Delete(path);
  160. }
  161. bool CTextureCache::ClearCachedImage(int id)
  162. {
  163. std::string cachedFile;
  164. if (ClearCachedTexture(id, cachedFile))
  165. {
  166. cachedFile = GetCachedPath(cachedFile);
  167. if (CFile::Exists(cachedFile))
  168. CFile::Delete(cachedFile);
  169. cachedFile = URIUtils::ReplaceExtension(cachedFile, ".dds");
  170. if (CFile::Exists(cachedFile))
  171. CFile::Delete(cachedFile);
  172. return true;
  173. }
  174. return false;
  175. }
  176. bool CTextureCache::GetCachedTexture(const std::string &url, CTextureDetails &details)
  177. {
  178. CSingleLock lock(m_databaseSection);
  179. return m_database.GetCachedTexture(url, details);
  180. }
  181. bool CTextureCache::AddCachedTexture(const std::string &url, const CTextureDetails &details)
  182. {
  183. CSingleLock lock(m_databaseSection);
  184. return m_database.AddCachedTexture(url, details);
  185. }
  186. void CTextureCache::IncrementUseCount(const CTextureDetails &details)
  187. {
  188. static const size_t count_before_update = 100;
  189. CSingleLock lock(m_useCountSection);
  190. m_useCounts.reserve(count_before_update);
  191. m_useCounts.push_back(details);
  192. if (m_useCounts.size() >= count_before_update)
  193. {
  194. AddJob(new CTextureUseCountJob(m_useCounts));
  195. m_useCounts.clear();
  196. }
  197. }
  198. bool CTextureCache::SetCachedTextureValid(const std::string &url, bool updateable)
  199. {
  200. CSingleLock lock(m_databaseSection);
  201. return m_database.SetCachedTextureValid(url, updateable);
  202. }
  203. bool CTextureCache::ClearCachedTexture(const std::string &url, std::string &cachedURL)
  204. {
  205. CSingleLock lock(m_databaseSection);
  206. return m_database.ClearCachedTexture(url, cachedURL);
  207. }
  208. bool CTextureCache::ClearCachedTexture(int id, std::string &cachedURL)
  209. {
  210. CSingleLock lock(m_databaseSection);
  211. return m_database.ClearCachedTexture(id, cachedURL);
  212. }
  213. std::string CTextureCache::GetCacheFile(const std::string &url)
  214. {
  215. Crc32 crc;
  216. crc.ComputeFromLowerCase(url);
  217. std::string hex = StringUtils::Format("%08x", (unsigned int)crc);
  218. std::string hash = StringUtils::Format("%c/%s", hex[0], hex.c_str());
  219. return hash;
  220. }
  221. std::string CTextureCache::GetCachedPath(const std::string &file)
  222. {
  223. return URIUtils::AddFileToFolder(CProfilesManager::GetInstance().GetThumbnailsFolder(), file);
  224. }
  225. void CTextureCache::OnCachingComplete(bool success, CTextureCacheJob *job)
  226. {
  227. if (success)
  228. {
  229. if (job->m_oldHash == job->m_details.hash)
  230. SetCachedTextureValid(job->m_url, job->m_details.updateable);
  231. else
  232. AddCachedTexture(job->m_url, job->m_details);
  233. }
  234. { // remove from our processing list
  235. CSingleLock lock(m_processingSection);
  236. std::set<std::string>::iterator i = m_processinglist.find(job->m_url);
  237. if (i != m_processinglist.end())
  238. m_processinglist.erase(i);
  239. }
  240. m_completeEvent.Set();
  241. }
  242. void CTextureCache::OnJobComplete(unsigned int jobID, bool success, CJob *job)
  243. {
  244. if (strcmp(job->GetType(), kJobTypeCacheImage) == 0)
  245. OnCachingComplete(success, (CTextureCacheJob *)job);
  246. return CJobQueue::OnJobComplete(jobID, success, job);
  247. }
  248. void CTextureCache::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
  249. {
  250. if (strcmp(job->GetType(), kJobTypeCacheImage) == 0 && !progress)
  251. { // check our processing list
  252. {
  253. CSingleLock lock(m_processingSection);
  254. const CTextureCacheJob *cacheJob = (CTextureCacheJob *)job;
  255. std::set<std::string>::iterator i = m_processinglist.find(cacheJob->m_url);
  256. if (i == m_processinglist.end())
  257. {
  258. m_processinglist.insert(cacheJob->m_url);
  259. return;
  260. }
  261. }
  262. CancelJob(job);
  263. }
  264. else
  265. CJobQueue::OnJobProgress(jobID, progress, total, job);
  266. }
  267. bool CTextureCache::Export(const std::string &image, const std::string &destination, bool overwrite)
  268. {
  269. CTextureDetails details;
  270. std::string cachedImage(GetCachedImage(image, details));
  271. if (!cachedImage.empty())
  272. {
  273. std::string dest = destination + URIUtils::GetExtension(cachedImage);
  274. if (overwrite || !CFile::Exists(dest))
  275. {
  276. if (CFile::Copy(cachedImage, dest))
  277. return true;
  278. CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), dest.c_str());
  279. }
  280. }
  281. return false;
  282. }
  283. bool CTextureCache::Export(const std::string &image, const std::string &destination)
  284. {
  285. CTextureDetails details;
  286. std::string cachedImage(GetCachedImage(image, details));
  287. if (!cachedImage.empty())
  288. {
  289. if (CFile::Copy(cachedImage, destination))
  290. return true;
  291. CLog::Log(LOGERROR, "%s failed exporting '%s' to '%s'", __FUNCTION__, cachedImage.c_str(), destination.c_str());
  292. }
  293. return false;
  294. }