[droid] splash: adjust progressbar color
[vuplus_xbmc] / xbmc / TextureCacheJob.cpp
1 /*
2  *      Copyright (C) 2012 Team XBMC
3  *      http://www.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 "TextureCacheJob.h"
22 #include "TextureCache.h"
23 #include "guilib/Texture.h"
24 #include "guilib/DDSImage.h"
25 #include "settings/Settings.h"
26 #include "settings/AdvancedSettings.h"
27 #include "settings/GUISettings.h"
28 #include "utils/log.h"
29 #include "filesystem/File.h"
30 #include "pictures/Picture.h"
31 #include "utils/URIUtils.h"
32 #include "utils/StringUtils.h"
33 #include "URL.h"
34 #include "FileItem.h"
35 #include "music/MusicThumbLoader.h"
36 #include "music/tags/MusicInfoTag.h"
37
38 CTextureCacheJob::CTextureCacheJob(const CStdString &url, const CStdString &oldHash)
39 {
40   m_url = url;
41   m_oldHash = oldHash;
42   m_cachePath = CTextureCache::GetCacheFile(m_url);
43 }
44
45 CTextureCacheJob::~CTextureCacheJob()
46 {
47 }
48
49 bool CTextureCacheJob::operator==(const CJob* job) const
50 {
51   if (strcmp(job->GetType(),GetType()) == 0)
52   {
53     const CTextureCacheJob* cacheJob = dynamic_cast<const CTextureCacheJob*>(job);
54     if (cacheJob && cacheJob->m_cachePath == m_cachePath)
55       return true;
56   }
57   return false;
58 }
59
60 bool CTextureCacheJob::DoWork()
61 {
62   if (ShouldCancel(0, 0))
63     return false;
64   if (ShouldCancel(1, 0)) // HACK: second check is because we cancel the job in the first callback, but we don't detect it
65     return false;         //       until the second
66
67   // check whether we need cache the job anyway
68   bool needsRecaching = false;
69   CStdString path(CTextureCache::Get().CheckCachedImage(m_url, false, needsRecaching));
70   if (!path.IsEmpty() && !needsRecaching)
71     return false;
72   return CacheTexture();
73 }
74
75 bool CTextureCacheJob::CacheTexture(CBaseTexture **out_texture)
76 {
77   // unwrap the URL as required
78   std::string additional_info;
79   unsigned int width, height;
80   CStdString image = DecodeImageURL(m_url, width, height, additional_info);
81
82   m_details.updateable = additional_info != "music" && UpdateableURL(image);
83
84   // generate the hash
85   m_details.hash = GetImageHash(image);
86   if (m_details.hash.empty())
87     return false;
88   else if (m_details.hash == m_oldHash)
89     return true;
90
91   CBaseTexture *texture = LoadImage(image, width, height, additional_info);
92   if (texture)
93   {
94     if (texture->HasAlpha())
95       m_details.file = m_cachePath + ".png";
96     else
97       m_details.file = m_cachePath + ".jpg";
98
99     CLog::Log(LOGDEBUG, "%s image '%s' to '%s':", m_oldHash.IsEmpty() ? "Caching" : "Recaching", image.c_str(), m_details.file.c_str());
100
101     if (CPicture::CacheTexture(texture, width, height, CTextureCache::GetCachedPath(m_details.file)))
102     {
103       m_details.width = width;
104       m_details.height = height;
105       if (out_texture) // caller wants the texture
106         *out_texture = texture;
107       else
108         delete texture;
109       return true;
110     }
111   }
112   delete texture;
113   return false;
114 }
115
116 CStdString CTextureCacheJob::DecodeImageURL(const CStdString &url, unsigned int &width, unsigned int &height, std::string &additional_info)
117 {
118   // unwrap the URL as required
119   CStdString image(url);
120   additional_info.clear();
121   width = height = 0;
122   if (url.compare(0, 8, "image://") == 0)
123   {
124     // format is image://[type@]<url_encoded_path>?options
125     CURL thumbURL(url);
126
127     if (!CTextureCache::CanCacheImageURL(thumbURL))
128       return "";
129     if (thumbURL.GetUserName() == "music")
130       additional_info = "music";
131
132     image = thumbURL.GetHostName();
133
134     CStdString optionString = thumbURL.GetOptions().Mid(1);
135     optionString.TrimRight('/'); // in case XBMC adds a slash
136
137     std::vector<CStdString> options;
138     StringUtils::SplitString(optionString, "&", options);
139     for (std::vector<CStdString>::iterator i = options.begin(); i != options.end(); i++)
140     {
141       CStdString option, value;
142       int pos = i->Find('=');
143       if (pos != -1)
144       {
145         option = i->Left(pos);
146         value  = i->Mid(pos + 1);
147       }
148       else
149       {
150         option = *i;
151         value = "";
152       }
153       if (option == "size" && value == "thumb")
154       {
155         width = height = g_advancedSettings.GetThumbSize();
156       }
157       else if (option == "flipped")
158       {
159         additional_info = "flipped";
160       }
161     }
162   }
163   return image;
164 }
165
166 CBaseTexture *CTextureCacheJob::LoadImage(const CStdString &image, unsigned int width, unsigned int height, const std::string &additional_info)
167 {
168   if (additional_info == "music")
169   { // special case for embedded music images
170     MUSIC_INFO::EmbeddedArt art;
171     if (CMusicThumbLoader::GetEmbeddedThumb(image, art))
172       return CBaseTexture::LoadFromFileInMemory(&art.data[0], art.size, art.mime, width, height);
173   }
174
175   // Validate file URL to see if it is an image
176   CFileItem file(image, false);
177   if (!(file.IsPicture() && !(file.IsZIP() || file.IsRAR() || file.IsCBR() || file.IsCBZ() ))
178       && !file.GetMimeType().Left(6).Equals("image/") && !file.GetMimeType().Equals("application/octet-stream")) // ignore non-pictures
179     return NULL;
180
181   CBaseTexture *texture = CBaseTexture::LoadFromFile(image, width, height, g_guiSettings.GetBool("pictures.useexifrotation"));
182   if (!texture)
183     return NULL;
184
185   // EXIF bits are interpreted as: <flipXY><flipY*flipX><flipX>
186   // where to undo the operation we apply them in reverse order <flipX>*<flipY*flipX>*<flipXY>
187   // When flipped we have an additional <flipX> on the left, which is equivalent to toggling the last bit
188   if (additional_info == "flipped")
189     texture->SetOrientation(texture->GetOrientation() ^ 1);
190
191   return texture;
192 }
193
194 bool CTextureCacheJob::UpdateableURL(const CStdString &url) const
195 {
196   // we don't constantly check online images
197   if (url.compare(0, 7, "http://") == 0 ||
198       url.compare(0, 8, "https://") == 0)
199     return false;
200   return true;
201 }
202
203 CStdString CTextureCacheJob::GetImageHash(const CStdString &url)
204 {
205   struct __stat64 st;
206   if (XFILE::CFile::Stat(url, &st) == 0)
207   {
208     int64_t time = st.st_mtime;
209     if (!time)
210       time = st.st_ctime;
211     if (time || st.st_size)
212     {
213       CStdString hash;
214       hash.Format("d%"PRId64"s%"PRId64, time, st.st_size);
215       return hash;
216     }
217   }
218   CLog::Log(LOGDEBUG, "%s - unable to stat url %s", __FUNCTION__, url.c_str());
219   return "";
220 }
221
222 CTextureDDSJob::CTextureDDSJob(const CStdString &original)
223 {
224   m_original = original;
225 }
226
227 bool CTextureDDSJob::operator==(const CJob* job) const
228 {
229   if (strcmp(job->GetType(),GetType()) == 0)
230   {
231     const CTextureDDSJob* ddsJob = dynamic_cast<const CTextureDDSJob*>(job);
232     if (ddsJob && ddsJob->m_original == m_original)
233       return true;
234   }
235   return false;
236 }
237
238 bool CTextureDDSJob::DoWork()
239 {
240   if (URIUtils::GetExtension(m_original).Equals(".dds"))
241     return false;
242   CBaseTexture *texture = CBaseTexture::LoadFromFile(m_original);
243   if (texture)
244   { // convert to DDS
245     CDDSImage dds;
246     CLog::Log(LOGDEBUG, "Creating DDS version of: %s", m_original.c_str());
247     bool ret = dds.Create(URIUtils::ReplaceExtension(m_original, ".dds"), texture->GetWidth(), texture->GetHeight(), texture->GetPitch(), texture->GetPixels(), 40);
248     delete texture;
249     return ret;
250   }
251   return false;
252 }
253
254 CTextureUseCountJob::CTextureUseCountJob(const std::vector<CTextureDetails> &textures) : m_textures(textures)
255 {
256 }
257
258 bool CTextureUseCountJob::operator==(const CJob* job) const
259 {
260   if (strcmp(job->GetType(),GetType()) == 0)
261   {
262     const CTextureUseCountJob* useJob = dynamic_cast<const CTextureUseCountJob*>(job);
263     if (useJob && useJob->m_textures == m_textures)
264       return true;
265   }
266   return false;
267 }
268
269 bool CTextureUseCountJob::DoWork()
270 {
271   CTextureDatabase db;
272   if (db.Open())
273   {
274     db.BeginTransaction();
275     for (std::vector<CTextureDetails>::const_iterator i = m_textures.begin(); i != m_textures.end(); ++i)
276       db.IncrementUseCount(*i);
277     db.CommitTransaction();
278   }
279   return true;
280 }