1697 lines
60 KiB
C++
1697 lines
60 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "rtengine.h"
|
|
#include "rtthumbnail.h"
|
|
#include "../rtgui/options.h"
|
|
#include "image8.h"
|
|
#include <lcms2.h>
|
|
#include "curves.h"
|
|
#include <glibmm.h>
|
|
#include "improcfun.h"
|
|
#include "colortemp.h"
|
|
#include "mytime.h"
|
|
#include "utils.h"
|
|
#include "iccstore.h"
|
|
#include "iccmatrices.h"
|
|
#include "rawimagesource.h"
|
|
#include "stdimagesource.h"
|
|
#include <glib/gstdio.h>
|
|
#include <csetjmp>
|
|
#include "safekeyfile.h"
|
|
#include "safegtk.h"
|
|
#include "rawimage.h"
|
|
#include "jpeg.h"
|
|
#include "../rtgui/ppversion.h"
|
|
#include "improccoordinator.h"
|
|
#include <locale.h>
|
|
|
|
|
|
extern Options options;
|
|
|
|
namespace rtengine {
|
|
using namespace procparams;
|
|
|
|
Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, double wbEq, bool inspectorMode) {
|
|
|
|
StdImageSource imgSrc;
|
|
if (imgSrc.load(fname)) {
|
|
return NULL;
|
|
}
|
|
|
|
ImageIO* img = imgSrc.getImageIO();
|
|
|
|
Thumbnail* tpp = new Thumbnail ();
|
|
|
|
unsigned char* data;
|
|
img->getEmbeddedProfileData (tpp->embProfileLength, data);
|
|
if (data && tpp->embProfileLength) {
|
|
tpp->embProfileData = new unsigned char [tpp->embProfileLength];
|
|
memcpy (tpp->embProfileData, data, tpp->embProfileLength);
|
|
}
|
|
|
|
tpp->scaleForSave = 8192;
|
|
tpp->defGain = 1.0;
|
|
tpp->gammaCorrected = false;
|
|
tpp->isRaw = 0;
|
|
memset (tpp->colorMatrix, 0, sizeof(tpp->colorMatrix));
|
|
tpp->colorMatrix[0][0] = 1.0;
|
|
tpp->colorMatrix[1][1] = 1.0;
|
|
tpp->colorMatrix[2][2] = 1.0;
|
|
|
|
if (inspectorMode) {
|
|
// Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature)
|
|
w = img->width;
|
|
h = img->height;
|
|
tpp->scale = 1.;
|
|
}
|
|
else {
|
|
if (fixwh==1) {
|
|
w = h * img->width / img->height;
|
|
tpp->scale = (double)img->height / h;
|
|
}
|
|
else {
|
|
h = w * img->height / img->width;
|
|
tpp->scale = (double)img->width / w;
|
|
}
|
|
}
|
|
// bilinear interpolation
|
|
if (tpp->thumbImg) {
|
|
delete tpp->thumbImg;
|
|
tpp->thumbImg = NULL;
|
|
}
|
|
if (inspectorMode) {
|
|
// we want an Image8
|
|
if (img->getType() == rtengine::sImage8) {
|
|
// copy the image
|
|
Image8 *srcImg = static_cast<Image8*>(img);
|
|
Image8 *thImg = new Image8 (w, h);
|
|
srcImg->copyData(thImg);
|
|
tpp->thumbImg = thImg;
|
|
}
|
|
else {
|
|
// copy the image with a conversion
|
|
tpp->thumbImg = resizeTo<Image8>(w, h, TI_Bilinear, img);
|
|
}
|
|
}
|
|
else {
|
|
// we want the same image type than the source file
|
|
tpp->thumbImg = resizeToSameType(w, h, TI_Bilinear, img);
|
|
|
|
// histogram computation
|
|
tpp->aeHistCompression = 3;
|
|
tpp->aeHistogram(65536>>tpp->aeHistCompression);
|
|
|
|
double avg_r = 0;
|
|
double avg_g = 0;
|
|
double avg_b = 0;
|
|
int n = 0;
|
|
|
|
if (img->getType() == rtengine::sImage8) {
|
|
Image8 *image = static_cast<Image8*>(img);
|
|
image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
|
|
}
|
|
else if (img->getType() == sImage16) {
|
|
Image16 *image = static_cast<Image16*>(img);
|
|
image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
|
|
}
|
|
else if (img->getType() == sImagefloat) {
|
|
Imagefloat *image = static_cast<Imagefloat*>(img);
|
|
image->computeHistogramAutoWB(avg_r, avg_g, avg_b, n, tpp->aeHistogram, tpp->aeHistCompression);
|
|
}
|
|
else {
|
|
printf("loadFromImage: Unsupported image type \"%s\"!\n", img->getType());
|
|
}
|
|
|
|
if (n>0) {
|
|
ColorTemp cTemp;
|
|
|
|
tpp->redAWBMul = avg_r/double(n);
|
|
tpp->greenAWBMul = avg_g/double(n);
|
|
tpp->blueAWBMul = avg_b/double(n);
|
|
tpp->wbEqual = wbEq;
|
|
|
|
cTemp.mul2temp (tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen);
|
|
}
|
|
|
|
tpp->init ();
|
|
}
|
|
|
|
return tpp;
|
|
}
|
|
|
|
Thumbnail* Thumbnail::loadQuickFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate, bool inspectorMode)
|
|
{
|
|
RawImage *ri= new RawImage(fname);
|
|
int r = ri->loadRaw(false,false);
|
|
if( r )
|
|
{
|
|
delete ri;
|
|
return NULL;
|
|
}
|
|
|
|
rml.exifBase = ri->get_exifBase();
|
|
rml.ciffBase = ri->get_ciffBase();
|
|
rml.ciffLength = ri->get_ciffLen();
|
|
|
|
Image8* img = new Image8 ();
|
|
// No sample format detection occurred earlier, so we set them here,
|
|
// as they are mandatory for the setScanline method
|
|
img->setSampleFormat(IIOSF_UNSIGNED_CHAR);
|
|
img->setSampleArrangement(IIOSA_CHUNKY);
|
|
|
|
int err = 1;
|
|
|
|
// see if it is something we support
|
|
if ( ri->is_supportedThumb() )
|
|
{
|
|
const char* data((const char*)fdata(ri->get_thumbOffset(),ri->get_file()));
|
|
if ( (unsigned char)data[1] == 0xd8 )
|
|
{
|
|
err = img->loadJPEGFromMemory(data,ri->get_thumbLength());
|
|
}
|
|
else
|
|
{
|
|
err = img->loadPPMFromMemory(data,ri->get_thumbWidth(),ri->get_thumbHeight(),ri->get_thumbSwap(),ri->get_thumbBPS());
|
|
}
|
|
}
|
|
|
|
// did we succeed?
|
|
if ( err )
|
|
{
|
|
printf("Could not extract thumb from %s\n",fname.data());
|
|
delete img;
|
|
delete ri;
|
|
return NULL;
|
|
}
|
|
|
|
Thumbnail* tpp = new Thumbnail ();
|
|
|
|
tpp->isRaw = 1;
|
|
memset (tpp->colorMatrix, 0, sizeof(tpp->colorMatrix));
|
|
tpp->colorMatrix[0][0] = 1.0;
|
|
tpp->colorMatrix[1][1] = 1.0;
|
|
tpp->colorMatrix[2][2] = 1.0;
|
|
|
|
if (inspectorMode) {
|
|
// Special case, meaning that we want a full sized thumbnail image (e.g. for the Inspector feature)
|
|
w = img->width;
|
|
h = img->height;
|
|
tpp->scale = 1.;
|
|
}
|
|
else {
|
|
if (fixwh==1) {
|
|
w = h * img->width / img->height;
|
|
tpp->scale = (double)img->height / h;
|
|
}
|
|
else {
|
|
h = w * img->height / img->width;
|
|
tpp->scale = (double)img->width / w;
|
|
}
|
|
}
|
|
|
|
if (tpp->thumbImg) {
|
|
delete tpp->thumbImg;
|
|
tpp->thumbImg = NULL;
|
|
}
|
|
if (inspectorMode) {
|
|
tpp->thumbImg = img;
|
|
}
|
|
else {
|
|
tpp->thumbImg = resizeTo<Image8>(w, h, TI_Nearest, img);
|
|
delete img;
|
|
}
|
|
|
|
if (rotate && ri->get_rotateDegree() > 0) {
|
|
std::string fname = ri->get_filename();
|
|
std::string suffix = fname.length() > 4 ? fname.substr(fname.length()-3) : "";
|
|
for (int i = 0; i < suffix.length(); i++) suffix[i] = std::tolower(suffix[i]);
|
|
// Leaf .mos, Mamiya .mef and Phase One .iiq files have thumbnails already rotated.
|
|
if (suffix != "mos" && suffix != "mef" && suffix != "iiq") {
|
|
tpp->thumbImg->rotate(ri->get_rotateDegree());
|
|
// width/height may have changed after rotating
|
|
w = tpp->thumbImg->width;
|
|
h = tpp->thumbImg->height;
|
|
}
|
|
}
|
|
|
|
if (!inspectorMode)
|
|
tpp->init ();
|
|
|
|
delete ri;
|
|
|
|
return tpp;
|
|
}
|
|
|
|
#define FISRED(filter,row,col) \
|
|
((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==0 || !filter)
|
|
#define FISGREEN(filter,row,col) \
|
|
((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==1 || !filter)
|
|
#define FISBLUE(filter,row,col) \
|
|
((filter >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)==2 || !filter)
|
|
|
|
RawMetaDataLocation Thumbnail::loadMetaDataFromRaw (const Glib::ustring& fname)
|
|
{
|
|
RawMetaDataLocation rml;
|
|
rml.exifBase = -1;
|
|
rml.ciffBase = -1;
|
|
rml.ciffLength = -1;
|
|
|
|
RawImage ri(fname);
|
|
int r = ri.loadRaw(false);
|
|
if( !r ){
|
|
rml.exifBase = ri.get_exifBase();
|
|
rml.ciffBase = ri.get_ciffBase();
|
|
rml.ciffLength = ri.get_ciffLen();
|
|
}
|
|
return rml;
|
|
}
|
|
|
|
Thumbnail* Thumbnail::loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, double wbEq, bool rotate)
|
|
{
|
|
RawImage *ri= new RawImage (fname);
|
|
int r = ri->loadRaw(1,0);
|
|
if( r ){
|
|
delete ri;
|
|
return NULL;
|
|
}
|
|
int width = ri->get_width();
|
|
int height = ri->get_height();
|
|
rtengine::Thumbnail* tpp = new rtengine::Thumbnail;
|
|
|
|
tpp->isRaw = true;
|
|
tpp->embProfile = NULL;
|
|
tpp->embProfileData = NULL;
|
|
tpp->embProfileLength = ri->get_profileLen();
|
|
if (ri->get_profileLen())
|
|
tpp->embProfile = cmsOpenProfileFromMem(ri->get_profile(),
|
|
ri->get_profileLen()); //\ TODO check if mutex is needed
|
|
|
|
tpp->redMultiplier = ri->get_pre_mul(0);
|
|
tpp->greenMultiplier = ri->get_pre_mul(1);
|
|
tpp->blueMultiplier = ri->get_pre_mul(2);
|
|
|
|
ri->scale_colors();
|
|
ri->pre_interpolate();
|
|
|
|
rml.exifBase = ri->get_exifBase();
|
|
rml.ciffBase = ri->get_ciffBase();
|
|
rml.ciffLength = ri->get_ciffLen();
|
|
|
|
tpp->camwbRed = tpp->redMultiplier / ri->get_pre_mul(0);
|
|
tpp->camwbGreen = tpp->greenMultiplier / ri->get_pre_mul(1);
|
|
tpp->camwbBlue = tpp->blueMultiplier / ri->get_pre_mul(2);
|
|
tpp->defGain= 1.0/ min(ri->get_pre_mul(0), ri->get_pre_mul(1), ri->get_pre_mul(2));
|
|
tpp->gammaCorrected = true;
|
|
|
|
unsigned filter = ri->get_filters();
|
|
int firstgreen = 1;
|
|
// locate first green location in the first row
|
|
if(ri->getSensorType()==ST_BAYER)
|
|
while (!FISGREEN(filter,1,firstgreen))
|
|
firstgreen++;
|
|
|
|
int skip = 1;
|
|
if (ri->get_FujiWidth() != 0){
|
|
if (fixwh == 1) // fix height, scale width
|
|
skip = ((ri->get_height() - ri->get_FujiWidth()) / sqrt(0.5) - firstgreen - 1) / h;
|
|
else
|
|
skip = (ri->get_FujiWidth()/sqrt(0.5) - firstgreen - 1) / w;
|
|
}else{
|
|
if (fixwh == 1) // fix height, scale width
|
|
skip = (ri->get_height() - firstgreen - 1) / h;
|
|
else
|
|
skip = (ri->get_width() - firstgreen - 1) / w;
|
|
}
|
|
if (skip % 2)
|
|
skip--;
|
|
if (skip < 1)
|
|
skip = 1;
|
|
|
|
int hskip = skip, vskip = skip;
|
|
if (!ri->get_model().compare("D1X"))
|
|
hskip *= 2;
|
|
|
|
int rofs = 0;
|
|
int tmpw = (width - 2) / hskip;
|
|
int tmph = (height - 2) / vskip;
|
|
|
|
DCraw::dcrawImage_t image = ri->get_image();
|
|
|
|
Imagefloat* tmpImg = new Imagefloat(tmpw, tmph);
|
|
if (ri->getSensorType()==ST_BAYER) {
|
|
// demosaicing! (sort of)
|
|
for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
|
|
rofs = row * width;
|
|
for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col+= hskip, x++) {
|
|
int ofs = rofs + col;
|
|
int g = image[ofs][1];
|
|
int r, b;
|
|
if (FISRED(filter,row,col+1)) {
|
|
r = (image[ofs + 1 ][0] + image[ofs - 1 ][0]) >> 1;
|
|
b = (image[ofs + width][2] + image[ofs - width][2]) >> 1;
|
|
} else {
|
|
b = (image[ofs + 1 ][2] + image[ofs - 1 ][2]) >> 1;
|
|
r = (image[ofs + width][0] + image[ofs - width][0]) >> 1;
|
|
}
|
|
tmpImg->r(y,x) = r;
|
|
tmpImg->g(y,x) = g;
|
|
tmpImg->b(y,x) = b;
|
|
}
|
|
}
|
|
} else if (ri->get_colors() == 1) {
|
|
for (int row = 1, y = 0; row < height - 1 && y < tmph; row += vskip, y++) {
|
|
rofs = row * width;
|
|
for (int col = firstgreen, x = 0; col < width - 1 && x < tmpw; col
|
|
+= hskip, x++) {
|
|
int ofs = rofs + col;
|
|
tmpImg->r(y,x) = tmpImg->g(y,x) = tmpImg->b(y,x) = image[ofs][0];
|
|
}
|
|
}
|
|
} else {
|
|
if(ri->getSensorType()==ST_FUJI_XTRANS) {
|
|
for( int row=1, y = 0; row < height-1 && y < tmph; row+=vskip, y++) {
|
|
rofs = row * width;
|
|
for( int col=1, x = 0; col < width-1 && x < tmpw; col+=hskip, x++ ) {
|
|
int ofs = rofs + col;
|
|
float sum[3] = {};
|
|
int c;
|
|
for(int v=-1;v<=1;v++) {
|
|
for(int h=-1;h<=1;h++) {
|
|
c = ri->XTRANSFC(row+v,col+h);
|
|
sum[c] += image[ofs + v*width + h][c];
|
|
}
|
|
}
|
|
c = ri->XTRANSFC(row,col);
|
|
|
|
switch (c) {
|
|
case 0: tmpImg->r(y,x) = image[ofs][0]; tmpImg->g(y,x) = sum[1] / 5.f; tmpImg->b(y,x) = sum[2] / 3.f; break;
|
|
case 1: tmpImg->r(y,x) = sum[0] / 2.f; tmpImg->g(y,x) = image[ofs][1]; tmpImg->b(y,x) = sum[2] / 2.f; break;
|
|
case 2: tmpImg->r(y,x) = sum[0] / 3.f; tmpImg->g(y,x) = sum[1] / 5.f; tmpImg->b(y,x) = image[ofs][2]; break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
int iwidth = ri->get_iwidth();
|
|
int iheight = ri->get_iheight();
|
|
int left_margin = ri->get_leftmargin();
|
|
firstgreen += left_margin;
|
|
int top_margin = ri->get_topmargin();
|
|
for (int row = 1 + top_margin, y = 0; row < iheight + top_margin - 1 && y < tmph; row += vskip, y++) {
|
|
rofs = row * iwidth;
|
|
for (int col = firstgreen, x = 0; col < iwidth + left_margin - 1 && x < tmpw; col += hskip, x++) {
|
|
int ofs = rofs + col;
|
|
tmpImg->r(y,x) = image[ofs][0];
|
|
tmpImg->g(y,x) = image[ofs][1];
|
|
tmpImg->b(y,x) = image[ofs][2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ri->get_FujiWidth() != 0) {
|
|
int fw = ri->get_FujiWidth() / hskip;
|
|
double step = sqrt(0.5);
|
|
int wide = fw / step;
|
|
int high = (tmph - fw) / step;
|
|
Imagefloat* fImg = new Imagefloat(wide, high);
|
|
float r, c;
|
|
|
|
for (int row = 0; row < high; row++)
|
|
for (int col = 0; col < wide; col++) {
|
|
unsigned ur = r = fw + (row - col) * step;
|
|
unsigned uc = c = (row + col) * step;
|
|
if (ur > tmph - 2 || uc > tmpw - 2)
|
|
continue;
|
|
double fr = r - ur;
|
|
double fc = c - uc;
|
|
fImg->r(row,col) = (tmpImg->r(ur,uc)*(1-fc) + tmpImg->r(ur,uc + 1)*fc) * (1-fr) + (tmpImg->r(ur + 1,uc)*(1-fc) + tmpImg->r(ur + 1,uc + 1)*fc) * fr;
|
|
fImg->g(row,col) = (tmpImg->g(ur,uc)*(1-fc) + tmpImg->g(ur,uc + 1)*fc) * (1-fr) + (tmpImg->g(ur + 1,uc)*(1-fc) + tmpImg->g(ur + 1,uc + 1)*fc) * fr;
|
|
fImg->b(row,col) = (tmpImg->b(ur,uc)*(1-fc) + tmpImg->b(ur,uc + 1)*fc) * (1-fr) + (tmpImg->b(ur + 1,uc)*(1-fc) + tmpImg->b(ur + 1,uc + 1)*fc) * fr;
|
|
}
|
|
delete tmpImg;
|
|
tmpImg = fImg;
|
|
tmpw = wide;
|
|
tmph = high;
|
|
}
|
|
|
|
if (fixwh == 1) // fix height, scale width
|
|
w = tmpw * h / tmph;
|
|
else
|
|
h = tmph * w / tmpw;
|
|
|
|
if (tpp->thumbImg) delete tpp->thumbImg; tpp->thumbImg = NULL;
|
|
tpp->thumbImg = resizeTo<Image16>(w, h, TI_Bilinear, tmpImg);
|
|
delete tmpImg;
|
|
|
|
|
|
if (ri->get_FujiWidth() != 0)
|
|
tpp->scale = (double) (height - ri->get_FujiWidth()) / sqrt(0.5) / h;
|
|
else
|
|
tpp->scale = (double) height / h;
|
|
|
|
// generate histogram for auto exposure
|
|
tpp->aeHistCompression = 3;
|
|
tpp->aeHistogram(65536 >> tpp->aeHistCompression);
|
|
tpp->aeHistogram.clear();
|
|
int radd = 4;
|
|
int gadd = 4;
|
|
int badd = 4;
|
|
if (!filter)
|
|
radd = gadd = badd = 1;
|
|
for (int i = 8; i < height - 8; i++) {
|
|
int start, end;
|
|
if (ri->get_FujiWidth() != 0) {
|
|
int fw = ri->get_FujiWidth();
|
|
start = ABS(fw-i) + 8;
|
|
end = min(height + width-fw-i, fw+i) - 8;
|
|
} else {
|
|
start = 8;
|
|
end = width - 8;
|
|
}
|
|
if (ri->get_colors() == 1) {
|
|
for (int j = start; j < end; j++) {
|
|
tpp->aeHistogram[((int)(image[i* width+j][0]))>>tpp->aeHistCompression]+=radd;
|
|
tpp->aeHistogram[((int)(image[i* width+j][0]))>>tpp->aeHistCompression]+=gadd;
|
|
tpp->aeHistogram[((int)(image[i* width+j][0]))>>tpp->aeHistCompression]+=badd;
|
|
}
|
|
} else if(ri->getSensorType()==ST_BAYER) {
|
|
for (int j = start; j < end; j++)
|
|
if (FISGREEN(filter,i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbGreen*image[i* width+j][1]))>>tpp->aeHistCompression]+=gadd;
|
|
else if (FISRED(filter,i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbRed * image[i* width+j][0]))>>tpp->aeHistCompression]+=radd;
|
|
else if (FISBLUE(filter,i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbBlue *image[i* width+j][2]))>>tpp->aeHistCompression]+=badd;
|
|
} else if(ri->getSensorType()==ST_FUJI_XTRANS) {
|
|
for (int j = start; j < end; j++)
|
|
if (ri->ISXTRANSGREEN(i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbGreen*image[i* width+j][1]))>>tpp->aeHistCompression]+=gadd;
|
|
else if (ri->ISXTRANSRED(i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbRed * image[i* width+j][0]))>>tpp->aeHistCompression]+=radd;
|
|
else if (ri->ISXTRANSBLUE(i,j))
|
|
tpp->aeHistogram[((int)(tpp->camwbBlue *image[i* width+j][2]))>>tpp->aeHistCompression]+=badd;
|
|
} else /* if(ri->getSensorType()==ST_FOVEON) */{
|
|
for (int j = start; j < end; j++) {
|
|
tpp->aeHistogram[((int)(image[i* width+j][0]*2.f))>>tpp->aeHistCompression]+=radd;
|
|
tpp->aeHistogram[((int)(image[i* width+j][1]))>>tpp->aeHistCompression]+=gadd;
|
|
tpp->aeHistogram[((int)(image[i* width+j][2]*0.5f))>>tpp->aeHistCompression]+=badd;
|
|
}
|
|
}
|
|
}
|
|
|
|
// generate autoWB
|
|
double avg_r = 0;
|
|
double avg_g = 0;
|
|
double avg_b = 0;
|
|
const float eps=1e-5; //tolerance to avoid dividing by zero
|
|
|
|
float rn = eps, gn = eps, bn = eps;
|
|
|
|
for (int i = 32; i < height - 32; i++) {
|
|
int start, end;
|
|
if (ri->get_FujiWidth() != 0) {
|
|
int fw = ri->get_FujiWidth();
|
|
start = ABS(fw-i) + 32;
|
|
end = min(height + width-fw-i, fw+i) - 32;
|
|
} else {
|
|
start = 32;
|
|
end = width - 32;
|
|
}
|
|
if(ri->getSensorType()==ST_BAYER) {
|
|
for (int j = start; j < end; j++) {
|
|
if (!filter) {
|
|
double d = tpp->defGain * image[i * width + j][0];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_g += d; avg_r += d; avg_b += d;
|
|
rn++; gn++; bn++;
|
|
} else if (FISGREEN(filter,i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][1];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_g += d;
|
|
gn++;
|
|
}
|
|
else if (FISRED(filter,i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][0];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_r += d;
|
|
rn++;
|
|
}
|
|
else if (FISBLUE(filter,i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][2];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_b += d;
|
|
bn++;
|
|
}
|
|
}
|
|
} else if(ri->getSensorType()==ST_FUJI_XTRANS) {
|
|
for (int j = start; j < end; j++) {
|
|
if (ri->ISXTRANSGREEN(i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][1];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_g += d;
|
|
gn++;
|
|
}
|
|
else if (ri->ISXTRANSRED(i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][0];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_r += d;
|
|
rn++;
|
|
}
|
|
else if (ri->ISXTRANSBLUE(i,j)) {
|
|
double d = tpp->defGain * image[i * width + j][2];
|
|
if (d > 64000.)
|
|
continue;
|
|
avg_b += d;
|
|
bn++;
|
|
}
|
|
}
|
|
} else /* if(ri->getSensorType()==ST_FOVEON) */ {
|
|
for (int j = start; j < end; j++) {
|
|
double d = tpp->defGain * image[i * width + j][0];
|
|
if (d <= 64000.) {
|
|
avg_r += d;
|
|
rn++;
|
|
}
|
|
d = tpp->defGain * image[i * width + j][1];
|
|
if (d <= 64000.) {
|
|
avg_g += d;
|
|
gn++;
|
|
}
|
|
d = tpp->defGain * image[i * width + j][2];
|
|
if (d <= 64000.) {
|
|
avg_b += d;
|
|
bn++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
double reds = avg_r / rn * tpp->camwbRed;
|
|
double greens = avg_g / gn * tpp->camwbGreen;
|
|
double blues = avg_b / bn * tpp->camwbBlue;
|
|
|
|
tpp->redAWBMul = ri->get_rgb_cam(0, 0) * reds + ri->get_rgb_cam(0, 1) * greens + ri->get_rgb_cam(0, 2) * blues;
|
|
tpp->greenAWBMul = ri->get_rgb_cam(1, 0) * reds + ri->get_rgb_cam(1, 1) * greens + ri->get_rgb_cam(1, 2) * blues;
|
|
tpp->blueAWBMul = ri->get_rgb_cam(2, 0) * reds + ri->get_rgb_cam(2, 1) * greens + ri->get_rgb_cam(2, 2) * blues;
|
|
tpp->wbEqual = wbEq;
|
|
|
|
ColorTemp cTemp;
|
|
cTemp.mul2temp(tpp->redAWBMul, tpp->greenAWBMul, tpp->blueAWBMul, tpp->wbEqual, tpp->autoWBTemp, tpp->autoWBGreen);
|
|
|
|
if (rotate && ri->get_rotateDegree() > 0) {
|
|
tpp->thumbImg->rotate(ri->get_rotateDegree());
|
|
}
|
|
|
|
for (int a = 0; a < 3; a++)
|
|
for (int b = 0; b < 3; b++)
|
|
tpp->colorMatrix[a][b] = ri->get_rgb_cam(a, b);
|
|
|
|
tpp->init();
|
|
delete ri;
|
|
return tpp;
|
|
}
|
|
#undef FISRED
|
|
#undef FISGREEN
|
|
#undef FISBLUE
|
|
|
|
|
|
unsigned short *Thumbnail::igammatab = 0;
|
|
unsigned char *Thumbnail::gammatab = 0;
|
|
|
|
void Thumbnail::initGamma () {
|
|
igammatab = new unsigned short[256];
|
|
gammatab = new unsigned char[65536];
|
|
for (int i=0; i<256; i++)
|
|
igammatab[i] = (unsigned short)(255.0*pow((double)i/255.0,Color::sRGBGamma));
|
|
for (int i=0; i<65536; i++)
|
|
gammatab[i] = (unsigned char)(255.0*pow((double)i/65535.0,1.f/Color::sRGBGamma));
|
|
}
|
|
|
|
void Thumbnail::cleanupGamma () {
|
|
delete [] igammatab;
|
|
delete [] gammatab;
|
|
}
|
|
|
|
void Thumbnail::init () {
|
|
|
|
RawImageSource::inverse33 (colorMatrix, iColorMatrix);
|
|
//colorMatrix is rgb_cam
|
|
memset (cam2xyz, 0, sizeof(cam2xyz));
|
|
for (int i=0; i<3; i++)
|
|
for (int j=0; j<3; j++)
|
|
for (int k=0; k<3; k++)
|
|
cam2xyz[i][j] += xyz_sRGB[i][k] * colorMatrix[k][j];
|
|
camProfile = iccStore->createFromMatrix (cam2xyz, false, "Camera");
|
|
}
|
|
|
|
Thumbnail::Thumbnail () :
|
|
camProfile(NULL), thumbImg(NULL),
|
|
camwbRed(1.0), camwbGreen(1.0), camwbBlue(1.0),
|
|
redAWBMul(-1.0), greenAWBMul(-1.0), blueAWBMul(-1.0),
|
|
autoWBTemp(2700), autoWBGreen(1.0), wbEqual(-1.0),
|
|
embProfileLength(0), embProfileData(NULL), embProfile(NULL),
|
|
redMultiplier(1.0), greenMultiplier(1.0), blueMultiplier(1.0),
|
|
defGain(1.0),
|
|
scaleForSave(8192),
|
|
gammaCorrected(false),
|
|
aeHistCompression(3) {
|
|
}
|
|
|
|
Thumbnail::~Thumbnail () {
|
|
|
|
delete thumbImg;
|
|
//delete [] aeHistogram;
|
|
delete [] embProfileData;
|
|
if (embProfile)
|
|
cmsCloseProfile(embProfile);
|
|
if (camProfile)
|
|
cmsCloseProfile(camProfile);
|
|
}
|
|
|
|
// Simple processing of RAW internal JPGs
|
|
IImage8* Thumbnail::quickProcessImage (const procparams::ProcParams& params, int rheight, rtengine::TypeInterpolation interp, double& myscale) {
|
|
|
|
int rwidth;
|
|
if (params.coarse.rotate==90 || params.coarse.rotate==270) {
|
|
rwidth = rheight;
|
|
rheight = thumbImg->height * rwidth / thumbImg->width;
|
|
}
|
|
else
|
|
rwidth = thumbImg->width * rheight / thumbImg->height;
|
|
|
|
Image8* baseImg = resizeTo<Image8>(rwidth, rheight, interp, thumbImg);
|
|
|
|
if (params.coarse.rotate)
|
|
baseImg->rotate (params.coarse.rotate);
|
|
|
|
if (params.coarse.hflip)
|
|
baseImg->hflip ();
|
|
|
|
if (params.coarse.vflip)
|
|
baseImg->vflip ();
|
|
return baseImg;
|
|
}
|
|
|
|
// Full thumbnail processing, second stage if complete profile exists
|
|
IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rheight, TypeInterpolation interp, std::string camName,
|
|
double focalLen, double focalLen35mm, float focusDist, float shutter, float fnumber, float iso,std::string expcomp_, double& myscale) {
|
|
// check if the WB's equalizer value has changed
|
|
if (wbEqual < (params.wb.equal-5e-4) || wbEqual > (params.wb.equal+5e-4)) {
|
|
wbEqual = params.wb.equal;
|
|
// recompute the autoWB
|
|
ColorTemp cTemp;
|
|
cTemp.mul2temp (redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen);
|
|
}
|
|
// compute WB multipliers
|
|
ColorTemp currWB = ColorTemp (params.wb.temperature, params.wb.green, params.wb.equal,params.wb.method);
|
|
if (params.wb.method=="Camera") {
|
|
//recall colorMatrix is rgb_cam
|
|
double cam_r = colorMatrix[0][0]*camwbRed + colorMatrix[0][1]*camwbGreen + colorMatrix[0][2]*camwbBlue;
|
|
double cam_g = colorMatrix[1][0]*camwbRed + colorMatrix[1][1]*camwbGreen + colorMatrix[1][2]*camwbBlue;
|
|
double cam_b = colorMatrix[2][0]*camwbRed + colorMatrix[2][1]*camwbGreen + colorMatrix[2][2]*camwbBlue;
|
|
currWB = ColorTemp (cam_r, cam_g, cam_b, params.wb.equal);
|
|
}
|
|
else if (params.wb.method=="Auto")
|
|
currWB = ColorTemp (autoWBTemp, autoWBGreen, wbEqual, "Custom");
|
|
double r, g, b;
|
|
currWB.getMultipliers (r, g, b);
|
|
//iColorMatrix is cam_rgb
|
|
double rm = iColorMatrix[0][0]*r + iColorMatrix[0][1]*g + iColorMatrix[0][2]*b;
|
|
double gm = iColorMatrix[1][0]*r + iColorMatrix[1][1]*g + iColorMatrix[1][2]*b;
|
|
double bm = iColorMatrix[2][0]*r + iColorMatrix[2][1]*g + iColorMatrix[2][2]*b;
|
|
rm = camwbRed / rm;
|
|
gm = camwbGreen / gm;
|
|
bm = camwbBlue / bm;
|
|
double mul_lum = 0.299*rm + 0.587*gm + 0.114*bm;
|
|
double logDefGain = log(defGain) / log(2.0);
|
|
int rmi, gmi, bmi;
|
|
// Since HL recovery is not rendered in thumbs
|
|
// if (!isRaw || !params.toneCurve.hrenabled) {
|
|
logDefGain = 0.0;
|
|
rmi = 1024.0 * rm * defGain / mul_lum;
|
|
gmi = 1024.0 * gm * defGain / mul_lum;
|
|
bmi = 1024.0 * bm * defGain / mul_lum;
|
|
/* }
|
|
else {
|
|
rmi = 1024.0 * rm / mul_lum;
|
|
gmi = 1024.0 * gm / mul_lum;
|
|
bmi = 1024.0 * bm / mul_lum;
|
|
}*/
|
|
|
|
// The RAW exposure is not reflected since it's done in preprocessing. If we only have e.g. the chached thumb,
|
|
// that is already preprocessed. So we simulate the effect here roughly my modifying the exposure accordingly
|
|
if (isRaw && fabs(1.0-params.raw.expos)>0.001) {
|
|
rmi*=params.raw.expos;
|
|
gmi*=params.raw.expos;
|
|
bmi*=params.raw.expos;
|
|
}
|
|
|
|
// resize to requested width and perform coarse transformation
|
|
int rwidth;
|
|
if (params.coarse.rotate==90 || params.coarse.rotate==270) {
|
|
rwidth = rheight;
|
|
rheight = int(size_t(thumbImg->height) * size_t(rwidth) / size_t(thumbImg->width));
|
|
}
|
|
else
|
|
rwidth = int(size_t(thumbImg->width) * size_t(rheight) / size_t(thumbImg->height));
|
|
|
|
Imagefloat* baseImg = resizeTo<Imagefloat>(rwidth, rheight, interp, thumbImg);
|
|
|
|
if (params.coarse.rotate) {
|
|
baseImg->rotate (params.coarse.rotate);
|
|
rwidth = baseImg->width;
|
|
rheight = baseImg->height;
|
|
}
|
|
|
|
if (params.coarse.hflip)
|
|
baseImg->hflip ();
|
|
|
|
if (params.coarse.vflip)
|
|
baseImg->vflip ();
|
|
|
|
// apply white balance and raw white point (simulated)
|
|
int val;
|
|
unsigned short val_;
|
|
for (int i=0; i<rheight; i++)
|
|
for (int j=0; j<rwidth; j++) {
|
|
|
|
baseImg->convertTo(baseImg->r(i,j), val_);
|
|
val = static_cast<int>(val_)*rmi>>10;
|
|
baseImg->r(i,j) = CLIP(val);
|
|
|
|
baseImg->convertTo(baseImg->g(i,j), val_);
|
|
val = static_cast<int>(val_)*gmi>>10;
|
|
baseImg->g(i,j) = CLIP(val);
|
|
|
|
baseImg->convertTo(baseImg->b(i,j), val_);
|
|
val = static_cast<int>(val_)*bmi>>10;
|
|
baseImg->b(i,j) = CLIP(val);
|
|
}
|
|
|
|
/*
|
|
// apply highlight recovery, if needed -- CURRENTLY BROKEN DUE TO INCOMPATIBLE DATA TYPES, BUT HL RECOVERY AREN'T COMPUTED FOR THUMBNAILS ANYWAY...
|
|
if (isRaw && params.toneCurve.hrenabled) {
|
|
int maxval = 65535 / defGain;
|
|
if (params.toneCurve.method=="Luminance" || params.toneCurve.method=="Color")
|
|
for (int i=0; i<rheight; i++)
|
|
RawImageSource::HLRecovery_Luminance (baseImg->r[i], baseImg->g[i], baseImg->b[i], baseImg->r[i], baseImg->g[i], baseImg->b[i], rwidth, maxval);
|
|
else if (params.toneCurve.method=="CIELab blending") {
|
|
double icamToD50[3][3];
|
|
RawImageSource::inverse33 (cam2xyz, icamToD50);
|
|
for (int i=0; i<rheight; i++)
|
|
RawImageSource::HLRecovery_CIELab (baseImg->r[i], baseImg->g[i], baseImg->b[i], baseImg->r[i], baseImg->g[i], baseImg->b[i], rwidth, maxval, cam2xyz, icamToD50);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// if luma denoise has to be done for thumbnails, it should be right here
|
|
|
|
// perform color space transformation
|
|
if (isRaw) {
|
|
double pre_mul[3] = { redMultiplier, greenMultiplier, blueMultiplier };
|
|
RawImageSource::colorSpaceConversion (baseImg, params.icm, currWB, pre_mul, embProfile, camProfile, cam2xyz, camName );
|
|
} else {
|
|
StdImageSource::colorSpaceConversion (baseImg, params.icm, embProfile, thumbImg->getSampleFormat());
|
|
}
|
|
|
|
int fw = baseImg->width;
|
|
int fh = baseImg->height;
|
|
//ColorTemp::CAT02 (baseImg, ¶ms) ;//perhaps not good!
|
|
|
|
ImProcFunctions ipf (¶ms, false);
|
|
ipf.setScale (sqrt(double(fw*fw+fh*fh))/sqrt(double(thumbImg->width*thumbImg->width+thumbImg->height*thumbImg->height))*scale);
|
|
|
|
LUTu hist16 (65536);
|
|
LUTu hist16C (65536);
|
|
|
|
double gamma = isRaw ? Color::sRGBGamma : 0; // usually in ImageSource, but we don't have that here
|
|
ipf.firstAnalysis (baseImg, ¶ms, hist16);
|
|
|
|
// perform transform
|
|
if (ipf.needsTransform()) {
|
|
Imagefloat* trImg = new Imagefloat (fw, fh);
|
|
int origFW;
|
|
int origFH;
|
|
double tscale;
|
|
getDimensions(origFW, origFH, tscale);
|
|
ipf.transform (baseImg, trImg, 0, 0, 0, 0, fw, fh, origFW*tscale+0.5, origFH*tscale+0.5, focalLen, focalLen35mm, focusDist, 0, true); // Raw rotate degree not detectable here
|
|
delete baseImg;
|
|
baseImg = trImg;
|
|
}
|
|
|
|
// update blurmap
|
|
SHMap* shmap = NULL;
|
|
if (params.sh.enabled) {
|
|
shmap = new SHMap (fw, fh, false);
|
|
double radius = sqrt (double(fw*fw+fh*fh)) / 2.0;
|
|
double shradius = params.sh.radius;
|
|
if (!params.sh.hq) shradius *= radius / 1800.0;
|
|
shmap->update (baseImg, shradius, ipf.lumimul, params.sh.hq, 16);
|
|
}
|
|
|
|
// RGB processing
|
|
double expcomp = params.toneCurve.expcomp;
|
|
int bright = params.toneCurve.brightness;
|
|
int contr = params.toneCurve.contrast;
|
|
int black = params.toneCurve.black;
|
|
int hlcompr = params.toneCurve.hlcompr;
|
|
int hlcomprthresh = params.toneCurve.hlcomprthresh;
|
|
|
|
if (params.toneCurve.autoexp && aeHistogram) {
|
|
ipf.getAutoExp (aeHistogram, aeHistCompression, logDefGain, params.toneCurve.clip, expcomp, bright, contr, black, hlcompr, hlcomprthresh);
|
|
//ipf.getAutoExp (aeHistogram, aeHistCompression, logDefGain, params.toneCurve.clip, params.toneCurve.expcomp, params.toneCurve.brightness, params.toneCurve.contrast, params.toneCurve.black, params.toneCurve.hlcompr);
|
|
}
|
|
|
|
LUTf curve1 (65536);
|
|
LUTf curve2 (65536);
|
|
LUTf curve (65536);
|
|
LUTf satcurve (65536);
|
|
LUTf lhskcurve (65536);
|
|
LUTf clcurve (65536);
|
|
LUTf clToningcurve (65536);
|
|
LUTf cl2Toningcurve (65536);
|
|
|
|
LUTf rCurve (65536);
|
|
LUTf gCurve (65536);
|
|
LUTf bCurve (65536);
|
|
|
|
LUTu dummy;
|
|
|
|
ToneCurve customToneCurve1, customToneCurve2;
|
|
ColorGradientCurve ctColorCurve;
|
|
OpacityCurve ctOpacityCurve;
|
|
// NoisCurve dnNoisCurve;
|
|
|
|
ColorAppearance customColCurve1;
|
|
ColorAppearance customColCurve2;
|
|
ColorAppearance customColCurve3;
|
|
ToneCurve customToneCurvebw1;
|
|
ToneCurve customToneCurvebw2;
|
|
|
|
ipf.g = gamma;
|
|
ipf.iGamma = true;
|
|
CurveFactory::complexCurve (0.0, black/65535.0, hlcompr, hlcomprthresh,
|
|
params.toneCurve.shcompr, bright, contr, ipf.g, !ipf.iGamma,
|
|
params.toneCurve.curveMode, params.toneCurve.curve,
|
|
params.toneCurve.curveMode2, params.toneCurve.curve2,
|
|
hist16, dummy, curve1, curve2, curve, dummy, customToneCurve1, customToneCurve2, 16);
|
|
|
|
CurveFactory::RGBCurve (params.rgbCurves.rcurve, rCurve, 16);
|
|
CurveFactory::RGBCurve (params.rgbCurves.gcurve, gCurve, 16);
|
|
CurveFactory::RGBCurve (params.rgbCurves.bcurve, bCurve, 16);
|
|
|
|
TMatrix wprof = iccStore->workingSpaceMatrix (params.icm.working);
|
|
double wp[3][3] = {
|
|
{wprof[0][0],wprof[0][1],wprof[0][2]},
|
|
{wprof[1][0],wprof[1][1],wprof[1][2]},
|
|
{wprof[2][0],wprof[2][1],wprof[2][2]}};
|
|
TMatrix wiprof = iccStore->workingSpaceInverseMatrix (params.icm.working);
|
|
double wip[3][3] = {
|
|
{wiprof[0][0],wiprof[0][1],wiprof[0][2]},
|
|
{wiprof[1][0],wiprof[1][1],wiprof[1][2]},
|
|
{wiprof[2][0],wiprof[2][1],wiprof[2][2]}
|
|
};
|
|
bool opautili=false;
|
|
params.colorToning.getCurves(ctColorCurve, ctOpacityCurve, wp, wip, opautili);
|
|
//params.dirpyrDenoise.getCurves(dnNoisCurve, lldenoisutili);
|
|
|
|
bool clctoningutili=false;
|
|
bool llctoningutili=false;
|
|
CurveFactory::curveToningCL(clctoningutili, params.colorToning.clcurve, clToningcurve,scale==1 ? 1 : 16);
|
|
CurveFactory::curveToningLL(llctoningutili, params.colorToning.cl2curve, cl2Toningcurve, scale==1 ? 1 : 16);
|
|
|
|
CurveFactory::curveBW (params.blackwhite.beforeCurve, params.blackwhite.afterCurve, hist16, dummy, customToneCurvebw1, customToneCurvebw2, 16);
|
|
|
|
double rrm, ggm, bbm;
|
|
float autor, autog, autob;
|
|
float satLimit = float(params.colorToning.satProtectionThreshold)/100.f*0.7f+0.3f;
|
|
float satLimitOpacity = 1.f-(float(params.colorToning.saturatedOpacity)/100.f);
|
|
|
|
if(params.colorToning.enabled && params.colorToning.autosat){//for colortoning evaluation of saturation settings
|
|
float moyS=0.f;
|
|
float eqty=0.f;
|
|
ipf.moyeqt (baseImg, moyS, eqty);//return image : mean saturation and standard dev of saturation
|
|
//printf("moy=%f ET=%f\n", moyS,eqty);
|
|
float satp=((moyS+1.5f*eqty)-0.3f)/0.7f;//1.5 sigma ==> 93% pixels with high saturation -0.3 / 0.7 convert to Hombre scale
|
|
if(satp >= 0.92f) satp=0.92f;//avoid values too high (out of gamut)
|
|
if(satp <= 0.15f) satp=0.15f;//avoid too low values
|
|
|
|
satLimit= 100.f*satp;
|
|
|
|
satLimitOpacity= 100.f*(moyS-0.85f*eqty);//-0.85 sigma==>20% pixels with low saturation
|
|
}
|
|
|
|
autor = autog = autob = -9000.f; // This will ask to compute the "auto" values for the B&W tool
|
|
|
|
LabImage* labView = new LabImage (fw,fh);
|
|
DCPProfile *dcpProf = NULL;
|
|
if (isRaw) {
|
|
cmsHPROFILE dummy;
|
|
RawImageSource::findInputProfile(params.icm.input, NULL, camName, &dcpProf, dummy);
|
|
if (dcpProf != NULL) {
|
|
dcpProf->setStep2ApplyState(params.icm.working, params.icm.toneCurve, params.icm.applyLookTable, params.icm.applyBaselineExposureOffset);
|
|
}
|
|
}
|
|
ipf.rgbProc (baseImg, labView, NULL, curve1, curve2, curve, shmap, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit ,satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2,rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf);
|
|
|
|
// freeing up some memory
|
|
customToneCurve1.Reset();
|
|
customToneCurve2.Reset();
|
|
ctColorCurve.Reset();
|
|
ctOpacityCurve.Reset();
|
|
// dnNoisCurve.Reset();
|
|
customToneCurvebw1.Reset();
|
|
customToneCurvebw2.Reset();
|
|
|
|
if (shmap)
|
|
delete shmap;
|
|
|
|
// luminance histogram update
|
|
hist16.clear();hist16C.clear();
|
|
for (int i=0; i<fh; i++)
|
|
for (int j=0; j<fw; j++){
|
|
hist16[CLIP((int)((labView->L[i][j])))]++;
|
|
hist16C[CLIP((int)sqrt(labView->a[i][j]*labView->a[i][j] + labView->b[i][j]*labView->b[i][j]))]++;
|
|
}
|
|
// luminance processing
|
|
// ipf.EPDToneMap(labView,0,6);
|
|
|
|
bool utili=false;
|
|
bool autili=false;
|
|
bool butili=false;
|
|
bool ccutili=false;
|
|
bool cclutili=false;
|
|
bool clcutili=false;
|
|
|
|
CurveFactory::complexLCurve (params.labCurve.brightness, params.labCurve.contrast, params.labCurve.lcurve,
|
|
hist16, hist16, curve, dummy, 16, utili);
|
|
|
|
CurveFactory::curveCL(clcutili, params.labCurve.clcurve, clcurve, hist16C, dummy, 16);
|
|
|
|
CurveFactory::complexsgnCurve (1.f, autili, butili, ccutili, cclutili,params.labCurve.chromaticity, params.labCurve.rstprotection,
|
|
params.labCurve.acurve, params.labCurve.bcurve,params.labCurve.cccurve,params.labCurve.lccurve, curve1, curve2, satcurve,lhskcurve,
|
|
hist16C, hist16C, dummy, dummy,
|
|
16);
|
|
//ipf.luminanceCurve (labView, labView, curve);
|
|
|
|
|
|
ipf.chromiLuminanceCurve (NULL, 1,labView, labView, curve1, curve2, satcurve,lhskcurve, clcurve, curve, utili, autili, butili, ccutili,cclutili, clcutili, dummy, dummy, dummy, dummy);
|
|
|
|
ipf.vibrance(labView);
|
|
|
|
if((params.colorappearance.enabled && !params.colorappearance.tonecie) || !params.colorappearance.enabled) ipf.EPDToneMap(labView,5,6);
|
|
|
|
//if(!params.colorappearance.enabled){ipf.EPDToneMap(labView,5,6);}
|
|
|
|
CurveFactory::curveLightBrightColor (
|
|
params.colorappearance.curveMode, params.colorappearance.curve,
|
|
params.colorappearance.curveMode2, params.colorappearance.curve2,
|
|
params.colorappearance.curveMode3, params.colorappearance.curve3,
|
|
hist16, hist16, dummy,
|
|
hist16C, dummy,
|
|
customColCurve1,
|
|
customColCurve2,
|
|
customColCurve3,
|
|
16);
|
|
|
|
if(params.colorappearance.enabled){
|
|
int begh = 0, endh = labView->H;
|
|
bool execsharp=false;
|
|
float d;
|
|
float fnum = fnumber;// F number
|
|
float fiso = iso;// ISO
|
|
float fspeed = shutter;//speed
|
|
char * writ = new char[expcomp_.size() + 1];//convert expcomp_ to char
|
|
std::copy(expcomp_.begin(), expcomp_.end(), writ);
|
|
writ[expcomp_.size()] = '\0';
|
|
float fcomp = atof(writ); //compensation + -
|
|
delete[] writ;
|
|
float adap;
|
|
if(fnum < 0.3f || fiso < 5.f || fspeed < 0.00001f)
|
|
//if no exif data or wrong
|
|
adap=2000.f;
|
|
else {
|
|
float E_V = fcomp + log2 ((fnum*fnum) / fspeed / (fiso/100.f));
|
|
float expo2= params.toneCurve.expcomp;// exposure compensation in tonecurve ==> direct EV
|
|
E_V += expo2;
|
|
float expo1;//exposure raw white point
|
|
expo1=log2(params.raw.expos);//log2 ==>linear to EV
|
|
E_V += expo1;
|
|
adap= powf(2.f, E_V-3.f);//cd / m2
|
|
//end calculation adaptation scene luminosity
|
|
}
|
|
|
|
LUTf CAMBrightCurveJ;
|
|
LUTf CAMBrightCurveQ;
|
|
float CAMMean;
|
|
int sk;
|
|
int scale;
|
|
sk=16;
|
|
int rtt=0;
|
|
CieImage* cieView = new CieImage (fw,fh);
|
|
ipf.ciecam_02float (cieView, adap, begh, endh, 1, 2, labView, ¶ms,customColCurve1,customColCurve2,customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, 6, execsharp, d, sk, rtt);
|
|
delete cieView;
|
|
}
|
|
// color processing
|
|
//ipf.colorCurve (labView, labView);
|
|
|
|
// obtain final image
|
|
Image8* readyImg = new Image8 (fw, fh);
|
|
ipf.lab2monitorRgb (labView, readyImg);
|
|
delete labView;
|
|
delete baseImg;
|
|
// calculate scale
|
|
if (params.coarse.rotate==90 || params.coarse.rotate==270)
|
|
myscale = scale * thumbImg->width / fh;
|
|
else
|
|
myscale = scale * thumbImg->height / fh;
|
|
|
|
myscale = 1.0 / myscale;
|
|
/* // apply crop
|
|
if (params.crop.enabled) {
|
|
int ix = 0;
|
|
for (int i=0; i<fh; i++)
|
|
for (int j=0; j<fw; j++)
|
|
if (i<params.crop.y/myscale || i>(params.crop.y+params.crop.h)/myscale || j<params.crop.x/myscale || j>(params.crop.x+params.crop.w)/myscale) {
|
|
readyImg->data[ix++] /= 3;
|
|
readyImg->data[ix++] /= 3;
|
|
readyImg->data[ix++] /= 3;
|
|
}
|
|
else
|
|
ix += 3;
|
|
}*/
|
|
|
|
return readyImg;
|
|
}
|
|
|
|
int Thumbnail::getImageWidth (const procparams::ProcParams& params, int rheight, float &ratio) {
|
|
if (thumbImg==NULL) return 0; // Can happen if thumb is just building and GUI comes in with resize wishes
|
|
|
|
int rwidth;
|
|
if (params.coarse.rotate==90 || params.coarse.rotate==270) {
|
|
ratio = (float)(thumbImg->height) / (float)(thumbImg->width);
|
|
}
|
|
else {
|
|
ratio = (float)(thumbImg->width) / (float)(thumbImg->height);
|
|
}
|
|
rwidth = (int)(ratio * (float)rheight);
|
|
|
|
return rwidth;
|
|
}
|
|
|
|
void Thumbnail::getDimensions (int& w, int& h, double& scaleFac) {
|
|
if (thumbImg) {
|
|
w=thumbImg->width; h=thumbImg->height; scaleFac=scale;
|
|
} else {
|
|
w=0; h=0; scale=1;
|
|
}
|
|
}
|
|
|
|
void Thumbnail::getCamWB (double& temp, double& green) {
|
|
|
|
double cam_r = colorMatrix[0][0]*camwbRed + colorMatrix[0][1]*camwbGreen + colorMatrix[0][2]*camwbBlue;
|
|
double cam_g = colorMatrix[1][0]*camwbRed + colorMatrix[1][1]*camwbGreen + colorMatrix[1][2]*camwbBlue;
|
|
double cam_b = colorMatrix[2][0]*camwbRed + colorMatrix[2][1]*camwbGreen + colorMatrix[2][2]*camwbBlue;
|
|
ColorTemp currWB = ColorTemp (cam_r, cam_g, cam_b, 1.0); // we do not take the equalizer into account here, because we want camera's WB
|
|
temp = currWB.getTemp ();
|
|
green = currWB.getGreen ();
|
|
}
|
|
|
|
void Thumbnail::getAutoWB (double& temp, double& green, double equal) {
|
|
|
|
if (equal != wbEqual) {
|
|
// compute the values depending on equal
|
|
ColorTemp cTemp;
|
|
wbEqual = equal;
|
|
// compute autoWBTemp and autoWBGreen
|
|
cTemp.mul2temp(redAWBMul, greenAWBMul, blueAWBMul, wbEqual, autoWBTemp, autoWBGreen);
|
|
}
|
|
temp = autoWBTemp;
|
|
green = autoWBGreen;
|
|
}
|
|
|
|
void Thumbnail::getAutoWBMultipliers (double& rm, double& gm, double& bm) {
|
|
rm = redAWBMul;
|
|
gm = greenAWBMul;
|
|
bm = blueAWBMul;
|
|
}
|
|
|
|
void Thumbnail::applyAutoExp (procparams::ProcParams& params) {
|
|
|
|
if (params.toneCurve.autoexp && aeHistogram) {
|
|
ImProcFunctions ipf (¶ms, false);
|
|
ipf.getAutoExp (aeHistogram, aeHistCompression, log(defGain)/log(2.0), params.toneCurve.clip, params.toneCurve.expcomp,
|
|
params.toneCurve.brightness, params.toneCurve.contrast, params.toneCurve.black, params.toneCurve.hlcompr, params.toneCurve.hlcomprthresh);
|
|
}
|
|
}
|
|
|
|
void Thumbnail::getSpotWB (const procparams::ProcParams& params, int xp, int yp, int rect, double& rtemp, double& rgreen) {
|
|
|
|
std::vector<Coord2D> points, red, green, blue;
|
|
for (int i=yp-rect; i<=yp+rect; i++)
|
|
for (int j=xp-rect; j<=xp+rect; j++)
|
|
points.push_back (Coord2D (j, i));
|
|
|
|
int fw = thumbImg->width, fh = thumbImg->height;
|
|
if (params.coarse.rotate==90 || params.coarse.rotate==270) {
|
|
fw = thumbImg->height;
|
|
fh = thumbImg->width;
|
|
}
|
|
ImProcFunctions ipf (¶ms, false);
|
|
ipf.transCoord (fw, fh, points, red, green, blue);
|
|
int tr = getCoarseBitMask(params.coarse);
|
|
// calculate spot wb (copy & pasted from stdimagesource)
|
|
double reds = 0, greens = 0, blues = 0;
|
|
int rn = 0, gn = 0, bn = 0;
|
|
thumbImg->getSpotWBData(reds, greens, blues, rn, gn, bn, red, green, blue, tr);
|
|
reds = reds/rn * camwbRed;
|
|
greens = greens/gn * camwbGreen;
|
|
blues = blues/bn * camwbBlue;
|
|
|
|
double rm = colorMatrix[0][0]*reds + colorMatrix[0][1]*greens + colorMatrix[0][2]*blues;
|
|
double gm = colorMatrix[1][0]*reds + colorMatrix[1][1]*greens + colorMatrix[1][2]*blues;
|
|
double bm = colorMatrix[2][0]*reds + colorMatrix[2][1]*greens + colorMatrix[2][2]*blues;
|
|
|
|
ColorTemp ct (rm, gm, bm, params.wb.equal);
|
|
rtemp = ct.getTemp ();
|
|
rgreen = ct.getGreen ();
|
|
}
|
|
void Thumbnail::transformPixel (int x, int y, int tran, int& tx, int& ty) {
|
|
|
|
int W = thumbImg->width;
|
|
int H = thumbImg->height;
|
|
int sw = W, sh = H;
|
|
if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
|
|
sw = H;
|
|
sh = W;
|
|
}
|
|
|
|
int ppx = x, ppy = y;
|
|
if (tran & TR_HFLIP)
|
|
ppx = sw - 1 - x ;
|
|
if (tran & TR_VFLIP)
|
|
ppy = sh - 1 - y;
|
|
|
|
tx = ppx;
|
|
ty = ppy;
|
|
|
|
if ((tran & TR_ROT) == TR_R180) {
|
|
tx = W - 1 - ppx;
|
|
ty = H - 1 - ppy;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R90) {
|
|
tx = ppy;
|
|
ty = H - 1 - ppx;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R270) {
|
|
tx = W - 1 - ppy;
|
|
ty = ppx;
|
|
}
|
|
tx/=scale;
|
|
ty/=scale;
|
|
}
|
|
|
|
unsigned char* Thumbnail::getGrayscaleHistEQ (int trim_width) {
|
|
if (!thumbImg)
|
|
return NULL;
|
|
|
|
if (thumbImg->width<trim_width)
|
|
return NULL;
|
|
|
|
// to utilize the 8 bit color range of the thumbnail we brighten it and apply gamma correction
|
|
unsigned char* tmpdata = new unsigned char[thumbImg->height*trim_width];
|
|
int ix = 0,max;
|
|
|
|
if (gammaCorrected) {
|
|
// if it's gamma correct (usually a RAW), we have the problem that there is a lot noise etc. that makes the maximum way too high.
|
|
// Strategy is limit a certain percent of pixels so the overall picture quality when scaling to 8 bit is way better
|
|
const double BurnOffPct=0.03; // *100 = percent pixels that may be clipped
|
|
|
|
// Calc the histogram
|
|
unsigned int* hist16 = new unsigned int [65536];
|
|
memset(hist16,0,sizeof(int)*65536);
|
|
|
|
if (thumbImg->getType() == sImage8) {
|
|
Image8 *image = static_cast<Image8*>(thumbImg);
|
|
image->calcGrayscaleHist(hist16);
|
|
}
|
|
else if (thumbImg->getType() == sImage16) {
|
|
Image16 *image = static_cast<Image16*>(thumbImg);
|
|
image->calcGrayscaleHist(hist16);
|
|
}
|
|
else if (thumbImg->getType() == sImagefloat) {
|
|
Imagefloat *image = static_cast<Imagefloat*>(thumbImg);
|
|
image->calcGrayscaleHist(hist16);
|
|
}
|
|
else {
|
|
printf("getGrayscaleHistEQ #1: Unsupported image type \"%s\"!\n", thumbImg->getType());
|
|
}
|
|
|
|
// Go down till we cut off that many pixels
|
|
unsigned long cutoff = thumbImg->height * thumbImg->height * 4 * BurnOffPct;
|
|
|
|
int max_;
|
|
unsigned long sum=0;
|
|
for (max_=65535; max_>16384 && sum<cutoff; max_--) sum+=hist16[max_];
|
|
|
|
delete[] hist16;
|
|
|
|
scaleForSave = 65535*8192 / max_;
|
|
|
|
// Correction and gamma to 8 Bit
|
|
if (thumbImg->getType() == sImage8) {
|
|
Image8 *image = static_cast<Image8*>(thumbImg);
|
|
for (int i=0; i<thumbImg->height; i++)
|
|
for (int j=(thumbImg->width-trim_width)/2; j<trim_width+(thumbImg->width-trim_width)/2; j++) {
|
|
unsigned short r_, g_, b_;
|
|
image->convertTo(image->r(i,j), r_);
|
|
image->convertTo(image->g(i,j), g_);
|
|
image->convertTo(image->b(i,j), b_);
|
|
int r= gammatab[min(r_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int g= gammatab[min(g_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int b= gammatab[min(b_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472) >> 16;
|
|
}
|
|
}
|
|
else if (thumbImg->getType() == sImage16) {
|
|
Image16 *image = static_cast<Image16*>(thumbImg);
|
|
for (int i=0; i<thumbImg->height; i++)
|
|
for (int j=(thumbImg->width-trim_width)/2; j<trim_width+(thumbImg->width-trim_width)/2; j++) {
|
|
unsigned short r_, g_, b_;
|
|
image->convertTo(image->r(i,j), r_);
|
|
image->convertTo(image->g(i,j), g_);
|
|
image->convertTo(image->b(i,j), b_);
|
|
int r= gammatab[min(r_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int g= gammatab[min(g_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int b= gammatab[min(b_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472) >> 16;
|
|
}
|
|
}
|
|
else if (thumbImg->getType() == sImagefloat) {
|
|
Imagefloat *image = static_cast<Imagefloat*>(thumbImg);
|
|
for (int i=0; i<thumbImg->height; i++)
|
|
for (int j=(thumbImg->width-trim_width)/2; j<trim_width+(thumbImg->width-trim_width)/2; j++) {
|
|
unsigned short r_, g_, b_;
|
|
image->convertTo(image->r(i,j), r_);
|
|
image->convertTo(image->g(i,j), g_);
|
|
image->convertTo(image->b(i,j), b_);
|
|
int r= gammatab[min(r_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int g= gammatab[min(g_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
int b= gammatab[min(b_,static_cast<unsigned short>(max_)) * scaleForSave >> 13];
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472) >> 16;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// If it's not gamma corrected (usually a JPG) we take the normal maximum
|
|
max=0;
|
|
|
|
if (thumbImg->getType() == sImage8) {
|
|
Image8 *image = static_cast<Image8*>(thumbImg);
|
|
unsigned char max_=0;
|
|
|
|
for (int row=0; row<image->height; row++)
|
|
for (int col=0; col<image->width; col++) {
|
|
if (image->r(row,col)>max_) max_ = image->r(row,col);
|
|
if (image->g(row,col)>max_) max_ = image->g(row,col);
|
|
if (image->b(row,col)>max_) max_ = image->b(row,col);
|
|
}
|
|
image->convertTo(max_, max);
|
|
|
|
if (max < 16384) max = 16384;
|
|
scaleForSave = 65535*8192 / max;
|
|
|
|
// Correction and gamma to 8 Bit
|
|
for (int i=0; i<image->height; i++)
|
|
for (int j=(image->width-trim_width)/2; j<trim_width+(image->width-trim_width)/2; j++) {
|
|
unsigned short rtmp, gtmp, btmp;
|
|
image->convertTo(image->r(i,j), rtmp);
|
|
image->convertTo(image->g(i,j), gtmp);
|
|
image->convertTo(image->b(i,j), btmp);
|
|
int r = rtmp * scaleForSave >> 21;
|
|
int g = gtmp * scaleForSave >> 21;
|
|
int b = btmp * scaleForSave >> 21;
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472)>>16;
|
|
}
|
|
}
|
|
else if (thumbImg->getType() == sImage16) {
|
|
Image16 *image = static_cast<Image16*>(thumbImg);
|
|
unsigned short max_=0;
|
|
|
|
for (int row=0; row<image->height; row++)
|
|
for (int col=0; col<image->width; col++) {
|
|
if (image->r(row,col)>max_) max_ = image->r(row,col);
|
|
if (image->g(row,col)>max_) max_ = image->g(row,col);
|
|
if (image->b(row,col)>max_) max_ = image->b(row,col);
|
|
}
|
|
image->convertTo(max_, max);
|
|
|
|
if (max < 16384) max = 16384;
|
|
scaleForSave = 65535*8192 / max;
|
|
|
|
// Correction and gamma to 8 Bit
|
|
for (int i=0; i<image->height; i++)
|
|
for (int j=(image->width-trim_width)/2; j<trim_width+(image->width-trim_width)/2; j++) {
|
|
unsigned short rtmp, gtmp, btmp;
|
|
image->convertTo(image->r(i,j), rtmp);
|
|
image->convertTo(image->g(i,j), gtmp);
|
|
image->convertTo(image->b(i,j), btmp);
|
|
int r = rtmp * scaleForSave >> 21;
|
|
int g = gtmp * scaleForSave >> 21;
|
|
int b = btmp * scaleForSave >> 21;
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472)>>16;
|
|
}
|
|
}
|
|
else if (thumbImg->getType() == sImagefloat) {
|
|
Imagefloat *image = static_cast<Imagefloat*>(thumbImg);
|
|
float max_=0.f;
|
|
|
|
for (int row=0; row<image->height; row++)
|
|
for (int col=0; col<image->width; col++) {
|
|
if (image->r(row,col)>max_) max_ = image->r(row,col);
|
|
if (image->g(row,col)>max_) max_ = image->g(row,col);
|
|
if (image->b(row,col)>max_) max_ = image->b(row,col);
|
|
}
|
|
image->convertTo(max_, max);
|
|
|
|
if (max < 16384) max = 16384;
|
|
scaleForSave = 65535*8192 / max;
|
|
|
|
// Correction and gamma to 8 Bit
|
|
for (int i=0; i<image->height; i++)
|
|
for (int j=(image->width-trim_width)/2; j<trim_width+(image->width-trim_width)/2; j++) {
|
|
unsigned short rtmp, gtmp, btmp;
|
|
image->convertTo(image->r(i,j), rtmp);
|
|
image->convertTo(image->g(i,j), gtmp);
|
|
image->convertTo(image->b(i,j), btmp);
|
|
int r = rtmp * scaleForSave >> 21;
|
|
int g = gtmp * scaleForSave >> 21;
|
|
int b = btmp * scaleForSave >> 21;
|
|
tmpdata[ix++] = (r*19595+g*38469+b*7472)>>16;
|
|
}
|
|
}
|
|
else {
|
|
printf("getGrayscaleHistEQ #2: Unsupported image type \"%s\"!\n", thumbImg->getType());
|
|
}
|
|
}
|
|
|
|
// histogram equalization
|
|
unsigned int hist[256] = {0};
|
|
|
|
for (int i=0; i<ix; i++) {
|
|
hist[tmpdata[i]]++;
|
|
}
|
|
|
|
int cdf = 0, cdf_min=-1;
|
|
for (int i=0; i<256; i++) {
|
|
cdf+=hist[i];
|
|
if (cdf>0 && cdf_min==-1) {
|
|
cdf_min=cdf;
|
|
}
|
|
if (cdf_min!=-1) {
|
|
hist[i] = (cdf-cdf_min)*255/((thumbImg->height*trim_width)-cdf_min);
|
|
}
|
|
}
|
|
|
|
for (int i=0; i<ix; i++) {
|
|
tmpdata[i] = hist[tmpdata[i]];
|
|
}
|
|
|
|
return tmpdata;
|
|
}
|
|
|
|
bool Thumbnail::writeImage (const Glib::ustring& fname, int format) {
|
|
|
|
if (!thumbImg)
|
|
return false;
|
|
|
|
Glib::ustring fullFName = fname+".rtti";
|
|
|
|
FILE* f = safe_g_fopen (fullFName, "wb");
|
|
if (!f)
|
|
return false;
|
|
fwrite (thumbImg->getType(), sizeof (char), strlen(thumbImg->getType()), f);
|
|
fputc ('\n', f);
|
|
guint32 w = guint32(thumbImg->width);
|
|
guint32 h = guint32(thumbImg->height);
|
|
fwrite (&w, sizeof (guint32), 1, f);
|
|
fwrite (&h, sizeof (guint32), 1, f);
|
|
|
|
if (thumbImg->getType() == sImage8) {
|
|
Image8 *image = static_cast<Image8*>(thumbImg);
|
|
image->writeData(f);
|
|
}
|
|
else if (thumbImg->getType() == sImage16) {
|
|
Image16 *image = static_cast<Image16*>(thumbImg);
|
|
image->writeData(f);
|
|
}
|
|
else if (thumbImg->getType() == sImagefloat) {
|
|
Imagefloat *image = static_cast<Imagefloat*>(thumbImg);
|
|
image->writeData(f);
|
|
}
|
|
|
|
//thumbImg->writeData(f);
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
|
|
bool Thumbnail::readImage (const Glib::ustring& fname) {
|
|
|
|
if (thumbImg) {
|
|
delete thumbImg;
|
|
thumbImg = NULL;
|
|
}
|
|
|
|
Glib::ustring fullFName = fname+".rtti";
|
|
|
|
if (!safe_file_test (fullFName, Glib::FILE_TEST_EXISTS))
|
|
return false;
|
|
|
|
FILE* f = safe_g_fopen (fullFName, "rb");
|
|
if (!f)
|
|
return false;
|
|
|
|
char imgType[31]; // 30 -> arbitrary size, but should be enough for all image type's name
|
|
fgets(imgType, 30, f);
|
|
imgType[strlen(imgType)-1] = '\0'; // imgType has a \n trailing character, so we overwrite it by the \0 char
|
|
|
|
guint32 width, height;
|
|
fread (&width, 1, sizeof (guint32), f);
|
|
fread (&height, 1, sizeof (guint32), f);
|
|
|
|
bool success = false;
|
|
if (!strcmp(imgType, sImage8)) {
|
|
Image8 *image = new Image8(width, height);
|
|
image->readData(f);
|
|
thumbImg = image;
|
|
success = true;
|
|
}
|
|
else if (!strcmp(imgType, sImage16)) {
|
|
Image16 *image = new Image16(width, height);
|
|
image->readData(f);
|
|
thumbImg = image;
|
|
success = true;
|
|
}
|
|
else if (!strcmp(imgType, sImagefloat)) {
|
|
Imagefloat *image = new Imagefloat(width, height);
|
|
image->readData(f);
|
|
thumbImg = image;
|
|
success = true;
|
|
}
|
|
else {
|
|
printf("readImage: Unsupported image type \"%s\"!\n", imgType);
|
|
}
|
|
fclose(f);
|
|
return success;
|
|
}
|
|
|
|
bool Thumbnail::readData (const Glib::ustring& fname) {
|
|
setlocale(LC_NUMERIC, "C"); // to set decimal point to "."
|
|
SafeKeyFile keyFile;
|
|
|
|
try {
|
|
MyMutex::MyLock thmbLock(thumbMutex);
|
|
if (!keyFile.load_from_file (fname))
|
|
return false;
|
|
|
|
if (keyFile.has_group ("LiveThumbData")) {
|
|
if (keyFile.has_key ("LiveThumbData", "CamWBRed")) camwbRed = keyFile.get_double ("LiveThumbData", "CamWBRed");
|
|
if (keyFile.has_key ("LiveThumbData", "CamWBGreen")) camwbGreen = keyFile.get_double ("LiveThumbData", "CamWBGreen");
|
|
if (keyFile.has_key ("LiveThumbData", "CamWBBlue")) camwbBlue = keyFile.get_double ("LiveThumbData", "CamWBBlue");
|
|
if (keyFile.has_key ("LiveThumbData", "RedAWBMul")) redAWBMul = keyFile.get_double ("LiveThumbData", "RedAWBMul");
|
|
if (keyFile.has_key ("LiveThumbData", "GreenAWBMul")) greenAWBMul = keyFile.get_double ("LiveThumbData", "GreenAWBMul");
|
|
if (keyFile.has_key ("LiveThumbData", "BlueAWBMul")) blueAWBMul = keyFile.get_double ("LiveThumbData", "BlueAWBMul");
|
|
if (keyFile.has_key ("LiveThumbData", "AEHistCompression")) aeHistCompression = keyFile.get_integer ("LiveThumbData", "AEHistCompression");
|
|
if (keyFile.has_key ("LiveThumbData", "RedMultiplier")) redMultiplier = keyFile.get_double ("LiveThumbData", "RedMultiplier");
|
|
if (keyFile.has_key ("LiveThumbData", "GreenMultiplier")) greenMultiplier = keyFile.get_double ("LiveThumbData", "GreenMultiplier");
|
|
if (keyFile.has_key ("LiveThumbData", "BlueMultiplier")) blueMultiplier = keyFile.get_double ("LiveThumbData", "BlueMultiplier");
|
|
if (keyFile.has_key ("LiveThumbData", "Scale")) scale = keyFile.get_double ("LiveThumbData", "Scale");
|
|
if (keyFile.has_key ("LiveThumbData", "DefaultGain")) defGain = keyFile.get_double ("LiveThumbData", "DefaultGain");
|
|
if (keyFile.has_key ("LiveThumbData", "ScaleForSave")) scaleForSave = keyFile.get_integer ("LiveThumbData", "ScaleForSave");
|
|
if (keyFile.has_key ("LiveThumbData", "GammaCorrected")) gammaCorrected = keyFile.get_boolean ("LiveThumbData", "GammaCorrected");
|
|
if (keyFile.has_key ("LiveThumbData", "ColorMatrix")) {
|
|
std::vector<double> cm = keyFile.get_double_list ("LiveThumbData", "ColorMatrix");
|
|
int ix = 0;
|
|
for (int i=0; i<3; i++)
|
|
for (int j=0; j<3; j++)
|
|
colorMatrix[i][j] = cm[ix++];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
catch (Glib::Error &err) {
|
|
if (options.rtSettings.verbose)
|
|
printf("Thumbnail::readData / Error code %d while reading values from \"%s\":\n%s\n", err.code(), fname.c_str(), err.what().c_str());
|
|
}
|
|
catch (...) {
|
|
if (options.rtSettings.verbose)
|
|
printf("Thumbnail::readData / Unknown exception while trying to load \"%s\"!\n", fname.c_str());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Thumbnail::writeData (const Glib::ustring& fname) {
|
|
|
|
SafeKeyFile keyFile;
|
|
|
|
MyMutex::MyLock thmbLock(thumbMutex);
|
|
|
|
try {
|
|
if( safe_file_test(fname,Glib::FILE_TEST_EXISTS) )
|
|
keyFile.load_from_file (fname);
|
|
}
|
|
catch (Glib::Error &err) {
|
|
if (options.rtSettings.verbose)
|
|
printf("Thumbnail::writeData / Error code %d while reading values from \"%s\":\n%s\n", err.code(), fname.c_str(), err.what().c_str());
|
|
}
|
|
catch (...) {
|
|
if (options.rtSettings.verbose)
|
|
printf("Thumbnail::writeData / Unknown exception while trying to save \"%s\"!\n", fname.c_str());
|
|
}
|
|
|
|
keyFile.set_double ("LiveThumbData", "CamWBRed", camwbRed);
|
|
keyFile.set_double ("LiveThumbData", "CamWBGreen", camwbGreen);
|
|
keyFile.set_double ("LiveThumbData", "CamWBBlue", camwbBlue);
|
|
keyFile.set_double ("LiveThumbData", "RedAWBMul", redAWBMul);
|
|
keyFile.set_double ("LiveThumbData", "GreenAWBMul", greenAWBMul);
|
|
keyFile.set_double ("LiveThumbData", "BlueAWBMul", blueAWBMul);
|
|
keyFile.set_integer ("LiveThumbData", "AEHistCompression", aeHistCompression);
|
|
keyFile.set_double ("LiveThumbData", "RedMultiplier", redMultiplier);
|
|
keyFile.set_double ("LiveThumbData", "GreenMultiplier", greenMultiplier);
|
|
keyFile.set_double ("LiveThumbData", "BlueMultiplier", blueMultiplier);
|
|
keyFile.set_double ("LiveThumbData", "Scale", scale);
|
|
keyFile.set_double ("LiveThumbData", "DefaultGain", defGain);
|
|
keyFile.set_integer ("LiveThumbData", "ScaleForSave", scaleForSave);
|
|
keyFile.set_boolean ("LiveThumbData", "GammaCorrected", gammaCorrected);
|
|
Glib::ArrayHandle<double> cm ((double*)colorMatrix, 9, Glib::OWNERSHIP_NONE);
|
|
keyFile.set_double_list ("LiveThumbData", "ColorMatrix", cm);
|
|
|
|
FILE *f = safe_g_fopen (fname, "wt");
|
|
if (!f) {
|
|
if (options.rtSettings.verbose)
|
|
printf("Thumbnail::writeData / Error: unable to open file \"%s\" with write access!\n", fname.c_str());
|
|
return false;
|
|
}
|
|
else {
|
|
fprintf (f, "%s", keyFile.to_data().c_str());
|
|
fclose (f);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Thumbnail::readEmbProfile (const Glib::ustring& fname) {
|
|
|
|
FILE* f = safe_g_fopen (fname, "rb");
|
|
if (!f) {
|
|
embProfileData = NULL;
|
|
embProfile = NULL;
|
|
embProfileLength = 0;
|
|
}
|
|
else {
|
|
fseek (f, 0, SEEK_END);
|
|
embProfileLength = ftell (f);
|
|
fseek (f, 0, SEEK_SET);
|
|
embProfileData = new unsigned char[embProfileLength];
|
|
fread (embProfileData, 1, embProfileLength, f);
|
|
fclose (f);
|
|
embProfile = cmsOpenProfileFromMem (embProfileData, embProfileLength);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Thumbnail::writeEmbProfile (const Glib::ustring& fname) {
|
|
|
|
if (embProfileData) {
|
|
FILE* f = safe_g_fopen(fname, "wb");
|
|
if (f) {
|
|
fwrite (embProfileData, 1, embProfileLength, f);
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Thumbnail::readAEHistogram (const Glib::ustring& fname) {
|
|
|
|
FILE* f = safe_g_fopen (fname, "rb");
|
|
if (!f)
|
|
aeHistogram(0);
|
|
else {
|
|
aeHistogram(65536>>aeHistCompression);
|
|
fread (&aeHistogram[0], 1, (65536>>aeHistCompression)*sizeof(aeHistogram[0]), f);
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Thumbnail::writeAEHistogram (const Glib::ustring& fname) {
|
|
|
|
if (aeHistogram) {
|
|
FILE* f = safe_g_fopen (fname, "wb");
|
|
if (f) {
|
|
fwrite (&aeHistogram[0], 1, (65536>>aeHistCompression)*sizeof(aeHistogram[0]), f);
|
|
fclose (f);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned char* Thumbnail::getImage8Data() {
|
|
if (thumbImg && thumbImg->getType()==rtengine::sImage8) {
|
|
Image8* img8 = static_cast<Image8*>(thumbImg);
|
|
return img8->data;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
}
|