diff --git a/core/libs/rawengine/drawdecoder.cpp b/core/libs/rawengine/drawdecoder.cpp index 754302b8f5..1cd9fb2a66 100644 --- a/core/libs/rawengine/drawdecoder.cpp +++ b/core/libs/rawengine/drawdecoder.cpp @@ -1,581 +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 + * Description : a tread-safe libraw Qt 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) { 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) { 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)((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)((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) { 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) { 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 e26c429d68..fceef0f398 100644 --- a/core/libs/rawengine/drawdecoder.h +++ b/core/libs/rawengine/drawdecoder.h @@ -1,281 +1,284 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-12-09 - * Description : a tread-safe libraw C++ program interface + * Description : a tread-safe libraw Qt 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. */ explicit DRawDecoder(); /** * Standard destructor. */ virtual ~DRawDecoder(); public: /** * 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(). */ 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(). */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ static QString rawFiles(); /** * 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. */ static int rawFilesVersion(); /** * Provide a list of supported RAW Camera name. */ static QStringList supportedCamera(); /** * Return LibRaw version string. */ static QString librawVersion(); /** * 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'. + /** + * 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); - /** 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'. + /** + * 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); - /** 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'. + /** + * 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. */ 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 */ bool m_cancel; /** * 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. */ 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). */ 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