ref: ec5795bea63188b508fe1079174510072e7297cf
dir: /exif.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
typedef struct IfdRec IfdRec;
typedef struct ExifData ExifData;
typedef struct EnumVal EnumVal;
enum{
BigEndian,
LittleEndian,
};
enum{
ExifTag=0x8769,
GpsTag=0x8825,
};
enum{
ImageDescription=0x010e,
Make=0x010f,
Model=0x0110,
Orientation=0x0112,
XResolution=0x011a,
YResolution=0x011b,
ResolutionUnit=0x0128,
Software=0x0131,
DateTime=0x0132,
Artist=0x013b,
WhitePoint=0x013e,
PrimaryChromaticities=0x013f,
YCbCrCoefficients=0x0211,
YCbCrPositioning=0x0213,
ReferenceBlackWhite=0x0214,
Copyright=0x8298,
ExposureTime=0x829a,
FNumber=0x829d,
ExposureProgram=0x8822,
ISOSpeedRatings=0x8827,
SensitivityType=0x8830,
DateTimeOriginal=0x9003,
DateTimeDigitized=0x9004,
CompressedBitsPerPixel=0x9102,
ExposureBiasValue=0x9204,
MaxApertureValue=0x9205,
MeteringMode=0x9207,
LightSource=0x9208,
Flash=0x9209,
FocalLength=0x920a,
MakerNote=0x927c,
ColorSpace=0xa001,
PixelXDimension=0xa002,
PixelYDimension=0xa003,
InteroperabilityTag=0xa005,
SensingMethod=0xa217,
CustomRendered=0xa401,
ExposureMode,
WhiteBalance,
DigitalZoomRatio,
FocalLengthIn35mmFilm,
SceneCaptureType,
GainControl,
Contrast,
Saturation,
Sharpness,
};
enum{
Tbyte = 1,
Tascii = 2,
Tshort = 3,
Tlong = 4,
Tratio = 5,
Tundef = 7,
Tslong = 9,
Tsratio = 10,
};
static int prenum(IfdRec*, int, int, void*);
static int prascii(IfdRec*, int, int, void*);
static int prbyte(IfdRec*, int, Biobuf*, void*);
static int prshort(IfdRec*, int, int, void*);
static int prlong(IfdRec*, int, int, void*);
static int prratio(IfdRec*, int, int, void*);
static int prslong(IfdRec*, int, int, void*);
static int prsratio(IfdRec*, int, int, void*);
static int prdec(IfdRec*, int, int, void*);
static int prundef(IfdRec*, int, int, void*);
static int prifdrec(IfdRec*, int, Biobuf*);
struct IfdRec
{
u16int tag;
u16int fmt;
u32int cnt;
u8int val[4];
u32int ival;
};
struct ExifData
{
u16int tag;
char str[32];
uchar show;
int (*print)(IfdRec*, int, int, void*);
void *aux;
};
struct EnumVal
{
u32int val;
char *str;
};
int aflag, uflag, rflag;
static EnumVal orientations[] = {
{ 1, "upper left" },
{ 3, "lower right" },
{ 6, "upper right" },
{ 8, "lower left" },
{ 9, "undefined" },
{ 0, nil }
};
static EnumVal resunits[] = {
{ 1, "no unit" },
{ 2, "inches" },
{ 3, "centimeters" },
{ 0, nil }
};
static EnumVal ycbcrpos[] = {
{ 1, "centered" },
{ 2, "co-sited" },
{ 0, nil }
};
static ExifData exiftab[] = {
{ Artist, "Artist", 0 },
{ DateTime, "DateTime", 1 },
{ ImageDescription, "ImageDescription", 1 },
{ Make, "Make", 1 },
{ Model, "Model", 1 },
{ Orientation, "Orientation", 0, prenum, orientations },
{ ResolutionUnit, "ResolutionUnit", 0, prenum, resunits },
{ Software, "Software", 1 },
{ XResolution, "XResolution", 0, prdec },
{ YResolution, "YResolution", 0, prdec },
{ YCbCrPositioning, "YCbCrPositioning", 0, prenum, ycbcrpos },
{ ExposureTime, "ExposureTime", 0, prdec, "s" },
{ FNumber, "FNumber", 0, prdec },
{ ExposureProgram, "ExposureProgram" },
{ ISOSpeedRatings, "ISOSpeedRatings" },
{ SensitivityType, "SensitivityType" },
{ DateTimeOriginal, "DateTimeOriginal" },
{ DateTimeDigitized, "DateTimeDigitized" },
{ CompressedBitsPerPixel, "CompressedBitsPerPixel", 0, prdec },
{ ExposureBiasValue, "ExposureBiasValue", 0, prdec },
{ MaxApertureValue, "MaxApertureValue", 0, prdec },
{ MeteringMode, "MeteringMode" },
{ LightSource, "LightSource" },
{ Flash, "Flash" },
{ FocalLength, "FocalLength", 0, prdec, "mm" },
{ ColorSpace, "ColorSpace" },
{ PixelXDimension, "PixelXDimension" },
{ PixelYDimension, "PixelYDimension" },
{ InteroperabilityTag, "InteroperabilityTag" },
{ SensingMethod, "SensingMethod" },
{ CustomRendered, "CustomRendered" },
{ ExposureMode, "ExposureMode" },
{ WhiteBalance, "WhiteBalance" },
{ DigitalZoomRatio, "DigitalZoomRatio", 0, prdec },
{ FocalLengthIn35mmFilm, "FocalLengthIn35mmFilm" },
{ SceneCaptureType, "SceneCaptureType" },
{ GainControl, "GainControl" },
{ Contrast, "Contrast" },
{ Saturation, "Saturation" },
{ Sharpness, "Sharpness" },
{ 0 }
};
static u16int
get16(uchar *b, int e)
{
if(e == BigEndian)
return (u16int)b[0]<<8 | b[1];
return (u16int)b[1]<<8 | b[0];
}
static int
read16(Biobuf *r, int e, u16int *v)
{
u8int b[2];
if(Bread(r, b, 2) != 2)
return -1;
if(v != nil)
*v = get16(b, e);
return 2;
}
static u32int
get32(uchar *b, int bo)
{
u32int h1, h2;
h1 = get16(&b[0], bo);
h2 = get16(&b[2], bo);
if(bo == BigEndian)
return h1<<16 | h2;
return h2<<16 | h1;
}
static int
read32(Biobuf *r, int e, u32int *v)
{
u8int b[4];
if(Bread(r, b, 4) != 4)
return -1;
if(v != nil)
*v = get32(b, e);
return 4;
}
static int
prenum(IfdRec *r, int, int, void *aux)
{
EnumVal *e;
e = aux;
if(r->cnt == 1){
while(e->str != nil){
if(e->val == r->ival)
return print("%s", e->str);
e++;
}
}
return print("unknown");
}
static char*
fmtstr(u8int fmt)
{
switch(fmt){
case Tbyte: return "byte";
case Tascii: return "ascii";
case Tshort: return "short";
case Tlong: return "long";
case Tratio: return "ratio";
case Tundef: return "undef";
case Tslong: return "slong";
case Tsratio: return "sratio";
}
return "inval";
}
static int
prifdrec(IfdRec *r, int bo, Biobuf *b)
{
ExifData *e;
for(e = exiftab; e->tag != 0; e++){
if(e->tag != r->tag)
continue;
break;
}
if(e->show || aflag){
if(*e->str != 0){
print("%s: ", e->str);
}else if(uflag)
print("unknown(0x%04uhx,%s): ", r->tag, fmtstr(r->fmt));
else
return 0;
if(rflag == 0 && e->print)
e->print(r, bo, b->fid, e->aux);
else
switch(r->fmt){
case Tbyte:
prbyte(r, bo, b, nil);
break;
case Tascii:
prascii(r, bo, b->fid, nil);
break;
case Tshort:
prshort(r, bo, b->fid, nil);
break;
case Tlong:
prlong(r, bo, b->fid, nil);
break;
case Tratio:
prratio(r, bo, b->fid, nil);
break;
case Tslong:
prslong(r, bo, b->fid, nil);
break;
case Tsratio:
prsratio(r, bo, b->fid, nil);
break;
case Tundef:
prundef(r, bo, b->fid, nil);
break;
}
print("\n");
}
return 0;
}
static int
prlong0(IfdRec *r, int bo, int fd, void*, int s)
{
int i;
uchar buf[4];
char *fmt;
fmt = s ? "%d" : "%ud";
switch(r->cnt){
case 0:
return 0;
case 1:
return print(fmt, get32(r->val, bo));
}
for(i = 0; i < r->cnt; i++){
if(pread(fd, buf, 2, r->ival + 12 + 4*i) < 0)
return -1;
print(fmt, get32(buf, bo));
}
return 0;
}
static int
prslong(IfdRec *r, int bo, int fd, void *aux)
{
return prlong0(r, bo, fd, aux, 1);
}
static int
prlong(IfdRec *r, int bo, int fd, void *aux)
{
return prlong0(r, bo, fd, aux, 0);
}
static int
prshort(IfdRec *r, int bo, int fd, void*)
{
int i;
uchar buf[2];
switch(r->cnt){
case 0:
return 0;
case 1:
return print("%uhd", get16(r->val, bo));
case 2:
return print("%uhd %uhd", get16(r->val, bo), get16(r->val+2, bo));
}
for(i = 0; i < r->cnt; i++){
if(pread(fd, buf, 2, r->ival + 12 + 2*i) < 0)
return -1;
print("%uhd", get16(buf, bo));
}
return 0;
}
static int
prratio0(IfdRec *r, int bo, int fd, void*, int s)
{
int i;
long n;
uchar buf[256];
n = sizeof(buf);
if(8*r->cnt < n)
n = 8*r->cnt;
if((n = pread(fd, buf, n, r->ival + 12)) < 0)
return -1;
for(i = 0; i < n; i += 8){
if(i > 0)
print(" ");
print(s ? "%d/%d" : "%ud/%ud", get32(&buf[i], bo), get32(&buf[i+4], bo));
}
return 0;
}
static int
prsratio(IfdRec *r, int bo, int fd, void *aux)
{
return prratio0(r, bo, fd, aux, 1);
}
static int
prratio(IfdRec *r, int bo, int fd, void *aux)
{
return prratio0(r, bo, fd, aux, 0);
}
static int
prdec0(IfdRec *r, int bo, int fd, void *aux, int s)
{
int i;
char *suffix;
uchar buf[8];
u32int n, d;
float f;
suffix = aux;
if(suffix == nil)
suffix = "";
for(i = 0; i < r->cnt; i++){
if(pread(fd, buf, 8, r->ival + 12 + 8*i) < 0)
return -1;
if(i > 0)
print(" ");
n = get32(&buf[i], bo);
d = get32(&buf[i+4], bo);
f = s ? (float)(long)n/(long)d : (float)n/d;
print("%g%s", f, suffix);
}
return 0;
}
static int
prdec(IfdRec *r, int bo, int fd, void *aux)
{
return prdec0(r, bo, fd, aux, r->fmt == Tsratio);
}
static int
prbyte(IfdRec *r, int, Biobuf *b, void*)
{
int c, i;
if(r->cnt <= 4)
return print("%c %c %c %c",
r->val[0], r->val[1], r->val[2], r->val[3]);
for(i = 0; i < r->cnt; i++){
if((c = Bgetc(b)) < 0)
return -1;
if(i > 0)
print(" ");
print("%c", (char)c);
}
return 0;
}
static int
prascii(IfdRec *r, int, int fd, void*)
{
long n;
char buf[256];
if(r->cnt <= 4){
if(r->cnt > 1)
return write(1, r->val, r->cnt - 1);
return 0;
}
n = sizeof(buf);
if(r->cnt < n)
n = r->cnt;
if((n = pread(fd, buf, n, r->ival + 12)) < 0)
return -1;
return write(1, buf, n);
}
static int
prundef(IfdRec *r, int, int, void*)
{
return print("cnt=%uhd val=(0x%uhhx 0x%uhhx 0x%uhhx 0x%uhhx)",
r->cnt, r->val[0], r->val[1], r->val[2], r->val[3]);
}
static void
usage(void)
{
fprint(2, "usage: %s file...\n", argv0);
exits("usage");
}
static int
vparse(Biobuf *r, int e, char *fmt, va_list a)
{
int c, i, n;
u8int *x;
u16int w;
u32int dw;
for(;;){
n = 0;
switch(*fmt++){
case '\0':
return 0;
case 'b':
if((c = Bgetc(r)) < 0)
return -1;
*va_arg(a, uchar*) = (uchar)c;
break;
case 'w':
if(read16(r, e, &w) < 0)
return -1;
*va_arg(a, u16int*) = w;
break;
case 'W':
if(read32(r, e, &dw) < 0)
return -1;
*va_arg(a, u32int*) = dw;
break;
case 'X':
n += 2;
case 'x':
n += 2;
x = va_arg(a, uchar*);
for(i = 0; i < n; i++){
if((c = Bgetc(r)) < 0)
return -1;
*x++ = (uchar)c;
}
break;
}
}
}
static int
parse(Biobuf *r, int e, char *fmt, ...)
{
int n;
va_list a;
va_start(a, fmt);
n = vparse(r, e, fmt, a);
va_end(a);
return n;
}
static int
dumpinfo(Biobuf *r)
{
char buf[64];
u16int w, soi, appn, len, ver, nents;
u32int ifdoff, exiftag, gpstag;
int i, bo, total;
IfdRec rec;
bo = BigEndian;
total = 0;
if(parse(r, bo, "www", &soi, &appn, &len) < 0)
goto Badfmt;
if(soi != 0xffd8 || (appn & 0xfff0) != 0xffe0)
goto Badfmt;
switch(appn & 0xf){
case 1:
break;
case 0:
fprint(2, "JFIF\n");
default:
goto Badfmt;
}
if(Bread(r, buf, 6) != 6 || strncmp(buf, "Exif", 6) != 0)
goto Badfmt;
if(read16(r, bo, &w) < 0)
goto Badfmt;
switch(w){
case 0x4949:
bo = LittleEndian;
break;
case 0x4d4d:
bo = BigEndian;
break;
default:
goto Badfmt;
}
if(parse(r, bo, "wW", &ver, &ifdoff) < 0)
goto Badfmt;
if(ver != 42 || ifdoff != 8)
goto Badfmt;
exiftag = 0;
gpstag = 0;
for(;;){
if(parse(r, bo, "w", &nents) < 0)
goto Badfmt;
for(i = 0; i < nents; i++){
memset(&rec, 0, sizeof(rec));
if(parse(r, bo, "wwWX", &rec.tag, &rec.fmt, &rec.cnt, rec.val) < 0)
goto Badfmt;
rec.ival = get32(rec.val, bo);
switch(rec.tag){
case ExifTag:
exiftag = rec.ival;
break;
case GpsTag:
gpstag = rec.ival;
break;
default:
prifdrec(&rec, bo, r);
break;
}
total++;
}
if(parse(r, bo, "w", &ifdoff) < 0)
goto Badfmt;
if(ifdoff == 0){
if(exiftag != 0){
ifdoff = exiftag;
exiftag = 0;
}else if(gpstag != 0){
ifdoff = gpstag;
gpstag = 0;
}else
break;
}
Bseek(r, ifdoff+12, 0);
}
return total;
Badfmt:
werrstr("unknown file format");
return -1;
}
void
main(int argc, char **argv)
{
int i;
Biobuf *r;
ARGBEGIN{
case 'a':
aflag++;
break;
case 'u':
uflag++;
break;
case 'r':
rflag++;
break;
default:
usage();
}ARGEND;
if(argc < 1)
usage();
for(i = 0; i < argc; i++){
r = Bopen(argv[i], OREAD);
if(r == nil)
sysfatal("open: %r");
if(i > 0)
print("\n");
print("File: %s\n", argv[i]);
switch(dumpinfo(r)){
case -1:
sysfatal("%r");
case 0:
fprint(2, "no exif data\n");
}
Bterm(r);
}
}