Enhancements: parsing for Leaf exif data & Phase One orientation;
Fix for Time in reading exif:DateTimeOriginal - REQUIRES CLEARING CACHE to take an effect on images prviously viewed in RT; - on behalf of Torger (issue 766)
This commit is contained in:
@@ -119,7 +119,7 @@ void ImageData::extractInfo () {
|
||||
static const char *corp[] =
|
||||
{ "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX",
|
||||
"MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One",
|
||||
"SAMSUNG", "Mamiya", "MOTOROLA" };
|
||||
"SAMSUNG", "Mamiya", "MOTOROLA", "Leaf" };
|
||||
for (size_t i=0; i < (sizeof(corp)/sizeof(*corp)); i++)
|
||||
if ( make.find( corp[i] ) != std::string::npos ){ /* Simplify company names */
|
||||
make = corp[i];
|
||||
@@ -175,6 +175,7 @@ void ImageData::extractInfo () {
|
||||
if (sscanf ((const char*)exif->getTag("DateTimeOriginal")->getValue(), "%d:%d:%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec) == 6) {
|
||||
time.tm_year -= 1900;
|
||||
time.tm_mon -= 1;
|
||||
time.tm_isdst = -1;
|
||||
timeStamp = mktime(&time);
|
||||
}
|
||||
}
|
||||
|
@@ -644,7 +644,7 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) {
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
// buffer for exif and iptc markers
|
||||
unsigned char* buffer = new unsigned char[165535]; //TODO: Is it really 165535... or 65535 ?
|
||||
unsigned char* buffer = new unsigned char[165535]; //FIXME: no buffer size check so it can be overflowed in createJPEGMarker() for large tags, and then software will crash
|
||||
unsigned int size;
|
||||
// assemble and write exif marker
|
||||
if (exifRoot) {
|
||||
|
261
rtexif/rtexif.cc
261
rtexif/rtexif.cc
@@ -1308,6 +1308,249 @@ void ExifManager::parseCIFF (FILE* f, int base, int length, TagDirectory* root)
|
||||
}
|
||||
}
|
||||
|
||||
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".
|
||||
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", 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", 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", 4) == 0 ||
|
||||
strncmp(&val[1], "PKTS", 4) == 0 ||
|
||||
strncmp(&val[2], "PKTS", 4) == 0 ||
|
||||
strncmp(&val[3], "PKTS", 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)
|
||||
{
|
||||
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) {
|
||||
t->initInt (atoi(val), 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, "%02d:%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
|
||||
@@ -1325,9 +1568,9 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
|
||||
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")) {
|
||||
Tag* make = root->getTag ("Make");
|
||||
if (make && !strncmp((char*)make->getValue(), "NIKON", 5)) {
|
||||
Tag* mn = exif->getDirectory()->getTag("MakerNote");
|
||||
if (mn) {
|
||||
@@ -1351,6 +1594,22 @@ TagDirectory* ExifManager::parse (FILE* f, int base, bool skipIgnored) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@@ -540,6 +540,7 @@ const TagAttrib iopAttribs[] = {
|
||||
{0, 1, 0, 0, 0x828d, "CFAPatternDim", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0x828e, "CFAPattern", &cfaInterpreter},
|
||||
{0, 1, 1, 0, 0x8298, "Copyright", &stdInterpreter},
|
||||
{0, 0, 0, 0, 0x8606, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format
|
||||
{0, 1, 0, exifAttribs, 0x8769, "Exif", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x8773, "ICCProfile", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x83BB, "IPTCData", &stdInterpreter},
|
||||
|
Reference in New Issue
Block a user