bugs: Advantages for incremental library separation by analogy with incremental
[Ale.git] / d2 / ppm.h
blob35f046dc34aad527b9d461d11691406fa98e8d9e
1 // Copyright 2002 David Hilvert <dhilvert@auricle.dyndns.org>,
2 // <dhilvert@ugcs.caltech.edu>
4 /* This file is part of the Anti-Lamenessing Engine.
6 The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with the Anti-Lamenessing Engine; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * ppm.h: Read and write PPM files.
25 #ifndef __ppm_h__
26 #define __ppm_h__
28 #include "image_ale_real.h"
29 #include "image_bayer_ale_real.h"
30 #include "exposure/exposure.h"
33 * Extended attributes
36 struct extended_t {
37 int is_extended;
38 ale_real black_level;
39 ale_real aperture; /* 1 == f/1.0, 1.4 == f/1.4, etc. */
40 ale_real shutter; /* 1 == 1 sec, 0.5 == 1/2 sec, etc. */
41 ale_real gain; /* 1 == ISO 100, 2 == ISO 200, etc. */
43 extended_t() {
44 is_extended = 0;
45 black_level = 0;
46 aperture = 0;
47 shutter = 0;
48 gain = 0;
52 static inline void error_ppm(const char *filename) {
53 fprintf(stderr,
54 "\n\n*** '%s' doesn't look like a PPM file.\n"
55 "\n*** (To handle other file types, use a version of ALE with\n"
56 "*** ImageMagick support enabled.)\n\n",
57 filename);
58 exit(1);
61 static inline int digest_comment(FILE *f, const char *filename, extended_t *extended) {
62 int next = '#';
63 int value;
64 double fvalue, fvalue2;
66 while (next != '\n' && next != '\r' && next != EOF) {
67 while (next == ' ' || next == '\t' || next == '#') {
68 next = fgetc(f);
69 if (feof(f))
70 error_ppm(filename);
73 if (ungetc(next, f) == EOF) {
74 assert(0);
75 fprintf(stderr, "Unable to ungetc().");
76 exit(1);
79 fvalue2 = 1;
81 if (extended->is_extended && fscanf(f, "Black-level: %d", &value) == 1)
82 extended->black_level = value;
83 else if (extended->is_extended && fscanf(f, "ISO: %lf", &fvalue) == 1)
84 extended->gain = fvalue / 100;
85 else if (extended->is_extended && fscanf(f, "Gain: %lf", &fvalue) == 1)
86 extended->gain = fvalue;
87 else if (extended->is_extended && fscanf(f, "Aperture: %lf", &fvalue) == 1)
88 extended->aperture = fvalue;
89 else if (extended->is_extended && fscanf(f, "Shutter: %lf/%lf", &fvalue, &fvalue2) > 0)
90 extended->shutter = fvalue / fvalue2;
91 else if (next != '\n' && next != '\r' && next != EOF)
92 next = fgetc(f);
94 next = fgetc(f);
97 return next;
100 static inline void eat_comments(FILE *f, const char *filename, extended_t *extended) {
101 int next = ' ';
103 while (next == ' ' || next == '\n' || next == '\t' || next == '#' || next == '\r') {
104 next = fgetc(f);
105 if (next == '#')
106 next = digest_comment(f, filename, extended);
107 if (feof(f))
108 error_ppm(filename);
111 if (ungetc(next, f) == EOF) {
112 assert(0);
113 fprintf(stderr, "Unable to ungetc().");
114 exit(1);
118 static inline int is_eppm(const char *filename) {
119 char m1, m2, m3, m4;
120 int n;
121 extended_t extended;
122 FILE *f = fopen(filename, "rb");
124 if (f == NULL)
125 return 0;
127 /* Magic */
129 eat_comments(f, filename, &extended); /* XXX - should we eat comments here? */
130 n = fscanf(f, "%c%c%c%c", &m1, &m2, &m3, &m4);
132 fclose(f);
134 if (n != 4 || m1 != 'P' || (m2 != '6' && m2 != '3') || m3 != '#' || m4 != 'E')
135 return 0;
137 return 1;
140 static int ppm_void_file_close(void *f) {
141 return fclose((FILE *) f);
144 static int ppm_short_little_endian_check() {
146 * Modified from http://unixpapa.com/incnote/byteorder.html
149 short one = 1;
150 return (*((char *)(&one)));
153 #define PPM_CHANNEL_READ(is_binary, transfer, f_source, f_dest, f_name, channel_type, channel_format, channel_mcv, extended_ptr) {\
154 channel_type ival;\
156 if (is_binary) {\
158 unsigned char val;\
159 int n;\
161 /* Binary data */\
163 n = fscanf(f_source, "%c", &val);\
164 assert (n == 1);\
166 if (n != 1)\
167 error_ppm(f_name);\
169 ival = val;\
171 for (channel_type mcv_r = channel_mcv / 256; mcv_r; mcv_r /= 256) {\
173 n = fscanf(f_source, "%c", &val);\
174 assert(n == 1);\
176 if (n != 1)\
177 error_ppm(f_name);\
179 ival = (ival << 8) | val;\
182 } else {\
184 /* ASCII data */\
186 eat_comments(f_source, f_name, extended_ptr);\
188 n = fscanf(f, channel_format, &ival);\
190 assert (n == 1);\
191 if (n != 1)\
192 error_ppm(f_name);\
195 if (transfer)\
196 fwrite(&ival, sizeof(channel_type), 1, f_dest);\
200 static inline ale_image read_ppm(const char *filename, exposure *e, unsigned int bayer, int init_reference_gain = 0) {
201 unsigned int i, j, k;
202 ale_image im;
203 unsigned char m1, m2, val;
204 int m3, m4;
205 int w, h;
206 cl_ulong mcv;
207 int n;
208 struct extended_t extended;
209 FILE *f = fopen(filename, "rb");
211 if (f == NULL) {
212 fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
213 exit(1);
216 assert(f);
218 /* Magic */
220 eat_comments(f, filename, &extended); /* XXX - should we eat comments here? */
221 n = fscanf(f, "%c%c", &m1, &m2);
223 if (n != 2 || m1 != 'P' || (m2 != '6' && m2 != '3'))
224 error_ppm(filename);
226 assert(n == 2 && m1 == 'P' && (m2 == '6' || m2 == '3'));
228 /* Extended flag */
230 m3 = fgetc(f);
232 if (m3 == '#') {
233 m4 = fgetc(f);
234 if (m4 == 'E')
235 extended.is_extended = 1;
236 else while (m4 != EOF && m4 != '\n' && m4 != '\r')
237 m4 = fgetc(f);
238 } else if (ungetc(m3, f) == EOF) {
239 assert(0);
240 fprintf(stderr, "Unable to ungetc().");
241 exit(1);
244 /* Width */
246 eat_comments(f, filename, &extended);
247 n = fscanf(f, " %d", &w);
248 assert(n == 1);
250 if (n != 1)
251 error_ppm(filename);
253 /* Height */
255 eat_comments(f, filename, &extended);
256 n = fscanf(f, "%d", &h);
257 assert(n == 1);
259 if (n != 1)
260 error_ppm(filename);
262 /* Maximum component value */
264 eat_comments(f, filename, &extended);
265 n = fscanf(f, "%llu", &mcv);
266 assert(n == 1);
267 assert(mcv <= 65535 || m2 == '3');
269 if (extended.black_level == 0) {
270 extended.black_level = e->get_black_level();
271 } else {
272 extended.black_level /= mcv;
275 if (n != 1 || (mcv > 65535 && m2 == '6'))
276 error_ppm(filename);
278 /* Trailing whitespace */
280 if (fgetc(f) == EOF) {
281 assert(0);
282 error_ppm(filename);
285 /* Make a new image */
287 im = ale_new_image(accel::context(),
288 (bayer == IMAGE_BAYER_NONE) ? ALE_IMAGE_RGB : ALE_IMAGE_Y,
289 (mcv <= 255) ? ALE_TYPE_UINT_8 :
290 ((mcv <= 65535) ? ALE_TYPE_UINT_16 :
291 ((mcv <= 4294967295ul) ? ALE_TYPE_UINT_32 : ALE_TYPE_UINT_64)));
293 if (m2 == '6' && bayer == IMAGE_BAYER_NONE
294 && (mcv <= 255 || !ppm_short_little_endian_check())) {
297 * For 3-channel binary 8-bit, and 16-bit on big-endian
298 * systems, use a file suffix.
301 ale_image_set_file_static(im, w, h, f, ftell(f), ppm_void_file_close, f);
303 } else {
306 * For all others, convert data via a new, temporary file.
309 FILE *converted_f = tmpfile();
311 /* Pixels */
313 for (i = 0; i < h; i++)
314 for (j = 0; j < w; j++)
315 for (k = 0; k < 3; k++) {
317 if (mcv <= 255)
318 PPM_CHANNEL_READ((m2 == '6'), (ale_has_channel(i, j, k, bayer)), f, converted_f, filename, cl_uchar, "%hhu", ((cl_uchar) mcv), (&extended))
319 else if (mcv <= 65535)
320 PPM_CHANNEL_READ((m2 == '6'), (ale_has_channel(i, j, k, bayer)), f, converted_f, filename, cl_ushort, "%hu", ((cl_ushort) mcv), (&extended))
321 else if (mcv <= 4294967295ul)
322 PPM_CHANNEL_READ((m2 == '6'), (ale_has_channel(i, j, k, bayer)), f, converted_f, filename, cl_uint, "%u", ((cl_uint) mcv), (&extended))
323 else
324 PPM_CHANNEL_READ((m2 == '6'), (ale_has_channel(i, j, k, bayer)), f, converted_f, filename, cl_ulong, "%llu", ((cl_ulong) mcv), (&extended))
328 ale_image_set_file_static(im, w, h, converted_f, 0, ppm_void_file_close, converted_f);
330 fclose(f);
333 /* Handle exposure and gain */
335 if (extended.is_extended) {
336 if (extended.aperture != 0
337 || extended.shutter != 0
338 || extended.gain != 0) {
340 if (extended.aperture == 0)
341 extended.aperture = 1;
342 if (extended.shutter == 0)
343 extended.shutter = 1;
344 if (extended.gain == 0)
345 extended.gain = 1;
347 ale_real combined_gain = (1 / pow(extended.aperture, 2))
348 * extended.shutter
349 * extended.gain;
351 if (init_reference_gain)
352 exposure::set_gain_reference(combined_gain);
353 else
354 e->set_gain_multiplier(exposure::get_gain_reference()
355 / combined_gain);
359 return im;
362 static inline void write_ppm(const char *filename, ale_image im, unsigned long mcv, int plain) {
363 unsigned int i, j, k;
364 FILE *f = fopen(filename, "wb");
366 if (f == NULL) {
367 fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
368 exit(1);
371 assert(f);
374 * XXX: For simplicity of implementation, we currently only handle up
375 * to 16-bit output, which should be within the limits of the current
376 * ALE user interface.
379 assert(mcv <= 65535);
381 if (mcv > 65535) {
382 fprintf(stderr, "error: I don't know how to produce greater than 16-bit output.\n");
383 exit(1);
387 * Output a plain (ASCII) or raw (binary) PPM file
390 /* Magic */
392 if (plain)
393 fprintf(f, "P3 ");
394 else
395 fprintf(f, "P6 ");
397 /* Width */
399 fprintf(f, "%d ", ale_image_get_width(im));
401 /* Height */
403 fprintf(f, "%d ", ale_image_get_height(im));
405 /* Maximum component value */
407 fprintf(f, "%lu\n", mcv);
409 /* Pixels */
411 FILE *image_data = ale_image_retain_file(im);
413 while (!feof(image_data)) {
415 cl_ushort val;
417 if (mcv > 255) {
418 if (!fread(&val, sizeof(cl_ushort), 1, image_data))
419 break;
420 } else {
421 val = fgetc(image_data);
424 if (feof(image_data))
425 break;
427 if (plain) {
428 fprintf(f, "%u ", (unsigned int) val);
429 } else if (mcv <= 255) {
430 fprintf(f, "%c", (unsigned char) val);
431 } else {
432 fprintf(f, "%c%c", (unsigned char) (val >> 8), (unsigned char) (val & 0xff));
436 ale_image_release_file(im, image_data);
438 /* Done */
440 fclose(f);
443 #endif