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...
This commit is contained in:
495
rtengine/imagedata.cc
Normal file
495
rtengine/imagedata.cc
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user