File indexing completed on 2024-06-16 04:17:07

0001 /*
0002  *  SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_raw_import.h"
0008 
0009 #include <cmath>
0010 #include <cstddef>
0011 #include <exiv2/exiv2.hpp>
0012 #include <utility>
0013 #include <kpluginfactory.h>
0014 #include <libkdcraw_version.h>
0015 
0016 #include <kdcraw.h>
0017 
0018 #include <KisCursorOverrideLock.h>
0019 #include <KisDocument.h>
0020 #include <KisExiv2IODevice.h>
0021 #include <KisImportExportErrorCode.h>
0022 #include <KoColorSpace.h>
0023 #include <KoColorSpaceRegistry.h>
0024 #include <KoColorSpaceTraits.h>
0025 #include <KoDialog.h>
0026 #include <KoUpdater.h>
0027 #include <kis_debug.h>
0028 #include <kis_group_layer.h>
0029 #include <kis_image.h>
0030 #include <kis_meta_data_backend_registry.h>
0031 #include <kis_meta_data_io_backend.h>
0032 #include <kis_meta_data_tags.h>
0033 #include <kis_paint_device.h>
0034 #include <kis_paint_layer.h>
0035 #include <kis_random_accessor_ng.h>
0036 #include <kis_transaction.h>
0037 
0038 using namespace KDcrawIface;
0039 
0040 class Q_DECL_HIDDEN MyKDcraw : public KDcraw
0041 {
0042 public:
0043     MyKDcraw(QPointer<KoUpdater> updater)
0044         : updater(std::move(updater))
0045     {
0046     }
0047 
0048     void setWaitingDataProgress(double value) override
0049     {
0050         if (updater)
0051             updater->setProgress(static_cast<int>(value * 100.0));
0052     }
0053 
0054 private:
0055     QPointer<KoUpdater> updater;
0056 };
0057 
0058 K_PLUGIN_FACTORY_WITH_JSON(KisRawImportFactory, "krita_raw_import.json", registerPlugin<KisRawImport>();)
0059 
0060 KisRawImport::KisRawImport(QObject *parent, const QVariantList &)
0061     : KisImportExportFilter(parent)
0062     , m_dialog(new KoDialog())
0063     , m_rawWidget(new WdgRawImport())
0064 {
0065     m_dialog->setButtons(KoDialog::Ok | KoDialog::Cancel | KoDialog::Apply);
0066     m_dialog->setDefaultButton(KoDialog::Ok);
0067 
0068     m_dialog->setMainWidget(m_rawWidget);
0069     connect(m_dialog, &KoDialog::applyClicked, this, &KisRawImport::slotUpdatePreview);
0070     connect(m_rawWidget, &WdgRawImport::paint, this, &KisRawImport::slotUpdatePreview);
0071     connect(m_rawWidget->rawSettings, &DcrawSettingsWidget::signalSettingsChanged, [&]() {
0072         m_dialog->enableButtonApply(true);
0073     });
0074 }
0075 
0076 KisRawImport::~KisRawImport()
0077 {
0078     delete m_dialog;
0079 }
0080 
0081 inline quint16 correctIndian(quint16 v)
0082 {
0083 #if KDCRAW_VERSION < 0x000400
0084     return ((v & 0x00FF) << 8) | ((v & 0xFF00 >> 8));
0085 #else
0086     return v;
0087 #endif
0088 }
0089 
0090 KisImportExportErrorCode
0091 KisRawImport::convert(KisDocument *document, QIODevice * /*io*/, KisPropertiesConfigurationSP /*configuration*/)
0092 {
0093 #if KDCRAW_VERSION < 0x010200
0094     m_rawWidget->rawSettings->setDefaultSettings();
0095 #else
0096     m_rawWidget->rawSettings->resetToDefault();
0097 #endif
0098 
0099     int r = QDialog::Accepted;
0100     if (!batchMode()) {
0101         r = m_dialog->exec();
0102     }
0103 
0104     if (r == QDialog::Accepted) {
0105         KisCursorOverrideLock cursorLock(Qt::WaitCursor);
0106 
0107         // Do the decoding
0108         QByteArray imageData;
0109         RawDecodingSettings settings = rawDecodingSettings();
0110         settings.sixteenBitsImage = true;
0111         int width = 0;
0112         int height = 0;
0113         int rgbmax = 0;
0114         MyKDcraw dcraw(updater());
0115         if (!dcraw.decodeRAWImage(filename(), settings, imageData, width, height, rgbmax))
0116             return ImportExportCodes::FileFormatIncorrect;
0117 
0118         const KoColorProfile *profile = nullptr;
0119 
0120         switch (settings.outputColorSpace) {
0121         case RawDecodingSettings::RAWCOLOR:
0122         case RawDecodingSettings::SRGB:
0123             profile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
0124             break;
0125         case RawDecodingSettings::ADOBERGB:
0126             profile = KoColorSpaceRegistry::instance()->profileByName("ClayRGB-elle-V2-g22.icc");
0127             break;
0128         case RawDecodingSettings::WIDEGAMMUT:
0129             profile = KoColorSpaceRegistry::instance()->profileByName("WideRGB-elle-V2-g22.icc");
0130             break;
0131         case RawDecodingSettings::PROPHOTO:
0132             profile = KoColorSpaceRegistry::instance()->profileByName("LargeRGB-elle-V2-g22.icc");
0133             break;
0134         case RawDecodingSettings::CUSTOMOUTPUTCS:
0135             QFileInfo info(settings.outputProfile);
0136 
0137             if (!info.exists()) {
0138                 qWarning() << "WARNING: couldn't find custom profile" << settings.outputProfile;
0139                 profile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
0140             } else {
0141                 QFile profileFile(settings.outputProfile);
0142 
0143                 if (profileFile.open(QFile::ReadOnly)) {
0144                     profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(),
0145                                                                                    Integer16BitsColorDepthID.id(),
0146                                                                                    profileFile.readAll());
0147                 } else {
0148                     qWarning() << "WARNING: couldn't open custom profile file" << settings.outputProfile;
0149                 }
0150             }
0151 
0152             if (!profile) {
0153                 qWarning() << "WARNING: reset profile to sRGB";
0154                 profile = KoColorSpaceRegistry::instance()->p709SRGBProfile();
0155             }
0156 
0157             break;
0158         }
0159 
0160         // Init the image
0161         const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb16(profile);
0162         KisImageSP image = new KisImage(document->createUndoStore(), width, height, cs, filename());
0163         KIS_ASSERT_RECOVER_RETURN_VALUE(!image.isNull(), ImportExportCodes::InternalError);
0164 
0165         KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), quint8_MAX);
0166 
0167         image->addNode(layer, image->rootLayer());
0168         KIS_ASSERT_RECOVER_RETURN_VALUE(!layer.isNull(), ImportExportCodes::InternalError);
0169 
0170         KisPaintDeviceSP device = layer->paintDevice();
0171         KIS_ASSERT_RECOVER_RETURN_VALUE(!device.isNull(), ImportExportCodes::InternalError);
0172 
0173         const auto readLayer = [](KisLayerSP layer,
0174                                   const QByteArray &imageData,
0175                                   int width,
0176                                   int height,
0177                                   const QPointer<KoUpdater> &updater) -> void {
0178             const float original = static_cast<float>(updater->progress());
0179             // Leave 10% for the metadata
0180             const float step = (static_cast<float>(updater->maximum() * 0.9) - original) / (float)height;
0181             // Copy the data
0182             KisRandomAccessorSP it = layer->paintDevice()->createRandomAccessorNG();
0183 
0184             for (int y = 0; y < height;) {
0185                 const int numContiguousRows = qMin(it->numContiguousRows(y), height - y);
0186                 for (int x = 0; x < width;) {
0187                     const int numContiguousColumns = qMin(it->numContiguousColumns(x), width - x);
0188 
0189                     it->moveTo(x, y);
0190 
0191                     const int rowStride = it->rowStride(x, y);
0192 
0193                     quint8 *d = it->rawData();
0194 
0195                     for (int i = 0; i < numContiguousRows; i++) {
0196                         const auto *ptr =
0197                             reinterpret_cast<const typename KoBgrU16Traits::channels_type *>(imageData.data())
0198                             + static_cast<ptrdiff_t>(((y + i) * width + x) * 3);
0199 
0200                         for (int j = 0; j < numContiguousColumns; j++) {
0201                             auto *pixel = reinterpret_cast<typename KoBgrU16Traits::Pixel *>(d) + j;
0202 #if KDCRAW_VERSION < 0x000400
0203                             pixel->red = correctIndian(ptr[2]);
0204                             pixel->green = correctIndian(ptr[1]);
0205                             pixel->blue = correctIndian(ptr[0]);
0206                             pixel->alpha = 0xFFFF;
0207 #else
0208                             *pixel = {correctIndian(ptr[2]), correctIndian(ptr[1]), correctIndian(ptr[0]), 0xFFFF};
0209 #endif
0210                             ptr += 3;
0211                         }
0212 
0213                         d += rowStride;
0214                     }
0215 
0216                     x += numContiguousColumns;
0217                 }
0218 
0219                 y += numContiguousRows;
0220                 updater->setProgress(static_cast<int>(original + step * float(y)));
0221             }
0222         };
0223 
0224         readLayer(layer, imageData, width, height, updater());
0225 
0226         {
0227             // HACK!! Externally parse the Exif metadata
0228             // libtiff has no way to access the fields wholesale
0229             try {
0230                 KisExiv2IODevice::ptr_type basicIoDevice(new KisExiv2IODevice(filename()));
0231 
0232 #if EXIV2_TEST_VERSION(0,28,0)
0233                 const std::unique_ptr<Exiv2::Image> readImg = Exiv2::ImageFactory::open(std::move(basicIoDevice));
0234 #else
0235                 const std::unique_ptr<Exiv2::Image> readImg(Exiv2::ImageFactory::open(basicIoDevice).release());
0236 #endif
0237 
0238                 readImg->readMetadata();
0239 
0240                 const KisMetaData::IOBackend *io = KisMetadataBackendRegistry::instance()->value("exif");
0241 
0242                 // All IFDs are paint layer children of root
0243                 KisNodeSP node = image->rootLayer()->firstChild();
0244 
0245                 QBuffer ioDevice;
0246 
0247                 {
0248                     // Synthesize the Exif blob
0249                     Exiv2::ExifData tempData;
0250                     Exiv2::Blob tempBlob;
0251 
0252                     // NOTE: do not use std::copy_if, auto_ptrs beware
0253                     for (const Exiv2::Exifdatum &i : readImg->exifData()) {
0254                         const uint16_t tag = i.tag();
0255 
0256                         if (tag == Exif::Image::ImageWidth || tag == Exif::Image::ImageLength
0257                             || tag == Exif::Image::BitsPerSample || tag == Exif::Image::Compression
0258                             || tag == Exif::Image::PhotometricInterpretation || tag == Exif::Image::Orientation
0259                             || tag == Exif::Image::SamplesPerPixel || tag == Exif::Image::PlanarConfiguration
0260                             || tag == Exif::Image::YCbCrSubSampling || tag == Exif::Image::YCbCrPositioning
0261                             || tag == Exif::Image::XResolution || tag == Exif::Image::YResolution
0262                             || tag == Exif::Image::ResolutionUnit || tag == Exif::Image::TransferFunction
0263                             || tag == Exif::Image::WhitePoint || tag == Exif::Image::PrimaryChromaticities
0264                             || tag == Exif::Image::YCbCrCoefficients || tag == Exif::Image::ReferenceBlackWhite
0265                             || tag == Exif::Image::InterColorProfile) {
0266                             dbgMetaData << "Ignoring TIFF-specific" << i.key().c_str();
0267                             continue;
0268                         }
0269 
0270                         tempData.add(i);
0271                     }
0272 
0273                     // Encode into temporary blob
0274                     Exiv2::ExifParser::encode(tempBlob, Exiv2::littleEndian, tempData);
0275 
0276                     // Reencode into Qt land
0277                     ioDevice.setData(reinterpret_cast<char *>(tempBlob.data()), static_cast<int>(tempBlob.size()));
0278                 }
0279 
0280                 // Get layer
0281                 KisLayer *layer = qobject_cast<KisLayer *>(node.data());
0282                 KIS_ASSERT_RECOVER(layer)
0283                 {
0284                     errFile << "Attempted to import metadata on an empty document";
0285                     return ImportExportCodes::InternalError;
0286                 }
0287 
0288                 // Inject the data as any other IOBackend
0289                 io->loadFrom(layer->metaData(), &ioDevice);
0290 #if EXIV2_TEST_VERSION(0,28,0)
0291             } catch (Exiv2::Error &e) {
0292                 errFile << "Failed injecting TIFF metadata:" << Exiv2::Error(e.code()).what();
0293 #else
0294             } catch (Exiv2::AnyError &e) {
0295                 errFile << "Failed metadata import:" << e.code() << e.what();
0296 #endif
0297             }
0298         }
0299 
0300         document->setCurrentImage(image);
0301         updater()->setProgress(updater()->maximum());
0302         return ImportExportCodes::OK;
0303     }
0304 
0305     return ImportExportCodes::Cancelled;
0306 }
0307 
0308 void KisRawImport::slotUpdatePreview()
0309 {
0310     QByteArray imageData;
0311     RawDecodingSettings settings = rawDecodingSettings();
0312     settings.sixteenBitsImage = false;
0313     int width = 0;
0314     int height = 0;
0315     int rgbmax = 0;
0316     KDcraw dcraw;
0317     if (dcraw.decodeHalfRAWImage(filename(), settings, imageData, width, height, rgbmax)) {
0318         QImage image(width, height, QImage::Format_RGB32);
0319         quint8 *ptr = reinterpret_cast<quint8 *>(imageData.data());
0320         for (int y = 0; y < height; ++y) {
0321             QRgb *pixel = reinterpret_cast<QRgb *>(image.scanLine(y));
0322             for (int x = 0; x < width; ++x) {
0323                 pixel[x] = qRgb(ptr[0], ptr[1], ptr[2]);
0324                 ptr += 3;
0325             }
0326         }
0327         const QSize previewSize = m_rawWidget->preview->size() * m_rawWidget->preview->devicePixelRatioF();
0328         QPixmap img = QPixmap::fromImage(image).scaled(previewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
0329         img.setDevicePixelRatio(m_rawWidget->preview->devicePixelRatioF());
0330         m_rawWidget->preview->setPixmap(img);
0331     }
0332 
0333     m_dialog->enableButtonApply(false);
0334 }
0335 
0336 RawDecodingSettings KisRawImport::rawDecodingSettings()
0337 {
0338 #if KDCRAW_VERSION < 0x010200
0339     RawDecodingSettings settings;
0340     settings.sixteenBitsImage = true;
0341     settings.brightness = m_rawWidget->rawSettings->brightness();
0342     settings.RAWQuality = m_rawWidget->rawSettings->quality();
0343     settings.outputColorSpace = m_rawWidget->rawSettings->outputColorSpace();
0344     settings.RGBInterpolate4Colors = m_rawWidget->rawSettings->useFourColor();
0345     settings.DontStretchPixels = m_rawWidget->rawSettings->useDontStretchPixels();
0346     settings.unclipColors = m_rawWidget->rawSettings->unclipColor();
0347     settings.whiteBalance = m_rawWidget->rawSettings->whiteBalance();
0348     settings.customWhiteBalance = m_rawWidget->rawSettings->customWhiteBalance();
0349     settings.customWhiteBalanceGreen = m_rawWidget->rawSettings->customWhiteBalanceGreen();
0350 
0351     settings.enableBlackPoint = m_rawWidget->rawSettings->useBlackPoint();
0352     settings.blackPoint = m_rawWidget->rawSettings->blackPoint();
0353 
0354     settings.enableNoiseReduction = m_rawWidget->rawSettings->useNoiseReduction();
0355     settings.NRThreshold = m_rawWidget->rawSettings->NRThreshold();
0356 
0357     settings.enableCACorrection = m_rawWidget->rawSettings->useCACorrection();
0358     settings.caMultiplier[0] = m_rawWidget->rawSettings->caRedMultiplier();
0359     settings.caMultiplier[1] = m_rawWidget->rawSettings->caBlueMultiplier();
0360 
0361     return settings;
0362 #else
0363     return m_rawWidget->rawSettings->settings();
0364 #endif
0365 }
0366 
0367 #include "kis_raw_import.moc"