diff options
Diffstat (limited to 'converter/other/fiasco/lib/image.c')
-rw-r--r-- | converter/other/fiasco/lib/image.c | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/converter/other/fiasco/lib/image.c b/converter/other/fiasco/lib/image.c new file mode 100644 index 00000000..019ba03c --- /dev/null +++ b/converter/other/fiasco/lib/image.c @@ -0,0 +1,512 @@ +/* + * image.c: Input and output of PNM images. + * + * Written by: Ullrich Hafner + * + * This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec) + * Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de> + */ + +/* + * $Date: 2000/06/15 17:21:30 $ + * $Author: hafner $ + * $Revision: 5.2 $ + * $State: Exp $ + */ + +#include "pnm.h" + +#include <string.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 + +*****************************************************************************/ + +fiasco_image_t * +fiasco_image_new (const char *filename) +/* + * FIASCO image constructor. + * Allocate memory for the FIASCO image structure and + * load the specified image `filename'. The image has to be in + * raw pgm or ppm format. + * + * Return value: + * pointer to the new image structure + * or NULL in case of an error + */ +{ + try + { + fiasco_image_t *image = Calloc (1, sizeof (fiasco_image_t)); + + image->private = read_image (filename); + image->delete = fiasco_image_delete; + image->get_width = fiasco_image_get_width; + image->get_height = fiasco_image_get_height; + image->is_color = fiasco_image_is_color; + + return image; + } + catch + { + return NULL; + } +} + +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 (!streq (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; + + strcpy (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 <NULL>."); +} + + +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 (const char *image_name) +/* + * Read image 'image_name'. + * + * Return value: + * pointer to the image structure. + */ +{ + FILE *input; /* input stream */ + image_t *image; /* pointer to new image structure */ + int width, height; /* image size */ + xelval maxval; /* Maxval of image */ + int format; /* Image's format code */ + bool_t color; /* color image ? (YES/NO) */ + + if (image_name == NULL) + input = stdin; + else + input = pm_openr((char*)image_name); + + pnm_readpnminit(input, &width, &height, &maxval, &format); + + 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, input, color, width, height, maxval, format); + + pm_close(input); + + return image; +} + +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 ("Writing of images in 4:2:0 format not supported."); + return; + } + + if (image_name == NULL) + output = stdout; + else if (strcmp(image_name, "-") == 0) + 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; +} + |