File indexing completed on 2025-01-19 03:51:03

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2005-06-17
0007  * Description : A TIFF IO file for DImg framework - load operations
0008  *
0009  * SPDX-FileCopyrightText: 2005      by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 // C ANSI includes
0017 extern "C"
0018 {
0019 #include <tiffvers.h>
0020 }
0021 
0022 // C++ includes
0023 
0024 #include <cstdio>
0025 #include <cmath>
0026 
0027 // Qt includes
0028 
0029 #include <QFile>
0030 #include <QFloat16>
0031 #include <QByteArray>
0032 
0033 // Local includes
0034 
0035 #include "digikam_debug.h"
0036 #include "digikam_config.h"
0037 #include "dimgloaderobserver.h"
0038 #include "dimgtiffloader.h"     //krazy:exclude=includes
0039 
0040 namespace DigikamTIFFDImgPlugin
0041 {
0042 
0043 bool DImgTIFFLoader::load(const QString& filePath, DImgLoaderObserver* const observer)
0044 {
0045     readMetadata(filePath);
0046 
0047     // -------------------------------------------------------------------
0048     // TIFF error handling. If an errors/warnings occurs during reading,
0049     // libtiff will call these methods
0050 
0051 #ifdef Q_OS_WIN
0052 
0053     TIFFSetWarningHandler(NULL);
0054 
0055 #else
0056 
0057     TIFFSetWarningHandler(dimg_tiff_warning);
0058 
0059 #endif
0060 
0061     TIFFSetErrorHandler(dimg_tiff_error);
0062 
0063     // -------------------------------------------------------------------
0064     // Open the file
0065 
0066 #ifdef Q_OS_WIN
0067 
0068     TIFF* const tif = TIFFOpenW((const wchar_t*)filePath.utf16(), "r");
0069 
0070 #else
0071 
0072     TIFF* const tif = TIFFOpen(filePath.toUtf8().constData(), "r");
0073 
0074 #endif
0075 
0076     if (!tif)
0077     {
0078         qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Cannot open image file.";
0079         loadingFailed();
0080         return false;
0081     }
0082 /*
0083     if (DIGIKAM_DIMG_LOG_TIFF().isDebugEnabled())
0084     {
0085         TIFFPrintDirectory(tif, stdout, 0);
0086     }
0087 */
0088     // -------------------------------------------------------------------
0089     // Get image information.
0090 
0091     uint32    w, h;
0092     uint16    bits_per_sample;
0093     uint16    samples_per_pixel;
0094     uint16    sample_format;
0095     uint16    photometric;
0096     uint16    planar_config;
0097     uint32    rows_per_strip;
0098     tsize_t   strip_size;
0099     tstrip_t  num_of_strips;
0100 
0101     TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, &w);
0102     TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, &h);
0103 
0104     TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
0105     TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
0106     TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format);
0107     TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
0108 
0109     if (TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip) == 0 || rows_per_strip == 0)
0110     {
0111         qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "TIFF loader: Cannot handle non-stripped images. Loading file "
0112                                          << filePath;
0113         TIFFClose(tif);
0114         loadingFailed();
0115 
0116         return false;
0117     }
0118 
0119     if (rows_per_strip > h)
0120     {
0121         rows_per_strip = h;
0122     }
0123 
0124     if (   (bits_per_sample   == 0)
0125         || (samples_per_pixel == 0)
0126         || (rows_per_strip    == 0)
0127 /*
0128         || (rows_per_strip >  h)
0129 */
0130        )
0131     {
0132         qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "TIFF loader: Encountered invalid value in image." << QT_ENDL
0133                                          << " bits_per_sample   : " << bits_per_sample         << QT_ENDL
0134                                          << " samples_per_pixel : " << samples_per_pixel       << QT_ENDL
0135                                          << " rows_per_strip    : " << rows_per_strip          << QT_ENDL
0136                                          << " h                 : " << h                       << QT_ENDL
0137                                          << " Loading file      : " << filePath;
0138         TIFFClose(tif);
0139         loadingFailed();
0140 
0141         return false;
0142     }
0143 
0144     // TODO: check others TIFF color-spaces here. Actually, only RGB, PALETTE and MINISBLACK
0145     // have been tested.
0146     // Complete description of TIFFTAG_PHOTOMETRIC tag can be found at this Url:
0147     // www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
0148 
0149     TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
0150 
0151     if (
0152          (photometric != PHOTOMETRIC_RGB)                                 &&
0153          (photometric != PHOTOMETRIC_PALETTE)                             &&
0154          (photometric != PHOTOMETRIC_MINISWHITE)                          &&
0155          (photometric != PHOTOMETRIC_MINISBLACK)                          &&
0156         ((photometric != PHOTOMETRIC_YCBCR) | (bits_per_sample != 8))     &&
0157         ((photometric != PHOTOMETRIC_SEPARATED) | (bits_per_sample != 8)) &&
0158         (m_loadFlags & LoadImageData)
0159        )
0160     {
0161         qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Can not handle image without RGB color-space: "
0162                                          << photometric;
0163         TIFFClose(tif);
0164         loadingFailed();
0165 
0166         return false;
0167     }
0168 
0169     int colorModel = DImg::COLORMODELUNKNOWN;
0170 
0171     switch (photometric)
0172     {
0173         case PHOTOMETRIC_MINISWHITE:
0174         case PHOTOMETRIC_MINISBLACK:
0175         {
0176             colorModel = DImg::GRAYSCALE;
0177             break;
0178         }
0179 
0180         case PHOTOMETRIC_RGB:
0181         {
0182             colorModel = DImg::RGB;
0183             break;
0184         }
0185 
0186         case PHOTOMETRIC_PALETTE:
0187         {
0188             colorModel = DImg::INDEXED;
0189             break;
0190         }
0191 
0192         case PHOTOMETRIC_MASK:
0193         {
0194             colorModel = DImg::MONOCHROME;
0195             break;
0196         }
0197 
0198         case PHOTOMETRIC_SEPARATED:
0199         {
0200             colorModel = DImg::CMYK;
0201             break;
0202         }
0203 
0204         case PHOTOMETRIC_YCBCR:
0205         {
0206             colorModel = DImg::YCBCR;
0207             break;
0208         }
0209 
0210         case PHOTOMETRIC_CIELAB:
0211         case PHOTOMETRIC_ICCLAB:
0212         case PHOTOMETRIC_ITULAB:
0213         {
0214             colorModel = DImg::CIELAB;
0215             break;
0216         }
0217 
0218         case PHOTOMETRIC_LOGL:
0219         case PHOTOMETRIC_LOGLUV:
0220         {
0221             colorModel = DImg::COLORMODELRAW;
0222             break;
0223         }
0224     }
0225 
0226     if (samples_per_pixel == 4)
0227     {
0228         m_hasAlpha = true;
0229     }
0230     else
0231     {
0232         m_hasAlpha = false;
0233     }
0234 
0235     if ((bits_per_sample == 16) || (bits_per_sample == 32))
0236     {
0237         m_sixteenBit = true;
0238     }
0239     else
0240     {
0241         m_sixteenBit = false;
0242     }
0243 
0244     // -------------------------------------------------------------------
0245     // Read image ICC profile
0246 
0247     if (m_loadFlags & LoadICCData)
0248     {
0249         uchar*  profile_data = nullptr;
0250         uint32  profile_size;
0251 
0252         if (TIFFGetField(tif, TIFFTAG_ICCPROFILE, &profile_size, &profile_data))
0253         {
0254             QByteArray profile_rawdata;
0255             profile_rawdata.resize(profile_size);
0256             memcpy(profile_rawdata.data(), profile_data, profile_size);
0257             imageSetIccProfile(IccProfile(profile_rawdata));
0258         }
0259         else
0260         {
0261             // If ICC profile is null, check Exif metadata.
0262 
0263             checkExifWorkingColorSpace();
0264         }
0265     }
0266 
0267     // -------------------------------------------------------------------
0268     // Get image data.
0269 
0270     QScopedArrayPointer<uchar> data;
0271 
0272     if (m_loadFlags & LoadImageData)
0273     {
0274         if (observer)
0275         {
0276             observer->progressInfo(0.1F);
0277         }
0278 
0279         strip_size    = TIFFStripSize(tif);
0280         num_of_strips = TIFFNumberOfStrips(tif);
0281 
0282         if (bits_per_sample == 16)          // 16 bits image.
0283         {
0284             data.reset(new_failureTolerant(w, h, 8));
0285             QScopedArrayPointer<uchar> strip(new_failureTolerant(strip_size));
0286 
0287             if (!data || strip.isNull())
0288             {
0289                 qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to allocate memory for TIFF image" << filePath;
0290                 TIFFClose(tif);
0291                 loadingFailed();
0292 
0293                 return false;
0294             }
0295 
0296             qint64 offset     = 0;
0297             qint64 bytesRead  = 0;
0298             uint   checkpoint = 0;
0299 
0300             for (tstrip_t st = 0 ; st < num_of_strips ; ++st)
0301             {
0302                 if (observer && (st == checkpoint))
0303                 {
0304                     checkpoint += granularity(observer, num_of_strips, 0.8F);
0305 
0306                     if (!observer->continueQuery())
0307                     {
0308                         TIFFClose(tif);
0309                         loadingFailed();
0310 
0311                         return false;
0312                     }
0313 
0314                     observer->progressInfo(0.1F + (0.8F * (((float)st) / ((float)num_of_strips))));
0315                 }
0316 
0317                 bytesRead = TIFFReadEncodedStrip(tif, st, strip.data(), strip_size);
0318 
0319                 if (bytesRead == -1)
0320                 {
0321                     qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to read strip";
0322                     TIFFClose(tif);
0323                     loadingFailed();
0324 
0325                     return false;
0326                 }
0327 
0328                 if ((num_of_strips != 0) && (samples_per_pixel != 0))
0329                 {
0330                     if ((planar_config == PLANARCONFIG_SEPARATE) &&
0331                         (remainder((double)st, (double)(num_of_strips / samples_per_pixel)) == 0.0))
0332                     {
0333                         offset = 0;
0334                     }
0335                 }
0336 
0337                 ushort* stripPtr = reinterpret_cast<ushort*>(strip.data());
0338                 ushort* dataPtr  = reinterpret_cast<ushort*>(data.data() + offset);
0339                 ushort* p        = nullptr;
0340 
0341                 // tiff data is read as BGR or ABGR or Greyscale
0342 
0343                 if      (samples_per_pixel == 1)   // See bug #148400: Greyscale pictures only have _one_ sample per pixel
0344                 {
0345                     for (int i = 0 ; i < (bytesRead / 2) ; ++i)
0346                     {
0347                         // We have to read two bytes for one pixel
0348 
0349                         p = dataPtr;
0350 
0351                         if (sample_format == SAMPLEFORMAT_IEEEFP)
0352                         {
0353                             p[0] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr))   * 65535.0F, 65535.0F);
0354                             p[1] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr))   * 65535.0F, 65535.0F);
0355                             p[2] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0356                             p[3] = 0xFFFF;
0357                         }
0358                         else
0359                         {
0360                             p[0] = *stripPtr;     // RGB have to be set to the _same_ value
0361                             p[1] = *stripPtr;
0362                             p[2] = *stripPtr++;
0363                             p[3] = 0xFFFF;        // set alpha to 100%
0364                         }
0365 
0366                         dataPtr += 4;
0367                     }
0368 
0369                     offset += bytesRead * 4;      // The _byte_offset in the data array is, of course, four times bytesRead
0370                 }
0371 
0372                 else if ((samples_per_pixel == 3) &&
0373                          (planar_config == PLANARCONFIG_CONTIG))
0374                 {
0375                     for (int i = 0 ; i < (bytesRead / 6) ; ++i)
0376                     {
0377                         p = dataPtr;
0378 
0379                         if (sample_format == SAMPLEFORMAT_IEEEFP)
0380                         {
0381                             p[2] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0382                             p[1] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0383                             p[0] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0384                             p[3] = 0xFFFF;
0385                         }
0386                         else
0387                         {
0388                             p[2] = *stripPtr++;
0389                             p[1] = *stripPtr++;
0390                             p[0] = *stripPtr++;
0391                             p[3] = 0xFFFF;
0392                         }
0393 
0394                         dataPtr += 4;
0395                     }
0396 
0397                     offset += bytesRead / 6 * 8;
0398                 }
0399 
0400                 // cppcheck-suppress knownConditionTrueFalse
0401                 else if ((samples_per_pixel == 3) &&
0402                          (planar_config == PLANARCONFIG_SEPARATE))
0403                 {
0404                     for (int i = 0 ; i < (bytesRead / 2) ; ++i)
0405                     {
0406                         p = dataPtr;
0407 
0408                         // cppcheck-suppress knownConditionTrueFalse
0409                         if (samples_per_pixel != 0)
0410                         {
0411                             int den = (int)num_of_strips / (int)samples_per_pixel;
0412 
0413                             if (den != 0)
0414                             {
0415                                 int val = st / den;
0416 
0417                                 if (sample_format == SAMPLEFORMAT_IEEEFP)
0418                                 {
0419                                     switch (val)
0420                                     {
0421                                         case 0:
0422                                         {
0423                                             p[2] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0424                                             p[3] = 0xFFFF;
0425                                             break;
0426                                         }
0427 
0428                                         case 1:
0429                                         {
0430                                             p[1] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0431                                             break;
0432                                         }
0433 
0434                                         case 2:
0435                                         {
0436                                             p[0] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0437                                             break;
0438                                         }
0439                                     }
0440                                 }
0441                                 else
0442                                 {
0443                                     switch (val)
0444                                     {
0445                                         case 0:
0446                                         {
0447                                             p[2] = *stripPtr++;
0448                                             p[3] = 0xFFFF;
0449                                             break;
0450                                         }
0451 
0452                                         case 1:
0453                                         {
0454                                             p[1] = *stripPtr++;
0455                                             break;
0456                                         }
0457 
0458                                         case 2:
0459                                         {
0460                                             p[0] = *stripPtr++;
0461                                             break;
0462                                         }
0463                                     }
0464                                 }
0465 
0466                                 dataPtr += 4;
0467                             }
0468                         }
0469                     }
0470 
0471                     offset += bytesRead / 2 * 8;
0472                 }
0473 
0474                 else if ((samples_per_pixel == 4) &&
0475                          (planar_config == PLANARCONFIG_CONTIG))
0476                 {
0477                     for (int i = 0 ; i < (bytesRead / 8) ; ++i)
0478                     {
0479                         p = dataPtr;
0480 
0481                         if (sample_format == SAMPLEFORMAT_IEEEFP)
0482                         {
0483                             p[2] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0484                             p[1] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0485                             p[0] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0486                             p[3] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0487                         }
0488                         else
0489                         {
0490                             p[2] = *stripPtr++;
0491                             p[1] = *stripPtr++;
0492                             p[0] = *stripPtr++;
0493                             p[3] = *stripPtr++;
0494                         }
0495 
0496                         dataPtr += 4;
0497                     }
0498 
0499                     offset += bytesRead;
0500                 }
0501 
0502                 // cppcheck-suppress knownConditionTrueFalse
0503                 else if ((samples_per_pixel == 4) &&
0504                          (planar_config == PLANARCONFIG_SEPARATE))
0505                 {
0506                     for (int i = 0 ; i < (bytesRead / 2) ; ++i)
0507                     {
0508                         p = dataPtr;
0509 
0510                         // cppcheck-suppress knownConditionTrueFalse
0511                         if (samples_per_pixel != 0)
0512                         {
0513                             int den = (int)num_of_strips / (int)samples_per_pixel;
0514 
0515                             if (den != 0)
0516                             {
0517                                 int val = st / den;
0518 
0519                                 if (sample_format == SAMPLEFORMAT_IEEEFP)
0520                                 {
0521                                     switch (val)
0522                                     {
0523                                         case 0:
0524                                         {
0525                                             p[2] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0526                                             break;
0527                                         }
0528 
0529                                         case 1:
0530                                         {
0531                                             p[1] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0532                                             break;
0533                                         }
0534 
0535                                         case 2:
0536                                         {
0537                                             p[0] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0538                                             break;
0539                                         }
0540 
0541                                         case 3:
0542                                         {
0543                                             p[3] = (ushort)qBound(0.0F, (*reinterpret_cast<qfloat16*>(stripPtr++)) * 65535.0F, 65535.0F);
0544                                             break;
0545                                         }
0546                                     }
0547                                 }
0548                                 else
0549                                 {
0550                                     switch (val)
0551                                     {
0552                                         case 0:
0553                                         {
0554                                             p[2] = *stripPtr++;
0555                                             break;
0556                                         }
0557 
0558                                         case 1:
0559                                         {
0560                                             p[1] = *stripPtr++;
0561                                             break;
0562                                         }
0563 
0564                                         case 2:
0565                                         {
0566                                             p[0] = *stripPtr++;
0567                                             break;
0568                                         }
0569 
0570                                         case 3:
0571                                         {
0572                                             p[3] = *stripPtr++;
0573                                             break;
0574                                         }
0575                                     }
0576                                 }
0577 
0578                                 dataPtr += 4;
0579                             }
0580                         }
0581                     }
0582 
0583                     offset += bytesRead / 2 * 8;
0584                 }
0585             }
0586         }
0587 
0588         else if ((bits_per_sample == 32) && (sample_format == SAMPLEFORMAT_IEEEFP))          // 32 bits float image.
0589         {
0590             data.reset(new_failureTolerant(w, h, 8));
0591             QScopedArrayPointer<uchar> strip(new_failureTolerant(strip_size));
0592 
0593             if (!data || strip.isNull())
0594             {
0595                 qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to allocate memory for TIFF image" << filePath;
0596                 TIFFClose(tif);
0597                 loadingFailed();
0598 
0599                 return false;
0600             }
0601 
0602             qint64 offset     = 0;
0603             qint64 bytesRead  = 0;
0604             uint   checkpoint = 0;
0605             float  maxValue   = 0.0;
0606 
0607             for (tstrip_t st = 0 ; st < num_of_strips ; ++st)
0608             {
0609                 if (observer && !observer->continueQuery())
0610                 {
0611                     TIFFClose(tif);
0612                     loadingFailed();
0613 
0614                     return false;
0615                 }
0616 
0617                 bytesRead = TIFFReadEncodedStrip(tif, st, strip.data(), strip_size);
0618 
0619                 if (bytesRead == -1)
0620                 {
0621                     qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to read strip";
0622                     TIFFClose(tif);
0623                     loadingFailed();
0624 
0625                     return false;
0626                 }
0627 
0628                 float* stripPtr = reinterpret_cast<float*>(strip.data());
0629 
0630                 for (int i = 0 ; i < (bytesRead / 4) ; ++i)
0631                 {
0632                     maxValue = qMax(maxValue, *stripPtr++);
0633                 }
0634             }
0635 
0636             double factor = (maxValue > 10.0) ? log10(maxValue) * 1.5 : 1.0;
0637             double scale  = (factor > 1.0)    ? 0.75                  : 1.0;
0638 
0639             if (factor > 1.0)
0640             {
0641                 qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "TIFF image cannot be converted lossless from 32 to 16 bits" << filePath;
0642             }
0643 
0644             for (tstrip_t st = 0 ; st < num_of_strips ; ++st)
0645             {
0646                 if (observer && st == checkpoint)
0647                 {
0648                     checkpoint += granularity(observer, num_of_strips, 0.8F);
0649 
0650                     if (!observer->continueQuery())
0651                     {
0652                         TIFFClose(tif);
0653                         loadingFailed();
0654 
0655                         return false;
0656                     }
0657 
0658                     observer->progressInfo(0.1F + (0.8F * (((float)st) / ((float)num_of_strips))));
0659                 }
0660 
0661                 bytesRead = TIFFReadEncodedStrip(tif, st, strip.data(), strip_size);
0662 
0663                 if (bytesRead == -1)
0664                 {
0665                     qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to read strip";
0666                     TIFFClose(tif);
0667                     loadingFailed();
0668                     return false;
0669                 }
0670 
0671 
0672                 if ((num_of_strips != 0) && (samples_per_pixel != 0))
0673                 {
0674                     if ((planar_config == PLANARCONFIG_SEPARATE) &&
0675                         (remainder((double)st, (double)(num_of_strips / samples_per_pixel)) == 0.0))
0676                     {
0677                         offset = 0;
0678                     }
0679                 }
0680 
0681                 float*  stripPtr = reinterpret_cast<float*>(strip.data());
0682                 ushort* dataPtr  = reinterpret_cast<ushort*>(data.data() + offset);
0683                 ushort* p        = nullptr;
0684 
0685                 if      ((samples_per_pixel == 3) &&
0686                          (planar_config == PLANARCONFIG_CONTIG))
0687                 {
0688                     for (int i = 0 ; i < (bytesRead / 12) ; ++i)
0689                     {
0690                         p = dataPtr;
0691 
0692                         p[2] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0693                         p[1] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0694                         p[0] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0695                         p[3] = 0xFFFF;
0696 
0697                         dataPtr += 4;
0698                     }
0699 
0700                     offset += bytesRead / 12 * 8;
0701                 }
0702 
0703                 // cppcheck-suppress knownConditionTrueFalse
0704                 else if ((samples_per_pixel == 3) &&
0705                          (planar_config == PLANARCONFIG_SEPARATE))
0706                 {
0707                     for (int i = 0 ; i < (bytesRead / 4) ; ++i)
0708                     {
0709                         p = dataPtr;
0710 
0711                         // cppcheck-suppress knownConditionTrueFalse
0712                         if (samples_per_pixel != 0)
0713                         {
0714                             int den = (int)num_of_strips / (int)samples_per_pixel;
0715 
0716                             if (den != 0)
0717                             {
0718                                 int val = st / den;
0719 
0720                                 switch (val)
0721                                 {
0722                                     case 0:
0723                                     {
0724                                         p[2] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0725                                         p[3] = 0xFFFF;
0726                                         break;
0727                                     }
0728 
0729                                     case 1:
0730                                     {
0731                                         p[1] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0732                                         break;
0733                                     }
0734 
0735                                     case 2:
0736                                     {
0737                                         p[0] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0738                                         break;
0739                                     }
0740                                 }
0741 
0742                                 dataPtr += 4;
0743                             }
0744                         }
0745                     }
0746 
0747                     offset += bytesRead / 4 * 8;
0748                 }
0749 
0750                 else if ((samples_per_pixel == 4) &&
0751                          (planar_config == PLANARCONFIG_CONTIG))
0752                 {
0753                     for (int i = 0 ; i < (bytesRead / 16) ; ++i)
0754                     {
0755                         p = dataPtr;
0756 
0757                         p[2] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0758                         p[1] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0759                         p[0] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0760                         p[3] = (ushort)qBound(0.0, (double)*stripPtr++ * 65535.0, 65535.0);
0761 
0762                         dataPtr += 4;
0763                     }
0764 
0765                     offset += bytesRead / 16 * 8;
0766                 }
0767 
0768                 // cppcheck-suppress knownConditionTrueFalse
0769                 else if ((samples_per_pixel == 4) &&
0770                          (planar_config == PLANARCONFIG_SEPARATE))
0771                 {
0772                     for (int i = 0 ; i < bytesRead / 4 ; ++i)
0773                     {
0774                         p = dataPtr;
0775 
0776                         // cppcheck-suppress knownConditionTrueFalse
0777                         if (samples_per_pixel != 0)
0778                         {
0779                             int den = (int)num_of_strips / (int)samples_per_pixel;
0780 
0781                             if (den != 0)
0782                             {
0783                                 int val = st / den;
0784 
0785                                 switch (val)
0786                                 {
0787                                     case 0:
0788                                     {
0789                                         p[2] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0790                                         break;
0791                                     }
0792 
0793                                     case 1:
0794                                     {
0795                                         p[1] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0796                                         break;
0797                                     }
0798 
0799                                     case 2:
0800                                     {
0801                                         p[0] = (ushort)qBound(0.0, pow((double)*stripPtr++ / factor, scale) * 65535.0, 65535.0);
0802                                         break;
0803                                     }
0804 
0805                                     case 3:
0806                                     {
0807                                         p[3] = (ushort)qBound(0.0, (double)*stripPtr++ * 65535.0, 65535.0);
0808                                         break;
0809                                     }
0810                                 }
0811 
0812                                 dataPtr += 4;
0813                             }
0814                         }
0815                     }
0816 
0817                     offset += bytesRead / 4 * 8;
0818                 }
0819             }
0820         }
0821 
0822         else       // Non 16 or 32 bits images ==> get it on BGRA 8 bits.
0823         {
0824             data.reset(new_failureTolerant(w, h, 4));
0825             QScopedArrayPointer<uchar> strip(new_failureTolerant(w, rows_per_strip, 4));
0826 
0827             if (!data || strip.isNull())
0828             {
0829                 qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to allocate memory for TIFF image" << filePath;
0830                 TIFFClose(tif);
0831                 loadingFailed();
0832 
0833                 return false;
0834             }
0835 
0836             qint64 offset       = 0;
0837             qint64 pixelsRead   = 0;
0838 
0839             // this is inspired by TIFFReadRGBAStrip, tif_getimage.c
0840 
0841             char emsg[1024]     = "";
0842             uint32 rows_to_read = 0;
0843             uint checkpoint     = 0;
0844             TIFFRGBAImage img;
0845 
0846             // test whether libtiff can read format and initiate reading
0847 
0848             if (!TIFFRGBAImageOK(tif, emsg) || !TIFFRGBAImageBegin(&img, tif, 0, emsg))
0849             {
0850                 qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to set up RGBA reading of image, filename "
0851                                                  << TIFFFileName(tif) <<  " error message from Libtiff: " << emsg;
0852                 TIFFClose(tif);
0853                 loadingFailed();
0854 
0855                 return false;
0856             }
0857 
0858             // libtiff cannot handle all possible orientations, it give weird results.
0859             // We rotate ourselves. (Bug 274865)
0860 
0861             img.req_orientation = img.orientation;
0862 
0863             // read strips from image: read rows_per_strip, so always start at beginning of a strip
0864 
0865             for (uint row = 0 ; row < h ; row += rows_per_strip)
0866             {
0867                 if (observer && (row >= checkpoint))
0868                 {
0869                     checkpoint += granularity(observer, h, 0.8F);
0870 
0871                     if (!observer->continueQuery())
0872                     {
0873                         TIFFClose(tif);
0874                         loadingFailed();
0875 
0876                         return false;
0877                     }
0878 
0879                     observer->progressInfo(0.1F + (0.8F * (((float)row) / ((float)h))));
0880                 }
0881 
0882                 img.row_offset  = row;
0883                 img.col_offset  = 0;
0884 
0885                 if (row + rows_per_strip > img.height)
0886                 {
0887                     rows_to_read = img.height - row;
0888                 }
0889                 else
0890                 {
0891                     rows_to_read = rows_per_strip;
0892                 }
0893 
0894                 // Read data
0895 
0896                 if (TIFFRGBAImageGet(&img, reinterpret_cast<uint32*>(strip.data()), img.width, rows_to_read) == -1)
0897                 {
0898                     qCWarning(DIGIKAM_DIMG_LOG_TIFF) << "Failed to read image data";
0899                     TIFFClose(tif);
0900                     loadingFailed();
0901 
0902                     return false;
0903                 }
0904 
0905                 pixelsRead      = (qint64)rows_to_read * (qint64)img.width;
0906 
0907                 uchar* stripPtr = (uchar*)(strip.data());
0908                 uchar* dataPtr  = (uchar*)(data.data() + offset);
0909                 uchar* p        = nullptr;
0910 
0911                 // Reverse red and blue
0912 
0913                 for (int i = 0 ; i < pixelsRead ; ++i)
0914                 {
0915                     p        = dataPtr;
0916 
0917                     p[2]     = *stripPtr++;
0918                     p[1]     = *stripPtr++;
0919                     p[0]     = *stripPtr++;
0920                     p[3]     = *stripPtr++;
0921 
0922                     dataPtr += 4;
0923                 }
0924 
0925                 offset += pixelsRead * 4;
0926             }
0927 
0928             TIFFRGBAImageEnd(&img);
0929         }
0930     }
0931 
0932     // -------------------------------------------------------------------
0933 
0934     TIFFClose(tif);
0935 
0936     if (observer)
0937     {
0938         observer->progressInfo(1.0F);
0939     }
0940 
0941     imageWidth()  = w;
0942     imageHeight() = h;
0943     imageData()   = data.take();
0944     imageSetAttribute(QLatin1String("format"),             QLatin1String("TIFF"));
0945     imageSetAttribute(QLatin1String("originalColorModel"), colorModel);
0946     imageSetAttribute(QLatin1String("originalBitDepth"),   bits_per_sample);
0947     imageSetAttribute(QLatin1String("originalSize"),       QSize(w, h));
0948 
0949     return true;
0950 }
0951 
0952 } // namespace DigikamTIFFDImgPlugin