1 /*
2  *  image.c:        Input and output of PNM images.
3  *
4  *  Written by:     Ullrich Hafner
5  *
6  *  This file is part of FIASCO (Fractal Image And Sequence COdec)
7  *  Copyright (C) 1994-2000 Ullrich Hafner
8  */
9 
10 /*
11  *  $Date: 2000/06/15 17:21:30 $
12  *  $Author: hafner $
13  *  $Revision: 5.2 $
14  *  $State: Exp $
15  */
16 
17 #include "pnm.h"
18 
19 #include <string.h>
20 
21 #include "nstring.h"
22 
23 #include "types.h"
24 #include "macros.h"
25 #include "error.h"
26 
27 #include "fiasco.h"
28 #include "misc.h"
29 #include "image.h"
30 
31 /*****************************************************************************
32 
33                 prototypes
34 
35 *****************************************************************************/
36 
37 static void
38 init_chroma_tables (void);
39 
40 /*****************************************************************************
41 
42                 local variables
43 
44 *****************************************************************************/
45 static int *Cr_r_tab = NULL;
46 static int *Cr_g_tab = NULL;
47 static int *Cb_g_tab = NULL;
48 static int *Cb_b_tab = NULL;
49 
50 /*****************************************************************************
51 
52                 public code
53 
54 *****************************************************************************/
55 
56 static fiasco_image_t *
make_image_base(void)57 make_image_base(void)
58 {
59     fiasco_image_t * const imageP = Calloc (1, sizeof (fiasco_image_t));
60 
61     if (imageP == NULL)
62         pm_error("Failed to allocate memory for image object");
63     else {
64         imageP->delete     = fiasco_image_delete;
65         imageP->get_width  = fiasco_image_get_width;
66         imageP->get_height = fiasco_image_get_height;
67         imageP->is_color   = fiasco_image_is_color;
68     }
69     return imageP;
70 }
71 
72 
73 
74 fiasco_image_t *
fiasco_image_new_file(const char * const filename)75 fiasco_image_new_file(const char * const filename)
76 /*
77  *  FIASCO image constructor.
78  *  Allocate memory for the FIASCO image structure and
79  *  load the image from PNM file `filename'.
80  *
81  *  Return value:
82  *  pointer to the new image structure
83  *  or NULL in case of an error
84  */
85 {
86     fiasco_image_t * imageP;
87 
88     imageP = make_image_base();
89 
90     imageP->private = read_image_file(filename);
91 
92     return imageP;
93 }
94 
95 
96 
97 fiasco_image_t *
fiasco_image_new_stream(FILE * const ifP,unsigned int const width,unsigned int const height,xelval const maxval,int const format)98 fiasco_image_new_stream(FILE *       const ifP,
99                         unsigned int const width,
100                         unsigned int const height,
101                         xelval       const maxval,
102                         int          const format)
103 /*
104  *  FIASCO image constructor.
105  *  Allocate memory for the FIASCO image structure and
106  *  load the image from the PNM stream in *ifP, which is positioned just
107  *  after the header.  'width', 'height', 'maxval', and 'format' are the
108  *  parameters of the image, i.e. the contents of that header.
109  *
110  *  Return value:
111  *  pointer to the new image structure
112  *  or NULL in case of an error
113  */
114 {
115     fiasco_image_t * imageP;
116 
117     imageP = make_image_base();
118 
119     imageP->private = read_image_stream(ifP, width, height, maxval, format);
120 
121     return imageP;
122 }
123 
124 
125 
126 void
fiasco_image_delete(fiasco_image_t * image)127 fiasco_image_delete (fiasco_image_t *image)
128 /*
129  *  FIASCO image destructor.
130  *  Free memory of FIASCO image struct.
131  *
132  *  No return value.
133  *
134  *  Side effects:
135  *  structure 'image' is discarded.
136  */
137 {
138    image_t *this = cast_image (image);
139 
140    if (!this)
141       return;
142 
143    try
144    {
145       free_image (this);
146    }
147    catch
148    {
149       return;
150    }
151 }
152 
153 unsigned
fiasco_image_get_width(fiasco_image_t * image)154 fiasco_image_get_width (fiasco_image_t *image)
155 {
156    image_t *this = cast_image (image);
157 
158    if (!this)
159       return 0;
160    else
161       return this->width;
162 }
163 
164 unsigned
fiasco_image_get_height(fiasco_image_t * image)165 fiasco_image_get_height (fiasco_image_t *image)
166 {
167    image_t *this = cast_image (image);
168 
169    if (!this)
170       return 0;
171    else
172       return this->width;
173 }
174 
175 int
fiasco_image_is_color(fiasco_image_t * image)176 fiasco_image_is_color (fiasco_image_t *image)
177 {
178    image_t *this = cast_image (image);
179 
180    if (!this)
181       return 0;
182    else
183       return this->color;
184 }
185 
186 image_t *
cast_image(fiasco_image_t * image)187 cast_image (fiasco_image_t *image)
188 /*
189  *  Cast pointer `image' to type image_t.
190  *  Check whether `image' is a valid object of type image_t.
191  *
192  *  Return value:
193  *  pointer to dfiasco_t struct on success
194  *      NULL otherwise
195  */
196 {
197    image_t *this = (image_t *) image->private;
198    if (this)
199    {
200       if (!STRSEQ(this->id, "IFIASCO"))
201       {
202      set_error (_("Parameter `image' doesn't match required type."));
203      return NULL;
204       }
205    }
206    else
207    {
208       set_error (_("Parameter `%s' not defined (NULL)."), "image");
209    }
210 
211    return this;
212 }
213 
214 image_t *
alloc_image(unsigned width,unsigned height,bool_t color,format_e format)215 alloc_image (unsigned width, unsigned height, bool_t color, format_e format)
216 /*
217  *  Image constructor:
218  *  Allocate memory for the image_t structure.
219  *  Image size is given by 'width' and 'height'.
220  *  If 'color' == YES then allocate memory for three color bands (Y, Cb, Cr).
221  *  otherwise just allocate memory for a grayscale image.
222  *  'format' specifies whether image pixels of color images
223  *  are stored in 4:4:4 or 4:2:0 format.
224  *
225  *  Return value:
226  *  pointer to the new image structure.
227  */
228 {
229    image_t *image;
230    color_e band;
231 
232    if ((width & 1) || (height & 1))
233       error ("Width and height of images must be even numbers.");
234    if (!color)
235       format = FORMAT_4_4_4;
236 
237    image              = Calloc (1, sizeof (image_t));
238    image->width       = width;
239    image->height      = height;
240    image->color       = color;
241    image->format      = format;
242    image->reference_count = 1;
243 
244    STRSCPY(image->id, "IFIASCO");
245 
246    for (band = first_band (color); band <= last_band (color); band++)
247       if (format == FORMAT_4_2_0 && band != Y)
248      image->pixels [band] = Calloc ((width * height) >> 2,
249                     sizeof (word_t));
250       else
251      image->pixels [band] = Calloc (width * height, sizeof (word_t));
252 
253    return image;
254 }
255 
256 image_t *
clone_image(image_t * image)257 clone_image (image_t *image)
258 /*
259  *  Copy constructor:
260  *  Construct new image by copying the given `image'.
261  *
262  *  Return value:
263  *  pointer to the new image structure.
264  */
265 {
266    image_t *new = alloc_image (image->width, image->height, image->color,
267                    image->format);
268    color_e band;
269 
270    for (band = first_band (new->color); band <= last_band (new->color); band++)
271       if (new->format == FORMAT_4_2_0 && band != Y)
272       {
273      memcpy (new->pixels [band], image->pixels [band],
274          ((new->width * new->height) >> 2) * sizeof (word_t));
275       }
276       else
277       {
278      memcpy (new->pixels [band], image->pixels [band],
279          new->width * new->height * sizeof (word_t));
280       }
281 
282    return new;
283 }
284 
285 void
free_image(image_t * image)286 free_image (image_t *image)
287 /*
288  *  Image destructor:
289  *  Free memory of 'image' struct and pixel data.
290  *
291  *  No return value.
292  *
293  *  Side effects:
294  *  structure 'image' is discarded.
295  */
296 {
297    if (image != NULL)
298    {
299       if (--image->reference_count)
300      return;            /* image is still referenced */
301       else
302       {
303      color_e band;
304 
305      for (band  = first_band (image->color);
306           band <= last_band (image->color); band++)
307         if (image->pixels [band])
308            Free (image->pixels [band]);
309      Free (image);
310       }
311    }
312    else
313       warning ("Can't free image <NULL>.");
314 }
315 
316 
317 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)318 read_image_data(image_t * const image, FILE *input, const bool_t color,
319                 const int width, const int height, const xelval maxval,
320                 const int format) {
321    int row;
322    int i;      /* Cursor into image->pixels arrays */
323    xel * xelrow;
324    /* The following are just the normal rgb -> YCbCr conversion matrix,
325       except normalization to maxval 4095 (12 bit color) is built in
326       */
327    const double coeff_lu_r = +0.2989 / maxval * 4095;
328    const double coeff_lu_g = +0.5866 / maxval * 4095;
329    const double coeff_lu_b = +0.1145 / maxval * 4095;
330    const double coeff_cb_r = -0.1687 / maxval * 4095;
331    const double coeff_cb_g = -0.3312 / maxval * 4095;
332    const double coeff_cb_b = +0.5000 / maxval * 4095;
333    const double coeff_cr_r = +0.5000 / maxval * 4095;
334    const double coeff_cr_g = -0.4183 / maxval * 4095;
335    const double coeff_cr_b = -0.0816 / maxval * 4095;
336 
337    xelrow = pnm_allocrow(width);
338 
339    i = 0;
340    for (row = 0; row < height; row++) {
341        int col;
342        pnm_readpnmrow(input, xelrow, width, maxval, format);
343        for (col = 0; col < width; col++) {
344            if (color) {
345                image->pixels[Y][i] =
346                    coeff_lu_r * PPM_GETR(xelrow[col])
347                    + coeff_lu_g * PPM_GETG(xelrow[col])
348                    + coeff_lu_b * PPM_GETB(xelrow[col]) - 2048;
349                image->pixels[Cb][i] =
350                    coeff_cb_r * PPM_GETR(xelrow[col])
351                    + coeff_cb_g * PPM_GETG(xelrow[col])
352                    + coeff_cb_b * PPM_GETB(xelrow[col]);
353                image->pixels[Cr][i] =
354                    coeff_cr_r * PPM_GETR(xelrow[col])
355                    + coeff_cr_g * PPM_GETG(xelrow[col])
356                    + coeff_cr_b * PPM_GETB(xelrow[col]);
357 
358                i++;
359            } else
360                image->pixels[GRAY][i++] =
361                    PNM_GET1(xelrow[col]) * 4095 / maxval - 2048;
362        }
363    }
364 
365    free(xelrow);
366 }
367 
368 
369 
370 image_t *
read_image_stream(FILE * const ifP,unsigned int const width,unsigned int const height,xelval const maxval,int const format)371 read_image_stream(FILE *       const ifP,
372                   unsigned int const width,
373                   unsigned int const height,
374                   xelval       const maxval,
375                   int          const format)
376 /*
377  * Read one PNM image from stream *ifP, which is positioned just after the
378  *  header.  'width', 'height', 'maxval', and 'format' are the parameters of
379  *  the image (i.e. the contents of that header).
380  */
381 {
382    image_t  *image;         /* pointer to new image structure */
383    bool_t    color;         /* color image ? (YES/NO) */
384 
385    if (PNM_FORMAT_TYPE(format) == PPM_FORMAT)
386        color = YES;
387    else
388        color = NO;
389 
390    if (width < 32)
391        pm_error("Image must have a width of at least 32 pixels.");
392 
393    if (height < 32)
394        pm_error("Image must have a height of at least 32 pixels.");
395 
396    image = alloc_image (width, height, color, FORMAT_4_4_4);
397 
398    read_image_data(image, ifP, color, width, height, maxval, format);
399 
400    return image;
401 }
402 
403 
404 
405 image_t *
read_image_file(const char * const filename)406 read_image_file(const char * const filename)
407 /*
408  *  Read the PNM image from the file named 'filename'.
409  *
410  *  Return value:
411  *  pointer to the image structure.
412  */
413 {
414     FILE * ifP;
415     int    width, height;    /* image dimensions */
416     xelval   maxval;         /* Maxval of image */
417     int format;              /* Image's format code */
418     image_t * imageP;        /* pointer to new image structure */
419 
420     ifP = pm_openr(filename);
421 
422     pnm_readpnminit(ifP, &width, &height, &maxval, &format);
423 
424     imageP = read_image_stream(ifP, width, height, maxval, format);
425 
426     pm_close(ifP);
427 
428     return imageP;
429 }
430 
431 
432 
433 void
write_image(const char * image_name,const image_t * image)434 write_image (const char *image_name, const image_t *image)
435 /*
436  *  Write given 'image' data to the file 'image_name'.
437  *
438  *  No return value.
439  */
440 {
441    FILE *output;            /* output stream */
442    int format;
443    int row;
444    int i;     /* Cursor into image->pixel arrays */
445    xel * xelrow;
446    unsigned *gray_clip;         /* clipping table */
447 
448    assert (image && image_name);
449 
450    if (image->format == FORMAT_4_2_0)
451    {
452       warning ("We cannot write images in 4:2:0 format.");
453       return;
454    }
455 
456    if (image_name == NULL)
457        output = stdout;
458    else if (streq(image_name, "-"))
459        output = stdout;
460    else
461        output = pm_openw((char*)image_name);
462 
463    gray_clip  = init_clipping ();   /* mapping of int -> unsigned */
464    if (!gray_clip)
465       error (fiasco_get_error_message ());
466    init_chroma_tables ();
467 
468    format = image->color ? PPM_TYPE : PGM_TYPE;
469 
470    pnm_writepnminit(output, image->width, image->height, 255, format, 0);
471 
472    xelrow = pnm_allocrow(image->width);
473    i = 0;
474    for (row = 0; row < image->height; row++) {
475        int col;
476        for (col = 0; col < image->width; col++) {
477            if (image->color) {
478                word_t yval, cbval, crval;
479 
480                yval  = image->pixels[Y][i]  / 16 + 128;
481                cbval = image->pixels[Cb][i] / 16;
482                crval = image->pixels[Cr][i] / 16;
483 
484                PPM_ASSIGN(xelrow[col],
485                           gray_clip[yval + Cr_r_tab[crval]],
486                           gray_clip[yval + Cr_g_tab[crval] + Cb_g_tab [cbval]],
487                           gray_clip[yval + Cb_b_tab[cbval]]);
488 
489            } else
490                /* The 16 below should be 4095/255 = 16.0588 */
491                PNM_ASSIGN1(xelrow[col],
492                            gray_clip[image->pixels[GRAY][i]/16+128]);
493            i++;
494        }
495        pnm_writepnmrow(output, xelrow,
496                        image->width, 255, format, 0);
497    }
498    pnm_freerow(xelrow);
499 
500    pm_close(output);
501 }
502 
503 bool_t
same_image_type(const image_t * img1,const image_t * img2)504 same_image_type (const image_t *img1, const image_t *img2)
505 /*
506  *  Check whether the given images 'img1' and `img2' are of the same type.
507  *
508  *  Return value:
509  *  YES if images 'img1' and `img2' are of the same type
510  *  NO  otherwise.
511  */
512 {
513    assert (img1 && img2);
514 
515    return ((img1->width == img2->width)
516        && (img1->height == img2->height)
517        && (img1->color == img2->color)
518        && (img1->format == img2->format));
519 }
520 
521 /*****************************************************************************
522 
523                 private code
524 
525 *****************************************************************************/
526 
527 static void
init_chroma_tables(void)528 init_chroma_tables (void)
529 /*
530  *  Chroma tables are used to perform fast YCbCr->RGB color space conversion.
531  */
532 {
533    int crval, cbval, i;
534 
535    if (Cr_r_tab != NULL || Cr_g_tab != NULL ||
536        Cb_g_tab != NULL || Cb_b_tab != NULL)
537       return;
538 
539    Cr_r_tab = Calloc (768, sizeof (int));
540    Cr_g_tab = Calloc (768, sizeof (int));
541    Cb_g_tab = Calloc (768, sizeof (int));
542    Cb_b_tab = Calloc (768, sizeof (int));
543 
544    for (i = 256; i < 512; i++)
545    {
546       cbval = crval  = i - 128 - 256;
547 
548       Cr_r_tab[i] =  1.4022 * crval + 0.5;
549       Cr_g_tab[i] = -0.7145 * crval + 0.5;
550       Cb_g_tab[i] = -0.3456 * cbval + 0.5;
551       Cb_b_tab[i] =  1.7710 * cbval + 0.5;
552    }
553    for (i = 0; i < 256; i++)
554    {
555       Cr_r_tab[i] = Cr_r_tab[256];
556       Cr_g_tab[i] = Cr_g_tab[256];
557       Cb_g_tab[i] = Cb_g_tab[256];
558       Cb_b_tab[i] = Cb_b_tab[256];
559    }
560    for (i = 512; i < 768; i++)
561    {
562       Cr_r_tab[i] = Cr_r_tab[511];
563       Cr_g_tab[i] = Cr_g_tab[511];
564       Cb_g_tab[i] = Cb_g_tab[511];
565       Cb_b_tab[i] = Cb_b_tab[511];
566    }
567 
568    Cr_r_tab += 256 + 128;
569    Cr_g_tab += 256 + 128;
570    Cb_g_tab += 256 + 128;
571    Cb_b_tab += 256 + 128;
572 }
573