rawTherapee/rtexif/rtexif.cc

2315 lines
80 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
* Some parts of the source code (e.g. ciff support) are taken from dcraw
* that is copyrighted by Dave Coffin
*
* 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 <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <sstream>
#include <stdint.h>
#include "../rtgui/cacheimagedata.h"
#include "rtexif.h"
#include "../rtengine/safegtk.h"
#include "../rtgui/version.h"
#include "../rtgui/ppversion.h"
using namespace std;
namespace rtexif {
Interpreter stdInterpreter;
//--------------- class TagDirectory ------------------------------------------
// this class is a collection (an array) of tags
//-----------------------------------------------------------------------------
#define TAG_SUBFILETYPE 0x00fe
TagDirectory::TagDirectory ()
: attribs(ifdAttribs), order(INTEL), parent(NULL) {}
TagDirectory::TagDirectory (TagDirectory* p, const TagAttrib* ta, ByteOrder border)
: attribs(ta), order(border), parent(p) {}
TagDirectory::TagDirectory (TagDirectory* p, FILE* f, int base, const TagAttrib* ta, ByteOrder border, bool skipIgnored)
: attribs(ta), order(border), parent(p)
{
int numOfTags = get2 (f, order);
if (numOfTags<=0 || numOfTags>1000) // KodakIfd has lots of tags, thus 1000 as the limit
return;
bool thumbdescr = false;
for (int i=0; i<numOfTags; i++) {
Tag* newTag = new Tag (this, f, base);
// filter out tags with unknown type
if ((int)newTag->getType()==0) {
delete newTag;
continue;
}
if (skipIgnored) {
int id = newTag->getID();
// detect and possibly ignore tags of directories belonging to the embedded thumbnail image
if (attribs==ifdAttribs && id==TAG_SUBFILETYPE && newTag->toInt()!=0)
thumbdescr = true;
const TagAttrib* attrib = getAttrib (id);
if (!attrib || attrib->ignore==1 || (thumbdescr && attrib->ignore==2))
delete newTag;
else
addTag (newTag);
} else addTag (newTag);
}
}
TagDirectory::~TagDirectory () {
for (size_t i=0; i<tags.size(); i++)
delete tags[i];
}
class CompareTags {
public:
int operator() (Tag* const& a, Tag* const& b) const {
return a->getID() < b->getID();
}
};
void TagDirectory::sort () {
std::sort (tags.begin(), tags.end(), CompareTags());
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->isDirectory())
for (int j=0; tags[i]->getDirectory(j); j++)
tags[i]->getDirectory(j)->sort ();
}
TagDirectory* TagDirectory::getRoot()
{
if(parent) return parent->getRoot();
else return this;
}
const TagAttrib* TagDirectory::getAttrib (int id) {
if (attribs)
for (int i=0; attribs[i].ignore!=-1; i++)
if (attribs[i].ID==id)
return &attribs[i];
return NULL;
}
const TagAttrib* TagDirectory::getAttrib (const char* name) {
if (attribs)
for (int i=0; attribs[i].ignore!=-1; i++)
if (!strcmp (attribs[i].name, name))
return &attribs[i];
return NULL;
}
const TagAttrib* TagDirectory::getAttribP (const char* name) {
if (attribs)
for (int i=0; attribs[i].ignore!=-1; i++) {
// Yeah, self made comparison!
const char *n = name;
const char *a = attribs[i].name;
while (*n && *a && *n==*a) { n++; a++; };
if (!*a && (!*n || *n=='/')) {
// we reached the end of the subpart of name and the end of attribs->name, so they match
if (*n=='/') {
Tag* tag = getTag (attribs[i].ID);
TagDirectory *tagDir;
if (attribs[i].subdirAttribs && tag && (tagDir=tag->getDirectory()))
return tagDir->getAttribP(n+1);
else
return NULL;
}
else
return &attribs[i];
}
}
return NULL;
}
void TagDirectory::printAll (unsigned int level) const {
// set the spacer prefix string
char prefixStr[level*4+1];
unsigned int i;
for (i=0; i<level*4; i++)
prefixStr[i] = ' ';
prefixStr[i] = '\0';
// recursively iterate over the tag list
for (size_t i=0; i<tags.size(); i++) {
std::string name = tags[i]->nameToString ();
if (tags[i]->isDirectory())
for (int j=0; tags[i]->getDirectory(j); j++) {
printf ("%s+-- DIRECTORY %s[%d]:\n", prefixStr, name.c_str(), j);
tags[i]->getDirectory(j)->printAll (level+1);
}
else
std::string value = tags[i]->valueToString ();
}
}
/** @brief Dump the TagDirectory and its sub-directories to the file 'fname'
*
* This method has been created to dump the metadata for the Custom Profile Builders.
* It contains an [RT General] section to communicate some parameters, then the TagDirectory follows.
*
* The key is composed as follow: "010F_Make", i.e. "tag number or ID _ tag name"
* Entries like:
*
* 927C_MakerNotesSony=$subdir
*
* indicates that this tag refer to a sub-directory. RT's Keywords begins with $, where & is the first char of the value.
* $subdir is the only keyword so far.
*
* You'll have then to check for the [EXIF/927C_MakerNotesSony] section, given that the root section
* is named [EXIF].
*
* WARNING: Some string will be sanitized, i.e. the new line char will be replaced by "\n". You'll
* have to check for this escape string if you want a correct display of the value, but your KeyFile module
* will most likely handle that automatically for you.
*
* @param commFNname Absolute path of the temporary communication file's name
* @param commFNname Absolute path of the image's file name
* @param commFNname Absolute path of the output profiles's file name
* @param defaultPParams absolute or relative path (to the application's folder) of the default ProcParams to use
* @param cfs pointer to a CacheImageData object that will contain common values
* @param flagMode will tell whether the Custom Profile Builder is called for on flagging event or for real development
* @param keyfile The KeyFile object to dump to. Has to be NULL (default value) on first call!
* @param tagDirName Name of the current TagDirectory (full path, i.e. "EXIF/MakerNotes/LensInfo"). Can be empty on first call, "EXIF" will then be used
*
* @return True if everything went fine, false otherwise
*/
bool TagDirectory::CPBDump (const Glib::ustring &commFName, const Glib::ustring &imageFName, const Glib::ustring &profileFName, const Glib::ustring &defaultPParams, const CacheImageData* cfs, const bool flagMode,
rtengine::SafeKeyFile *keyFile, Glib::ustring tagDirName) const
{
rtengine::SafeKeyFile *kf;
if (!keyFile)
kf = new rtengine::SafeKeyFile();
else
kf = keyFile;
if (!kf)
return false;
if (!keyFile || tagDirName.empty())
tagDirName = "EXIF";
std::vector<const TagDirectory *> tagDirList;
std::vector<Glib::ustring> tagDirPaths;
FILE *f;
if (!keyFile) {
// open the file in write mode
f = safe_g_fopen (commFName, "wt");
if (f==NULL) {
printf("TagDirectory::keyFileDump(\"%s\") >>> Error: unable to open file with write access!\n", commFName.c_str());
delete kf;
return false;
}
kf->set_string ("RT General", "CachePath", options.cacheBaseDir);
kf->set_string ("RT General", "AppVersion", VERSION);
kf->set_integer("RT General", "ProcParamsVersion", PPVERSION);
kf->set_string ("RT General", "ImageFileName", imageFName);
kf->set_string ("RT General", "OutputProfileFileName", profileFName);
kf->set_string ("RT General", "DefaultProcParams", defaultPParams);
kf->set_boolean("RT General", "FlaggingMode", flagMode);
kf->set_double ("Common Data", "FNumber", cfs->fnumber);
kf->set_double ("Common Data", "Shutter", cfs->shutter);
kf->set_double ("Common Data", "FocalLength", cfs->focalLen);
kf->set_integer("Common Data", "ISO", cfs->iso);
kf->set_string ("Common Data", "Lens", cfs->lens);
kf->set_string ("Common Data", "Make", cfs->camMake);
kf->set_string ("Common Data", "Model", cfs->camModel);
}
// recursively iterate over the tag list
for (size_t i=0; i<tags.size(); i++) {
std::string tagName = tags[i]->nameToString ();
if (tags[i]->isDirectory())
for (int j=0; tags[i]->getDirectory(j); j++) {
// Accumulating the TagDirectories to dump later
tagDirPaths.push_back( Glib::ustring( tagDirName + "/" + getDumpKey(tags[i]->getID(), tagName) ) );
tagDirList.push_back(tags[i]->getDirectory(j));
kf->set_string (tagDirName, getDumpKey(tags[i]->getID(), tagName), "$subdir");
}
else {
kf->set_string (tagDirName, getDumpKey(tags[i]->getID(), tagName), tags[i]->valueToString());
}
}
// dumping the sub-directories
for (size_t i=0; i< tagDirList.size(); i++)
tagDirList.at(i)->CPBDump(commFName, imageFName, profileFName, defaultPParams, cfs, flagMode, kf, tagDirPaths.at(i));
if (!keyFile) {
fprintf (f, "%s", kf->to_data().c_str());
fclose (f);
delete kf;
}
return true;
}
Glib::ustring TagDirectory::getDumpKey (int tagID, const Glib::ustring tagName) {
Glib::ustring key;
if (options.CPBKeys == CPBKT_TID || options.CPBKeys == CPBKT_TID_NAME)
key = Glib::ustring(Glib::ustring::format(std::fixed, std::hex, std::setfill(L'0'), std::setw(4), tagID));
if (options.CPBKeys == CPBKT_TID_NAME)
key += Glib::ustring("_");
if (options.CPBKeys == CPBKT_TID_NAME || options.CPBKeys == CPBKT_NAME)
key += Glib::ustring(tagName);
return key;
}
void TagDirectory::addTag (Tag* tag) {
// look up if it already exists:
if (getTag (tag->getID()))
delete tag;
else
tags.push_back (tag);
}
void TagDirectory::addTagFront (Tag* tag) {
// look up if it already exists:
if (getTag (tag->getID()))
delete tag;
else
tags.insert (tags.begin(), tag);
}
void TagDirectory::replaceTag (Tag* tag) {
// look up if it already exists:
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->getID()==tag->getID()) {
delete tags[i];
tags[i] = tag;
return;
}
tags.push_back (tag);
}
Tag* TagDirectory::getTag (int ID) const {
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->getID()==ID)
return tags[i];
return NULL;
}
Tag* TagDirectory::getTag (const char* name) const {
if (attribs) {
for (int i=0; attribs[i].ignore!=-1; i++)
if (!strcmp (attribs[i].name, name))
return getTag (attribs[i].ID);
}
return NULL;
}
Tag* TagDirectory::getTagP (const char* name) const {
if (attribs)
for (int i=0; attribs[i].ignore!=-1; i++) {
// Yeah, self made comparison!
const char *n = name;
const char *a = attribs[i].name;
while (*n && *a && *n==*a) { n++; a++; };
if (!*a && (!*n || *n=='/')) {
// we reached the end of the subpart of name and the end of attribs->name, so they match
if (*n=='/') {
Tag* tag = getTag (attribs[i].ID);
TagDirectory *tagDir;
if (attribs[i].subdirAttribs && tag && (tagDir=tag->getDirectory()))
return tagDir->getTagP(n+1);
else
return NULL;
}
else
return getTag (attribs[i].ID);
}
}
return NULL;
}
Tag* TagDirectory::findTag (const char* name) const {
if (attribs) {
for (int i=0; attribs[i].ignore!=-1; i++)
if (!strcmp (attribs[i].name, name)){
Tag* t= getTag (attribs[i].ID);
if(t) return t;
else break;
}
}
for (size_t i=0; i<tags.size(); i++)
if(tags[i]->isDirectory()){
TagDirectory *dir = tags[i]->getDirectory();
Tag* t=dir->findTag(name);
if(t) return t;
}
return NULL;
}
// Searches a simple value, as either attribute or element
// only for simple values, not for entries with special chars or free text
bool TagDirectory::getXMPTagValue(const char* name, char* value) const {
*value=0;
if (!getTag("ApplicationNotes")) return false;
char *sXMP = (char*)getTag("ApplicationNotes")->getValue();
// Check for full word
char *pos=sXMP;
bool found=false;
do {
pos=strstr(pos,name);
if (pos) {
char nextChar=*(pos+strlen(name));
if (nextChar==' ' || nextChar=='>' || nextChar=='=')
found=true;
else
pos+=strlen(name);
}
} while (pos && !found);
if (!found) return false;
char *posTag =strchr(pos,'>');
char *posAttr=strchr(pos,'"');
if (!posTag && !posAttr) return false;
if (posTag && (!posAttr || posTag<posAttr)) {
// Tag
pos=strchr(posTag+1,'<');
strncpy(value,posTag+1,pos-posTag-1);
value[pos-posTag-1]=0;
return true;
} else
if (posAttr && (!posTag || posAttr<posTag)) {
// Attribute
pos=strchr(posAttr+1,'"');
strncpy(value,posAttr+1,pos-posAttr-1);
value[pos-posAttr-1]=0;
return true;
} else return false;
}
void TagDirectory::keepTag (int ID) {
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->getID()==ID) tags[i]->setKeep(true);
}
int TagDirectory::calculateSize () {
int size = 2; // space to store the number of tags
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->getKeep())
size += 12 + tags[i]->calculateSize ();
size += 4; // next ifd pointer
return size;
}
TagDirectory* TagDirectory::clone (TagDirectory* parent) {
TagDirectory* td = new TagDirectory (parent, attribs, order);
for (size_t i=0; i<tags.size(); i++)
td->tags.push_back (tags[i]->clone (td));
return td;
}
int TagDirectory::write (int start, unsigned char* buffer) {
int size = calculateSize ();
int tagnum = 0;
int nondirspace = 0;
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->getKeep()) {
tagnum++;
if (!tags[i]->isDirectory())
nondirspace += tags[i]->calculateSize();
}
int nextValOffs = start + 2 + tagnum * 12 + 4;
int nextDirOffs = nextValOffs + nondirspace;
int pos = start;
sset2 (tagnum, buffer+start, order);
pos += 2;
int maxPos = start + size;
for (size_t i=0; i<tags.size(); i++) {
if (tags[i]->getKeep()) {
if (!tags[i]->isDirectory())
nextValOffs = tags[i]->write (pos, nextValOffs, buffer); // pos: where to put the tag, dataoffset: the place where the value can be put. return: next data offset
else
nextDirOffs = tags[i]->write (pos, nextDirOffs, buffer); // pos: where to put the tag, dataoffset: the place where the value can be put. return: next data offset
pos += 12;
}
}
sset4 (0, buffer+pos, order);
return maxPos;
}
void TagDirectory::applyChange (std::string name, std::string value) {
std::string::size_type dp = name.find_first_of ('.');
std::string fseg = name.substr (0,dp);
// this is a final segment: apply change
if (dp==std::string::npos) {
Tag* t = NULL;
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->nameToString()==fseg) {
t = tags[i];
break;
}
if (value=="#keep" && t)
t->setKeep (true);
else if (value=="#delete" && t)
t->setKeep (false);
else if (t && !t->isDirectory())
t->valueFromString (value);
else {
const TagAttrib* attrib = NULL;
for (int i=0; attribs[i].ignore!=-1; i++)
if (!strcmp (attribs[i].name, fseg.c_str())) {
attrib = &attribs[i];
break;
}
if (attrib) {
Tag* nt = new Tag (this, attrib);
nt->initString (value.c_str());
addTag (nt);
}
}
}
// this is a subdirectory
else {
// try to find it
std::string::size_type dp1 = fseg.find_first_of ('[');
std::string basename = fseg.substr (0,dp1);
Tag* t = NULL;
int dirnum = -1;
for (size_t i=0; i<tags.size(); i++)
if (tags[i]->isDirectory()) {
for (int j=0; tags[i]->getDirectory(j); j++) {
if (tags[i]->nameToString(j) == fseg) {
t = tags[i];
dirnum = j;
break;
}
}
if (!t && tags[i]->nameToString() == basename) { // found it, but that directory index does not exist
t = tags[i];
dirnum = -1;
}
}
if (!t && value!="#keep" && value!="#delete") {
const TagAttrib* attrib = NULL;
for (int i=0; attribs[i].ignore!=-1; i++)
if (!strcmp (attribs[i].name, fseg.c_str())) {
attrib = &attribs[i];
break;
}
if (attrib && attrib->subdirAttribs) {
t = new Tag (this, attrib);
t->initSubDir ();
addTag (t);
}
dirnum = 0;
}
if (t && dirnum>=0)
t->getDirectory(dirnum)->applyChange (name.substr (dp+1, std::string::npos), value);
}
}
TagDirectoryTable::TagDirectoryTable ()
:zeroOffset(0),valuesSize(0)
{
}
TagDirectoryTable::TagDirectoryTable (TagDirectory* p, unsigned char *v,int memsize,int offs, TagType type, const TagAttrib* ta, ByteOrder border)
:TagDirectory(p,ta,border),zeroOffset(offs),valuesSize(memsize),defaultType( type )
{
values = new unsigned char[valuesSize];
memcpy(values,v,valuesSize);
// Security ; will avoid to read above the buffer limit if the RT's tagDirectoryTable is longer that what's in the file
int count = valuesSize/getTypeSize(type);
for(const TagAttrib* tattr = ta; tattr->ignore != -1 && tattr->ID<count; ++tattr){
Tag* newTag = new Tag (this, tattr, (values + zeroOffset+ tattr->ID*getTypeSize(type)), tattr->type == AUTO ? type : tattr->type);
tags.push_back(newTag); // Here we can insert more tag in the same offset because of bitfield meaning
}
}
TagDirectoryTable::TagDirectoryTable (TagDirectory* p, FILE* f, int memsize,int offs, TagType type, const TagAttrib* ta, ByteOrder border)
:TagDirectory(p,ta,border),zeroOffset(offs),valuesSize(memsize),defaultType( type )
{
values = new unsigned char[valuesSize];
fread (values, 1, valuesSize, f);
// Security ; will avoid to read above the buffer limit if the RT's tagDirectoryTable is longer that what's in the file
int count = valuesSize/getTypeSize(type);
for(const TagAttrib* tattr = ta; tattr->ignore != -1 && tattr->ID<count; ++tattr){
Tag* newTag = new Tag (this, tattr, (values + zeroOffset+ tattr->ID*getTypeSize(type)), tattr->type == AUTO ? type : tattr->type);
tags.push_back(newTag); // Here we can insert more tag in the same offset because of bitfield meaning
}
}
TagDirectory* TagDirectoryTable::clone (TagDirectory* parent) {
TagDirectory* td = new TagDirectoryTable (parent,values,valuesSize,zeroOffset,defaultType, attribs, order);
return td;
}
TagDirectoryTable::~TagDirectoryTable()
{
if(values)
delete [] values;
}
int TagDirectoryTable::calculateSize ()
{
return valuesSize;
}
int TagDirectoryTable::write (int start, unsigned char* buffer) {
if( values && valuesSize){
memcpy(buffer+start,values,valuesSize);
return start+valuesSize;
}else
return start;
}
//--------------- class Tag ---------------------------------------------------
// this class represents a tag stored in the directory
//-----------------------------------------------------------------------------
Tag::Tag (TagDirectory* p, FILE* f, int base)
: type(INVALID), count(0), value(NULL), allocOwnMemory(true), attrib(NULL), parent(p), directory(NULL) {
ByteOrder order = getOrder();
tag = get2 (f, order);
type = (TagType)get2 (f, order);
count = get4 (f, order);
if (!count) count = 1;
makerNoteKind = NOMK;
keep = false;
// filter out invalid tags
// note the large count is to be able to pass LeafData ASCII tag which can be up to almost 10 megabytes,
// (only a small part of it will actually be parsed though)
if ((int)type<1 || (int)type>14 || count>10*1024*1024) {
type = INVALID;
return;
}
// store next Tag's position in file
int save = ftell(f) + 4;
// load value field (possibly seek before)
valuesize = count * getTypeSize(type);
if (valuesize > 4)
fseek (f, get4(f, getOrder()) + base, SEEK_SET);
attrib = parent->getAttrib (tag);
if (attrib && (attrib->action==AC_WRITE || attrib->action==AC_NEW))
keep = true;
if( tag == 0xc634 ){ // DNGPrivateData
int currPos = ftell(f);
char buffer[32],*p=buffer;
while( fread (p, 1, 1, f ) && *p != 0 && p-buffer<sizeof(buffer)-1 )p++;
*p=0;
if( !strncmp(buffer,"Adobe",5) ){
fread (buffer, 1, 14, f );
if( !strncmp( buffer,"MakN",4) ){
ByteOrder bom = ((buffer[8]=='M' && buffer[9]=='M')?MOTOROLA:INTEL) ;
Tag* tmake = parent->getRoot()->findTag("Make");
std::string make( tmake ? tmake->valueToString():"");
int save = ftell(f);
int originalOffset = sget4( (unsigned char*)&buffer[10], ( make.find("SONY") != std::string::npos ) || ( make.find("Canon") != std::string::npos ) || ( make.find("OLYMPUS") != std::string::npos ) ?MOTOROLA:bom );
if( !parseMakerNote(f, save - originalOffset , bom ))
type = INVALID;
}
}else if( !strncmp(buffer,"PENTAX",6) ){
makerNoteKind = HEADERIFD;
fread (buffer, 1, 2, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, currPos, pentaxAttribs, strncmp(buffer,"MM",2)? INTEL:MOTOROLA);
directory[1] = NULL;
}else
/* SONY uses this tag to write hidden info and pointer to private encrypted tags
{
unsigned offset =sget4((unsigned char*)buffer, order);
fseek(f,offset,SEEK_SET);
makerNoteKind = TABLESUBDIR;
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, sonyDNGMakerNote, order);
directory[1] = NULL;
fseek (f, save, SEEK_SET);
return;
}*/
type = INVALID;
}
// if this tag is the makernote, it needs special treatment (brand specific parsing)
if (tag==0x927C && attrib && !strcmp (attrib->name, "MakerNote") ) {
if( !parseMakerNote(f,base,order )){
type = INVALID;
fseek (f, save, SEEK_SET);
return;
}
}
else if (attrib && attrib->subdirAttribs) {
// Some subdirs are specific of maker and model
char make[128], model[128];
make[0]=0;
model[0]=0;
Tag* tmake = parent->getRoot()->getTag ("Make");
if (tmake) tmake->toString (make);
Tag* tmodel = parent->getRoot()->getTag ("Model");
if (tmodel) tmodel->toString (model);
if (!strncmp(make, "SONY", 4)) {
switch( tag ){
case 0x0010:
directory = new TagDirectory*[2];
directory[1] = NULL;
if (count == 15360)
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , sonyCameraInfoAttribs, order);
else
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , sonyCameraInfo2Attribs, order);
break;
case 0x0114:
directory = new TagDirectory*[2];
directory[1] = NULL;
if (count == 280 || count == 364)
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,SHORT , sonyCameraSettingsAttribs, MOTOROLA);
else if (count == 332)
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,SHORT , sonyCameraSettingsAttribs2, MOTOROLA);
else if(count == 1536 || count == 2048)
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , sonyCameraSettingsAttribs3, INTEL);
else {
// Unknown CameraSettings
delete [] directory;
directory = NULL;
type = INVALID;
}
makerNoteKind = directory ? TABLESUBDIR : NOMK;
break;
case 0x9405:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,SHORT , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
default:
goto defsubdirs;
}
}else if ((!strncmp(make, "PENTAX", 6)) || (!strncmp(make, "RICOH", 5) && !strncmp(model, "PENTAX", 6))) { // Either the former Pentax brand or the RICOH brand + PENTAX model"
switch( tag ){
case 0x007d:
case 0x0205:
case 0x0208:
case 0x0216:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
case 0x0215:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,LONG , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
case 0x005c:
directory = new TagDirectory*[2];
directory[1] = NULL;
if (count == 4) // SRInfo
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , pentaxSRInfoAttribs, order);
else if (count == 2) // SRInfo2
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , pentaxSRInfo2Attribs, order);
else {
// Unknown SRInfo
delete [] directory;
directory = NULL;
type = INVALID;
}
makerNoteKind = directory ? TABLESUBDIR : NOMK;
break;
case 0x0206:
directory = new TagDirectory*[2];
directory[1] = NULL;
if (count == 21) // AEInfo2
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , pentaxAEInfo2Attribs, order);
else if (count == 48) // AEInfo3
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , pentaxAEInfo3Attribs, order);
else if (count <= 25) // AEInfo
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , pentaxAEInfoAttribs, order);
else {
// Unknown AEInfo
delete [] directory;
directory = NULL;
type = INVALID;
}
makerNoteKind = directory ? TABLESUBDIR : NOMK;
break;
case 0x0207:
{ // There are 2 format pentaxLensDataAttribs
int offsetFirst = 4; // LensInfo2
if( strstr(model, "*ist") || strstr(model, "GX-1") || strstr(model, "K100D") || strstr(model, "K110D") )
offsetFirst = 3; // LensInfo
else if( strstr(model, "645D") )
offsetFirst = 13; // LensInfo3
else if( strstr(model, "K-5") || strstr(model, "K-r") )
offsetFirst = 12; // LensInfo4
else if( strstr(model, "K-01") || strstr(model, "K-30"))
offsetFirst = 15; // LensInfo5
else if(!strncmp(make, "RICOH", 5)) { // all PENTAX camera model produced under the RICOH era uses LensInfo5, for now...
offsetFirst = 15; // LensInfo5 too
}
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,offsetFirst,BYTE , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
}
break;
case 0x0239:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
default:
goto defsubdirs;
}
}else if (!strncmp(make, "Canon", 5)) {
switch( tag ){
case 0x0001:
case 0x0002:
case 0x0004:
case 0x0005:
case 0x0093:
case 0x0098:
case 0x00a0:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,SSHORT , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
case 0x009a:
case 0x4013:
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,LONG , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
default:
goto defsubdirs;
}
}else if (!strncmp(make, "NIKON", 5)) {
switch (tag) {
case 0x0025: {
directory = new TagDirectory*[2];
directory[1] = NULL;
directory[0] = new TagDirectoryTable (parent, f, valuesize,0,BYTE , attrib->subdirAttribs, order);
makerNoteKind = TABLESUBDIR;
break;
}
default:
goto defsubdirs;
}
}else if(type==UNDEFINED){
count = 1;
type = LONG;
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, attrib->subdirAttribs, order);
directory[1] = NULL;
}else
goto defsubdirs;
}
else {
// read value
value = new unsigned char [valuesize+1];
fread (value, 1, valuesize, f);
value[valuesize] = '\0';
}
// seek back to the saved position
fseek (f, save, SEEK_SET);
return;
defsubdirs:
// read value
value = new unsigned char [valuesize];
fread (value, 1, valuesize, f);
int pos = ftell (f);
// count the number of valid subdirs
int sdcount = count;
if (sdcount>0) {
if (parent->getAttribTable()==olympusAttribs)
sdcount = 1;
// allocate space
directory = new TagDirectory*[sdcount+1];
// load directories
for (size_t j=0,i=0; j<count; j++,i++) {
int newpos = base + toInt(j*4, LONG);
fseek (f, newpos, SEEK_SET);
directory[i] = new TagDirectory (parent, f, base, attrib->subdirAttribs, order);
fseek (f, pos, SEEK_SET);
}
// set the terminating NULL
directory[sdcount] = NULL;
}
else
type = INVALID;
// seek back to the saved position
fseek (f, save, SEEK_SET);
return;
}
bool Tag::parseMakerNote(FILE* f, int base, ByteOrder bom )
{
value = NULL;
Tag* tmake = parent->getRoot()->findTag("Make");
std::string make( tmake ? tmake->valueToString():"");
Tag* tmodel = parent->getRoot()->findTag ("Model");
std::string model( tmodel ? tmodel->valueToString():"");
if ( make.find( "NIKON" ) != std::string::npos ) {
if ( model.find("NIKON E700")!= std::string::npos ||
model.find("NIKON E800")!= std::string::npos ||
model.find("NIKON E900")!= std::string::npos ||
model.find("NIKON E900S")!= std::string::npos ||
model.find("NIKON E910")!= std::string::npos ||
model.find("NIKON E950")!= std::string::npos ) {
makerNoteKind = HEADERIFD;
valuesize = 8;
value = new unsigned char[8];
fread (value, 1, 8, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, nikon2Attribs, bom);
directory[1] = NULL;
} else if ( model.find("NIKON E990")!= std::string::npos ||
(model.find("NIKON D1")!= std::string::npos && model.size()>8 && model.at(8)!='0')) {
makerNoteKind = IFD;
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, nikon3Attribs, bom);
directory[1] = NULL;
} else {
// needs refinement! (embedded tiff header parsing)
makerNoteKind = NIKON3;
valuesize = 18;
value = new unsigned char[18];
int basepos = ftell (f);
fread (value, 1, 18, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, basepos+10, nikon3Attribs, bom);
directory[1] = NULL;
}
} else if ( make.find( "Canon" ) != std::string::npos ) {
makerNoteKind = IFD;
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, canonAttribs, bom);
directory[1] = NULL;
} else if ( make.find( "PENTAX" ) != std::string::npos ) {
makerNoteKind = HEADERIFD;
valuesize = 6;
value = new unsigned char[6];
fread (value, 1, 6, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, pentaxAttribs, bom);
directory[1] = NULL;
} else if ( (make.find( "RICOH" ) != std::string::npos ) && (model.find("PENTAX") != std::string::npos) ) {
makerNoteKind = HEADERIFD;
valuesize = 10;
value = new unsigned char[10];
fread (value, 1, 10, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, ftell (f)-10, pentaxAttribs, bom);
directory[1] = NULL;
} else if ( make.find( "FUJIFILM" ) != std::string::npos ) {
makerNoteKind = FUJI;
valuesize = 12;
value = new unsigned char[12];
fread (value, 1, 12, f);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, ftell(f)-12, fujiAttribs, INTEL);
directory[1] = NULL;
} else if ( make.find( "KONICA MINOLTA" ) != std::string::npos || make.find( "Minolta" ) != std::string::npos ) {
makerNoteKind = IFD;
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, minoltaAttribs, bom);
directory[1] = NULL;
} else if ( make.find( "SONY" ) != std::string::npos ) {
valuesize = 12;
value = new unsigned char[12];
fread (value, 1, 12, f);
if (!strncmp((char*)value, "SONY DSC", 8))
makerNoteKind = HEADERIFD;
else {
makerNoteKind = IFD;
fseek (f, -12, SEEK_CUR);
}
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, f, base, sonyAttribs, bom );
directory[1] = NULL;
} else if ( make.find( "OLYMPUS" ) != std::string::npos ) {
makerNoteKind = HEADERIFD;
valuesize = 8;
value = new unsigned char[12];
fread (value, 1, 8, f);
directory = new TagDirectory*[2];
directory[1] = NULL;
if (!strncmp((char*)value, "OLYMPUS", 7)) {
makerNoteKind = OLYMPUS2;
fread (value+8, 1, 4, f);
valuesize = 12;
directory[0] = new TagDirectory (parent, f, ftell(f)-12, olympusAttribs, value[8]=='I' ? INTEL : MOTOROLA);
} else
directory[0] = new TagDirectory (parent, f, base, olympusAttribs, bom);
} else
return false;
return true;
}
Tag* Tag::clone (TagDirectory* parent) {
Tag* t = new Tag (parent, attrib);
t->tag = tag;
t->type = type;
t->count = count;
t->keep = keep;
t->valuesize = valuesize;
if (value) {
t->value = new unsigned char [valuesize];
memcpy (t->value, value, valuesize);
}
else
value = NULL;
t->makerNoteKind = makerNoteKind;
if (directory) {
int ds = 0;
for (; directory[ds]; ds++);
t->directory = new TagDirectory*[ds+1];
for (int i=0; i<ds; i++)
t->directory[i] = directory[i]->clone (parent);
t->directory[ds] = NULL;
}
else
t->directory = NULL;
return t;
}
Tag::~Tag () {
// delete value
if (value && allocOwnMemory)
delete [] value;
// if there are directories behind the tag, delete them
int i = 0;
if (directory) {
while (directory[i])
delete directory[i++];
delete [] directory;
}
}
void Tag::setInt (int v, int ofs, TagType astype) {
if (astype==SHORT)
sset2 (v, value+ofs, getOrder());
else if (astype==RATIONAL) {
sset4 (v, value+ofs, getOrder());
sset4 (1, value+ofs+4, getOrder());
}
else
sset4 (v, value+ofs, getOrder());
}
void Tag::fromInt (int v) {
if (type==SHORT)
sset2 (v, value, getOrder());
else
sset4 (v, value, getOrder());
}
void Tag::fromString (const char* v, int size) {
if( value && allocOwnMemory)
delete [] value;
if (size<0)
valuesize = strlen (v) + 1;
else
valuesize = size;
count = valuesize;
if( allocOwnMemory )
value = new unsigned char [valuesize];
memcpy ((char*)value, v, valuesize);
}
int Tag::toInt (int ofs, TagType astype) {
if (attrib)
return attrib->interpreter->toInt(this, ofs, astype);
int a;
if (astype == INVALID)
astype = type;
switch (astype) {
//case SBYTE: return (signed char)(value[ofs]);
case SBYTE: return int((reinterpret_cast<signed char*>(value))[ofs]);
case BYTE: return value[ofs];
case ASCII: return 0;
case SSHORT:return (int)int2_to_signed(sget2 (value+ofs, getOrder()));
case SHORT: return (int)sget2 (value+ofs, getOrder());
case SLONG:
case LONG: return (int)sget4 (value+ofs, getOrder());
case SRATIONAL:
case RATIONAL: a = (int)sget4 (value+ofs+4, getOrder()); return a==0 ? 0 : (int)sget4 (value+ofs, getOrder()) / a;
case FLOAT: return (int)toDouble(ofs);
case UNDEFINED: return 0;
default: return 0; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
}
return 0;
}
double Tag::toDouble (int ofs) {
if (attrib)
return attrib->interpreter->toDouble(this, ofs);
union IntFloat { uint32_t i; float f; } conv;
double ud, dd;
switch (type) {
case SBYTE: return (double)(int((reinterpret_cast<signed char*>(value))[ofs]));
case BYTE: return (double)((int)value[ofs]);
case ASCII: return 0.0;
case SSHORT:return (double)int2_to_signed(sget2 (value+ofs, getOrder()));
case SHORT: return (double)((int)sget2 (value+ofs, getOrder()));
case SLONG:
case LONG: return (double)((int)sget4 (value+ofs, getOrder()));
case SRATIONAL:
case RATIONAL: ud = (int)sget4 (value+ofs, getOrder()); dd = (int)sget4 (value+ofs+4, getOrder()); return dd==0. ? 0. : (double)ud / (double)dd;
case FLOAT:
conv.i=sget4 (value+ofs, getOrder());
return conv.f; // IEEE FLOATs are already C format, they just need a recast
case UNDEFINED: return 0.;
default: return 0.; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
}
return 0.;
}
/**
* @brief Create an array of the elements
*/
double *Tag::toDoubleArray(int ofs) {
double *values = new double[count];
for (int i=0; i<count; ++i) {
values[i] = toDouble(ofs+i*getTypeSize(type));
}
return values;
}
void Tag::toRational (int& num, int& denom, int ofs) {
switch (type) {
case BYTE: num = (int)value[ofs]; denom = 1; break;
case ASCII: num = 0; denom = 0; break;
case SSHORT:
case SHORT: num = (int)sget2 (value+ofs, getOrder()); denom = 1; break;
case SLONG:
case LONG: num = (int)sget4 (value+ofs, getOrder()); denom = 1; break;
case SRATIONAL:
case RATIONAL: num = (int)sget4 (value+ofs, getOrder()); denom = (int)sget4 (value+ofs+4, getOrder()); break;
case FLOAT: num = 0; denom = 0; break;
case UNDEFINED: num = 0; denom = 0; break;
default: num = 0; denom = 0; break; // Quick fix for missing cases (INVALID, DOUBLE, OLYUNDEF, SUBDIR)
}
}
void Tag::toString (char* buffer, int ofs) {
if (type==UNDEFINED && !directory) {
bool isstring = true;
int i=0;
for (i=0; i+ofs<count && i<64 && value[i+ofs]; i++)
if (value[i+ofs]<32 || value[i+ofs]>126)
isstring = false;
if (isstring) {
int j = 0;
for (i=0; i+ofs<count && i<64 && value[i+ofs]; i++) {
if (value[i+ofs]=='<' || value[i+ofs]=='>')
buffer[j++] = '\\';
buffer[j++] = value[i+ofs];
}
buffer[j++] = 0;
return;
}
}
else if (type==ASCII) {
sprintf (buffer, "%.64s", value+ofs);
return;
}
size_t maxcount = 4;
if (count<4)
maxcount = count;
strcpy (buffer, "");
for (size_t i=0; i<maxcount; i++) {
if (i>0)
strcat (buffer, ", ");
char* b = buffer + strlen(buffer);
switch (type) {
case UNDEFINED:
case BYTE: sprintf (b, "%d", value[i+ofs]); break;
case SSHORT: sprintf (b, "%d", toInt(2*i+ofs)); break;
case SHORT: sprintf (b, "%u", toInt(2*i+ofs)); break;
case SLONG: sprintf (b, "%ld", (long)toInt(4*i+ofs)); break;
case LONG: sprintf (b, "%lu", (unsigned long)toInt(4*i+ofs)); break;
case SRATIONAL:
case RATIONAL: sprintf (b, "%d/%d", (int)sget4 (value+8*i+ofs, getOrder()), (int)sget4 (value+8*i+ofs+4, getOrder())); break;
case FLOAT: sprintf (b, "%g", toDouble(8*i+ofs)); break;
default: break;
}
}
if (count > maxcount)
strcat (buffer, "...");
}
std::string Tag::nameToString (int i) {
char buffer[1024];
if (attrib)
strcpy (buffer, attrib->name);
else
sprintf (buffer, "0x%x", tag);
if (i>0)
sprintf (buffer+strlen(buffer)-1, "[%d]", i);
return buffer;
}
std::string Tag::valueToString () {
char buffer[1024];
if (attrib && attrib->interpreter)
return attrib->interpreter->toString (this);
else {
toString (buffer);
return buffer;
}
}
void Tag::valueFromString (const std::string& value) {
if (attrib && attrib->interpreter)
attrib->interpreter->fromString (this, value);
}
int Tag::calculateSize () {
int size = 0;
if (directory) {
int j;
for (j=0; directory[j]; j++)
size += directory[j]->calculateSize ();
if (j>1)
size += 4*j;
}
else if (valuesize > 4)
size += valuesize + (valuesize%2); // we align tags to even byte positions
if (makerNoteKind!=NOMK)
count = directory[0]->calculateSize () / getTypeSize(type);
if (makerNoteKind==NIKON3 || makerNoteKind==OLYMPUS2 || makerNoteKind==FUJI)
size += valuesize;
else if (makerNoteKind==HEADERIFD)
size += valuesize;
return size;
}
int Tag::write (int offs, int dataOffs, unsigned char* buffer) {
if ((int)type==0 || offs>65500)
return dataOffs;
sset2 (tag, buffer+offs, parent->getOrder());
offs += 2;
unsigned short typ = type;
sset2 (typ, buffer+offs, parent->getOrder());
offs += 2;
sset4 (count, buffer+offs, parent->getOrder());
offs += 4;
if (!directory) {
if (valuesize>4) {
sset4 (dataOffs, buffer+offs, parent->getOrder());
memcpy (buffer+dataOffs, value, valuesize);
if (valuesize%2)
buffer[dataOffs+valuesize] = 0; // zero padding required by the exif standard
return dataOffs + valuesize + (valuesize%2);
}
else {
memcpy (buffer+offs, value, valuesize);
return dataOffs;
}
}
else {
if (makerNoteKind==NIKON3) {
sset4 (dataOffs, buffer+offs, parent->getOrder());
memcpy (buffer+dataOffs, value, 18);
dataOffs += 10;
dataOffs += directory[0]->write (8, buffer+dataOffs);
return dataOffs;
}
else if (makerNoteKind==OLYMPUS2 || makerNoteKind==FUJI) {
sset4 (dataOffs, buffer+offs, parent->getOrder());
memcpy (buffer+dataOffs, value, valuesize);
dataOffs += valuesize + directory[0]->write (valuesize, buffer+dataOffs);
return dataOffs;
}
else if (makerNoteKind==HEADERIFD) {
sset4 (dataOffs, buffer+offs, parent->getOrder());
memcpy (buffer+dataOffs, value, valuesize);
dataOffs += valuesize;
dataOffs += directory[0]->write (dataOffs, buffer);
return dataOffs;
}
else if( makerNoteKind==TABLESUBDIR){
sset4 (dataOffs, buffer+offs, parent->getOrder());
dataOffs = directory[0]->write (dataOffs, buffer);
return dataOffs;
}
else if (!directory[1]) {
sset4 (dataOffs, buffer+offs, parent->getOrder());
return directory[0]->write (dataOffs, buffer);
}
else {
sset4 (dataOffs, buffer+offs, parent->getOrder());
int linkOffs = dataOffs;
for (int i=0; directory[i]; i++)
dataOffs += 4;
for (int i=0; directory[i]; i++) {
sset4 (dataOffs, buffer+linkOffs, parent->getOrder());
linkOffs += 4;
dataOffs = directory[i]->write (dataOffs, buffer);
}
return dataOffs;
}
}
}
Tag::Tag (TagDirectory* p, const TagAttrib* attr)
: tag(attr ? attr->ID : -1), type(INVALID), count(0), value(NULL), valuesize(0), keep(true), allocOwnMemory(true), attrib(attr), parent(p), directory(NULL), makerNoteKind (NOMK) {
}
Tag::Tag (TagDirectory* p, const TagAttrib* attr, int data, TagType t)
: tag(attr ? attr->ID : -1), type(t), count(1), value(NULL), valuesize(0), keep(true), allocOwnMemory(true), attrib(attr), parent(p), directory(NULL), makerNoteKind (NOMK) {
initInt (data, t);
}
Tag::Tag (TagDirectory* p, const TagAttrib* attr, unsigned char *data, TagType t)
: tag(attr ? attr->ID : -1), type(t), count(1), value(NULL), valuesize(0), keep(true), allocOwnMemory(false), attrib(attr), parent(p), directory(NULL), makerNoteKind (NOMK) {
initType (data, t);
}
Tag::Tag (TagDirectory* p, const TagAttrib* attr, const char* text)
: tag(attr ? attr->ID : -1), type(ASCII), count(1), value(NULL), valuesize(0), keep(true), allocOwnMemory(true), attrib(attr), parent(p), directory(NULL), makerNoteKind (NOMK) {
initString (text);
}
void Tag::initType (unsigned char *data, TagType type)
{
valuesize = getTypeSize(type);
if( allocOwnMemory ){
value = new unsigned char[valuesize];
memcpy ((char*)value, data, valuesize);
}else
value = data;
}
void Tag::initInt (int data, TagType t, int cnt) {
type = t;
if (t==LONG)
valuesize = 4;
else if (t==SHORT)
valuesize = 2;
else if (t==BYTE)
valuesize = 1;
else if (t==RATIONAL)
valuesize = 8;
count = cnt;
valuesize *= count;
value = new unsigned char[valuesize];
setInt (data, 0, t);
}
void Tag::initString (const char* text) {
type = ASCII;
count = strlen(text)+1;
valuesize = count;
value = new unsigned char[valuesize];
strcpy ((char*)value, text);
}
void Tag::initSubDir () {
type = LONG;
valuesize = 4;
count = 1;
value = new unsigned char[4];
setInt (0);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, attrib ? attrib->subdirAttribs : NULL, parent->getOrder());
directory[1] = NULL;
}
void Tag::initSubDir (TagDirectory* dir) {
type = LONG;
valuesize = 4;
count = 1;
value = new unsigned char[4];
setInt (0);
directory = new TagDirectory*[2];
directory[0] = dir;
directory[1] = NULL;
}
void Tag::initMakerNote (MNKind mnk, const TagAttrib* ta) {
type = UNDEFINED;
valuesize = 4;
count = 1;
value = new unsigned char[4];
setInt (0);
directory = new TagDirectory*[2];
directory[0] = new TagDirectory (parent, ta, parent->getOrder());
directory[1] = NULL;
makerNoteKind = mnk;
}
void Tag::initUndefArray (const char* data, int len) {
type = UNDEFINED;
count = valuesize = len;
value = new unsigned char[valuesize];
memcpy (value, data, len);
}
void Tag::initLongArray (const char* data, int len) {
type = LONG;
count = (len+3)/4;
valuesize = count * 4;
value = new unsigned char[valuesize];
memcpy (value, data, len);
}
void Tag::initRational (int num, int den) {
count = 1;
valuesize = 8;
value = new unsigned char[8];
type = RATIONAL;
setInt (num, 0);
setInt (den, 4);
}
//--------------- class IFDParser ---------------------------------------------
// static functions to read tag directories from different kinds of files
//-----------------------------------------------------------------------------
const TagAttrib* lookupAttrib (const TagAttrib* dir, const char* field) {
for (int i=0; dir[i].ignore!=-1; i++)
if (!strcmp (dir[i].name, field))
return &dir[i];
return 0;
}
TagDirectory* ExifManager::parseCIFF (FILE* f, int base, int length) {
TagDirectory* root = new TagDirectory (NULL, ifdAttribs, INTEL);
Tag* exif = new Tag (root, lookupAttrib(ifdAttribs,"Exif"));
exif->initSubDir ();
Tag* mn = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"MakerNote"));
mn->initMakerNote (IFD, canonAttribs);
root->addTag (exif);
exif->getDirectory()->addTag (mn);
parseCIFF (f, base, length, root);
root->sort ();
return root;
}
Tag* ExifManager::saveCIFFMNTag (FILE* f, TagDirectory* root, int len, const char* name) {
int s = ftell (f);
char* data = new char [len];
fread (data, len, 1, f);
TagDirectory* mn = root->getTag ("Exif")->getDirectory()->getTag("MakerNote")->getDirectory();
Tag* cs = new Tag (mn, lookupAttrib(canonAttribs, name));
cs->initUndefArray (data, len);
mn->addTag (cs);
fseek (f, s, SEEK_SET);
return cs;
}
void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root) {
char buffer[1024];
Tag* t;
fseek (f, base+length-4, SEEK_SET);
int dirStart = get4 (f, INTEL) + base;
fseek (f, dirStart, SEEK_SET);
int numOfTags = get2 (f, INTEL);
if (numOfTags > 100) return;
float exptime, shutter, aperture, fnumber, ev;
exptime = fnumber = shutter = aperture = ev = -1000.f;
int focal_len, iso;
focal_len = iso = -1;
TagDirectory* exif = root->getTag("Exif")->getDirectory();
time_t timestamp = time (NULL);
for (int i=0; i<numOfTags; i++) {
int type = get2 (f, INTEL);
int len = get4 (f, INTEL);
int nextPos = ftell (f) + 4;
// seek to the location of the value
fseek (f, base + get4 (f, INTEL), SEEK_SET);
if ((((type >> 8) + 8) | 8) == 0x38)
parseCIFF (f, ftell(f), len, root); // Parse a sub-table
if (type == 0x0810) {
fread (buffer, 64, 1, f);
t = new Tag (root, lookupAttrib(ifdAttribs,"Artist"));
t->initString (buffer);
root->addTag (t);
}
if (type == 0x080a) {
fread (buffer, 64, 1, f);
t = new Tag (root, lookupAttrib(ifdAttribs,"Make"));
t->initString (buffer);
root->addTag (t);
fseek (f, strlen(buffer) - 63, SEEK_CUR);
fread (buffer, 64, 1, f);
t = new Tag (root, lookupAttrib(ifdAttribs,"Model"));
t->initString (buffer);
root->addTag (t);
}
if (type == 0x1818) {
ev = int_to_float(get4(f, INTEL));
shutter = int_to_float(get4(f, INTEL));
exptime = pow (2, -shutter);
aperture = int_to_float(get4(f, INTEL));
fnumber = pow (2, aperture/2);
}
if (type == 0x102d) {
Tag* t = saveCIFFMNTag (f, root, len, "CanonCameraSettings");
int mm = t->toInt (34, SHORT);
Tag* nt = new Tag (exif, lookupAttrib(exifAttribs,"MeteringMode"));
switch (mm) {
case 0: nt->initInt (5, SHORT); break;
case 1: nt->initInt (3, SHORT); break;
case 2: nt->initInt (1, SHORT); break;
case 3: nt->initInt (5, SHORT); break;
case 4: nt->initInt (6, SHORT); break;
case 5: nt->initInt (2, SHORT); break;
}
exif->addTag (nt);
nt = new Tag (exif, lookupAttrib(exifAttribs,"MaxApertureValue"));
nt->initRational (t->toInt(52,SHORT), 32);
exif->addTag (nt);
int em = t->toInt(40,SHORT);
nt = new Tag (exif, lookupAttrib(exifAttribs,"ExposureProgram"));
switch (em) {
case 0: nt->initInt (2, SHORT); break;
case 1: nt->initInt (2, SHORT); break;
case 2: nt->initInt (4, SHORT); break;
case 3: nt->initInt (3, SHORT); break;
case 4: nt->initInt (1, SHORT); break;
default: nt->initInt (0, SHORT); break;
}
exif->addTag (nt);
nt = new Tag (exif, lookupAttrib(exifAttribs,"Flash"));
if (t->toInt(8,SHORT)==0)
nt->initInt (0, SHORT);
else
nt->initInt (1, SHORT);
exif->addTag (nt);
nt = new Tag (exif, lookupAttrib(exifAttribs,"MaxApertureValue"));
nt->initRational (t->toInt(52,SHORT), 32);
exif->addTag (nt);
}
if (type == 0x1029)
saveCIFFMNTag (f, root, len, "CanonFocalLength");
if (type == 0x1031)
saveCIFFMNTag (f, root, len, "SensorInfo");
if (type == 0x1033)
saveCIFFMNTag (f, root, len, "CustomFunctions");
if (type == 0x1038)
saveCIFFMNTag (f, root, len, "CanonAFInfo");
if (type == 0x1093)
saveCIFFMNTag (f, root, len, "CanonFileInfo");
if (type == 0x10a9)
saveCIFFMNTag (f, root, len, "ColorBalance");
if (type == 0x102a) {
saveCIFFMNTag (f, root, len, "CanonShotInfo");
iso = pow (2, (get4(f, INTEL),get2(f, INTEL))/32.0 - 4) * 50;
aperture = (get2(f, INTEL),(short)get2(f, INTEL))/32.0f;
fnumber = pow (2, aperture/2);
shutter = ((short)get2(f, INTEL))/32.0f;
ev = ((short)get2(f, INTEL))/32.0f;
fseek (f, 34, SEEK_CUR);
if (shutter > 1e6) shutter = get2 (f, INTEL) / 10.0f;
exptime = pow (2,-shutter);
}
if (type == 0x5029) {
focal_len = len >> 16;
if ((len & 0xffff) == 2) focal_len /= 32;
}
// if (type == 0x5813) flash_used = int_to_float(len);
if (type == 0x580e) timestamp = len;
if (type == 0x180e) timestamp = get4 (f, INTEL);
if ((type | 0x4000) == 0x580e)
timestamp = mktime (gmtime (&timestamp));
fseek (f, nextPos, SEEK_SET);
}
if (shutter>-999) {
t = new Tag (exif, lookupAttrib(exifAttribs,"ShutterSpeedValue"));
t->initRational ((int)(shutter*10000), 10000);
exif->addTag (t);
}
if (exptime>-999) {
t = new Tag (exif, lookupAttrib(exifAttribs,"ExposureTime"));
t->initRational ((int)(exptime*10000), 10000);
exif->addTag (t);
}
if (aperture>-999) {
t = new Tag (exif, lookupAttrib(exifAttribs,"ApertureValue"));
t->initRational ((int)(aperture*10), 10);
exif->addTag (t);
}
if (fnumber>-999) {
t = new Tag (exif, lookupAttrib(exifAttribs,"FNumber"));
t->initRational ((int)(fnumber*10), 10);
exif->addTag (t);
}
if (ev>-999) {
t = new Tag (exif, lookupAttrib(exifAttribs,"ExposureBiasValue"));
t->initRational ((int)(ev*1000), 1000);
exif->addTag (t);
}
if (iso>0) {
t = new Tag (exif, lookupAttrib(exifAttribs,"ISOSpeedRatings"));
t->initInt (iso, LONG);
exif->addTag (t);
}
if (focal_len>0) {
t = new Tag (exif, lookupAttrib(exifAttribs,"FocalLength"));
t->initRational (focal_len*32, 32);
exif->addTag (t);
}
if (timestamp!=time(NULL)) {
struct tm* tim = localtime (&timestamp);
strftime (buffer, 20, "%Y:%m:%d %H:%M:%S", tim);
t = new Tag (exif, lookupAttrib(exifAttribs,"DateTimeOriginal"));
t->initString (buffer);
exif->addTag (t);
t = new Tag (exif, lookupAttrib(exifAttribs,"DateTimeDigitized"));
t->initString (buffer);
exif->addTag (t);
t = new Tag (root, lookupAttrib(ifdAttribs,"DateTime"));
t->initString (buffer);
root->addTag (t);
}
}
static void
parse_leafdata(TagDirectory* root, ByteOrder order) {
Tag *leafdata = root->getTag("LeafData");
if (!leafdata) {
return;
}
unsigned char *value = leafdata->getValue();
int valuesize = leafdata->getValueSize();
// parse LeafData tag, a tag specific to Leaf digital backs, and has a custom
// format with 52 byte tag headers starting with "PKTS"
const char *PKTS_tag = (order == MOTOROLA) ? "PKTS" : "STKP";
char *hdr;
int pos = 0;
// There are lots of sub-tags in here, but for now we only care about those directly
// useful to RT, which is ISO and rotation. Shutter speed and aperture is not
// available here.
int iso_speed = 0;
int rotation_angle = 0;
int found_count = 0;
while (pos + sizeof(hdr) <= valuesize && found_count < 2) {
hdr = (char *)&value[pos];
if (strncmp(hdr, PKTS_tag, 4) != 0) {
// in a few cases the header can be offset a few bytes, don't know why
// it does not seem to be some sort of alignment, it appears random,
// this check takes care of it, restart if we find an offset match.
int offset = 1;
for (; offset <= 3; offset++) {
if (strncmp(&hdr[offset], PKTS_tag, 4) == 0) {
pos += offset;
break;
}
}
if (offset <= 3) {
continue;
}
break;
}
int size = sget4((unsigned char *)&hdr[48], order);
if (pos + size > valuesize) {
break;
}
pos += 52;
char *val = (char *)&value[pos];
if (strncmp(&hdr[8], "CameraObj_ISO_speed", 19) == 0) {
iso_speed = 25 * (1 << (atoi(val) - 1));
found_count++;
} else if (strncmp(&hdr[8], "ImgProf_rotation_angle", 22) == 0) {
rotation_angle = atoi(val);
found_count++;
} else {
// check if this is a sub-directory, include test for that strange offset of next header
if (size >= 8 &&
(strncmp(val, PKTS_tag, 4) == 0 ||
strncmp(&val[1], PKTS_tag, 4) == 0 ||
strncmp(&val[2], PKTS_tag, 4) == 0 ||
strncmp(&val[3], PKTS_tag, 4) == 0))
{
// start of next hdr, this is a sub-directory, we skip those for now.
size = 0;
}
}
pos += size;
}
// create standard tags from the custom Leaf tags
Tag* exif = root->getTag ("Exif");
if (!exif) {
exif = new Tag (root, root->getAttrib ("Exif"));
exif->initSubDir();
root->addTagFront (exif);
}
if (!exif->getDirectory()->getTag("ISOSpeedRatings")) {
Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
t->initInt (iso_speed, LONG);
exif->getDirectory()->addTagFront (t);
}
if (!root->getTag("Orientation")) {
int orientation;
switch (rotation_angle) {
case 0: orientation = 1; break;
case 90: orientation = 6; break;
case 180: orientation = 3; break;
case 270: orientation = 8; break;
default: orientation = 1; break;
}
Tag *t = new Tag (root, root->getAttrib ("Orientation"));
t->initInt (orientation, SHORT);
root->addTagFront (t);
}
// now look in ApplicationNotes tag for additional information
Tag *appnotes = root->getTag("ApplicationNotes");
if (!appnotes) {
return;
}
char *xmp = (char *)appnotes->getValue();
char *end, *p;
// Quick-and-dirty value extractor, no real xml parsing.
// We could make it more generic, but we just get most important
// values we know use to be in there.
if ((p = strstr(xmp, "xmlns:tiff")) != NULL &&
(end = strstr(p, "</rdf:Description>")) != NULL)
{
*end = '\0';
while ((p = strstr(p, "<tiff:")) != NULL) {
char *tag = &p[6], *tagend;
if ((tagend = strchr(tag, '>')) == NULL) {
break;
}
*tagend = '\0';
char *val = &tagend[1];
if ((p = strstr(val, "</tiff:")) == NULL) {
*tagend = '>';
break;
}
*p = '\0';
if (root->getAttrib (tag) && !root->getTag (tag)) {
Tag *t = new Tag (root, root->getAttrib (tag));
if (strcmp(tag, "Make") == 0 ||
strcmp(tag, "Model") == 0)
{
if (strcmp(tag, "Model") == 0) {
// Leaf adds back serial number and camera model to the 'Model'
// tag, we strip that away here so the back can be recognized
// and matched against DCP profile
char *p1 = strchr(val, '(');
if (p1 != NULL) {
*p1 = '\0';
}
// Model name also contains a leading "Leaf " which we already
// have in the Make name, remove that.
if (strstr(val, "Leaf ") == val) {
t->initString (&val[5]);
} else {
t->initString (val);
}
if (p1 != NULL) {
*p1 = '(';
}
} else {
t->initString (val);
}
root->addTagFront (t);
} else {
delete t;
}
}
*p = '<';
*tagend = '>';
}
*end = '<';
}
if ((p = strstr(xmp, "xmlns:exif")) != NULL &&
(end = strstr(p, "</rdf:Description>")) != NULL)
{
*end = '\0';
while ((p = strstr(p, "<exif:")) != NULL) {
char *tag = &p[6], *tagend;
if ((tagend = strchr(tag, '>')) == NULL) {
break;
}
*tagend = '\0';
char *val = &tagend[1];
if ((p = strstr(val, "</exif:")) == NULL) {
*tagend = '>';
break;
}
*p = '\0';
if (exif->getDirectory()->getAttrib (tag) && !exif->getDirectory()->getTag (tag)) {
Tag *t = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib (tag));
int num, denom;
struct tm tm;
if (strcmp(tag, "ApertureValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
// we also make an "FNumber" tag since many tools don't interpret ApertureValue
// according to Exif standard
t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"FNumber"));
double f = pow(sqrt(2.0), ((double)num / denom));
if (f > 10.0) {
t->initRational ((int)floor(f), 1);
} else {
t->initRational ((int)floor(f * 10.0), 10);
}
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "ShutterSpeedValue") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
// we also make an "ExposureTime" tag since many tools don't interpret ShutterSpeedValue
// according to Exif standard
t = new Tag (exif->getDirectory(), lookupAttrib(exifAttribs,"ExposureTime"));
double f = 1.0 / pow(2.0, ((double)num / denom));
if (f > 10.0) {
t->initRational ((int)floor(f), 1);
} else if (f > 1.0) {
t->initRational ((int)floor(f * 10.0), 10);
} else if (f == 1.0) {
t->initRational (1, 1);
} else {
f = 1.0 / f;
static const double etimes[] = {
10000, 8000, 6400, 6000, 5000,
4000, 3200, 3000, 2500,
2000, 1600, 1500, 1250,
1000, 800, 750, 640,
500, 400, 350, 320,
250, 200, 180, 160,
125, 100, 90, 80,
60, 50, 45, 40,
30, 25, 22, 20,
15, 13, 11, 10,
8, 6, 5,
4, 3, 2.5,
2, 1.6, 1.5, 1.3,
1, -1 };
double diff = etimes[0];
int idx = -1;
for (int i = 1; etimes[i] > 0; i++) {
if (abs(etimes[i] - f) < diff) {
idx = i;
diff = abs(etimes[i] - f);
}
}
if (idx != -1 && f < etimes[0]) {
f = etimes[idx];
}
if (f < 2) {
t->initRational (10, (int)(10 * f));
} else {
t->initRational (1, (int)f);
}
}
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "FocalLength") == 0 && sscanf(val, "%d/%d", &num, &denom) == 2) {
t->initRational (num, denom);
exif->getDirectory()->addTagFront (t);
} else if (strcmp(tag, "ISOSpeedRatings") == 0) {
char *p1 = val;
while (*p1 != '\0' && !isdigit(*p1)) p1++;
if (*p1 != '\0') {
t->initInt (atoi(p1), LONG);
exif->getDirectory()->addTagFront (t);
}
} else if (strcmp(tag, "DateTimeOriginal") == 0 &&
sscanf(val, "%d-%d-%dT%d:%d:%dZ",
&tm.tm_year, &tm.tm_mon,
&tm.tm_mday, &tm.tm_hour,
&tm.tm_min, &tm.tm_sec) == 6)
{
char tstr[64];
sprintf(tstr, "%04d:%02d:%02d %02d:%02d:%02d", tm.tm_year, tm.tm_mon,
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
t->initString (tstr);
exif->getDirectory()->addTagFront (t);
} else {
delete t;
}
}
*p = '<';
*tagend = '>';
}
*end = '<';
}
}
TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
setlocale(LC_NUMERIC, "C"); // to set decimal point in sscanf
// read tiff header
fseek (f, base, SEEK_SET);
unsigned short bo;
fread (&bo, 1, 2, f);
ByteOrder order = (ByteOrder)((int)bo);
get2 (f, order);
int firstifd = get4 (f, order);
// seek to IFD0
fseek (f, base+firstifd, SEEK_SET);
// first read the IFD directory
TagDirectory* root = new TagDirectory (NULL, f, base, ifdAttribs, order, skipIgnored);
// fix ISO issue with nikon and panasonic cameras
Tag* make = root->getTag ("Make");
Tag* exif = root->getTag ("Exif");
if (exif && !exif->getDirectory()->getTag("ISOSpeedRatings")) {
if (make && !strncmp((char*)make->getValue(), "NIKON", 5)) {
Tag* mn = exif->getDirectory()->getTag("MakerNote");
if (mn) {
Tag* iso = mn->getDirectory()->getTag("ISOSpeed");
if (iso) {
std::string isov = iso->valueToString ();
Tag* niso = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
niso->initInt (atoi(isov.c_str()), SHORT);
exif->getDirectory()->addTagFront (niso);
}
}
}
else if (make && (!strncmp((char*)make->getValue(), "Panasonic", 9) || !strncmp((char*)make->getValue(), "LEICA", 5))) {
Tag* iso = root->getTag("PanaISO");
if (iso) {
std::string isov = iso->valueToString ();
Tag* niso = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
niso->initInt (atoi(isov.c_str()), SHORT);
exif->getDirectory()->addTagFront (niso);
}
}
}
if (make && !strncmp((char*)make->getValue(), "Kodak", 5)) {
if (!exif) {
// old Kodak cameras may have exif tags in IFD0, reparse and create an exif subdir
fseek (f, base+firstifd, SEEK_SET);
TagDirectory* exifdir = new TagDirectory (NULL, f, base, exifAttribs, order, true);
exif = new Tag (root, root->getAttrib ("Exif"));
exif->initSubDir(exifdir);
root->addTagFront (exif);
if (!exif->getDirectory()->getTag("ISOSpeedRatings") && exif->getDirectory()->getTag ("ExposureIndex")) {
Tag* niso = new Tag (exif->getDirectory(), exif->getDirectory()->getAttrib ("ISOSpeedRatings"));
niso->initInt (exif->getDirectory()->getTag ("ExposureIndex")->toInt(), SHORT);
exif->getDirectory()->addTagFront (niso);
}
}
Tag *kodakIFD = root->getTag("KodakIFD");
if (kodakIFD && kodakIFD->getDirectory()->getTag("TextualInfo")) {
parseKodakIfdTextualInfo(kodakIFD->getDirectory()->getTag("TextualInfo"), exif);
}
}
parse_leafdata(root, order);
if (!root->getTag("Orientation")) {
if (make && !strncmp((char*)make->getValue(), "Phase One", 9)) {
int orientation = 0;
Tag *iw = root->getTag("ImageWidth");
if (iw) {
// from dcraw, derive orientation from image width
orientation = "0653"[iw->toInt()&3]-'0';
}
Tag *t = new Tag (root, root->getAttrib ("Orientation"));
t->initInt (orientation, SHORT);
root->addTagFront (t);
}
}
// root->printAll ();
return root;
}
TagDirectory* ExifManager::parseJPEG (FILE* f) {
fseek (f, 0, SEEK_SET);
unsigned char markerl = 0xff;
unsigned char c;
fread (&c, 1, 1, f);
const char exifid[] = "Exif\0\0";
char idbuff[8];
int tiffbase = -1;
while (fread (&c, 1, 1, f)) {
if (c!=markerl) continue;
if (fread (&c, 1, 1, f) && c==0xe1) { // APP1 marker found
if (fread (idbuff, 1, 8, f)<8)
return NULL;
if (!memcmp(idbuff+2, exifid, 6)) { // Exif info found
tiffbase = ftell (f);
return parse (f, tiffbase);
}
}
}
return NULL;
}
TagDirectory* ExifManager::parseTIFF (FILE* f, bool skipIgnored) {
return parse (f, 0, skipIgnored);
}
std::vector<Tag*> ExifManager::defTags;
// forthis: the byte order will be taken from directory "forthis"
const std::vector<Tag*>& ExifManager::getDefaultTIFFTags (TagDirectory* forthis) {
for (size_t i=0; i<defTags.size(); i++)
delete defTags[i];
defTags.clear ();
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"ImageWidth"), 0, LONG));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"ImageHeight"), 0, LONG));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"XResolution"), 300, RATIONAL));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"YResolution"), 300, RATIONAL));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"ResolutionUnit"), 2, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"Software"), "RawTherapee"));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"Orientation"), 1, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"SamplesPerPixel"), 3, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"BitsPerSample"), 8, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"PlanarConfiguration"), 1, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"PhotometricInterpretation"), 2, SHORT));
defTags.push_back (new Tag (forthis, lookupAttrib(ifdAttribs,"Compression"), 1, SHORT));
return defTags;
}
int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, unsigned char* buffer) {
// write tiff header
int offs = 6;
memcpy (buffer, "Exif\0\0", 6);
ByteOrder order = INTEL;
if (root)
order = root->getOrder ();
sset2 ((unsigned short)order, buffer+offs, order); offs += 2;
sset2 (42, buffer+offs, order); offs += 2;
sset4 (8, buffer+offs, order); offs += 4;
TagDirectory* cl;
if (root)
cl = (const_cast<TagDirectory*>(root))->clone (NULL);
else
cl = new TagDirectory (NULL, ifdAttribs, INTEL);
for (rtengine::procparams::ExifPairs::const_iterator i=changeList.begin(); i!=changeList.end(); i++)
cl->applyChange (i->first, i->second);
getDefaultTIFFTags (cl);
defTags[0]->setInt (W, 0, LONG);
defTags[1]->setInt (H, 0, LONG);
defTags[8]->setInt (8, 0, SHORT);
for (int i=defTags.size()-1; i>=0; i--)
cl->replaceTag (defTags[i]->clone (cl));
cl->sort ();
int size = cl->write (8, buffer+6);
delete cl;
return size + 6;
}
int ExifManager::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) {
// write tiff header
int offs = 0;
ByteOrder order = INTEL;
if (root)
order = root->getOrder ();
sset2 ((unsigned short)order, buffer+offs, order); offs += 2;
sset2 (42, buffer+offs, order); offs += 2;
sset4 (8, buffer+offs, order); offs += 4;
TagDirectory* cl;
if (root)
cl = (const_cast<TagDirectory*>(root))->clone (NULL);
else
cl = new TagDirectory (NULL, ifdAttribs, INTEL);
// add tiff strip data
int rps = 8;
int strips = ceil((double)H/rps);
cl->replaceTag (new Tag (cl, lookupAttrib(ifdAttribs,"RowsPerStrip"), rps, LONG));
Tag* stripBC = new Tag (cl, lookupAttrib(ifdAttribs,"StripByteCounts"));
stripBC->initInt (0, LONG, strips);
cl->replaceTag (stripBC);
Tag* stripOffs = new Tag (cl, lookupAttrib(ifdAttribs,"StripOffsets"));
stripOffs->initInt (0, LONG, strips);
cl->replaceTag (stripOffs);
for (int i=0; i<strips-1; i++)
stripBC->setInt (rps*W*3*bps/8, i*4);
int remaining = (H-rps*floor((double)H/rps))*W*3*bps/8;
if (remaining)
stripBC->setInt (remaining, (strips-1)*4);
else
stripBC->setInt (rps*W*3*bps/8, (strips-1)*4);
if (profiledata) {
Tag* icc = new Tag (cl, lookupAttrib(ifdAttribs,"ICCProfile"));
icc->initUndefArray (profiledata, profilelen);
cl->replaceTag (icc);
}
if (iptcdata) {
Tag* iptc = new Tag (cl, lookupAttrib(ifdAttribs,"IPTCData"));
iptc->initLongArray (iptcdata, iptclen);
cl->replaceTag (iptc);
}
// apply list of changes
for (rtengine::procparams::ExifPairs::const_iterator i=changeList.begin(); i!=changeList.end(); i++)
cl->applyChange (i->first, i->second);
// append default properties
getDefaultTIFFTags (cl);
defTags[0]->setInt (W, 0, LONG);
defTags[1]->setInt (H, 0, LONG);
defTags[8]->initInt(0, SHORT, 3);
for (int i=0;i<3;i++) defTags[8]->setInt(bps, i*2, SHORT);
for (int i=defTags.size()-1; i>=0; i--)
cl->replaceTag (defTags[i]->clone (cl));
// calculate strip offsets
int size = cl->calculateSize ();
int byps = bps / 8;
for (int i=0; i<strips; i++)
stripOffs->setInt (size + 8 + i*rps*W*3*byps, i*4);
cl->sort ();
int endOffs = cl->write (8, buffer);
// cl->printAll();
delete cl;
return endOffs;
}
//-----------------------------------------------------------------------------
// global functions to read byteorder dependent data
//-----------------------------------------------------------------------------
unsigned short sget2 (unsigned char *s, rtexif::ByteOrder order) {
if (order == rtexif::INTEL) return s[0] | s[1] << 8;
else return s[0] << 8 | s[1];
}
int sget4 (unsigned char *s, rtexif::ByteOrder order) {
if (order == rtexif::INTEL) return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24;
else return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3];
}
inline unsigned short get2 (FILE* f, rtexif::ByteOrder order) {
unsigned char str[2] = { 0xff,0xff };
fread (str, 1, 2, f);
return rtexif::sget2 (str, order);
}
int get4 (FILE* f, rtexif::ByteOrder order) {
unsigned char str[4] = { 0xff,0xff,0xff,0xff };
fread (str, 1, 4, f);
return rtexif::sget4 (str, order);
}
void sset2 (unsigned short v, unsigned char *s, rtexif::ByteOrder order) {
if (order == rtexif::INTEL) {
s[0] = v & 0xff; v >>= 8;
s[1] = v;
}
else {
s[1] = v & 0xff; v >>= 8;
s[0] = v;
}
}
void sset4 (int v, unsigned char *s, rtexif::ByteOrder order) {
if (order == rtexif::INTEL) {
s[0] = v & 0xff; v >>= 8;
s[1] = v & 0xff; v >>= 8;
s[2] = v & 0xff; v >>= 8;
s[3] = v;
}
else {
s[3] = v & 0xff; v >>= 8;
s[2] = v & 0xff; v >>= 8;
s[1] = v & 0xff; v >>= 8;
s[0] = v;
}
}
float int_to_float (int i) {
union { int i; float f; } u;
u.i = i;
return u.f;
}
short int int2_to_signed (short unsigned int i) {
union { short unsigned int i; short int s; } u;
u.i = i;
return u.s;
}
/* Function to parse and extract focal length and aperture information from description
* @fullname must conform to the following formats
* <focal>mm f/<aperture>
* <focal>-<focal>mm f/<aperture>
* <focal>-<focal>mm f/<aperture>-<aperture>
* NB: no space between separator '-'; no space between focal length and 'mm'
*/
bool extractLensInfo(std::string &fullname,double &minFocal, double &maxFocal, double &maxApertureAtMinFocal, double &maxApertureAtMaxFocal)
{
minFocal=0.0;
maxFocal=0.0;
maxApertureAtMinFocal=0.0;
maxApertureAtMaxFocal=0.0;
char buffer[1024];
strcpy(buffer,fullname.c_str());
char *pF = strstr(buffer,"f/" );
if( pF ){
sscanf(pF+2,"%lf-%lf",&maxApertureAtMinFocal,&maxApertureAtMaxFocal);
if(maxApertureAtMinFocal >0. && maxApertureAtMaxFocal==0.)
maxApertureAtMaxFocal= maxApertureAtMinFocal;
char *pMM = pF-3;
while( pMM[0]!= 'm' && pMM[1]!= 'm' && pMM>buffer) pMM--;
if( pMM[0]== 'm' && pMM[1]== 'm' ){
char *sp=pMM;
while( *sp != ' ' && sp > buffer )sp--;
sscanf(sp+1,"%lf-%lf",&minFocal,&maxFocal);
if(maxFocal==0.) {
maxFocal = minFocal;
}
return true;
}
}
return false;
}
}