Index: libs/libmythui/mythpainter_vdpau.cpp =================================================================== --- libs/libmythui/mythpainter_vdpau.cpp (revision 22170) +++ libs/libmythui/mythpainter_vdpau.cpp (working copy) @@ -9,6 +9,7 @@ #include #include #include +#include #include // Mythdb headers @@ -87,6 +88,9 @@ int surfaceNum; bool initialized; + std::list m_surfaceDeleteList; + QMutex m_surfaceDeleteLock; + VdpOutputSurface curOutput; VdpRect outRect; @@ -369,6 +373,19 @@ initialized = true; } + if (m_surfaceDeleteList.size()) + { + m_surfaceDeleteLock.lock(); + while (m_surfaceDeleteList.size()) + { + VdpBitmapSurface bitmap = m_surfaceDeleteList.front(); + m_surfaceDeleteList.pop_front(); + + vdp_bitmap_surface_destroy(bitmap); + } + m_surfaceDeleteLock.unlock(); + } + curOutput = outputSurfaces[surfaceNum]; vdp_st = vdp_presentation_queue_block_until_surface_idle( vdpFlipQueue, @@ -411,8 +428,9 @@ { if (m_ImageBitmapMap.contains(im)) { - VdpBitmapSurface bitmap = m_ImageBitmapMap[im]; - vdp_bitmap_surface_destroy(bitmap); + m_surfaceDeleteLock.lock(); + m_surfaceDeleteList.push_back(m_ImageBitmapMap[im]); + m_surfaceDeleteLock.unlock(); m_ImageBitmapMap.remove(im); m_ImageExpireList.remove(im); Index: libs/libmythui/mythpainter_ogl.h =================================================================== --- libs/libmythui/mythpainter_ogl.h (revision 22170) +++ libs/libmythui/mythpainter_ogl.h (working copy) @@ -2,6 +2,7 @@ #define MYTHPAINTER_OPENGL_H_ #include +#include #include @@ -60,6 +61,9 @@ bool init_extensions; bool generate_mipmaps; + + std::list m_textureDeleteList; + QMutex m_textureDeleteLock; }; #endif Index: libs/libmythui/mythuiimage.cpp =================================================================== --- libs/libmythui/mythuiimage.cpp (revision 22173) +++ libs/libmythui/mythuiimage.cpp (working copy) @@ -1,20 +1,81 @@ #include "mythuiimage.h" +// C/C++ #include +// QT #include #include #include #include +#include +#include +#include +// Libmythdb #include "mythverbose.h" +// Mythui #include "mythpainter.h" #include "mythmainwindow.h" #include "mythuihelper.h" #include "mythscreentype.h" +class ImageLoadThread; + +const int kImageLoadEventType = 35112; + +/*! + * \class ImageLoadThread + */ +class ImageLoadEvent : public QEvent +{ + public: + ImageLoadEvent(ImageLoadThread *thread, MythImage *image, + const QString &filename, int number) + : QEvent((QEvent::Type)kImageLoadEventType), + m_thread(thread), m_image(image), m_filename(filename), + m_number(number) { } + + ImageLoadThread *GetThread() const { return m_thread; } + MythImage *GetImage() const { return m_image; } + const QString GetFilename() const { return m_filename; } + const int GetNumber() const { return m_number; } + + private: + ImageLoadThread *m_thread; + MythImage *m_image; + QString m_filename; + int m_number; +}; + +/*! +* \class ImageLoadThread +*/ +class ImageLoadThread : public QRunnable +{ + public: + ImageLoadThread(MythUIImage *parent, const QString &filename, + int number) + : m_parent(parent), m_filename(filename), m_number(number) { }; + + void run() + { + MythImage *image = m_parent->LoadImage(m_filename, m_number); + ImageLoadEvent *le = new ImageLoadEvent(this, image, m_filename, + m_number); + QCoreApplication::postEvent(m_parent, le); + } + + private: + MythUIImage *m_parent; + QString m_filename; + int m_number; +}; + +///////////////////////////////////////////////////////////////// + MythUIImage::MythUIImage(const QString &filepattern, int low, int high, int delayms, MythUIType *parent, const QString &name) @@ -26,6 +87,8 @@ m_Delay = delayms; + m_imageLoadThread = NULL; + Init(); } @@ -65,10 +128,12 @@ */ void MythUIImage::Clear(void) { + QMutexLocker locker(&m_ImagesLock); while (!m_Images.isEmpty()) { - m_Images.back()->DownRef(); - m_Images.pop_back(); + QHash::iterator it = m_Images.begin(); + (*it)->DownRef(); + m_Images.remove(it.key()); } } @@ -192,7 +257,10 @@ if (m_ForceSize.isNull()) SetSize(img->size()); - m_Images.push_back(img); + m_ImagesLock.lock(); + m_Images[0] = img; + m_ImagesLock.unlock(); + m_CurPos = 0; SetRedraw(); } @@ -237,7 +305,9 @@ if (m_isGreyscale && !im->isGrayscale()) im->ToGreyscale(); - m_Images.push_back(im); + m_ImagesLock.lock(); + m_Images[m_Images.size()] = im; + m_ImagesLock.unlock(); aSize = aSize.expandedTo(im->size()); } @@ -263,8 +333,9 @@ return; QSize aSize = m_Area.size(); + QMutexLocker locker(&m_ImagesLock); - QVector::iterator it; + QHash::iterator it; for (it = m_Images.begin(); it != m_Images.end(); ++it) { MythImage *im = (*it); @@ -319,7 +390,7 @@ * \brief Generates a unique identifying string for this image which is used * as a key in the image cache. */ -QString MythUIImage::GenImageLabel(const QString &filename, int w, int h) +QString MythUIImage::GenImageLabel(const QString &filename, int w, int h) const { QString imagelabel; QString s_Attrib; @@ -347,165 +418,214 @@ * \brief Generates a unique identifying string for this image which is used * as a key in the image cache. */ -QString MythUIImage::GenImageLabel(int w, int h) +QString MythUIImage::GenImageLabel(int w, int h) const { return GenImageLabel(m_Filename, w, h); } /** - * \brief Load the image(s) + * \brief Load the image(s), wraps LoadImage() */ -bool MythUIImage::Load(void) +bool MythUIImage::Load(bool allowLoadInBackground) { + if (m_Filename.isEmpty() && (!m_gradient)) + { + Clear(); + SetRedraw(); + + return false; + } + Clear(); - SetRedraw(); +// SetRedraw(); -// if (!IsVisible(true)) -// return false; +// if (!IsVisible(true)) +// return false; - if (m_Filename.isEmpty() && (!m_gradient)) - return false; + int w = -1; + int h = -1; - QSize newSize; + if (!m_ForceSize.isNull()) + { + if (m_ForceSize.width() != -1) + w = m_ForceSize.width(); - VERBOSE(VB_FILE, QString("MythUIImage::Load (%1) Object %2").arg(m_Filename).arg(objectName())); + if (m_ForceSize.height() != -1) + h = m_ForceSize.height(); + } + QString imagelabel; + QString filename = m_Filename; + for (int i = m_LowNum; i <= m_HighNum; i++) { - MythImage *image = NULL; - bool bNeedLoad = false; + if (m_HighNum >= 1) + filename = m_Filename.arg(i); - if (m_gradient) + imagelabel = GenImageLabel(filename, w, h); + + // Only load in the background if allowed and the image is + // not already in our mem cache + if ((allowLoadInBackground) && + (!GetMythUI()->LoadCacheImage(filename, imagelabel, false))) { - QSize gradsize; - if (!m_Area.isEmpty()) - gradsize = m_Area.size(); - else - gradsize = QSize(10, 10); - - image = MythImage::Gradient(gradsize, m_gradientStart, - m_gradientEnd, m_gradientAlpha, - m_gradientDirection); - image->UpRef(); + m_imageLoadThread = new ImageLoadThread(this, filename, i); + GetMythUI()->GetImageThreadPool()->start(m_imageLoadThread); } else - bNeedLoad = true; - - int w = -1; - int h = -1; - - bool bForceResize = false; - bool bFoundInCache = false; - - QString imagelabel; - - if (!m_ForceSize.isNull()) { - if (m_ForceSize.width() != -1) - w = m_ForceSize.width(); + // Perform a blocking load + MythImage *image = LoadImage(filename, i); + if (image) + { + if (m_ForceSize.isNull()) + SetSize(image->size()); - if (m_ForceSize.height() != -1) - h = m_ForceSize.height(); + m_ImagesLock.lock(); + m_Images[i] = image; + m_ImagesLock.unlock(); - bForceResize = true; + SetRedraw(); + m_LastDisplay = QTime::currentTime(); + } } + } - if (bNeedLoad) - { + return true; +} - QString filename = m_Filename; - if (m_HighNum >= 1) - filename = QString(m_Filename).arg(i); +/** +* \brief Load an image +*/ +MythImage *MythUIImage::LoadImage(const QString &imFile, int imageNumber) const +{ + QString filename = imFile; - imagelabel = GenImageLabel(filename, w, h); + VERBOSE(VB_FILE, QString("MythUIImage::LoadImage(%1) Object %2").arg(filename) + .arg(objectName())); - image = GetMythUI()->LoadCacheImage(filename, imagelabel); - if (image) - { - image->UpRef(); - VERBOSE(VB_FILE, QString("MythUIImage::Load found in cache :%1:").arg(imagelabel)); - if (m_isReflected) - image->setIsReflected(true); + MythImage *image = NULL; + bool bNeedLoad = false; - bFoundInCache = true; - } - else - { - VERBOSE(VB_FILE, QString("MythUIImage::Load Not Found in " - "cache. Loading Directly :%1:") - .arg(filename)); - image = GetMythPainter()->GetFormatImage(); - image->UpRef(); - if (!image->Load(filename)) - { - VERBOSE(VB_FILE, QString("MythUIImage::Load Could not load :%1:").arg(filename)); - image->DownRef(); - SetRedraw(); - return false; - } - } - } + if (m_gradient) + { + QSize gradsize; + if (!m_Area.isEmpty()) + gradsize = m_Area.size(); else - imagelabel = GenImageLabel(w,h); + gradsize = QSize(10, 10); - if (!bFoundInCache) - { - if (bForceResize) - image->Resize(QSize(w, h), m_preserveAspect); + image = MythImage::Gradient(gradsize, m_gradientStart, + m_gradientEnd, m_gradientAlpha, + m_gradientDirection); + image->UpRef(); + } + else + bNeedLoad = true; - if (m_isMasked) - { - QRect imageArea = image->rect(); - QRect maskArea = m_maskImage->rect(); - // Crop the mask to the image - int x = 0; - int y = 0; - if (maskArea.width() > imageArea.width()) - x = (maskArea.width() - imageArea.width()) / 2; - if (maskArea.height() > imageArea.height()) - y = (maskArea.height() - imageArea.height()) / 2; + bool bForceResize = false; + bool bFoundInCache = false; - if (x > 0 || y > 0) - imageArea.translate(x,y); - QImage mask = m_maskImage->copy(imageArea); - image->setAlphaChannel(mask.alphaChannel()); - } + QString imagelabel; - if (m_isReflected) - image->Reflect(m_reflectAxis, m_reflectShear, m_reflectScale, - m_reflectLength, m_reflectSpacing); + int w = -1; + int h = -1; - if (m_isGreyscale) - image->ToGreyscale(); + if (!m_ForceSize.isNull()) + { + if (m_ForceSize.width() != -1) + w = m_ForceSize.width(); - // Save scaled copy to cache - if (bNeedLoad) - GetMythUI()->CacheImage(imagelabel, image); - } + if (m_ForceSize.height() != -1) + h = m_ForceSize.height(); - if (!bForceResize) + bForceResize = true; + } + + if (bNeedLoad) + { + imagelabel = GenImageLabel(filename, w, h); + + image = GetMythUI()->LoadCacheImage(filename, imagelabel); + if (image) { - newSize = newSize.expandedTo(image->size()); - SetSize(newSize); - } + image->UpRef(); + VERBOSE(VB_FILE, QString("MythUIImage::LoadImage found in cache :%1:").arg(imagelabel)); + if (m_isReflected) + image->setIsReflected(true); - if (image->isNull()) - { - VERBOSE(VB_FILE, QString("MythUIImage::Load Image is NULL :%1:").arg(m_Filename)); - image->DownRef(); + bFoundInCache = true; } else { - image->SetChanged(); - m_Images.push_back(image); + VERBOSE(VB_FILE, QString("MythUIImage::LoadImage Not Found in " + "cache. Loading Directly :%1:") + .arg(filename)); + image = GetMythPainter()->GetFormatImage(); + image->UpRef(); + if (!image->Load(filename)) + { + VERBOSE(VB_FILE, QString("MythUIImage::LoadImage Could not load " + ":%1:").arg(filename)); + image->DownRef(); + return NULL; + } } } + else + imagelabel = GenImageLabel(w,h); - m_LastDisplay = QTime::currentTime(); + if (!bFoundInCache) + { + if (bForceResize) + image->Resize(QSize(w, h), m_preserveAspect); - return true; + if (m_isMasked) + { + QRect imageArea = image->rect(); + QRect maskArea = m_maskImage->rect(); + + // Crop the mask to the image + int x = 0; + int y = 0; + if (maskArea.width() > imageArea.width()) + x = (maskArea.width() - imageArea.width()) / 2; + if (maskArea.height() > imageArea.height()) + y = (maskArea.height() - imageArea.height()) / 2; + + if (x > 0 || y > 0) + imageArea.translate(x,y); + QImage mask = m_maskImage->copy(imageArea); + image->setAlphaChannel(mask.alphaChannel()); + } + + if (m_isReflected) + image->Reflect(m_reflectAxis, m_reflectShear, m_reflectScale, + m_reflectLength, m_reflectSpacing); + + if (m_isGreyscale) + image->ToGreyscale(); + + // Save scaled copy to cache + if (bNeedLoad) + GetMythUI()->CacheImage(imagelabel, image); + } + + if (image->isNull()) + { + VERBOSE(VB_FILE, QString("MythUIImage::LoadImage Image is NULL :%1:") + .arg(filename)); + image->DownRef(); + return NULL; + } + else + { + image->SetChanged(); + } + + return image; } /** @@ -516,9 +636,15 @@ if (m_Delay > 0 && abs(m_LastDisplay.msecsTo(QTime::currentTime())) > m_Delay) { - m_CurPos++; - if (m_CurPos >= (uint)m_Images.size()) - m_CurPos = 0; + m_ImagesLock.lock(); + unsigned int origPos = m_CurPos; + do + { + m_CurPos++; + if (m_CurPos >= (uint)m_Images.size()) + m_CurPos = 0; + } while (!m_Images.contains(m_CurPos) && m_CurPos != origPos); + m_ImagesLock.unlock(); SetRedraw(); m_LastDisplay = QTime::currentTime(); @@ -533,10 +659,22 @@ void MythUIImage::DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) { + m_ImagesLock.lock(); if (m_Images.size() > 0) { if (m_CurPos > (uint)m_Images.size()) m_CurPos = 0; + if (!m_Images.contains(m_CurPos)) + { + unsigned int origPos = m_CurPos; + m_CurPos++; + while (!m_Images.contains(m_CurPos) && m_CurPos != origPos) + { + m_CurPos++; + if (m_CurPos >= (uint)m_Images.size()) + m_CurPos = 0; + } + } QRect area = m_Area.toQRect(); area.translate(xoffset, yoffset); @@ -544,6 +682,7 @@ int alpha = CalcAlpha(alphaMod); MythImage *currentImage = m_Images[m_CurPos]; + m_ImagesLock.unlock(); QRect currentImageArea = currentImage->rect(); if (!m_ForceSize.isNull()) @@ -569,6 +708,8 @@ p->DrawImage(area, currentImage, srcRect, alpha); } + else + m_ImagesLock.unlock(); } /** @@ -784,8 +925,65 @@ return; m_NeedLoad = true; - Load(); + Load(false); MythUIType::LoadNow(); } +/** +* \copydoc MythUIType::customEvent() +*/ +void MythUIImage::customEvent(QEvent *event) +{ + if (event->type() == kImageLoadEventType) + { + ImageLoadEvent *le = dynamic_cast(event); + + ImageLoadThread *thread = le->GetThread(); + + if (thread && m_imageLoadThread && thread != m_imageLoadThread) + return; + + MythImage *image = le->GetImage(); + + if (image) + { + QString filename = le->GetFilename(); + + if (filename == m_Filename) + { + int number = le->GetNumber(); + + if (m_ForceSize.isNull()) + SetSize(image->size()); + + m_ImagesLock.lock(); + if (m_Images.contains(number)) + { + // There should be no image at this location, if + // we find one then something is wrong. + VERBOSE(VB_IMPORTANT, QString("DEBUG Image Thread: " + "Found image in m_Images position %1 where " + "there should be none. Filename: %2") + .arg(number).arg(filename)); + image->DownRef(); + } + else + { + m_Images[number] = image; + } + m_ImagesLock.unlock(); + + SetRedraw(); + m_LastDisplay = QTime::currentTime(); + } + else + { + // should never get here unless a non-thread sent the event + VERBOSE(VB_IMPORTANT, QString("Mis-Match File: %1 Not: %2") + .arg(filename).arg(m_Filename)); + image->DownRef(); + } + } + } +} Index: libs/libmythui/mythuihelper.h =================================================================== --- libs/libmythui/mythuihelper.h (revision 22173) +++ libs/libmythui/mythuihelper.h (working copy) @@ -5,6 +5,7 @@ #include #include #include +#include #include "mythexp.h" @@ -64,7 +65,8 @@ QPixmap *LoadScalePixmap(QString filename, bool fromcache = true); QImage *LoadScaleImage(QString filename, bool fromcache = true); - MythImage *LoadCacheImage(QString srcfile, QString label); + MythImage *LoadCacheImage(QString srcfile, QString label, + bool allowLoadFromDisk = true); void ThemeWidget(QWidget *widget); @@ -113,6 +115,8 @@ QString RemoveCurrentLocation(void); QString GetCurrentLocation(bool fullPath = false, bool mainStackOnly = true); + QThreadPool *GetImageThreadPool(void); + protected: MythUIHelper(); ~MythUIHelper(); @@ -126,8 +130,6 @@ MythUIHelperPrivate *d; - size_t m_cacheSize; - QMutex m_locationLock; QStringList m_currentLocation; }; Index: libs/libmythui/mythpainter_ogl.cpp =================================================================== --- libs/libmythui/mythpainter_ogl.cpp (revision 22170) +++ libs/libmythui/mythpainter_ogl.cpp (working copy) @@ -93,6 +93,22 @@ return; } + if (m_textureDeleteList.size()) + { + GLuint textures[1]; + + m_textureDeleteLock.lock(); + while (m_textureDeleteList.size()) + { + textures[0] = m_textureDeleteList.front(); + m_textureDeleteList.pop_front(); + + glDeleteTextures(1, textures); + } + m_textureDeleteLock.unlock(); + } + + realParent->makeCurrent(); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); @@ -156,10 +172,10 @@ { if (m_ImageIntMap.contains(im)) { - GLuint textures[1]; - textures[0] = m_ImageIntMap[im]; + m_textureDeleteLock.lock(); + m_textureDeleteList.push_back(m_ImageIntMap[im]); + m_textureDeleteLock.unlock(); - glDeleteTextures(1, textures); m_ImageIntMap.remove(im); m_ImageExpireList.remove(im); Index: libs/libmythui/mythuihelper.cpp =================================================================== --- libs/libmythui/mythuihelper.cpp (revision 22173) +++ libs/libmythui/mythuihelper.cpp (working copy) @@ -14,6 +14,7 @@ #include #include #include +#include #include "mythdirs.h" #include "mythverbose.h" @@ -97,6 +98,8 @@ QMap imageCache; QMap CacheTrack; + QMutex *m_cacheLock; + size_t m_cacheSize; uint maxImageCacheSize; @@ -121,6 +124,8 @@ DisplayRes *display_res; bool screenSetup; + QThreadPool *m_imageThreadPool; + MythUIMenuCallbacks callbacks; MythUIHelper *parent; @@ -138,10 +143,11 @@ m_wmult(1.0), m_hmult(1.0), m_xbase(0), m_ybase(0), m_height(0), m_width(0), m_baseWidth(800), m_baseHeight(600), m_isWide(false), + m_cacheLock(new QMutex(QMutex::Recursive)), m_cacheSize(0), m_screenxbase(0), m_screenybase(0), m_screenwidth(0), m_screenheight(0), bigfontsize(0), mediumfontsize(0), smallfontsize(0), screensaver(NULL), screensaverEnabled(false), display_res(NULL), - screenSetup(false), parent(p) + screenSetup(false), m_imageThreadPool(new QThreadPool()), parent(p) { } @@ -158,6 +164,7 @@ CacheTrack.clear(); + delete m_imageThreadPool; delete m_qtThemeSettings; delete screensaver; if(display_res) @@ -322,7 +329,6 @@ } MythUIHelper::MythUIHelper() - : m_cacheSize(0) { d = new MythUIHelperPrivate(this); } @@ -336,8 +342,11 @@ { d->Init(); d->callbacks = cbs; + + d->m_cacheLock->lock(); d->maxImageCacheSize = GetMythDB()->GetNumSetting("UIImageCacheSize", 20) * 1024 * 1024; + d->m_cacheLock->unlock(); VERBOSE(VB_GENERAL, QString("MythUI Image Cache size set to %1 bytes") .arg(d->maxImageCacheSize)); @@ -424,6 +433,8 @@ void MythUIHelper::UpdateImageCache(void) { + QMutexLocker locker(d->m_cacheLock); + QMutableMapIterator i(d->imageCache); while (i.hasNext()) { @@ -434,13 +445,14 @@ } d->CacheTrack.clear(); - m_cacheSize = 0; + d->m_cacheSize = 0; ClearOldImageCache(); } MythImage *MythUIHelper::GetImageFromCache(const QString &url) { + QMutexLocker locker(d->m_cacheLock); if (d->imageCache.contains(url)) return d->imageCache[url]; @@ -461,7 +473,9 @@ if (!im) return; - m_cacheSize += im->numBytes(); + d->m_cacheLock->lock(); + d->m_cacheSize += im->numBytes(); + d->m_cacheLock->unlock(); } void MythUIHelper::ExcludeFromCacheSize(MythImage *im) @@ -469,7 +483,9 @@ if (!im) return; - m_cacheSize -= im->numBytes(); + d->m_cacheLock->lock(); + d->m_cacheSize -= im->numBytes(); + d->m_cacheLock->unlock(); } MythImage *MythUIHelper::CacheImage(const QString &url, MythImage *im, @@ -494,7 +510,8 @@ } // delete the oldest cached images until we fall below threshold. - while (m_cacheSize + im->numBytes() >= d->maxImageCacheSize && + QMutexLocker locker(d->m_cacheLock); + while (d->m_cacheSize + im->numBytes() >= d->maxImageCacheSize && d->imageCache.size()) { QMap::iterator it = d->imageCache.begin(); @@ -519,7 +536,7 @@ if (count > 0) { VERBOSE(VB_FILE, QString("Cache too big (%1), removing :%2:") - .arg(m_cacheSize + im->numBytes()).arg(oldestKey)); + .arg(d->m_cacheSize + im->numBytes()).arg(oldestKey)); d->imageCache[oldestKey]->DownRef(); d->imageCache.remove(oldestKey); @@ -538,18 +555,19 @@ im->SetIsInCache(true); VERBOSE(VB_FILE, QString("NOT IN RAM CACHE, Adding, and adding to size " - ":%1: :%2:").arg(url).arg(m_cacheSize)); + ":%1: :%2:").arg(url).arg(d->m_cacheSize)); } VERBOSE(VB_FILE, QString("MythUIHelper::CacheImage : Cache Count = :%1: " "size :%2:") - .arg(d->imageCache.count()).arg(m_cacheSize)); + .arg(d->imageCache.count()).arg(d->m_cacheSize)); return d->imageCache[url]; } void MythUIHelper::RemoveFromCacheByURL(const QString &url) { + QMutexLocker locker(d->m_cacheLock); QMap::iterator it = d->imageCache.find(url); if (it != d->imageCache.end()) @@ -576,11 +594,13 @@ QString partialKey = fname; partialKey.replace('/', '-'); + d->m_cacheLock->lock(); for (it = d->imageCache.begin(); it != d->imageCache.end(); ++it) { if (it.key().contains(partialKey)) RemoveFromCacheByURL(it.key()); } + d->m_cacheLock->unlock(); // Loop through files to cache any that were not caught by // RemoveFromCacheByURL @@ -600,6 +620,8 @@ bool MythUIHelper::IsImageInCache(const QString &url) { + QMutexLocker locker(d->m_cacheLock); + if (d->imageCache.contains(url)) return true; @@ -1216,7 +1238,8 @@ return ret; } -MythImage *MythUIHelper::LoadCacheImage(QString srcfile, QString label) +MythImage *MythUIHelper::LoadCacheImage(QString srcfile, QString label, + bool allowLoadFromDisk) { //VERBOSE(VB_GENERAL, QString("LoadCacheImage %1:%2").arg(srcfile).arg(label)); @@ -1234,7 +1257,7 @@ { // Check Memory Cache ret = GetImageFromCache(label); - if (!ret) + if (!ret && allowLoadFromDisk) { // Load file from disk cache to memory cache ret = GetMythPainter()->GetFormatImage(); @@ -1458,3 +1481,8 @@ return result; } + +QThreadPool *MythUIHelper::GetImageThreadPool(void) +{ + return d->m_imageThreadPool; +} Index: libs/libmythui/mythuiimage.h =================================================================== --- libs/libmythui/mythuiimage.h (revision 22173) +++ libs/libmythui/mythuiimage.h (working copy) @@ -2,11 +2,14 @@ #define MYTHUI_IMAGE_H_ #include +#include +#include #include "mythuitype.h" #include "mythimage.h" class MythScreenType; +class ImageLoadThread; /** * \class MythUIImage @@ -22,6 +25,8 @@ MythUIImage(MythUIType *parent, const QString &name); ~MythUIImage(); + QString GetFilename(void) { return m_Filename; } + /** Must be followed by a call to Load() to load the image. */ void SetFilename(const QString &filename); @@ -43,7 +48,7 @@ void SetDelay(int delayms); void Reset(void); - bool Load(void); + bool Load(bool allowLoadInBackground = true); bool IsGradient(void) const { return m_gradient; } @@ -57,6 +62,8 @@ void Init(void); void Clear(void); + MythImage* LoadImage(const QString &imFile, int imageNumber = 0) const; + void customEvent(QEvent *event); virtual bool ParseElement(QDomElement &element); virtual void CopyFrom(MythUIType *base); @@ -70,13 +77,14 @@ void SetCropRect(int x, int y, int width, int height); void SetCropRect(const MythRect &rect); - QString GenImageLabel(const QString &filename, int w, int h); - QString GenImageLabel(int w, int h); + QString GenImageLabel(const QString &filename, int w, int h) const; + QString GenImageLabel(int w, int h) const; QString m_Filename; QString m_OrigFilename; - QVector m_Images; + QHash m_Images; + QMutex m_ImagesLock; MythRect m_cropRect; QSize m_ForceSize; @@ -110,10 +118,13 @@ bool m_isGreyscale; + ImageLoadThread *m_imageLoadThread; + friend class MythThemeBase; friend class MythUIButtonListItem; friend class MythUIProgressBar; friend class MythUITextEdit; + friend class ImageLoadThread; }; #endif Index: programs/mythfrontend/playbackbox.cpp =================================================================== --- programs/mythfrontend/playbackbox.cpp (revision 22171) +++ programs/mythfrontend/playbackbox.cpp (working copy) @@ -541,15 +541,15 @@ InfoMap infoMap; int countInGroup; - if (m_fanart) - m_fanart->SetVisible(false); - if (groupname.isEmpty()) { countInGroup = m_progLists[""].size(); infoMap["title"] = m_groupDisplayName; infoMap["group"] = m_groupDisplayName; infoMap["show"] = ProgramInfo::i18n("All Programs"); + + if (m_fanart) + m_fanart->Reset(); } else { @@ -561,30 +561,26 @@ if (m_fanart) { - m_fanart->Reset(); - m_fanartTimer->stop(); - - QString fanartHost; + static int itemsPast = 0; +VERBOSE(VB_IMPORTANT, QString("have m_fanart, itemsPast: %1").arg(itemsPast)); + QString artworkHost; if (gContext->GetNumSetting("MasterBackendOverride", 0)) - fanartHost = gContext->GetMasterHostName(); + artworkHost = gContext->GetMasterHostName(); else - fanartHost = (*(m_progLists[groupname].begin()))->hostname; + artworkHost = (*(m_progLists[groupname].begin()))->hostname; - QString fanartDir = gContext->GetSetting("mythvideo.fanartDir"); - QString fanartTitle = groupname; - QString fanartSeriesID; - QString fanartFile = testImageFiles(fanartDir, fanartSeriesID, - fanartTitle, "fanart", - fanartHost); - if (!fanartFile.isEmpty()) - { - m_fanart->SetFilename(fanartFile); - - m_fanartTimer->setSingleShot(true); - m_fanartTimer->start(500); - } + QString artworkDir = gContext->GetSetting("mythvideo.fanartDir"); + QString artworkTitle = groupname; + QString artworkSeriesID; + QString artworkFile = findArtworkFile(artworkDir, artworkSeriesID, + artworkTitle, "fanart", + artworkHost); + if (loadArtwork(artworkFile, m_fanart, m_fanartTimer, 300, + itemsPast > 2)) + itemsPast++; + else + itemsPast = 0; } - } if (countInGroup > 1) @@ -615,13 +611,13 @@ jobState->Reset(); if (m_previewImage) - m_previewImage->SetVisible(false); + m_previewImage->Reset(); if (m_banner) - m_banner->SetVisible(false); + m_banner->Reset(); if (m_coverart) - m_coverart->SetVisible(false); + m_coverart->Reset(); updateIcons(); } @@ -684,84 +680,57 @@ if (m_previewImage) { imagefile = (imagefile.isEmpty()) ? oldimgfile : imagefile; - m_previewImage->SetVisible(!imagefile.isEmpty()); m_previewImage->SetFilename(imagefile); m_previewImage->Load(); } - QString artworkHost; - if (gContext->GetNumSetting("MasterBackendOverride", 0)) - artworkHost = gContext->GetMasterHostName(); - else - artworkHost = pginfo->hostname; - - if (m_fanart) + if (m_fanart || m_banner || m_coverart) { - m_fanartTimer->stop(); - m_fanart->SetVisible(false); - m_fanart->Reset(); + QString artworkHost; + QString artworkDir; + QString artworkFile; + QString artworkTitle = pginfo->title; + QString artworkSeriesID = pginfo->seriesid; - QString fanartDir = gContext->GetSetting("mythvideo.fanartDir"); - QString fanartTitle = pginfo->title; - QString fanartSeriesID = pginfo->seriesid; - QString fanartFile = testImageFiles(fanartDir, - fanartSeriesID, - fanartTitle, - "fanart", - artworkHost); - if (!fanartFile.isEmpty()) + if (gContext->GetNumSetting("MasterBackendOverride", 0)) + artworkHost = gContext->GetMasterHostName(); + else + artworkHost = pginfo->hostname; + + if (m_fanart) { - m_fanart->SetFilename(fanartFile); - - m_fanartTimer->stop(); - m_fanartTimer->setSingleShot(true); - m_fanartTimer->start(500); + static int itemsPast = 0; +VERBOSE(VB_IMPORTANT, QString("have m_fanart, itemsPast: %1").arg(itemsPast)); + artworkDir = gContext->GetSetting("mythvideo.fanartDir"); + artworkFile = findArtworkFile(artworkDir, artworkSeriesID, + artworkTitle, "fanart", + artworkHost); + if (loadArtwork(artworkFile, m_fanart, m_fanartTimer, 300, + itemsPast > 2)) + itemsPast++; + else + itemsPast = 0; } - } - if (m_banner) - { - m_bannerTimer->stop(); - m_banner->SetVisible(false); - m_banner->Reset(); - - QString bannerDir = gContext->GetSetting("mythvideo.bannerDir"); - QString bannerTitle = pginfo->title; - QString bannerSeriesID = pginfo->seriesid; - QString bannerFile = testImageFiles(bannerDir, - bannerSeriesID, - bannerTitle, - "banner", - artworkHost); - if (!bannerFile.isEmpty()) + if (m_banner) { - m_banner->SetFilename(bannerFile); - - m_bannerTimer->setSingleShot(true); - m_bannerTimer->start(500); +VERBOSE(VB_IMPORTANT, "have m_banner"); + artworkDir = gContext->GetSetting("mythvideo.bannerDir"); + artworkFile = findArtworkFile(artworkDir, artworkSeriesID, + artworkTitle, "banner", + artworkHost); + loadArtwork(artworkFile, m_banner, m_bannerTimer, 50); } - } - if (m_coverart) - { - m_coverartTimer->stop(); - m_coverart->SetVisible(false); - m_coverart->Reset(); - - QString coverDir = gContext->GetSetting("VideoArtworkDir"); - QString coverTitle = pginfo->title; - QString coverSeriesID = pginfo->seriesid; - QString coverFile = testImageFiles(coverDir, - coverSeriesID, - coverTitle, - "coverart", - artworkHost); - if (!coverFile.isEmpty()) + if (m_coverart) { - m_coverart->SetFilename(coverFile); - - m_coverartTimer->setSingleShot(true); - m_coverartTimer->start(500); +VERBOSE(VB_IMPORTANT, "have m_coverart"); + artworkDir = gContext->GetSetting("VideoArtworkDir"); + artworkFile = findArtworkFile(artworkDir, artworkSeriesID, + artworkTitle, "coverart", + artworkHost); +VERBOSE(VB_IMPORTANT, QString("have m_coverart, artworkfile: %1").arg(artworkFile)); + loadArtwork(artworkFile, m_coverart, m_coverartTimer, 50); } } @@ -2020,25 +1989,59 @@ void PlaybackBox::fanartLoad(void) { - m_fanart->SetVisible(true); m_fanart->Load(); } void PlaybackBox::bannerLoad(void) { - m_banner->SetVisible(true); m_banner->Load(); } void PlaybackBox::coverartLoad(void) { - m_coverart->SetVisible(true); m_coverart->Load(); } -QString PlaybackBox::testImageFiles(QString &testDirectory, QString &seriesID, - QString &titleIn, QString imagetype, QString host) +bool PlaybackBox::loadArtwork(QString artworkFile, MythUIImage *image, QTimer *timer, + int delay, bool resetImage) { +VERBOSE(VB_IMPORTANT, QString("PBB::loadArtwork('%1', '%2', timer, %3, %4)").arg(artworkFile).arg(image->GetFilename()).arg(delay).arg((int)resetImage)); + bool wasActive = timer->isActive(); + + if (artworkFile.isEmpty()) + { + if (wasActive) + timer->stop(); + + image->Reset(); + + return true; + } + else + { + if (artworkFile != image->GetFilename()) + { + if (wasActive) + timer->stop(); + + if (resetImage) + image->Reset(); + + image->SetFilename(artworkFile); + + timer->setSingleShot(true); + timer->start(delay); + + return wasActive; + } + } + + return false; +} + +QString PlaybackBox::findArtworkFile(QString &testDirectory, QString &seriesID, + QString &titleIn, QString imagetype, QString host) +{ static QHash imageFileCache; QString cacheKey = QString("%1:%2:%3").arg(imagetype).arg(seriesID) Index: programs/mythfrontend/playbackbox.h =================================================================== --- programs/mythfrontend/playbackbox.h (revision 22171) +++ programs/mythfrontend/playbackbox.h (working copy) @@ -309,9 +309,11 @@ QString getRecGroupPassword(const QString &recGroup); void fillRecGroupPasswordCache(void); - QString testImageFiles(QString &testDirectory, - QString &seriesID, QString &titleIn, - QString imagetype, QString host); + bool loadArtwork(QString artworkFile, MythUIImage *image, QTimer *timer, + int delay = 500, bool resetImage = false); + QString findArtworkFile(QString &testDirectory, + QString &seriesID, QString &titleIn, + QString imagetype, QString host); void updateGroupList(); void updateIcons(const ProgramInfo *pginfo = NULL);