Files
rawTherapee/rtengine/imagedata.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

496 lines
16 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 "imagedata.h"
#include "iptcpairs.h"
#include <glib/gstdio.h>
#include "safegtk.h"
#ifndef GLIBMM_EXCEPTIONS_ENABLED
#include <memory>
#endif
using namespace rtengine;
extern "C" IptcData *iptc_data_new_from_jpeg_file (FILE* infile);
ImageMetaData* ImageMetaData::fromFile (const Glib::ustring& fname, RawMetaDataLocation* rml) {
return new ImageData (fname, rml);
}
ImageData::ImageData (Glib::ustring fname, RawMetaDataLocation* ri) {
size_t dotpos = fname.find_last_of ('.');
root = NULL;
iptc = NULL;
if (ri && (ri->exifBase>=0 || ri->ciffBase>=0)) {
FILE* f = safe_g_fopen (fname, "rb");
if (f) {
if (ri->exifBase>=0) {
root = rtexif::ExifManager::parse (f, ri->exifBase);
if (root) {
rtexif::Tag* t = root->getTag (0x83BB);
if (t)
iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ());
}
}
else if (ri->ciffBase>=0)
root = rtexif::ExifManager::parseCIFF (f, ri->ciffBase, ri->ciffLength);
fclose (f);
extractInfo ();
}
}
else if ((dotpos<fname.size()-3 && !fname.casefold().compare (dotpos, 4, ".jpg")) || (dotpos<fname.size()-4 && !fname.casefold().compare (dotpos, 5, ".jpeg"))) {
FILE* f = safe_g_fopen (fname, "rb");
if (f) {
root = rtexif::ExifManager::parseJPEG (f);
extractInfo ();
fclose (f);
FILE* ff = safe_g_fopen (fname, "rb");
iptc = iptc_data_new_from_jpeg_file (ff);
fclose (ff);
}
}
else if ((dotpos<fname.size()-3 && !fname.casefold().compare (dotpos, 4, ".tif")) || (dotpos<fname.size()-4 && !fname.casefold().compare (dotpos, 5, ".tiff"))) {
FILE* f = safe_g_fopen (fname, "rb");
if (f) {
root = rtexif::ExifManager::parseTIFF (f);
fclose (f);
extractInfo ();
if (root) {
rtexif::Tag* t = root->getTag (0x83BB);
if (t)
iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ());
}
}
}
else {
root = new rtexif::TagDirectory ();
shutter = 0;
aperture = 0;
iso_speed = 0;
lens = "Unknown";
make = "Unknown";
model = "Unknown";
orientation = "Unknown";
expcomp = 0;
focal_len = 0;
memset (&time, 0, sizeof(time));
}
}
void ImageData::extractInfo () {
if (!root)
return;
make = "";
model = "";
serial = "";
orientation = "";
expcomp = 0;
shutter = 0;
aperture = 0;
focal_len = focal_len35mm = 0;
focus_dist = 0;
iso_speed = 0;
memset (&time, 0, sizeof(time));
timeStamp = 0;
if (root->getTag ("Make")){
make = root->getTag ("Make")->valueToString ();
// same dcraw treatment
static const char *corp[] =
{ "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
"MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
"SAMSUNG", "Mamiya", "MOTOROLA", "Leaf" };
for (size_t i=0; i < (sizeof(corp)/sizeof(*corp)); i++)
if ( make.find( corp[i] ) != std::string::npos ){ /* Simplify company names */
make = corp[i];
break;
}
make.erase( make.find_last_not_of(' ')+1 );
}
if (root->getTag ("Model")){
model = root->getTag ("Model")->valueToString ();
std::size_t i=0;
if ( make.find("KODAK") != std::string::npos ){
if( (i = model.find(" DIGITAL CAMERA")) != std::string::npos ||
(i = model.find(" Digital Camera")) != std::string::npos ||
(i = model.find("FILE VERSION")) )
model.resize( i );
}
model.erase( model.find_last_not_of(' ')+1 );
//if( (i=model.find( make )) != std::string::npos )
if( !strncasecmp (model.c_str(), make.c_str(), make.size()) )
if( model.at( make.size() )==' ')
model.erase(0,make.size()+1);
if( model.find( "Digital Camera ") != std::string::npos )
model.erase(0,15);
}
if (root->getTag ("Orientation")){
orientation = root->getTag ("Orientation")->valueToString ();
}
rtexif::TagDirectory* exif = NULL;
if (root->getTag ("Exif"))
exif = root->getTag ("Exif")->getDirectory ();
if (exif) {
// standard exif tags
if (exif->getTag ("ShutterSpeedValue"))
shutter = exif->getTag ("ShutterSpeedValue")->toDouble ();
if (exif->getTag ("ExposureTime"))
shutter = exif->getTag ("ExposureTime")->toDouble ();
if (exif->getTag ("ApertureValue"))
aperture = exif->getTag ("ApertureValue")->toDouble ();
if (exif->getTag ("FNumber"))
aperture = exif->getTag ("FNumber")->toDouble ();
if (exif->getTag ("ExposureBiasValue"))
expcomp = exif->getTag ("ExposureBiasValue")->toDouble ();
if (exif->getTag ("FocalLength"))
focal_len = exif->getTag ("FocalLength")->toDouble ();
if (exif->getTag ("FocalLengthIn35mmFilm"))
focal_len35mm = exif->getTag ("FocalLengthIn35mmFilm")->toDouble ();
// Focus distance from EXIF or XMP. MakerNote ones are scattered and partly encrypted
int num=-3, denom=-3;
// First try, offical EXIF. Set by Adobe on some DNGs
rtexif::Tag* pDst=exif->getTag("SubjectDistance");
if (pDst) {
int num, denom;
pDst->toRational(num,denom);
} else {
// Second try, XMP data
char sXMPVal[64];
if (root->getXMPTagValue("aux:ApproximateFocusDistance",sXMPVal)) { sscanf(sXMPVal,"%d/%d",&num,&denom); }
}
if (num!=-3) {
if ((denom==1 && num>=10000) || num<0 || denom<0)
focus_dist=10000; // infinity
else if (denom>0) {
focus_dist=(float)num/denom;
}
}
if (exif->getTag ("ISOSpeedRatings"))
iso_speed = exif->getTag ("ISOSpeedRatings")->toDouble ();
if (exif->getTag ("DateTimeOriginal")) {
if (sscanf ((const char*)exif->getTag("DateTimeOriginal")->getValue(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) {
time.tm_year -= 1900;
time.tm_mon -= 1;
time.tm_isdst = -1;
timeStamp = mktime(&time);
}
}
rtexif::Tag *snTag = exif->findTag ("SerialNumber");
if(!snTag)
snTag = exif->findTag ("InternalSerialNumber");
if ( snTag )
serial = snTag->valueToString();
// guess lens...
lens = "Unknown";
// Sometimes (e.g. DNG) EXIF already contains lens data
if (root->findTag("MakerNote")) {
rtexif::TagDirectory* mnote = root->findTag("MakerNote")->getDirectory();
if (mnote && !make.compare (0, 5, "NIKON")) {
// ISO at max value supported, check manufacturer specific
if (iso_speed == 65535 || iso_speed == 0) {
rtexif::Tag* isoTag = mnote->getTagP("ISOInfo/ISO");
if (isoTag)
iso_speed = isoTag->toInt();
}
bool lensOk = false;
if (mnote->getTag ("LensData")) {
std::string ldata = mnote->getTag ("LensData")->valueToString ();
int pos;
if (ldata.size()>10 && (pos=ldata.find ("Lens = "))!=Glib::ustring::npos) {
lens = ldata.substr (pos + 7);
if (lens.compare (0, 7, "Unknown"))
lensOk = true;
else {
int pos = lens.find("$FL$"); // is there a placeholder for focallength?
if(pos != Glib::ustring::npos) { // then fill in focallength
lens = lens.replace(pos,4,exif->getTag ("FocalLength")->valueToString ());
if(mnote->getTag ("LensType")) {
std::string ltype = mnote->getTag ("LensType")->valueToString ();
if(ltype.find("MF = Yes")!=Glib::ustring::npos) // check, whether it's a MF lens, should be always
lens = lens.replace(0,7,"MF");
lensOk = true;
}
}
}
}
}
if (!lensOk && mnote->getTag ("Lens")) {
std::string ldata = mnote->getTag ("Lens")->valueToString ();
size_t i=0, j=0;
double n[4];
for (int m=0; m<4; m++) {
while (i<ldata.size() && ldata[i]!='/') i++;
int nom = atoi(ldata.substr(j, i).c_str());
j = i+1; i++;
while (i<ldata.size() && ldata[i]!=',') i++;
int den = atoi(ldata.substr(j, i).c_str());
j = i+2; i+=2;
n[m] = (double) nom/den;
}
std::ostringstream str;
if (n[0]==n[1])
str << "Unknown " << n[0] << "mm F/" << n[2];
else if (n[2]==n[3])
str << "Unknown " << n[0] << "-" << n[1] << "mm F/" << n[2];
else
str << "Unknown " << n[0] << "-" << n[1] << "mm F/" << n[2] << "-" << n[3];
lens = str.str();
// Look whether it's MF or AF
if(mnote->getTag ("LensType")) {
std::string ltype = mnote->getTag ("LensType")->valueToString ();
if(ltype.find("MF = Yes")!=Glib::ustring::npos) // check, whether it's a MF lens
lens = lens.replace(0,7,"MF"); // replace 'Unknwon' with 'MF'
else
lens = lens.replace(0,7,"AF"); // replace 'Unknwon' with 'AF'
}
}
}
else if (mnote && !make.compare (0, 5, "Canon")) {
// ISO at max value supported, check manufacturer specific
if (iso_speed == 65535 || iso_speed == 0) {
rtexif::Tag* baseIsoTag = mnote->getTagP("CanonShotInfo/BaseISO");
if (baseIsoTag)
iso_speed = baseIsoTag->toInt();
}
int found=false;
// canon EXIF have a string for lens model
rtexif::Tag *lt = mnote->getTag("LensType");
if ( lt ) {
std::string ldata = lt->valueToString ();
if (ldata.size()>1) {
found=true;
lens = "Canon " + ldata;
}
}
if( !found || lens.substr(lens.find(' ')).length() < 7 ){
lt = mnote->findTag("LensID");
if ( lt ) {
std::string ldata = lt->valueToString ();
if (ldata.size()>1) {
lens = ldata;
}
}
}
}
else if (mnote && !make.compare (0, 6, "PENTAX")) {
if (mnote->getTag ("LensType"))
lens = mnote->getTag ("LensType")->valueToString ();
// Try to get the FocalLength from the LensInfo structure, where length below 10mm will be correctly set
rtexif::Tag* flt=mnote->getTagP ("LensInfo/FocalLength");
if (flt)
focal_len = flt->toDouble ();
else if ((flt = mnote->getTagP ("FocalLength"))) {
rtexif::Tag* flt = mnote->getTag ("FocalLength");
focal_len = flt->toDouble ();
}
if (mnote->getTag ("FocalLengthIn35mmFilm"))
focal_len35mm = mnote->getTag ("FocalLengthIn35mmFilm")->toDouble ();
}
else if (mnote && (!make.compare (0, 4, "SONY") || !make.compare (0, 6, "KONICA"))) {
if (mnote->getTag ("LensID"))
lens = mnote->getTag ("LensID")->valueToString ();
}
else if (mnote && !make.compare (0, 7, "OLYMPUS")) {
if (mnote->getTag ("Equipment")) {
rtexif::TagDirectory* eq = mnote->getTag ("Equipment")->getDirectory ();
if (eq->getTag ("LensType"))
lens = eq->getTag ("LensType")->valueToString ();
}
}
} else if (exif->getTag ("DNGLensInfo")) {
lens = exif->getTag ("DNGLensInfo")->valueToString ();
} else if (exif->getTag ("LensModel")) {
lens = exif->getTag ("LensModel")->valueToString ();
} else if (exif->getTag ("LensInfo")) {
lens = exif->getTag ("LensInfo")->valueToString ();
}
}
}
ImageData::~ImageData () {
delete root;
if (iptc)
iptc_data_free (iptc);
}
const procparams::IPTCPairs ImageData::getIPTCData () const {
procparams::IPTCPairs iptcc;
if (!iptc)
return iptcc;
unsigned char buffer[2100];
for (int i=0; i<16; i++) {
IptcDataSet* ds = iptc_data_get_next_dataset (iptc, NULL, IPTC_RECORD_APP_2, strTags[i].tag);
if (ds) {
iptc_dataset_get_data (ds, buffer, 2100);
std::vector<Glib::ustring> icValues;
icValues.push_back (safe_locale_to_utf8((char*)buffer));
iptcc[strTags[i].field] = icValues;
iptc_dataset_unref (ds);
}
}
IptcDataSet* ds = NULL;
std::vector<Glib::ustring> keywords;
while ((ds=iptc_data_get_next_dataset (iptc, ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS))) {
iptc_dataset_get_data (ds, buffer, 2100);
keywords.push_back (safe_locale_to_utf8((char*)buffer));
}
iptcc["Keywords"] = keywords;
ds = NULL;
std::vector<Glib::ustring> suppCategories;
while ((ds=iptc_data_get_next_dataset (iptc, ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY))) {
iptc_dataset_get_data (ds, buffer, 2100);
suppCategories.push_back (safe_locale_to_utf8((char*)buffer));
iptc_dataset_unref (ds);
}
iptcc["SupplementalCategories"] = suppCategories;
return iptcc;
}
//------inherited functions--------------//
std::string ImageMetaData::apertureToString (double aperture) {
char buffer[256];
sprintf (buffer, "%0.1f", aperture);
return buffer;
}
std::string ImageMetaData::shutterToString (double shutter) {
char buffer[256];
if (shutter > 0.0 && shutter < 0.9)
sprintf (buffer, "1/%0.0f", 1.0 / shutter);
else
sprintf (buffer, "%0.1f", shutter);
return buffer;
}
std::string ImageMetaData::expcompToString (double expcomp, bool maskZeroexpcomp) {
char buffer[256];
if (maskZeroexpcomp==true){
if (expcomp!=0.0){
sprintf (buffer, "%0.2f", expcomp);
return buffer;
}
else
return "";
}
else{
sprintf (buffer, "%0.2f", expcomp);
return buffer;
}
}
double ImageMetaData::shutterFromString (std::string s) {
size_t i = s.find_first_of ('/');
if (i==std::string::npos)
return atof (s.c_str());
else
return atof (s.substr(0,i).c_str()) / atof (s.substr(i+1).c_str());
}
double ImageMetaData::apertureFromString (std::string s) {
return atof (s.c_str());
}
extern "C" {
#include <libiptcdata/iptc-data.h>
#include <libiptcdata/iptc-jpeg.h>
struct _IptcDataPrivate
{
unsigned int ref_count;
IptcLog *log;
IptcMem *mem;
};
IptcData *
iptc_data_new_from_jpeg_file (FILE *infile)
{
IptcData *d;
unsigned char * buf;
int buf_len = 256*256;
int len, offset;
unsigned int iptc_len;
if (!infile)
return NULL;
d = iptc_data_new ();
if (!d)
return NULL;
buf = (unsigned char*)iptc_mem_alloc (d->priv->mem, buf_len);
if (!buf) {
iptc_data_unref (d);
return NULL;
}
len = iptc_jpeg_read_ps3 (infile, buf, buf_len);
if (len <= 0) {
goto failure;
}
offset = iptc_jpeg_ps3_find_iptc (buf, len, &iptc_len);
if (offset <= 0) {
goto failure;
}
iptc_data_load (d, buf + offset, iptc_len);
iptc_mem_free (d->priv->mem, buf);
return d;
failure:
iptc_mem_free (d->priv->mem, buf);
iptc_data_unref (d);
return NULL;
}
}