1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2005-06-14
7  * Description : digiKam 8/16 bits image management API.
8  *               Properties accessors.
9  *
10  * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11  * Copyright (C) 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 #include "dimg_p.h"
27 
28 namespace Digikam
29 {
30 
isNull() const31 bool DImg::isNull() const
32 {
33     return m_priv->null;
34 }
35 
width() const36 uint DImg::width() const
37 {
38     return m_priv->width;
39 }
40 
height() const41 uint DImg::height() const
42 {
43     return m_priv->height;
44 }
45 
size() const46 QSize DImg::size() const
47 {
48     return QSize(m_priv->width, m_priv->height);
49 }
50 
bits() const51 uchar* DImg::bits() const
52 {
53     return m_priv->data;
54 }
55 
copyBits() const56 uchar* DImg::copyBits() const
57 {
58     uchar* const data = new uchar[numBytes()];
59     memcpy(data, bits(), numBytes());
60 
61     return data;
62 }
63 
scanLine(uint i) const64 uchar* DImg::scanLine(uint i) const
65 {
66     if (i >= height())
67     {
68         return nullptr;
69     }
70 
71     uchar* const data = bits() + (width() * bytesDepth() * i);
72 
73     return data;
74 }
75 
hasAlpha() const76 bool DImg::hasAlpha() const
77 {
78     return m_priv->alpha;
79 }
80 
sixteenBit() const81 bool DImg::sixteenBit() const
82 {
83     return m_priv->sixteenBit;
84 }
85 
isReadOnly() const86 bool DImg::isReadOnly() const
87 {
88     return attribute(QLatin1String("isReadOnly")).toBool();
89 }
90 
originalColorModel() const91 DImg::COLORMODEL DImg::originalColorModel() const
92 {
93     if (hasAttribute(QLatin1String("originalColorModel")))
94     {
95         return (COLORMODEL)attribute(QLatin1String("originalColorModel")).toInt();
96     }
97     else
98     {
99         return COLORMODELUNKNOWN;
100     }
101 }
102 
originalBitDepth() const103 int DImg::originalBitDepth() const
104 {
105     return attribute(QLatin1String("originalBitDepth")).toInt();
106 }
107 
originalSize() const108 QSize DImg::originalSize() const
109 {
110     if (hasAttribute(QLatin1String("originalSize")))
111     {
112         QSize size = attribute(QLatin1String("originalSize")).toSize();
113 
114         if (size.isValid() && !size.isNull())
115         {
116             return size;
117         }
118     }
119 
120     return size();
121 }
122 
originalRatioSize() const123 QSize DImg::originalRatioSize() const
124 {
125     QSize size = originalSize();
126 
127     if (((width() < height()) && (size.width() > size.height())) ||
128         ((width() > height()) && (size.width() < size.height())))
129     {
130         size.transpose();
131     }
132 
133     return size;
134 }
135 
detectedFormat() const136 DImg::FORMAT DImg::detectedFormat() const
137 {
138     if (hasAttribute(QLatin1String("detectedFileFormat")))
139     {
140         return (FORMAT)attribute(QLatin1String("detectedFileFormat")).toInt();
141     }
142     else
143     {
144         return NONE;
145     }
146 }
147 
format() const148 QString DImg::format() const
149 {
150     return attribute(QLatin1String("format")).toString();
151 }
152 
savedFormat() const153 QString DImg::savedFormat() const
154 {
155     return attribute(QLatin1String("savedFormat")).toString();
156 }
157 
rawDecodingSettings() const158 DRawDecoding DImg::rawDecodingSettings() const
159 {
160     if (hasAttribute(QLatin1String("rawDecodingSettings")))
161     {
162         return attribute(QLatin1String("rawDecodingSettings")).value<DRawDecoding>();
163     }
164     else
165     {
166         return DRawDecoding();
167     }
168 }
169 
getIccProfile() const170 IccProfile DImg::getIccProfile() const
171 {
172     return m_priv->iccProfile;
173 }
174 
setIccProfile(const IccProfile & profile)175 void DImg::setIccProfile(const IccProfile& profile)
176 {
177     m_priv->iccProfile = profile;
178 }
179 
getMetadata() const180 MetaEngineData DImg::getMetadata() const
181 {
182     return m_priv->metaData;
183 }
184 
setMetadata(const MetaEngineData & data)185 void DImg::setMetadata(const MetaEngineData& data)
186 {
187     m_priv->metaData = data;
188 }
189 
numBytes() const190 quint64 DImg::numBytes() const
191 {
192     return ((quint64)width()  *
193             (quint64)height() *
194             (quint64)bytesDepth());
195 }
196 
numPixels() const197 quint64 DImg::numPixels() const
198 {
199     return ((quint64)width() *
200             (quint64)height());
201 }
202 
bytesDepth() const203 int DImg::bytesDepth() const
204 {
205     if (m_priv->sixteenBit)
206     {
207         return 8;
208     }
209 
210     return 4;
211 }
212 
bitsDepth() const213 int DImg::bitsDepth() const
214 {
215     if (m_priv->sixteenBit)
216     {
217         return 16;
218     }
219 
220     return 8;
221 }
222 
setAttribute(const QString & key,const QVariant & value)223 void DImg::setAttribute(const QString& key, const QVariant& value)
224 {
225     m_priv->attributes.insert(key, value);
226 }
227 
attribute(const QString & key) const228 QVariant DImg::attribute(const QString& key) const
229 {
230     if (m_priv->attributes.contains(key))
231     {
232         return m_priv->attributes[key];
233     }
234 
235     return QVariant();
236 }
237 
hasAttribute(const QString & key) const238 bool DImg::hasAttribute(const QString& key) const
239 {
240     return m_priv->attributes.contains(key);
241 }
242 
removeAttribute(const QString & key)243 void DImg::removeAttribute(const QString& key)
244 {
245     m_priv->attributes.remove(key);
246 }
247 
setEmbeddedText(const QString & key,const QString & text)248 void DImg::setEmbeddedText(const QString& key, const QString& text)
249 {
250     m_priv->embeddedText.insert(key, text);
251 }
252 
embeddedText(const QString & key) const253 QString DImg::embeddedText(const QString& key) const
254 {
255     if (m_priv->embeddedText.contains(key))
256     {
257         return m_priv->embeddedText[key];
258     }
259 
260     return QString();
261 }
262 
imageSavedAs(const QString & savePath)263 void DImg::imageSavedAs(const QString& savePath)
264 {
265     setAttribute(QLatin1String("savedFilePath"), savePath);
266     addAsReferredImage(savePath);
267 }
268 
originalFilePath() const269 QString DImg::originalFilePath() const
270 {
271     return attribute(QLatin1String("originalFilePath")).toString();
272 }
273 
lastSavedFilePath() const274 QString DImg::lastSavedFilePath() const
275 {
276     return attribute(QLatin1String("savedFilePath")).toString();
277 }
278 
fileOriginData() const279 QVariant DImg::fileOriginData() const
280 {
281     QVariantMap map;
282 
283     foreach (const QString& key, DImgStaticPriv::fileOriginAttributes())
284     {
285         QVariant attr = attribute(key);
286 
287         if (!attr.isNull())
288         {
289             map.insert(key, attr);
290         }
291     }
292 
293     return map;
294 }
295 
lastSavedFileOriginData() const296 QVariant DImg::lastSavedFileOriginData() const
297 {
298     QVariantMap map;
299     QVariant savedformat = attribute(QLatin1String("savedFormat"));
300 
301     if (!savedformat.isNull())
302     {
303         map.insert(QLatin1String("format"), savedformat);
304     }
305 
306     QVariant readonly = attribute(QLatin1String("savedFormat-isReadOnly"));
307 
308     if (!readonly.isNull())
309     {
310         map.insert(QLatin1String("isReadOnly"), readonly);
311     }
312 
313     QVariant filePath = attribute(QLatin1String("savedFilePath"));
314 
315     if (!filePath.isNull())
316     {
317         map.insert(QLatin1String("originalFilePath"), filePath);
318     }
319 
320     DImageHistory history = m_priv->imageHistory;
321 
322     if (!history.isEmpty())
323     {
324         history.adjustReferredImages();
325 
326         if (!history.entries().last().referredImages.isEmpty())
327         {
328             history.entries().last().referredImages.last().setType(HistoryImageId::Current);
329         }
330 
331         map.insert(QLatin1String("originalImageHistory"), QVariant::fromValue(history));
332     }
333 
334     return map;
335 }
336 
setFileOriginData(const QVariant & data)337 void DImg::setFileOriginData(const QVariant& data)
338 {
339     QVariantMap map = data.toMap();
340 
341     foreach (const QString& key, DImgStaticPriv::fileOriginAttributes())
342     {
343         removeAttribute(key);
344         QVariant attr = map.value(key);
345 
346         if (!attr.isNull())
347         {
348             setAttribute(key, attr);
349         }
350     }
351 }
352 
switchOriginToLastSaved()353 void DImg::switchOriginToLastSaved()
354 {
355     setFileOriginData(lastSavedFileOriginData());
356 }
357 
getPixelColor(uint x,uint y) const358 DColor DImg::getPixelColor(uint x, uint y) const
359 {
360     if (m_priv->null || (x >= m_priv->width) || (y >= m_priv->height))
361     {
362         return DColor();
363     }
364 
365     int depth         = bytesDepth();
366     uchar* const data = m_priv->data + x * depth + (m_priv->width * y * depth);
367 
368     return (DColor(data, m_priv->sixteenBit));
369 }
370 
prepareSubPixelAccess()371 void DImg::prepareSubPixelAccess()
372 {
373     if (m_priv->lanczos_func)
374     {
375         return;
376     }
377 
378     /* Precompute the Lanczos kernel */
379 
380     LANCZOS_DATA_TYPE* lanczos_func = new LANCZOS_DATA_TYPE[LANCZOS_SUPPORT * LANCZOS_SUPPORT * LANCZOS_TABLE_RES];
381 
382     for (int i = 0 ; (i < LANCZOS_SUPPORT * LANCZOS_SUPPORT * LANCZOS_TABLE_RES) ; ++i)
383     {
384         if (i == 0)
385         {
386             lanczos_func [i] = LANCZOS_DATA_ONE;
387         }
388         else
389         {
390             float d          = sqrt(((float)i) / LANCZOS_TABLE_RES);
391             lanczos_func [i] = (LANCZOS_DATA_TYPE)((LANCZOS_DATA_ONE * LANCZOS_SUPPORT *
392                                                     sin(M_PI * d) * sin((M_PI / LANCZOS_SUPPORT) * d)) /
393                                                    (M_PI * M_PI * d * d));
394         }
395     }
396 
397     m_priv->lanczos_func = lanczos_func;
398 }
399 
400 #ifdef LANCZOS_DATA_FLOAT
401 
normalizeAndClamp(float norm,int sum,int max)402 static inline int normalizeAndClamp(float norm, int sum, int max)
403 {
404     int r = 0;
405 
406     if (norm != 0.0)
407     {
408         r = sum / norm;
409     }
410 
411     if      (r < 0)
412     {
413         r = 0;
414     }
415     else if (r > max)
416     {
417         r = max;
418     }
419 
420     return r;
421 }
422 
423 #else /* LANCZOS_DATA_FLOAT */
424 
normalizeAndClamp(int norm,int sum,int max)425 static inline int normalizeAndClamp(int norm, int sum, int max)
426 {
427     int r = 0;
428 
429     if (norm != 0)
430     {
431         r = sum / norm;
432     }
433 
434     if      (r < 0)
435     {
436         r = 0;
437     }
438     else if (r > max)
439     {
440         r = max;
441     }
442 
443     return r;
444 }
445 
446 #endif /* LANCZOS_DATA_FLOAT */
447 
getSubPixelColor(float x,float y) const448 DColor DImg::getSubPixelColor(float x, float y) const
449 {
450     if (isNull())
451     {
452         return DColor();
453     }
454 
455     const LANCZOS_DATA_TYPE* lanczos_func = m_priv->lanczos_func;
456 
457     if (lanczos_func == nullptr)
458     {
459         return DColor();
460     }
461 
462     x = qBound(0.0f, x, (float)width()  - 1);
463     y = qBound(0.0f, y, (float)height() - 1);
464 
465     Digikam::DColor col(0, 0, 0, 0xFFFF, sixteenBit());
466 
467 #ifdef LANCZOS_DATA_FLOAT
468 
469     float xs = ::ceilf(x)  - LANCZOS_SUPPORT;
470     float xe = ::floorf(x) + LANCZOS_SUPPORT;
471     float ys = ::ceilf(y)  - LANCZOS_SUPPORT;
472     float ye = ::floorf(y) + LANCZOS_SUPPORT;
473 
474     if ((xs >= 0) && (ys >= 0) && (xe < width()) && (ye < height()))
475     {
476         float norm = 0.0;
477         float sumR = 0.0;
478         float sumG = 0.0;
479         float sumB = 0.0;
480         float _dx  = x - xs;
481         float dy   = y - ys;
482 
483         for ( ; ys <= ye ; ys += 1.0, dy -= 1.0)
484         {
485             float xc, dx = _dx;
486 
487             for (xc = xs ; xc <= xe ; xc += 1.0, dx -= 1.0)
488             {
489                 uchar* const data = bits() + (int)(xs * bytesDepth()) + (int)(width() * ys * bytesDepth());
490                 DColor src        = DColor(data, sixteenBit());
491                 float d           = dx * dx + dy * dy;
492 
493                 if (d >= LANCZOS_SUPPORT * LANCZOS_SUPPORT)
494                 {
495                     continue;
496                 }
497 
498                 d     = lanczos_func [(int)(d * LANCZOS_TABLE_RES)];
499                 norm += d;
500                 sumR += d * src.red();
501                 sumG += d * src.green();
502                 sumB += d * src.blue();
503             }
504         }
505 
506         int max = sixteenBit() ? 65535 : 255;
507         col.setRed(normalizeAndClamp(norm, sumR, max));
508         col.setGreen(normalizeAndClamp(norm, sumG, max));
509         col.setBlue(normalizeAndClamp(norm, sumB, max));
510     }
511 
512 #else /* LANCZOS_DATA_FLOAT */
513 
514     // Do it in integer arithmetic, it's faster
515 
516     int xx   = (int)x;
517     int yy   = (int)y;
518     int xs   = xx + 1 - LANCZOS_SUPPORT;
519     int xe   = xx     + LANCZOS_SUPPORT;
520     int ys   = yy + 1 - LANCZOS_SUPPORT;
521     int ye   = yy     + LANCZOS_SUPPORT;
522     int norm = 0;
523     int sumR = 0;
524     int sumG = 0;
525     int sumB = 0;
526     int _dx  = (int)(x * 4096.0) - (xs << 12);
527     int dy   = (int)(y * 4096.0) - (ys << 12);
528 
529     for ( ; ys <= ye ; ++ys, dy -= 4096)
530     {
531         int xc;
532         int dx = _dx;
533 
534         for (xc = xs ; xc <= xe ; ++xc, dx -= 4096)
535         {
536             DColor src(0, 0, 0, 0xFFFF, sixteenBit());
537 
538             if ((xc >= 0) && (ys >= 0) && (xc < (int)width()) && (ys < (int)height()))
539             {
540                 uchar* const data = bits() + xc * bytesDepth() + width() * ys * bytesDepth();
541                 src.setColor(data, sixteenBit());
542             }
543 
544             int d = (dx * dx + dy * dy) >> 12;
545 
546             if (d >= (4096 * LANCZOS_SUPPORT * LANCZOS_SUPPORT))
547             {
548                 continue;
549             }
550 
551             d     = lanczos_func [(d * LANCZOS_TABLE_RES) >> 12];
552             norm += d;
553             sumR += d * src.red();
554             sumG += d * src.green();
555             sumB += d * src.blue();
556         }
557     }
558 
559     int max = sixteenBit() ? 65535 : 255;
560     col.setRed(normalizeAndClamp(norm, sumR, max));
561     col.setGreen(normalizeAndClamp(norm, sumG, max));
562     col.setBlue(normalizeAndClamp(norm, sumB, max));
563 
564 #endif /* LANCZOS_DATA_FLOAT */
565 
566     return col;
567 }
568 
getSubPixelColorFast(float x,float y) const569 DColor DImg::getSubPixelColorFast(float x, float y) const
570 {
571     if (isNull())
572     {
573         return DColor();
574     }
575 
576     x = qBound(0.0f, x, (float)width()  - 1);
577     y = qBound(0.0f, y, (float)height() - 1);
578 
579     int xx      = (int)x;
580     int yy      = (int)y;
581     float d_x   = x - (int)x;
582     float d_y   = y - (int)y;
583     uchar* data = nullptr;
584     (void)data;     // To prevent cppcheck warnings.
585 
586     DColor d00, d01, d10, d11;
587     DColor col;
588 
589     data = bits() + xx * bytesDepth() + yy * width() * bytesDepth();
590     d00.setColor(data, sixteenBit());
591 
592     if ((xx + 1) < (int)width())
593     {
594         data = bits() + (xx + 1) * bytesDepth() + yy * width() * bytesDepth();
595         d10.setColor(data, sixteenBit());
596     }
597 
598     if ((yy + 1) < (int)height())
599     {
600         data = bits() + xx * bytesDepth() + (yy + 1) * width() * bytesDepth();
601         d01.setColor(data, sixteenBit());
602     }
603 
604     if (((xx + 1) < (int)width()) && ((yy + 1) < (int)height()))
605     {
606         data = bits() + (xx + 1) * bytesDepth() + (yy + 1) * width() * bytesDepth();
607         d11.setColor(data, sixteenBit());
608     }
609 
610     d00.multiply(1.0 - d_x);
611     d00.multiply(1.0 - d_y);
612 
613     d10.multiply(d_x);
614     d10.multiply(1.0 - d_y);
615 
616     d01.multiply(1.0 - d_x);
617     d01.multiply(d_y);
618 
619     d11.multiply(d_x);
620     d11.multiply(d_y);
621 
622     col.blendAdd(d00);
623     col.blendAdd(d10);
624     col.blendAdd(d01);
625     col.blendAdd(d11);
626 
627     if (sixteenBit())
628     {
629         col.blendClamp16();
630     }
631     else
632     {
633         col.blendClamp8();
634     }
635 
636     return col;
637 }
638 
setPixelColor(uint x,uint y,const DColor & color)639 void DImg::setPixelColor(uint x, uint y, const DColor& color)
640 {
641     if (m_priv->null || (x >= m_priv->width) || (y >= m_priv->height))
642     {
643         return;
644     }
645 
646     if (color.sixteenBit() != m_priv->sixteenBit)
647     {
648         return;
649     }
650 
651     int depth         = bytesDepth();
652     uchar* const data = m_priv->data + x * depth + (m_priv->width * y * depth);
653     color.setPixel(data);
654 }
655 
hasTransparentPixels() const656 bool DImg::hasTransparentPixels() const
657 {
658     if (m_priv->null || !m_priv->alpha)
659     {
660         return false;
661     }
662 
663     const uint w = m_priv->width;
664     const uint h = m_priv->height;
665 
666     if (!m_priv->sixteenBit)     // 8 bits image.
667     {
668         uchar* srcPtr = m_priv->data;
669 
670         for (uint j = 0 ; j < h ; ++j)
671         {
672             for (uint i = 0 ; i < w ; ++i)
673             {
674                 if (srcPtr[3] != 0xFF)
675                 {
676                     return true;
677                 }
678 
679                 srcPtr += 4;
680             }
681         }
682     }
683     else
684     {
685         unsigned short* srcPtr = reinterpret_cast<unsigned short*>(m_priv->data);
686 
687         for (uint j = 0 ; j < h ; ++j)
688         {
689             for (uint i = 0 ; i < w ; ++i)
690             {
691                 if (srcPtr[3] != 0xFFFF)
692                 {
693                     return true;
694                 }
695 
696                 srcPtr += 4;
697             }
698         }
699     }
700 
701     return false;
702 }
703 
704 } // namespace Digikam
705