00001 #include <qpixmap.h>
00002 #include <qimage.h>
00003
00004 #include <map>
00005 #include <list>
00006
00007 #include "mythtv/mythcontext.h"
00008
00009 #include "cleanup.h"
00010 #include "imagecache.h"
00011 #include "quicksp.h"
00012
00013 class ImageCacheImp
00014 {
00015 public:
00016 ImageCacheImp(unsigned int cache_max) : m_max_cache_size(cache_max),
00017 m_clean_stub(this)
00018 {
00019 ensure_cache_min();
00020 }
00021
00022 const QPixmap *load(const QString &image_file)
00023 {
00024 cache_entry_ptr cep = addImage(image_file);
00025 if (cep)
00026 {
00027 return &cep->image;
00028 }
00029
00030 return NULL;
00031 }
00032
00033 const QPixmap *load(const QString &image_file, int width, int height,
00034 QImage::ScaleMode scale)
00035 {
00036 QPixmap *ret = NULL;
00037 cache_entry_ptr cep = addScaleImage(image_file, width, height, scale);
00038 if (cep && !cep->scale_image.isNull())
00039 {
00040 ret = &cep->scale_image;
00041 }
00042
00043 return ret;
00044 }
00045
00046 const QPixmap *load(const QString &image_file, QPixmap *pre_loaded)
00047 {
00048 QPixmap *ret = NULL;
00049 if (pre_loaded)
00050 {
00051 cache_entry_ptr cep(new cache_entry(image_file, pre_loaded));
00052 m_cache.push_back(cep);
00053 ret = &cep->image;
00054 m_cache_index.insert(cache_map::value_type(cep->filename,
00055 --m_cache.end()));
00056 trim_cache();
00057 }
00058 return ret;
00059 }
00060
00061 bool hitTest(const QString &image_file) const
00062 {
00063 return m_cache_index.find(image_file) != m_cache_index.end();
00064 }
00065
00066 void resize(unsigned int new_size)
00067 {
00068 while (m_cache.size() > new_size)
00069 {
00070 unload_first();
00071 }
00072
00073 m_max_cache_size = new_size;
00074 ensure_cache_min();
00075 }
00076
00077 unsigned int size()
00078 {
00079 return m_cache.size();
00080 }
00081
00082 void clear()
00083 {
00084 m_cache.clear();
00085 m_cache_index.clear();
00086 }
00087
00088 void cleanup()
00089 {
00090 clear();
00091 }
00092
00093 private:
00094 struct cache_entry
00095 {
00096 cache_entry(const QString &file) :
00097 filename(file), scale_mode(QImage::ScaleFree),
00098 scale_width(0), scale_height(0)
00099 {
00100 }
00101
00102 cache_entry(const QString &file, QPixmap *img) :
00103 filename(file), scale_mode(QImage::ScaleFree),
00104 scale_width(0), scale_height(0)
00105 {
00106 if (img)
00107 {
00108 image = *img;
00109 }
00110 }
00111
00112 QString filename;
00113 QPixmap image;
00114 QPixmap scale_image;
00115 QImage::ScaleMode scale_mode;
00116 int scale_width;
00117 int scale_height;
00118 };
00119
00120 typedef simple_ref_ptr<cache_entry> cache_entry_ptr;
00121 typedef std::list<cache_entry_ptr> cache_list;
00122 typedef std::map<QString, cache_list::iterator> cache_map;
00123
00124 private:
00125 cache_entry_ptr addImage(const QString &image_file)
00126 {
00127 cache_entry_ptr ret;
00128 cache_map::iterator p = m_cache_index.find(image_file);
00129 if (p != m_cache_index.end())
00130 {
00131 m_cache.push_back(*p->second);
00132 m_cache.erase(p->second);
00133 ret = *(p->second = --m_cache.end());
00134 VERBOSE(VB_FILE, QString("ImageCache hit for: %1")
00135 .arg(image_file));
00136 }
00137 else
00138 {
00139 VERBOSE(VB_FILE, QString("ImageCache miss for: %1")
00140 .arg(image_file));
00141
00142 cache_entry_ptr cep(new cache_entry(image_file));
00143
00144 if (cep->image.load(cep->filename))
00145 {
00146 m_cache.push_back(cep);
00147 m_cache_index.insert(cache_map::value_type(cep->filename,
00148 --m_cache.end()));
00149 ret = cep;
00150 }
00151
00152 trim_cache();
00153 }
00154
00155 return ret;
00156 }
00157
00158 cache_entry_ptr addScaleImage(const QString &image_file, int width,
00159 int height, QImage::ScaleMode scale)
00160 {
00161 cache_entry_ptr ret = addImage(image_file);
00162 if (ret && !ret->image.isNull())
00163 {
00164 if (ret->scale_image.isNull() || ret->scale_mode != scale ||
00165 ret->scale_width != width || ret->scale_height != height)
00166 {
00167 VERBOSE(VB_FILE,
00168 QString("ImageCache miss for scale image: %1")
00169 .arg(image_file));
00170 ret->scale_mode = scale;
00171 QImage scale_me(ret->image.convertToImage());
00172 ret->scale_image.
00173 convertFromImage(scale_me.smoothScale(width,height,
00174 scale));
00175 ret->scale_width = width;
00176 ret->scale_height = height;
00177 }
00178 else
00179 {
00180 VERBOSE(VB_FILE,
00181 QString("ImageCache hit for scale image: %1")
00182 .arg(image_file));
00183 }
00184 }
00185
00186 return ret;
00187 }
00188
00189 void unload_first()
00190 {
00191 if (m_cache.size())
00192 {
00193 const QString &filename = m_cache.front()->filename;
00194 cache_map::iterator p = m_cache_index.find(filename);
00195 if (p != m_cache_index.end())
00196 {
00197 m_cache_index.erase(p);
00198 }
00199 m_cache.pop_front();
00200 }
00201 }
00202
00203 void trim_cache()
00204 {
00205 if (m_cache.size() > m_max_cache_size)
00206 {
00207 unload_first();
00208 }
00209 }
00210
00211 void ensure_cache_min()
00212 {
00213 if (m_max_cache_size < 2) m_max_cache_size = 2;
00214 }
00215
00216 private:
00217 cache_list m_cache;
00218 cache_map m_cache_index;
00219 unsigned int m_max_cache_size;
00220 SimpleCleanup<ImageCacheImp> m_clean_stub;
00221 };
00222
00223 ImageCache &ImageCache::getImageCache()
00224 {
00225 static ImageCache image_cache;
00226 return image_cache;
00227 }
00228
00229 const QPixmap *ImageCache::load(const QString &image_file)
00230 {
00231 return m_imp->load(image_file);
00232 }
00233
00234 const QPixmap *ImageCache::load(const QString &image_file, int width,
00235 int height, QImage::ScaleMode scale)
00236 {
00237 return m_imp->load(image_file, width, height, scale);
00238 }
00239
00240 const QPixmap *ImageCache::load(const QString &image_file, QPixmap *pre_loaded)
00241 {
00242 return m_imp->load(image_file, pre_loaded);
00243 }
00244
00245 bool ImageCache::hitTest(const QString &image_file) const
00246 {
00247 return m_imp->hitTest(image_file);
00248 }
00249
00250 void ImageCache::resize(unsigned int new_size)
00251 {
00252 return m_imp->resize(new_size);
00253 }
00254
00255 unsigned int ImageCache::size()
00256 {
00257 return m_imp->size();
00258 }
00259
00260 void ImageCache::clear()
00261 {
00262 m_imp->clear();
00263 }
00264
00265 ImageCache::ImageCache()
00266 {
00267 m_imp = new ImageCacheImp(gContext->
00268 GetNumSetting("mythvideo.ImageCacheSize", 50));
00269 }
00270
00271 ImageCache::~ImageCache()
00272 {
00273 delete m_imp;
00274 }