Merge pull request #3007 from aballier/ffmpeg_includes
[vuplus_xbmc] / xbmc / GUILargeTextureManager.cpp
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
21 #include "threads/SystemClock.h"
22 #include "GUILargeTextureManager.h"
23 #include "settings/Settings.h"
24 #include "guilib/Texture.h"
25 #include "threads/SingleLock.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/JobManager.h"
28 #include "guilib/GraphicContext.h"
29 #include "utils/log.h"
30 #include "TextureCache.h"
31
32 using namespace std;
33
34
35 CImageLoader::CImageLoader(const CStdString &path)
36 {
37   m_path = path;
38   m_texture = NULL;
39 }
40
41 CImageLoader::~CImageLoader()
42 {
43   delete(m_texture);
44 }
45
46 bool CImageLoader::DoWork()
47 {
48   bool needsChecking = false;
49
50   CStdString texturePath = g_TextureManager.GetTexturePath(m_path);
51   CStdString loadPath = CTextureCache::Get().CheckCachedImage(texturePath, true, needsChecking); 
52
53   if (loadPath.IsEmpty())
54   {
55     // not in our texture cache, so try and load directly and then cache the result
56     loadPath = CTextureCache::Get().CacheImage(texturePath, &m_texture);
57     if (m_texture)
58       return true; // we're done
59   }
60   if (!loadPath.IsEmpty())
61   {
62     // direct route - load the image
63     unsigned int start = XbmcThreads::SystemClockMillis();
64     m_texture = CBaseTexture::LoadFromFile(loadPath, g_graphicsContext.GetWidth(), g_graphicsContext.GetHeight(), CSettings::Get().GetBool("pictures.useexifrotation"));
65     if (!m_texture)
66       return false;
67     if (XbmcThreads::SystemClockMillis() - start > 100)
68       CLog::Log(LOGDEBUG, "%s - took %u ms to load %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - start, loadPath.c_str());
69
70     if (needsChecking)
71       CTextureCache::Get().BackgroundCacheImage(texturePath);
72   }
73   return true;
74 }
75
76 CGUILargeTextureManager::CLargeTexture::CLargeTexture(const CStdString &path)
77 {
78   m_path = path;
79   m_refCount = 1;
80   m_timeToDelete = 0;
81 }
82
83 CGUILargeTextureManager::CLargeTexture::~CLargeTexture()
84 {
85   assert(m_refCount == 0);
86   m_texture.Free();
87 }
88
89 void CGUILargeTextureManager::CLargeTexture::AddRef()
90 {
91   m_refCount++;
92 }
93
94 bool CGUILargeTextureManager::CLargeTexture::DecrRef(bool deleteImmediately)
95 {
96   assert(m_refCount);
97   m_refCount--;
98   if (m_refCount == 0)
99   {
100     if (deleteImmediately)
101       delete this;
102     else
103       m_timeToDelete = CTimeUtils::GetFrameTime() + TIME_TO_DELETE;
104     return true;
105   }
106   return false;
107 }
108
109 bool CGUILargeTextureManager::CLargeTexture::DeleteIfRequired(bool deleteImmediately)
110 {
111   if (m_refCount == 0 && (deleteImmediately || m_timeToDelete < CTimeUtils::GetFrameTime()))
112   {
113     delete this;
114     return true;
115   }
116   return false;
117 }
118
119 void CGUILargeTextureManager::CLargeTexture::SetTexture(CBaseTexture* texture)
120 {
121   assert(!m_texture.size());
122   if (texture)
123     m_texture.Set(texture, texture->GetWidth(), texture->GetHeight());
124 }
125
126 CGUILargeTextureManager::CGUILargeTextureManager()
127 {
128 }
129
130 CGUILargeTextureManager::~CGUILargeTextureManager()
131 {
132 }
133
134 void CGUILargeTextureManager::CleanupUnusedImages(bool immediately)
135 {
136   CSingleLock lock(m_listSection);
137   // check for items to remove from allocated list, and remove
138   listIterator it = m_allocated.begin();
139   while (it != m_allocated.end())
140   {
141     CLargeTexture *image = *it;
142     if (image->DeleteIfRequired(immediately))
143       it = m_allocated.erase(it);
144     else
145       ++it;
146   }
147 }
148
149 // if available, increment reference count, and return the image.
150 // else, add to the queue list if appropriate.
151 bool CGUILargeTextureManager::GetImage(const CStdString &path, CTextureArray &texture, bool firstRequest)
152 {
153   CSingleLock lock(m_listSection);
154   for (listIterator it = m_allocated.begin(); it != m_allocated.end(); ++it)
155   {
156     CLargeTexture *image = *it;
157     if (image->GetPath() == path)
158     {
159       if (firstRequest)
160         image->AddRef();
161       texture = image->GetTexture();
162       return texture.size() > 0;
163     }
164   }
165
166   if (firstRequest)
167     QueueImage(path);
168
169   return true;
170 }
171
172 void CGUILargeTextureManager::ReleaseImage(const CStdString &path, bool immediately)
173 {
174   CSingleLock lock(m_listSection);
175   for (listIterator it = m_allocated.begin(); it != m_allocated.end(); ++it)
176   {
177     CLargeTexture *image = *it;
178     if (image->GetPath() == path)
179     {
180       if (image->DecrRef(immediately) && immediately)
181         m_allocated.erase(it);
182       return;
183     }
184   }
185   for (queueIterator it = m_queued.begin(); it != m_queued.end(); ++it)
186   {
187     unsigned int id = it->first;
188     CLargeTexture *image = it->second;
189     if (image->GetPath() == path && image->DecrRef(true))
190     {
191       // cancel this job
192       CJobManager::GetInstance().CancelJob(id);
193       m_queued.erase(it);
194       return;
195     }
196   }
197 }
198
199 // queue the image, and start the background loader if necessary
200 void CGUILargeTextureManager::QueueImage(const CStdString &path)
201 {
202   CSingleLock lock(m_listSection);
203   for (queueIterator it = m_queued.begin(); it != m_queued.end(); ++it)
204   {
205     CLargeTexture *image = it->second;
206     if (image->GetPath() == path)
207     {
208       image->AddRef();
209       return; // already queued
210     }
211   }
212
213   // queue the item
214   CLargeTexture *image = new CLargeTexture(path);
215   unsigned int jobID = CJobManager::GetInstance().AddJob(new CImageLoader(path), this, CJob::PRIORITY_NORMAL);
216   m_queued.push_back(make_pair(jobID, image));
217 }
218
219 void CGUILargeTextureManager::OnJobComplete(unsigned int jobID, bool success, CJob *job)
220 {
221   // see if we still have this job id
222   CSingleLock lock(m_listSection);
223   for (queueIterator it = m_queued.begin(); it != m_queued.end(); ++it)
224   {
225     if (it->first == jobID)
226     { // found our job
227       CImageLoader *loader = (CImageLoader *)job;
228       CLargeTexture *image = it->second;
229       image->SetTexture(loader->m_texture);
230       loader->m_texture = NULL; // we want to keep the texture, and jobs are auto-deleted.
231       m_queued.erase(it);
232       m_allocated.push_back(image);
233       return;
234     }
235   }
236 }
237
238
239