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