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