]>
Commit | Line | Data |
---|---|---|
3d481f66 PG |
1 | /* |
2 | fuji_green -- read Fuji green pixels | |
3 | ||
4 | $Revision$ | |
5 | $Date$ | |
6 | */ | |
7 | ||
8 | #include <ctype.h> | |
9 | #include <math.h> | |
10 | #include <setjmp.h> | |
11 | #include <stdio.h> | |
12 | #include <stdlib.h> | |
13 | #include <string.h> | |
14 | ||
15 | #define ushort UshORt | |
16 | typedef unsigned char uchar; | |
17 | typedef unsigned short ushort; | |
18 | ||
19 | FILE *ifp; | |
20 | short order; | |
21 | char *ifname, make[64], model[64]; | |
22 | int data_offset, raw_height, raw_width, height, width; | |
23 | int fuji_layout, fuji_secondary, use_secondary=0, verbose=0; | |
24 | ushort *image; | |
25 | void (*load_raw)(); | |
26 | float bright=1.0; | |
27 | void write_ppm(FILE *); | |
28 | void (*write_fun)(FILE *) = write_ppm; | |
29 | jmp_buf failure; | |
30 | ||
31 | #define CLASS | |
32 | ||
33 | void CLASS merror (void *ptr, char *where) | |
34 | { | |
35 | if (ptr) return; | |
36 | fprintf (stderr, "%s: Out of memory in %s\n", ifname, where); | |
37 | longjmp (failure, 1); | |
38 | } | |
39 | ||
40 | ushort CLASS get2() | |
41 | { | |
42 | uchar a, b; | |
43 | ||
44 | a = fgetc(ifp); | |
45 | b = fgetc(ifp); | |
46 | if (order == 0x4949) /* "II" means little-endian */ | |
47 | return a + (b << 8); | |
48 | else /* "MM" means big-endian */ | |
49 | return (a << 8) + b; | |
50 | } | |
51 | ||
52 | int CLASS get4() | |
53 | { | |
54 | uchar a, b, c, d; | |
55 | ||
56 | a = fgetc(ifp); | |
57 | b = fgetc(ifp); | |
58 | c = fgetc(ifp); | |
59 | d = fgetc(ifp); | |
60 | if (order == 0x4949) | |
61 | return a + (b << 8) + (c << 16) + (d << 24); | |
62 | else | |
63 | return (a << 24) + (b << 16) + (c << 8) + d; | |
64 | } | |
65 | ||
66 | /* | |
67 | Faster than calling get2() multiple times. | |
68 | */ | |
69 | void CLASS read_shorts (ushort *pixel, int count) | |
70 | { | |
71 | fread (pixel, 2, count, ifp); | |
72 | if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) | |
73 | swab (pixel, pixel, count*2); | |
74 | } | |
75 | ||
76 | void CLASS fuji_load_raw() | |
77 | { | |
78 | ushort *pixel, *img; | |
79 | int row, col; | |
80 | ||
81 | pixel = calloc (raw_width, 2); | |
82 | merror (pixel, "fuji_load_raw()"); | |
83 | for (row=0; row < height; row++) | |
84 | if (fuji_layout) { | |
85 | read_shorts (image+row*width, width); | |
86 | fseek (ifp, (raw_width*2 - width)*2, SEEK_CUR); | |
87 | } else { | |
88 | read_shorts (pixel, raw_width); | |
89 | for (img=image+row*width, col=0; col < width; col++) | |
90 | img[col] = pixel[col*2+1]; | |
91 | } | |
92 | free (pixel); | |
93 | } | |
94 | ||
95 | void CLASS parse_fuji (int offset) | |
96 | { | |
97 | unsigned entries, tag, len, save; | |
98 | ||
99 | fseek (ifp, offset, SEEK_SET); | |
100 | entries = get4(); | |
101 | if (entries > 255) return; | |
102 | while (entries--) { | |
103 | tag = get2(); | |
104 | len = get2(); | |
105 | save = ftell(ifp); | |
106 | if (tag == 0x100) { | |
107 | raw_height = get2(); | |
108 | raw_width = get2(); | |
109 | } else if (tag == 0x121) { | |
110 | height = get2(); | |
111 | width = get2(); | |
112 | } else if (tag == 0x130) | |
113 | fuji_layout = fgetc(ifp) >> 7; | |
114 | fseek (ifp, save+len, SEEK_SET); | |
115 | } | |
116 | if (fuji_layout) { | |
117 | height *= 2; | |
118 | width /= 2; | |
119 | } | |
120 | } | |
121 | ||
122 | void CLASS parse_tiff (int base) | |
123 | { | |
124 | int doff, entries, tag, type, len, save; | |
125 | ||
126 | fseek (ifp, base, SEEK_SET); | |
127 | order = get2(); | |
128 | get2(); /* Should be 42 for standard TIFF */ | |
129 | while ((doff = get4())) { | |
130 | fseek (ifp, doff+base, SEEK_SET); | |
131 | entries = get2(); | |
132 | while (entries--) { | |
133 | tag = get2(); | |
134 | type = get2(); | |
135 | len = get4(); | |
136 | save = ftell(ifp)+4; | |
137 | fseek (ifp, base+get4(), SEEK_SET); | |
138 | switch (tag) { | |
139 | case 0x10f: /* Make tag */ | |
140 | fgets (make, 64, ifp); | |
141 | break; | |
142 | case 0x110: /* Model tag */ | |
143 | fgets (model, 64, ifp); | |
144 | } | |
145 | fseek (ifp, save, SEEK_SET); | |
146 | } | |
147 | } | |
148 | } | |
149 | ||
150 | int CLASS identify() | |
151 | { | |
152 | char head[32], *c; | |
153 | int thumb_offset; | |
154 | ||
155 | make[0] = model[0] = 0; | |
156 | data_offset = raw_height = raw_width = height = width = 0; | |
157 | fuji_secondary = 0; | |
158 | ||
159 | order = 0x4d4d; | |
160 | fread (head, 1, 32, ifp); | |
161 | if (memcmp (head, "FUJIFILM", 8)) return 1; | |
162 | fseek (ifp, 84, SEEK_SET); | |
163 | thumb_offset = get4(); | |
164 | fseek (ifp, 92, SEEK_SET); | |
165 | parse_fuji (get4()); | |
166 | if (thumb_offset > 120) { | |
167 | fseek (ifp, 120, SEEK_SET); | |
168 | fuji_secondary = get4() && 1; | |
169 | } | |
170 | fseek (ifp, 100, SEEK_SET); | |
171 | data_offset = get4(); | |
172 | parse_tiff (thumb_offset+12); | |
173 | c = model + strlen(model); /* Remove trailing spaces */ | |
174 | while (*--c == ' ') *c = 0; | |
175 | if (!strcmp(model,"FinePix S5100") || | |
176 | !strcmp(model,"FinePix S5500")) return 1; | |
177 | load_raw = fuji_load_raw; | |
178 | if (!strcmp(model+7,"S2Pro")) { | |
179 | strcpy (model+7," S2Pro"); | |
180 | height = 2144; | |
181 | width = 2880; | |
182 | } | |
183 | data_offset += (raw_height - height + 1)*raw_width - width; | |
184 | if (fuji_secondary) | |
185 | data_offset += use_secondary * ( strcmp(model+7," S3Pro") | |
186 | ? (raw_width *= 2) : raw_height*raw_width*2 ); | |
187 | data_offset += fuji_layout*raw_width*2; | |
188 | width >>= !fuji_layout; | |
189 | height >>= fuji_layout; | |
190 | fseek (ifp, data_offset, SEEK_SET); | |
191 | return 0; | |
192 | } | |
193 | ||
194 | void CLASS write_ppm (FILE *ofp) | |
195 | { | |
196 | int i, size, val, total, histogram[0x2000]; | |
197 | float white, r; | |
198 | uchar lut[0x10000]; | |
199 | ||
200 | memset (histogram, 0, sizeof histogram); | |
201 | size = width * height; | |
202 | for (i = 0; i < size; i++) | |
203 | histogram[image[i] >> 4]++; | |
204 | i = size * 0.01; /* 99th percentile white point */ | |
205 | for (val=0x2000, total=0; --val; ) | |
206 | if ((total += histogram[val]) > i) break; | |
207 | white = (val << 4) / bright; | |
208 | ||
209 | for (i=0; i < 0x10000; i++) { | |
210 | r = i / white; | |
211 | val = (r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099) * 256; | |
212 | if (val > 255) val = 255; | |
213 | lut[i] = val; | |
214 | } | |
215 | fprintf (ofp, "P5\n%d %d\n255\n", width, height); | |
216 | for (i=0; i < size; i++) | |
217 | fputc (lut[image[i]], ofp); | |
218 | } | |
219 | ||
220 | void CLASS write_raw16 (FILE *ofp) | |
221 | { | |
222 | if (ntohs(0x1234) != 0x1234) | |
223 | swab (image, image, width*height*2); | |
224 | fwrite (image, width*height, 2, ofp); | |
225 | } | |
226 | ||
227 | void CLASS write_ppm16 (FILE *ofp) | |
228 | { | |
229 | fprintf (ofp, "P5\n%d %d\n%d\n", width, height, 65535); | |
230 | write_raw16 (ofp); | |
231 | } | |
232 | ||
233 | void CLASS write_psd (FILE *ofp) | |
234 | { | |
235 | char head[] = { | |
236 | '8','B','P','S', /* signature */ | |
237 | 0,1,0,0,0,0,0,0, /* version and reserved */ | |
238 | 0,1, /* number of channels */ | |
239 | 0,0,0,0, /* height, big-endian */ | |
240 | 0,0,0,0, /* width, big-endian */ | |
241 | 0,16, /* 16-bit color */ | |
242 | 0,1, /* mode (1=grey, 3=rgb) */ | |
243 | 0,0,0,0, /* color mode data */ | |
244 | 0,0,0,0, /* image resources */ | |
245 | 0,0,0,0, /* layer/mask info */ | |
246 | 0,0 }; /* no compression */ | |
247 | int hw[2]; | |
248 | ||
249 | hw[0] = htonl(height*2); /* write the header */ | |
250 | hw[1] = htonl(width*2); | |
251 | memcpy (head+14, hw, sizeof hw); | |
252 | fwrite (head, 40, 1, ofp); | |
253 | write_raw16 (ofp); | |
254 | } | |
255 | ||
256 | int CLASS main (int argc, char **argv) | |
257 | { | |
258 | int arg, status=0; | |
259 | int identify_only=0, write_to_stdout=0; | |
260 | char opt, *ofname, *cp; | |
261 | const char *write_ext = ".pgm"; | |
262 | FILE *ofp = stdout; | |
263 | ||
264 | if (argc == 1) { | |
265 | fprintf (stderr, | |
266 | "\nFuji Green channel output" | |
267 | "\nby Dave Coffin, dcoffin a cybercom o net" | |
268 | "\n\nUsage: %s [options] file1 file2 ...\n" | |
269 | "\nValid options:" | |
270 | "\n-v Print verbose messages" | |
271 | "\n-c Write image data to standard output" | |
272 | "\n-i Identify files without decoding them" | |
273 | "\n-s Use secondary pixels if available" | |
274 | "\n-b <num> Set brightness (default = 1.0)" | |
275 | "\n-2 Write 8-bit non-linear PGM (default)" | |
276 | "\n-4 Write 16-bit linear PGM" | |
277 | "\n-3 Write 16-bit linear PSD (Adobe Photoshop)" | |
278 | "\n\n", argv[0]); | |
279 | return 1; | |
280 | } | |
281 | argv[argc] = ""; | |
282 | for (arg=1; argv[arg][0] == '-'; ) { | |
283 | opt = argv[arg++][1]; | |
284 | if (strchr ("b", opt) && !isdigit(argv[arg][0])) { | |
285 | fprintf (stderr, "\"-%c\" requires a numeric argument.\n", opt); | |
286 | return 1; | |
287 | } | |
288 | switch (opt) { | |
289 | case 'v': verbose = 1; break; | |
290 | case 'i': identify_only = 1; break; | |
291 | case 'c': write_to_stdout = 1; break; | |
292 | case 's': use_secondary = 1; break; | |
293 | case 'b': bright = atof(argv[arg++]); break; | |
294 | case '2': write_fun = write_ppm; break; | |
295 | case '4': write_fun = write_ppm16; break; | |
296 | case '3': write_fun = write_psd; write_ext = ".psd"; break; | |
297 | default: | |
298 | fprintf (stderr, "Unknown option \"-%c\".\n", opt); | |
299 | return 1; | |
300 | } | |
301 | } | |
302 | if (arg == argc) { | |
303 | fprintf (stderr, "No files to process.\n"); | |
304 | return 1; | |
305 | } | |
306 | if (write_to_stdout) { | |
307 | if (isatty(1)) { | |
308 | fprintf (stderr, "Will not write an image to the terminal!\n"); | |
309 | return 1; | |
310 | } | |
311 | #if defined(WIN32) || defined(DJGPP) | |
312 | if (setmode(1,O_BINARY) < 0) { | |
313 | perror("setmode()"); | |
314 | return 1; | |
315 | } | |
316 | #endif | |
317 | } | |
318 | for ( ; arg < argc; arg++) { | |
319 | status = 1; | |
320 | image = NULL; | |
321 | if (setjmp (failure)) { | |
322 | if (fileno(ifp) > 2) fclose (ifp); | |
323 | if (fileno(ofp) > 2) fclose (ofp); | |
324 | if (image) free (image); | |
325 | status = 1; | |
326 | continue; | |
327 | } | |
328 | ifname = argv[arg]; | |
329 | if (!(ifp = fopen (ifname, "rb"))) { | |
330 | perror (ifname); | |
331 | continue; | |
332 | } | |
333 | if ((status = identify())) { | |
334 | fprintf (stderr, "%s: unsupported file format.\n", ifname); | |
335 | fclose (ifp); | |
336 | continue; | |
337 | } | |
338 | if (identify_only) { | |
339 | fprintf (stderr, "%s is a %s %s image.\n", ifname, make, model); | |
340 | fclose (ifp); | |
341 | continue; | |
342 | } | |
343 | image = calloc (height * width, sizeof *image); | |
344 | merror (image, "main()"); | |
345 | if (verbose) | |
346 | fprintf (stderr, | |
347 | "Loading %s %s image from %s...\n", make, model, ifname); | |
348 | (*load_raw)(); | |
349 | fclose (ifp); | |
350 | ofname = malloc (strlen(ifname) + 16); | |
351 | merror (ofname, "main()"); | |
352 | if (write_to_stdout) | |
353 | strcpy (ofname, "standard output"); | |
354 | else { | |
355 | strcpy (ofname, ifname); | |
356 | if ((cp = strrchr (ofname, '.'))) *cp = 0; | |
357 | strcat (ofname, write_ext); | |
358 | ofp = fopen (ofname, "wb"); | |
359 | if (!ofp) { | |
360 | status = 1; | |
361 | perror (ofname); | |
362 | goto cleanup; | |
363 | } | |
364 | } | |
365 | if (verbose) | |
366 | fprintf (stderr, "Writing data to %s...\n", ofname); | |
367 | (*write_fun)(ofp); | |
368 | if (ofp != stdout) | |
369 | fclose (ofp); | |
370 | cleanup: | |
371 | free (ofname); | |
372 | free (image); | |
373 | } | |
374 | return status; | |
375 | } |