File indexing completed on 2025-01-05 03:57:11

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-12-09
0007  * Description : a tread-safe libraw Qt interface
0008  *
0009  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0011  * SPDX-FileCopyrightText: 2007-2008 by Guillaume Castagnino <casta at xwing dot info>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "drawdecoder_p.h"
0018 
0019 namespace Digikam
0020 {
0021 
0022 DRawDecoder::DRawDecoder()
0023     : m_cancel(false),
0024       d       (new Private(this))
0025 {
0026 }
0027 
0028 DRawDecoder::~DRawDecoder()
0029 {
0030     cancel();
0031     delete d;
0032 }
0033 
0034 void DRawDecoder::cancel()
0035 {
0036     m_cancel = true;
0037 }
0038 
0039 bool DRawDecoder::loadRawPreview(QImage& image, const QString& path)
0040 {
0041     // In first, try to extract the embedded JPEG preview. Very fast.
0042 
0043     bool ret = loadEmbeddedPreview(image, path);
0044 
0045     if (ret)
0046     {
0047         return true;
0048     }
0049 
0050     // In second, decode and half size of RAW picture. More slow.
0051 
0052     return (loadHalfPreview(image, path));
0053 }
0054 
0055 bool DRawDecoder::loadEmbeddedPreview(QImage& image, const QString& path)
0056 {
0057     QByteArray imgData;
0058 
0059     if (loadEmbeddedPreview(imgData, path))
0060     {
0061         qCDebug(DIGIKAM_RAWENGINE_LOG) << "Preview data size:" << imgData.size();
0062 
0063         if (image.loadFromData(imgData))
0064         {
0065             qCDebug(DIGIKAM_RAWENGINE_LOG) << "Using embedded RAW preview extraction";
0066 
0067             return true;
0068         }
0069     }
0070 
0071     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load embedded RAW preview";
0072 
0073     return false;
0074 }
0075 
0076 bool DRawDecoder::loadEmbeddedPreview(QByteArray& imgData, const QString& path)
0077 {
0078     QFileInfo fileInfo(path);
0079     QString   rawFilesExt = rawFiles();
0080     QString   ext         = fileInfo.suffix().toUpper();
0081 
0082     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0083     {
0084         return false;
0085     }
0086 
0087     qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: loadEmbeddedPreview from" << path;
0088 
0089     LibRaw* const raw = new LibRaw;
0090 
0091 #ifdef Q_OS_WIN
0092 
0093     int ret           = raw->open_file((const wchar_t*)path.utf16());
0094 
0095 #else
0096 
0097     int ret           = raw->open_file(path.toUtf8().constData());
0098 
0099 #endif
0100 
0101     if (ret != LIBRAW_SUCCESS)
0102     {
0103         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret);
0104         raw->recycle();
0105         delete raw;
0106 
0107         return false;
0108     }
0109 
0110     return (Private::loadEmbeddedPreview(imgData, raw));
0111 }
0112 
0113 bool DRawDecoder::loadEmbeddedPreview(QByteArray& imgData, const QBuffer& buffer)
0114 {
0115     QString rawFilesExt = rawFiles();
0116     LibRaw* const raw   = new LibRaw;
0117     QByteArray inData   = buffer.data();
0118     int ret             = raw->open_buffer((void*) inData.data(), (size_t) inData.size());
0119 
0120     if (ret != LIBRAW_SUCCESS)
0121     {
0122         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_buffer: " << libraw_strerror(ret);
0123         raw->recycle();
0124         delete raw;
0125 
0126         return false;
0127     }
0128 
0129     return (Private::loadEmbeddedPreview(imgData, raw));
0130 }
0131 
0132 bool DRawDecoder::loadHalfPreview(QImage& image, const QString& path, bool rotate)
0133 {
0134     QFileInfo fileInfo(path);
0135     QString   rawFilesExt = rawFiles();
0136     QString   ext         = fileInfo.suffix().toUpper();
0137 
0138     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0139     {
0140         return false;
0141     }
0142 
0143     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to use reduced RAW picture extraction";
0144 
0145     LibRaw* const raw                 = new LibRaw;
0146     raw->imgdata.params.use_auto_wb   = 1;         // Use automatic white balance.
0147     raw->imgdata.params.use_camera_wb = 1;         // Use camera white balance, if possible.
0148     raw->imgdata.params.half_size     = 1;         // Half-size color image (3x faster than -q).
0149 
0150 #ifdef Q_OS_WIN
0151 
0152     int ret                           = raw->open_file((const wchar_t*)path.utf16());
0153 
0154 #else
0155 
0156     int ret                           = raw->open_file(path.toUtf8().constData());
0157 
0158 #endif
0159 
0160     if (ret != LIBRAW_SUCCESS)
0161     {
0162         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret);
0163         raw->recycle();
0164         delete raw;
0165 
0166         return false;
0167     }
0168 
0169 
0170     if (!Private::loadHalfPreview(image, raw, rotate))
0171     {
0172         qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to get half preview from LibRaw!";
0173         return false;
0174     }
0175 
0176     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Using reduced RAW picture extraction for" << path;
0177 
0178     return true;
0179 }
0180 
0181 bool DRawDecoder::loadHalfPreview(QByteArray& imgData, const QString& path)
0182 {
0183     QFileInfo fileInfo(path);
0184     QString   rawFilesExt = rawFiles();
0185     QString   ext         = fileInfo.suffix().toUpper();
0186 
0187     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0188     {
0189         return false;
0190     }
0191 
0192     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to use reduced RAW picture extraction";
0193 
0194     LibRaw* const raw = new LibRaw;
0195 
0196 #ifdef Q_OS_WIN
0197 
0198     int ret           = raw->open_file((const wchar_t*)path.utf16());
0199 
0200 #else
0201 
0202     int ret           = raw->open_file(path.toUtf8().constData());
0203 
0204 #endif
0205 
0206     if (ret != LIBRAW_SUCCESS)
0207     {
0208         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret);
0209         raw->recycle();
0210         delete raw;
0211 
0212         return false;
0213     }
0214 
0215     QImage image;
0216 
0217     if (!Private::loadHalfPreview(image, raw))
0218     {
0219         qCDebug(DIGIKAM_RAWENGINE_LOG) << "DRawDecoder: failed to get half preview: " << libraw_strerror(ret);
0220 
0221         return false;
0222     }
0223 
0224     QBuffer buffer(&imgData);
0225     buffer.open(QIODevice::WriteOnly);
0226     image.save(&buffer, "JPEG");
0227 
0228     return true;
0229 }
0230 
0231 bool DRawDecoder::loadHalfPreview(QByteArray& imgData, const QBuffer& inBuffer)
0232 {
0233     QString rawFilesExt = rawFiles();
0234     LibRaw* const raw   = new LibRaw;
0235     QByteArray inData   = inBuffer.data();
0236     int ret             = raw->open_buffer((void*) inData.data(), (size_t) inData.size());
0237 
0238     if (ret != LIBRAW_SUCCESS)
0239     {
0240         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret);
0241         raw->recycle();
0242         delete raw;
0243 
0244         return false;
0245     }
0246 
0247     QImage image;
0248 
0249     if (!Private::loadHalfPreview(image, raw))
0250     {
0251         qCDebug(DIGIKAM_RAWENGINE_LOG) << "DRawDecoder: failed to get half preview: " << libraw_strerror(ret);
0252         return false;
0253     }
0254 
0255     QBuffer buffer(&imgData);
0256     buffer.open(QIODevice::WriteOnly);
0257     image.save(&buffer, "JPG");
0258     buffer.close();
0259 
0260     return true;
0261 }
0262 
0263 bool DRawDecoder::loadFullImage(QImage& image,
0264                                 const QString& path,
0265                                 const DRawDecoderSettings& settings)
0266 {
0267     QFileInfo fileInfo(path);
0268     QString   rawFilesExt = rawFiles();
0269     QString   ext         = fileInfo.suffix().toUpper();
0270 
0271     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0272     {
0273         return false;
0274     }
0275 
0276     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to load full RAW picture...";
0277 
0278     QPointer<DRawDecoder> decoder(new DRawDecoder);
0279     QByteArray imgData;
0280     int width, height, rgbmax;
0281     DRawDecoderSettings prm = settings;
0282     prm.sixteenBitsImage    = false;
0283     bool ret                = decoder->decodeRAWImage(path, prm, imgData, width, height, rgbmax);
0284 
0285     if (!ret)
0286     {
0287         qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load full RAW picture";
0288 
0289         return false;
0290     }
0291 
0292     uchar* sptr             = (uchar*)imgData.data();
0293     uchar tmp8[2];
0294 
0295     // Set RGB color components.
0296 
0297     for (int i = 0 ; i < (width * height) ; ++i)
0298     {
0299         // Swap Red and Blue
0300 
0301         tmp8[0] = sptr[2];
0302         tmp8[1] = sptr[0];
0303         sptr[0] = tmp8[0];
0304         sptr[2] = tmp8[1];
0305         sptr   += 3;
0306     }
0307 
0308     image      = QImage(width, height, QImage::Format_ARGB32);
0309     uint* dptr = reinterpret_cast<uint*>(image.bits());
0310     sptr       = (uchar*)imgData.data();
0311 
0312     for (int i = 0 ; i < (width * height) ; ++i)
0313     {
0314         *dptr++ = qRgba(sptr[2], sptr[1], sptr[0], 0xFF);
0315         sptr   += 3;
0316     }
0317 
0318     qCDebug(DIGIKAM_RAWENGINE_LOG) << "Load full RAW picture done";
0319 
0320     return true;
0321 }
0322 
0323 bool DRawDecoder::rawFileIdentify(DRawInfo& identify, const QString& path)
0324 {
0325     QFileInfo fileInfo(path);
0326     QString rawFilesExt  = rawFiles();
0327     QString ext          = fileInfo.suffix().toUpper();
0328     identify.isDecodable = false;
0329 
0330     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0331     {
0332         return false;
0333     }
0334 
0335     LibRaw* const raw = new LibRaw;
0336 
0337 #ifdef Q_OS_WIN
0338 
0339     int ret           = raw->open_file((const wchar_t*)path.utf16());
0340 
0341 #else
0342 
0343     int ret           = raw->open_file(path.toUtf8().constData());
0344 
0345 #endif
0346 
0347     if (ret != LIBRAW_SUCCESS)
0348     {
0349         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret);
0350         raw->recycle();
0351         delete raw;
0352 
0353         return false;
0354     }
0355 
0356     ret               = raw->adjust_sizes_info_only();
0357 
0358     if (ret != LIBRAW_SUCCESS)
0359     {
0360         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run adjust_sizes_info_only: " << libraw_strerror(ret);
0361         raw->recycle();
0362         delete raw;
0363 
0364         return false;
0365     }
0366 
0367     Private::fillIndentifyInfo(raw, identify);
0368     raw->recycle();
0369     delete raw;
0370 
0371     return true;
0372 }
0373 
0374 // ----------------------------------------------------------------------------------
0375 
0376 bool DRawDecoder::extractRAWData(const QString& filePath,
0377                                  QByteArray& rawData,
0378                                  DRawInfo& identify,
0379                                  unsigned int shotSelect)
0380 {
0381     QFileInfo fileInfo(filePath);
0382     QString rawFilesExt  = rawFiles();
0383     QString ext          = fileInfo.suffix().toUpper();
0384     identify.isDecodable = false;
0385 
0386     if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext))
0387     {
0388         return false;
0389     }
0390 
0391     if (m_cancel)
0392     {
0393         return false;
0394     }
0395 
0396     d->setProgress(0.1);
0397 
0398     LibRaw* const raw = new LibRaw;
0399 
0400     // Set progress call back function.
0401 
0402     raw->set_progress_handler(s_progressCallbackForLibRaw, d);
0403     raw->set_exifparser_handler(s_exifParserCallbackForLibRaw, d);
0404 
0405 #ifdef Q_OS_WIN
0406 
0407     int ret           = raw->open_file((const wchar_t*)filePath.utf16());
0408 
0409 #else
0410 
0411     int ret           = raw->open_file(filePath.toUtf8().constData());
0412 
0413 #endif
0414 
0415     if (ret != LIBRAW_SUCCESS)
0416     {
0417         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret);
0418         raw->recycle();
0419         delete raw;
0420 
0421         return false;
0422     }
0423 
0424     if (m_cancel)
0425     {
0426         raw->recycle();
0427         delete raw;
0428 
0429         return false;
0430     }
0431 
0432     d->setProgress(0.3);
0433 
0434     raw->imgdata.params.output_bps     = 16;
0435     raw->imgdata.rawparams.shot_select = shotSelect;
0436     raw->imgdata.params.user_flip      = -1;
0437     ret                                = raw->unpack();
0438 
0439     if (ret != LIBRAW_SUCCESS)
0440     {
0441         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret);
0442         raw->recycle();
0443         delete raw;
0444 
0445         return false;
0446     }
0447 
0448     if (m_cancel)
0449     {
0450         raw->recycle();
0451         delete raw;
0452 
0453         return false;
0454     }
0455 
0456     d->setProgress(0.4);
0457 
0458     ret = raw->raw2image();
0459 
0460     if (ret != LIBRAW_SUCCESS)
0461     {
0462         qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run raw2image: " << libraw_strerror(ret);
0463         raw->recycle();
0464         delete raw;
0465 
0466         return false;
0467     }
0468 
0469     if (m_cancel)
0470     {
0471         raw->recycle();
0472         delete raw;
0473 
0474         return false;
0475     }
0476 
0477     d->setProgress(0.6);
0478 
0479     Private::fillIndentifyInfo(raw, identify);
0480 
0481     if (m_cancel)
0482     {
0483         raw->recycle();
0484         delete raw;
0485 
0486         return false;
0487     }
0488 
0489     d->setProgress(0.8);
0490 
0491     rawData = QByteArray();
0492 
0493     if (raw->imgdata.idata.filters == 0)
0494     {
0495         rawData.resize((int)((int)raw->imgdata.sizes.iwidth * (int)raw->imgdata.sizes.iheight * (int)raw->imgdata.idata.colors * sizeof(unsigned short)));
0496 
0497         unsigned short* output = reinterpret_cast<unsigned short*>(rawData.data());
0498 
0499         for (unsigned int row = 0 ; row < raw->imgdata.sizes.iheight ; ++row)
0500         {
0501             for (unsigned int col = 0 ; col < raw->imgdata.sizes.iwidth ; ++col)
0502             {
0503                 for (int color = 0 ; color < raw->imgdata.idata.colors ; ++color)
0504                 {
0505                     *output = raw->imgdata.image[raw->imgdata.sizes.iwidth*row + col][color];
0506                     output++;
0507                 }
0508             }
0509         }
0510     }
0511     else
0512     {
0513         rawData.resize((int)((int)raw->imgdata.sizes.iwidth * (int)raw->imgdata.sizes.iheight * sizeof(unsigned short)));
0514 
0515         unsigned short* output = reinterpret_cast<unsigned short*>(rawData.data());
0516 
0517         for (unsigned int row = 0 ; row < raw->imgdata.sizes.iheight ; ++row)
0518         {
0519             for (unsigned int col = 0 ; col < raw->imgdata.sizes.iwidth ; ++col)
0520             {
0521                 *output = raw->imgdata.image[raw->imgdata.sizes.iwidth*row + col][raw->COLOR(row, col)];
0522                 output++;
0523             }
0524         }
0525     }
0526 
0527     raw->recycle();
0528     delete raw;
0529     d->setProgress(1.0);
0530 
0531     return true;
0532 }
0533 
0534 bool DRawDecoder::decodeHalfRAWImage(const QString& filePath,
0535                                      const DRawDecoderSettings& DRawDecoderSettings,
0536                                      QByteArray& imageData,
0537                                      int& width,
0538                                      int& height,
0539                                      int& rgbmax)
0540 {
0541     m_decoderSettings                    = DRawDecoderSettings;
0542     m_decoderSettings.halfSizeColorImage = true;
0543 
0544     return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax));
0545 }
0546 
0547 bool DRawDecoder::decodeRAWImage(const QString& filePath,
0548                                  const DRawDecoderSettings& DRawDecoderSettings,
0549                                  QByteArray& imageData,
0550                                  int& width,
0551                                  int& height,
0552                                  int& rgbmax)
0553 {
0554     m_decoderSettings = DRawDecoderSettings;
0555 
0556     return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax));
0557 }
0558 
0559 bool DRawDecoder::checkToCancelWaitingData()
0560 {
0561     return m_cancel;
0562 }
0563 
0564 void DRawDecoder::setWaitingDataProgress(double)
0565 {
0566 }
0567 
0568 QString DRawDecoder::rawFiles()
0569 {
0570     return s_rawFileExtensions();
0571 }
0572 
0573 QStringList DRawDecoder::rawFilesList()
0574 {
0575     QString string = rawFiles();
0576 
0577     return string.remove(QLatin1String("*.")).split(QLatin1Char(' '));
0578 }
0579 
0580 int DRawDecoder::rawFilesVersion()
0581 {
0582     return s_rawFileExtensionsVersion();
0583 }
0584 
0585 QStringList DRawDecoder::supportedCamera()
0586 {
0587     QStringList camera;
0588     const char** const list = LibRaw::cameraList();
0589 
0590     for (int i = 0 ; i < LibRaw::cameraCount() ; ++i)
0591     {
0592         camera.append(QString::fromUtf8(list[i]));
0593     }
0594 
0595     return camera;
0596 }
0597 
0598 QString DRawDecoder::librawVersion()
0599 {
0600     QString simplified = QString::fromLatin1(LIBRAW_VERSION_STR)
0601                          .section(QLatin1Char('-'), 0, -2);
0602 
0603     if (simplified.isEmpty())
0604     {
0605         simplified = QString::fromLatin1(LIBRAW_VERSION_STR);
0606     }
0607 
0608     return simplified;
0609 }
0610 
0611 int DRawDecoder::librawUseGomp()
0612 {
0613 
0614 #ifdef LIBRAW_FORCE_OPENMP
0615 
0616     return true;
0617 
0618 #else
0619 
0620 #   ifdef LIBRAW_USE_OPENMP
0621 
0622     return true;
0623 
0624 #   else
0625 
0626     return false;
0627 
0628 #   endif
0629 
0630 #endif
0631 
0632 }
0633 
0634 bool DRawDecoder::isRawFile(const QUrl& url)
0635 {
0636     QString   rawFilesExt = rawFiles();
0637     QFileInfo fileInfo(url.toLocalFile());
0638 
0639     return (rawFilesExt.toUpper().contains(fileInfo.suffix().toUpper()));
0640 }
0641 
0642 } // namespace Digikam
0643 
0644 #include "moc_drawdecoder.cpp"