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 "TextureCache.h"
22 #include "TextureCacheJob.h"
23 #include "filesystem/File.h"
24 #include "profiles/ProfilesManager.h"
25 #include "threads/SingleLock.h"
26 #include "utils/Crc32.h"
27 #include "settings/AdvancedSettings.h"
28 #include "utils/log.h"
29 #include "utils/URIUtils.h"
30 #include "utils/StringUtils.h"
33 using namespace XFILE
;
35 CTextureCache
&CTextureCache::GetInstance()
37 static CTextureCache s_cache
;
41 CTextureCache::CTextureCache() : CJobQueue(false, 1, CJob::PRIORITY_LOW_PAUSABLE
)
45 CTextureCache::~CTextureCache()
49 void CTextureCache::Initialize()
51 CSingleLock
lock(m_databaseSection
);
52 if (!m_database
.IsOpen())
56 void CTextureCache::Deinitialize()
59 CSingleLock
lock(m_databaseSection
);
63 bool CTextureCache::IsCachedImage(const std::string
&url
) const
65 if (url
!= "-" && !CURL::IsFullPath(url
))
67 if (URIUtils::PathHasParent(url
, "special://skin", true) ||
68 URIUtils::PathHasParent(url
, "special://temp", true) ||
69 URIUtils::PathHasParent(url
, "resource://", true) ||
70 URIUtils::PathHasParent(url
, "androidapp://", true) ||
71 URIUtils::PathHasParent(url
, CProfilesManager::GetInstance().GetThumbnailsFolder(), true))
76 bool CTextureCache::HasCachedImage(const std::string
&url
)
78 CTextureDetails details
;
79 std::string
cachedImage(GetCachedImage(url
, details
));
80 return (!cachedImage
.empty() && cachedImage
!= url
);
83 std::string
CTextureCache::GetCachedImage(const std::string
&image
, CTextureDetails
&details
, bool trackUsage
)
85 std::string url
= CTextureUtils::UnwrapImageURL(image
);
87 if (IsCachedImage(url
))
90 // lookup the item in the database
91 if (GetCachedTexture(url
, details
))
94 IncrementUseCount(details
);
95 return GetCachedPath(details
.file
);
100 bool CTextureCache::CanCacheImageURL(const CURL
&url
)
102 return (url
.GetUserName().empty() || url
.GetUserName() == "music");
105 std::string
CTextureCache::CheckCachedImage(const std::string
&url
, bool &needsRecaching
)
107 CTextureDetails details
;
108 std::string
path(GetCachedImage(url
, details
, true));
109 needsRecaching
= !details
.hash
.empty();
115 void CTextureCache::BackgroundCacheImage(const std::string
&url
)
120 CTextureDetails details
;
121 std::string
path(GetCachedImage(url
, details
));
122 if (!path
.empty() && details
.hash
.empty())
123 return; // image is already cached and doesn't need to be checked further
125 path
= CTextureUtils::UnwrapImageURL(url
);
130 AddJob(new CTextureCacheJob(path
, details
.hash
));
133 std::string
CTextureCache::CacheImage(const std::string
&image
, CBaseTexture
**texture
/* = NULL */, CTextureDetails
*details
/* = NULL */)
135 std::string url
= CTextureUtils::UnwrapImageURL(image
);
139 CSingleLock
lock(m_processingSection
);
140 if (m_processinglist
.find(url
) == m_processinglist
.end())
142 m_processinglist
.insert(url
);
144 // cache the texture directly
145 CTextureCacheJob
job(url
);
146 bool success
= job
.CacheTexture(texture
);
147 OnCachingComplete(success
, &job
);
148 if (success
&& details
)
149 *details
= job
.m_details
;
150 return success
? GetCachedPath(job
.m_details
.file
) : "";
154 // wait for currently processing job to end.
157 m_completeEvent
.WaitMSec(1000);
159 CSingleLock
lock(m_processingSection
);
160 if (m_processinglist
.find(url
) == m_processinglist
.end())
164 CTextureDetails tempDetails
;
166 details
= &tempDetails
;
167 return GetCachedImage(url
, *details
, true);
170 bool CTextureCache::CacheImage(const std::string
&image
, CTextureDetails
&details
)
172 std::string path
= GetCachedImage(image
, details
);
173 if (path
.empty()) // not cached
174 path
= CacheImage(image
, NULL
, &details
);
176 return !path
.empty();
179 void CTextureCache::ClearCachedImage(const std::string
&url
, bool deleteSource
/*= false */)
181 //! @todo This can be removed when the texture cache covers everything.
182 std::string path
= deleteSource
? url
: "";
183 std::string cachedFile
;
184 if (ClearCachedTexture(url
, cachedFile
))
185 path
= GetCachedPath(cachedFile
);
186 if (CFile::Exists(path
))
188 path
= URIUtils::ReplaceExtension(path
, ".dds");
189 if (CFile::Exists(path
))
193 bool CTextureCache::ClearCachedImage(int id
)
195 std::string cachedFile
;
196 if (ClearCachedTexture(id
, cachedFile
))
198 cachedFile
= GetCachedPath(cachedFile
);
199 if (CFile::Exists(cachedFile
))
200 CFile::Delete(cachedFile
);
201 cachedFile
= URIUtils::ReplaceExtension(cachedFile
, ".dds");
202 if (CFile::Exists(cachedFile
))
203 CFile::Delete(cachedFile
);
209 bool CTextureCache::GetCachedTexture(const std::string
&url
, CTextureDetails
&details
)
211 CSingleLock
lock(m_databaseSection
);
212 return m_database
.GetCachedTexture(url
, details
);
215 bool CTextureCache::AddCachedTexture(const std::string
&url
, const CTextureDetails
&details
)
217 CSingleLock
lock(m_databaseSection
);
218 return m_database
.AddCachedTexture(url
, details
);
221 void CTextureCache::IncrementUseCount(const CTextureDetails
&details
)
223 static const size_t count_before_update
= 100;
224 CSingleLock
lock(m_useCountSection
);
225 m_useCounts
.reserve(count_before_update
);
226 m_useCounts
.push_back(details
);
227 if (m_useCounts
.size() >= count_before_update
)
229 AddJob(new CTextureUseCountJob(m_useCounts
));
234 bool CTextureCache::SetCachedTextureValid(const std::string
&url
, bool updateable
)
236 CSingleLock
lock(m_databaseSection
);
237 return m_database
.SetCachedTextureValid(url
, updateable
);
240 bool CTextureCache::ClearCachedTexture(const std::string
&url
, std::string
&cachedURL
)
242 CSingleLock
lock(m_databaseSection
);
243 return m_database
.ClearCachedTexture(url
, cachedURL
);
246 bool CTextureCache::ClearCachedTexture(int id
, std::string
&cachedURL
)
248 CSingleLock
lock(m_databaseSection
);
249 return m_database
.ClearCachedTexture(id
, cachedURL
);
252 std::string
CTextureCache::GetCacheFile(const std::string
&url
)
254 auto crc
= Crc32::ComputeFromLowerCase(url
);
255 std::string hex
= StringUtils::Format("%08x", crc
);
256 std::string hash
= StringUtils::Format("%c/%s", hex
[0], hex
.c_str());
260 std::string
CTextureCache::GetCachedPath(const std::string
&file
)
262 return URIUtils::AddFileToFolder(CProfilesManager::GetInstance().GetThumbnailsFolder(), file
);
265 void CTextureCache::OnCachingComplete(bool success
, CTextureCacheJob
*job
)
269 if (job
->m_oldHash
== job
->m_details
.hash
)
270 SetCachedTextureValid(job
->m_url
, job
->m_details
.updateable
);
272 AddCachedTexture(job
->m_url
, job
->m_details
);
275 { // remove from our processing list
276 CSingleLock
lock(m_processingSection
);
277 std::set
<std::string
>::iterator i
= m_processinglist
.find(job
->m_url
);
278 if (i
!= m_processinglist
.end())
279 m_processinglist
.erase(i
);
282 m_completeEvent
.Set();
285 void CTextureCache::OnJobComplete(unsigned int jobID
, bool success
, CJob
*job
)
287 if (strcmp(job
->GetType(), kJobTypeCacheImage
) == 0)
288 OnCachingComplete(success
, (CTextureCacheJob
*)job
);
289 return CJobQueue::OnJobComplete(jobID
, success
, job
);
292 void CTextureCache::OnJobProgress(unsigned int jobID
, unsigned int progress
, unsigned int total
, const CJob
*job
)
294 if (strcmp(job
->GetType(), kJobTypeCacheImage
) == 0 && !progress
)
295 { // check our processing list
297 CSingleLock
lock(m_processingSection
);
298 const CTextureCacheJob
*cacheJob
= (CTextureCacheJob
*)job
;
299 std::set
<std::string
>::iterator i
= m_processinglist
.find(cacheJob
->m_url
);
300 if (i
== m_processinglist
.end())
302 m_processinglist
.insert(cacheJob
->m_url
);
309 CJobQueue::OnJobProgress(jobID
, progress
, total
, job
);
312 bool CTextureCache::Export(const std::string
&image
, const std::string
&destination
, bool overwrite
)
314 CTextureDetails details
;
315 std::string
cachedImage(GetCachedImage(image
, details
));
316 if (!cachedImage
.empty())
318 std::string dest
= destination
+ URIUtils::GetExtension(cachedImage
);
319 if (overwrite
|| !CFile::Exists(dest
))
321 if (CFile::Copy(cachedImage
, dest
))
323 CLog::Log(LOGERROR
, "%s failed exporting '%s' to '%s'", __FUNCTION__
, cachedImage
.c_str(), dest
.c_str());
329 bool CTextureCache::Export(const std::string
&image
, const std::string
&destination
)
331 CTextureDetails details
;
332 std::string
cachedImage(GetCachedImage(image
, details
));
333 if (!cachedImage
.empty())
335 if (CFile::Copy(cachedImage
, destination
))
337 CLog::Log(LOGERROR
, "%s failed exporting '%s' to '%s'", __FUNCTION__
, cachedImage
.c_str(), destination
.c_str());