/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-11-01 * Description : A digital camera RAW files loader for DImg * framework using an external dcraw instance. * * Copyright (C) 2005-2008 by Gilles Caulier * Copyright (C) 2005-2008 by Marcel Wiesweg * * 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. * * ============================================================ */ // C++ includes. #include // TQt includes. #include // KDE includes. #include // Local includes. #include "ddebug.h" #include "imagehistogram.h" #include "imagecurves.h" #include "imagelevels.h" #include "dimg.h" #include "dimgloaderobserver.h" #include "bcgmodifier.h" #include "whitebalance.h" #include "rawloader.h" #include "rawloader.moc" namespace Digikam { RAWLoader::RAWLoader(DImg* image, DRawDecoding rawDecodingSettings) : DImgLoader(image) { m_rawDecodingSettings = rawDecodingSettings; m_customRawSettings = rawDecodingSettings; m_observer = 0; } bool RAWLoader::load(const TQString& filePath, DImgLoaderObserver *observer) { m_observer = observer; // We are using TDEProcess here, and make two assumptions: // - there is an event loop (not for ioslaves) // - we are not called from the event loop thread // These assumptions are currently true for all use cases in digikam, // except the thumbnails iosalve, which will set this attribute. // I hope when porting to TQt4, all the event loop stuff (and this problem) can be removed. if (imageGetAttribute("noeventloop").isValid()) return false; readMetadata(filePath, DImg::RAW); // NOTE: Here, we don't check a possible embedded work-space color profile using // the method checkExifWorkingColorSpace() like with JPEG, PNG, and TIFF loaders, // because RAW file are always in linear mode. int width, height, rgbmax; TQByteArray data; if (!KDcrawIface::KDcraw::decodeRAWImage(filePath, m_rawDecodingSettings, data, width, height, rgbmax)) return false; return (loadedFromDcraw(data, width, height, rgbmax, observer)); } bool RAWLoader::checkToCancelWaitingData() { return (m_observer ? !m_observer->continueQuery(m_image) : false); } void RAWLoader::setWaitingDataProgress(double value) { if (m_observer) m_observer->progressInfo(m_image, value); } #if KDCRAW_VERSION < 0x000106 bool RAWLoader::checkToCancelRecievingData() { return (m_observer ? m_observer->isShuttingDown() : false); } void RAWLoader::setRecievingDataProgress(double value) { if (m_observer) m_observer->progressInfo(m_image, value); } #endif bool RAWLoader::loadedFromDcraw(TQByteArray data, int width, int height, int rgbmax, DImgLoaderObserver *observer) { int checkpoint = 0; if (m_rawDecodingSettings.sixteenBitsImage) // 16 bits image { uchar *image = new uchar[width*height*8]; unsigned short *dst = (unsigned short *)image; uchar *src = (uchar*)data.data(); float fac = 65535.0 / rgbmax; checkpoint = 0; for (int h = 0; h < height; h++) { if (observer && h == checkpoint) { checkpoint += granularity(observer, height, 1.0); if (!observer->continueQuery(m_image)) { return false; } observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) ); } for (int w = 0; w < width; w++) { #if KDCRAW_VERSION < 0x000106 dst[0] = (unsigned short)((src[4]*256 + src[5]) * fac); // Blue dst[1] = (unsigned short)((src[2]*256 + src[3]) * fac); // Green dst[2] = (unsigned short)((src[0]*256 + src[1]) * fac); // Red #else dst[0] = (unsigned short)((src[5]*256 + src[4]) * fac); // Blue dst[1] = (unsigned short)((src[3]*256 + src[2]) * fac); // Green dst[2] = (unsigned short)((src[1]*256 + src[0]) * fac); // Red #endif dst[3] = 0xFFFF; dst += 4; src += 6; } } #if KDCRAW_VERSION < 0x000106 // ---------------------------------------------------------- // Special case : if Color Management is not used here, output color space is in sRGB* color space // RAW decoded image is a linear-histogram image with 16 bits color depth. // No auto white balance and no gamma adjustemnts are performed. Image is a black hole. // We need to reproduce all dcraw 8 bits color depth adjustements here. if (m_rawDecodingSettings.outputColorSpace != DRawDecoding::RAWCOLOR) { ImageHistogram histogram(image, width, height, true); int perc, val, total; float white=0.0, r, gamma=2.222222; unsigned short lut[65536]; // Search 99th percentile white level. perc = (int)(width * height * 0.01); DDebug() << "White Level: " << perc << endl; for (int c = 1 ; c < 4 ; c++) { total = 0; for (val = 65535 ; val > 256 ; --val) if ((total += (int)histogram.getValue(c, val)) > perc) break; if (white < val) white = (float)val; } white *= 1.0 / m_rawDecodingSettings.brightness; DDebug() << "White Point: " << white << endl; // Compute the Gamma lut accordingly. for (int i=0; i < 65536; i++) { r = i / white; val = (int)(65536.0 * (r <= 0.018 ? r*4.5 : pow(r, 1.0/gamma) * 1.099-0.099)); if (val > 65535) val = 65535; lut[i] = val; } // Apply Gamma lut to the whole image. unsigned short *im = (unsigned short *)image; for (int i = 0; i < width*height; i++) { im[0] = lut[im[0]]; // Blue im[1] = lut[im[1]]; // Green im[2] = lut[im[2]]; // Red im += 4; } } #endif // ---------------------------------------------------------- imageData() = (uchar *)image; } else // 8 bits image { uchar *image = new uchar[width*height*4]; uchar *dst = image; uchar *src = (uchar*)data.data(); checkpoint = 0; for (int h = 0; h < height; h++) { if (observer && h == checkpoint) { checkpoint += granularity(observer, height, 1.0); if (!observer->continueQuery(m_image)) { return false; } observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) ); } for (int w = 0; w < width; w++) { // No need to adapt RGB components accordinly with rgbmax value because dcraw // always return rgbmax to 255 in 8 bits/color/pixels. dst[0] = src[2]; // Blue dst[1] = src[1]; // Green dst[2] = src[0]; // Red dst[3] = 0xFF; // Alpha dst += 4; src += 3; } } // NOTE: if Color Management is not used here, output color space is in sRGB* color space. // Gamma and White balance are previously adjusted by dcraw in 8 bits color depth. imageData() = image; } //---------------------------------------------------------- // Assign the right color-space profile. TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles"); switch(m_rawDecodingSettings.outputColorSpace) { case DRawDecoding::SRGB: { TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "srgb.icm"); m_image->getICCProfilFromFile(directory + "srgb.icm"); break; } case DRawDecoding::ADOBERGB: { TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "adobergb.icm"); m_image->getICCProfilFromFile(directory + "adobergb.icm"); break; } case DRawDecoding::WIDEGAMMUT: { TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "widegamut.icm"); m_image->getICCProfilFromFile(directory + "widegamut.icm"); break; } case DRawDecoding::PROPHOTO: { TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "prophoto.icm"); m_image->getICCProfilFromFile(directory + "prophoto.icm"); break; } default: // No icc color-space profile to assign in RAW color mode. break; } //---------------------------------------------------------- imageWidth() = width; imageHeight() = height; imageSetAttribute("format", "RAW"); postProcessing(observer); return true; } void RAWLoader::postProcessing(DImgLoaderObserver *observer) { if (!m_customRawSettings.postProcessingSettingsIsDirty()) return; if (m_customRawSettings.exposureComp != 0.0 || m_customRawSettings.saturation != 1.0) { WhiteBalance wb(m_rawDecodingSettings.sixteenBitsImage); wb.whiteBalance(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage, 0.0, // black m_customRawSettings.exposureComp, // exposure 6500.0, // temperature (neutral) 1.0, // green 0.5, // dark 1.0, // gamma m_customRawSettings.saturation); // saturation } if (observer) observer->progressInfo(m_image, 0.92); if (m_customRawSettings.lightness != 0.0 || m_customRawSettings.contrast != 1.0 || m_customRawSettings.gamma != 1.0) { BCGModifier bcg; bcg.setBrightness(m_customRawSettings.lightness); bcg.setContrast(m_customRawSettings.contrast); bcg.setGamma(m_customRawSettings.gamma); bcg.applyBCG(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); } if (observer) observer->progressInfo(m_image, 0.94); if (!m_customRawSettings.curveAdjust.isEmpty()) { DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); ImageCurves curves(m_rawDecodingSettings.sixteenBitsImage); curves.setCurvePoints(ImageHistogram::ValueChannel, m_customRawSettings.curveAdjust); curves.curvesCalculateCurve(ImageHistogram::ValueChannel); curves.curvesLutSetup(ImageHistogram::AlphaChannel); curves.curvesLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight()); memcpy(imageData(), tmp.bits(), tmp.numBytes()); } if (observer) observer->progressInfo(m_image, 0.96); if (!m_customRawSettings.levelsAdjust.isEmpty()) { DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage); ImageLevels levels(m_rawDecodingSettings.sixteenBitsImage); int j=0; for (int i = 0 ; i < 4; i++) { levels.setLevelLowInputValue(i, m_customRawSettings.levelsAdjust[j++]); levels.setLevelHighInputValue(i, m_customRawSettings.levelsAdjust[j++]); levels.setLevelLowOutputValue(i, m_customRawSettings.levelsAdjust[j++]); levels.setLevelHighOutputValue(i, m_customRawSettings.levelsAdjust[j++]); } levels.levelsLutSetup(ImageHistogram::AlphaChannel); levels.levelsLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight()); memcpy(imageData(), tmp.bits(), tmp.numBytes()); } if (observer) observer->progressInfo(m_image, 0.98); } } // NameSpace Digikam