376 lines
9.1 KiB
C
376 lines
9.1 KiB
C
|
/*
|
||
|
fuji_green -- read Fuji green pixels
|
||
|
|
||
|
$Revision: 1.2 $
|
||
|
$Date: 2006/03/01 01:46:47 $
|
||
|
*/
|
||
|
|
||
|
#include <ctype.h>
|
||
|
#include <math.h>
|
||
|
#include <setjmp.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#define ushort UshORt
|
||
|
typedef unsigned char uchar;
|
||
|
typedef unsigned short ushort;
|
||
|
|
||
|
FILE *ifp;
|
||
|
short order;
|
||
|
char *ifname, make[64], model[64];
|
||
|
int data_offset, raw_height, raw_width, height, width;
|
||
|
int fuji_layout, fuji_secondary, use_secondary=0, verbose=0;
|
||
|
ushort *image;
|
||
|
void (*load_raw)();
|
||
|
float bright=1.0;
|
||
|
void write_ppm(FILE *);
|
||
|
void (*write_fun)(FILE *) = write_ppm;
|
||
|
jmp_buf failure;
|
||
|
|
||
|
#define CLASS
|
||
|
|
||
|
void CLASS merror (void *ptr, char *where)
|
||
|
{
|
||
|
if (ptr) return;
|
||
|
fprintf (stderr, "%s: Out of memory in %s\n", ifname, where);
|
||
|
longjmp (failure, 1);
|
||
|
}
|
||
|
|
||
|
ushort CLASS 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;
|
||
|
}
|
||
|
|
||
|
int CLASS 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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Faster than calling get2() multiple times.
|
||
|
*/
|
||
|
void CLASS read_shorts (ushort *pixel, int count)
|
||
|
{
|
||
|
fread (pixel, 2, count, ifp);
|
||
|
if ((order == 0x4949) == (ntohs(0x1234) == 0x1234))
|
||
|
swab (pixel, pixel, count*2);
|
||
|
}
|
||
|
|
||
|
void CLASS fuji_load_raw()
|
||
|
{
|
||
|
ushort *pixel, *img;
|
||
|
int row, col;
|
||
|
|
||
|
pixel = calloc (raw_width, 2);
|
||
|
merror (pixel, "fuji_load_raw()");
|
||
|
for (row=0; row < height; row++)
|
||
|
if (fuji_layout) {
|
||
|
read_shorts (image+row*width, width);
|
||
|
fseek (ifp, (raw_width*2 - width)*2, SEEK_CUR);
|
||
|
} else {
|
||
|
read_shorts (pixel, raw_width);
|
||
|
for (img=image+row*width, col=0; col < width; col++)
|
||
|
img[col] = pixel[col*2+1];
|
||
|
}
|
||
|
free (pixel);
|
||
|
}
|
||
|
|
||
|
void CLASS parse_fuji (int offset)
|
||
|
{
|
||
|
unsigned entries, tag, len, save;
|
||
|
|
||
|
fseek (ifp, offset, SEEK_SET);
|
||
|
entries = get4();
|
||
|
if (entries > 255) return;
|
||
|
while (entries--) {
|
||
|
tag = get2();
|
||
|
len = get2();
|
||
|
save = ftell(ifp);
|
||
|
if (tag == 0x100) {
|
||
|
raw_height = get2();
|
||
|
raw_width = get2();
|
||
|
} else if (tag == 0x121) {
|
||
|
height = get2();
|
||
|
width = get2();
|
||
|
} else if (tag == 0x130)
|
||
|
fuji_layout = fgetc(ifp) >> 7;
|
||
|
fseek (ifp, save+len, SEEK_SET);
|
||
|
}
|
||
|
if (fuji_layout) {
|
||
|
height *= 2;
|
||
|
width /= 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CLASS parse_tiff (int base)
|
||
|
{
|
||
|
int doff, entries, tag, type, len, save;
|
||
|
|
||
|
fseek (ifp, base, SEEK_SET);
|
||
|
order = get2();
|
||
|
get2(); /* Should be 42 for standard TIFF */
|
||
|
while ((doff = get4())) {
|
||
|
fseek (ifp, doff+base, SEEK_SET);
|
||
|
entries = get2();
|
||
|
while (entries--) {
|
||
|
tag = get2();
|
||
|
type = get2();
|
||
|
len = get4();
|
||
|
save = ftell(ifp)+4;
|
||
|
fseek (ifp, base+get4(), SEEK_SET);
|
||
|
switch (tag) {
|
||
|
case 0x10f: /* Make tag */
|
||
|
fgets (make, 64, ifp);
|
||
|
break;
|
||
|
case 0x110: /* Model tag */
|
||
|
fgets (model, 64, ifp);
|
||
|
}
|
||
|
fseek (ifp, save, SEEK_SET);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CLASS identify()
|
||
|
{
|
||
|
char head[32], *c;
|
||
|
int thumb_offset;
|
||
|
|
||
|
make[0] = model[0] = 0;
|
||
|
data_offset = raw_height = raw_width = height = width = 0;
|
||
|
fuji_secondary = 0;
|
||
|
|
||
|
order = 0x4d4d;
|
||
|
fread (head, 1, 32, ifp);
|
||
|
if (memcmp (head, "FUJIFILM", 8)) return 1;
|
||
|
fseek (ifp, 84, SEEK_SET);
|
||
|
thumb_offset = get4();
|
||
|
fseek (ifp, 92, SEEK_SET);
|
||
|
parse_fuji (get4());
|
||
|
if (thumb_offset > 120) {
|
||
|
fseek (ifp, 120, SEEK_SET);
|
||
|
fuji_secondary = get4() && 1;
|
||
|
}
|
||
|
fseek (ifp, 100, SEEK_SET);
|
||
|
data_offset = get4();
|
||
|
parse_tiff (thumb_offset+12);
|
||
|
c = model + strlen(model); /* Remove trailing spaces */
|
||
|
while (*--c == ' ') *c = 0;
|
||
|
if (!strcmp(model,"FinePix S5100") ||
|
||
|
!strcmp(model,"FinePix S5500")) return 1;
|
||
|
load_raw = fuji_load_raw;
|
||
|
if (!strcmp(model+7,"S2Pro")) {
|
||
|
strcpy (model+7," S2Pro");
|
||
|
height = 2144;
|
||
|
width = 2880;
|
||
|
}
|
||
|
data_offset += (raw_height - height + 1)*raw_width - width;
|
||
|
if (fuji_secondary)
|
||
|
data_offset += use_secondary * ( strcmp(model+7," S3Pro")
|
||
|
? (raw_width *= 2) : raw_height*raw_width*2 );
|
||
|
data_offset += fuji_layout*raw_width*2;
|
||
|
width >>= !fuji_layout;
|
||
|
height >>= fuji_layout;
|
||
|
fseek (ifp, data_offset, SEEK_SET);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void CLASS write_ppm (FILE *ofp)
|
||
|
{
|
||
|
int i, size, val, total, histogram[0x2000];
|
||
|
float white, r;
|
||
|
uchar lut[0x10000];
|
||
|
|
||
|
memset (histogram, 0, sizeof histogram);
|
||
|
size = width * height;
|
||
|
for (i = 0; i < size; i++)
|
||
|
histogram[image[i] >> 4]++;
|
||
|
i = size * 0.01; /* 99th percentile white point */
|
||
|
for (val=0x2000, total=0; --val; )
|
||
|
if ((total += histogram[val]) > i) break;
|
||
|
white = (val << 4) / bright;
|
||
|
|
||
|
for (i=0; i < 0x10000; i++) {
|
||
|
r = i / white;
|
||
|
val = (r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099) * 256;
|
||
|
if (val > 255) val = 255;
|
||
|
lut[i] = val;
|
||
|
}
|
||
|
fprintf (ofp, "P5\n%d %d\n255\n", width, height);
|
||
|
for (i=0; i < size; i++)
|
||
|
fputc (lut[image[i]], ofp);
|
||
|
}
|
||
|
|
||
|
void CLASS write_raw16 (FILE *ofp)
|
||
|
{
|
||
|
if (ntohs(0x1234) != 0x1234)
|
||
|
swab (image, image, width*height*2);
|
||
|
fwrite (image, width*height, 2, ofp);
|
||
|
}
|
||
|
|
||
|
void CLASS write_ppm16 (FILE *ofp)
|
||
|
{
|
||
|
fprintf (ofp, "P5\n%d %d\n%d\n", width, height, 65535);
|
||
|
write_raw16 (ofp);
|
||
|
}
|
||
|
|
||
|
void CLASS write_psd (FILE *ofp)
|
||
|
{
|
||
|
char head[] = {
|
||
|
'8','B','P','S', /* signature */
|
||
|
0,1,0,0,0,0,0,0, /* version and reserved */
|
||
|
0,1, /* number of channels */
|
||
|
0,0,0,0, /* height, big-endian */
|
||
|
0,0,0,0, /* width, big-endian */
|
||
|
0,16, /* 16-bit color */
|
||
|
0,1, /* mode (1=grey, 3=rgb) */
|
||
|
0,0,0,0, /* color mode data */
|
||
|
0,0,0,0, /* image resources */
|
||
|
0,0,0,0, /* layer/mask info */
|
||
|
0,0 }; /* no compression */
|
||
|
int hw[2];
|
||
|
|
||
|
hw[0] = htonl(height*2); /* write the header */
|
||
|
hw[1] = htonl(width*2);
|
||
|
memcpy (head+14, hw, sizeof hw);
|
||
|
fwrite (head, 40, 1, ofp);
|
||
|
write_raw16 (ofp);
|
||
|
}
|
||
|
|
||
|
int CLASS main (int argc, char **argv)
|
||
|
{
|
||
|
int arg, status=0;
|
||
|
int identify_only=0, write_to_stdout=0;
|
||
|
char opt, *ofname, *cp;
|
||
|
const char *write_ext = ".pgm";
|
||
|
FILE *ofp = stdout;
|
||
|
|
||
|
if (argc == 1) {
|
||
|
fprintf (stderr,
|
||
|
"\nFuji Green channel output"
|
||
|
"\nby Dave Coffin, dcoffin a cybercom o net"
|
||
|
"\n\nUsage: %s [options] file1 file2 ...\n"
|
||
|
"\nValid options:"
|
||
|
"\n-v Print verbose messages"
|
||
|
"\n-c Write image data to standard output"
|
||
|
"\n-i Identify files without decoding them"
|
||
|
"\n-s Use secondary pixels if available"
|
||
|
"\n-b <num> Set brightness (default = 1.0)"
|
||
|
"\n-2 Write 8-bit non-linear PGM (default)"
|
||
|
"\n-4 Write 16-bit linear PGM"
|
||
|
"\n-3 Write 16-bit linear PSD (Adobe Photoshop)"
|
||
|
"\n\n", argv[0]);
|
||
|
return 1;
|
||
|
}
|
||
|
argv[argc] = "";
|
||
|
for (arg=1; argv[arg][0] == '-'; ) {
|
||
|
opt = argv[arg++][1];
|
||
|
if (strchr ("b", opt) && !isdigit(argv[arg][0])) {
|
||
|
fprintf (stderr, "\"-%c\" requires a numeric argument.\n", opt);
|
||
|
return 1;
|
||
|
}
|
||
|
switch (opt) {
|
||
|
case 'v': verbose = 1; break;
|
||
|
case 'i': identify_only = 1; break;
|
||
|
case 'c': write_to_stdout = 1; break;
|
||
|
case 's': use_secondary = 1; break;
|
||
|
case 'b': bright = atof(argv[arg++]); break;
|
||
|
case '2': write_fun = write_ppm; break;
|
||
|
case '4': write_fun = write_ppm16; break;
|
||
|
case '3': write_fun = write_psd; write_ext = ".psd"; break;
|
||
|
default:
|
||
|
fprintf (stderr, "Unknown option \"-%c\".\n", opt);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
if (arg == argc) {
|
||
|
fprintf (stderr, "No files to process.\n");
|
||
|
return 1;
|
||
|
}
|
||
|
if (write_to_stdout) {
|
||
|
if (isatty(1)) {
|
||
|
fprintf (stderr, "Will not write an image to the terminal!\n");
|
||
|
return 1;
|
||
|
}
|
||
|
#if defined(WIN32) || defined(DJGPP)
|
||
|
if (setmode(1,O_BINARY) < 0) {
|
||
|
perror("setmode()");
|
||
|
return 1;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
for ( ; arg < argc; arg++) {
|
||
|
status = 1;
|
||
|
image = NULL;
|
||
|
if (setjmp (failure)) {
|
||
|
if (fileno(ifp) > 2) fclose (ifp);
|
||
|
if (fileno(ofp) > 2) fclose (ofp);
|
||
|
if (image) free (image);
|
||
|
status = 1;
|
||
|
continue;
|
||
|
}
|
||
|
ifname = argv[arg];
|
||
|
if (!(ifp = fopen (ifname, "rb"))) {
|
||
|
perror (ifname);
|
||
|
continue;
|
||
|
}
|
||
|
if ((status = identify())) {
|
||
|
fprintf (stderr, "%s: unsupported file format.\n", ifname);
|
||
|
fclose (ifp);
|
||
|
continue;
|
||
|
}
|
||
|
if (identify_only) {
|
||
|
fprintf (stderr, "%s is a %s %s image.\n", ifname, make, model);
|
||
|
fclose (ifp);
|
||
|
continue;
|
||
|
}
|
||
|
image = calloc (height * width, sizeof *image);
|
||
|
merror (image, "main()");
|
||
|
if (verbose)
|
||
|
fprintf (stderr,
|
||
|
"Loading %s %s image from %s...\n", make, model, ifname);
|
||
|
(*load_raw)();
|
||
|
fclose (ifp);
|
||
|
ofname = malloc (strlen(ifname) + 16);
|
||
|
merror (ofname, "main()");
|
||
|
if (write_to_stdout)
|
||
|
strcpy (ofname, "standard output");
|
||
|
else {
|
||
|
strcpy (ofname, ifname);
|
||
|
if ((cp = strrchr (ofname, '.'))) *cp = 0;
|
||
|
strcat (ofname, write_ext);
|
||
|
ofp = fopen (ofname, "wb");
|
||
|
if (!ofp) {
|
||
|
status = 1;
|
||
|
perror (ofname);
|
||
|
goto cleanup;
|
||
|
}
|
||
|
}
|
||
|
if (verbose)
|
||
|
fprintf (stderr, "Writing data to %s...\n", ofname);
|
||
|
(*write_fun)(ofp);
|
||
|
if (ofp != stdout)
|
||
|
fclose (ofp);
|
||
|
cleanup:
|
||
|
free (ofname);
|
||
|
free (image);
|
||
|
}
|
||
|
return status;
|
||
|
}
|