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.
28 #include "image_ale_real.h"
29 #include "image_bayer_ale_real.h"
30 #include "exposure/exposure.h"
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. */
52 static inline void error_ppm(const char *filename
) {
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",
61 static inline int digest_comment(FILE *f
, const char *filename
, extended_t
*extended
) {
64 double fvalue
, fvalue2
;
66 while (next
!= '\n' && next
!= '\r' && next
!= EOF
) {
67 while (next
== ' ' || next
== '\t' || next
== '#') {
73 if (ungetc(next
, f
) == EOF
) {
75 fprintf(stderr
, "Unable to ungetc().");
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
)
100 static inline void eat_comments(FILE *f
, const char *filename
, extended_t
*extended
) {
103 while (next
== ' ' || next
== '\n' || next
== '\t' || next
== '#' || next
== '\r') {
106 next
= digest_comment(f
, filename
, extended
);
111 if (ungetc(next
, f
) == EOF
) {
113 fprintf(stderr
, "Unable to ungetc().");
118 static inline int is_eppm(const char *filename
) {
122 FILE *f
= fopen(filename
, "rb");
129 eat_comments(f
, filename
, &extended
); /* XXX - should we eat comments here? */
130 n
= fscanf(f
, "%c%c%c%c", &m1
, &m2
, &m3
, &m4
);
134 if (n
!= 4 || m1
!= 'P' || (m2
!= '6' && m2
!= '3') || m3
!= '#' || m4
!= 'E')
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
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) {\
163 n = fscanf(f_source, "%c", &val);\
171 for (channel_type mcv_r = channel_mcv / 256; mcv_r; mcv_r /= 256) {\
173 n = fscanf(f_source, "%c", &val);\
179 ival = (ival << 8) | val;\
186 eat_comments(f_source, f_name, extended_ptr);\
188 n = fscanf(f, channel_format, &ival);\
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
;
203 unsigned char m1
, m2
, val
;
208 struct extended_t extended
;
209 FILE *f
= fopen(filename
, "rb");
212 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
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'))
226 assert(n
== 2 && m1
== 'P' && (m2
== '6' || m2
== '3'));
235 extended
.is_extended
= 1;
236 else while (m4
!= EOF
&& m4
!= '\n' && m4
!= '\r')
238 } else if (ungetc(m3
, f
) == EOF
) {
240 fprintf(stderr
, "Unable to ungetc().");
246 eat_comments(f
, filename
, &extended
);
247 n
= fscanf(f
, " %d", &w
);
255 eat_comments(f
, filename
, &extended
);
256 n
= fscanf(f
, "%d", &h
);
262 /* Maximum component value */
264 eat_comments(f
, filename
, &extended
);
265 n
= fscanf(f
, "%llu", &mcv
);
267 assert(mcv
<= 65535 || m2
== '3');
269 if (extended
.black_level
== 0) {
270 extended
.black_level
= e
->get_black_level();
272 extended
.black_level
/= mcv
;
275 if (n
!= 1 || (mcv
> 65535 && m2
== '6'))
278 /* Trailing whitespace */
280 if (fgetc(f
) == EOF
) {
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
);
306 * For all others, convert data via a new, temporary file.
309 FILE *converted_f
= tmpfile();
313 for (i
= 0; i
< h
; i
++)
314 for (j
= 0; j
< w
; j
++)
315 for (k
= 0; k
< 3; k
++) {
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
))
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
);
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)
347 ale_real combined_gain
= (1 / pow(extended
.aperture
, 2))
351 if (init_reference_gain
)
352 exposure::set_gain_reference(combined_gain
);
354 e
->set_gain_multiplier(exposure::get_gain_reference()
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");
367 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
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);
382 fprintf(stderr
, "error: I don't know how to produce greater than 16-bit output.\n");
387 * Output a plain (ASCII) or raw (binary) PPM file
399 fprintf(f
, "%d ", ale_image_get_width(im
));
403 fprintf(f
, "%d ", ale_image_get_height(im
));
405 /* Maximum component value */
407 fprintf(f
, "%lu\n", mcv
);
411 FILE *image_data
= ale_image_retain_file(im
);
413 while (!feof(image_data
)) {
418 if (!fread(&val
, sizeof(cl_ushort
), 1, image_data
))
421 val
= fgetc(image_data
);
424 if (feof(image_data
))
428 fprintf(f
, "%u ", (unsigned int) val
);
429 } else if (mcv
<= 255) {
430 fprintf(f
, "%c", (unsigned char) val
);
432 fprintf(f
, "%c%c", (unsigned char) (val
>> 8), (unsigned char) (val
& 0xff));
436 ale_image_release_file(im
, image_data
);