1460 lines
42 KiB
C++
1460 lines
42 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <functional>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <tiff.h>
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <glibmm/convert.h>
|
|
|
|
#include "imagedata.h"
|
|
#include "imagesource.h"
|
|
#include "iptcpairs.h"
|
|
#include "procparams.h"
|
|
#include "rt_math.h"
|
|
#include "utils.h"
|
|
#include "../rtexif/rtexif.h"
|
|
|
|
#pragma GCC diagnostic warning "-Wextra"
|
|
#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", "?");
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T getFromFrame(
|
|
const std::vector<std::unique_ptr<FrameData>>& frames,
|
|
std::size_t frame,
|
|
const std::function<T (const FrameData&)>& function
|
|
)
|
|
{
|
|
if (frame < frames.size()) {
|
|
return function(*frames[frame]);
|
|
}
|
|
if (!frames.empty()) {
|
|
return function(*frames[0]);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
const std::string& validateUft8(const std::string& str, const std::string& on_error = "???")
|
|
{
|
|
if (Glib::ustring(str).validate()) {
|
|
return str;
|
|
}
|
|
|
|
return on_error;
|
|
}
|
|
|
|
}
|
|
|
|
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"),
|
|
rating(0),
|
|
lens("Unknown"),
|
|
sampleFormat(IIOSF_UNKNOWN),
|
|
isPixelShift(false),
|
|
isHDR(false)
|
|
{
|
|
if (!frameRootDir) {
|
|
return;
|
|
}
|
|
|
|
make.clear();
|
|
model.clear();
|
|
serial.clear();
|
|
orientation.clear();
|
|
lens.clear();
|
|
|
|
rtexif::TagDirectory* newFrameRootDir = frameRootDir;
|
|
|
|
rtexif::Tag* 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 = validateUft8(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 = validateUft8(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";
|
|
}
|
|
|
|
if (model == "Unknown") {
|
|
tag = newFrameRootDir->findTag("UniqueCameraModel");
|
|
if (tag) {
|
|
model = validateUft8(tag->valueToString());
|
|
}
|
|
}
|
|
|
|
tag = newFrameRootDir->findTagUpward("Orientation");
|
|
|
|
if (tag) {
|
|
orientation = validateUft8(tag->valueToString());
|
|
}
|
|
|
|
// Look for Rating metadata in the following order:
|
|
// 1. EXIF
|
|
// 2. XMP
|
|
// 3. pp3 sidecar file
|
|
tag = newFrameRootDir->findTagUpward("Rating");
|
|
if (tag && tag->toInt() != 0) {
|
|
rating = tag->toInt();
|
|
}
|
|
char sXMPRating[64];
|
|
if (newFrameRootDir->getXMPTagValue("xmp:Rating", sXMPRating)) {
|
|
// Guard against out-of-range values (<0, >5)
|
|
rating = rtengine::max(0, rtengine::min(5, atoi(sXMPRating)));
|
|
// Currently, Rating=-1 is not supported. A value of -1 should mean
|
|
// "Rejected" according to the specification. Maybe in the future, Rating=-1
|
|
// sets InTrash=true?
|
|
}
|
|
|
|
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, official EXIF. Set by Adobe on some DNGs
|
|
tag = exif->getTag("SubjectDistance");
|
|
|
|
if (tag) {
|
|
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->findTag("DateTimeOriginal", true))) {
|
|
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 = validateUft8(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 = validateUft8(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") {
|
|
const auto lens_from_make_and_model =
|
|
[this, exif]() -> bool
|
|
{
|
|
if (!exif) {
|
|
return false;
|
|
}
|
|
|
|
const rtexif::Tag* const lens_model = exif->getTag(0xA434);
|
|
|
|
if (lens_model) {
|
|
const rtexif::Tag* const lens_make = exif->getTag(0xA433);
|
|
const std::string make =
|
|
lens_make
|
|
? validateUft8(lens_make->valueToString())
|
|
: std::string();
|
|
const std::string model = validateUft8(lens_model->valueToString());
|
|
|
|
if (!model.empty()) {
|
|
lens = make;
|
|
|
|
if (!lens.empty()) {
|
|
lens += ' ';
|
|
}
|
|
|
|
lens += model;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
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 = validateUft8(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, validateUft8(exif->getTag("FocalLength")->valueToString()));
|
|
|
|
if (mnote->getTag("LensType")) {
|
|
const std::string ltype = validateUft8(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 MakeNotes are vague, fall back to Exif LensMake and LensModel if set
|
|
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#LensType
|
|
if (lens == "Manual Lens No CPU") {
|
|
lens_from_make_and_model();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!lensOk && mnote->getTag("Lens")) {
|
|
const std::string ldata = validateUft8(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")) {
|
|
const std::string ltype = validateUft8(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) {
|
|
if (lt->toInt()) {
|
|
const std::string ldata = validateUft8(lt->valueToString());
|
|
|
|
if (ldata.size() > 1) {
|
|
found = true;
|
|
lens = "Canon " + ldata;
|
|
}
|
|
} else {
|
|
found = lens_from_make_and_model();
|
|
}
|
|
}
|
|
|
|
const std::string::size_type first_space_pos = lens.find(' ');
|
|
const std::string::size_type remaining_size =
|
|
first_space_pos != std::string::npos
|
|
? lens.size() - first_space_pos
|
|
: 0;
|
|
|
|
if( !found || remaining_size < 7U ) {
|
|
lt = mnote->findTag("LensID");
|
|
|
|
if (lt) {
|
|
const std::string ldata = validateUft8(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) {
|
|
const rtexif::Tag* const baseIsoTag = mnote->getTag("ISO");
|
|
|
|
if (baseIsoTag) {
|
|
const std::string isoData = baseIsoTag->valueToString();
|
|
|
|
if (isoData.size() > 1) {
|
|
iso_speed = std::stoi(isoData);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mnote->getTag("LensType")) {
|
|
lens = validateUft8(mnote->getTag("LensType")->valueToString());
|
|
// If MakeNotes are vague, fall back to Exif LensMake and LensModel if set
|
|
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Pentax.html#LensType
|
|
if (lens == "M-42 or No Lens" || lens == "K or M Lens" || lens == "A Series Lens" || lens == "Sigma") {
|
|
lens_from_make_and_model();
|
|
}
|
|
} else {
|
|
lens_from_make_and_model();
|
|
}
|
|
|
|
// 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"))) {
|
|
focal_len = mnote->getTag("FocalLength")->toDouble ();
|
|
}
|
|
|
|
if (mnote->getTag("FocalLengthIn35mmFilm")) {
|
|
focal_len35mm = mnote->getTag("FocalLengthIn35mmFilm")->toDouble();
|
|
}
|
|
} else if (!make.compare (0, 4, "SONY") || !make.compare (0, 6, "KONICA")) {
|
|
if (mnote->getTag ("LensID")) {
|
|
lens = validateUft8(mnote->getTag("LensID")->valueToString());
|
|
if (lens == "Unknown") {
|
|
lens_from_make_and_model();
|
|
}
|
|
}
|
|
} else if (!make.compare(0, 7, "OLYMPUS")) {
|
|
if (mnote->getTag("Equipment")) {
|
|
rtexif::TagDirectory* eq = mnote->getTag("Equipment")->getDirectory();
|
|
|
|
if (eq->getTag("LensType")) {
|
|
lens = validateUft8(eq->getTag("LensType")->valueToString());
|
|
}
|
|
}
|
|
if (lens == "Unknown") {
|
|
lens_from_make_and_model();
|
|
}
|
|
} else if (!make.compare (0, 9, "Panasonic")) {
|
|
if (mnote->getTag ("LensType")) {
|
|
const std::string panalens = validateUft8(mnote->getTag("LensType")->valueToString());
|
|
|
|
if (panalens.find("LUMIX") != Glib::ustring::npos) {
|
|
lens = "Panasonic " + panalens;
|
|
} else {
|
|
lens = panalens;
|
|
}
|
|
}
|
|
}
|
|
} else if (exif->getTag("DNGLensInfo")) {
|
|
lens = validateUft8(exif->getTag("DNGLensInfo")->valueToString());
|
|
} else if (!lens_from_make_and_model() && exif->getTag ("LensInfo")) {
|
|
lens = validateUft8(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, samplesperpixel = 0, sampleformat = 0, photometric = 0, compression = 0;
|
|
const rtexif::Tag* const bps = frameRootDir->findTag("BitsPerSample");
|
|
const rtexif::Tag* const spp = frameRootDir->findTag("SamplesPerPixel");
|
|
const rtexif::Tag* const sf = frameRootDir->findTag("SampleFormat");
|
|
const rtexif::Tag* const pi = frameRootDir->findTag("PhotometricInterpretation");
|
|
const rtexif::Tag* const c = frameRootDir->findTag("Compression");
|
|
|
|
if (mnote && (!make.compare(0, 6, "PENTAX") || (!make.compare(0, 5, "RICOH") && !model.compare(0, 6, "PENTAX")))) {
|
|
const rtexif::Tag* const hdr = mnote->findTag("HDR");
|
|
|
|
if (hdr) {
|
|
if (hdr->toInt() > 0) {
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> \"HDR\" tag found\n");
|
|
#endif
|
|
}
|
|
} else {
|
|
const rtexif::Tag* const 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) {
|
|
const rtexif::Tag* const q = mnote->findTag("Quality");
|
|
if (q && (q->toInt() == 7 || q->toInt() == 8)) {
|
|
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();
|
|
samplesperpixel = spp->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) {
|
|
if (bitspersample==16) {
|
|
sampleFormat = IIOSF_FLOAT16;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (16-bit)\n", sampleFormat);
|
|
#endif
|
|
}
|
|
else if (bitspersample == 24) {
|
|
sampleFormat = IIOSF_FLOAT24;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (24-bit)\n", sampleFormat);
|
|
#endif
|
|
}
|
|
else if (bitspersample == 32) {
|
|
sampleFormat = IIOSF_FLOAT32;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (32-bit)\n", sampleFormat);
|
|
#endif
|
|
}
|
|
}
|
|
} else if (photometric == PHOTOMETRIC_CFA) {
|
|
if (sampleformat == SAMPLEFORMAT_IEEEFP) {
|
|
if (bitspersample == 16) {
|
|
sampleFormat = IIOSF_FLOAT16;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (16-bit)\n", sampleFormat);
|
|
#endif
|
|
}
|
|
else if (bitspersample == 24) {
|
|
sampleFormat = IIOSF_FLOAT24;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (24-bit)\n", sampleFormat);
|
|
#endif
|
|
}
|
|
else if (bitspersample == 32) {
|
|
sampleFormat = IIOSF_FLOAT32;
|
|
isHDR = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("HDR detected ! -> sampleFormat = %d (32-bit)\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 == 34892 || photometric == 32892 /* Linear RAW (see DNG spec ; 32892 seem to be a flaw from Sony's ARQ files) */) {
|
|
if (sampleformat == SAMPLEFORMAT_IEEEFP) {
|
|
sampleFormat = IIOSF_FLOAT32;
|
|
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;
|
|
|
|
if (mnote && (!make.compare(0, 4, "SONY")) && bitspersample >= 12 && samplesperpixel == 4) {
|
|
isPixelShift = true;
|
|
#if PRINT_HDR_PS_DETECTION
|
|
printf("PixelShift detected ! -> \"Make\" = SONY, bitsPerPixel > 8, samplesPerPixel == 4\n");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
std::string FrameData::getImageType () const
|
|
{
|
|
return isPixelShift ? "PS" : isHDR ? "HDR" : "STD";
|
|
}
|
|
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;
|
|
}
|
|
|
|
int FrameData::getRating () const
|
|
{
|
|
return rating;
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
bool FramesData::getPixelShift () const
|
|
{
|
|
// So far only Pentax and Sony provide multi-frame Pixel Shift files.
|
|
// Only the first frame contains the Pixel Shift tag
|
|
// If more brand have to be supported, this rule may need
|
|
// to evolve
|
|
|
|
return frames.empty() ? false : frames.at(0)->getPixelShift ();
|
|
}
|
|
bool FramesData::getHDR(unsigned int frame) const
|
|
{
|
|
// So far only Pentax provides 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.empty() || frame >= frames.size() ? false : frames.at(0)->getHDR();
|
|
}
|
|
|
|
std::string FramesData::getImageType (unsigned int frame) const
|
|
{
|
|
return frames.empty() || frame >= frames.size() ? "STD" : frames.at(0)->getImageType();
|
|
}
|
|
|
|
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)
|
|
*/
|
|
}
|
|
|
|
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 getFromFrame<bool>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.hasExif();
|
|
}
|
|
);
|
|
}
|
|
|
|
bool FramesData::hasIPTC(unsigned int frame) const
|
|
{
|
|
return getFromFrame<bool>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.hasIPTC();
|
|
}
|
|
);
|
|
}
|
|
|
|
tm FramesData::getDateTime(unsigned int frame) const
|
|
{
|
|
return getFromFrame<tm>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getDateTime();
|
|
}
|
|
);
|
|
}
|
|
|
|
time_t FramesData::getDateTimeAsTS(unsigned int frame) const
|
|
{
|
|
return getFromFrame<time_t>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getDateTimeAsTS();
|
|
}
|
|
);
|
|
}
|
|
|
|
int FramesData::getISOSpeed(unsigned int frame) const
|
|
{
|
|
return getFromFrame<int>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getISOSpeed();
|
|
}
|
|
);
|
|
}
|
|
|
|
double FramesData::getFNumber(unsigned int frame) const
|
|
{
|
|
return getFromFrame<double>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getFNumber();
|
|
}
|
|
);
|
|
}
|
|
|
|
double FramesData::getFocalLen(unsigned int frame) const
|
|
{
|
|
return getFromFrame<double>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getFocalLen();
|
|
}
|
|
);
|
|
}
|
|
|
|
double FramesData::getFocalLen35mm(unsigned int frame) const
|
|
{
|
|
return getFromFrame<double>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getFocalLen35mm();
|
|
}
|
|
);
|
|
}
|
|
|
|
float FramesData::getFocusDist(unsigned int frame) const
|
|
{
|
|
return getFromFrame<float>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getFocusDist();
|
|
}
|
|
);
|
|
}
|
|
|
|
double FramesData::getShutterSpeed(unsigned int frame) const
|
|
{
|
|
return getFromFrame<double>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getShutterSpeed();
|
|
}
|
|
);
|
|
}
|
|
|
|
double FramesData::getExpComp(unsigned int frame) const
|
|
{
|
|
return getFromFrame<double>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getExpComp();
|
|
}
|
|
);
|
|
}
|
|
|
|
std::string FramesData::getMake(unsigned int frame) const
|
|
{
|
|
return getFromFrame<std::string>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getMake();
|
|
}
|
|
);
|
|
}
|
|
|
|
std::string FramesData::getModel(unsigned int frame) const
|
|
{
|
|
return getFromFrame<std::string>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getModel();
|
|
}
|
|
);
|
|
}
|
|
|
|
std::string FramesData::getLens(unsigned int frame) const
|
|
{
|
|
return getFromFrame<std::string>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getLens();
|
|
}
|
|
);
|
|
}
|
|
|
|
std::string FramesData::getSerialNumber(unsigned int frame) const
|
|
{
|
|
return getFromFrame<std::string>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getSerialNumber();
|
|
}
|
|
);
|
|
}
|
|
|
|
std::string FramesData::getOrientation(unsigned int frame) const
|
|
{
|
|
return getFromFrame<std::string>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getOrientation();
|
|
}
|
|
);
|
|
}
|
|
|
|
int FramesData::getRating(unsigned int frame) const
|
|
{
|
|
return getFromFrame<int>(
|
|
frames,
|
|
frame,
|
|
[](const FrameData& frame_data)
|
|
{
|
|
return frame_data.getRating();
|
|
}
|
|
);
|
|
}
|
|
|
|
//------inherited functions--------------//
|
|
|
|
std::string FramesMetaData::apertureToString(double aperture)
|
|
{
|
|
|
|
char buffer[256];
|
|
snprintf(buffer, sizeof(buffer), "%0.1f", aperture);
|
|
return buffer;
|
|
}
|
|
|
|
std::string FramesMetaData::shutterToString(double shutter)
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
if (shutter > 0.0 && shutter <= 0.5) {
|
|
snprintf(buffer, sizeof(buffer), "1/%0.0f", 1.0 / shutter);
|
|
} else {
|
|
snprintf(buffer, sizeof(buffer), "%0.1f", shutter);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
std::string FramesMetaData::expcompToString(double expcomp, bool maskZeroexpcomp)
|
|
{
|
|
|
|
char buffer[256];
|
|
|
|
if (maskZeroexpcomp) {
|
|
if (expcomp != 0.0) {
|
|
snprintf(buffer, sizeof(buffer), "%0.2f", expcomp);
|
|
return buffer;
|
|
} else {
|
|
return "";
|
|
}
|
|
} else {
|
|
snprintf(buffer, sizeof(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) :
|
|
iptc(nullptr), dcrawFrameCount(0)
|
|
{
|
|
if (rml && (rml->exifBase >= 0 || rml->ciffBase >= 0)) {
|
|
FILE* f = g_fopen(fname.c_str(), "rb");
|
|
|
|
if (f) {
|
|
rtexif::ExifManager exifManager(f, std::move(rml), firstFrameOnly);
|
|
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) {
|
|
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
|
}
|
|
|
|
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) {
|
|
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
|
}
|
|
|
|
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();
|
|
roots = exifManager.roots;
|
|
|
|
// creating FrameData
|
|
for (auto currFrame : exifManager.frames) {
|
|
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|