bugs: Consider info format for help pages.
[Ale.git] / d2 / image_rw.h
blob6a704a62e31699515e937cc80a78b2f9db91dea9
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 * image_rw.h: Read and write images.
26 #ifndef __image_rw_h__
27 #define __image_rw_h__
29 #include "image.h"
30 #include "image_ale_real.h"
31 #include "image_bayer_ale_real.h"
32 #include "image_accel.h"
33 #include "ppm.h"
34 #include "exposure/exposure.h"
35 #include "exposure/exposure_default.h"
37 class image_rw {
40 * Private data members
44 * PPM type
46 * 0 = No type selected
47 * 1 = PPM Raw
48 * 2 = PPM Plain
50 static int ppm_type;
53 * Bit depth
55 static unsigned int num_bits;
56 static unsigned int mcv;
59 * Nearest-neighbor defined value radius.
61 static double nn_defined_radius;
64 * Input and output exposure models
66 static exposure **input_exposure;
67 static exposure *output_exposure;
68 static int exposure_scale;
71 * Default bayer pattern
73 static unsigned int bayer_default;
76 * Image-specific bayer patterns.
78 static unsigned int *bayer_specific;
81 * Pointer to the output filename
83 static const char *output_filename;
86 * Variables relating to input image files and image data structures.
88 static const char **filenames;
89 static unsigned int file_count;
90 static const image **images;
91 static int *files_open;
94 * The most recently closed image number.
96 static int latest_close_num;
99 * Maximum cache size, in megabytes (2^20 * bytes), for images not most
100 * recently closed.
102 static double cache_size_max;
105 * Actual cache size.
107 static double cache_size;
110 * Number of cached files.
112 static unsigned int cache_count;
115 * Private methods to init and shut down the file reader.
119 * Initialize the image file handler
121 static void init_image() {
122 #ifdef USE_MAGICK
123 InitializeMagick("ale");
124 #endif
128 * Destroy the image file handler
130 static void destroy_image() {
131 #ifdef USE_MAGICK
132 DestroyMagick();
133 #endif
136 static image *read_image_im_unaccel(const char *filename, exposure *exp, const char *name,
137 unsigned int bayer, int init_reference_gain) {
138 static int warned = 0;
139 #ifdef USE_MAGICK
141 if (MaxRGB < 65535 && mcv == 65535 && !warned) {
142 fprintf(stderr, "\n\n*** Warning: " MagickPackageName " has not been compiled with 16 bit support.\n");
143 fprintf(stderr, "*** Reading input using 8 bits per channel.\n");
144 fprintf(stderr, "*** \n");
145 fprintf(stderr, "*** (To silence this warning, specify option --8bpc)\n\n\n");
147 warned = 1;
151 * Patterned after http://www.imagemagick.org/www/api.html
152 * and http://www.imagemagick.org/www/smile.c
155 ExceptionInfo exception;
156 Image *mi;
157 ImageInfo *image_info;
158 image *im;
159 const PixelPacket *p;
161 unsigned int i, j;
163 ale_real black_level = exp->get_black_level();
165 GetExceptionInfo(&exception);
166 image_info = CloneImageInfo((ImageInfo *) NULL);
168 strncpy(image_info->filename, filename, MaxTextExtent);
169 mi = ReadImage(image_info, &exception);
170 if (exception.severity != UndefinedException) {
171 fprintf(stderr, "\n\n");
172 CatchException(&exception);
173 fprintf(stderr, "\n");
175 if (mi == (Image *) NULL)
176 exit(1);
178 if (bayer == IMAGE_BAYER_NONE)
179 im = new_image_ale_real(mi->rows, mi->columns, 3, name, exp);
180 else
181 im = new_image_bayer_ale_real(mi->rows, mi->columns, 3, bayer, name, exp);
183 for (i = 0; i < mi->rows; i++) {
184 p = AcquireImagePixels(mi, 0, i, mi->columns, 1, &exception);
186 if (exception.severity != UndefinedException)
187 CatchException(&exception);
188 if (p == NULL)
189 exit(1);
191 for (j = 0; j < mi->columns; j++) {
193 pixel input ( ale_real_from_int(p->red, MaxRGB),
194 ale_real_from_int(p->green, MaxRGB),
195 ale_real_from_int(p->blue, MaxRGB) );
197 pixel linear_input = (exp->linearize(input) - exp->get_multiplier() * black_level)
198 / (1 - black_level);
200 im->set_pixel(i, j, linear_input);
202 p++;
206 DestroyImage(mi);
207 DestroyImageInfo(image_info);
209 return im;
210 #else
211 return NULL;
212 #endif
215 public:
218 * Read an image from a file
220 static image *read_image(const char *filename, exposure *exp, const char *name = "file",
221 unsigned int bayer = IMAGE_BAYER_DEFAULT, int init_reference_gain = 0) {
222 image *result;
224 if (bayer == IMAGE_BAYER_DEFAULT)
225 bayer = bayer_default;
227 if (is_eppm(filename)) {
228 result = read_ppm(filename, exp, bayer, init_reference_gain);
231 #ifdef USE_MAGICK
232 result = read_image_im_unaccel(filename, exp, name, bayer, init_reference_gain);
233 #else
234 result = read_ppm(filename, exp, bayer);
235 #endif
237 #if 0
238 result->accel_domain_sequence();
240 image *accel_result = new image_accel(result);
242 delete result;
244 result = accel_result;
245 #endif
247 return result;
251 * Initializer.
253 * Handle FILE_COUNT input files with names in array FILENAMES and
254 * output file OUTPUT_FILENAME. FILENAMES should be an array of char *
255 * that is never freed. OUTPUT_FILENAME should be a char * that is
256 * never freed.
258 * INPUT_EXPOSURE should be an array of FILE_COUNT exposure objects
259 * that is never freed. OUTPUT_EXPOSURE should be an exposure * that
260 * is never freed.
262 static void init(unsigned int _file_count, const char **_filenames,
263 const char *_output_filename, exposure **_input_exposure,
264 exposure *_output_exposure){
265 assert (_file_count > 0);
267 init_image();
269 filenames = _filenames;
270 file_count = _file_count;
271 output_filename = _output_filename;
272 input_exposure = _input_exposure;
273 output_exposure = _output_exposure;
275 images = (const image **)malloc(file_count * sizeof(image *));
276 bayer_specific = (unsigned int *)malloc(file_count * sizeof(unsigned int));
277 files_open = (int *)calloc(file_count, sizeof(int));
279 assert (images);
280 assert (bayer_specific);
281 assert (files_open);
283 if (!images || !files_open || !bayer_specific) {
284 fprintf(stderr, "Unable to allocate memory for images.\n");
285 exit(1);
288 for (unsigned int i = 0; i < file_count; i++)
289 bayer_specific[i] = IMAGE_BAYER_DEFAULT;
291 ui::get()->identify_output(output_filename);
294 static void ppm_plain() {
295 ppm_type = 2;
298 static void ppm_raw() {
299 ppm_type = 1;
302 static void ppm_auto() {
303 #ifdef USE_MAGICK
304 ppm_type = 0;
305 #else
306 fprintf(stderr, "\n\n*** Error: --auto flag not supported on this build. ***\n"
307 "*** (Hint: Rebuild with IMAGEMAGICK=1) ***\n\n");
308 exit(1);
309 #endif
312 static void set_default_bayer(unsigned int b) {
313 bayer_default = b;
316 static void set_specific_bayer(unsigned int index, unsigned int b) {
317 assert (bayer_specific);
318 bayer_specific[index] = b;
321 static void depth16() {
322 num_bits = 16;
323 mcv = 65535;
326 static void depth8() {
327 num_bits = 8;
328 mcv = 255;
331 static void set_cache(double size) {
332 cache_size_max = size;
335 static void destroy() {
336 assert (file_count > 0);
337 destroy_image();
340 static unsigned int count() {
341 assert (file_count > 0);
342 return file_count;
345 static const char *name(unsigned int image) {
346 assert (image < file_count);
348 return filenames[image];
351 static void def_nn(double _nn) {
352 nn_defined_radius = _nn;
355 static const char *output_name() {
356 assert (file_count > 0);
357 return output_filename;
361 * Write an image to a file
363 static void write_image(const char *filename, const image *im, exposure *exp = output_exposure, int rezero = 0, int exp_scale_override = 0) {
364 static int warned = 0;
367 * Handle ALE-specific magical filenames.
370 if (!strcmp(filename, "dump:")) {
371 fprintf(stderr, "Image dump: ");
372 for (unsigned int i = 0; i < im->height(); i++)
373 for (unsigned int j = 0; j < im->width(); j++) {
374 pixel p = im->get_pixel(i, j);
375 fprintf(stderr, "(%d, %d): [%f %f %f] ", i, j, (double) p[0], (double) p[1], (double) p[2]);
377 fprintf(stderr, "\n");
379 return;
382 image *unaccel_im = im->unaccel_equiv();
384 if (unaccel_im) {
385 im = unaccel_im;
388 #ifdef USE_MAGICK
391 * Patterned after http://www.imagemagick.org/www/api.html
392 * and http://www.imagemagick.org/www/smile.c
395 ExceptionInfo exception;
396 Image *mi;
397 ImageInfo *image_info;
398 PixelPacket *p;
400 unsigned int i, j;
402 GetExceptionInfo(&exception);
403 image_info = CloneImageInfo((ImageInfo *) NULL);
404 strncpy(image_info->filename, filename, MaxTextExtent);
406 mi = AllocateImage(image_info);
407 if (mi == (Image *) NULL)
408 MagickError(ResourceLimitError,
409 "Unable to display image", "MemoryAllocationFailed");
411 mi->columns = im->width();
412 mi->rows = im->height();
415 * Set the output image depth
418 if (MaxRGB < 65535 || mcv < 65535)
419 mi->depth = 8;
420 else
421 mi->depth = 16;
423 if (MaxRGB < 65535 && mcv == 65535 && !warned) {
424 fprintf(stderr, "\n\n*** Warning: " MagickPackageName " has not been compiled with 16 bit support.\n");
425 fprintf(stderr, "*** Writing output using 8 bits per channel.\n");
426 fprintf(stderr, "*** \n");
427 fprintf(stderr, "*** (To silence this warning, specify option --8bpc)\n\n\n");
429 warned = 1;
433 * Set compression type
436 if (ppm_type == 2) {
437 mi->compression = NoCompression;
438 image_info->compression = NoCompression;
439 strncpy(mi->magick, "PNM", MaxTextExtent);
440 strncpy(image_info->magick, "PNM", MaxTextExtent);
441 } else if (ppm_type == 1) {
442 strncpy(mi->magick, "PNM", MaxTextExtent);
443 strncpy(image_info->magick, "PNM", MaxTextExtent);
447 * Automatic exposure adjustment (don't blow out highlights)
449 ale_real maxval = 1;
450 ale_real minval = (rezero ? im->minval() : (ale_real) 0);
451 if (minval > 0)
452 minval = 0;
453 pixel minval_pixel(minval, minval, minval);
456 if (exposure_scale || exp_scale_override) {
457 ale_real new_maxval = im->maxval();
459 if (new_maxval > maxval)
460 maxval = new_maxval;
464 * Write the image
467 for (i = 0; i < mi->rows; i++) {
468 p = SetImagePixels(mi, 0, i, mi->columns, 1);
469 if (p == NULL)
470 break;
472 for (j = 0; j < mi->columns; j++) {
474 pixel value = im->get_pixel(i, j);
477 * Get nearest-neighbor defined values.
479 * XXX: While this implementation is correct, it is inefficient
480 * for large radii. A better implementation would search
481 * perimeters of squares of ever-increasing radius, tracking
482 * the best-so-far data until the square perimeter exceeded the
483 * best-so-far radius.
486 for (int k = 0; k < 3; k++)
487 if (isnan(value[k]))
488 for (int radius = 1; radius <= nn_defined_radius; radius++) {
489 double nearest_radius_squared = (radius + 1) * (radius + 1);
490 for (int ii = -radius; ii <= radius; ii++)
491 for (int jj = -radius; jj <= radius; jj++) {
492 if (!im->in_bounds(point(i + ii, j + jj)))
493 continue;
494 if (ii * ii + jj * jj < nearest_radius_squared
495 && finite(im->get_pixel(i + ii, j + jj)[k])) {
496 value[k] = im->get_pixel(i + ii, j + jj)[k];
497 nearest_radius_squared = ii * ii + jj * jj;
500 if (nearest_radius_squared < (radius + 1) * (radius + 1))
501 break;
505 * Unlinearize
508 pixel unlinearized(exp->unlinearize((value - minval_pixel)
509 / (maxval - minval)));
511 unlinearized = unlinearized.clamp();
513 p->red = (Quantum) ale_real_to_int(unlinearized[0], MaxRGB);
514 p->green = (Quantum) ale_real_to_int(unlinearized[1], MaxRGB);
515 p->blue = (Quantum) ale_real_to_int(unlinearized[2], MaxRGB);
516 p++;
519 if (!SyncImagePixels(mi))
520 break;
523 if (!WriteImage(image_info, mi)) {
526 * Perhaps file type was unknown? Set to PNM by default.
529 strncpy(mi->magick, "PNM", MaxTextExtent);
530 strncpy(image_info->magick, "PNM", MaxTextExtent);
532 if (!WriteImage(image_info, mi)) {
533 fprintf(stderr, "\n\n");
534 CatchException(&mi->exception);
535 fprintf(stderr, "\n");
536 exit(1);
540 DestroyImage(mi);
541 DestroyImageInfo(image_info);
542 #else
543 write_ppm(filename, im, exp, mcv, ppm_type == 2, rezero, exposure_scale || exp_scale_override,
544 nn_defined_radius);
545 #endif
547 if (unaccel_im) {
548 delete unaccel_im;
552 static void output(const image *i) {
553 assert (file_count > 0);
554 write_image(output_name(), i, output_exposure);
557 static void vise_write(const char *p, const char *s, const image *i) {
558 static int count = 0;
559 int length = strlen(p) + strlen(s) + 8;
560 char *output_string = (char *) malloc(length * sizeof(char));
562 snprintf(output_string, length, "%s%08d%s", p, count, s);
564 write_image(output_string, i, output_exposure);
566 count++;
569 static exposure &exp(int n) {
570 return *input_exposure[n];
573 static const exposure &const_exp(int n) {
574 return *input_exposure[n];
577 static exposure &exp() {
578 return *output_exposure;
581 static void exp_scale() {
582 exposure_scale = 1;
585 static void exp_noscale() {
586 exposure_scale = 0;
589 static const exposure &const_exp() {
590 return *output_exposure;
593 static const unsigned int bayer(unsigned int n) {
594 if (bayer_specific[n] == IMAGE_BAYER_DEFAULT)
595 return bayer_default;
596 else
597 return bayer_specific[n];
600 static const image *open(unsigned int n) {
601 assert (n < file_count);
602 assert (!files_open[n]);
604 files_open[n] = 1;
606 if (latest_close_num >= 0 && n == (unsigned int) latest_close_num) {
607 latest_close_num = -1;
608 return images[n];
611 if (n < cache_count)
612 return images[n];
614 ui::get()->loading_file();
615 image *i = read_image(filenames[n], input_exposure[n], "file", bayer(n), (n == 0));
617 images[n] = i;
619 return images[n];
622 static void open_all() {
623 for (unsigned int n = 0; n < file_count; n++)
624 open(n);
627 static const image *get_open(unsigned int n) {
628 assert (files_open[n]);
629 return images[n];
632 static image *copy(unsigned int n, const char *name) {
633 assert (n < file_count);
635 if (files_open[n])
636 return images[n]->clone(name);
637 else {
638 image *i = read_image(filenames[n], input_exposure[n], name, bayer(n), (n == 0));
639 return i;
643 static void close(unsigned int image) {
644 assert (image < file_count);
645 assert (files_open[image]);
647 files_open[image] = 0;
649 if (image < cache_count)
650 return;
652 if (image == cache_count) {
653 double image_size = ((double) images[image]->storage_size()) / pow(2, 20);
655 if (image_size + cache_size < cache_size_max) {
656 cache_size += image_size;
657 cache_count++;
658 ui::get()->cache(cache_size, cache_size_max);
659 return;
660 } else {
661 ui::get()->cache_status(0);
665 if (latest_close_num >= 0)
666 delete images[latest_close_num];
668 latest_close_num = image;
671 static void close_all() {
672 for (unsigned int n = 0; n < file_count; n++)
673 close(n);
678 #endif