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"