You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/dimg/loaders/rawloader.cpp

372 lines
13 KiB

/* ============================================================
*
* 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 <caulier dot gilles at gmail dot com>
* Copyright (C) 2005-2008 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
*
* 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 <cmath>
// TQt includes.
#include <tqcstring.h>
// KDE includes.
#include <kstandarddirs.h>
// 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