Changeset View
Changeset View
Standalone View
Standalone View
lib/jpegcontent.cpp
Show First 20 Lines • Show All 107 Lines • ▼ Show 20 Line(s) | |||||
108 | //--------------------- | 108 | //--------------------- | ||
109 | struct JpegContent::Private | 109 | struct JpegContent::Private | ||
110 | { | 110 | { | ||
111 | // JpegContent usually stores the image pixels as compressed JPEG data in | 111 | // JpegContent usually stores the image pixels as compressed JPEG data in | ||
112 | // mRawData. However if the image is set with setImage() because the user | 112 | // mRawData. However if the image is set with setImage() because the user | ||
113 | // performed a lossy image manipulation, mRawData is cleared and the image | 113 | // performed a lossy image manipulation, mRawData is cleared and the image | ||
114 | // pixels are kept in mImage until updateRawDataFromImage() is called. | 114 | // pixels are kept in mImage until updateRawDataFromImage() is called. | ||
115 | QImage mImage; | 115 | QImage mImage; | ||
116 | | ||||
117 | // Store the input file, keep it open readOnly. This allows the file to be memory mapped | ||||
118 | // (i.e. mRawData may point to mFile.map()) rather than completely read on load. Postpone | ||||
119 | // QFile::readAll() as long as possible (currently in save()). | ||||
120 | QFile mFile; | ||||
116 | QByteArray mRawData; | 121 | QByteArray mRawData; | ||
122 | | ||||
117 | QSize mSize; | 123 | QSize mSize; | ||
118 | QString mComment; | 124 | QString mComment; | ||
119 | bool mPendingTransformation; | 125 | bool mPendingTransformation; | ||
120 | QTransform mTransformMatrix; | 126 | QTransform mTransformMatrix; | ||
121 | Exiv2::ExifData mExifData; | 127 | Exiv2::ExifData mExifData; | ||
122 | QString mErrorString; | 128 | QString mErrorString; | ||
123 | 129 | | |||
124 | Private() | 130 | Private() | ||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Line(s) | |||||
200 | 206 | | |||
201 | JpegContent::~JpegContent() | 207 | JpegContent::~JpegContent() | ||
202 | { | 208 | { | ||
203 | delete d; | 209 | delete d; | ||
204 | } | 210 | } | ||
205 | 211 | | |||
206 | bool JpegContent::load(const QString& path) | 212 | bool JpegContent::load(const QString& path) | ||
207 | { | 213 | { | ||
208 | QFile file(path); | 214 | if(d->mFile.isOpen()) | ||
209 | if (!file.open(QIODevice::ReadOnly)) { | 215 | { | ||
216 | d->mFile.unmap(reinterpret_cast<unsigned char*>(d->mRawData.data())); | ||||
217 | d->mFile.close(); | ||||
218 | d->mRawData.clear(); | ||||
219 | } | ||||
220 | | ||||
221 | d->mFile.setFileName(path); | ||||
222 | if (!d->mFile.open(QIODevice::ReadOnly)) { | ||||
210 | qCritical() << "Could not open '" << path << "' for reading\n"; | 223 | qCritical() << "Could not open '" << path << "' for reading\n"; | ||
211 | return false; | 224 | return false; | ||
212 | } | 225 | } | ||
213 | return loadFromData(file.readAll()); | 226 | | ||
227 | QByteArray rawData; | ||||
228 | uchar* mappedFile = d->mFile.map(0, d->mFile.size(), QFileDevice::MapPrivateOption); | ||||
229 | if(mappedFile == nullptr) { | ||||
230 | // process' mapping limit exceeded, file is sealed or filesystem doesn't support it, etc. | ||||
231 | qDebug() << "Could not mmap '" << path << "', falling back to QFile::readAll()\n"; | ||||
232 | | ||||
233 | rawData = d->mFile.readAll(); | ||||
234 | // all read in, no need to keep it open | ||||
235 | d->mFile.close(); | ||||
236 | } | ||||
237 | else { | ||||
238 | rawData = QByteArray::fromRawData(reinterpret_cast<char*>(mappedFile), d->mFile.size()); | ||||
239 | } | ||||
240 | | ||||
241 | return loadFromData(rawData); | ||||
214 | } | 242 | } | ||
215 | 243 | | |||
216 | bool JpegContent::loadFromData(const QByteArray& data) | 244 | bool JpegContent::loadFromData(const QByteArray& data) | ||
217 | { | 245 | { | ||
218 | std::unique_ptr<Exiv2::Image> image; | 246 | std::unique_ptr<Exiv2::Image> image; | ||
219 | Exiv2ImageLoader loader; | 247 | Exiv2ImageLoader loader; | ||
220 | if (!loader.load(data)) { | 248 | if (!loader.load(data)) { | ||
221 | qCritical() << "Could not load image with Exiv2, reported error:" << loader.errorMessage(); | 249 | qCritical() << "Could not load image with Exiv2, reported error:" << loader.errorMessage(); | ||
▲ Show 20 Lines • Show All 348 Lines • ▼ Show 20 Line(s) | 596 | #if (EXIV2_TEST_VERSION(0,17,91)) | |||
570 | thumb.setJpegThumbnail((unsigned char*)array.data(), array.size()); | 598 | thumb.setJpegThumbnail((unsigned char*)array.data(), array.size()); | ||
571 | #else | 599 | #else | ||
572 | d->mExifData.setJpegThumbnail((unsigned char*)array.data(), array.size()); | 600 | d->mExifData.setJpegThumbnail((unsigned char*)array.data(), array.size()); | ||
573 | #endif | 601 | #endif | ||
574 | } | 602 | } | ||
575 | 603 | | |||
576 | bool JpegContent::save(const QString& path) | 604 | bool JpegContent::save(const QString& path) | ||
577 | { | 605 | { | ||
606 | // we need to take ownership of the input file's data | ||||
607 | // if the input file is still open, data is still only mem-mapped | ||||
608 | if(d->mFile.isOpen()) | ||||
609 | { | ||||
610 | // backup the mmap() pointer | ||||
611 | auto* mappedFile = reinterpret_cast<unsigned char*>(d->mRawData.data()); | ||||
612 | // read the file to memory | ||||
613 | d->mRawData = d->mFile.readAll(); | ||||
614 | d->mFile.unmap(mappedFile); | ||||
615 | d->mFile.close(); | ||||
616 | } | ||||
617 | | ||||
578 | QFile file(path); | 618 | QFile file(path); | ||
579 | if (!file.open(QIODevice::WriteOnly)) { | 619 | if (!file.open(QIODevice::WriteOnly)) { | ||
580 | d->mErrorString = i18nc("@info", "Could not open file for writing."); | 620 | d->mErrorString = i18nc("@info", "Could not open file for writing."); | ||
581 | return false; | 621 | return false; | ||
582 | } | 622 | } | ||
583 | 623 | | |||
584 | return save(&file); | 624 | return save(&file); | ||
585 | } | 625 | } | ||
▲ Show 20 Lines • Show All 59 Lines • Show Last 20 Lines |