File indexing completed on 2024-05-05 04:48:20

0001 /****************************************************************************************
0002  * Copyright (c) 2011 Ralf Engels <ralf-engels@gmx.de>                                  *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "covermanager/CoverCache.h"
0018 
0019 #include "core/meta/Meta.h"
0020 #include "core/support/Amarok.h"
0021 
0022 #include <QDir>
0023 #include <QImage>
0024 #include <QMutexLocker>
0025 #include <QPixmapCache>
0026 #include <QReadWriteLock>
0027 #include <QReadLocker>
0028 #include <QStandardPaths>
0029 #include <QWriteLocker>
0030 
0031 #include <KLocalizedString>
0032 
0033 #include <thread>
0034 
0035 CoverCache* CoverCache::s_instance = nullptr;
0036 
0037 CoverCache*
0038 CoverCache::instance()
0039 {
0040     return s_instance ? s_instance : (s_instance =  new CoverCache());
0041 }
0042 
0043 void CoverCache::destroy()
0044 {
0045     if( s_instance )
0046     {
0047         delete s_instance;
0048         s_instance = nullptr;
0049     }
0050 }
0051 
0052 CoverCache::CoverCache()
0053 { }
0054 
0055 CoverCache::~CoverCache()
0056 {
0057     m_lock.lockForWrite();
0058     m_lock.unlock();
0059 }
0060 
0061 void
0062 CoverCache::invalidateAlbum( const Meta::Album* album )
0063 {
0064     if( !s_instance )
0065         return;
0066 
0067     QWriteLocker locker( &s_instance->m_lock );
0068 
0069     if( !s_instance->m_keys.contains( album ) )
0070         return;
0071 
0072     CoverKeys allKeys = s_instance->m_keys.take( album );
0073     foreach( const QPixmapCache::Key &key, allKeys.values() )
0074     {
0075         QPixmapCache::remove( key );
0076     }
0077 }
0078 
0079 QPixmap
0080 CoverCache::getCover( const Meta::AlbumPtr &album, int size ) const
0081 {
0082     QPixmap pixmap;
0083 
0084     if( size > 1 ) // full size covers are not cached
0085     {
0086         QReadLocker locker( &m_lock );
0087         const CoverKeys &allKeys = m_keys.value( album.data() );
0088         if( !allKeys.isEmpty() )
0089         {
0090             QPixmapCache::Key key = allKeys.value( size );
0091             if( key != QPixmapCache::Key() && QPixmapCache::find( key, &pixmap ) )
0092                 return pixmap;
0093         }
0094     }
0095 
0096     QImage image = album->image( size );
0097 
0098     // -- get the null cover if someone really wants to have a pixmap
0099     // in this case the album hasCover should have already returned false
0100     if( image.isNull() )
0101     {
0102         const QDir &cacheCoverDir = QDir( Amarok::saveLocation( "albumcovers/cache/" ) );
0103         if( size <= 1 )
0104             size = 100;
0105         const QString &noCoverKey = QString::number( size ) + "@nocover.png";
0106 
0107         QPixmap pixmap;
0108         // look in the memory pixmap cache
0109         if( QPixmapCache::find( noCoverKey, &pixmap ) )
0110             return pixmap;
0111 
0112         if( cacheCoverDir.exists( noCoverKey ) )
0113         {
0114             pixmap.load( cacheCoverDir.filePath( noCoverKey ) );
0115         }
0116         else
0117         {
0118             const QPixmap orgPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/nocover.png" ) );
0119             pixmap = orgPixmap.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
0120             std::thread thread( QOverload<const QString&, const char*, int>::of( &QPixmap::save ), pixmap, cacheCoverDir.filePath( noCoverKey ), "PNG", -1 );
0121             thread.detach();
0122         }
0123         QPixmapCache::insert( noCoverKey, pixmap );
0124         return pixmap;
0125     }
0126 
0127     pixmap = QPixmap::fromImage( image );
0128 
0129     // -- add the cover to the cache if not full-scale or too big
0130     if( size > 1 && size < 1000 )
0131     {
0132         // sadly I can't relock for write
0133         QWriteLocker locker( &m_lock );
0134 
0135         QPixmapCache::Key key = QPixmapCache::insert( pixmap );
0136         m_keys[ album.data() ][ size ] = key;
0137     }
0138 
0139     return pixmap;
0140 }
0141 
0142 CoverCache* The::coverCache()
0143 {
0144     return CoverCache::instance();
0145 }
0146