Warning, file /office/calligra/libs/flake/KoImageData.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org> 0003 * Copyright (C) 2007 Jan Hambrecht <jaham@gmx.net> 0004 * Copyright (C) 2008 C. Boemann <cbo@boemann.dk> 0005 * Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org> 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Library General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Library General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Library General Public License 0018 * along with this library; see the file COPYING.LIB. If not, write to 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "KoImageData.h" 0024 #include "KoImageData_p.h" 0025 0026 #include "KoImageCollection.h" 0027 0028 #include <KoUnit.h> 0029 #include <KoStore.h> 0030 #include <KoStoreDevice.h> 0031 0032 #include <FlakeDebug.h> 0033 0034 #include <QBuffer> 0035 #include <QCryptographicHash> 0036 #include <QTemporaryFile> 0037 #include <QPainter> 0038 0039 /// the maximum amount of bytes the image can be while we store it in memory instead of 0040 /// spooling it to disk in a temp-file. 0041 #define MAX_MEMORY_IMAGESIZE 90000 0042 0043 KoImageData::KoImageData() 0044 : d(0) 0045 { 0046 } 0047 0048 KoImageData::KoImageData(const KoImageData &imageData) 0049 : KoShapeUserData(), 0050 d(imageData.d) 0051 { 0052 if (d) 0053 d->refCount.ref(); 0054 } 0055 0056 KoImageData::KoImageData(KoImageDataPrivate *priv) 0057 : d(priv) 0058 { 0059 d->refCount.ref(); 0060 } 0061 0062 KoImageData::~KoImageData() 0063 { 0064 if (d && !d->refCount.deref()) 0065 delete d; 0066 } 0067 0068 QPixmap KoImageData::pixmap(const QSize &size) 0069 { 0070 if (!d) return QPixmap(); 0071 QSize wantedSize = size; 0072 if (! wantedSize.isValid()) { 0073 if (d->pixmap.isNull()) // we have a problem, Houston.. 0074 wantedSize = QSize(100, 100); 0075 else 0076 wantedSize = d->pixmap.size(); 0077 } 0078 if (d->pixmap.isNull() || d->pixmap.size() != wantedSize) { 0079 switch (d->dataStoreState) { 0080 case KoImageDataPrivate::StateEmpty: { 0081 #if 0 // this is not possible as it gets called during the paint method 0082 // and will crash. Therefore create a tmp pixmap and return it. 0083 d->pixmap = QPixmap(1, 1); 0084 QPainter p(&d->pixmap); 0085 p.setPen(QPen(Qt::gray, 0)); 0086 p.drawPoint(0, 0); 0087 p.end(); 0088 break; 0089 #endif 0090 QPixmap tmp(1, 1); 0091 tmp.fill(Qt::gray); 0092 return tmp; 0093 } 0094 case KoImageDataPrivate::StateNotLoaded: 0095 image(); // forces load 0096 // fall through 0097 case KoImageDataPrivate::StateImageLoaded: 0098 case KoImageDataPrivate::StateImageOnly: 0099 if (!d->image.isNull()) { 0100 // create pixmap from image. 0101 // this is the highest quality and lowest memory usage way of doing the conversion. 0102 d->pixmap = QPixmap::fromImage(d->image.scaled(wantedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 0103 } 0104 } 0105 0106 if (d->dataStoreState == KoImageDataPrivate::StateImageLoaded) { 0107 if (d->cleanCacheTimer.isActive()) 0108 d->cleanCacheTimer.stop(); 0109 // schedule an auto-unload of the big QImage in a second. 0110 d->cleanCacheTimer.start(); 0111 } 0112 } 0113 return d->pixmap; 0114 } 0115 0116 bool KoImageData::hasCachedPixmap() const 0117 { 0118 return d && !d->pixmap.isNull(); 0119 } 0120 0121 QSizeF KoImageData::imageSize() 0122 { 0123 if (!d->imageSize.isValid()) { 0124 // The imagesize have not yet been calculated 0125 if (image().isNull()) // auto loads the image 0126 return QSizeF(100, 100); 0127 0128 if (d->image.dotsPerMeterX()) 0129 d->imageSize.setWidth(DM_TO_POINT(d->image.width() / (qreal) d->image.dotsPerMeterX() * 10.0)); 0130 else 0131 d->imageSize.setWidth(d->image.width() / 72.0); 0132 0133 if (d->image.dotsPerMeterY()) 0134 d->imageSize.setHeight(DM_TO_POINT(d->image.height() / (qreal) d->image.dotsPerMeterY() * 10.0)); 0135 else 0136 d->imageSize.setHeight(d->image.height() / 72.0); 0137 } 0138 return d->imageSize; 0139 } 0140 0141 QImage KoImageData::image() const 0142 { 0143 if (d->dataStoreState == KoImageDataPrivate::StateNotLoaded) { 0144 // load image 0145 if (d->temporaryFile) { 0146 bool r = d->temporaryFile->open(); 0147 if (!r) { 0148 d->errorCode = OpenFailed; 0149 } 0150 else if (d->errorCode == Success && !d->image.load(d->temporaryFile->fileName(), d->suffix.toLatin1())) { 0151 qWarning() << "Failed to open image" << d->temporaryFile->fileName() << "with format" << d->suffix; 0152 d->errorCode = OpenFailed; 0153 } 0154 d->temporaryFile->close(); 0155 } else { 0156 if (d->errorCode == Success && !d->image.load(d->imageLocation.toLocalFile())) { 0157 d->errorCode = OpenFailed; 0158 } 0159 } 0160 if (d->errorCode == Success) { 0161 d->dataStoreState = KoImageDataPrivate::StateImageLoaded; 0162 } 0163 } 0164 return d->image; 0165 } 0166 0167 bool KoImageData::hasCachedImage() const 0168 { 0169 return d && !d->image.isNull(); 0170 } 0171 0172 void KoImageData::setImage(const QImage &image, KoImageCollection *collection) 0173 { 0174 qint64 oldKey = 0; 0175 if (d) { 0176 oldKey = d->key; 0177 } 0178 Q_ASSERT(!image.isNull()); 0179 if (collection) { 0180 // let the collection first check if it already has one. If it doesn't it'll call this method 0181 // again and well go to the other clause 0182 KoImageData *other = collection->createImageData(image); 0183 this->operator=(*other); 0184 delete other; 0185 } else { 0186 if (d == 0) { 0187 d = new KoImageDataPrivate(this); 0188 d->refCount.ref(); 0189 } 0190 delete d->temporaryFile; 0191 d->temporaryFile = 0; 0192 d->clear(); 0193 d->suffix = "png"; // good default for non-lossy storage. 0194 if (image.byteCount() > MAX_MEMORY_IMAGESIZE) { 0195 // store image 0196 QBuffer buffer; 0197 buffer.open(QIODevice::WriteOnly); 0198 if (!image.save(&buffer, d->suffix.toLatin1())) { 0199 warnFlake << "Write temporary file failed"; 0200 d->errorCode = StorageFailed; 0201 delete d->temporaryFile; 0202 d->temporaryFile = 0; 0203 return; 0204 } 0205 buffer.close(); 0206 buffer.open(QIODevice::ReadOnly); 0207 d->copyToTemporary(buffer); 0208 } else { 0209 d->image = image; 0210 d->dataStoreState = KoImageDataPrivate::StateImageOnly; 0211 0212 QByteArray ba; 0213 QBuffer buffer(&ba); 0214 buffer.open(QIODevice::WriteOnly); 0215 image.save(&buffer, "PNG"); // use .png for images we get as QImage 0216 QCryptographicHash md5(QCryptographicHash::Md5); 0217 md5.addData(ba); 0218 d->key = KoImageDataPrivate::generateKey(md5.result()); 0219 } 0220 if (oldKey != 0 && d->collection) { 0221 d->collection->update(oldKey, d->key); 0222 } 0223 0224 } 0225 0226 } 0227 0228 void KoImageData::setImage(const QString &url, KoStore *store, KoImageCollection *collection) 0229 { 0230 if (collection) { 0231 // Let the collection first check if it already has one. If it 0232 // doesn't it'll call this method again and we'll go to the 0233 // other clause. 0234 KoImageData *other = collection->createImageData(url, store); 0235 this->operator=(*other); 0236 delete other; 0237 } else { 0238 if (d == 0) { 0239 d = new KoImageDataPrivate(this); 0240 d->refCount.ref(); 0241 } else { 0242 d->clear(); 0243 } 0244 d->setSuffix(url); 0245 0246 if (store->open(url)) { 0247 struct Finalizer { 0248 ~Finalizer() { store->close(); } 0249 KoStore *store; 0250 }; 0251 Finalizer closer; 0252 closer.store = store; 0253 KoStoreDevice device(store); 0254 const bool lossy = url.endsWith(".jpg", Qt::CaseInsensitive) || url.endsWith(".gif", Qt::CaseInsensitive); 0255 if (!lossy && device.size() < MAX_MEMORY_IMAGESIZE) { 0256 QByteArray data = device.readAll(); 0257 if (d->image.loadFromData(data)) { 0258 QCryptographicHash md5(QCryptographicHash::Md5); 0259 md5.addData(data); 0260 qint64 oldKey = d->key; 0261 d->key = KoImageDataPrivate::generateKey(md5.result()); 0262 if (oldKey != 0 && d->collection) { 0263 d->collection->update(oldKey, d->key); 0264 } 0265 d->dataStoreState = KoImageDataPrivate::StateImageOnly; 0266 return; 0267 } 0268 } 0269 if (!device.open(QIODevice::ReadOnly)) { 0270 warnFlake << "open file from store " << url << "failed"; 0271 d->errorCode = OpenFailed; 0272 return; 0273 } 0274 d->copyToTemporary(device); 0275 } else { 0276 warnFlake << "Find file in store " << url << "failed"; 0277 d->errorCode = OpenFailed; 0278 return; 0279 } 0280 } 0281 } 0282 0283 void KoImageData::setImage(const QByteArray &imageData, KoImageCollection *collection) 0284 { 0285 if (collection) { 0286 // let the collection first check if it already has one. If it doesn't it'll call this method 0287 // again and we'll go to the other clause 0288 KoImageData *other = collection->createImageData(imageData); 0289 this->operator=(*other); 0290 delete other; 0291 } 0292 else { 0293 if (d == 0) { 0294 d = new KoImageDataPrivate(this); 0295 d->refCount.ref(); 0296 } 0297 0298 d->suffix = "png"; // good default for non-lossy storage. 0299 0300 if (imageData.size() <= MAX_MEMORY_IMAGESIZE) { 0301 QImage image; 0302 if (!image.loadFromData(imageData)) { 0303 // mark the image as invalid, but keep the data in memory 0304 // even if Calligra cannot handle the format, the data should 0305 // be retained 0306 d->errorCode = OpenFailed; 0307 } 0308 d->image = image; 0309 d->dataStoreState = KoImageDataPrivate::StateImageOnly; 0310 } 0311 0312 if (imageData.size() > MAX_MEMORY_IMAGESIZE 0313 || d->errorCode == OpenFailed) { 0314 d->image = QImage(); 0315 // store image data 0316 QBuffer buffer; 0317 buffer.setData(imageData); 0318 buffer.open(QIODevice::ReadOnly); 0319 d->copyToTemporary(buffer); 0320 d->suffix.clear(); // let QImage find out what the data contains 0321 } 0322 0323 QCryptographicHash md5(QCryptographicHash::Md5); 0324 md5.addData(imageData); 0325 qint64 oldKey = d->key; 0326 d->key = KoImageDataPrivate::generateKey(md5.result()); 0327 if (oldKey != 0 && d->collection) { 0328 d->collection->update(oldKey, d->key); 0329 } 0330 0331 } 0332 } 0333 0334 bool KoImageData::isValid() const 0335 { 0336 return d && d->dataStoreState != KoImageDataPrivate::StateEmpty 0337 && d->errorCode == Success; 0338 } 0339 0340 bool KoImageData::operator==(const KoImageData &other) const 0341 { 0342 return other.d == d; 0343 } 0344 0345 KoImageData &KoImageData::operator=(const KoImageData &other) 0346 { 0347 if (other.d) 0348 other.d->refCount.ref(); 0349 if (d && !d->refCount.deref()) 0350 delete d; 0351 d = other.d; 0352 return *this; 0353 } 0354 0355 qint64 KoImageData::key() const 0356 { 0357 return d->key; 0358 } 0359 0360 QString KoImageData::suffix() const 0361 { 0362 return d->suffix; 0363 } 0364 0365 KoImageData::ErrorCode KoImageData::errorCode() const 0366 { 0367 return d->errorCode; 0368 } 0369 0370 bool KoImageData::saveData(QIODevice &device) 0371 { 0372 return d->saveData(device); 0373 } 0374 0375 //have to include this because of Q_PRIVATE_SLOT 0376 #include "moc_KoImageData.cpp"