[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / TextureCacheJob.cpp
blob2850046e035b4535aa641d506edf554d48aa2fb2
1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "TextureCacheJob.h"
11 #include "FileItem.h"
12 #include "ServiceBroker.h"
13 #include "TextureCache.h"
14 #include "TextureDatabase.h"
15 #include "URL.h"
16 #include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/audiodecoder.h"
17 #include "commons/ilog.h"
18 #include "filesystem/File.h"
19 #include "guilib/Texture.h"
20 #include "imagefiles/SpecialImageLoaderFactory.h"
21 #include "pictures/Picture.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/SettingsComponent.h"
24 #include "utils/StringUtils.h"
25 #include "utils/URIUtils.h"
26 #include "utils/log.h"
28 #include <cstdlib>
29 #include <cstring>
30 #include <exception>
31 #include <utility>
33 #include "PlatformDefs.h"
35 CTextureCacheJob::CTextureCacheJob(const std::string &url, const std::string &oldHash):
36 m_url(url),
37 m_oldHash(oldHash),
38 m_cachePath(CTextureCache::GetCacheFile(m_url))
42 CTextureCacheJob::~CTextureCacheJob() = default;
44 bool CTextureCacheJob::operator==(const CJob* job) const
46 if (strcmp(job->GetType(),GetType()) == 0)
48 const CTextureCacheJob* cacheJob = dynamic_cast<const CTextureCacheJob*>(job);
49 if (cacheJob && cacheJob->m_cachePath == m_cachePath)
50 return true;
52 return false;
55 bool CTextureCacheJob::DoWork()
57 if (ShouldCancel(0, 0))
58 return false;
60 // check whether we need cache the job anyway
61 bool needsRecaching = false;
62 std::string path(CServiceBroker::GetTextureCache()->CheckCachedImage(m_url, needsRecaching));
63 if (!path.empty() && !needsRecaching)
64 return false;
65 if (CServiceBroker::GetTextureCache()->StartCacheImage(m_url))
66 return CacheTexture();
68 return false;
71 namespace
73 // Most PVR images use "additional_info" to signify 'ownership' of basic images for easy
74 // cache cleaning, rather than special generated images
75 bool IsPVROwnedImage(const std::string& additional_info)
77 return additional_info == "pvrchannel_radio" || additional_info == "pvrchannel_tv" ||
78 additional_info == "pvrprovider" || additional_info == "pvrrecording" ||
79 StringUtils::StartsWith(additional_info, "epgtag_");
82 // DecodeImageURL can also set "additional_info" to 'flipped' for mirror images selected in
83 // the GUI, so is not a special generated image
84 bool IsControl(const std::string& additional_info)
86 return additional_info == "flipped";
89 // special generated images and images served via HTTP should not be regularly checked for changes
90 bool ShouldCheckForChanges(const std::string& additional_info, const std::string& url)
92 const bool isSpecialImage =
93 !additional_info.empty() && !IsControl(additional_info) && !IsPVROwnedImage(additional_info);
94 if (isSpecialImage)
95 return false;
97 const bool isHTTP =
98 StringUtils::StartsWith(url, "http://") || StringUtils::StartsWith(url, "https://");
99 return !isHTTP;
101 } // namespace
103 bool CTextureCacheJob::CacheTexture(std::unique_ptr<CTexture>* out_texture)
105 // unwrap the URL as required
106 std::string additional_info;
107 unsigned int width, height;
108 CPictureScalingAlgorithm::Algorithm scalingAlgorithm;
109 std::string image = DecodeImageURL(m_url, width, height, scalingAlgorithm, additional_info);
111 m_details.updateable = ShouldCheckForChanges(additional_info, image);
113 if (m_details.updateable)
115 // generate the hash
116 m_details.hash = GetImageHash(image);
117 if (m_details.hash.empty())
118 return false;
120 if (m_details.hash == m_oldHash)
122 m_details.hashRevalidated = true;
123 return true;
127 std::unique_ptr<CTexture> texture = LoadImage(image, width, height, additional_info, true);
128 if (texture)
130 if (texture->HasAlpha())
131 m_details.file = m_cachePath + ".png";
132 else
133 m_details.file = m_cachePath + ".jpg";
135 CLog::Log(LOGDEBUG, "{} image '{}' to '{}':", m_oldHash.empty() ? "Caching" : "Recaching",
136 CURL::GetRedacted(image), m_details.file);
138 if (CPicture::CacheTexture(texture.get(), width, height,
139 CTextureCache::GetCachedPath(m_details.file), scalingAlgorithm))
141 m_details.width = width;
142 m_details.height = height;
143 if (out_texture) // caller wants the texture
144 *out_texture = std::move(texture);
145 return true;
148 return false;
151 bool CTextureCacheJob::ResizeTexture(const std::string &url, uint8_t* &result, size_t &result_size)
153 result = NULL;
154 result_size = 0;
156 if (url.empty())
157 return false;
159 // unwrap the URL as required
160 std::string additional_info;
161 unsigned int width, height;
162 CPictureScalingAlgorithm::Algorithm scalingAlgorithm;
163 std::string image = DecodeImageURL(url, width, height, scalingAlgorithm, additional_info);
164 if (image.empty())
165 return false;
167 std::unique_ptr<CTexture> texture = LoadImage(image, width, height, additional_info, true);
168 if (texture == NULL)
169 return false;
171 bool success = CPicture::ResizeTexture(image, texture.get(), width, height, result, result_size,
172 scalingAlgorithm);
174 return success;
177 std::string CTextureCacheJob::DecodeImageURL(const std::string &url, unsigned int &width, unsigned int &height, CPictureScalingAlgorithm::Algorithm& scalingAlgorithm, std::string &additional_info)
179 // unwrap the URL as required
180 std::string image(url);
181 additional_info.clear();
182 width = height = 0;
183 scalingAlgorithm = CPictureScalingAlgorithm::NoAlgorithm;
184 if (StringUtils::StartsWith(url, "image://"))
186 // format is image://[type@]<url_encoded_path>?options
187 CURL thumbURL(url);
189 if (!CTextureCache::CanCacheImageURL(thumbURL))
190 return "";
191 if (thumbURL.GetUserName() == "music" || thumbURL.GetUserName() == "video" ||
192 thumbURL.GetUserName() == "picturefolder")
193 additional_info = thumbURL.GetUserName();
194 if (StringUtils::StartsWith(thumbURL.GetUserName(), "video_") ||
195 StringUtils::StartsWith(thumbURL.GetUserName(), "pvr") ||
196 StringUtils::StartsWith(thumbURL.GetUserName(), "epg"))
197 additional_info = thumbURL.GetUserName();
199 image = thumbURL.GetHostName();
201 if (thumbURL.HasOption("flipped"))
202 additional_info = "flipped";
204 if (thumbURL.GetOption("size") == "thumb")
205 width = height = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_imageRes;
206 else
208 if (thumbURL.HasOption("width") && StringUtils::IsInteger(thumbURL.GetOption("width")))
209 width = strtol(thumbURL.GetOption("width").c_str(), NULL, 0);
210 if (thumbURL.HasOption("height") && StringUtils::IsInteger(thumbURL.GetOption("height")))
211 height = strtol(thumbURL.GetOption("height").c_str(), NULL, 0);
214 if (thumbURL.HasOption("scaling_algorithm"))
215 scalingAlgorithm = CPictureScalingAlgorithm::FromString(thumbURL.GetOption("scaling_algorithm"));
218 if (StringUtils::StartsWith(url, "chapter://"))
220 // workaround for chapter thumbnail paths, which don't yet conform to the image:// path.
221 additional_info = "videochapter";
224 // Handle special case about audiodecoder addon music files, e.g. SACD
225 if (StringUtils::EndsWith(URIUtils::GetExtension(image), KODI_ADDON_AUDIODECODER_TRACK_EXT))
227 std::string addonImageURL = URIUtils::GetDirectory(image);
228 URIUtils::RemoveSlashAtEnd(addonImageURL);
229 if (XFILE::CFile::Exists(addonImageURL))
230 image = addonImageURL;
233 return image;
236 std::unique_ptr<CTexture> CTextureCacheJob::LoadImage(const std::string& image,
237 unsigned int width,
238 unsigned int height,
239 const std::string& additional_info,
240 bool requirePixels)
242 if (!additional_info.empty())
244 IMAGE_FILES::CSpecialImageLoaderFactory specialImageLoader{};
245 auto texture = specialImageLoader.Load(additional_info, image, width, height);
246 if (texture)
247 return texture;
250 // Validate file URL to see if it is an image
251 CFileItem file(image, false);
252 file.FillInMimeType();
253 if (!(file.IsPicture() && !(file.IsZIP() || file.IsRAR() || file.IsCBR() || file.IsCBZ() ))
254 && !StringUtils::StartsWithNoCase(file.GetMimeType(), "image/") && !StringUtils::EqualsNoCase(file.GetMimeType(), "application/octet-stream")) // ignore non-pictures
255 return NULL;
257 std::unique_ptr<CTexture> texture =
258 CTexture::LoadFromFile(image, width, height, requirePixels, file.GetMimeType());
259 if (!texture)
260 return NULL;
262 // EXIF bits are interpreted as: <flipXY><flipY*flipX><flipX>
263 // where to undo the operation we apply them in reverse order <flipX>*<flipY*flipX>*<flipXY>
264 // When flipped we have an additional <flipX> on the left, which is equivalent to toggling the last bit
265 if (additional_info == "flipped")
266 texture->SetOrientation(texture->GetOrientation() ^ 1);
268 return texture;
271 std::string CTextureCacheJob::GetImageHash(const std::string &url)
273 // silently ignore - we cannot stat these
274 // in the case of upnp thumbs are/should be provided when filling the directory list, there's no reason to stat all object ids
275 if (URIUtils::IsProtocol(url, "addons") || URIUtils::IsProtocol(url, "plugin") ||
276 URIUtils::IsProtocol(url, "upnp"))
277 return "";
279 struct __stat64 st;
280 if (XFILE::CFile::Stat(url, &st) == 0)
282 int64_t time = st.st_mtime;
283 if (!time)
284 time = st.st_ctime;
285 if (time || st.st_size)
286 return StringUtils::Format("d{}s{}", time, st.st_size);
288 // the image exists but we couldn't determine the mtime/ctime and/or size
289 // so set an obviously bad hash
290 return "BADHASH";
293 CLog::Log(LOGDEBUG, "{} - unable to stat url {}", __FUNCTION__, CURL::GetRedacted(url));
294 return "";
297 CTextureUseCountJob::CTextureUseCountJob(const std::vector<CTextureDetails> &textures) : m_textures(textures)
301 bool CTextureUseCountJob::operator==(const CJob* job) const
303 if (strcmp(job->GetType(),GetType()) == 0)
305 const CTextureUseCountJob* useJob = dynamic_cast<const CTextureUseCountJob*>(job);
306 if (useJob && useJob->m_textures == m_textures)
307 return true;
309 return false;
312 bool CTextureUseCountJob::DoWork()
314 CTextureDatabase db;
315 if (db.Open())
317 db.BeginTransaction();
318 for (std::vector<CTextureDetails>::const_iterator i = m_textures.begin(); i != m_textures.end(); ++i)
319 db.IncrementUseCount(*i);
320 db.CommitTransaction();
322 return true;