1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2005-06-14
7 * Description : A QImage loader for DImg framework.
8 *
9 * Copyright (C) 2005 by Renchi Raju <renchi dot raju at gmail dot com>
10 * Copyright (C) 2006-2019 by Caulier Gilles <caulier dot gilles at gmail dot com>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "dimgqimageloader.h"
26
27 // Qt includes
28
29 #include <QImage>
30 #include <QByteArray>
31 #include <QImageReader>
32
33 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
34 # include <QColorSpace>
35 #endif
36
37 // Local includes
38
39 #include "digikam_debug.h"
40 #include "dimgloaderobserver.h"
41
42 namespace DigikamQImageDImgPlugin
43 {
44
DImgQImageLoader(DImg * const image)45 DImgQImageLoader::DImgQImageLoader(DImg* const image)
46 : DImgLoader (image),
47 m_hasAlpha (false),
48 m_sixteenBit(false)
49 {
50 }
51
~DImgQImageLoader()52 DImgQImageLoader::~DImgQImageLoader()
53 {
54 }
55
load(const QString & filePath,DImgLoaderObserver * const observer)56 bool DImgQImageLoader::load(const QString& filePath, DImgLoaderObserver* const observer)
57 {
58 readMetadata(filePath);
59
60 // Loading is opaque to us. No support for stopping from observer,
61 // progress info are only pseudo values
62
63 QImageReader reader(filePath);
64 reader.setDecideFormatFromContent(true);
65
66 QByteArray readFormat = reader.format();
67 QImage image = reader.read();
68
69 if (observer)
70 {
71 observer->progressInfo(0.9F);
72 }
73
74 if (image.isNull())
75 {
76 qCWarning(DIGIKAM_DIMG_LOG_QIMAGE) << "Can not load \"" << filePath << "\" using DImg::DImgQImageLoader!";
77 qCWarning(DIGIKAM_DIMG_LOG_QIMAGE) << "Error message from loader:" << reader.errorString();
78 loadingFailed();
79 return false;
80 }
81
82 int colorModel = DImg::COLORMODELUNKNOWN;
83 int originalDepth = 0;
84
85 switch (image.format())
86 {
87 case QImage::Format_Invalid:
88 default:
89 colorModel = DImg::COLORMODELUNKNOWN;
90 originalDepth = 0;
91 break;
92
93 case QImage::Format_Mono:
94 case QImage::Format_MonoLSB:
95 colorModel = DImg::MONOCHROME;
96 originalDepth = 1;
97 break;
98
99 case QImage::Format_Indexed8:
100 colorModel = DImg::INDEXED;
101 originalDepth = 0;
102 break;
103
104 case QImage::Format_RGB32:
105 colorModel = DImg::RGB;
106 originalDepth = 8;
107 break;
108
109 case QImage::Format_ARGB32:
110 case QImage::Format_ARGB32_Premultiplied:
111 colorModel = DImg::RGB;
112 originalDepth = 8;
113 break;
114
115 #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
116
117 case QImage::Format_RGBX64:
118 case QImage::Format_RGBA64:
119 case QImage::Format_RGBA64_Premultiplied:
120 colorModel = DImg::RGB;
121 m_sixteenBit = true;
122 originalDepth = 16;
123 break;
124
125 #endif
126
127 }
128
129 QImage target;
130 uint w = 0;
131 uint h = 0;
132 uchar* data = nullptr;
133
134 if (!m_sixteenBit)
135 {
136 qCDebug(DIGIKAM_DIMG_LOG_QIMAGE) << filePath << "is a 8 bits per color per pixels QImage";
137
138 m_hasAlpha = (image.hasAlphaChannel() && (readFormat != "psd"));
139 target = image.convertToFormat(QImage::Format_ARGB32);
140 w = target.width();
141 h = target.height();
142 data = new_failureTolerant(w, h, 4);
143
144 if (!data)
145 {
146 qCWarning(DIGIKAM_DIMG_LOG_QIMAGE) << "Failed to allocate memory for loading" << filePath;
147 loadingFailed();
148 return false;
149 }
150
151 const QRgb* sptr = reinterpret_cast<const QRgb*>(target.constBits());
152 uchar* dptr = data;
153
154 for (uint i = 0 ; i < w * h ; ++i)
155 {
156 dptr[0] = qBlue(*sptr);
157 dptr[1] = qGreen(*sptr);
158 dptr[2] = qRed(*sptr);
159 dptr[3] = m_hasAlpha ? qAlpha(*sptr) : 255;
160
161 dptr += 4;
162 sptr++;
163 }
164 }
165 else
166 {
167
168 #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
169
170 qCDebug(DIGIKAM_DIMG_LOG_QIMAGE) << filePath << "is a 16 bits per color per pixels QImage";
171
172 m_hasAlpha = (image.hasAlphaChannel() && (readFormat != "psd"));
173 target = image.convertToFormat(QImage::Format_RGBA64);
174 w = target.width();
175 h = target.height();
176 data = new_failureTolerant(w, h, 8);
177
178 if (!data)
179 {
180 qCWarning(DIGIKAM_DIMG_LOG_QIMAGE) << "Failed to allocate memory for loading" << filePath;
181 loadingFailed();
182 return false;
183 }
184
185 const QRgba64* sptr = reinterpret_cast<const QRgba64*>(target.constBits());
186 ushort* dptr = reinterpret_cast<ushort*>(data);
187
188 for (uint i = 0 ; i < w * h ; ++i)
189 {
190 dptr[0] = (*sptr).blue();
191 dptr[1] = (*sptr).green();
192 dptr[2] = (*sptr).red();
193 dptr[3] = m_hasAlpha ? (*sptr).alpha() : 65535;
194
195 dptr += 4;
196 sptr++;
197 }
198
199 #endif
200
201 }
202
203 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
204
205 if (m_loadFlags & LoadICCData)
206 {
207 QByteArray iccRawProfile(target.colorSpace().iccProfile());
208
209 if (!iccRawProfile.isEmpty())
210 {
211 imageSetIccProfile(IccProfile(iccRawProfile));
212 }
213 }
214
215 #endif
216
217 if (observer)
218 {
219 observer->progressInfo(1.0F);
220 }
221
222 imageWidth() = w;
223 imageHeight() = h;
224 imageData() = data;
225
226 imageSetAttribute(QLatin1String("format"), QString::fromLatin1(readFormat).toUpper());
227 imageSetAttribute(QLatin1String("originalColorModel"), colorModel);
228 imageSetAttribute(QLatin1String("originalBitDepth"), originalDepth);
229 imageSetAttribute(QLatin1String("originalSize"), QSize(w, h));
230
231 return true;
232 }
233
save(const QString & filePath,DImgLoaderObserver * const observer)234 bool DImgQImageLoader::save(const QString& filePath, DImgLoaderObserver* const observer)
235 {
236 QVariant qualityAttr = imageGetAttribute(QLatin1String("quality"));
237 int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
238
239 if (quality < 0)
240 {
241 quality = 90;
242 }
243
244 if (quality > 100)
245 {
246 quality = 100;
247 }
248
249 QVariant formatAttr = imageGetAttribute(QLatin1String("format"));
250 QByteArray format = formatAttr.toByteArray();
251 QImage image = m_image->copyQImage();
252
253 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
254
255 QByteArray iccRawProfile = m_image->getIccProfile().data();
256
257 if (!iccRawProfile.isEmpty())
258 {
259 image.setColorSpace(QColorSpace::fromIccProfile(iccRawProfile));
260 }
261
262 #endif
263
264 if (observer)
265 {
266 observer->progressInfo(0.1F);
267 }
268
269 // Saving is opaque to us. No support for stopping from observer,
270 // progress info are only pseudo values
271 bool success = image.save(filePath, format.toUpper().constData(), quality);
272
273 if (observer && success)
274 {
275 observer->progressInfo(1.0F);
276 }
277
278 imageSetAttribute(QLatin1String("format"), format.toUpper());
279
280 saveMetadata(filePath);
281
282 return success;
283 }
284
hasAlpha() const285 bool DImgQImageLoader::hasAlpha() const
286 {
287 return m_hasAlpha;
288 }
289
sixteenBit() const290 bool DImgQImageLoader::sixteenBit() const
291 {
292 return m_sixteenBit;
293 }
294
isReadOnly() const295 bool DImgQImageLoader::isReadOnly() const
296 {
297 return false;
298 }
299
300 } // namespace DigikamQImageDImgPlugin
301