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"