Files
rawTherapee/rtengine/imagedata.cc
Hombre57 2ef064f7d4 Various bugfix (see #4008) :
1. RT could crash in some circumstances with single frame image
2. UNICODE UserComment now done when already existing in raw file
3. When editing UserComment in the ExifPanel, the Value filed is now
filled with the current value
2017-10-07 00:43:45 +02:00

1138 lines
35 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 <strings.h>
#include <glib/gstdio.h>
#include <tiff.h>
#include "imagedata.h"
#include "iptcpairs.h"
#include "imagesource.h"
#include "rt_math.h"
#define PRINT_HDR_PS_DETECTION 0
using namespace rtengine;
extern "C" IptcData *iptc_data_new_from_jpeg_file (FILE* infile);
namespace
{
Glib::ustring to_utf8 (const std::string& str)
{
try {
return Glib::locale_to_utf8 (str);
} catch (Glib::Error&) {
return Glib::convert_with_fallback (str, "UTF-8", "ISO-8859-1", "?");
}
}
}
FramesMetaData* FramesMetaData::fromFile (const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml, bool firstFrameOnly)
{
return new FramesData (fname, std::move(rml), firstFrameOnly);
}
FrameData::FrameData (rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir)
: frameRootDir(frameRootDir_), iptc(nullptr), time(), timeStamp(), iso_speed(0), aperture(0.), focal_len(0.), focal_len35mm(0.), focus_dist(0.f),
shutter(0.), expcomp(0.), make("Unknown"), model("Unknown"), orientation("Unknown"), lens("Unknown"),
sampleFormat(IIOSF_UNKNOWN), isPixelShift(false), isHDR(false)
{
memset (&time, 0, sizeof(time));
if (!frameRootDir) {
return;
}
rtexif::Tag* tag;
rtexif::TagDirectory* newFrameRootDir = frameRootDir;
memset(&time, 0, sizeof(time));
timeStamp = 0;
iso_speed = 0;
aperture = 0.0;
focal_len = 0.0;
focal_len35mm = 0.0;
focus_dist = 0.0f;
shutter = 0.0;
expcomp = 0.0;
make.clear();
model.clear();
serial.clear();
orientation.clear();
lens.clear();
tag = newFrameRootDir->findTag("Make");
if (!tag) {
newFrameRootDir = rootDir;
tag = newFrameRootDir->findTag("Make");
if (!tag) {
// For some raw files (like Canon's CR2 files), the metadata are contained in the first root directory
newFrameRootDir = firstRootDir;
tag = newFrameRootDir->findTag("Make");
}
}
if (tag) {
make = tag->valueToString();
// Same dcraw treatment
for (const auto& corp : {
"Canon",
"NIKON",
"EPSON",
"KODAK",
"Kodak",
"OLYMPUS",
"PENTAX",
"RICOH",
"MINOLTA",
"Minolta",
"Konica",
"CASIO",
"Sinar",
"Phase One",
"SAMSUNG",
"Mamiya",
"MOTOROLA",
"Leaf",
"Panasonic"
}) {
if (make.find(corp) != std::string::npos) { // Simplify company names
make = corp;
break;
}
}
make.erase(make.find_last_not_of(' ') + 1);
}
tag = newFrameRootDir->findTagUpward("Model");
if (tag) {
model = tag->valueToString();
}
if (!model.empty()) {
std::string::size_type i = 0;
if (
make.find("KODAK") != std::string::npos
&& (
(i = model.find(" DIGITAL CAMERA")) != std::string::npos
|| (i = model.find(" Digital Camera")) != std::string::npos
|| (i = model.find("FILE VERSION")) != std::string::npos
)
) {
model.resize(i);
}
model.erase(model.find_last_not_of(' ') + 1);
if (!strncasecmp(model.c_str(), make.c_str(), make.size())) {
if (model.size() >= make.size() && model[make.size()] == ' ') {
model.erase(0, make.size() + 1);
}
}
if (model.find( "Digital Camera ") != std::string::npos) {
model.erase(0, 15);
}
} else {
model = "Unknown";
}
tag = newFrameRootDir->findTagUpward("Orientation");
if (tag) {
orientation = tag->valueToString ();
}
tag = newFrameRootDir->findTagUpward("MakerNote");
rtexif::TagDirectory* mnote = nullptr;
if (tag) {
mnote = tag->getDirectory();
}
rtexif::TagDirectory* exif = nullptr;
tag = newFrameRootDir->findTagUpward("Exif");
if (tag) {
exif = tag->getDirectory ();
}
if (exif) {
// standard exif tags
if ((tag = exif->getTag ("ShutterSpeedValue"))) {
shutter = tag->toDouble ();
}
if ((tag = exif->getTag ("ExposureTime"))) {
shutter = tag->toDouble ();
}
if ((tag = exif->getTag ("ApertureValue"))) {
aperture = tag->toDouble ();
}
if ((tag = exif->getTag ("FNumber"))) {
aperture = tag->toDouble ();
}
if ((tag = exif->getTag ("ExposureBiasValue"))) {
expcomp = tag->toDouble ();
}
if ((tag = exif->getTag ("FocalLength"))) {
focal_len = tag->toDouble ();
}
if ((tag = exif->getTag ("FocalLengthIn35mmFilm"))) {
focal_len35mm = tag->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
tag = exif->getTag("SubjectDistance");
if (tag) {
int num, denom;
tag->toRational(num, denom);
} else {
// Second try, XMP data
char sXMPVal[64];
if (newFrameRootDir->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 ((tag = exif->getTag ("ISOSpeedRatings"))) {
iso_speed = tag->toDouble ();
}
if ((tag = exif->getTag ("DateTimeOriginal"))) {
if (sscanf ((const char*)tag->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);
}
}
tag = exif->findTag ("SerialNumber");
if(!tag) {
tag = exif->findTag ("InternalSerialNumber");
}
if (tag) {
serial = tag->valueToString();
}
// guess lens...
lens = "Unknown";
// Sometimes (e.g. DNG) EXIF already contains lens data
if(!make.compare (0, 8, "FUJIFILM")) {
if(exif->getTag ("LensModel")) {
lens = exif->getTag ("LensModel")->valueToString ();
}
} else if(!make.compare (0, 4, "SONY")) {
if (iso_speed == 65535 || iso_speed == 0) {
rtexif::Tag* isoTag = exif->getTag ("RecommendedExposureIndex");
if(isoTag) {
iso_speed = isoTag->toDouble();
}
}
}
if (lens == "Unknown") {
if (mnote) {
if (!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 ();
size_t 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 {
size_t 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] = {0.0};
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 / std::max(den,1);
}
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 (!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 (!make.compare (0, 6, "PENTAX") || (!make.compare (0, 5, "RICOH") && !model.compare (0, 6, "PENTAX"))) {
// ISO at max value supported, check manufacturer specific
if (iso_speed == 65535 || iso_speed == 0) {
rtexif::Tag* baseIsoTag = mnote->getTag("ISO");
if (baseIsoTag) {
std::string isoData = baseIsoTag->valueToString();
if (isoData.size() > 1) {
iso_speed = stoi(isoData);
}
}
}
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) {
// Don't replace Exif focal_len if Makernotes focal_len is 0
if (flt->toDouble() > 0) {
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 (!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 (mnote && !make.compare (0, 9, "Panasonic")) {
if (mnote->getTag ("LensType")) {
std::string panalens = mnote->getTag("LensType")->valueToString();
if (panalens.find("LUMIX") != Glib::ustring::npos) {
lens = "Panasonic " + panalens;
}
else {
lens = panalens;
}
}
}
} 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 ();
}
}
}
rtexif::Tag* t = newFrameRootDir->getTag(0x83BB);
if (t) {
iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ());
}
// ----------------------- Special file type detection (HDR, PixelShift) ------------------------
uint16 bitspersample = 0, sampleformat = 0, photometric = 0, compression = 0;
rtexif::Tag* bps = frameRootDir->findTag("BitsPerSample");
rtexif::Tag* spp = frameRootDir->findTag("SamplesPerPixel");
rtexif::Tag* sf = frameRootDir->findTag("SampleFormat");
rtexif::Tag* pi = frameRootDir->findTag("PhotometricInterpretation");
rtexif::Tag* c = frameRootDir->findTag("Compression");
if (mnote && (!make.compare (0, 6, "PENTAX") || (!make.compare (0, 5, "RICOH") && !model.compare (0, 6, "PENTAX")))) {
rtexif::Tag* hdr = mnote->findTag("HDR");
if (hdr) {
if (hdr->toInt() > 0 && hdr->toInt(2) > 0) {
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> \"HDR\" tag found\n");
#endif
}
} else {
rtexif::Tag* dm = mnote->findTag("DriveMode");
if (dm) {
char buffer[60];
dm->toString(buffer, 3);
buffer[3] = 0;
if (!strcmp(buffer, "HDR")) {
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> DriveMode = \"HDR\"\n");
#endif
}
}
}
if (!isHDR) {
rtexif::Tag* q = mnote->findTag("Quality");
if (q && q->toInt() == 7) {
isPixelShift = true;
#if PRINT_HDR_PS_DETECTION
printf("PixelShift detected ! -> \"Quality\" = 7\n");
#endif
}
}
}
sampleFormat = IIOSF_UNKNOWN;
if (!sf)
/*
* WARNING: This is a dirty hack!
* We assume that files which doesn't contain the TIFFTAG_SAMPLEFORMAT tag
* (which is the case with uncompressed TIFFs produced by RT!) are RGB files,
* but that may be not true. --- Hombre
*/
{
sampleformat = SAMPLEFORMAT_UINT;
} else {
sampleformat = sf->toInt();
}
if ((!bps & !spp) || !pi) {
return;
}
bitspersample = bps->toInt();
photometric = pi->toInt();
if (photometric == PHOTOMETRIC_LOGLUV) {
if (!c) {
compression = COMPRESSION_NONE;
} else {
compression = c->toInt();
}
}
if (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK) {
if (sampleformat == SAMPLEFORMAT_INT || sampleformat == SAMPLEFORMAT_UINT) {
if (bitspersample == 8) {
sampleFormat = IIOSF_UNSIGNED_CHAR;
} else if (bitspersample <= 16) {
sampleFormat = IIOSF_UNSIGNED_SHORT;
}
} else if (sampleformat == SAMPLEFORMAT_IEEEFP) {
/*
* Not yet supported
*
if (bitspersample==16) {
sampleFormat = IIOSF_HALF;
isHDR = true;
}*/
if (bitspersample == 32) {
sampleFormat = IIOSF_FLOAT;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
}
}
} else if (photometric == PHOTOMETRIC_CFA) {
if (sampleformat == SAMPLEFORMAT_IEEEFP) {
sampleFormat = IIOSF_FLOAT;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
} else if (sampleformat == SAMPLEFORMAT_INT || sampleformat == SAMPLEFORMAT_UINT) {
if (bitspersample == 8) { // shouldn't occur...
sampleFormat = IIOSF_UNSIGNED_CHAR;
} else if (bitspersample <= 16) {
sampleFormat = IIOSF_UNSIGNED_SHORT;
}
}
} else if (photometric == PHOTOMETRIC_LOGLUV) {
if (compression == COMPRESSION_SGILOG24) {
sampleFormat = IIOSF_LOGLUV24;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
} else if (compression == COMPRESSION_SGILOG) {
sampleFormat = IIOSF_LOGLUV32;
isHDR = true;
#if PRINT_HDR_PS_DETECTION
printf("HDR detected ! -> sampleFormat = %d\n", sampleFormat);
#endif
}
}
}
FrameData::~FrameData ()
{
if (iptc) {
iptc_data_free (iptc);
}
}
procparams::IPTCPairs FrameData::getIPTCData () const
{
return getIPTCData(iptc);
}
procparams::IPTCPairs FrameData::getIPTCData (IptcData* iptc_)
{
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_, nullptr, IPTC_RECORD_APP_2, strTags[i].tag);
if (ds) {
iptc_dataset_get_data (ds, buffer, 2100);
std::vector<Glib::ustring> icValues;
icValues.push_back (to_utf8((char*)buffer));
iptcc[strTags[i].field] = icValues;
iptc_dataset_unref (ds);
}
}
IptcDataSet* ds = nullptr;
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 (to_utf8((char*)buffer));
}
iptcc["Keywords"] = keywords;
ds = nullptr;
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 (to_utf8((char*)buffer));
iptc_dataset_unref (ds);
}
iptcc["SupplementalCategories"] = suppCategories;
return iptcc;
}
bool FrameData::getPixelShift () const
{
return isPixelShift;
}
bool FrameData::getHDR () const
{
return isHDR;
}
IIOSampleFormat FrameData::getSampleFormat () const
{
return sampleFormat;
}
rtexif::TagDirectory* FrameData::getExifData () const
{
return frameRootDir;
}
bool FrameData::hasExif () const
{
return frameRootDir && frameRootDir->getCount();
}
bool FrameData::hasIPTC () const
{
return iptc;
}
tm FrameData::getDateTime () const
{
return time;
}
time_t FrameData::getDateTimeAsTS () const
{
return timeStamp;
}
int FrameData::getISOSpeed () const
{
return iso_speed;
}
double FrameData::getFNumber () const
{
return aperture;
}
double FrameData::getFocalLen () const
{
return focal_len;
}
double FrameData::getFocalLen35mm () const
{
return focal_len35mm;
}
float FrameData::getFocusDist () const
{
return focus_dist;
}
double FrameData::getShutterSpeed () const
{
return shutter;
}
double FrameData::getExpComp () const
{
return expcomp;
}
std::string FrameData::getMake () const
{
return make;
}
std::string FrameData::getModel () const
{
return model;
}
std::string FrameData::getLens () const
{
return lens;
}
std::string FrameData::getSerialNumber () const
{
return serial;
}
std::string FrameData::getOrientation () const
{
return orientation;
}
void FramesData::setDCRawFrameCount (unsigned int frameCount)
{
dcrawFrameCount = frameCount;
}
unsigned int FramesData::getRootCount () const
{
return roots.size();
}
unsigned int FramesData::getFrameCount () const
{
return dcrawFrameCount ? dcrawFrameCount : frames.size();
}
FrameData *FramesData::getFrameData (int frame) const
{
return frames.empty() || frame >= frames.size() ? nullptr : frames.at(frame);
}
bool FramesData::getPixelShift (unsigned int frame) const
{
// So far only Pentax provide multi-frame HDR file.
// Only the first frame contains the HDR tag
// If more brand have to be supported, this rule may need
// to evolve
//return frames.at(frame)->getPixelShift ();
return frames.empty() || frame >= frames.size() ? false : frames.at(0)->getPixelShift ();
}
bool FramesData::getHDR (unsigned int frame) const
{
// So far only Pentax provide multi-frame HDR file.
// Only the first frame contains the HDR tag
// If more brand have to be supported, this rule may need
// to evolve
//return frames.at(frame)->getHDR ();
return frames.empty() || frame >= frames.size() ? false : frames.at(0)->getHDR ();
}
IIOSampleFormat FramesData::getSampleFormat (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? IIOSF_UNKNOWN : frames.at(frame)->getSampleFormat ();
}
rtexif::TagDirectory* FramesData::getFrameExifData (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? nullptr : frames.at(frame)->getExifData ();
}
rtexif::TagDirectory* FramesData::getBestExifData (ImageSource *imgSource, procparams::RAWParams *rawParams) const
{
rtexif::TagDirectory *td = nullptr;
if (frames.empty()) {
return nullptr;
}
if (imgSource && rawParams) {
eSensorType sensorType = imgSource->getSensorType();
unsigned int imgNum = 0;
if (sensorType == ST_BAYER) {
imgNum = rtengine::LIM<unsigned int>(rawParams->bayersensor.imageNum, 0, frames.size() - 1);
/*
// might exist someday ?
} else if (sensorType == ST_FUJI_XTRANS) {
imgNum = rtengine::LIM<unsigned int>(rawParams->xtranssensor.imageNum, 0, frames.size() - 1);
} else if (sensorType == ST_NONE && !imgSource->isRAW()) {
// standard image multiframe support should come here (when implemented in GUI)
*/
}
frames[imgNum]->getExifData ();
td = getFrameExifData (imgNum);
rtexif::Tag* makeTag;
if (td && (makeTag = td->findTag("Make", true))) {
td = makeTag->getParent();
} else {
td = getRootExifData(0);
}
}
return td;
}
rtexif::TagDirectory* FramesData::getRootExifData (unsigned int root) const
{
return roots.empty() || root >= roots.size() ? nullptr : roots.at(root);
}
procparams::IPTCPairs FramesData::getIPTCData (unsigned int frame) const
{
if (frame < frames.size() && frames.at(frame)->hasIPTC()) {
return frames.at(frame)->getIPTCData();
} else {
if (iptc) {
return FrameData::getIPTCData(iptc);
} else {
procparams::IPTCPairs emptyPairs;
return emptyPairs;
}
}
}
bool FramesData::hasExif (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? false : frames.at(frame)->hasExif ();
}
bool FramesData::hasIPTC (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? false : frames.at(frame)->hasIPTC ();
}
tm FramesData::getDateTime (unsigned int frame) const
{
if (frames.empty() || frame >= frames.size() ) {
tm emptytm = {0, 0, 0, 0, 0, 0, 0, 0, 0};
return emptytm;
} else {
return frames.at(frame)->getDateTime ();
}
}
time_t FramesData::getDateTimeAsTS(unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0 : frames.at(frame)->getDateTimeAsTS ();
}
int FramesData::getISOSpeed (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0 : frames.at(frame)->getISOSpeed ();
}
double FramesData::getFNumber (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0. : frames.at(frame)->getFNumber ();
}
double FramesData::getFocalLen (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0. : frames.at(frame)->getFocalLen ();
}
double FramesData::getFocalLen35mm (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0. : frames.at(frame)->getFocalLen35mm ();
}
float FramesData::getFocusDist (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0.f : frames.at(frame)->getFocusDist ();
}
double FramesData::getShutterSpeed (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0. : frames.at(frame)->getShutterSpeed ();
}
double FramesData::getExpComp (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? 0. : frames.at(frame)->getExpComp ();
}
std::string FramesData::getMake (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? std::string() : frames.at(frame)->getMake ();
}
std::string FramesData::getModel (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? std::string() : frames.at(frame)->getModel ();
}
std::string FramesData::getLens (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? std::string() : frames.at(frame)->getLens ();
}
std::string FramesData::getSerialNumber (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? std::string() : frames.at(frame)->getSerialNumber ();
}
std::string FramesData::getOrientation (unsigned int frame) const
{
return frames.empty() || frame >= frames.size() ? std::string() : frames.at(frame)->getOrientation ();
}
//------inherited functions--------------//
std::string FramesMetaData::apertureToString (double aperture)
{
char buffer[256];
sprintf (buffer, "%0.1f", aperture);
return buffer;
}
std::string FramesMetaData::shutterToString (double shutter)
{
char buffer[256];
if (shutter > 0.0 && shutter <= 0.5) {
sprintf (buffer, "1/%0.0f", 1.0 / shutter);
} else {
sprintf (buffer, "%0.1f", shutter);
}
return buffer;
}
std::string FramesMetaData::expcompToString (double expcomp, bool maskZeroexpcomp)
{
char buffer[256];
if (maskZeroexpcomp) {
if (expcomp != 0.0) {
sprintf (buffer, "%0.2f", expcomp);
return buffer;
} else {
return "";
}
} else {
sprintf (buffer, "%0.2f", expcomp);
return buffer;
}
}
double FramesMetaData::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 FramesMetaData::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 nullptr;
}
d = iptc_data_new ();
if (!d) {
return nullptr;
}
buf = (unsigned char*)iptc_mem_alloc (d->priv->mem, buf_len);
if (!buf) {
iptc_data_unref (d);
return nullptr;
}
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 nullptr;
}
}
FramesData::FramesData (const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml, bool firstFrameOnly, bool loadAll) :
iptc(nullptr), dcrawFrameCount (0)
{
if (rml && (rml->exifBase >= 0 || rml->ciffBase >= 0)) {
FILE* f = g_fopen (fname.c_str (), "rb");
if (f) {
const bool has_rml_exif_base = rml->exifBase >= 0;
rtexif::ExifManager exifManager (f, std::move(rml), firstFrameOnly);
if (has_rml_exif_base) {
if (exifManager.f && exifManager.rml) {
if (exifManager.rml->exifBase >= 0) {
exifManager.parseRaw ();
} else if (exifManager.rml->ciffBase >= 0) {
exifManager.parseCIFF ();
}
}
// copying roots
roots = exifManager.roots;
// creating FrameData
for (auto currFrame : exifManager.frames) {
FrameData* fd = new FrameData(currFrame, currFrame->getRoot(), roots.at(0));
frames.push_back(fd);
}
for (auto currRoot : roots) {
rtexif::Tag* t = currRoot->getTag(0x83BB);
if (t && !iptc) {
iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ());
break;
}
}
}
fclose (f);
}
} else if (hasJpegExtension(fname)) {
FILE* f = g_fopen (fname.c_str (), "rb");
if (f) {
rtexif::ExifManager exifManager (f, std::move(rml), true);
if (exifManager.f) {
exifManager.parseJPEG ();
roots = exifManager.roots;
for (auto currFrame : exifManager.frames) {
FrameData* fd = new FrameData(currFrame, currFrame->getRoot(), roots.at(0));
frames.push_back(fd);
}
rewind (exifManager.f); // Not sure this is necessary
iptc = iptc_data_new_from_jpeg_file (exifManager.f);
}
fclose (f);
}
} else if (hasTiffExtension(fname)) {
FILE* f = g_fopen (fname.c_str (), "rb");
if (f) {
rtexif::ExifManager exifManager (f, std::move(rml), firstFrameOnly);
exifManager.parseTIFF();
// creating FrameData
for (auto currFrame : exifManager.frames) {
FrameData* fd = new FrameData(currFrame, currFrame->getRoot(), roots.at(0));
frames.push_back(fd);
}
for (auto currRoot : roots) {
rtexif::Tag* t = currRoot->getTag(0x83BB);
if (t && !iptc) {
iptc = iptc_data_new_from_data ((unsigned char*)t->getValue (), (unsigned)t->getValueSize ());
break;
}
}
fclose (f);
}
}
}
FramesData::~FramesData ()
{
for (auto currRoot : roots) {
delete currRoot;
}
if (iptc) {
iptc_data_free (iptc);
}
}