All pastes #1591668 Raw Edit

Mine

public text v1 · immutable
#1591668 ·published 2009-10-04 03:08 UTC
rendered paste body
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 <QApplication>
 #include <QPixmap>
 #include <QPainter>
+#include <QMutex>
 #include <QX11Info>
 
 // Mythdb headers
@@ -87,6 +88,9 @@
     int surfaceNum;
     bool initialized;
 
+    std::list<VdpBitmapSurface> 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 <QMap>
+#include <QMutex>
 
 #include <list>
 
@@ -60,6 +61,9 @@
 
     bool init_extensions;
     bool generate_mipmaps;
+
+    std::list<long long> 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 <cstdlib>
 
+// QT
 #include <QFile>
 #include <QDir>
 #include <QDomDocument>
 #include <QImageReader>
+#include <QRunnable>
+#include <QEvent>
+#include <QCoreApplication>
 
+// 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<int, MythImage*>::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<MythImage *>::iterator it;
+    QHash<int, MythImage *>::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<ImageLoadEvent*>(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 <QString>
 #include <QFont>
 #include <QMutex>
+#include <QThreadPool>
 
 #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 <QPainter>
 #include <QDesktopWidget>
 #include <QStyleFactory>
+#include <QThreadPool>
 
 #include "mythdirs.h"
 #include "mythverbose.h"
@@ -97,6 +98,8 @@
 
     QMap<QString, MythImage*> imageCache;
     QMap<QString, uint> 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<QString, MythImage*> 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<QString, MythImage*>::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<QString, MythImage*>::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 <QDateTime>
+#include <QHash>
+#include <QMutex>
 
 #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<MythImage *> m_Images;
+    QHash<int, MythImage *> 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 <QString, QString>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);