/* * image.c: Input and output of PNM images. * * Written by: Ullrich Hafner * * This file is part of FIASCO (Fractal Image And Sequence COdec) * Copyright (C) 1994-2000 Ullrich Hafner */ /* * $Date: 2000/06/15 17:21:30 $ * $Author: hafner $ * $Revision: 5.2 $ * $State: Exp $ */ #include "pnm.h" #include #include "nstring.h" #include "types.h" #include "macros.h" #include "error.h" #include "fiasco.h" #include "misc.h" #include "image.h" /***************************************************************************** prototypes *****************************************************************************/ static void init_chroma_tables (void); /***************************************************************************** local variables *****************************************************************************/ static int *Cr_r_tab = NULL; static int *Cr_g_tab = NULL; static int *Cb_g_tab = NULL; static int *Cb_b_tab = NULL; /***************************************************************************** public code *****************************************************************************/ static fiasco_image_t * make_image_base(void) { fiasco_image_t * const imageP = Calloc (1, sizeof (fiasco_image_t)); if (imageP == NULL) pm_error("Failed to allocate memory for image object"); else { imageP->delete = fiasco_image_delete; imageP->get_width = fiasco_image_get_width; imageP->get_height = fiasco_image_get_height; imageP->is_color = fiasco_image_is_color; } return imageP; } fiasco_image_t * fiasco_image_new_file(const char * const filename) /* * FIASCO image constructor. * Allocate memory for the FIASCO image structure and * load the image from PNM file `filename'. * * Return value: * pointer to the new image structure * or NULL in case of an error */ { fiasco_image_t * imageP; imageP = make_image_base(); imageP->private = read_image_file(filename); return imageP; } fiasco_image_t * fiasco_image_new_stream(FILE * const ifP, unsigned int const width, unsigned int const height, xelval const maxval, int const format) /* * FIASCO image constructor. * Allocate memory for the FIASCO image structure and * load the image from the PNM stream in *ifP, which is positioned just * after the header. 'width', 'height', 'maxval', and 'format' are the * parameters of the image, i.e. the contents of that header. * * Return value: * pointer to the new image structure * or NULL in case of an error */ { fiasco_image_t * imageP; imageP = make_image_base(); imageP->private = read_image_stream(ifP, width, height, maxval, format); return imageP; } void fiasco_image_delete (fiasco_image_t *image) /* * FIASCO image destructor. * Free memory of FIASCO image struct. * * No return value. * * Side effects: * structure 'image' is discarded. */ { image_t *this = cast_image (image); if (!this) return; try { free_image (this); } catch { return; } } unsigned fiasco_image_get_width (fiasco_image_t *image) { image_t *this = cast_image (image); if (!this) return 0; else return this->width; } unsigned fiasco_image_get_height (fiasco_image_t *image) { image_t *this = cast_image (image); if (!this) return 0; else return this->width; } int fiasco_image_is_color (fiasco_image_t *image) { image_t *this = cast_image (image); if (!this) return 0; else return this->color; } image_t * cast_image (fiasco_image_t *image) /* * Cast pointer `image' to type image_t. * Check whether `image' is a valid object of type image_t. * * Return value: * pointer to dfiasco_t struct on success * NULL otherwise */ { image_t *this = (image_t *) image->private; if (this) { if (!STRSEQ(this->id, "IFIASCO")) { set_error (_("Parameter `image' doesn't match required type.")); return NULL; } } else { set_error (_("Parameter `%s' not defined (NULL)."), "image"); } return this; } image_t * alloc_image (unsigned width, unsigned height, bool_t color, format_e format) /* * Image constructor: * Allocate memory for the image_t structure. * Image size is given by 'width' and 'height'. * If 'color' == YES then allocate memory for three color bands (Y, Cb, Cr). * otherwise just allocate memory for a grayscale image. * 'format' specifies whether image pixels of color images * are stored in 4:4:4 or 4:2:0 format. * * Return value: * pointer to the new image structure. */ { image_t *image; color_e band; if ((width & 1) || (height & 1)) error ("Width and height of images must be even numbers."); if (!color) format = FORMAT_4_4_4; image = Calloc (1, sizeof (image_t)); image->width = width; image->height = height; image->color = color; image->format = format; image->reference_count = 1; STRSCPY(image->id, "IFIASCO"); for (band = first_band (color); band <= last_band (color); band++) if (format == FORMAT_4_2_0 && band != Y) image->pixels [band] = Calloc ((width * height) >> 2, sizeof (word_t)); else image->pixels [band] = Calloc (width * height, sizeof (word_t)); return image; } image_t * clone_image (image_t *image) /* * Copy constructor: * Construct new image by copying the given `image'. * * Return value: * pointer to the new image structure. */ { image_t *new = alloc_image (image->width, image->height, image->color, image->format); color_e band; for (band = first_band (new->color); band <= last_band (new->color); band++) if (new->format == FORMAT_4_2_0 && band != Y) { memcpy (new->pixels [band], image->pixels [band], ((new->width * new->height) >> 2) * sizeof (word_t)); } else { memcpy (new->pixels [band], image->pixels [band], new->width * new->height * sizeof (word_t)); } return new; } void free_image (image_t *image) /* * Image destructor: * Free memory of 'image' struct and pixel data. * * No return value. * * Side effects: * structure 'image' is discarded. */ { if (image != NULL) { if (--image->reference_count) return; /* image is still referenced */ else { color_e band; for (band = first_band (image->color); band <= last_band (image->color); band++) if (image->pixels [band]) Free (image->pixels [band]); Free (image); } } else warning ("Can't free image ."); } static void read_image_data(image_t * const image, FILE *input, const bool_t color, const int width, const int height, const xelval maxval, const int format) { int row; int i; /* Cursor into image->pixels arrays */ xel * xelrow; /* The following are just the normal rgb -> YCbCr conversion matrix, except normalization to maxval 4095 (12 bit color) is built in */ const double coeff_lu_r = +0.2989 / maxval * 4095; const double coeff_lu_g = +0.5866 / maxval * 4095; const double coeff_lu_b = +0.1145 / maxval * 4095; const double coeff_cb_r = -0.1687 / maxval * 4095; const double coeff_cb_g = -0.3312 / maxval * 4095; const double coeff_cb_b = +0.5000 / maxval * 4095; const double coeff_cr_r = +0.5000 / maxval * 4095; const double coeff_cr_g = -0.4183 / maxval * 4095; const double coeff_cr_b = -0.0816 / maxval * 4095; xelrow = pnm_allocrow(width); i = 0; for (row = 0; row < height; row++) { int col; pnm_readpnmrow(input, xelrow, width, maxval, format); for (col = 0; col < width; col++) { if (color) { image->pixels[Y][i] = coeff_lu_r * PPM_GETR(xelrow[col]) + coeff_lu_g * PPM_GETG(xelrow[col]) + coeff_lu_b * PPM_GETB(xelrow[col]) - 2048; image->pixels[Cb][i] = coeff_cb_r * PPM_GETR(xelrow[col]) + coeff_cb_g * PPM_GETG(xelrow[col]) + coeff_cb_b * PPM_GETB(xelrow[col]); image->pixels[Cr][i] = coeff_cr_r * PPM_GETR(xelrow[col]) + coeff_cr_g * PPM_GETG(xelrow[col]) + coeff_cr_b * PPM_GETB(xelrow[col]); i++; } else image->pixels[GRAY][i++] = PNM_GET1(xelrow[col]) * 4095 / maxval - 2048; } } free(xelrow); } image_t * read_image_stream(FILE * const ifP, unsigned int const width, unsigned int const height, xelval const maxval, int const format) /* * Read one PNM image from stream *ifP, which is positioned just after the * header. 'width', 'height', 'maxval', and 'format' are the parameters of * the image (i.e. the contents of that header). */ { image_t *image; /* pointer to new image structure */ bool_t color; /* color image ? (YES/NO) */ if (PNM_FORMAT_TYPE(format) == PPM_FORMAT) color = YES; else color = NO; if (width < 32) pm_error("Image must have a width of at least 32 pixels."); if (height < 32) pm_error("Image must have a height of at least 32 pixels."); image = alloc_image (width, height, color, FORMAT_4_4_4); read_image_data(image, ifP, color, width, height, maxval, format); return image; } image_t * read_image_file(const char * const filename) /* * Read the PNM image from the file named 'filename'. * * Return value: * pointer to the image structure. */ { FILE * ifP; int width, height; /* image dimensions */ xelval maxval; /* Maxval of image */ int format; /* Image's format code */ image_t * imageP; /* pointer to new image structure */ ifP = pm_openr(filename); pnm_readpnminit(ifP, &width, &height, &maxval, &format); imageP = read_image_stream(ifP, width, height, maxval, format); pm_close(ifP); return imageP; } void write_image (const char *image_name, const image_t *image) /* * Write given 'image' data to the file 'image_name'. * * No return value. */ { FILE *output; /* output stream */ int format; int row; int i; /* Cursor into image->pixel arrays */ xel * xelrow; unsigned *gray_clip; /* clipping table */ assert (image && image_name); if (image->format == FORMAT_4_2_0) { warning ("We cannot write images in 4:2:0 format."); return; } if (image_name == NULL) output = stdout; else if (streq(image_name, "-")) output = stdout; else output = pm_openw((char*)image_name); gray_clip = init_clipping (); /* mapping of int -> unsigned */ if (!gray_clip) error (fiasco_get_error_message ()); init_chroma_tables (); format = image->color ? PPM_TYPE : PGM_TYPE; pnm_writepnminit(output, image->width, image->height, 255, format, 0); xelrow = pnm_allocrow(image->width); i = 0; for (row = 0; row < image->height; row++) { int col; for (col = 0; col < image->width; col++) { if (image->color) { word_t yval, cbval, crval; yval = image->pixels[Y][i] / 16 + 128; cbval = image->pixels[Cb][i] / 16; crval = image->pixels[Cr][i] / 16; PPM_ASSIGN(xelrow[col], gray_clip[yval + Cr_r_tab[crval]], gray_clip[yval + Cr_g_tab[crval] + Cb_g_tab [cbval]], gray_clip[yval + Cb_b_tab[cbval]]); } else /* The 16 below should be 4095/255 = 16.0588 */ PNM_ASSIGN1(xelrow[col], gray_clip[image->pixels[GRAY][i]/16+128]); i++; } pnm_writepnmrow(output, xelrow, image->width, 255, format, 0); } pnm_freerow(xelrow); pm_close(output); } bool_t same_image_type (const image_t *img1, const image_t *img2) /* * Check whether the given images 'img1' and `img2' are of the same type. * * Return value: * YES if images 'img1' and `img2' are of the same type * NO otherwise. */ { assert (img1 && img2); return ((img1->width == img2->width) && (img1->height == img2->height) && (img1->color == img2->color) && (img1->format == img2->format)); } /***************************************************************************** private code *****************************************************************************/ static void init_chroma_tables (void) /* * Chroma tables are used to perform fast YCbCr->RGB color space conversion. */ { int crval, cbval, i; if (Cr_r_tab != NULL || Cr_g_tab != NULL || Cb_g_tab != NULL || Cb_b_tab != NULL) return; Cr_r_tab = Calloc (768, sizeof (int)); Cr_g_tab = Calloc (768, sizeof (int)); Cb_g_tab = Calloc (768, sizeof (int)); Cb_b_tab = Calloc (768, sizeof (int)); for (i = 256; i < 512; i++) { cbval = crval = i - 128 - 256; Cr_r_tab[i] = 1.4022 * crval + 0.5; Cr_g_tab[i] = -0.7145 * crval + 0.5; Cb_g_tab[i] = -0.3456 * cbval + 0.5; Cb_b_tab[i] = 1.7710 * cbval + 0.5; } for (i = 0; i < 256; i++) { Cr_r_tab[i] = Cr_r_tab[256]; Cr_g_tab[i] = Cr_g_tab[256]; Cb_g_tab[i] = Cb_g_tab[256]; Cb_b_tab[i] = Cb_b_tab[256]; } for (i = 512; i < 768; i++) { Cr_r_tab[i] = Cr_r_tab[511]; Cr_g_tab[i] = Cr_g_tab[511]; Cb_g_tab[i] = Cb_g_tab[511]; Cb_b_tab[i] = Cb_b_tab[511]; } Cr_r_tab += 256 + 128; Cr_g_tab += 256 + 128; Cb_g_tab += 256 + 128; Cb_b_tab += 256 + 128; }