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