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.
372 lines
13 KiB
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
|