Ignore:
Timestamp:
Feb 11, 2010, 11:19:06 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/gui/image/qpixmapcache.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the QtGui module of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4141
    4242#include "qpixmapcache.h"
    43 #include "qcache.h"
    4443#include "qobject.h"
    4544#include "qdebug.h"
    46 
    47 #include "qpaintengine.h"
    48 #include <private/qimage_p.h>
    49 #include <private/qpixmap_raster_p.h>
     45#include "qpixmapcache_p.h"
    5046
    5147QT_BEGIN_NAMESPACE
     
    5652    \brief The QPixmapCache class provides an application-wide cache for pixmaps.
    5753
    58     \ingroup environment
    59     \ingroup multimedia
     54    \ingroup painting
    6055
    6156    This class is a tool for optimized drawing with QPixmap. You can
     
    6964    object for caching the pixmaps.
    7065
    71     The cache associates a pixmap with a string (key). If two pixmaps
    72     are inserted into the cache using equal keys, then the last pixmap
    73     will hide the first pixmap. The QHash and QCache classes do
    74     exactly the same.
     66    The cache associates a pixmap with a user-provided string as a key,
     67    or with a QPixmapCache::Key that the cache generates.
     68    Using QPixmapCache::Key for keys is faster than using strings. The string API is
     69    very convenient for complex keys but the QPixmapCache::Key API will be very
     70    efficient and convenient for a one-to-one object-to-pixmap mapping \mdash in
     71    this case, you can store the keys as members of an object.
     72
     73    If two pixmaps are inserted into the cache using equal keys then the
     74    last pixmap will replace the first pixmap in the cache. This follows the
     75    behavior of the QHash and QCache classes.
    7576
    7677    The cache becomes full when the total size of all pixmaps in the
    77     cache exceeds cacheLimit(). The initial cache limit is 1024 KB (1
    78     MB); it is changed with setCacheLimit(). A pixmap takes roughly
    79     (\e{width} * \e{height} * \e{depth})/8 bytes of memory.
     78    cache exceeds cacheLimit(). The initial cache limit is
     79    2048 KB (2 MB) on embedded platforms, 10240 KB (10 MB) on desktop
     80    platforms; you can change this by calling setCacheLimit() with the
     81    required value.
     82    A pixmap takes roughly (\e{width} * \e{height} * \e{depth})/8 bytes of
     83    memory.
    8084
    8185    The \e{Qt Quarterly} article
    82     \l{http://doc.trolltech.com/qq/qq12-qpixmapcache.html}{Optimizing
     86    \l{http://qt.nokia.com/doc/qq/qq12-qpixmapcache.html}{Optimizing
    8387    with QPixmapCache} explains how to use QPixmapCache to speed up
    8488    applications by caching the results of painting.
     
    8791*/
    8892
    89 #if defined(Q_WS_QWS) || defined(Q_OS_WINCE)
     93#if defined(Q_OS_SYMBIAN)
     94static int cache_limit = 1024; // 1048 KB cache limit for symbian
     95#elif defined(Q_WS_QWS) || defined(Q_WS_WINCE)
    9096static int cache_limit = 2048; // 2048 KB cache limit for embedded
    9197#else
     
    9399#endif
    94100
    95 // XXX: hw: is this a general concept we need to abstract?
    96 class QDetachedPixmap : public QPixmap
    97 {
    98 public:
    99     QDetachedPixmap(const QPixmap &pix) : QPixmap(pix)
    100     {
    101         if (data && data->classId() == QPixmapData::RasterClass) {
    102             QRasterPixmapData *d = static_cast<QRasterPixmapData*>(data);
    103             if (!d->image.isNull() && d->image.d->paintEngine
    104                 && !d->image.d->paintEngine->isActive())
    105             {
    106                 delete d->image.d->paintEngine;
    107                 d->image.d->paintEngine = 0;
    108             }
    109         }
    110     }
    111 };
    112 
    113 class QPMCache : public QObject, public QCache<qint64, QDetachedPixmap>
     101/*!
     102    \class QPixmapCache::Key
     103    \brief The QPixmapCache::Key class can be used for efficient access
     104    to the QPixmapCache.
     105    \since 4.6
     106
     107    Use QPixmapCache::insert() to receive an instance of Key generated
     108    by the pixmap cache. You can store the key in your own objects for
     109    a very efficient one-to-one object-to-pixmap mapping.
     110*/
     111
     112/*!
     113    Constructs an empty Key object.
     114*/
     115QPixmapCache::Key::Key() : d(0)
     116{
     117}
     118
     119/*!
     120   \internal
     121    Constructs a copy of \a other.
     122*/
     123QPixmapCache::Key::Key(const Key &other)
     124{
     125    if (other.d)
     126        ++(other.d->ref);
     127    d = other.d;
     128}
     129
     130/*!
     131    Destroys the key.
     132*/
     133QPixmapCache::Key::~Key()
     134{
     135    if (d && --(d->ref) == 0)
     136        delete d;
     137}
     138
     139/*!
     140    \internal
     141
     142    Returns true if this key is the same as the given \a key; otherwise returns
     143    false.
     144*/
     145bool QPixmapCache::Key::operator ==(const Key &key) const
     146{
     147    return (d == key.d);
     148}
     149
     150/*!
     151    \fn bool QPixmapCache::Key::operator !=(const Key &key) const
     152    \internal
     153*/
     154
     155/*!
     156    \internal
     157*/
     158QPixmapCache::Key &QPixmapCache::Key::operator =(const Key &other)
     159{
     160    if (d != other.d) {
     161        if (other.d)
     162            ++(other.d->ref);
     163        if (d && --(d->ref) == 0)
     164            delete d;
     165        d = other.d;
     166    }
     167    return *this;
     168}
     169
     170class QPMCache : public QObject, public QCache<QPixmapCache::Key, QPixmapCacheEntry>
    114171{
    115172    Q_OBJECT
    116173public:
    117     QPMCache()
    118         : QObject(0),
    119           QCache<qint64, QDetachedPixmap>(cache_limit * 1024),
    120           theid(0), ps(0), t(false) { }
    121     ~QPMCache() { }
     174    QPMCache();
     175    ~QPMCache();
    122176
    123177    void timerEvent(QTimerEvent *);
    124178    bool insert(const QString& key, const QPixmap &pixmap, int cost);
     179    QPixmapCache::Key insert(const QPixmap &pixmap, int cost);
     180    bool replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost);
    125181    bool remove(const QString &key);
     182    bool remove(const QPixmapCache::Key &key);
     183
     184    void resizeKeyArray(int size);
     185    QPixmapCache::Key createKey();
     186    void releaseKey(const QPixmapCache::Key &key);
     187    void clear();
    126188
    127189    QPixmap *object(const QString &key) const;
     190    QPixmap *object(const QPixmapCache::Key &key) const;
     191
     192    static inline QPixmapCache::KeyData *get(const QPixmapCache::Key &key)
     193    {return key.d;}
     194
     195    static QPixmapCache::KeyData* getKeyData(QPixmapCache::Key *key);
    128196
    129197private:
    130     QHash<QString, qint64> cacheKeys;
     198    int *keyArray;
    131199    int theid;
    132200    int ps;
     201    int keyArraySize;
     202    int freeKey;
     203    QHash<QString, QPixmapCache::Key> cacheKeys;
    133204    bool t;
    134205};
     206
    135207QT_BEGIN_INCLUDE_NAMESPACE
    136208#include "qpixmapcache.moc"
    137209QT_END_INCLUDE_NAMESPACE
    138210
     211uint qHash(const QPixmapCache::Key &k)
     212{
     213    return qHash(QPMCache::get(k)->key);
     214}
     215
     216QPMCache::QPMCache()
     217    : QObject(0),
     218      QCache<QPixmapCache::Key, QPixmapCacheEntry>(cache_limit * 1024),
     219      keyArray(0), theid(0), ps(0), keyArraySize(0), freeKey(0), t(false)
     220{
     221}
     222QPMCache::~QPMCache()
     223{
     224    clear();
     225    free(keyArray);
     226}
     227
    139228/*
    140   This is supposed to cut the cache size down by about 80-90% in a
     229  This is supposed to cut the cache size down by about 25% in a
    141230  minute once the application becomes idle, to let any inserted pixmap
    142231  remain in the cache for some time before it becomes a candidate for
     
    147236  timer so Qt won't keep the CPU from going into sleep mode.
    148237*/
    149 
    150238void QPMCache::timerEvent(QTimerEvent *)
    151239{
     
    156244    ps = totalCost();
    157245
    158     QHash<QString, qint64>::iterator it = cacheKeys.begin();
     246    QHash<QString, QPixmapCache::Key>::iterator it = cacheKeys.begin();
    159247    while (it != cacheKeys.end()) {
    160248        if (!contains(it.value())) {
     249            releaseKey(it.value());
    161250            it = cacheKeys.erase(it);
    162251        } else {
     
    177266QPixmap *QPMCache::object(const QString &key) const
    178267{
    179     return QCache<qint64, QDetachedPixmap>::object(cacheKeys.value(key, -1));
    180 }
    181 
     268    QPixmapCache::Key cacheKey = cacheKeys.value(key);
     269    if (!cacheKey.d || !cacheKey.d->isValid) {
     270        const_cast<QPMCache *>(this)->cacheKeys.remove(key);
     271        return 0;
     272    }
     273    QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(cacheKey);
     274     //We didn't find the pixmap in the cache, the key is not valid anymore
     275    if (!ptr) {
     276        const_cast<QPMCache *>(this)->cacheKeys.remove(key);
     277    }
     278    return ptr;
     279}
     280
     281QPixmap *QPMCache::object(const QPixmapCache::Key &key) const
     282{
     283    Q_ASSERT(key.d->isValid);
     284    QPixmap *ptr = QCache<QPixmapCache::Key, QPixmapCacheEntry>::object(key);
     285    //We didn't find the pixmap in the cache, the key is not valid anymore
     286    if (!ptr)
     287        const_cast<QPMCache *>(this)->releaseKey(key);
     288    return ptr;
     289}
    182290
    183291bool QPMCache::insert(const QString& key, const QPixmap &pixmap, int cost)
    184292{
    185     qint64 cacheKey = pixmap.cacheKey();
    186     if (QCache<qint64, QDetachedPixmap>::object(cacheKey)) {
    187         cacheKeys.insert(key, cacheKey);
    188         return true;
    189     }
    190     bool success = QCache<qint64, QDetachedPixmap>::insert(cacheKey, new QDetachedPixmap(pixmap), cost);
     293    QPixmapCache::Key cacheKey;
     294    QPixmapCache::Key oldCacheKey = cacheKeys.value(key);
     295    //If for the same key we add already a pixmap we should delete it
     296    if (oldCacheKey.d) {
     297        QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(oldCacheKey);
     298        cacheKeys.remove(key);
     299    }
     300
     301    //we create a new key the old one has been removed
     302    cacheKey = createKey();
     303
     304    bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
    191305    if (success) {
    192306        cacheKeys.insert(key, cacheKey);
     
    195309            t = false;
    196310        }
     311    } else {
     312        //Insertion failed we released the new allocated key
     313        releaseKey(cacheKey);
    197314    }
    198315    return success;
    199316}
    200317
     318QPixmapCache::Key QPMCache::insert(const QPixmap &pixmap, int cost)
     319{
     320    QPixmapCache::Key cacheKey = createKey();
     321    bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
     322    if (success) {
     323        if (!theid) {
     324            theid = startTimer(30000);
     325            t = false;
     326        }
     327    } else {
     328        //Insertion failed we released the key and return an invalid one
     329        releaseKey(cacheKey);
     330    }
     331    return cacheKey;
     332}
     333
     334bool QPMCache::replace(const QPixmapCache::Key &key, const QPixmap &pixmap, int cost)
     335{
     336    Q_ASSERT(key.d->isValid);
     337    //If for the same key we had already an entry so we should delete the pixmap and use the new one
     338    QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
     339
     340    QPixmapCache::Key cacheKey = createKey();
     341
     342    bool success = QCache<QPixmapCache::Key, QPixmapCacheEntry>::insert(cacheKey, new QPixmapCacheEntry(cacheKey, pixmap), cost);
     343    if (success) {
     344        if(!theid) {
     345            theid = startTimer(30000);
     346            t = false;
     347        }
     348        const_cast<QPixmapCache::Key&>(key) = cacheKey;
     349    } else {
     350        //Insertion failed we released the key
     351        releaseKey(cacheKey);
     352    }
     353    return success;
     354}
     355
    201356bool QPMCache::remove(const QString &key)
    202357{
    203     qint64 cacheKey = cacheKeys.value(key, -1);
     358    QPixmapCache::Key cacheKey = cacheKeys.value(key);
     359    //The key was not in the cache
     360    if (!cacheKey.d)
     361        return false;
    204362    cacheKeys.remove(key);
    205     return QCache<qint64, QDetachedPixmap>::remove(cacheKey);
     363    return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(cacheKey);
     364}
     365
     366bool QPMCache::remove(const QPixmapCache::Key &key)
     367{
     368    return QCache<QPixmapCache::Key, QPixmapCacheEntry>::remove(key);
     369}
     370
     371void QPMCache::resizeKeyArray(int size)
     372{
     373    if (size <= keyArraySize || size == 0)
     374        return;
     375    keyArray = q_check_ptr(reinterpret_cast<int *>(realloc(keyArray,
     376                    size * sizeof(int))));
     377    for (int i = keyArraySize; i != size; ++i)
     378        keyArray[i] = i + 1;
     379    keyArraySize = size;
     380}
     381
     382QPixmapCache::Key QPMCache::createKey()
     383{
     384    if (freeKey == keyArraySize)
     385        resizeKeyArray(keyArraySize ? keyArraySize << 1 : 2);
     386    int id = freeKey;
     387    freeKey = keyArray[id];
     388    QPixmapCache::Key key;
     389    QPixmapCache::KeyData *d = QPMCache::getKeyData(&key);
     390    d->key = ++id;
     391    return key;
     392}
     393
     394void QPMCache::releaseKey(const QPixmapCache::Key &key)
     395{
     396    if (key.d->key > keyArraySize || key.d->key <= 0)
     397        return;
     398    key.d->key--;
     399    keyArray[key.d->key] = freeKey;
     400    freeKey = key.d->key;
     401    key.d->isValid = false;
     402    key.d->key = 0;
     403}
     404
     405void QPMCache::clear()
     406{
     407    free(keyArray);
     408    keyArray = 0;
     409    freeKey = 0;
     410    keyArraySize = 0;
     411    //Mark all keys as invalid
     412    QList<QPixmapCache::Key> keys = QCache<QPixmapCache::Key, QPixmapCacheEntry>::keys();
     413    for (int i = 0; i < keys.size(); ++i)
     414        keys.at(i).d->isValid = false;
     415    QCache<QPixmapCache::Key, QPixmapCacheEntry>::clear();
     416}
     417
     418QPixmapCache::KeyData* QPMCache::getKeyData(QPixmapCache::Key *key)
     419{
     420    if (!key->d)
     421        key->d = new QPixmapCache::KeyData;
     422    return key->d;
    206423}
    207424
    208425Q_GLOBAL_STATIC(QPMCache, pm_cache)
    209426
    210 /*!
    211   \obsolete
     427int Q_AUTOTEST_EXPORT q_QPixmapCache_keyHashSize()
     428{
     429    return pm_cache()->size();
     430}
     431
     432QPixmapCacheEntry::~QPixmapCacheEntry()
     433{
     434    pm_cache()->releaseKey(key);
     435}
     436
     437/*!
     438    \obsolete
    212439    \overload
    213440
     
    218445    fast). Subsequent insertions into the cache could cause the
    219446    pointer to become invalid. For this reason, we recommend you use
    220     find(const QString&, QPixmap&) instead.
     447    bool find(const QString&, QPixmap*) instead.
    221448
    222449    Example:
     
    231458
    232459/*!
    233     Looks for a cached pixmap associated with the \a key in the cache.
    234     If the pixmap is found, the function sets \a pm to that pixmap and
    235     returns true; otherwise it leaves \a pm alone and returns false.
     460    \obsolete
     461
     462    Use bool find(const QString&, QPixmap*) instead.
     463*/
     464
     465bool QPixmapCache::find(const QString &key, QPixmap& pixmap)
     466{
     467    return find(key, &pixmap);
     468}
     469
     470/*!
     471    Looks for a cached pixmap associated with the given \a key in the cache.
     472    If the pixmap is found, the function sets \a pixmap to that pixmap and
     473    returns true; otherwise it leaves \a pixmap alone and returns false.
     474
     475    \since 4.6
    236476
    237477    Example:
     
    239479*/
    240480
    241 bool QPixmapCache::find(const QString &key, QPixmap& pm)
     481bool QPixmapCache::find(const QString &key, QPixmap* pixmap)
    242482{
    243483    QPixmap *ptr = pm_cache()->object(key);
    244     if (ptr)
    245         pm = *ptr;
     484    if (ptr && pixmap)
     485        *pixmap = *ptr;
    246486    return ptr != 0;
    247487}
    248488
    249 
    250 /*!
    251     Inserts a copy of the pixmap \a pm associated with the \a key into
     489/*!
     490    Looks for a cached pixmap associated with the given \a key in the cache.
     491    If the pixmap is found, the function sets \a pixmap to that pixmap and
     492    returns true; otherwise it leaves \a pixmap alone and returns false. If
     493    the pixmap is not found, it means that the \a key is no longer valid,
     494    so it will be released for the next insertion.
     495
     496    \since 4.6
     497*/
     498bool QPixmapCache::find(const Key &key, QPixmap* pixmap)
     499{
     500    //The key is not valid anymore, a flush happened before probably
     501    if (!key.d || !key.d->isValid)
     502        return false;
     503    QPixmap *ptr = pm_cache()->object(key);
     504    if (ptr && pixmap)
     505        *pixmap = *ptr;
     506    return ptr != 0;
     507}
     508
     509/*!
     510    Inserts a copy of the pixmap \a pixmap associated with the \a key into
    252511    the cache.
    253512
     
    268527*/
    269528
    270 bool QPixmapCache::insert(const QString &key, const QPixmap &pm)
    271 {
    272     return pm_cache()->insert(key, pm, pm.width() * pm.height() * pm.depth() / 8);
     529bool QPixmapCache::insert(const QString &key, const QPixmap &pixmap)
     530{
     531    return pm_cache()->insert(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
     532}
     533
     534/*!
     535    Inserts a copy of the given \a pixmap into the cache and returns a key
     536    that can be used to retrieve it.
     537
     538    When a pixmap is inserted and the cache is about to exceed its
     539    limit, it removes pixmaps until there is enough room for the
     540    pixmap to be inserted.
     541
     542    The oldest pixmaps (least recently accessed in the cache) are
     543    deleted when more space is needed.
     544
     545    \sa setCacheLimit(), replace()
     546
     547    \since 4.6
     548*/
     549QPixmapCache::Key QPixmapCache::insert(const QPixmap &pixmap)
     550{
     551    return pm_cache()->insert(pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
     552}
     553
     554/*!
     555    Replaces the pixmap associated with the given \a key with the \a pixmap
     556    specified. Returns true if the \a pixmap has been correctly inserted into
     557    the cache; otherwise returns false.
     558
     559    \sa setCacheLimit(), insert()
     560
     561    \since 4.6
     562*/
     563bool QPixmapCache::replace(const Key &key, const QPixmap &pixmap)
     564{
     565    //The key is not valid anymore, a flush happened before probably
     566    if (!key.d || !key.d->isValid)
     567        return false;
     568    return pm_cache()->replace(key, pixmap, pixmap.width() * pixmap.height() * pixmap.depth() / 8);
    273569}
    274570
     
    276572    Returns the cache limit (in kilobytes).
    277573
    278     The default cache limit is 2048 KB for Embedded, 10240 KB for Desktops.
     574    The default cache limit is 2048 KB on embedded platforms, 10240 KB on
     575    desktop platforms.
    279576
    280577    \sa setCacheLimit()
     
    289586    Sets the cache limit to \a n kilobytes.
    290587
    291     The default setting is 1024 kilobytes.
     588    The default setting is 2048 KB on embedded platforms, 10240 KB on
     589    desktop platforms.
    292590
    293591    \sa cacheLimit()
     
    308606}
    309607
     608/*!
     609  Removes the pixmap associated with \a key from the cache and releases
     610  the key for a future insertion.
     611
     612  \since 4.6
     613*/
     614void QPixmapCache::remove(const Key &key)
     615{
     616    //The key is not valid anymore, a flush happened before probably
     617    if (!key.d || !key.d->isValid)
     618        return;
     619    pm_cache()->remove(key);
     620}
    310621
    311622/*!
     
    315626void QPixmapCache::clear()
    316627{
    317     pm_cache()->clear();
     628    QT_TRY {
     629        pm_cache()->clear();
     630    } QT_CATCH(const std::bad_alloc &) {
     631        // if we ran out of memory during pm_cache(), it's no leak,
     632        // so just ignore it.
     633    }
    318634}
    319635
Note: See TracChangeset for help on using the changeset viewer.