/* ------------------------------------------------------------------------------ MetaCam - Extract EXIF information from digital camera files, with support for Vendor specific blocks. Copyright (C) 2000 Daniel Stephens (daniel@cheeseplant.org) This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ------------------------------------------------------------------------------ */ #include #include #include #include #include "metacam.h" #include "dpyfuncs.h" static const char *rcsid __attribute__((unused))="$Id: dpyfuncs.cc,v 1.14 2004/08/21 17:54:20 daniel Exp $"; void dpyISO(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); ctx.startBlock(name) << v[1] << " (" << v[0] << ")"; ctx.endBlock(); } void dpyString(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getSTRING(); ctx.startBlock(name) << v[0]; ctx.endBlock(); } void displayRational(OutputContext &ctx, const tiffRATIONAL &r) { if (r.getNumerator() == 0) { ctx.os() << "0"; } else if (r.getDenominator() == 1) { ctx.os() << r.getNumerator(); } else { ctx.os() << r; } } void displayRational(OutputContext &ctx, const tiffSRATIONAL &r) { if (r.getNumerator() == 0) { ctx.os() << "0"; } else if (r.getDenominator() == 1) { ctx.os() << r.getNumerator(); } else { ctx.os() << r; } } void dpyLens(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); v[0] = v[0].normalize(); v[1] = v[1].normalize(); v[2] = v[2].normalize(); v[3] = v[3].normalize(); ctx.startBlock(name); if (v[0] == v[1]) { displayRational(ctx,v[0]); ctx.os() << "mm "; } else { displayRational(ctx,v[0]); ctx.os() << "-"; displayRational(ctx,v[1]); ctx.os() << "mm "; } if (v[2] == v[3]) { ctx.os() << "f" << (double)v[2]; } else { ctx.os() << "f" << (double)v[2]; ctx.os() << "-"; ctx.os() << "f" << (double)v[3]; } ctx.endBlock(); } void dpyZoom(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); v[0] = v[0].normalize(); ctx.startBlock(name); // displayRational(ctx,v[0]); double d = v[0]; d *= 100.0; d = floor(d); d /= 100.0; ctx.os() << d << "mm"; ctx.endBlock(); } void dpyExpAdjust(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getSRATIONAL(); v[0] = v[0].normalize(); ctx.startBlock(name); double d = v[0]; d *= 1000.0; d = floor(d); d /= 1000.0; if (d > 0.0) {ctx.os() << "+";} ctx.os() << d << " EV"; ctx.endBlock(); } void dpyShutter(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); v[0]=v[0].normalize(); ctx.startBlock(name); displayRational(ctx,v[0]); ctx.os() << " Sec."; ctx.endBlock(); } void dpyAperture(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); ctx.startBlock(name) << "f" << (double)v[0]; ctx.endBlock(); } void dpyPixels(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); ctx.startBlock(name) << v[0] << " pixels"; ctx.endBlock(); } void dpySigned(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getSVALUE(); ctx.startBlock(name); if (v[0] > 0) { ctx.os() << "+" << v[0]; } else { ctx.os() << v[0]; } ctx.endBlock(); } void dpyUnsigned(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); ctx.startBlock(name) << v[0]; ctx.endBlock(); } void dpyResolution(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); v[0]=v[0].normalize(); ctx.startBlock(name); displayRational(ctx,v[0]); ctx.os() << " Pixels/" << ctx.getContextValue("resolutionUnit"); ctx.endBlock(); } void dpyNULL(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) {} void dpyYes(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { ctx.startBlock(name) << "Yes"; ctx.endBlock(); } void dpyResolutionType(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); const char *resolution_unit = "???"; switch (v[0]) { case 1: resolution_unit = "???"; break; case 2: resolution_unit = "Inch"; break; case 3: resolution_unit = "Centimeter"; break; } ctx.setContextValue("resolutionUnit", resolution_unit); } void dpyBitsPerSample(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); ctx.startBlock(name) << "("; vector::iterator iter; bool first=true; for (iter = v.begin(); iter != v.end(); ++iter) { if (first) { first=false; } else { ctx.os() << ","; } ctx.os() << (*iter); } ctx.os() << ")"; ctx.endBlock(); } const char *findLookup(int key, const void *table) { if (table == 0) return 0; const lookupValue *ltable = static_cast(table); /* Simplified test (Prohibits use of explicitly unknown values in lookups, but those would have been confusing anyway. */ for (;ltable->value; ++ltable) { if (ltable->key == key) return ltable->value; } return 0; } void dpyUnsignedLookup(OutputContext &ctx, const char *name, const IFDEntry &e, const void*tbl) { vector v = e.getUVALUE(); ctx.startBlock(name); const char *val = findLookup((int)v[0], tbl); if (val) { ctx.os() << val; } else { ctx.os() << "Unknown (" << v[0] << ")"; } ctx.endBlock(); } void dpyUndefinedLookup(OutputContext &ctx, const char *name, const IFDEntry &e, const void*tbl) { vector v = e.getOPAQUE(); ctx.startBlock(name); const char *val = findLookup((int)v[0], tbl); if (val) { ctx.os() << val; } else { ctx.os() << "Unknown (" << v[0] << ")"; } ctx.endBlock(); } void dpyExifVersion(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getOPAQUE(); ctx.startBlock(name); vector::iterator iter; for (iter=v.begin(); iter!=v.end(); ++iter) { unsigned char c = (*iter); if ((c < 32) || (c>126)) {c = ' ';} ctx.os() << c; } ctx.endBlock(); } void dpyExifAperture(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); ctx.startBlock(name); double a = v[0]; double f = pow(M_SQRT2, a); f = f * 10.0; f = floor(f); f = f / 10.0; ctx.os() << "f" << f; ctx.endBlock(); } void dpyExifShutter(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getSRATIONAL(); ctx.startBlock(name); double a = v[0]; if (a > 0.0) { double f = pow(2.0, a); ctx.os() << "1/" << f << " Sec."; } else { double f = pow(2.0, -a); ctx.os() << f << " Sec."; } ctx.endBlock(); } void dpyRationalAsDouble(OutputContext &ctx, const char *name, const IFDEntry &e, const void *units) { ctx.startBlock(name); if (e.getType() == tSRATIONAL) { vector v = e.getSRATIONAL(); if (v[0].getDenominator() == 0) { ctx.os() << "Infinity"; } else { double a = v[0]; ctx.os() << a; if (units) { ctx.os() << (const char *) units; } } } else { vector v = e.getRATIONAL(); if (v[0].getDenominator() == 0) { ctx.os() << "Infinity"; } else { double a = v[0]; ctx.os() << a; if (units) { ctx.os() << (const char *) units; } } } ctx.endBlock(); } void dpyOlymSpecialMode(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); ctx.startBlock(name); switch (v[0]) { case 0: ctx.os() << "Normal"; break; case 1: ctx.os() << "Unknown"; break; case 2: ctx.os() << "Fast"; break; case 3: ctx.os() << "Panorama"; break; default: ctx.os() << "Unknown (" << v[0] << ")"; break; } ctx.os() << "; Seq " << v[1]; if (v[0] == 3) { switch(v[2]) { case 1: ctx.os() << " Left -> Right"; break; case 2: ctx.os() << " Right -> Left"; break; case 3: ctx.os() << " Bottom -> Top"; break; case 4: ctx.os() << " Top -> Bottom"; break; default: ctx.os() << " Unknown (" << v[2] << ")"; break; } } ctx.endBlock(); } void dpyOlymZoom(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getRATIONAL(); ctx.startBlock(name); double a = v[0]; if (a == 0.0) { ctx.os() << "Normal"; ctx.endBlock(); return; } a = a * 100.0; a = floor(a); a = a / 100.0; ctx.os() << "x" << a; ctx.endBlock(); } static void fmt(OutputContext &ctx, const char *prefix, const char *value) { ctx.startBlock(prefix); ctx.os() << value; ctx.endBlock(); } void dpyCanonBlock1(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); try { int n = v[0] / 2; if (v[1]) fmt(ctx, "Macro Mode", v[1] == 1 ? "Macro" : "No Macro"); if (v[2]) { char tl[32]; snprintf(tl, sizeof tl, "%.1g", (double)v[2] / 10.0); fmt(ctx, "Self Timer", tl); } if (v[4]) { static char *flashmodes[] = { "Auto", "On", "Red-Eye Reduction", "Slow Synchro", "Auto + Red-Eye Reduction", "On + Red-Eye Reduction" }; if (v[4] <= 6) fmt(ctx, "Flash Mode", flashmodes[v[4]]); else if (v[4] == 16) fmt(ctx, "Flash Mode", "External"); } else fmt(ctx, "Flash Mode", "No Flash"); fmt(ctx, "Drive Mode", v[5] ? "Continuous" : (v[2] ? "Timer" : "Normal")); if (n >= 32 && v[32]) { fmt(ctx, "Focus Mode", "Continuous"); } else if (v[7] <= 6) { static char *focusModes[] = { "One-Shot", "AI Servo", "AI Focus", "Manual Focus", "Single Focus", "Continuous Focus", "Manual Focus" }; fmt(ctx, "Focus Mode", focusModes[v[7]]); } if (v[10] <= 2) { static char *imageSizes[] = { "Large", "Medium", "Small" }; fmt(ctx, "Image Size", imageSizes[v[10]]); } { static char *adjustment[] = { "Low", "Normal", "High", "unknown" }; fmt(ctx, "Contrast", adjustment[((short)v[13] + 1) & 0x3]); fmt(ctx, "Saturation", adjustment[((short)v[14] + 1) & 0x3]); fmt(ctx, "Sharpness", adjustment[((short)v[15] + 1) & 0x3]); } if (v[16] >= 15 && v[16] <= 19) { static char *speeds[] = { "auto", "50", "100", "200", "400" }; fmt(ctx, "ISO Speed", speeds[v[16] - 15]); } if (v[17] >= 3 && v[17] <= 5) { static char *meterModes[] = { "Evaluative", "Partial", "Center-weighted" }; fmt(ctx, "Metering Mode", meterModes[v[17] - 3]); } if (v[18] >= 0x3000 && v[18] <= 0x3004) { static char *afPoints[] = { "Manual Focus", "Auto-Select", "Right", "Center", "Left" }; fmt(ctx, "Autofocus Point", afPoints[v[18] - 0x3000]); } if (v[20] <= 5) { static char *exposureModes[] = { "Preprogrammed", "Program", "Tv-priority", "Av-priority", "Manual", "A-DEP" }; fmt(ctx, "Exposure Mode", exposureModes[v[20]]); } if (v[20] == 0 && v[11] <= 11) { static char *shootModes[] = { "Full Auto", "Manual", "Landscape", "Fast Shutter", "Slow Shutter", "Night", "B&W", "Sepia", "Portrait", "Sports", "Macro / Close-Up", "Pan Focus" }; fmt(ctx, "Preprogrammed Mode", shootModes[v[11]]); } // printf("%d, %d, %d\n", v[23], v[24], v[25]); // DEBUG if (v[25] && v[23] && v[24]) { double maxFocalLen = v[23]; double minFocalLen = v[24]; double focalFactor = (double)v[25]; char zoom[32]; if (maxFocalLen == minFocalLen) { snprintf(zoom, sizeof zoom, "%.1gmm", maxFocalLen / focalFactor); } else { snprintf(zoom, sizeof zoom, "%g-%gmm", minFocalLen / focalFactor, maxFocalLen / focalFactor); } fmt(ctx, "Focal Length", zoom); } if (v[29]) { #define V29BIT(N) (v[29] & (1 << (N))) string flashDetails; if (V29BIT(14)) flashDetails += ", External E-TTL"; if (V29BIT(13)) flashDetails += ", Internal"; if (V29BIT(11)) flashDetails += ", FP Sync"; if (V29BIT(4)) flashDetails += ", FP Sync Enabled"; if (flashDetails != "") fmt(ctx, "Flash Details", flashDetails.data() + 2); } } catch (...) { return; } } extern void dpyCanonBlock4(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); try { // int n = v[0] / 2; if (v[7] <= 6) { static char *whiteBalances[] = { "Auto", "Sunny", "Cloudy", "Tungsten", "Fluorescent", "Flash", "Custom" }; fmt(ctx, "White Balance", whiteBalances[v[7]]); } if (v[9] > 1) { char n[32]; snprintf(n, sizeof n, "%u", (unsigned int)v[9]); fmt(ctx, "Burst Sequence Number", n); } { struct { unsigned short value; char *bias; } flashBias[] = { {0xffc0, "-2 EV"}, {0xffcc, "-1.67 EV"}, {0xffd0, "-1.50 EV"}, {0xffd4, "-1.33 EV"}, {0xffe0, "-1 EV"}, {0xffec, "-0.67 EV"}, {0xfff0, "-0.50 EV"}, {0xfff4, "-0.33 EV"}, {0x0000, "0 EV"}, {0x000c, "0.33 EV"}, {0x0010, "0.50 EV"}, {0x0014, "0.67 EV"}, {0x0020, "1 EV"}, {0x002c, "1.33 EV"}, {0x0030, "1.50 EV"}, {0x0034, "1.67 EV"}, {0x0040, "2 EV"}, {0, NULL} }; for (int fb = 0; flashBias[fb].bias != NULL; fb++) { if (v[15] == flashBias[fb].value) { fmt(ctx, "Flash Exposure Bias", flashBias[fb].bias); break; } } } } catch (...) { return; } } extern void dpyCanonImageNumber(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); try { unsigned long n = v[0]; char img[32]; snprintf(img, sizeof img, "%03lu-%04lu", n / 10000, n % 10000); ctx.startBlock(name) << img; ctx.endBlock(); } catch (...) { return; } } extern void dpyCanonSerialNumber(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); try { unsigned long n = v[0]; char sn[32]; snprintf(sn, sizeof sn, "%04lX%05lu", n >> 16, n & 0xffff); fmt(ctx, "Camera Serial Number", sn); } catch (...) { return; } } extern void dpyCasioDistance(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getUVALUE(); const char* units = "mm"; double dist = double(v[0]); if (dist > 1000.0) { dist /= 1000.0; units = "m"; } else if (dist > 10) { dist /= 10; units = "cm"; } ctx.startBlock(name) << dist << units; ctx.endBlock(); } extern void dpyExifFlash(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { ctx.startBlock(name); vector v = e.getUVALUE(); if (v[0] & 0x01) { ctx.os() << "Flash Fired; "; } else { ctx.os() << "Flash Not Fired; "; } if (v[0] & 0x04) { if (v[0] & 0x02) { ctx.os() << "Return Light Not Detected; "; } else { ctx.os() << "Return Light Detected; "; } } switch (v[0] & 0x18) { case 0x00: break; case 0x08: ctx.os() << "Compulsory Flash Firing; "; break; case 0x10: ctx.os() << "Compulsory Flash Suppression; "; break; case 0x18: ctx.os() << "Auto Mode; "; break; } if (v[0] & 0x20) { ctx.os() << "No Flash Function; "; } if (v[0] & 0x40) { ctx.os() << "Red-Eye Reduction Supported; "; } ctx.endBlock(); } extern void dpyExifComponentConfiguration(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { ctx.startBlock(name); vector v = e.getOPAQUE(); for (unsigned int i=0; i v = e.getSRATIONAL(); if (v[0].getNumerator() == 0) { ctx.os() << "None"; } else { displayRational(ctx, v[0]); ctx.os() << "x"; } } else { vector v = e.getRATIONAL(); if (v[0].getNumerator() == 0) { ctx.os() << "None"; } else { displayRational(ctx, v[0]); ctx.os() << "x"; } } ctx.endBlock(); } extern void dpyRationalDistance(OutputContext &ctx, const char *name, const IFDEntry &e, const void * units) { ctx.startBlock(name); if (e.getType() == tSRATIONAL) { vector v = e.getSRATIONAL(); if (v[0].getNumerator() == 0) { ctx.os() << "Unknown"; } else if (v[0].getDenominator() == 0) { ctx.os() << "Infinity"; } else { ctx.os() << (double)v[0]; if (units) { ctx.os() << (const char *) units; } } } else { vector v = e.getRATIONAL(); if (v[0].getNumerator() == 0) { ctx.os() << "Unknown"; } else if (v[0].getDenominator() == 0) { ctx.os() << "Infinity"; } else { ctx.os() << (double)v[0]; if (units) { ctx.os() << (const char *) units; } } } ctx.endBlock(); } extern void dpy35mmFocal(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { ctx.startBlock(name); vector v = e.getUVALUE(); if (v[0] == 0) { ctx.os() << "Unknown"; } else { ctx.os() << v[0] << "mm"; } ctx.endBlock(); } void dpyNikonFocusPosition(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getOPAQUE(); ctx.startBlock(name); const char *val = findLookup((int)v[1], lookFocusPosition); if (val) { ctx.os() << val; } else { ctx.os() << "Unknown (" << v[1] << ")"; } ctx.endBlock(); } void dpyTypedComment(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getOPAQUE(); ctx.startBlock(name); // The first 8 bytes specify the type char typecode[9]; for (unsigned int i = 0; i < 8; ++i) { if (i < v.size()) { typecode[i] = v[i]; } else { typecode[i] = 0; } } typecode[8]=0; if ((strcmp(typecode,"ASCII") == 0) || (typecode[0] == 0)) { for (unsigned int i = 8; i < v.size(); ++i) { if (isprint(v[i])) { ctx.os() << v[i]; } else { ctx.os() << "."; } } } else { ctx.os() << "Unknown encoding (" << typecode << ")"; } ctx.endBlock(); } void dpyUndefinedString(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getOPAQUE(); ctx.startBlock(name); for (unsigned int i = 0; i < v.size(); ++i) { if (v[i] == 0) { ctx.os() << ' '; } else if (isprint(v[i])) { ctx.os() << v[i]; } else { ctx.os() << "."; } } ctx.endBlock(); } // Nikkor Lens display method provided by Ro Ro // Updates for consistency with other modules by Daniel void dpyNikkorLensInfo(OutputContext &ctx, const char *name, const IFDEntry &e, const void*) { vector v = e.getOPAQUE(); ctx.startBlock(name); double d; if ((v[0x13] + v[0x14] + v[0x15] + v[0x16] + v[0x17])!=0) { // should be 'D' d = v[0x09]; // cm = 10^(x/40) d /= 40.0; d = pow(10,d); ctx.os() << "Distance: " << d << "cm; "; } d = v[0x12]; // calc k value - better use the tables from Ken Hancock d /= 24.0; d = pow(2,d); ctx.os() << "Effective Aperture Open: " << d << "; "; d = v[0x12]+v[0x0C]; d /= 24.0; d = pow(2,d); ctx.os() << "Effective Aperture Stopped Down: " << d << "; "; unsigned int c; c = v[0x0B]; if (c<16){ ctx.os() << "Lens ID = 0x0" << hex << c << dec << "; "; } else { ctx.os() << "Lens ID = 0x" << hex << c << dec << "; "; } c = v[0x11]; if (c<16) { ctx.os() << "Matrix Chip ID = 0x0" << hex << c<< dec << "; "; } else { ctx.os() << "Matrix Chip ID = 0x" << hex << c << dec << ";"; } // 76 - 0B - Lens ID // 7A - 11 - Lens Chip Number ctx.endBlock(); /* --------------------------------------------------------------------------------------- **** following documents by * Copyright (c) 1997 Ken Hancock * This document resides at http://members.aol.com/khancock/pilot/nbuddy/ **** provide infos about nikkor lenses - N90s-memory-map.txt 0xFE29 Aperature (zoomed setting ?) 0xFE2D Focus distance cm = 10^(x/40) 0xFE2E Zoom Position (SEE TABLES) 0xFE2F Lens Focal Length Range(Lo) (SEE kFocalTable) 0xFE30 Lens Focal Length Range(Hi) (SEE kFocalTable) 0xFE31 Lens Apperature Range(Lo) (SEE kCameraApertureTable) 0xFE32 Lens Apperature Range(Hi) (SEE kCameraApertureTable) 0xFE33 Lens ID 0xFE51 Effective Aperture (SEE kApertureTable) - NikonStringTables.c.txt const char * kFocalTable[] = { const char * kCameraApertureTable[] = { --------------------------------------------------------------------------------------- * a N90s is useful for comparing data * the Chip Number was guessed through luck: the AF 20mm/2.8 and the AF 35-70mm/3.3-4.5 use the same chip (NEC uPD7554G577) which is different wirded * sample data for Nikkor 50mm/1.8D [0x98-7]: 30 31 30 31 - "0101" ?? Revision 1.01 ?? 25 13 08 03 01 - ?? don't know 85 - 09 - Distance info if D-lens, but how to know? 50 - 0A - Zoom Position 76 - 0B - Lens ID 58 - 0C - Lens Apperature Stops 50 - 0D - Lens Focal Length Short 50 - 0E - Lens Focal Length Long 14 - 0F - Lens Apperature Short 14 - 10 - Lens Apperature Long 7A - 11 - Lens Chip Number 14 - 12 - Effective Aperture 17 42 33 43 06 - ?? only on D ?? 00 00 00 00 00 02 02 - ?? don't know ?? constant */ }