707 lines
24 KiB
C++
707 lines
24 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/>.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iomanip>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <glibmm/ustring.h>
|
|
|
|
#include "../rtengine/noncopyable.h"
|
|
#include "../rtengine/rawmetadatalocation.h"
|
|
|
|
namespace Glib
|
|
{
|
|
class KeyFile;
|
|
}
|
|
namespace rtengine
|
|
{
|
|
|
|
namespace procparams
|
|
{
|
|
class ExifPairs;
|
|
}
|
|
|
|
}
|
|
|
|
class CacheImageData;
|
|
|
|
namespace rtexif
|
|
{
|
|
|
|
enum TagType {INVALID = 0, BYTE = 1, ASCII = 2, SHORT = 3, LONG = 4, RATIONAL = 5, SBYTE = 6, UNDEFINED = 7, SSHORT = 8, SLONG = 9, SRATIONAL = 10, FLOAT = 11, DOUBLE = 12, OLYUNDEF = 13, AUTO = 98, SUBDIR = 99};
|
|
enum ActionCode {
|
|
AC_DONTWRITE, // don't write it to the output
|
|
AC_WRITE, // write it to the output
|
|
AC_SYSTEM, // changed by RT (not editable/deletable) - don't write, don't show
|
|
AC_NEW, // new addition - write, don't show
|
|
|
|
AC_INVALID = 100, // invalid state
|
|
};
|
|
enum ByteOrder {UNKNOWN = 0, INTEL = 0x4949, MOTOROLA = 0x4D4D};
|
|
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
|
const ByteOrder HOSTORDER = INTEL;
|
|
#else
|
|
const enum ByteOrder HOSTORDER = MOTOROLA;
|
|
#endif
|
|
enum MNKind {NOMK, IFD, HEADERIFD, NIKON3, OLYMPUS2, FUJI, TABLESUBDIR};
|
|
|
|
bool extractLensInfo (const std::string &fullname, double &minFocal, double &maxFocal, double &maxApertureAtMinFocal, double &maxApertureAtMaxFocal);
|
|
|
|
unsigned short sget2 (unsigned char *s, ByteOrder order);
|
|
int sget4 (unsigned char *s, ByteOrder order);
|
|
unsigned short get2 (FILE* f, ByteOrder order);
|
|
int get4 (FILE* f, ByteOrder order);
|
|
void sset2 (unsigned short v, unsigned char *s, ByteOrder order);
|
|
void sset4 (int v, unsigned char *s, ByteOrder order);
|
|
float int_to_float (int i);
|
|
short int int2_to_signed (short unsigned int i);
|
|
|
|
struct TIFFHeader {
|
|
|
|
unsigned short byteOrder;
|
|
unsigned short fixed;
|
|
unsigned int ifdOffset;
|
|
};
|
|
|
|
class Tag;
|
|
class Interpreter;
|
|
|
|
/// Structure of information describing an Exif tag
|
|
struct TagAttrib {
|
|
int ignore; // =0: never ignore, =1: always ignore, =2: ignore if the subdir type is reduced image, =-1: end of table
|
|
ActionCode action;
|
|
int editable;
|
|
const TagAttrib* subdirAttribs; // !NULL if this tag points to a subdir
|
|
/** Numeric identifier of tag (or index inside DirectoryTable)
|
|
To avoid rewriting all the tables, and to address the problem of TagDirectoryTable with heterogeneous tag's type,
|
|
this parameter is now an unsigned int, where the leftmost 2 bytes represent the tag's type, which by default will be aqual
|
|
to 0 (INVALID). Only non null tag type will be used. See nikon attrib for an example
|
|
*/
|
|
unsigned short ID;
|
|
TagType type;
|
|
const char* name;
|
|
Interpreter* interpreter; // Call back hook
|
|
};
|
|
|
|
const TagAttrib* lookupAttrib (const TagAttrib* dir, const char* field);
|
|
|
|
/// A directory of tags
|
|
class TagDirectory
|
|
{
|
|
|
|
protected:
|
|
std::vector<Tag*> tags; // tags in the directory
|
|
const TagAttrib* attribs; // descriptor table to decode the tags
|
|
ByteOrder order; // byte order
|
|
TagDirectory* parent; // parent directory (NULL if root)
|
|
bool parseJPEG;
|
|
static Glib::ustring getDumpKey (int tagID, const Glib::ustring &tagName);
|
|
|
|
public:
|
|
TagDirectory ();
|
|
TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored = true, bool parseJpeg = true);
|
|
TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border);
|
|
virtual ~TagDirectory ();
|
|
|
|
inline ByteOrder getOrder () const
|
|
{
|
|
return order;
|
|
}
|
|
TagDirectory* getParent ()
|
|
{
|
|
return parent;
|
|
}
|
|
inline bool getParseJpeg() const
|
|
{
|
|
return parseJPEG;
|
|
}
|
|
TagDirectory* getRoot ();
|
|
inline int getCount () const
|
|
{
|
|
return tags.size ();
|
|
}
|
|
const TagAttrib* getAttrib (int id) const;
|
|
// Find a Tag by scanning the whole tag tree and stopping at the first occurrence
|
|
const TagAttrib* getAttrib (const char* name);
|
|
// Try to get the Tag at a given location. 'name' is a path relative to this directory (e.g. "LensInfo/FocalLength")
|
|
const TagAttrib* getAttribP (const char* name);
|
|
const TagAttrib* getAttribTable() const
|
|
{
|
|
return attribs;
|
|
}
|
|
// Find a Tag by scanning the whole tag tree and stopping at the first occurrence
|
|
Tag* getTag (const char* name) const;
|
|
// Try to get the Tag at a given location. 'name' is a path relative to this directory (e.g. "LensInfo/FocalLength")
|
|
Tag* getTagP (const char* name) const;
|
|
Tag* getTag (int ID) const;
|
|
|
|
// Try to get the Tag in the current directory and in subdirectories
|
|
// if lookUpward = true, it will scan the parents TagDirectory up to the root one,
|
|
// but w/o looking into their subdirs
|
|
Tag* findTag (const char* name, bool lookUpward = false) const;
|
|
// Find a all Tags with the given name by scanning the whole tag tree
|
|
std::vector<const Tag*> findTags (const char* name);
|
|
// Find a all Tags with the given ID by scanning the whole tag tree
|
|
std::vector<const Tag*> findTags (int ID);
|
|
// Try to get the Tag in the current directory and in parent directories
|
|
// (won't look into subdirs)
|
|
Tag* findTagUpward (const char* name) const;
|
|
bool getXMPTagValue (const char* name, char* value) const;
|
|
|
|
void keepTag (int ID);
|
|
void addTag (Tag* &a);
|
|
void addTagFront (Tag* &a);
|
|
void replaceTag (Tag* a);
|
|
inline Tag* getTagByIndex (int ix)
|
|
{
|
|
return tags[ix];
|
|
}
|
|
inline void setOrder (ByteOrder bo)
|
|
{
|
|
order = bo;
|
|
}
|
|
|
|
virtual int calculateSize ();
|
|
virtual int write (int start, unsigned char* buffer);
|
|
virtual TagDirectory* clone (TagDirectory* parent) const;
|
|
void applyChange (const std::string &field, const Glib::ustring &value);
|
|
|
|
void printAll (unsigned int level = 0) const; // reentrant debug function, keep level=0 on first call !
|
|
bool CPBDump (const Glib::ustring &commFName, const Glib::ustring &imageFName, const Glib::ustring &profileFName, const Glib::ustring &defaultPParams,
|
|
const CacheImageData* cfs, const bool flagMode, Glib::KeyFile *keyFile = nullptr, Glib::ustring tagDirName = "") const;
|
|
void sort ();
|
|
};
|
|
|
|
// a table of tags: id are offset from beginning and not identifiers
|
|
class TagDirectoryTable: public TagDirectory, public rtengine::NonCopyable
|
|
{
|
|
protected:
|
|
unsigned char *values; // Tags values are saved internally here
|
|
long zeroOffset; // Offset 0 (index 0) could be at an offset from values
|
|
long valuesSize; // Size of allocated memory
|
|
TagType defaultType; // Default type of all tags in this directory
|
|
public:
|
|
TagDirectoryTable();
|
|
TagDirectoryTable (TagDirectory* p, unsigned char *v, int memsize, int offs, TagType type, const TagAttrib* ta, ByteOrder border);
|
|
TagDirectoryTable (TagDirectory* p, FILE* f, int memsize, int offset, TagType type, const TagAttrib* ta, ByteOrder border);
|
|
~TagDirectoryTable() override;
|
|
int calculateSize () override;
|
|
int write (int start, unsigned char* buffer) override;
|
|
TagDirectory* clone (TagDirectory* parent) const override;
|
|
};
|
|
|
|
// a class representing a single tag
|
|
class Tag :
|
|
public rtengine::NonCopyable
|
|
{
|
|
|
|
protected:
|
|
unsigned short tag;
|
|
TagType type;
|
|
unsigned int count;
|
|
unsigned char* value;
|
|
int valuesize;
|
|
bool keep;
|
|
bool allocOwnMemory;
|
|
|
|
const TagAttrib* attrib;
|
|
TagDirectory* parent;
|
|
TagDirectory** directory;
|
|
MNKind makerNoteKind;
|
|
bool parseMakerNote (FILE* f, int base, ByteOrder bom );
|
|
|
|
public:
|
|
Tag (TagDirectory* parent, FILE* f, int base); // parse next tag from the file
|
|
Tag (TagDirectory* parent, const TagAttrib* attr);
|
|
Tag (TagDirectory* parent, const TagAttrib* attr, unsigned char *data, TagType t);
|
|
Tag (TagDirectory* parent, const TagAttrib* attr, int data, TagType t); // create a new tag from array (used
|
|
Tag (TagDirectory* parent, const TagAttrib* attr, const char* data); // create a new tag from array (used
|
|
|
|
~Tag ();
|
|
void initType (unsigned char *data, TagType type);
|
|
void initInt (int data, TagType t, int count = 1);
|
|
void initUserComment (const Glib::ustring &text);
|
|
void initString (const char* text);
|
|
void initSubDir ();
|
|
void initSubDir (TagDirectory* dir);
|
|
void initMakerNote (MNKind mnk, const TagAttrib* ta);
|
|
void initUndefArray (const char* data, int len);
|
|
void initLongArray (const char* data, int len);
|
|
void initRational (int num, int den);
|
|
|
|
static void swapByteOrder2 (unsigned char *buffer, int count);
|
|
|
|
// get basic tag properties
|
|
int getID () const
|
|
{
|
|
return tag;
|
|
}
|
|
int getCount () const
|
|
{
|
|
return count;
|
|
}
|
|
TagType getType () const
|
|
{
|
|
return (attrib && attrib->type > INVALID && attrib->type < AUTO) ? attrib->type : type;
|
|
}
|
|
unsigned char* getValue () const
|
|
{
|
|
return value;
|
|
}
|
|
signed char* getSignedValue () const
|
|
{
|
|
return reinterpret_cast<signed char*> (value);
|
|
}
|
|
const TagAttrib* getAttrib () const
|
|
{
|
|
return attrib;
|
|
}
|
|
inline ByteOrder getOrder () const
|
|
{
|
|
return parent ? parent->getOrder() : HOSTORDER;
|
|
}
|
|
inline TagDirectory* getParent () const
|
|
{
|
|
return parent;
|
|
}
|
|
int getValueSize () const
|
|
{
|
|
return valuesize;
|
|
}
|
|
bool getOwnMemory () const
|
|
{
|
|
return allocOwnMemory;
|
|
}
|
|
|
|
// read/write value
|
|
int toInt (int ofs = 0, TagType astype = INVALID) const;
|
|
void fromInt (int v);
|
|
double toDouble (int ofs = 0) const;
|
|
double* toDoubleArray (int ofs = 0) const;
|
|
void toRational (int& num, int& denom, int ofs = 0) const;
|
|
void toString (char* buffer, std::size_t size, int ofs = 0) const;
|
|
void fromString (const char* v, int size = -1);
|
|
void setInt (int v, int ofs = 0, TagType astype = LONG);
|
|
int getDistanceFrom (const TagDirectory *root);
|
|
|
|
// additional getter/setter for more comfortable use
|
|
std::string valueToString () const;
|
|
std::string nameToString (int i = 0);
|
|
void valueFromString (const std::string& value);
|
|
void userCommentFromString (const Glib::ustring& text);
|
|
|
|
// functions for writing
|
|
int calculateSize ();
|
|
int write (int offs, int dataOffs, unsigned char* buffer);
|
|
Tag* clone (TagDirectory* parent) const;
|
|
|
|
// to control if the tag shall be written
|
|
bool getKeep () const
|
|
{
|
|
return keep;
|
|
}
|
|
void setKeep (bool k)
|
|
{
|
|
keep = k;
|
|
}
|
|
|
|
// get subdirectory (there can be several, the last is NULL)
|
|
bool isDirectory () const
|
|
{
|
|
return directory != nullptr;
|
|
}
|
|
TagDirectory* getDirectory (int i = 0)
|
|
{
|
|
return (directory) ? directory[i] : nullptr;
|
|
}
|
|
|
|
MNKind getMakerNoteFormat () const
|
|
{
|
|
return makerNoteKind;
|
|
}
|
|
};
|
|
|
|
class ExifManager
|
|
{
|
|
|
|
Tag* saveCIFFMNTag (TagDirectory* root, int len, const char* name);
|
|
void parseCIFF (int length, TagDirectory* root);
|
|
void parse (bool isRaw, bool skipIgnored = true, bool parseJpeg = true);
|
|
|
|
public:
|
|
FILE* f;
|
|
std::unique_ptr<rtengine::RawMetaDataLocation> rml;
|
|
ByteOrder order;
|
|
bool onlyFirst; // Only first IFD
|
|
unsigned int IFDOffset;
|
|
std::vector<TagDirectory*> roots;
|
|
std::vector<TagDirectory*> frames;
|
|
|
|
ExifManager (FILE* fHandle, std::unique_ptr<rtengine::RawMetaDataLocation> _rml, bool onlyFirstIFD)
|
|
: f(fHandle), rml(std::move(_rml)), order(UNKNOWN), onlyFirst(onlyFirstIFD),
|
|
IFDOffset(0) {}
|
|
|
|
void setIFDOffset(unsigned int offset);
|
|
|
|
|
|
void parseRaw (bool skipIgnored = true);
|
|
void parseStd (bool skipIgnored = true);
|
|
void parseJPEG (int offset = 0); // offset: to extract exif data from a embedded preview/thumbnail
|
|
void parseTIFF (bool skipIgnored = true);
|
|
void parseCIFF ();
|
|
|
|
/// @brief Get default tag for TIFF
|
|
/// @param forthis The byte order will be taken from the given directory.
|
|
/// @return The ownership of the return tags is passed to the caller.
|
|
static std::vector<Tag*> getDefaultTIFFTags (TagDirectory* forthis);
|
|
static int createJPEGMarker (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, unsigned char* buffer);
|
|
static int createTIFFHeader (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, int bps, const char* profiledata, int profilelen, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize);
|
|
static int createPNGMarker(const TagDirectory *root, const rtengine::procparams::ExifPairs &changeList, int W, int H, int bps, const char *iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize);
|
|
};
|
|
|
|
class Interpreter
|
|
{
|
|
public:
|
|
Interpreter () {}
|
|
virtual ~Interpreter() {};
|
|
virtual std::string toString (const Tag* t) const
|
|
{
|
|
char buffer[1024];
|
|
t->toString (buffer, sizeof(buffer));
|
|
std::string s (buffer);
|
|
std::string::size_type p1 = s.find_first_not_of (' ');
|
|
|
|
if ( p1 == std::string::npos ) {
|
|
return s;
|
|
} else {
|
|
return s.substr (p1, s.find_last_not_of (' ') - p1 + 1);
|
|
}
|
|
}
|
|
virtual void fromString (Tag* t, const std::string& value)
|
|
{
|
|
if (t->getType() == SHORT || t->getType() == LONG) {
|
|
t->fromInt (atoi (value.c_str()));
|
|
} else {
|
|
t->fromString (value.c_str());
|
|
}
|
|
}
|
|
// Get the value as a double
|
|
virtual double toDouble (const Tag* t, int ofs = 0)
|
|
{
|
|
|
|
switch (t->getType()) {
|
|
case SBYTE:
|
|
return double (int (t->getSignedValue()[ofs]));
|
|
|
|
case BYTE:
|
|
return (double) ((int)t->getValue()[ofs]);
|
|
|
|
case ASCII:
|
|
return 0.0;
|
|
|
|
case SSHORT:
|
|
return (double)int2_to_signed (sget2 (t->getValue() + ofs, t->getOrder()));
|
|
|
|
case SHORT:
|
|
return (double) ((int)sget2 (t->getValue() + ofs, t->getOrder()));
|
|
|
|
case SLONG:
|
|
case LONG:
|
|
return (double) ((int)sget4 (t->getValue() + ofs, t->getOrder()));
|
|
|
|
case SRATIONAL: {
|
|
const double dividend = (int)sget4 (t->getValue() + ofs, t->getOrder());
|
|
const double divisor = (int)sget4 (t->getValue() + ofs + 4, t->getOrder());
|
|
return divisor == 0. ? 0. : dividend / divisor;
|
|
}
|
|
|
|
case RATIONAL: {
|
|
const double dividend = (uint32_t)sget4 (t->getValue() + ofs, t->getOrder());
|
|
const double divisor = (uint32_t)sget4 (t->getValue() + ofs + 4, t->getOrder());
|
|
return divisor == 0. ? 0. : dividend / divisor;
|
|
}
|
|
|
|
case FLOAT:
|
|
return double (sget4 (t->getValue() + ofs, t->getOrder()));
|
|
|
|
case UNDEFINED:
|
|
return 0.;
|
|
|
|
default:
|
|
return 0.; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
|
|
}
|
|
}
|
|
// Get the value as an int
|
|
virtual int toInt (const Tag* t, int ofs = 0, TagType astype = INVALID)
|
|
{
|
|
if (astype == INVALID || astype == AUTO) {
|
|
astype = t->getType();
|
|
}
|
|
|
|
switch (astype) {
|
|
case SBYTE:
|
|
return int (t->getSignedValue()[ofs]);
|
|
|
|
case BYTE:
|
|
return t->getValue()[ofs];
|
|
|
|
case ASCII:
|
|
return 0;
|
|
|
|
case SSHORT:
|
|
return (int)int2_to_signed (sget2 (t->getValue() + ofs, t->getOrder()));
|
|
|
|
case SHORT:
|
|
return (int)sget2 (t->getValue() + ofs, t->getOrder());
|
|
|
|
case SLONG:
|
|
case LONG:
|
|
return (int)sget4 (t->getValue() + ofs, t->getOrder());
|
|
|
|
case SRATIONAL: {
|
|
int a = (int)sget4 (t->getValue() + ofs + 4, t->getOrder());
|
|
return a == 0 ? 0 : (int)sget4 (t->getValue() + ofs, t->getOrder()) / a;
|
|
}
|
|
|
|
case RATIONAL: {
|
|
uint32_t a = (uint32_t)sget4 (t->getValue() + ofs + 4, t->getOrder());
|
|
return a == 0 ? 0 : (uint32_t)sget4 (t->getValue() + ofs, t->getOrder()) / a;
|
|
}
|
|
|
|
case FLOAT:
|
|
return (int)toDouble (t, ofs);
|
|
|
|
case UNDEFINED:
|
|
return 0;
|
|
|
|
default:
|
|
return 0; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
extern Interpreter stdInterpreter;
|
|
|
|
template<typename T = std::uint32_t>
|
|
class ChoiceInterpreter : public Interpreter
|
|
{
|
|
protected:
|
|
using Choices = std::map<T, std::string>;
|
|
using ChoicesIterator = typename Choices::const_iterator;
|
|
Choices choices;
|
|
public:
|
|
ChoiceInterpreter () {};
|
|
std::string toString (const Tag* t) const override
|
|
{
|
|
const typename std::map<T, std::string>::const_iterator r = choices.find(t->toInt());
|
|
|
|
if (r != choices.end()) {
|
|
return r->second;
|
|
} else {
|
|
char buffer[1024];
|
|
t->toString(buffer, sizeof(buffer));
|
|
return buffer;
|
|
}
|
|
}
|
|
};
|
|
|
|
template< class T >
|
|
class IntLensInterpreter : public Interpreter
|
|
{
|
|
protected:
|
|
typedef std::multimap< T, std::string> container_t;
|
|
typedef typename std::multimap< T, std::string>::const_iterator it_t;
|
|
typedef std::pair< T, std::string> p_t;
|
|
container_t choices;
|
|
|
|
virtual std::string guess (const T lensID, double focalLength, double maxApertureAtFocal, double *lensInfoArray) const
|
|
{
|
|
it_t r;
|
|
size_t nFound = choices.count ( lensID );
|
|
|
|
switch ( nFound ) {
|
|
case 0: { // lens Unknown
|
|
std::ostringstream s;
|
|
s << lensID;
|
|
return s.str();
|
|
}
|
|
|
|
case 1: // lens found
|
|
r = choices.find ( lensID );
|
|
return r->second;
|
|
|
|
default:
|
|
// More than one hit: we must guess
|
|
break;
|
|
}
|
|
|
|
std::string bestMatch ("Unknown");
|
|
double a1, a2, f1, f2;
|
|
|
|
/* FIRST TRY
|
|
*
|
|
* Get the lens info (min/man focal, min/max aperture) and compare them to the possible choice
|
|
*/
|
|
if (lensInfoArray) {
|
|
for ( r = choices.lower_bound ( lensID ); r != choices.upper_bound (lensID); ++r ) {
|
|
if ( !extractLensInfo ( r->second, f1, f2, a1, a2) ) {
|
|
continue;
|
|
}
|
|
|
|
if (f1 == lensInfoArray[0] && f2 == lensInfoArray[1] && a1 == lensInfoArray[2] && a2 == lensInfoArray[3])
|
|
// can't match better! we take this entry as being the one
|
|
{
|
|
return r->second;
|
|
}
|
|
}
|
|
|
|
// No lens found, we update the "unknown" string with the lens info values
|
|
if (lensInfoArray[0] == lensInfoArray[1]) {
|
|
bestMatch += Glib::ustring::compose (" (%1mm", int (lensInfoArray[0]));
|
|
} else {
|
|
bestMatch += Glib::ustring::compose (" (%1-%2mm", int (lensInfoArray[0]), int (lensInfoArray[1]));
|
|
}
|
|
|
|
if (lensInfoArray[2] == lensInfoArray[3]) {
|
|
bestMatch += Glib::ustring::compose (" f/%1)", Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[2]));
|
|
} else
|
|
bestMatch += Glib::ustring::compose (" f/%1-%2)",
|
|
Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[2]),
|
|
Glib::ustring::format (std::fixed, std::setprecision (1), lensInfoArray[3]));
|
|
}
|
|
|
|
/* SECOND TRY
|
|
*
|
|
* Choose the best match: thanks to exiftool by Phil Harvey
|
|
* first throws for "out of focal range" and lower or upper aperture of the lens compared to MaxApertureAtFocal
|
|
* if the lens is not constant aperture, calculate aprox. aperture of the lens at focalLength
|
|
* and compare with actual aperture.
|
|
*/
|
|
std::ostringstream candidates;
|
|
double deltaMin = 1000.;
|
|
|
|
for ( r = choices.lower_bound ( lensID ); r != choices.upper_bound (lensID); ++r ) {
|
|
double dif;
|
|
|
|
if ( !extractLensInfo ( r->second, f1, f2, a1, a2) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( f1 == 0. || a1 == 0.) {
|
|
continue;
|
|
}
|
|
|
|
if ( focalLength < f1 - .5 || focalLength > f2 + 0.5 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( maxApertureAtFocal > 0.1) {
|
|
double lensAperture;
|
|
|
|
if ( maxApertureAtFocal < a1 - 0.15 || maxApertureAtFocal > a2 + 0.15) {
|
|
continue;
|
|
}
|
|
|
|
if ( a1 == a2 || f1 == f2) {
|
|
lensAperture = a1;
|
|
} else {
|
|
lensAperture = exp ( log (a1) + (log (a2) - log (a1)) / (log (f2) - log (f1)) * (log (focalLength) - log (f1)) );
|
|
}
|
|
|
|
dif = std::abs (lensAperture - maxApertureAtFocal);
|
|
} else {
|
|
dif = 0;
|
|
}
|
|
|
|
if ( dif < deltaMin ) {
|
|
deltaMin = dif;
|
|
bestMatch = r->second;
|
|
}
|
|
|
|
if ( dif < 0.15) {
|
|
if ( candidates.tellp() ) {
|
|
candidates << "\n or " << r->second;
|
|
} else {
|
|
candidates << r->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !candidates.tellp() ) {
|
|
return bestMatch;
|
|
} else {
|
|
return candidates.str();
|
|
}
|
|
}
|
|
};
|
|
|
|
inline static int getTypeSize ( TagType type )
|
|
{
|
|
return ("11124811248484"[type < 14 ? type : 0] - '0');
|
|
}
|
|
|
|
extern const TagAttrib exifAttribs[];
|
|
extern const TagAttrib gpsAttribs[];
|
|
extern const TagAttrib iopAttribs[];
|
|
extern const TagAttrib ifdAttribs[];
|
|
extern const TagAttrib nikon2Attribs[];
|
|
extern const TagAttrib nikon3Attribs[];
|
|
extern const TagAttrib canonAttribs[];
|
|
extern const TagAttrib pentaxAttribs[];
|
|
extern const TagAttrib pentaxLensDataAttribs[];
|
|
extern const TagAttrib pentaxLensInfoQAttribs[];
|
|
extern const TagAttrib pentaxLensCorrAttribs[];
|
|
extern const TagAttrib pentaxAEInfoAttribs[];
|
|
extern const TagAttrib pentaxAEInfo2Attribs[];
|
|
extern const TagAttrib pentaxAEInfo3Attribs[];
|
|
extern const TagAttrib pentaxCameraSettingsAttribs[];
|
|
extern const TagAttrib pentaxFlashInfoAttribs[];
|
|
extern const TagAttrib pentaxSRInfoAttribs[];
|
|
extern const TagAttrib pentaxSRInfo2Attribs[];
|
|
extern const TagAttrib pentaxBatteryInfoAttribs[];
|
|
extern const TagAttrib pentaxCameraInfoAttribs[];
|
|
extern const TagAttrib fujiAttribs[];
|
|
extern const TagAttrib minoltaAttribs[];
|
|
extern const TagAttrib sonyAttribs[];
|
|
extern const TagAttrib sonyTag9405Attribs[];
|
|
extern const TagAttrib sonyCameraInfoAttribs[];
|
|
extern const TagAttrib sonyCameraInfo2Attribs[];
|
|
extern const TagAttrib sonyCameraSettingsAttribs[];
|
|
extern const TagAttrib sonyCameraSettingsAttribs2[];
|
|
extern const TagAttrib sonyCameraSettingsAttribs3[];
|
|
//extern const TagAttrib sonyDNGMakerNote[];
|
|
extern const TagAttrib olympusAttribs[];
|
|
extern const TagAttrib kodakIfdAttribs[];
|
|
void parseKodakIfdTextualInfo (Tag *textualInfo, Tag* exif);
|
|
extern const TagAttrib panasonicAttribs[];
|
|
extern const TagAttrib panasonicRawAttribs[];
|
|
|
|
}
|