diff --git a/core/libs/rawengine/drawdecoder.cpp b/core/libs/rawengine/drawdecoder.cpp index fd8296659f..754302b8f5 100644 --- a/core/libs/rawengine/drawdecoder.cpp +++ b/core/libs/rawengine/drawdecoder.cpp @@ -1,547 +1,581 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-12-09 * Description : a tread-safe libraw C++ program interface * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * Copyright (C) 2007-2008 by Guillaume Castagnino * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "drawdecoder.h" #include "drawdecoder_p.h" // Qt includes #include #include #include #include // LibRaw includes #include "libraw_version.h" #include "libraw_config.h" // Local includes #include "digikam_debug.h" #include "drawfiles.h" namespace Digikam { DRawDecoder::DRawDecoder() : d(new Private(this)) { m_cancel = false; } DRawDecoder::~DRawDecoder() { cancel(); delete d; } void DRawDecoder::cancel() { m_cancel = true; } bool DRawDecoder::loadRawPreview(QImage& image, const QString& path) { // In first, try to extract the embedded JPEG preview. Very fast. + bool ret = loadEmbeddedPreview(image, path); if (ret) + { return true; + } // In second, decode and half size of RAW picture. More slow. + return (loadHalfPreview(image, path)); } bool DRawDecoder::loadEmbeddedPreview(QImage& image, const QString& path) { QByteArray imgData; if ( loadEmbeddedPreview(imgData, path) ) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Preview data size:" << imgData.size(); if (image.loadFromData( imgData )) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Using embedded RAW preview extraction"; return true; } } qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load embedded RAW preview"; + return false; } bool DRawDecoder::loadEmbeddedPreview(QByteArray& imgData, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) + { return false; + } LibRaw* const raw = new LibRaw; int ret = raw->open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } return (Private::loadEmbeddedPreview(imgData, raw)); } bool DRawDecoder::loadEmbeddedPreview(QByteArray& imgData, const QBuffer& buffer) { QString rawFilesExt = rawFiles(); LibRaw* const raw = new LibRaw; QByteArray inData = buffer.data(); int ret = raw->open_buffer((void*) inData.data(), (size_t) inData.size()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_buffer: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } return (Private::loadEmbeddedPreview(imgData, raw)); } bool DRawDecoder::loadHalfPreview(QImage& image, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) + { return false; + } qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to use reduced RAW picture extraction"; LibRaw* const raw = new LibRaw; raw->imgdata.params.use_auto_wb = 1; // Use automatic white balance. raw->imgdata.params.use_camera_wb = 1; // Use camera white balance, if possible. raw->imgdata.params.half_size = 1; // Half-size color image (3x faster than -q). int ret = raw->open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if(!Private::loadHalfPreview(image, raw)) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to get half preview from LibRaw!"; return false; } qCDebug(DIGIKAM_RAWENGINE_LOG) << "Using reduced RAW picture extraction"; return true; } bool DRawDecoder::loadHalfPreview(QByteArray& imgData, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) return false; qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to use reduced RAW picture extraction"; LibRaw* const raw = new LibRaw; int ret = raw->open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } QImage image; if (!Private::loadHalfPreview(image, raw)) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "DRawDecoder: failed to get half preview: " << libraw_strerror(ret); return false; } QBuffer buffer(&imgData); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPEG"); return true; } bool DRawDecoder::loadHalfPreview(QByteArray& imgData, const QBuffer& inBuffer) { QString rawFilesExt = rawFiles(); LibRaw* const raw = new LibRaw; QByteArray inData = inBuffer.data(); int ret = raw->open_buffer((void*) inData.data(), (size_t) inData.size()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } QImage image; if (!Private::loadHalfPreview(image, raw)) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "DRawDecoder: failed to get half preview: " << libraw_strerror(ret); return false; } QBuffer buffer(&imgData); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "JPG"); buffer.close(); return true; } -bool DRawDecoder::loadFullImage(QImage& image, const QString& path, const DRawDecoderSettings& settings) +bool DRawDecoder::loadFullImage(QImage& image, + const QString& path, + const DRawDecoderSettings& settings) { QFileInfo fileInfo(path); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) + { return false; + } qCDebug(DIGIKAM_RAWENGINE_LOG) << "Try to load full RAW picture..."; DRawDecoderSettings prm = settings; prm.sixteenBitsImage = false; QByteArray imgData; int width, height, rgbmax; DRawDecoder decoder; bool ret = decoder.decodeRAWImage(path, prm, imgData, width, height, rgbmax); if (!ret) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failled to load full RAW picture"; return false; } uchar* sptr = (uchar*)imgData.data(); uchar tmp8[2]; // Set RGB color components. for (int i = 0 ; i < width * height ; ++i) { // Swap Red and Blue tmp8[0] = sptr[2]; tmp8[1] = sptr[0]; sptr[0] = tmp8[0]; sptr[2] = tmp8[1]; sptr += 3; } image = QImage(width, height, QImage::Format_ARGB32); uint* dptr = reinterpret_cast(image.bits()); sptr = (uchar*)imgData.data(); for (int i = 0 ; i < width * height ; ++i) { *dptr++ = qRgba(sptr[2], sptr[1], sptr[0], 0xFF); sptr += 3; } qCDebug(DIGIKAM_RAWENGINE_LOG) << "Load full RAW picture done"; return true; } bool DRawDecoder::rawFileIdentify(DRawInfo& identify, const QString& path) { QFileInfo fileInfo(path); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); identify.isDecodable = false; if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) + { return false; + } LibRaw* const raw = new LibRaw; int ret = raw->open_file((const char*)(QFile::encodeName(path)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } ret = raw->adjust_sizes_info_only(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run adjust_sizes_info_only: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } Private::fillIndentifyInfo(raw, identify); raw->recycle(); delete raw; + return true; } // ---------------------------------------------------------------------------------- -bool DRawDecoder::extractRAWData(const QString& filePath, QByteArray& rawData, DRawInfo& identify, unsigned int shotSelect) +bool DRawDecoder::extractRAWData(const QString& filePath, + QByteArray& rawData, + DRawInfo& identify, + unsigned int shotSelect) { QFileInfo fileInfo(filePath); QString rawFilesExt = rawFiles(); QString ext = fileInfo.suffix().toUpper(); identify.isDecodable = false; if (!fileInfo.exists() || ext.isEmpty() || !rawFilesExt.toUpper().contains(ext)) + { return false; + } if (m_cancel) + { return false; + } d->setProgress(0.1); LibRaw* const raw = new LibRaw; // Set progress call back function. raw->set_progress_handler(callbackForLibRaw, d); int ret = raw->open_file((const char*)(QFile::encodeName(filePath)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_cancel) { raw->recycle(); delete raw; return false; } d->setProgress(0.3); raw->imgdata.params.output_bps = 16; raw->imgdata.params.shot_select = shotSelect; ret = raw->unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_cancel) { raw->recycle(); delete raw; return false; } d->setProgress(0.4); ret = raw->raw2image(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run raw2image: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_cancel) { raw->recycle(); delete raw; return false; } d->setProgress(0.6); Private::fillIndentifyInfo(raw, identify); if (m_cancel) { raw->recycle(); delete raw; return false; } d->setProgress(0.8); rawData = QByteArray(); if (raw->imgdata.idata.filters == 0) { - rawData.resize((int)(raw->imgdata.sizes.iwidth * raw->imgdata.sizes.iheight * raw->imgdata.idata.colors * sizeof(unsigned short))); + rawData.resize((int)((int)raw->imgdata.sizes.iwidth * (int)raw->imgdata.sizes.iheight * (int)raw->imgdata.idata.colors * sizeof(unsigned short))); unsigned short* output = reinterpret_cast(rawData.data()); for (unsigned int row = 0 ; row < raw->imgdata.sizes.iheight ; ++row) { for (unsigned int col = 0 ; col < raw->imgdata.sizes.iwidth ; ++col) { for (int color = 0 ; color < raw->imgdata.idata.colors ; ++color) { *output = raw->imgdata.image[raw->imgdata.sizes.iwidth*row + col][color]; output++; } } } } else { - rawData.resize((int)(raw->imgdata.sizes.iwidth * raw->imgdata.sizes.iheight * sizeof(unsigned short))); + rawData.resize((int)((int)raw->imgdata.sizes.iwidth * (int)raw->imgdata.sizes.iheight * sizeof(unsigned short))); unsigned short* output = reinterpret_cast(rawData.data()); for (uint row = 0 ; row < raw->imgdata.sizes.iheight ; ++row) { for (uint col = 0 ; col < raw->imgdata.sizes.iwidth ; ++col) { *output = raw->imgdata.image[raw->imgdata.sizes.iwidth*row + col][raw->COLOR(row, col)]; output++; } } } raw->recycle(); delete raw; d->setProgress(1.0); return true; } -bool DRawDecoder::decodeHalfRAWImage(const QString& filePath, const DRawDecoderSettings& DRawDecoderSettings, - QByteArray& imageData, int& width, int& height, int& rgbmax) +bool DRawDecoder::decodeHalfRAWImage(const QString& filePath, + const DRawDecoderSettings& DRawDecoderSettings, + QByteArray& imageData, + int& width, + int& height, + int& rgbmax) { m_decoderSettings = DRawDecoderSettings; m_decoderSettings.halfSizeColorImage = true; + return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax)); } -bool DRawDecoder::decodeRAWImage(const QString& filePath, const DRawDecoderSettings& DRawDecoderSettings, - QByteArray& imageData, int& width, int& height, int& rgbmax) +bool DRawDecoder::decodeRAWImage(const QString& filePath, + const DRawDecoderSettings& DRawDecoderSettings, + QByteArray& imageData, + int& width, + int& height, + int& rgbmax) { m_decoderSettings = DRawDecoderSettings; + return (d->loadFromLibraw(filePath, imageData, width, height, rgbmax)); } bool DRawDecoder::checkToCancelWaitingData() { return m_cancel; } void DRawDecoder::setWaitingDataProgress(double) { } QString DRawDecoder::rawFiles() { return s_rawFileExtensions(); } QStringList DRawDecoder::rawFilesList() { QString string = rawFiles(); + return string.remove(QLatin1String("*.")).split(QLatin1Char(' ')); } int DRawDecoder::rawFilesVersion() { return s_rawFileExtensionsVersion(); } QStringList DRawDecoder::supportedCamera() { QStringList camera; const char** const list = LibRaw::cameraList(); for (int i = 0 ; i < LibRaw::cameraCount() ; ++i) camera.append(QString::fromUtf8(list[i])); return camera; } QString DRawDecoder::librawVersion() { return QString::fromLatin1(LIBRAW_VERSION_STR).remove(QLatin1String("-Release")); } int DRawDecoder::librawUseGomp() { #ifdef LIBRAW_USE_OPENMP return true; #else return false; #endif } bool DRawDecoder::isRawFile(const QUrl& url) { QString rawFilesExt = rawFiles(); QFileInfo fileInfo(url.toLocalFile()); return (rawFilesExt.toUpper().contains(fileInfo.suffix().toUpper())); } } // namespace Digikam diff --git a/core/libs/rawengine/drawdecoder.h b/core/libs/rawengine/drawdecoder.h index 270a2a134a..e26c429d68 100644 --- a/core/libs/rawengine/drawdecoder.h +++ b/core/libs/rawengine/drawdecoder.h @@ -1,244 +1,281 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-12-09 * Description : a tread-safe libraw C++ program interface * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * Copyright (C) 2007-2008 by Guillaume Castagnino * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DRAW_DECODER_H #define DIGIKAM_DRAW_DECODER_H // C++ includes #include // Qt includes #include #include #include #include // Local includes #include "drawdecodersettings.h" #include "drawinfo.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT DRawDecoder : public QObject { Q_OBJECT public: - /** Standard constructor. + /** + * Standard constructor. */ explicit DRawDecoder(); - /** Standard destructor. + /** + * Standard destructor. */ virtual ~DRawDecoder(); public: - /** Get the preview of RAW picture as a QImage. - It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). + /** + * Get the preview of RAW picture as a QImage. + * It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QImage& image, const QString& path); - /** Get the preview of RAW picture as a QByteArray holding JPEG data. - It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). + /** + * Get the preview of RAW picture as a QByteArray holding JPEG data. + * It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QByteArray& imgData, const QString& path); - /** Get the preview of RAW picture passed in QBuffer as a QByteArray holding JPEG data. - It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). + /** + * Get the preview of RAW picture passed in QBuffer as a QByteArray holding JPEG data. + * It tries loadEmbeddedPreview() first and if it fails, calls loadHalfPreview(). */ static bool loadRawPreview(QByteArray& imgData, const QBuffer& inBuffer); - /** Get the embedded JPEG preview image from RAW picture as a QByteArray which will include Exif Data. - This is fast and non cancelable. This method does not require a class instance to run. + /** + * Get the embedded JPEG preview image from RAW picture as a QByteArray which will include Exif Data. + * This is fast and non cancelable. This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QByteArray& imgData, const QString& path); - /** Get the embedded JPEG preview image from RAW picture as a QImage. This is fast and non cancelable - This method does not require a class instance to run. + /** + * Get the embedded JPEG preview image from RAW picture as a QImage. This is fast and non cancelable + * This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QImage& image, const QString& path); - /** Get the embedded JPEG preview image from RAW image passed in QBuffer as a QByteArray which will include Exif Data. - This is fast and non cancelable. This method does not require a class instance to run. + /** + * Get the embedded JPEG preview image from RAW image passed in QBuffer as a QByteArray which will include Exif Data. + * This is fast and non cancelable. This method does not require a class instance to run. */ static bool loadEmbeddedPreview(QByteArray& imgData, const QBuffer& inBuffer); - /** Get the half decoded RAW picture. This is slower than loadEmbeddedPreview() method - and non cancelable. This method does not require a class instance to run. + /** + * Get the half decoded RAW picture. This is slower than loadEmbeddedPreview() method + * and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QImage& image, const QString& path); - /** Get the half decoded RAW picture as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() - method and non cancelable. This method does not require a class instance to run. + /** + * Get the half decoded RAW picture as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() + * method and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QByteArray& imgData, const QString& path); - /** Get the half decoded RAW picture passed in QBuffer as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() - method and non cancelable. This method does not require a class instance to run. + /** + * Get the half decoded RAW picture passed in QBuffer as JPEG data in QByteArray. This is slower than loadEmbeddedPreview() + * method and non cancelable. This method does not require a class instance to run. */ static bool loadHalfPreview(QByteArray& imgData, const QBuffer& inBuffer); - /** Get the full decoded RAW picture. This is a more slower than loadHalfPreview() method - and non cancelable. This method does not require a class instance to run. + /** + * Get the full decoded RAW picture. This is a more slower than loadHalfPreview() method + * and non cancelable. This method does not require a class instance to run. */ - static bool loadFullImage(QImage& image, const QString& path, const DRawDecoderSettings& settings = DRawDecoderSettings()); - - /** Get the camera settings which have taken RAW file. Look into rawinfo.h - for more details. This is a fast and non cancelable method which do not require - a class instance to run. + static bool loadFullImage(QImage& image, + const QString& path, + const DRawDecoderSettings& settings = DRawDecoderSettings()); + + /** + * Get the camera settings which have taken RAW file. Look into rawinfo.h + * for more details. This is a fast and non cancelable method which do not require + * a class instance to run. */ static bool rawFileIdentify(DRawInfo& identify, const QString& path); - /** Return the string of all RAW file type mime supported. + /** + * Return the string of all RAW file type mime supported. */ static QString rawFiles(); - /** Return the list of all RAW file type mime supported, - as a QStringList, without wildcard and suffix dot. + /** + * Return the list of all RAW file type mime supported, + * as a QStringList, without wildcard and suffix dot. */ static QStringList rawFilesList(); - /** Returns a version number for the list of supported RAW file types. - This version is incremented if the list of supported formats has changed - between library releases. + /** + * Returns a version number for the list of supported RAW file types. + * This version is incremented if the list of supported formats has changed + * between library releases. */ static int rawFilesVersion(); - /** Provide a list of supported RAW Camera name. + /** + * Provide a list of supported RAW Camera name. */ static QStringList supportedCamera(); - /** Return LibRaw version string. + /** + * Return LibRaw version string. */ static QString librawVersion(); - /** Return true or false if LibRaw use parallel demosaicing or not (libgomp support). - * Return -1 if undefined. + /** + * Return true or false if LibRaw use parallel demosaicing or not (libgomp support). + * Return -1 if undefined. */ static int librawUseGomp(); static bool isRawFile(const QUrl& url); public: /** Extract Raw image data undemosaiced and without post processing from 'filePath' picture file. This is a cancelable method which require a class instance to run because RAW pictures loading can take a while. This method return: - A byte array container 'rawData' with raw data. - All info about Raw image into 'identify' container. - 'false' is returned if loading failed, else 'true'. */ - bool extractRAWData(const QString& filePath, QByteArray& rawData, DRawInfo& identify, unsigned int shotSelect=0); + bool extractRAWData(const QString& filePath, + QByteArray& rawData, + DRawInfo& identify, + unsigned int shotSelect=0); /** Extract a small size of decode RAW data from 'filePath' picture file using 'DRawDecoderSettings' settings. This is a cancelable method which require a class instance to run because RAW pictures decoding can take a while. This method return: - A byte array container 'imageData' with picture data. Pixels order is RGB. Color depth can be 8 or 16. In 8 bits you can access to color component using (uchar*), in 16 bits using (ushort*). - Size size of image in number of pixels ('width' and 'height'). - The max average of RGB components from decoded picture. - 'false' is returned if decoding failed, else 'true'. */ - bool decodeHalfRAWImage(const QString& filePath, const DRawDecoderSettings& DRawDecoderSettings, - QByteArray& imageData, int& width, int& height, int& rgbmax); + bool decodeHalfRAWImage(const QString& filePath, + const DRawDecoderSettings& DRawDecoderSettings, + QByteArray& imageData, + int& width, + int& height, + int& rgbmax); /** Extract a full size of RAW data from 'filePath' picture file using 'DRawDecoderSettings' settings. This is a cancelable method which require a class instance to run because RAW pictures decoding can take a while. This method return: - A byte array container 'imageData' with picture data. Pixels order is RGB. Color depth can be 8 or 16. In 8 bits you can access to color component using (uchar*), in 16 bits using (ushort*). - Size size of image in number of pixels ('width' and 'height'). - The max average of RGB components from decoded picture. - 'false' is returned if decoding failed, else 'true'. */ - bool decodeRAWImage(const QString& filePath, const DRawDecoderSettings& DRawDecoderSettings, - QByteArray& imageData, int& width, int& height, int& rgbmax); - - /** To cancel 'decodeHalfRAWImage' and 'decodeRAWImage' methods running - in a separate thread. + bool decodeRAWImage(const QString& filePath, + const DRawDecoderSettings& DRawDecoderSettings, + QByteArray& imageData, + int& width, + int& height, + int& rgbmax); + + /** + * To cancel 'decodeHalfRAWImage' and 'decodeRAWImage' methods running + * in a separate thread. */ void cancel(); protected: - /** Used internally to cancel RAW decoding operation. Normally, you don't need to use it - directly, excepted if you derivated this class. Usual way is to use cancel() method + /** + * Used internally to cancel RAW decoding operation. Normally, you don't need to use it + * directly, excepted if you derivated this class. Usual way is to use cancel() method */ bool m_cancel; - /** The settings container used to perform RAW pictures decoding. See 'rawdecodingsetting.h' - for details. + /** + * The settings container used to perform RAW pictures decoding. See 'rawdecodingsetting.h' + * for details. */ DRawDecoderSettings m_decoderSettings; protected: - /** Re-implement this method to control the cancelisation of loop which wait data - from RAW decoding process with your proper environment. - By default, this method check if m_cancel is true. + /** + * Re-implement this method to control the cancelisation of loop which wait data + * from RAW decoding process with your proper environment. + * By default, this method check if m_cancel is true. */ virtual bool checkToCancelWaitingData(); - /** Re-implement this method to control the pseudo progress value during RAW decoding (when dcraw run with an - internal loop without feedback) with your proper environment. By default, this method does nothing. - Progress value average for this stage is 0%-n%, with 'n' == 40% max (see setWaitingDataProgress() method). + /** + * Re-implement this method to control the pseudo progress value during RAW decoding (when dcraw run with an + * internal loop without feedback) with your proper environment. By default, this method does nothing. + * Progress value average for this stage is 0%-n%, with 'n' == 40% max (see setWaitingDataProgress() method). */ virtual void setWaitingDataProgress(double value); public: // Declared public to be called externally by callbackForLibRaw() static method. class Private; private: Private* const d; friend class Private; }; } // namespace Digikam #endif // DIGIKAM_DRAW_DECODER_H diff --git a/core/libs/rawengine/drawdecoder_p.cpp b/core/libs/rawengine/drawdecoder_p.cpp index b43171b510..3625e991bb 100644 --- a/core/libs/rawengine/drawdecoder_p.cpp +++ b/core/libs/rawengine/drawdecoder_p.cpp @@ -1,674 +1,681 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-10-09 * Description : internal private container for DRawDecoder * * Copyright (C) 2008-2019 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "drawdecoder_p.h" // Qt includes #include #include // Local includes #include "digikam_debug.h" namespace Digikam { int callbackForLibRaw(void* data, enum LibRaw_progress p, int iteration, int expected) { if (data) { DRawDecoder::Private* const d = static_cast(data); if (d) { return d->progressCallback(p, iteration, expected); } } return 0; } // -------------------------------------------------------------------------------------------------- DRawDecoder::Private::Private(DRawDecoder* const p) { m_progress = 0.0; m_parent = p; } DRawDecoder::Private::~Private() { } void DRawDecoder::Private::createPPMHeader(QByteArray& imgData, libraw_processed_image_t* const img) { QString header = QString::fromUtf8("P%1\n%2 %3\n%4\n").arg(img->colors == 3 ? QLatin1String("6") : QLatin1String("5")) .arg(img->width) .arg(img->height) .arg((1 << img->bits)-1); imgData.append(header.toLatin1()); imgData.append(QByteArray((const char*)img->data, (int)img->data_size)); } int DRawDecoder::Private::progressCallback(enum LibRaw_progress p, int iteration, int expected) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw progress: " << libraw_strprogress(p) << " pass " << iteration << " of " << expected; // post a little change in progress indicator to show raw processor activity. setProgress(progressValue()+0.01); // Clean processing termination by user... if (m_parent->checkToCancelWaitingData()) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw process terminaison invoked..."; m_parent->m_cancel = true; m_progress = 0.0; return 1; } // Return 0 to continue processing... return 0; } void DRawDecoder::Private::setProgress(double value) { m_progress = value; m_parent->setWaitingDataProgress(m_progress); } double DRawDecoder::Private::progressValue() const { return m_progress; } void DRawDecoder::Private::fillIndentifyInfo(LibRaw* const raw, DRawInfo& identify) { identify.dateTime.setTime_t(raw->imgdata.other.timestamp); identify.make = QString::fromUtf8(raw->imgdata.idata.make); identify.model = QString::fromUtf8(raw->imgdata.idata.model); identify.owner = QString::fromUtf8(raw->imgdata.other.artist); identify.DNGVersion = QString::number(raw->imgdata.idata.dng_version); identify.sensitivity = raw->imgdata.other.iso_speed; identify.exposureTime = raw->imgdata.other.shutter; identify.aperture = raw->imgdata.other.aperture; identify.focalLength = raw->imgdata.other.focal_len; identify.imageSize = QSize(raw->imgdata.sizes.width, raw->imgdata.sizes.height); identify.fullSize = QSize(raw->imgdata.sizes.raw_width, raw->imgdata.sizes.raw_height); identify.outputSize = QSize(raw->imgdata.sizes.iwidth, raw->imgdata.sizes.iheight); identify.thumbSize = QSize(raw->imgdata.thumbnail.twidth, raw->imgdata.thumbnail.theight); identify.topMargin = raw->imgdata.sizes.top_margin; identify.leftMargin = raw->imgdata.sizes.left_margin; identify.hasIccProfile = raw->imgdata.color.profile ? true : false; identify.isDecodable = true; identify.pixelAspectRatio = raw->imgdata.sizes.pixel_aspect; identify.rawColors = raw->imgdata.idata.colors; identify.rawImages = raw->imgdata.idata.raw_count; identify.blackPoint = raw->imgdata.color.black; for (int ch = 0 ; ch < 4 ; ++ch) { identify.blackPointCh[ch] = raw->imgdata.color.cblack[ch]; } identify.whitePoint = raw->imgdata.color.maximum; identify.orientation = (DRawInfo::ImageOrientation)raw->imgdata.sizes.flip; memcpy(&identify.cameraColorMatrix1, &raw->imgdata.color.cmatrix, sizeof(raw->imgdata.color.cmatrix)); memcpy(&identify.cameraColorMatrix2, &raw->imgdata.color.rgb_cam, sizeof(raw->imgdata.color.rgb_cam)); memcpy(&identify.cameraXYZMatrix, &raw->imgdata.color.cam_xyz, sizeof(raw->imgdata.color.cam_xyz)); if (raw->imgdata.idata.filters) { if (!raw->imgdata.idata.cdesc[3]) { raw->imgdata.idata.cdesc[3] = 'G'; } for (int i = 0 ; i < 16 ; ++i) { identify.filterPattern.append(QLatin1Char(raw->imgdata.idata.cdesc[raw->COLOR(i >> 1, i & 1)])); } identify.colorKeys = QLatin1String(raw->imgdata.idata.cdesc); } for (int c = 0 ; c < raw->imgdata.idata.colors ; ++c) { identify.daylightMult[c] = raw->imgdata.color.pre_mul[c]; } if (raw->imgdata.color.cam_mul[0] > 0) { for (int c = 0 ; c < 4 ; ++c) { identify.cameraMult[c] = raw->imgdata.color.cam_mul[c]; } } } bool DRawDecoder::Private::loadFromLibraw(const QString& filePath, QByteArray& imageData, int& width, int& height, int& rgbmax) { m_parent->m_cancel = false; LibRaw* const raw = new LibRaw; // Set progress call back function. raw->set_progress_handler(callbackForLibRaw, this); QByteArray deadpixelPath = QFile::encodeName(m_parent->m_decoderSettings.deadPixelMap); QByteArray cameraProfile = QFile::encodeName(m_parent->m_decoderSettings.inputProfile); QByteArray outputProfile = QFile::encodeName(m_parent->m_decoderSettings.outputProfile); if (!m_parent->m_decoderSettings.autoBrightness) { // Use a fixed white level, ignoring the image histogram. raw->imgdata.params.no_auto_bright = 1; } if (m_parent->m_decoderSettings.sixteenBitsImage) { // (-4) 16bit ppm output raw->imgdata.params.output_bps = 16; } if (m_parent->m_decoderSettings.halfSizeColorImage) { // (-h) Half-size color image (3x faster than -q). raw->imgdata.params.half_size = 1; } if (m_parent->m_decoderSettings.RGBInterpolate4Colors) { // (-f) Interpolate RGB as four colors. raw->imgdata.params.four_color_rgb = 1; } if (m_parent->m_decoderSettings.DontStretchPixels) { // (-j) Do not stretch the image to its correct aspect ratio. raw->imgdata.params.use_fuji_rotate = 1; } // (-H) Unclip highlight color. raw->imgdata.params.highlight = m_parent->m_decoderSettings.unclipColors; if (m_parent->m_decoderSettings.brightness != 1.0) { // (-b) Set Brightness value. raw->imgdata.params.bright = m_parent->m_decoderSettings.brightness; } if (m_parent->m_decoderSettings.enableBlackPoint) { // (-k) Set Black Point value. raw->imgdata.params.user_black = m_parent->m_decoderSettings.blackPoint; } if (m_parent->m_decoderSettings.enableWhitePoint) { // (-S) Set White Point value (saturation). raw->imgdata.params.user_sat = m_parent->m_decoderSettings.whitePoint; } if (m_parent->m_decoderSettings.medianFilterPasses > 0) { // (-m) After interpolation, clean up color artifacts by repeatedly applying a 3x3 median filter to the R-G and B-G channels. raw->imgdata.params.med_passes = m_parent->m_decoderSettings.medianFilterPasses; } if (!m_parent->m_decoderSettings.deadPixelMap.isEmpty()) { // (-P) Read the dead pixel list from this file. raw->imgdata.params.bad_pixels = deadpixelPath.data(); } switch (m_parent->m_decoderSettings.whiteBalance) { case DRawDecoderSettings::NONE: { break; } case DRawDecoderSettings::CAMERA: { // (-w) Use camera white balance, if possible. raw->imgdata.params.use_camera_wb = 1; break; } case DRawDecoderSettings::AUTO: { // (-a) Use automatic white balance. raw->imgdata.params.use_auto_wb = 1; break; } case DRawDecoderSettings::CUSTOM: { /* Convert between Temperature and RGB. */ double T; double RGB[3]; double xD, yD, X, Y, Z; DRawInfo identify; T = m_parent->m_decoderSettings.customWhiteBalance; - /* Here starts the code picked and adapted from ufraw (0.12.1) - to convert Temperature + green multiplier to RGB multipliers - */ + /* + * Here starts the code picked and adapted from ufraw (0.12.1) + * to convert Temperature + green multiplier to RGB multipliers + */ + /* Convert between Temperature and RGB. * Base on information from http://www.brucelindbloom.com/ * The fit for D-illuminant between 4000K and 12000K are from CIE * The generalization to 2000K < T < 4000K and the blackbody fits * are my own and should be taken with a grain of salt. */ const double XYZ_to_RGB[3][3] = { { 3.24071, -0.969258, 0.0556352 }, {-1.53726, 1.87599, -0.203996 }, {-0.498571, 0.0415557, 1.05707 } }; // Fit for CIE Daylight illuminant + if (T <= 4000) { xD = 0.27475e9/(T*T*T) - 0.98598e6/(T*T) + 1.17444e3/T + 0.145986; } else if (T <= 7000) { xD = -4.6070e9/(T*T*T) + 2.9678e6/(T*T) + 0.09911e3/T + 0.244063; } else { xD = -2.0064e9/(T*T*T) + 1.9018e6/(T*T) + 0.24748e3/T + 0.237040; } yD = -3*xD*xD + 2.87*xD - 0.275; X = xD/yD; Y = 1; Z = (1-xD-yD)/yD; RGB[0] = X*XYZ_to_RGB[0][0] + Y*XYZ_to_RGB[1][0] + Z*XYZ_to_RGB[2][0]; RGB[1] = X*XYZ_to_RGB[0][1] + Y*XYZ_to_RGB[1][1] + Z*XYZ_to_RGB[2][1]; RGB[2] = X*XYZ_to_RGB[0][2] + Y*XYZ_to_RGB[1][2] + Z*XYZ_to_RGB[2][2]; - /* End of the code picked to ufraw - */ + + /* + * End of the code picked to ufraw + */ RGB[1] = RGB[1] / m_parent->m_decoderSettings.customWhiteBalanceGreen; /* By default, decraw override his default D65 WB We need to keep it as a basis : if not, colors with some DSLR will have a high dominant of color that will lead to a completely wrong WB */ if (rawFileIdentify(identify, filePath)) { RGB[0] = identify.daylightMult[0] / RGB[0]; RGB[1] = identify.daylightMult[1] / RGB[1]; RGB[2] = identify.daylightMult[2] / RGB[2]; } else { RGB[0] = 1.0 / RGB[0]; RGB[1] = 1.0 / RGB[1]; RGB[2] = 1.0 / RGB[2]; qCDebug(DIGIKAM_RAWENGINE_LOG) << "Warning: cannot get daylight multipliers"; } // (-r) set Raw Color Balance Multipliers. raw->imgdata.params.user_mul[0] = RGB[0]; raw->imgdata.params.user_mul[1] = RGB[1]; raw->imgdata.params.user_mul[2] = RGB[2]; raw->imgdata.params.user_mul[3] = RGB[1]; break; } case DRawDecoderSettings::AERA: { // (-A) Calculate the white balance by averaging a rectangular area from image. raw->imgdata.params.greybox[0] = m_parent->m_decoderSettings.whiteBalanceArea.left(); raw->imgdata.params.greybox[1] = m_parent->m_decoderSettings.whiteBalanceArea.top(); raw->imgdata.params.greybox[2] = m_parent->m_decoderSettings.whiteBalanceArea.width(); raw->imgdata.params.greybox[3] = m_parent->m_decoderSettings.whiteBalanceArea.height(); break; } } // (-q) Use an interpolation method. raw->imgdata.params.user_qual = m_parent->m_decoderSettings.RAWQuality; switch (m_parent->m_decoderSettings.NRType) { case DRawDecoderSettings::WAVELETSNR: { // (-n) Use wavelets to erase noise while preserving real detail. raw->imgdata.params.threshold = m_parent->m_decoderSettings.NRThreshold; break; } case DRawDecoderSettings::FBDDNR: { // (100 - 1000) => (1 - 10) conversion raw->imgdata.params.fbdd_noiserd = lround(m_parent->m_decoderSettings.NRThreshold / 100.0); break; } default: // No Noise Reduction { raw->imgdata.params.threshold = 0; raw->imgdata.params.fbdd_noiserd = 0; break; } } // Exposure Correction before interpolation. raw->imgdata.params.exp_correc = m_parent->m_decoderSettings.expoCorrection; raw->imgdata.params.exp_shift = m_parent->m_decoderSettings.expoCorrectionShift; raw->imgdata.params.exp_preser = m_parent->m_decoderSettings.expoCorrectionHighlight; switch (m_parent->m_decoderSettings.inputColorSpace) { case DRawDecoderSettings::EMBEDDED: { // (-p embed) Use input profile from RAW file to define the camera's raw colorspace. raw->imgdata.params.camera_profile = (char*)"embed"; break; } case DRawDecoderSettings::CUSTOMINPUTCS: { if (!m_parent->m_decoderSettings.inputProfile.isEmpty()) { // (-p) Use input profile file to define the camera's raw colorspace. raw->imgdata.params.camera_profile = cameraProfile.data(); } break; } default: { // No input profile break; } } switch (m_parent->m_decoderSettings.outputColorSpace) { case DRawDecoderSettings::CUSTOMOUTPUTCS: { if (!m_parent->m_decoderSettings.outputProfile.isEmpty()) { // (-o) Use ICC profile file to define the output colorspace. raw->imgdata.params.output_profile = outputProfile.data(); } break; } default: { // (-o) Define the output colorspace. raw->imgdata.params.output_color = m_parent->m_decoderSettings.outputColorSpace; break; } } //-- Extended demosaicing settings ---------------------------------------------------------- raw->imgdata.params.dcb_iterations = m_parent->m_decoderSettings.dcbIterations; raw->imgdata.params.dcb_enhance_fl = m_parent->m_decoderSettings.dcbEnhanceFl; //------------------------------------------------------------------------------------------- setProgress(0.1); qCDebug(DIGIKAM_RAWENGINE_LOG) << filePath; qCDebug(DIGIKAM_RAWENGINE_LOG) << m_parent->m_decoderSettings; int ret = raw->open_file((const char*)(QFile::encodeName(filePath)).constData()); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run open_file: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_parent->m_cancel) { raw->recycle(); delete raw; return false; } setProgress(0.2); ret = raw->unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_parent->m_cancel) { raw->recycle(); delete raw; return false; } setProgress(0.25); if (m_parent->m_decoderSettings.fixColorsHighlights) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Applying LibRaw highlights adjustments"; // 1.0 is fallback to default value raw->imgdata.params.adjust_maximum_thr = 1.0; } else { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Disabling LibRaw highlights adjustments"; // 0.0 disables this feature raw->imgdata.params.adjust_maximum_thr = 0.0; } ret = raw->dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_parent->m_cancel) { raw->recycle(); delete raw; return false; } setProgress(0.3); libraw_processed_image_t* img = raw->dcraw_make_mem_image(&ret); if (!img) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (m_parent->m_cancel) { // Clear memory allocation. Introduced with LibRaw 0.11.0 raw->dcraw_clear_mem(img); raw->recycle(); delete raw; return false; } setProgress(0.35); width = img->width; height = img->height; rgbmax = (1 << img->bits)-1; if (img->colors == 3) { imageData = QByteArray((const char*)img->data, (int)img->data_size); } else { // img->colors == 1 (Grayscale) : convert to RGB imageData = QByteArray(); for (int i = 0 ; i < (int)img->data_size ; ++i) { for (int j = 0 ; j < 3 ; ++j) { imageData.append(img->data[i]); } } } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw->dcraw_clear_mem(img); raw->recycle(); delete raw; if (m_parent->m_cancel) { return false; } setProgress(0.4); qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: data info: width=" << width << " height=" << height << " rgbmax=" << rgbmax; return true; } bool DRawDecoder::Private::loadEmbeddedPreview(QByteArray& imgData, LibRaw* const raw) { int ret = raw->unpack_thumb(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run unpack_thumb: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } libraw_processed_image_t* const thumb = raw->dcraw_make_mem_thumb(&ret); if (!thumb) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_make_mem_thumb: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } if (thumb->type == LIBRAW_IMAGE_BITMAP) { createPPMHeader(imgData, thumb); } else { imgData = QByteArray((const char*)thumb->data, (int)thumb->data_size); } // Clear memory allocation. Introduced with LibRaw 0.11.0 raw->dcraw_clear_mem(thumb); raw->recycle(); delete raw; if (imgData.isEmpty()) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load JPEG thumb from LibRaw!"; return false; } return true; } bool DRawDecoder::Private::loadHalfPreview(QImage& image, LibRaw* const raw) { raw->imgdata.params.use_auto_wb = 1; // Use automatic white balance. raw->imgdata.params.use_camera_wb = 1; // Use camera white balance, if possible. raw->imgdata.params.half_size = 1; // Half-size color image (3x faster than -q). QByteArray imgData; int ret = raw->unpack(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run unpack: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } ret = raw->dcraw_process(); if (ret != LIBRAW_SUCCESS) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_process: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } libraw_processed_image_t* halfImg = raw->dcraw_make_mem_image(&ret); if (!halfImg) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "LibRaw: failed to run dcraw_make_mem_image: " << libraw_strerror(ret); raw->recycle(); delete raw; return false; } Private::createPPMHeader(imgData, halfImg); + // Clear memory allocation. Introduced with LibRaw 0.11.0 + raw->dcraw_clear_mem(halfImg); raw->recycle(); delete raw; if (imgData.isEmpty()) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load half preview from LibRaw!"; return false; } if (!image.loadFromData(imgData)) { qCDebug(DIGIKAM_RAWENGINE_LOG) << "Failed to load PPM data from LibRaw!"; return false; } return true; } } // namespace Digikam