Index: libs/libmythui/mythimage.cpp =================================================================== --- libs/libmythui/mythimage.cpp (revision 22250) +++ libs/libmythui/mythimage.cpp (working copy) @@ -56,6 +56,7 @@ void MythImage::UpRef(void) { + QMutexLocker locker(&m_RefCountLock); if (m_ui && m_cached && m_RefCount == 1) m_ui->ExcludeFromCacheSize(this); m_RefCount++; @@ -63,6 +64,7 @@ bool MythImage::DownRef(void) { + m_RefCountLock.lock(); m_RefCount--; if (m_ui && m_cached) { @@ -74,19 +76,23 @@ if (m_RefCount <= 0) { + m_RefCountLock.unlock(); delete this; return true; } + m_RefCountLock.unlock(); return false; } int MythImage::RefCount(void) { + QMutexLocker locker(&m_RefCountLock); return m_RefCount; } void MythImage::SetIsInCache(bool bCached) { + QMutexLocker locker(&m_RefCountLock); if (m_ui && m_RefCount == 1) { if (!m_cached && bCached) @@ -99,6 +105,7 @@ void MythImage::Assign(const QImage &img) { + QMutexLocker locker(&m_RefCountLock); if (m_ui && m_RefCount == 1 && m_cached) m_ui->ExcludeFromCacheSize(this); *(static_cast (this)) = img; Index: libs/libmythui/mythuiimage.cpp =================================================================== --- libs/libmythui/mythuiimage.cpp (revision 22249) +++ libs/libmythui/mythuiimage.cpp (working copy) @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,10 @@ const int kImageLoadEventType = 35112; +#define LOC QString("MythUIImage(%1): ").arg((long long)this) +#define LOC_ERR QString("MythUIImage(%1) ERROR: ").arg((long long)this) +#define LOC_WARN QString("MythUIImage(%1) WARNING: ").arg((long long)this) + /*! * \class ImageLoadThread */ @@ -80,7 +85,21 @@ }; ///////////////////////////////////////////////////////////////// +class MythUIImagePrivate +{ + public: + MythUIImagePrivate(MythUIImage *p) + : m_parent(p), m_UpdateLock(QReadWriteLock::Recursive) + { }; + ~MythUIImagePrivate() {}; + MythUIImage *m_parent; + + QReadWriteLock m_UpdateLock; +}; + +///////////////////////////////////////////////////////////////// + QHash MythUIImage::m_loadingImages; QMutex MythUIImage::m_loadingImagesLock; QWaitCondition MythUIImage::m_loadingImagesCond; @@ -98,6 +117,8 @@ m_imageLoadThread = NULL; + d = new MythUIImagePrivate(this); + Init(); } @@ -112,6 +133,8 @@ m_HighNum = 0; m_Delay = -1; + d = new MythUIImagePrivate(this); + Init(); } @@ -122,6 +145,8 @@ m_HighNum = 0; m_Delay = -1; + d = new MythUIImagePrivate(this); + Init(); } @@ -130,6 +155,9 @@ Clear(); if (m_maskImage) m_maskImage->DownRef(); + + if (d) + delete d; } /** @@ -151,11 +179,16 @@ */ void MythUIImage::Reset(void) { + d->m_UpdateLock.lockForWrite(); if (m_Filename != m_OrigFilename) { m_Filename = m_OrigFilename; + d->m_UpdateLock.unlock(); Load(); } + else + d->m_UpdateLock.unlock(); + MythUIType::Reset(); } @@ -197,6 +230,7 @@ */ void MythUIImage::SetFilename(const QString &filename) { + QWriteLocker updateLocker(&d->m_UpdateLock); m_Filename = filename; } @@ -207,6 +241,7 @@ void MythUIImage::SetFilepattern(const QString &filepattern, int low, int high) { + QWriteLocker updateLocker(&d->m_UpdateLock); m_Filename = filepattern; m_LowNum = low; m_HighNum = high; @@ -217,6 +252,7 @@ */ void MythUIImage::SetImageCount(int low, int high) { + QWriteLocker updateLocker(&d->m_UpdateLock); m_LowNum = low; m_HighNum = high; } @@ -226,6 +262,7 @@ */ void MythUIImage::SetDelay(int delayms) { + QWriteLocker updateLocker(&d->m_UpdateLock); m_Delay = delayms; m_LastDisplay = QTime::currentTime(); m_CurPos = 0; @@ -237,6 +274,7 @@ */ void MythUIImage::SetImage(MythImage *img) { + QWriteLocker updateLocker(&d->m_UpdateLock); if (!img) { Reset(); @@ -285,12 +323,16 @@ // HACK: This is just a workaround for the buttonlist inheritance problem // until the image cache is completed. + d->m_UpdateLock.lockForRead(); if (m_gradient) { + d->m_UpdateLock.unlock(); Load(); return; } + d->m_UpdateLock.unlock(); + QWriteLocker updateLocker(&d->m_UpdateLock); QSize aSize = m_Area.size(); QVector::iterator it; @@ -333,6 +375,7 @@ */ void MythUIImage::ForceSize(const QSize &size) { + QWriteLocker updateLocker(&d->m_UpdateLock); if (m_ForceSize == size) return; @@ -341,22 +384,10 @@ if (size.isEmpty()) return; - QSize aSize = m_Area.size(); - QMutexLocker locker(&m_ImagesLock); + SetSize(m_ForceSize); - QHash::iterator it; - for (it = m_Images.begin(); it != m_Images.end(); ++it) - { - MythImage *im = (*it); - - int w = (m_ForceSize.width() <= 0) ? im->width() : m_ForceSize.width(); - int h = (m_ForceSize.height() <= 0) ? im->height() : m_ForceSize.height(); - - im->Resize(QSize(w, h), m_preserveAspect); - aSize = aSize.expandedTo(im->size()); - } - - SetSize(m_ForceSize); + SetRedraw(); + return; } /** @@ -372,6 +403,7 @@ */ void MythUIImage::SetSize(const QSize &size) { + QWriteLocker updateLocker(&d->m_UpdateLock); MythUIType::SetSize(size); m_NeedLoad = true; } @@ -391,6 +423,7 @@ */ void MythUIImage::SetCropRect(const MythRect &rect) { + QWriteLocker updateLocker(&d->m_UpdateLock); m_cropRect = rect; SetRedraw(); } @@ -401,6 +434,7 @@ */ QString MythUIImage::GenImageLabel(const QString &filename, int w, int h) const { + QReadLocker updateLocker(&d->m_UpdateLock); QString imagelabel; QString s_Attrib; @@ -429,6 +463,7 @@ */ QString MythUIImage::GenImageLabel(int w, int h) const { + QReadLocker updateLocker(&d->m_UpdateLock); return GenImageLabel(m_Filename, w, h); } @@ -437,13 +472,17 @@ */ bool MythUIImage::Load(bool allowLoadInBackground) { + d->m_UpdateLock.lockForRead(); if (m_Filename.isEmpty() && (!m_gradient)) { + d->m_UpdateLock.unlock(); Clear(); + QWriteLocker updateLocker(&d->m_UpdateLock); SetRedraw(); return false; } + d->m_UpdateLock.unlock(); Clear(); @@ -454,7 +493,9 @@ int w = -1; int h = -1; + bool bForceResize = false; + d->m_UpdateLock.lockForWrite(); if (!m_ForceSize.isNull()) { if (m_ForceSize.width() != -1) @@ -462,11 +503,46 @@ if (m_ForceSize.height() != -1) h = m_ForceSize.height(); + + bForceResize = true; } + if (m_gradient) + { + int gradWidth = 10; + int gradHeight = 10; + + if (!m_Area.isEmpty()) + { + gradWidth = m_Area.size().width(); + gradHeight = m_Area.size().height(); + } + else if ((w > 0) && (h > 0)) + { + gradWidth = w; + gradHeight = h; + } + + int sr, sg, sb; + m_gradientStart.getRgb(&sr, &sg, &sb); + + int er, eg, eb; + m_gradientEnd.getRgb(&er, &eg, &eb); + + m_Filename = + QString("gradient-%1x%2-%3-%4-%5-%6-%7-%8-%9-%10") + .arg(gradWidth).arg(gradHeight) + .arg(sr).arg(sg).arg(sb).arg(er).arg(eg).arg(eb) + .arg(m_gradientAlpha).arg(m_gradientDirection); + VERBOSE(VB_FILE+VB_EXTRA, LOC + QString("Auto-generated gradient " + "filename of '%1'").arg(m_Filename)); + } + d->m_UpdateLock.unlock(); + QString imagelabel; QString filename = m_Filename; + d->m_UpdateLock.lockForRead(); for (int i = m_LowNum; i <= m_HighNum; i++) { if (m_HighNum >= 1) @@ -479,6 +555,8 @@ if ((allowLoadInBackground) && (!GetMythUI()->LoadCacheImage(filename, imagelabel, false))) { + VERBOSE(VB_FILE+VB_EXTRA, LOC + QString("Load(), spawning " + "thread to load '%1'").arg(filename)); m_imageLoadThread = new ImageLoadThread(this, m_Filename, filename, i); GetMythUI()->GetImageThreadPool()->start(m_imageLoadThread); @@ -486,9 +564,14 @@ else { // Perform a blocking load + VERBOSE(VB_FILE+VB_EXTRA, LOC + QString("Load(), loading " + "'%1' in foreground").arg(filename)); MythImage *image = LoadImage(filename, i); if (image) { + d->m_UpdateLock.unlock(); + d->m_UpdateLock.lockForWrite(); + if (m_ForceSize.isNull()) SetSize(image->size()); @@ -498,9 +581,12 @@ SetRedraw(); m_LastDisplay = QTime::currentTime(); + d->m_UpdateLock.unlock(); + d->m_UpdateLock.lockForRead(); } } } + d->m_UpdateLock.unlock(); return true; } @@ -510,6 +596,7 @@ */ MythImage *MythUIImage::LoadImage(const QString &imFile, int imageNumber) { + d->m_UpdateLock.lockForRead(); QString filename = imFile; m_loadingImagesLock.lock(); @@ -522,6 +609,7 @@ "file is already being loaded by this same MythUIImage in " "another thread.").arg(filename)); m_loadingImagesLock.unlock(); + d->m_UpdateLock.unlock(); return NULL; } @@ -607,6 +695,7 @@ m_loadingImagesCond.wakeAll(); m_loadingImagesLock.unlock(); + d->m_UpdateLock.unlock(); return NULL; } } @@ -649,6 +738,7 @@ if (bNeedLoad) GetMythUI()->CacheImage(imagelabel, image); } + d->m_UpdateLock.unlock(); if (image->isNull()) { @@ -680,6 +770,7 @@ */ void MythUIImage::Pulse(void) { + QWriteLocker updateLocker(&d->m_UpdateLock); if (m_Delay > 0 && abs(m_LastDisplay.msecsTo(QTime::currentTime())) > m_Delay) { @@ -709,6 +800,7 @@ m_ImagesLock.lock(); if (m_Images.size() > 0) { + d->m_UpdateLock.lockForWrite(); if (m_CurPos > (uint)m_Images.size()) m_CurPos = 0; if (!m_Images.contains(m_CurPos)) @@ -729,7 +821,19 @@ int alpha = CalcAlpha(alphaMod); MythImage *currentImage = m_Images[m_CurPos]; + currentImage->UpRef(); m_ImagesLock.unlock(); + d->m_UpdateLock.unlock(); + d->m_UpdateLock.lockForRead(); + + if (!m_ForceSize.isNull() && m_ForceSize != currentImage->size()) + { + int w = (m_ForceSize.width() <= 0) ? currentImage->width() : m_ForceSize.width(); + int h = (m_ForceSize.height() <= 0) ? currentImage->height() : m_ForceSize.height(); + + currentImage->Resize(QSize(w, h), m_preserveAspect); + } + QRect currentImageArea = currentImage->rect(); if (!m_ForceSize.isNull()) @@ -754,6 +858,8 @@ srcRect = currentImageArea; p->DrawImage(area, currentImage, srcRect, alpha); + currentImage->DownRef(); + d->m_UpdateLock.unlock(); } else m_ImagesLock.unlock(); @@ -764,6 +870,7 @@ */ bool MythUIImage::ParseElement(QDomElement &element) { + QWriteLocker updateLocker(&d->m_UpdateLock); if (element.tagName() == "filename") { m_OrigFilename = m_Filename = getFirstText(element); @@ -891,10 +998,12 @@ */ void MythUIImage::CopyFrom(MythUIType *base) { + d->m_UpdateLock.lockForWrite(); MythUIImage *im = dynamic_cast(base); if (!im) { VERBOSE(VB_IMPORTANT, "ERROR, bad parsing"); + d->m_UpdateLock.unlock(); return; } @@ -939,8 +1048,16 @@ m_NeedLoad = im->m_NeedLoad; + d->m_UpdateLock.unlock(); + + d->m_UpdateLock.lockForRead(); if (m_NeedLoad) + { + d->m_UpdateLock.unlock(); Load(); + } + else + d->m_UpdateLock.unlock(); } /** @@ -948,6 +1065,7 @@ */ void MythUIImage::CreateCopy(MythUIType *parent) { + QReadLocker updateLocker(&d->m_UpdateLock); MythUIImage *im = new MythUIImage(parent, objectName()); im->CopyFrom(this); } @@ -957,8 +1075,14 @@ */ void MythUIImage::Finalize(void) { + d->m_UpdateLock.lockForRead(); if (m_NeedLoad) + { + d->m_UpdateLock.unlock(); Load(); + } + else + d->m_UpdateLock.unlock(); MythUIType::Finalize(); } @@ -968,10 +1092,16 @@ */ void MythUIImage::LoadNow(void) { + d->m_UpdateLock.lockForWrite(); if (m_NeedLoad) + { + d->m_UpdateLock.unlock(); return; + } m_NeedLoad = true; + d->m_UpdateLock.unlock(); + Load(false); MythUIType::LoadNow(); @@ -994,17 +1124,25 @@ if (!image) return; + d->m_UpdateLock.lockForRead(); if (le->GetBasefile() != m_Filename) { + //VERBOSE(VB_FILE+VB_EXTRA,QString("MythUIImage::customEvent(%1): " + // "Expecting '%2', got '%3'").arg((long long)this) + // .arg(m_Filename).arg(le->GetBasefile())); + d->m_UpdateLock.unlock(); image->DownRef(); return; } + d->m_UpdateLock.unlock(); QString filename = le->GetFilename(); int number = le->GetNumber(); + d->m_UpdateLock.lockForWrite(); if (m_ForceSize.isNull()) SetSize(image->size()); + d->m_UpdateLock.unlock(); m_ImagesLock.lock(); if (m_Images.contains(number)) @@ -1017,7 +1155,9 @@ m_Images[number] = image; m_ImagesLock.unlock(); + d->m_UpdateLock.lockForWrite(); SetRedraw(); m_LastDisplay = QTime::currentTime(); + d->m_UpdateLock.unlock(); } } Index: libs/libmythui/mythuihelper.cpp =================================================================== --- libs/libmythui/mythuihelper.cpp (revision 22249) +++ libs/libmythui/mythuihelper.cpp (working copy) @@ -100,6 +100,7 @@ QMap CacheTrack; QMutex *m_cacheLock; size_t m_cacheSize; + QMutex *m_cacheSizeLock; uint maxImageCacheSize; @@ -144,6 +145,7 @@ 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_cacheSizeLock(new QMutex(QMutex::Recursive)), 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), @@ -343,10 +345,10 @@ d->Init(); d->callbacks = cbs; - d->m_cacheLock->lock(); + d->m_cacheSizeLock->lock(); d->maxImageCacheSize = GetMythDB()->GetNumSetting("UIImageCacheSize", 20) * 1024 * 1024; - d->m_cacheLock->unlock(); + d->m_cacheSizeLock->unlock(); VERBOSE(VB_GENERAL, QString("MythUI Image Cache size set to %1 bytes") .arg(d->maxImageCacheSize)); @@ -445,7 +447,10 @@ } d->CacheTrack.clear(); + + d->m_cacheSizeLock->lock(); d->m_cacheSize = 0; + d->m_cacheSizeLock->unlock(); ClearOldImageCache(); } @@ -473,9 +478,9 @@ if (!im) return; - d->m_cacheLock->lock(); + d->m_cacheSizeLock->lock(); d->m_cacheSize += im->numBytes(); - d->m_cacheLock->unlock(); + d->m_cacheSizeLock->unlock(); } void MythUIHelper::ExcludeFromCacheSize(MythImage *im) @@ -483,9 +488,9 @@ if (!im) return; - d->m_cacheLock->lock(); + d->m_cacheSizeLock->lock(); d->m_cacheSize -= im->numBytes(); - d->m_cacheLock->unlock(); + d->m_cacheSizeLock->unlock(); } MythImage *MythUIHelper::CacheImage(const QString &url, MythImage *im, @@ -511,9 +516,11 @@ // delete the oldest cached images until we fall below threshold. QMutexLocker locker(d->m_cacheLock); + d->m_cacheSizeLock->lock(); while (d->m_cacheSize + im->numBytes() >= d->maxImageCacheSize && d->imageCache.size()) { + d->m_cacheSizeLock->unlock(); QMap::iterator it = d->imageCache.begin(); uint oldestTime = QDateTime::currentDateTime().toTime_t(); QString oldestKey = it.key(); @@ -541,10 +548,16 @@ d->imageCache[oldestKey]->DownRef(); d->imageCache.remove(oldestKey); d->CacheTrack.remove(oldestKey); + + d->m_cacheSizeLock->lock(); } else + { + d->m_cacheSizeLock->lock(); break; + } } + d->m_cacheSizeLock->unlock(); QMap::iterator it = d->imageCache.find(url); if (it == d->imageCache.end()) @@ -1241,7 +1254,10 @@ MythImage *MythUIHelper::LoadCacheImage(QString srcfile, QString label, bool allowLoadFromDisk) { - //VERBOSE(VB_GENERAL, QString("LoadCacheImage %1:%2").arg(srcfile).arg(label)); + //VERBOSE(VB_FILE+VB_EXTRA, QString("LoadCacheImage %1:%2").arg(srcfile).arg(label)); + + if (srcfile.isEmpty() || label.isEmpty()) + return NULL; QString cachefilepath = GetThemeCacheDir() + '/' + label; QFileInfo fi(cachefilepath); Index: libs/libmythui/mythuiimage.h =================================================================== --- libs/libmythui/mythuiimage.h (revision 22249) +++ libs/libmythui/mythuiimage.h (working copy) @@ -9,6 +9,7 @@ #include "mythuitype.h" #include "mythimage.h" +class MythUIImagePrivate; class MythScreenType; class ImageLoadThread; @@ -125,6 +126,8 @@ ImageLoadThread *m_imageLoadThread; + MythUIImagePrivate *d; + friend class MythThemeBase; friend class MythUIButtonListItem; friend class MythUIProgressBar;