diff options
Diffstat (limited to 'kfile-plugins/raw/parse.c')
-rw-r--r-- | kfile-plugins/raw/parse.c | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/kfile-plugins/raw/parse.c b/kfile-plugins/raw/parse.c new file mode 100644 index 00000000..1abbbfce --- /dev/null +++ b/kfile-plugins/raw/parse.c @@ -0,0 +1,1080 @@ +/* + Raw Photo Parser + Copyright 2004 by Dave Coffin, dcoffin a cybercom o net + + This program extracts thumbnail images (preferably JPEGs) + from any raw digital camera formats that have them, and + shows table contents. + + $Revision: 1.36 $ + $Date: 2005/05/10 21:43:10 $ + */ + +/* Hacked for thumbnail extraction in KDE by + Steffen Hansen <[email protected]> + + Based on parse.c and parts of dcraw.c by Dave Coffin +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <sys/types.h> + +#ifdef WIN32 +#include <winsock2.h> +typedef __int64 INT64; +#else +#include <netinet/in.h> +typedef long long INT64; +#endif + +/* + TIFF and CIFF data blocks can be quite large. + Display only the first DLEN bytes. + */ +#ifndef DLEN +#define DLEN 768 +#endif + +typedef unsigned char uchar; +/*typedef unsigned short ushort;*/ + +FILE *ifp; +short order; +char *fname; +char make[128], model[128], model2[128], thumb_head[128]; +int width, height, offset, length, bps, is_dng; +int thumb_offset, thumb_length, thumb_layers; +float cam_mul[4], pre_mul[4], coeff[3][4]; +#define camera_red cam_mul[0] +#define camera_blue cam_mul[2] +/*float flash_used, canon_5814;*/ +time_t timestamp; +/*int data_offset, meta_offset*/ +int raw_height, raw_width, top_margin, left_margin; +static int flip = 0; + +struct decode { + struct decode *branch[2]; + int leaf; +} first_decode[640], *free_decode; + +#define CLASS + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < 4; c++) +#define FORCC for (c=0; c < colors; c++) + +/* + Get a 2-byte integer, making no assumptions about CPU byte order. + Nor should we assume that the compiler evaluates left-to-right. + */ +ushort get2() +{ + uchar a, b; + + a = fgetc(ifp); b = fgetc(ifp); + + if (order == 0x4949) /* "II" means little-endian */ + return a | b << 8; + else /* "MM" means big-endian */ + return a << 8 | b; +} + +/* + Same for a 4-byte integer. + */ +int get4() +{ + uchar a, b, c, d; + + a = fgetc(ifp); b = fgetc(ifp); + c = fgetc(ifp); d = fgetc(ifp); + + if (order == 0x4949) + return a | b << 8 | c << 16 | d << 24; + else + return a << 24 | b << 16 | c << 8 | d; +} + +void tiff_dump(int base, int tag, int type, int count, int level) +{ + int save, j, num, den; + uchar c; + int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + + if (count * size[type < 13 ? type:0] > 4) + fseek (ifp, get4()+base, SEEK_SET); + save = ftell(ifp); + fseek (ifp, save, SEEK_SET); +} + +void nikon_decrypt (uchar ci, uchar cj, int tag, int i, int size, uchar *buf) +{ +} + +int parse_tiff_ifd (int base, int level); + +void nef_parse_makernote (base) +{ + int offset=0, entries, tag, type, count, val, save; + unsigned serial=0, key=0; + uchar buf91[630], buf97[608], buf98[31]; + short sorder; + char buf[10]; + +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + sorder = order; + fread (buf, 1, 10, ifp); + if (!strcmp (buf,"Nikon")) { /* starts with "Nikon\0\2\0\0\0" ? */ + base = ftell(ifp); + order = get2(); /* might differ from file-wide byteorder */ + val = get2(); /* should be 42 decimal */ + offset = get4(); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC")) + fseek (ifp, -4, SEEK_CUR); + else + fseek (ifp, -10, SEEK_CUR); + + entries = get2(); + if (entries > 100) return; + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + tiff_dump (base, tag, type, count, 2); + if (tag == 0x1d) + fscanf (ifp, "%d", &serial); + if (tag == 0x91) + fread (buf91, sizeof buf91, 1, ifp); + if (tag == 0x97) + fread (buf97, sizeof buf97, 1, ifp); + if (tag == 0x98) + fread (buf98, sizeof buf98, 1, ifp); + if (tag == 0xa7) + key = fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp); + + if (tag == 0x100 && type == 7 && !strncmp(make,"OLYMPUS",7)) { + thumb_offset = ftell(ifp); + thumb_length = count; + } + if (tag == 0x280 && type == 1) { /* EPSON */ + strncpy (thumb_head, "\xff", sizeof(thumb_head) ); + thumb_offset = ftell(ifp)+1; + thumb_length = count-1; + } + if (strstr(make,"Minolta") || strstr(make,"MINOLTA")) { + switch (tag) { + case 0x81: + thumb_offset = ftell(ifp); + thumb_length = count; + break; + case 0x88: + thumb_offset = get4() + base; + break; + case 0x89: + thumb_length = get4(); + } + } + if (!strcmp (buf,"OLYMP") && tag >> 8 == 0x20) + parse_tiff_ifd (base, 3); + fseek (ifp, save+12, SEEK_SET); + } + nikon_decrypt (serial, key, 0x91, 4, sizeof buf91, buf91); + nikon_decrypt (serial, key, 0x97, 284, sizeof buf97, buf97); + nikon_decrypt (serial, key, 0x98, 4, sizeof buf98, buf98); + order = sorder; +} + +void nef_parse_exif(int base) +{ + int entries, tag, type, count, save; + + entries = get2(); + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + tiff_dump (base, tag, type, count, 1); + if (tag == 0x927c) + nef_parse_makernote (base); + fseek (ifp, save+12, SEEK_SET); + } +} + +int parse_tiff_ifd (int base, int level) +{ + int entries, tag, type, count, slen, save, save2, val, i; + int comp=0; + static const int flip_map[] = { 0,1,3,2,4,6,7,5 }; + + entries = get2(); + if (entries > 255) return 1; + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + slen = count; + if (slen > 128) slen = 128; + + tiff_dump (base, tag, type, count, level); + + save2 = ftell(ifp); + if (type == 3) /* short int */ + val = get2(); + else + val = get4(); + fseek (ifp, save2, SEEK_SET); + + if (tag > 50700 && tag < 50800) + is_dng = 1; + + if (level == 3) { /* Olympus E-1 and E-300 */ + if (type == 4) { + if (tag == 0x101) + thumb_offset = val; + else if (tag == 0x102) + thumb_length = val; + } + goto cont; + } + switch (tag) { + case 0x100: /* ImageWidth */ + if (!width) width = val; + break; + case 0x101: /* ImageHeight */ + if (!height) height = val; + break; + case 0x102: /* Bits per sample */ + if (bps) break; + bps = val; + if (count == 1) + thumb_layers = 1; + break; + case 0x103: /* Compression */ + comp = val; + break; + case 0x10f: /* Make tag */ + fgets (make, slen, ifp); + break; + case 0x110: /* Model tag */ + fgets (model, slen, ifp); + break; + case 33405: /* Model2 tag */ + fgets (model2, slen, ifp); + break; + case 0x111: /* StripOffset */ + if (!offset || is_dng) offset = val; + break; + case 0x112: /* Orientation */ + flip = flip_map[(val-1) & 7]; + break; + case 0x117: /* StripByteCounts */ + if (!length || is_dng) length = val; + if (offset > val && !strncmp(make,"KODAK",5) && !is_dng) + offset -= val; + break; + case 0x14a: /* SubIFD tag */ + save2 = ftell(ifp); + for (i=0; i < count; i++) { + fseek (ifp, save2 + i*4, SEEK_SET); + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base, level+1); + } + break; + case 0x201: + if (strncmp(make,"OLYMPUS",7) || !thumb_offset) + thumb_offset = val; + break; + case 0x202: + if (strncmp(make,"OLYMPUS",7) || !thumb_length) + thumb_length = val; + break; + case 34665: + fseek (ifp, get4()+base, SEEK_SET); + nef_parse_exif (base); + break; + case 50706: + is_dng = 1; + } +cont: + fseek (ifp, save+12, SEEK_SET); + } + if ((comp == 6 && !strcmp(make,"Canon")) || + (comp == 7 && is_dng)) { + thumb_offset = offset; + thumb_length = length; + } + return 0; +} + +/* + Parse a TIFF file looking for camera model and decompress offsets. + */ +void parse_tiff (int base) +{ + int doff, spp=3, ifd=0; + + width = height = offset = length = bps = is_dng = 0; + fseek (ifp, base, SEEK_SET); + order = get2(); + if (order != 0x4949 && order != 0x4d4d) return; + get2(); + while ((doff = get4())) { + fseek (ifp, doff+base, SEEK_SET); + printf ("IFD #%d:\n", ifd++); + if (parse_tiff_ifd (base, 0)) break; + } + if (is_dng) return; + + if (strncmp(make,"KODAK",5)) + thumb_layers = 0; + if (!strncmp(make,"Kodak",5)) { + fseek (ifp, 12+base, SEEK_SET); + puts ("\nSpecial Kodak image directory:"); + parse_tiff_ifd (base, 0); + } + if (!strncmp(model,"DCS460A",7)) { + spp = 1; + thumb_layers = 0; + } + if (!thumb_length && offset) { + thumb_offset = offset; + sprintf (thumb_head, "P%d %d %d %d\n", + spp > 1 ? 6:5, width, height, (1 << bps) - 1); + thumb_length = width * height * spp * ((bps+7)/8); + } +} + +void parse_minolta() +{ + int data_offset, save, tag, len; + + fseek (ifp, 4, SEEK_SET); + data_offset = get4() + 8; + while ((save=ftell(ifp)) < data_offset) { + tag = get4(); + len = get4(); + printf ("Tag %c%c%c offset %06x length %06x\n", + tag>>16, tag>>8, tag, save, len); + switch (tag) { + case 0x545457: /* TTW */ + parse_tiff (ftell(ifp)); + } + fseek (ifp, save+len+8, SEEK_SET); + } + strncpy (thumb_head, "\xff", sizeof(thumb_head) ); + thumb_offset++; + thumb_length--; +} + +/* + Parse a CIFF file, better known as Canon CRW format. + */ +void parse_ciff (int offset, int length, int level /*unused*/) +{ + int tboff, nrecs, i, c, type, len, roff, aoff, save, wbi=-1; + static const int remap[] = { 1,2,3,4,5,1 }; + static const int remap_10d[] = { 0,1,3,4,5,6,0,0,2,8 }; + static const int remap_s70[] = { 0,1,2,9,4,3,6,7,8,9,10,0,0,0,7,0,0,8 }; + ushort key[] = { 0x410, 0x45f3 }; + + if (strcmp(model,"Canon PowerShot G6") && + strcmp(model,"Canon PowerShot S60") && + strcmp(model,"Canon PowerShot S70") && + strcmp(model,"Canon PowerShot Pro1")) + key[0] = key[1] = 0; + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4() + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(); + if (nrecs > 100) return; + for (i = 0; i < nrecs; i++) { + type = get2(); + len = get4(); + roff = get4(); + aoff = offset + roff; + save = ftell(ifp); + if (type == 0x080a) { /* Get the camera make and model */ + fseek (ifp, aoff, SEEK_SET); + fread (make, 64, 1, ifp); + fseek (ifp, aoff+strlen(make)+1, SEEK_SET); + fread (model, 64, 1, ifp); + } + if (type == 0x102a) { /* Find the White Balance index */ + fseek (ifp, aoff+14, SEEK_SET); /* 0=auto, 1=daylight, 2=cloudy ... */ + wbi = get2(); + if (((!strcmp(model,"Canon EOS DIGITAL REBEL") || + !strcmp(model,"Canon EOS 300D DIGITAL"))) && wbi == 6) + wbi++; + } + if (type == 0x102c) { /* Get white balance (G2) */ + if (!strcmp(model,"Canon PowerShot G1") || + !strcmp(model,"Canon PowerShot Pro90 IS")) { + fseek (ifp, aoff+120, SEEK_SET); + FORC4 cam_mul[c ^ 2] = get2(); + } else { + fseek (ifp, aoff+100, SEEK_SET); + goto common; + } + } + if (type == 0x0032) { /* Get white balance (D30 & G3) */ + if (!strcmp(model,"Canon EOS D30")) { + fseek (ifp, aoff+72, SEEK_SET); +common: + camera_red = get2() ^ key[0]; + camera_red =(get2() ^ key[1]) / camera_red; + camera_blue = get2() ^ key[0]; + camera_blue /= get2() ^ key[1]; + } else if (!strcmp(model,"Canon PowerShot G6") || + !strcmp(model,"Canon PowerShot S60") || + !strcmp(model,"Canon PowerShot S70")) { + fseek (ifp, aoff+96 + remap_s70[wbi]*8, SEEK_SET); + goto common; + } else if (!strcmp(model,"Canon PowerShot Pro1")) { + fseek (ifp, aoff+96 + wbi*8, SEEK_SET); + goto common; + } else { + fseek (ifp, aoff+80 + (wbi < 6 ? remap[wbi]*8 : 0), SEEK_SET); + if (!camera_red) + goto common; + } + } + if (type == 0x10a9) { /* Get white balance (D60) */ + if (!strcmp(model,"Canon EOS 10D")) + wbi = remap_10d[wbi]; + fseek (ifp, aoff+2 + wbi*8, SEEK_SET); + camera_red = get2(); + camera_red /= get2(); + camera_blue = get2(); + camera_blue = get2() / camera_blue; + } + /* Skip this for now /steffen */ +#if 0 + if (type == 0x1030 && (wbi == 6 || wbi == 15)) { + fseek (ifp, aoff, SEEK_SET); /* Get white sample */ + ciff_block_1030(); + } +#endif + if (type == 0x1031) { /* Get the raw width and height */ + fseek (ifp, aoff+2, SEEK_SET); + raw_width = get2(); + raw_height = get2(); + } + if (type == 0x180e) { /* Get the timestamp */ + fseek (ifp, aoff, SEEK_SET); + timestamp = get4(); + } + if (type == 0x580e) + timestamp = len; +#if 0 + if (type == 0x5813) + flash_used = *((float *) &len); + if (type == 0x5814) + canon_5814 = *((float *) &len); +#endif + if (type == 0x1810) { /* Get the rotation */ + fseek (ifp, aoff+12, SEEK_SET); + flip = get4(); + } + /* Skip this for now /steffen */ +#if 0 + if (type == 0x1835) { /* Get the decoder table */ + fseek (ifp, aoff, SEEK_SET); + crw_init_tables (get4()); + } +#endif + if (type == 0x2007) { /* Found the JPEG thumbnail */ + thumb_offset = aoff; + thumb_length = len; + } + if (type >> 8 == 0x28 || type >> 8 == 0x30) /* Get sub-tables */ + parse_ciff(aoff, len, level+1); + fseek (ifp, save, SEEK_SET); + } + if (wbi == 0 && !strcmp(model,"Canon EOS D30")) + camera_red = -1; /* Use my auto WB for this photo */ +} + + +void parse_mos(int level) +{ + uchar data[256]; + int i, j, skip, save; + char *cp; + + save = ftell(ifp); + while (1) { + fread (data, 1, 8, ifp); + if (strcmp(data,"PKTS")) break; + strcpy (model, "Valeo"); + fread (data, 1, 40, ifp); + skip = get4(); + if (!strcmp(data,"icc_camera_to_tone_matrix")) { + for (i=0; i < skip/4; i++) { + j = get4(); + } + continue; + } + if (!strcmp(data,"JPEG_preview_data")) { + thumb_head[0] = 0; + thumb_offset = ftell(ifp); + thumb_length = skip; + } + fread (data, 1, sizeof data, ifp); + fseek (ifp, -sizeof data, SEEK_CUR); + data[sizeof data - 1] = 0; + while ((cp=index(data,'\n'))) + *cp = ' '; + parse_mos(level+2); + fseek (ifp, skip, SEEK_CUR); + } + fseek (ifp, save, SEEK_SET); +} + +void parse_rollei() +{ + char line[128], *val; + + fseek (ifp, 0, SEEK_SET); + do { + fgets (line, 128, ifp); + fputs (line, stdout); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + if (!strcmp(line,"HDR")) + thumb_offset = atoi(val); + if (!strcmp(line,"TX ")) + width = atoi(val); + if (!strcmp(line,"TY ")) + height = atoi(val); + } while (strncmp(line,"EOHD",4)); + strcpy (make, "Rollei"); + strcpy (model, "d530flex"); + thumb_length = width*height*2; +} + +void rollei_decode (FILE *tfp) +{ + ushort data; + int row, col; + + fseek (ifp, thumb_offset, SEEK_SET); + fprintf (tfp, "P6\n%d %d\n255\n", width, height); + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + fread (&data, 2, 1, ifp); + data = ntohs(data); + putc (data << 3, tfp); + putc (data >> 5 << 2, tfp); + putc (data >> 11 << 3, tfp); + } +} + +void get_utf8 (int offset, char *buf, int len) +{ + ushort c; + char *cp; + + fseek (ifp, offset, SEEK_SET); + for (cp=buf; (c = get2()) && cp+3 < buf+len; ) { + if (c < 0x80) + *cp++ = c; + else if (c < 0x800) { + *cp++ = 0xc0 + (c >> 6); + *cp++ = 0x80 + (c & 0x3f); + } else { + *cp++ = 0xe0 + (c >> 12); + *cp++ = 0x80 + (c >> 6 & 0x3f); + *cp++ = 0x80 + (c & 0x3f); + } + } + *cp = 0; +} + +ushort sget2 (uchar *s) +{ + return s[0] + (s[1]<<8); +} + +int sget4 (uchar *s) +{ + return s[0] + (s[1]<<8) + (s[2]<<16) + (s[3]<<24); +} + +void parse_foveon() +{ + int entries, img=0, off, len, tag, save, i, j, k, pent, poff[256][2]; + char name[128], value[128], camf[0x20000], *pos, *cp, *dp; + unsigned val, key, type, num, ndim, dim[3]; + + order = 0x4949; /* Little-endian */ + fseek (ifp, -4, SEEK_END); + fseek (ifp, get4(), SEEK_SET); + if (get4() != 0x64434553) { /* SECd */ + printf ("Bad Section identifier at %6x\n", (int)ftell(ifp)-4); + return; + } + get4(); + entries = get4(); + while (entries--) { + off = get4(); + len = get4(); + tag = get4(); + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + if (get4() != (0x20434553 | (tag << 24))) { + printf ("Bad Section identifier at %6x\n", off); + goto next; + } + val = get4(); + switch (tag) { + case 0x32414d49: /* IMA2 */ + case 0x47414d49: /* IMAG */ + if (++img == 2) { /* second image */ + thumb_offset = off; + thumb_length = 1; + } + printf ("type %d, " , get4()); + printf ("format %2d, " , get4()); + printf ("columns %4d, " , get4()); + printf ("rows %4d, " , get4()); + printf ("rowsize %d\n" , get4()); + break; + case 0x464d4143: /* CAMF */ + printf ("type %d, ", get4()); + get4(); + for (i=0; i < 4; i++) + putchar(fgetc(ifp)); + val = get4(); + printf (" version %d.%d:\n",val >> 16, val & 0xffff); + key = get4(); + if ((len -= 28) > 0x20000) + len = 0x20000; + fread (camf, 1, len, ifp); + for (i=0; i < len; i++) { + key = (key * 1597 + 51749) % 244944; + val = key * (INT64) 301593171 >> 24; + camf[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } + for (pos=camf; (unsigned) (pos-camf) < len; pos += sget4(pos+8)) { + if (strncmp (pos, "CMb", 3)) { + printf("Bad CAMF tag \"%.4s\"\n", pos); + break; + } + val = sget4(pos+4); + printf (" %4.4s version %d.%d: ", pos, val >> 16, val & 0xffff); + switch (pos[3]) { + case 'M': + cp = pos + sget4(pos+16); + type = sget4(cp); + ndim = sget4(cp+4); + dim[0] = dim[1] = dim[2] = 1; + printf ("%d-dimensonal array %s of type %d:\n Key: (", + ndim, pos+sget4(pos+12), sget4(cp)); + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + printf ("%s %d%s", pos+sget4(cp+4), dim[i], i ? ", ":")\n"); + } + for (i=0; i < dim[2]; i++) { + for (j=0; j < dim[1]; j++) { + printf (" "); + for (k=0; k < dim[0]; k++) + switch (type) { + case 0: + case 6: + printf ("%7d", sget2(dp)); + dp += 2; + break; + case 1: + case 2: + printf (" %d", sget4(dp)); + dp += 4; + break; + case 3: { + union { int ival; float fval; } __t; + __t.ival = sget4(dp); + printf (" %9f", __t.fval); + dp += 4; + } + } + printf ("\n"); + } + printf ("\n"); + } + break; + case 'P': + val = sget4(pos+16); + num = sget4(pos+val); + printf ("%s, %d parameters:\n", pos+sget4(pos+12), num); + cp = pos+val+8 + num*8; + for (i=0; i < num; i++) { + val += 8; + printf (" %s = %s\n", cp+sget4(pos+val), cp+sget4(pos+val+4)); + } + break; + case 'T': + cp = pos + sget4(pos+16); + printf ("%s = %.*s\n", pos+sget4(pos+12), sget4(cp), cp+4); + break; + default: + printf ("\n"); + } + } + break; + case 0x504f5250: /* PROP */ + printf ("entries %d, ", pent=get4()); + printf ("charset %d, ", get4()); + get4(); + printf ("nchars %d\n", get4()); + off += pent*8 + 24; + if (pent > 256) pent=256; + for (i=0; i < pent*2; i++) + poff[0][i] = off + get4()*2; + for (i=0; i < pent; i++) { + get_utf8 (poff[i][0], name, 128); + get_utf8 (poff[i][1], value, 128); + printf (" %s = %s\n", name, value); + if (!strcmp (name,"CAMMANUF")) + strncpy (make, value, sizeof(make)); + if (!strcmp (name,"CAMMODEL")) + strncpy (model, value, sizeof(value)); + } + } +next: + fseek (ifp, save, SEEK_SET); + } +} + +void foveon_tree (unsigned huff[1024], unsigned code) +{ + struct decode *cur; + int i, len; + + cur = free_decode++; + if (code) { + for (i=0; i < 1024; i++) + if (huff[i] == code) { + cur->leaf = i; + return; + } + } + if ((len = code >> 27) > 26) return; + code = (len+1) << 27 | (code & 0x3ffffff) << 1; + + cur->branch[0] = free_decode; + foveon_tree (huff, code); + cur->branch[1] = free_decode; + foveon_tree (huff, code+1); +} + +void foveon_decode (FILE *tfp) +{ + int bwide, row, col, bit=-1, c, i; + char *buf; + struct decode *dindex; + short pred[3]; + unsigned huff[1024], bitbuf=0; + + fseek (ifp, thumb_offset+16, SEEK_SET); + width = get4(); + height = get4(); + bwide = get4(); + fprintf (tfp, "P6\n%d %d\n255\n", width, height); + if (bwide > 0) { + buf = malloc(bwide); + for (row=0; row < height; row++) { + fread (buf, 1, bwide, ifp); + fwrite (buf, 3, width, tfp); + } + free (buf); + return; + } + for (i=0; i < 256; i++) + huff[i] = get4(); + memset (first_decode, 0, sizeof first_decode); + free_decode = first_decode; + foveon_tree (huff, 0); + + for (row=0; row < height; row++) { + memset (pred, 0, sizeof pred); + if (!bit) get4(); + for (col=bit=0; col < width; col++) { + for (c=0; c < 3; c++) { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += dindex->leaf; + fputc (pred[c], tfp); + } + } + } +} + +void kodak_yuv_decode (FILE *tfp) +{ + uchar c, blen[384]; + unsigned row, col, len, bits=0; + INT64 bitbuf=0; + int i, li=0, si, diff, six[6], y[4], cb=0, cr=0, rgb[3]; + ushort *out, *op; + + fseek (ifp, thumb_offset, SEEK_SET); + width = (width+1) & -2; + height = (height+1) & -2; + fprintf (tfp, "P6\n%d %d\n65535\n", width, height); + out = malloc (width * 12); + if (!out) { + fprintf (stderr, "kodak_yuv_decode() malloc failed!\n"); + exit(1); + } + + for (row=0; row < height; row+=2) { + for (col=0; col < width; col+=2) { + if ((col & 127) == 0) { + len = (width - col + 1) * 3 & -4; + if (len > 384) len = 384; + for (i=0; i < len; ) { + c = fgetc(ifp); + blen[i++] = c & 15; + blen[i++] = c >> 4; + } + li = bitbuf = bits = y[1] = y[3] = cb = cr = 0; + if (len % 8 == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + } + for (si=0; si < 6; si++) { + len = blen[li++]; + if (bits < len) { + for (i=0; i < 32; i+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(i^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + six[si] = diff; + } + y[0] = six[0] + y[1]; + y[1] = six[1] + y[0]; + y[2] = six[2] + y[3]; + y[3] = six[3] + y[2]; + cb += six[4]; + cr += six[5]; + for (i=0; i < 4; i++) { + op = out + ((i >> 1)*width + col+(i & 1)) * 3; + rgb[0] = y[i] + 1.40200/2 * cr; + rgb[1] = y[i] - 0.34414/2 * cb - 0.71414/2 * cr; + rgb[2] = y[i] + 1.77200/2 * cb; + for (c=0; c < 3; c++) + if (rgb[c] > 0) op[c] = htons(rgb[c]); + } + } + fwrite (out, sizeof *out, width*6, tfp); + } + free(out); +} + +void parse_phase_one (int base) +{ + unsigned entries, tag, type, len, data, save; + char str[256]; + + fseek (ifp, base + 8, SEEK_SET); + fseek (ifp, base + get4(), SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + type = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + printf ("Phase One tag=0x%x, type=%d, len=%2d, data = 0x%x\n", + tag, type, len, data); + if (type == 1 && len < 256) { + fseek (ifp, base + data, SEEK_SET); + fread (str, 256, 1, ifp); + puts (str); + } + if (tag == 0x110) { + thumb_offset = data + base; + thumb_length = len; + } + fseek (ifp, save, SEEK_SET); + } + strcpy (make, "Phase One"); + strcpy (model, "unknown"); +} + +void parse_jpeg (int offset) +{ + int len, save, hlen; + + fseek (ifp, offset, SEEK_SET); + if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return; + + while (fgetc(ifp) == 0xff && fgetc(ifp) >> 4 != 0xd) { + order = 0x4d4d; + len = get2() - 2; + save = ftell(ifp); + order = get2(); + hlen = get4(); + if (get4() == 0x48454150) /* "HEAP" */ + parse_ciff (save+hlen, len-hlen, 0); + parse_tiff (save+6); + fseek (ifp, save+len, SEEK_SET); + } +} + +char *raw_memmem (char *haystack, size_t haystacklen, + char *needle, size_t needlelen) +{ + char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return NULL; +} + +/* + Identify which camera created this file, and set global variables + accordingly. + Return nonzero if the file cannot be decoded or no thumbnail is found + */ +int identify(FILE* tfp) +{ + char head[32], *thumb, *rgb, *cp; + unsigned hlen, fsize, toff, tlen, lsize, i; + + make[0] = model[0] = model2[0] = is_dng = 0; + thumb_head[0] = thumb_offset = thumb_length = thumb_layers = 0; + order = get2(); + hlen = get4(); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((cp = raw_memmem (head, 32, "MMMMRawT", 8)) || + (cp = raw_memmem (head, 32, "IIIITwaR", 8))) + parse_phase_one (cp - head); + else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp(head+6,"HEAPCCDR",8)) { + parse_ciff (hlen, fsize - hlen, 0); + fseek (ifp, hlen, SEEK_SET); + } else + parse_tiff (0); + } else if (!memcmp (head, "\0MRM", 4)) + parse_minolta(); + else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) && + !memcmp (head+6, "Exif", 4)) { + parse_tiff (12); + thumb_length = 0; + } else if (!memcmp (head, "FUJIFILM", 8)) { + fseek (ifp, 84, SEEK_SET); + toff = get4(); + tlen = get4(); + thumb_offset = toff; + thumb_length = tlen; + } else if (!memcmp (head, "DSC-Image", 9)) + parse_rollei(); + else if (!memcmp (head, "FOVb", 4)) + parse_foveon(); + fseek (ifp, 8, SEEK_SET); + parse_mos(0); + fseek (ifp, 3472, SEEK_SET); + parse_mos(0); + parse_jpeg(0); + + if (!thumb_length) { + fprintf (stderr, "Thumbnail image not found\n"); + return -1; + } + + if (is_dng) goto dng_skip; + if (!strncmp(model,"DCS Pro",7)) { + kodak_yuv_decode (tfp); + goto done; + } + if (!strcmp(make,"Rollei")) { + rollei_decode (tfp); + goto done; + } + if (!strcmp(make,"SIGMA")) { + foveon_decode (tfp); + goto done; + } +dng_skip: + thumb = (char *) malloc(thumb_length); + if (!thumb) { + fprintf (stderr, "Cannot allocate %d bytes!!\n", thumb_length); + exit(1); + } + fseek (ifp, thumb_offset, SEEK_SET); + fread (thumb, 1, thumb_length, ifp); + if (thumb_layers && !is_dng) { + rgb = (char *) malloc(thumb_length); + if (!rgb) { + fprintf (stderr, "Cannot allocate %d bytes!!\n", thumb_length); + return -1; + } + lsize = thumb_length/3; + for (i=0; i < thumb_length; i++) + rgb[(i%lsize)*3 + i/lsize] = thumb[i]; + free(thumb); + thumb = rgb; + } + fputs (thumb_head, tfp); + fwrite(thumb, 1, thumb_length, tfp); + free (thumb); +done: + fprintf (stderr, "Thumbnail image written, make=%s, model=%s\n",&(make[0]),&(model[0])); + return 0; +} + +int extract_thumbnail( FILE* input, FILE* output, int* orientation ) +{ + /* Coffin's code has different meaning for orientation + values than TIFF, so we map them to TIFF values */ + static const int flip_map[] = { 0,1,3,2,4,7,5,6 }; + int rc; + ifp = input; + rc = identify(output); + switch ((flip+3600) % 360) { + case 270: flip = 5; break; + case 180: flip = 3; break; + case 90: flip = 6; + } + if( orientation ) *orientation = flip_map[flip%7]; + return rc; +} |