util/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the plugins of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    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.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include "qsvgiconengine.h"
       
    42 
       
    43 #ifndef QT_NO_SVGRENDERER
       
    44 
       
    45 #include "qpainter.h"
       
    46 #include "qpixmap.h"
       
    47 #include "qsvgrenderer.h"
       
    48 #include "qpixmapcache.h"
       
    49 #include "qstyle.h"
       
    50 #include "qapplication.h"
       
    51 #include "qstyleoption.h"
       
    52 #include "qfileinfo.h"
       
    53 #include <QAtomicInt>
       
    54 #include "qdebug.h"
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 class QSvgIconEnginePrivate : public QSharedData
       
    59 {
       
    60 public:
       
    61     QSvgIconEnginePrivate()
       
    62         : svgBuffers(0), addedPixmaps(0)
       
    63         { stepSerialNum(); }
       
    64 
       
    65     ~QSvgIconEnginePrivate()
       
    66         { delete addedPixmaps; delete svgBuffers; }
       
    67 
       
    68     static int hashKey(QIcon::Mode mode, QIcon::State state)
       
    69         { return (((mode)<<4)|state); }
       
    70 
       
    71     QString pmcKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
       
    72         { return QLatin1String("$qt_svgicon_")
       
    73                  + QString::number(serialNum, 16).append(QLatin1Char('_'))
       
    74                  + QString::number((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state, 16); }
       
    75 
       
    76     void stepSerialNum()
       
    77         { serialNum = lastSerialNum.fetchAndAddRelaxed(1); }
       
    78 
       
    79     void loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state);
       
    80 
       
    81     QHash<int, QString> svgFiles;
       
    82     QHash<int, QByteArray> *svgBuffers;
       
    83     QHash<int, QPixmap> *addedPixmaps;
       
    84     int serialNum;
       
    85     static QAtomicInt lastSerialNum;
       
    86 };
       
    87 
       
    88 QAtomicInt QSvgIconEnginePrivate::lastSerialNum;
       
    89 
       
    90 static inline int pmKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
       
    91 {
       
    92     return ((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state);
       
    93 }
       
    94 
       
    95 QSvgIconEngine::QSvgIconEngine()
       
    96     : d(new QSvgIconEnginePrivate)
       
    97 {
       
    98 }
       
    99 
       
   100 QSvgIconEngine::QSvgIconEngine(const QSvgIconEngine &other)
       
   101     : QIconEngineV2(other), d(new QSvgIconEnginePrivate)
       
   102 {
       
   103     d->svgFiles = other.d->svgFiles;
       
   104     if (other.d->svgBuffers)
       
   105         d->svgBuffers = new QHash<int, QByteArray>(*other.d->svgBuffers);
       
   106     if (other.d->addedPixmaps)
       
   107         d->addedPixmaps = new QHash<int, QPixmap>(*other.d->addedPixmaps);
       
   108 }
       
   109 
       
   110 
       
   111 QSvgIconEngine::~QSvgIconEngine()
       
   112 {
       
   113 }
       
   114 
       
   115 
       
   116 QSize QSvgIconEngine::actualSize(const QSize &size, QIcon::Mode mode,
       
   117                                  QIcon::State state)
       
   118 {
       
   119     if (d->addedPixmaps) {
       
   120         QPixmap pm = d->addedPixmaps->value(d->hashKey(mode, state));
       
   121         if (!pm.isNull() && pm.size() == size)
       
   122             return size;
       
   123     }
       
   124 
       
   125     QPixmap pm = pixmap(size, mode, state);
       
   126     if (pm.isNull())
       
   127         return QSize();
       
   128     return pm.size();
       
   129 }
       
   130 
       
   131 void QSvgIconEnginePrivate::loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state)
       
   132 {
       
   133     QByteArray buf;
       
   134     if (svgBuffers) {
       
   135         buf = svgBuffers->value(hashKey(mode, state));
       
   136         if (buf.isEmpty())
       
   137             buf = svgBuffers->value(hashKey(QIcon::Normal, QIcon::Off));
       
   138     }
       
   139     if (!buf.isEmpty()) {
       
   140 #ifndef QT_NO_COMPRESS
       
   141         buf = qUncompress(buf);
       
   142 #endif
       
   143         renderer->load(buf);
       
   144     } else {
       
   145         QString svgFile = svgFiles.value(hashKey(mode, state));
       
   146         if (svgFile.isEmpty())
       
   147             svgFile = svgFiles.value(hashKey(QIcon::Normal, QIcon::Off));
       
   148         if (!svgFile.isEmpty())
       
   149             renderer->load(svgFile);
       
   150     }
       
   151 }
       
   152 
       
   153 QPixmap QSvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode,
       
   154                                QIcon::State state)
       
   155 {
       
   156     QPixmap pm;
       
   157 
       
   158     QString pmckey(d->pmcKey(size, mode, state));
       
   159     if (QPixmapCache::find(pmckey, pm))
       
   160         return pm;
       
   161 
       
   162     if (d->addedPixmaps) {
       
   163         pm = d->addedPixmaps->value(d->hashKey(mode, state));
       
   164         if (!pm.isNull() && pm.size() == size)
       
   165             return pm;
       
   166     }
       
   167 
       
   168     QSvgRenderer renderer;
       
   169     d->loadDataForModeAndState(&renderer, mode, state);
       
   170     if (!renderer.isValid())
       
   171         return pm;
       
   172 
       
   173     QSize actualSize = renderer.defaultSize();
       
   174     if (!actualSize.isNull())
       
   175         actualSize.scale(size, Qt::KeepAspectRatio);
       
   176 
       
   177     QImage img(actualSize, QImage::Format_ARGB32_Premultiplied);
       
   178     img.fill(0x00000000);
       
   179     QPainter p(&img);
       
   180     renderer.render(&p);
       
   181     p.end();
       
   182     pm = QPixmap::fromImage(img);
       
   183     QStyleOption opt(0);
       
   184     opt.palette = QApplication::palette();
       
   185     QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
       
   186     if (!generated.isNull())
       
   187         pm = generated;
       
   188 
       
   189     if (!pm.isNull())
       
   190         QPixmapCache::insert(pmckey, pm);
       
   191 
       
   192     return pm;
       
   193 }
       
   194 
       
   195 
       
   196 void QSvgIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode,
       
   197                                QIcon::State state)
       
   198 {
       
   199     if (!d->addedPixmaps)
       
   200         d->addedPixmaps = new QHash<int, QPixmap>;
       
   201     d->stepSerialNum();
       
   202     d->addedPixmaps->insert(d->hashKey(mode, state), pixmap);
       
   203 }
       
   204 
       
   205 
       
   206 void QSvgIconEngine::addFile(const QString &fileName, const QSize &,
       
   207                              QIcon::Mode mode, QIcon::State state)
       
   208 {
       
   209     if (!fileName.isEmpty()) {
       
   210         QString abs = fileName;
       
   211         if (fileName.at(0) != QLatin1Char(':'))
       
   212             abs = QFileInfo(fileName).absoluteFilePath();
       
   213         if (abs.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)
       
   214 #ifndef QT_NO_COMPRESS
       
   215                 || abs.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
       
   216                 || abs.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive))
       
   217 #endif
       
   218         {
       
   219             QSvgRenderer renderer(abs);
       
   220             if (renderer.isValid()) {
       
   221                 d->stepSerialNum();
       
   222                 d->svgFiles.insert(d->hashKey(mode, state), abs);
       
   223             }
       
   224         } else {
       
   225             QPixmap pm(abs);
       
   226             if (!pm.isNull())
       
   227                 addPixmap(pm, mode, state);
       
   228         }
       
   229     }
       
   230 }
       
   231 
       
   232 void QSvgIconEngine::paint(QPainter *painter, const QRect &rect,
       
   233                            QIcon::Mode mode, QIcon::State state)
       
   234 {
       
   235     painter->drawPixmap(rect, pixmap(rect.size(), mode, state));
       
   236 }
       
   237 
       
   238 QString QSvgIconEngine::key() const
       
   239 {
       
   240     return QLatin1String("svg");
       
   241 }
       
   242 
       
   243 QIconEngineV2 *QSvgIconEngine::clone() const
       
   244 {
       
   245     return new QSvgIconEngine(*this);
       
   246 }
       
   247 
       
   248 
       
   249 bool QSvgIconEngine::read(QDataStream &in)
       
   250 {
       
   251     d = new QSvgIconEnginePrivate;
       
   252     d->svgBuffers = new QHash<int, QByteArray>;
       
   253 
       
   254     if (in.version() >= QDataStream::Qt_4_4) {
       
   255         int isCompressed;
       
   256         QHash<int, QString> fileNames;  // For memoryoptimization later
       
   257         in >> fileNames >> isCompressed >> *d->svgBuffers;
       
   258 #ifndef QT_NO_COMPRESS
       
   259         if (!isCompressed) {
       
   260             foreach(int key, d->svgBuffers->keys())
       
   261                 d->svgBuffers->insert(key, qCompress(d->svgBuffers->value(key)));
       
   262         }
       
   263 #else
       
   264         if (isCompressed) {
       
   265             qWarning("QSvgIconEngine: Can not decompress SVG data");
       
   266             d->svgBuffers->clear();
       
   267         }
       
   268 #endif
       
   269         int hasAddedPixmaps;
       
   270         in >> hasAddedPixmaps;
       
   271         if (hasAddedPixmaps) {
       
   272             d->addedPixmaps = new QHash<int, QPixmap>;
       
   273             in >> *d->addedPixmaps;
       
   274         }
       
   275     }
       
   276     else {
       
   277         QPixmap pixmap;
       
   278         QByteArray data;
       
   279         uint mode;
       
   280         uint state;
       
   281         int num_entries;
       
   282 
       
   283         in >> data;
       
   284         if (!data.isEmpty()) {
       
   285 #ifndef QT_NO_COMPRESS
       
   286             data = qUncompress(data);
       
   287 #endif
       
   288             if (!data.isEmpty())
       
   289                 d->svgBuffers->insert(d->hashKey(QIcon::Normal, QIcon::Off), data);
       
   290         }
       
   291         in >> num_entries;
       
   292         for (int i=0; i<num_entries; ++i) {
       
   293             if (in.atEnd())
       
   294                 return false;
       
   295             in >> pixmap;
       
   296             in >> mode;
       
   297             in >> state;
       
   298             // The pm list written by 4.3 is buggy and/or useless, so ignore.
       
   299             //addPixmap(pixmap, QIcon::Mode(mode), QIcon::State(state));
       
   300         }
       
   301     }
       
   302 
       
   303     return true;
       
   304 }
       
   305 
       
   306 
       
   307 bool QSvgIconEngine::write(QDataStream &out) const
       
   308 {
       
   309     if (out.version() >= QDataStream::Qt_4_4) {
       
   310         int isCompressed = 0;
       
   311 #ifndef QT_NO_COMPRESS
       
   312         isCompressed = 1;
       
   313 #endif
       
   314         QHash<int, QByteArray> svgBuffers;
       
   315         if (d->svgBuffers)
       
   316             svgBuffers = *d->svgBuffers;
       
   317         foreach(int key, d->svgFiles.keys()) {
       
   318             QByteArray buf;
       
   319             QFile f(d->svgFiles.value(key));
       
   320             if (f.open(QIODevice::ReadOnly))
       
   321                 buf = f.readAll();
       
   322 #ifndef QT_NO_COMPRESS
       
   323             buf = qCompress(buf);
       
   324 #endif
       
   325             svgBuffers.insert(key, buf);
       
   326         }
       
   327         out << d->svgFiles << isCompressed << svgBuffers;
       
   328         if (d->addedPixmaps)
       
   329             out << (int)1 << *d->addedPixmaps;
       
   330         else
       
   331             out << (int)0;
       
   332     }
       
   333     else {
       
   334         QByteArray buf;
       
   335         if (d->svgBuffers)
       
   336             buf = d->svgBuffers->value(d->hashKey(QIcon::Normal, QIcon::Off));
       
   337         if (buf.isEmpty()) {
       
   338             QString svgFile = d->svgFiles.value(d->hashKey(QIcon::Normal, QIcon::Off));
       
   339             if (!svgFile.isEmpty()) {
       
   340                 QFile f(svgFile);
       
   341                 if (f.open(QIODevice::ReadOnly))
       
   342                     buf = f.readAll();
       
   343             }
       
   344         }
       
   345 #ifndef QT_NO_COMPRESS
       
   346         buf = qCompress(buf);
       
   347 #endif
       
   348         out << buf;
       
   349         // 4.3 has buggy handling of added pixmaps, so don't write any
       
   350         out << (int)0;
       
   351     }
       
   352     return true;
       
   353 }
       
   354 
       
   355 QT_END_NAMESPACE
       
   356 
       
   357 #endif // QT_NO_SVGRENDERER