/* * This file is part of the XForms library package. * * XForms is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1, or * (at your option) any later version. * * XForms is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with XForms. If not, see . */ /* * This file is part of the XForms library package. * Copyright (c) 1998-2002 T.C. Zhao * All rights reserved. * * Public API for image file IO */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "include/forms.h" #include "flinternal.h" #include "flimage.h" #include "flimage_int.h" #include "private/flsnprintf.h" #include #include static int visual_cue( FL_IMAGE *, const char * ); static void error_message( FL_IMAGE *, const char * ); static int nimage; static FLIMAGE_SETUP current_setup; #define MaxImageFileNameLen 260 FLIMAGE_IO *flimage_io; FLIMAGE_QUANTIZE_RGB flimage_quantize_rgb; FLIMAGE_QUANTIZE_PACKED flimage_quantize_packed; static int ppm_added, gzip_added; static void add_default_formats( void ); /********************************************************************* *****************************************************************{**/ static void null_op( FL_IMAGE * im FL_UNUSED_ARG ) { } /********************************************************************* * Setup and image structure creation *****************************************************************{**/ void flimage_setup( FLIMAGE_SETUP * setup ) { current_setup = *setup; if ( setup->max_frames == 0 ) current_setup.max_frames = 30; if ( setup->delay > 2000 ) current_setup.delay = 2000; add_default_formats( ); } /*************************************** ***************************************/ static void init_setup( void ) { if ( current_setup.max_frames || current_setup.delay ) return; current_setup.max_frames = 30; current_setup.delay = 50; } /*************************************** ***************************************/ FL_IMAGE * flimage_alloc( void ) { FL_IMAGE *image = fl_calloc( 1, sizeof *image ); init_setup( ); add_default_formats( ); image->setup = ¤t_setup; image->visual_cue = current_setup.visual_cue; image->error_message = current_setup.error_message; image->gray_maxval = 255; image->ci_maxval = 255; image->tran_index = -1; image->tran_rgb = -1; image->app_background = -1; image->app_data = current_setup.app_data; image->total_frames = 1; image->xdist_scale = image->ydist_scale = 1.0; image->pscale = 1.0; image->display = flimage_display; image->xdisplay = current_setup.xdisplay; image->infile = fl_malloc( MaxImageFileNameLen * sizeof *image->infile ); image->outfile = fl_malloc( MaxImageFileNameLen * sizeof *image->outfile ); image->infile[0] = image->outfile[ 0 ] = '\0'; if ( ! image->xdisplay ) image->xdisplay = fl_display; /* initialize the quantizer */ if ( ! flimage_quantize_rgb ) { flimage_quantize_rgb = j2pass_quantize_rgb; flimage_quantize_packed = j2pass_quantize_packed; } /* make sure visual_cue and error_message are ok */ if ( ! image->visual_cue ) image->visual_cue = visual_cue; if ( ! image->error_message ) image->error_message = error_message; /* annotation stuff */ image->display_markers = null_op; image->free_markers = null_op; image->display_text = null_op; image->free_text = null_op; return image; } /************************************************************************* * Image identification *************************************************************************/ /* it's important that this routine be silient. is_supported calls this */ static FL_IMAGE * identify_image( const char *file ) { FILE *fp; FLIMAGE_IO *io; FL_IMAGE *image = NULL; if ( ! file || ! *file ) return NULL; if ( ! ( fp = fopen( file, "rb" ) ) ) { fprintf( stderr, "Can't open %s\n", file ); return NULL; } if ( ! ppm_added ) add_default_formats( ); for ( io = flimage_io; io->formal_name; io++ ) { if ( io->identify( fp ) > 0 ) { image = flimage_alloc( ); image->image_io = io; image->original_type = io->type; image->fpin = fp; strncpy( image->infile, file, MaxImageFileNameLen - 5 ); image->infile[ MaxImageFileNameLen - 5 ] = '\0'; return image; } else rewind( fp ); } fclose( fp ); return NULL; } /*************************************** * Check if the file is an image file. Should never bitch ***************************************/ int flimage_is_supported( const char * file ) { FILE *fp; FLIMAGE_IO *io; int n; if ( ! file || ! *file || ! ( fp = fopen( file, "rb" ) ) ) return 0; if ( ! ppm_added ) add_default_formats( ); for ( n = 0, io = flimage_io; io->formal_name; io++, n++ ) { if ( io->identify( fp ) > 0 ) { fclose( fp ); return n + 1; } else rewind( fp ); } fclose( fp ); return 0; } /********************************************************************* * Basic image file open routines ********************************************************************/ FL_IMAGE * flimage_open( const char * file ) { FL_IMAGE *im = identify_image( file ); if ( ! im ) M_err( "OpenImage", "%s: Unknown image format", file ? file : "null" ); return im; } /*************************************** ***************************************/ int flimage_close( FL_IMAGE * im ) { int status = 0; if ( ! im ) return -1; if ( im->fpin ) status = fclose( im->fpin ); if ( im->fpout ) status = fclose( im->fpout ); im->fpin = NULL; im->fpout = NULL; return status; } /************************************************************************ * Input routines **********************************************************************{*/ FL_IMAGE * flimage_read( FL_IMAGE * im ) { FL_IMAGE *image = im; FLIMAGE_IO *io; int error = 0; char buf[ 256 ]; if ( ! im || ! im->fpin ) return NULL; io = image->image_io; if ( ! io->read_description || ! io->read_pixels ) return NULL; /* Copy some default info */ image->type = io->type; image->fmt_name = io->short_name; image->foffset = ftell( image->fpin ); if ( ( error = ( io->read_description( image ) < 0 ) ) ) return NULL; /* image_io can change between description and read_pixels for io_filter'ed image */ io = image->image_io; if ( ( error = ( flimage_getmem( image ) < 0 ) ) ) { im->error_message( im, "ImageGetMem:Failed to allocate memory" ); flimage_freemem( im ); return NULL; } image->completed = 0; image->total = im->h; image->error_message( image, "" ); error = error || ( io->read_pixels( image ) < 0 ); image->completed = im->total; sprintf( buf, "Done Reading %s", image->fmt_name ); image->visual_cue( image, error ? "Error Reading" : buf ); image->original_type = image->type; image->available_type = image->type; image->modified = 1; if ( error ) { flimage_freemem( image ); image = NULL; } return image; } /*************************************** * turn a random frame into next frame ***************************************/ static int default_next_frame( FL_IMAGE * im ) { return im->random_frame( im, im->current_frame ); } /*************************************** ***************************************/ FL_IMAGE * flimage_load( const char * file ) { FL_IMAGE *image, *im; int err, tc, total_frames = 1; char buf[ 256 ]; add_default_formats( ); if ( ( image = flimage_open( file ) ) ) { if ( ! ( im = flimage_read( image ) ) ) { flimage_free( image ); image = NULL; } else image = im; } if ( ! image ) return image; /* transparency */ tc = image->tran_index; if ( FL_IsCI( image->type ) && tc >= 0 && tc < image->map_len ) image->tran_rgb = FL_PACK3( image->red_lut[ tc ], image->green_lut[ tc ], image->blue_lut[ tc ] ); /* Create a next_frame function on the fly if possible */ if ( ! image->next_frame && image->random_frame ) image->next_frame = default_next_frame; if ( ! image->more || ! image->next_frame ) { if ( ( ( FLIMAGE_IO * ) image->image_io )->annotation ) flimage_read_annotation( image ); flimage_close( image ); fli_safe_free( image->io_spec ); image->spec_size = 0; image->display = flimage_sdisplay; return image; } image->current_frame = 1; /* We have multi-frames */ err = 0; im = image; while ( ! err && im->more && im->current_frame < current_setup.max_frames ) { if ( ! ( err = ! ( im->next = flimage_dup_( im, 0 ) ) ) ) { im = im->next; im->current_frame++; } sprintf( buf, "Done image %d of %d", im->current_frame, current_setup.max_frames ); im->visual_cue( im, buf ); err = err || ( im->next_frame( im ) < 0 ); total_frames += ! err; } flimage_close( image ); image->completed = im->total; sprintf( buf, "Done Reading multi-frame %s", image->fmt_name ); image->visual_cue( image, err ? "Error Reading" : buf ); /* multi frame cleanup */ if ( image->cleanup ) image->cleanup( image ); /* update the number of frames */ image->total_frames = total_frames; return image; } /********************************************************************** * Output routines *********************************************************************/ static void convert_type( FL_IMAGE *, FLIMAGE_IO * ); int flimage_dump( FL_IMAGE * image, const char * filename, const char * fmt ) { FLIMAGE_IO *io = flimage_io; char buf[ 256 ], *p; FILE *fp; int status = -1, otype; FL_IMAGE *tmpimage; if ( ! image || image->type == FL_IMAGE_NONE ) { fprintf( stderr, "WriteImage: invalid or null image\n" ); return -1; } if ( ! fmt || ! *fmt ) fmt = image->fmt_name; for ( ; io->formal_name; io++ ) { if ( ( strcasecmp( io->formal_name, fmt ) == 0 || strcasecmp( io->short_name, fmt ) == 0 || strcasecmp( io->extension, fmt ) == 0 ) && io->write_image ) { strncpy( image->outfile, filename, MaxImageFileNameLen - 5 ); image->outfile[ MaxImageFileNameLen - 5 ] = '\0'; /* change extension */ if ( ! image->setup->no_auto_extension ) { if ( ( p = strrchr( image->outfile, '.' ) ) ) *p = '\0'; strcat( strcat( image->outfile, "." ), io->extension ); } if ( ! ( fp = fopen( image->outfile, "wb" ) ) ) { flimage_error( image, "can't open %s", image->outfile ); return -1; } image->fpout = fp; otype = image->type; for ( tmpimage = image; tmpimage; tmpimage = tmpimage->next ) convert_type( tmpimage, io ); if ( image->pre_write && image->pre_write( image ) < 0 ) { flimage_close( image ); return FL_CANCEL; } image->completed = 0; image->total = image->h; status = io->write_image( image ); if ( status >= 0 && image->post_write ) image->post_write( image ); image->type = otype; if ( io->annotation ) flimage_write_annotation( image ); flimage_close( image ); image->completed = image->total; fli_snprintf( buf, sizeof buf, "Done Writing %s(%s)", image->outfile, fmt ); image->visual_cue( image, buf ); return status; } } flimage_error( image, "don't know how to write %s", fmt ); return -1; } /*************************************** * Convert the image to a type the output routine can handle ***************************************/ static void convert_type( FL_IMAGE * im, FLIMAGE_IO * io ) { const int types[ ] = { FL_IMAGE_RGB, FL_IMAGE_PACKED, FL_IMAGE_CI, FL_IMAGE_GRAY, FL_IMAGE_MONO }; int ntypes = sizeof types / sizeof *types; int i, done; /* if the output routine can handle the current image type, do nothing */ if ( ( im->type & io->type ) ) return; /* must force the conversion */ im->force_convert = 1; if ( im->type == FL_IMAGE_CI || im->type == FL_IMAGE_RGB ) { for ( i = done = 0; !done && i < ntypes; i++ ) { if ( ( done = io->type & types[ i ] ) ) flimage_convert( im, types[ i ], 256 ); } } else if ( im->type == FL_IMAGE_GRAY || im->type == FL_IMAGE_MONO ) { if ( io->type & FL_IMAGE_CI ) flimage_convert( im, FL_IMAGE_CI, 256 ); else { for ( i = done = 0; !done && i < ntypes; i++ ) { if ( ( done = io->type & types[ i ] )) flimage_convert( im, types[ i ], 256 ); } } } else if ( im->type == FL_IMAGE_GRAY16 ) { if ( io->type & FL_IMAGE_GRAY ) flimage_convert( im, FL_IMAGE_GRAY, 0 ); else { for ( i = done = 0; !done && i < ntypes; i++ ) { if ( ( done = io->type & types[ i ] ) ) flimage_convert( im, types[ i ], 256 ); } } } else { M_err( "Output", "InternalError: unhandled image type: %s", flimage_type_name( im->type ) ); im->force_convert = 0; } } /***********************************************************************/ /*********************************************************************** * Memory related routines ***********************************************************************/ void flimage_free( FL_IMAGE * image ) { FL_IMAGE *im, *imnext; for ( im = image; im; im = imnext) { flimage_freemem( im ); if ( im == image ) flimage_close( im ); imnext = im->next; fli_safe_free( im->infile ); fli_safe_free( im->outfile ); im->next = NULL; fl_free( im ); } } /*************************************** ***************************************/ int flimage_get_linearlut( FL_IMAGE * im ) { if ( ! im->map_len ) { im->map_len = FL_PCMAX + 1; flimage_getcolormap( im ); } if ( im->map_len > im->llut_len ) { fli_safe_free( im->llut[ 0 ] ); fli_safe_free( im->llut[ 1 ] ); fli_safe_free( im->llut[ 2 ] ); } if ( ! im->llut[ 0 ] ) { im->llut[ 0 ] = fl_malloc( im->map_len * sizeof **im->llut ); im->llut[ 1 ] = fl_malloc( im->map_len * sizeof **im->llut ); im->llut[ 2 ] = fl_malloc( im->map_len * sizeof **im->llut ); } if ( ! im->llut[ 2 ] ) { fli_safe_free( im->llut[ 0 ] ); fli_safe_free( im->llut[ 1 ]); return -1; } im->llut_len = im->map_len; return 0; } /*************************************** ***************************************/ void flimage_free_linearlut( FL_IMAGE * im ) { fli_safe_free( im->llut[ 0 ] ); fli_safe_free( im->llut[ 1 ] ); fli_safe_free( im->llut[ 2 ] ); im->llut_len = 0; } /*************************************** ***************************************/ int flimage_getmem( FL_IMAGE * im ) { int nomap, err = 0, same_size; unsigned int esize; if ( ! im || ! im->w || ! im->h ) return -1; same_size = im->w == im->matc && im->h == im->matr; switch ( im->type ) { case FL_IMAGE_CI: case FL_IMAGE_MONO: if ( ( nomap = ( im->map_len <= 0 ) ) ) im->map_len = 2; if ( flimage_getcolormap( im ) < 0 ) { flimage_error( im, "can't alloc colormap" ); return -1; } if ( im->type == FL_IMAGE_MONO && nomap ) { im->red_lut[ 0 ] = im->green_lut[ 0 ] = im->blue_lut[ 0 ] = FL_PCMAX; im->red_lut[ 1 ] = im->green_lut[ 1 ] = im->blue_lut[ 1 ] = 0; } if ( ! same_size || ! im->ci ) { if ( im->ci ) fl_free_matrix( im->ci ); err = ! ( im->ci = fl_get_matrix( im->h, im->w, sizeof **im->ci ) ); } break; case FL_IMAGE_GRAY: case FL_IMAGE_GRAY16: flimage_getcolormap( im ); if ( ! same_size || ! im->gray ) { if ( im->gray ) fl_free_matrix( im->gray ); err = ! ( im->gray = fl_get_matrix( im->h, im->w, sizeof **im->gray ) ); } if ( ! err && ( ! im->wlut || im->gray_maxval > im->wlut_len ) ) { fli_safe_free( im->wlut ); if ( ( im->wlut_len = im->gray_maxval + 1 ) < 256 ) im->wlut_len = 256; err = ! ( im->wlut = fl_malloc( im->wlut_len * sizeof *im->wlut ) ); } break; case FL_IMAGE_PACKED: if ( ! same_size || ! im->packed ) { if ( im->packed ) fl_free_matrix( im->packed ); err = ! ( im->packed = fl_get_matrix( im->h, im->w, sizeof **im->packed ) ); } break; case FL_IMAGE_RGB: /* it's possible for some image format to be RGB yet * have a redundant colormap in it (xwd for example) */ flimage_getcolormap( im ); if ( ! same_size || ! im->red ) { if ( im->red ) { fl_free_matrix( im->red ); fl_free_matrix( im->green ); fl_free_matrix( im->blue ); fl_free_matrix( im->alpha ); } esize = sizeof **im->red; err = ! ( im->red = fl_get_matrix( im->h, im->w, esize ) ) || ! ( im->green = fl_get_matrix( im->h, im->w, esize ) ) || ! ( im->blue = fl_get_matrix( im->h, im->w, esize ) ) || ! ( im->alpha = fl_get_matrix( im->h, im->w, esize ) ); if ( err ) { fl_free_matrix( im->red ); fl_free_matrix( im->green ); fl_free_matrix( im->blue ); fl_free_matrix( im->alpha ); im->red = NULL; } else { im->rgba[ 0 ] = im->red; im->rgba[ 1 ] = im->green; im->rgba[ 2 ] = im->blue; im->rgba[ 3 ] = im->alpha; } } break; default: return -1; } im->matr = im->h; im->matc = im->w; return err ? -1 : 0; } /*************************************** ***************************************/ int flimage_getcolormap( FL_IMAGE * im ) { int len = im->map_len; if ( len <= 0 ) return -1; if ( len > FLIMAGE_MAXLUT ) len = im->map_len = FLIMAGE_MAXLUT; im->red_lut = fl_realloc( im->red_lut, len * sizeof *im->red_lut ); im->green_lut = fl_realloc( im->green_lut, len * sizeof *im->green_lut ); im->blue_lut = fl_realloc( im->blue_lut, len * sizeof *im->blue_lut ); im->alpha_lut = fl_realloc( im->alpha_lut, len * sizeof *im->alpha_lut ); if ( ! im->alpha_lut ) { fli_safe_free( im->red_lut ); fli_safe_free( im->green_lut ); fli_safe_free( im->blue_lut ); return -1; } im->lut[ 0 ] = im->red_lut; im->lut[ 1 ] = im->green_lut; im->lut[ 2 ] = im->blue_lut; im->lut[ 3 ] = im->alpha_lut; if ( im->map_len > im->wlut_len && FL_IsGray( im->type ) ) { im->wlut = fl_realloc( im->wlut, im->map_len * sizeof *im->wlut ); if ( ! im->wlut ) { im->wlut_len = 0; return -1; } im->wlut_len = im->map_len; } return 0; } /*************************************** * Before we modify the current image, need to invalidate/free all * other types ***************************************/ void flimage_invalidate_pixels( FL_IMAGE * im ) { if ( ! FL_IsGray( im->type ) ) { fl_free_matrix( im->gray ); im->gray = NULL; } if ( ! FL_IsCI( im->type ) ) { fl_free_matrix( im->ci ); im->ci = NULL; } if ( im->type != FL_IMAGE_PACKED ) { fl_free_matrix( im->packed ); im->packed = NULL; } if ( im->type != FL_IMAGE_RGB ) { fl_free_matrix( im->red ); fl_free_matrix( im->green ); fl_free_matrix( im->blue ); fl_free_matrix( im->alpha ); im->red = im->green = im->blue = im->alpha = NULL; } im->available_type = im->type; } /*************************************** * Free all allocated memory associated with the image ***************************************/ void flimage_freemem( FL_IMAGE * image ) { if ( ! image || ! image->w || ! image->h ) return; if ( image->ci ) { fl_free_matrix( image->ci ); image->ci = NULL; } fli_safe_free( image->wlut ); image->wlut_len = 0; if ( image->gray ) { fl_free_matrix( image->gray ); image->gray = NULL; } if ( image->packed ) { fl_free_matrix( image->packed ); image->packed = NULL; } if ( image->red ) { fl_free_matrix( image->red ); fl_free_matrix( image->green ); fl_free_matrix( image->blue ); fl_free_matrix( image->alpha ); image->red = image->green = image->blue = image->alpha = NULL; } if ( image->map_len > 0 && image->red_lut ) { fl_free( image->red_lut ); fl_free( image->green_lut ); fl_free( image->blue_lut ); fl_free( image->alpha_lut ); image->red_lut = image->green_lut = image->blue_lut = NULL; image->map_len = 0; } fli_safe_free( image->comments ); image->comments_len = 0; image->free_text( image ); image->free_markers( image ); flimage_free_linearlut( image ); if ( image->pixmap ) { XFreePixmap( image->xdisplay, image->pixmap ); image->pixmap = None; image->pixmap_depth = 0; } if ( image->ximage ) { XDestroyImage( ( XImage * ) image->ximage ); image->ximage = NULL; } if ( image->gc ) { XFreeGC( image->xdisplay, image->gc ); image->gc = None; } if ( image->textgc ) { XFreeGC( image->xdisplay, image->textgc ); image->textgc = None; } if ( image->markergc ) { XFreeGC( image->xdisplay, image->markergc ); image->markergc = None; } if ( image->pixels ) { fl_free_matrix( image->pixels ); image->pixels = NULL; } fli_safe_free( image->io_spec ); image->spec_size = 0; fli_safe_free( image->info ); image->w = image->h = 0; image->matr = image->matc = 0; image->type = FL_IMAGE_NONE; image->available_type = FL_IMAGE_NONE; } /*************************************** ***************************************/ int flimage_add_format( const char * formal_name, const char * short_name, const char * extension, int type, FLIMAGE_Identify identify, FLIMAGE_Description description, FLIMAGE_Read_Pixels read_pixels, FLIMAGE_Write_Image write_image ) { int i, k; FLIMAGE_IO *thisIO; if ( ! formal_name || ! *formal_name || ! short_name || ! *short_name ) return -1; ppm_added = ppm_added || ( extension && strcmp( "ppm", extension ) == 0 ); gzip_added = gzip_added || ( extension && strcmp("gz", extension ) == 0 ); if ( type <= 0 || type > FL_IMAGE_FLEX ) return -1; if ( ! description || ! read_pixels || ! identify ) description = read_pixels = 0; if ( flimage_io == 0 ) { nimage = 1; /* sentinel */ flimage_io = fl_calloc( 3, sizeof *flimage_io ); } else flimage_io = fl_realloc( flimage_io, ( nimage + 2 ) * sizeof *flimage_io ); k = nimage; /* find out if the image format is already installed. Replace if yes */ for ( i = 0; flimage_io[ i ].formal_name; i++ ) { if ( strcmp( flimage_io[ i ].formal_name, formal_name ) == 0 && strcmp( flimage_io[ i ].short_name, short_name ) == 0 ) { M_err( "flimage_add_format", "%s already supported. Replaced", short_name ); k = i + 1; } } thisIO = flimage_io + k - 1; thisIO->formal_name = formal_name; thisIO->short_name = short_name; thisIO->extension = extension ? extension : short_name; thisIO->type = type; thisIO->identify = identify; thisIO->read_description = description; thisIO->read_pixels = read_pixels; thisIO->write_image = write_image; thisIO->annotation = 0; nimage += k == nimage; if ( ! strcmp( short_name, "ppm" ) || ! strcmp( short_name, "pgm" ) ) thisIO->annotation = 1; /* sentinel */ ( ++thisIO )->formal_name = NULL; return k; } /*************************************** ***************************************/ void flimage_set_annotation_support( int in, int flag ) { --in; if ( in < 0 || in >= nimage ) return; flimage_io[ in ].annotation = flag != 0; } #define VN( a ) {a,#a} static FLI_VN_PAIR types[ ] = { VN( FL_IMAGE_NONE ), VN( FL_IMAGE_CI ), VN( FL_IMAGE_MONO ), VN( FL_IMAGE_RGB ), VN( FL_IMAGE_PACKED ), VN( FL_IMAGE_GRAY ), VN( FL_IMAGE_GRAY16 ), VN( FL_IMAGE_FLEX ), { -1, NULL } }; /*************************************** ***************************************/ const char * flimage_type_name( int type ) { return fli_get_vn_name( types, type ); } /*************************************** ***************************************/ int flimage_get_number_of_formats( void ) { add_default_formats( ); return nimage - 1; } /*************************************** ***************************************/ #define MaxInfoBuffer 6 const FLIMAGE_FORMAT_INFO * flimage_get_format_info( int n ) { static FLIMAGE_FORMAT_INFO fmt_return[ MaxInfoBuffer ]; static int k; FLIMAGE_IO *io; FLIMAGE_FORMAT_INFO *fmtinfo; add_default_formats( ); if ( n <= 0 || n > nimage ) return 0; fmtinfo = fmt_return + k++ % MaxInfoBuffer; n--; io = flimage_io + n; fmtinfo->formal_name = io->formal_name; fmtinfo->short_name = io->short_name; fmtinfo->extension = io->extension; fmtinfo->type = io->type; fmtinfo->annotation = io->annotation; fmtinfo->read_write = ( io->write_image ? FLIMAGE_WRITABLE : 0 ) | ( io->read_pixels ? FLIMAGE_READABLE : 0 ); return fmtinfo; } /*************************************** ***************************************/ void flimage_replace_image( FL_IMAGE * im, int w, int h, void * r, void * g, void * b ) { /* If we replace an image, all old backingstore pixels are invalid. Free all images */ flimage_invalidate_pixels( im ); im->w = w; im->h = h; if ( im->type == FL_IMAGE_RGB ) { fl_free_matrix( im->red ); fl_free_matrix( im->green ); fl_free_matrix( im->blue ); fl_free_matrix( im->alpha ); im->red = r; im->green = g; im->blue = b; /* Dont' have to preserve the alpha. remember to doc it */ im->alpha = fl_get_matrix( h, w, sizeof **im->alpha ); } else if ( FL_IsCI( im->type ) ) { fl_free_matrix( im->ci ); im->ci = r; } else if ( FL_IsGray( im->type ) ) { fl_free_matrix( im->gray ); im->gray = r; } else { M_err( "flimage_replace_image", "InternalError: bad type=%s", flimage_type_name( im->type ) ); return; } im->matr = h; im->matc = w; im->total = im->h; /* invalidate subimage settings if any */ im->sx = im->sy = im->sw = im->sh = 0; im->modified = 1; } /*************************************** ***************************************/ void flimage_add_comments( FL_IMAGE * im, const char * s, int len ) { /* Null entry clears comments */ if ( ! s || len <= 0 ) { fli_safe_free( im->comments ); im->comments_len = 0; return; } im->comments = fl_realloc( im->comments, im->comments_len + len + 1 ); strcpy( im->comments + im->comments_len, s ); im->comments_len += len; } /*************************************** ***************************************/ int flimage_windowlevel( FL_IMAGE * im, int level, int width) { if ( ! im || im->type != FL_IMAGE_GRAY16 ) return -1; im->modified = im->level != level || im->wwidth != width; if ( im->modified ) { im->level = level > im->gray_maxval ? im->gray_maxval : level; im->wwidth = width; } return im->modified; } /*************************************** ***************************************/ static int visual_cue( FL_IMAGE * im, const char * s ) { if ( im->completed < 0 ) { /* don't know how long */ fprintf( stderr, "\r%s", s ); } else if ( im->completed >= 0 && im->completed != im->total ) { fprintf( stderr, "\r%s %3.0f%%(%d of %d) ", s, 100.0 * im->completed / ( im->total - 1.0 ), im->completed, im->h ); } else if ( im->completed == im->total ) fprintf( stderr, "\n%s 100%%(%d of %d)\n", s, im->total, im->total ); return 0; } /*************************************** ***************************************/ static void copy_pixels( FL_IMAGE * dim, FL_IMAGE * sim ) { unsigned int size; flimage_getmem( dim ); switch ( sim->type ) { case FLIMAGE_RGB: size = sim->w * sim->h * sizeof **sim->red; memcpy( dim->red[ 0 ], sim->red[ 0 ], size ); memcpy( dim->green[ 0 ], sim->green[ 0 ], size ); memcpy( dim->blue[ 0 ], sim->blue[ 0 ], size ); memcpy( dim->alpha[0 ], sim->alpha[ 0 ], size ); break; case FLIMAGE_CI: case FLIMAGE_MONO: size = sim->w * sim->h * sizeof **sim->ci; memcpy( dim->ci[ 0 ], sim->ci[ 0 ], size ); break; case FLIMAGE_GRAY: case FLIMAGE_GRAY16: size = sim->w * sim->h * sizeof **sim->gray; memcpy( dim->gray[ 0 ], sim->gray[ 0 ], size ); break; default: M_err( "copy_pixel", "Bad type: %d", sim->type ); break; } } /*************************************** ***************************************/ FL_IMAGE * flimage_dup( FL_IMAGE * sim ) { if ( ! sim || ! sim->w || sim->type == FLIMAGE_NONE ) return NULL; return flimage_dup_( sim, 1 ); } /*************************************** * duplicate an image, with or without the pixels ***************************************/ FL_IMAGE * flimage_dup_( FL_IMAGE * sim, int pix ) { FL_IMAGE *im = flimage_alloc( ); unsigned int mapsize = sim->map_len * sizeof *sim->red_lut; char *infile, *outfile; if ( ! im ) { flimage_error( sim,"malloc() failed in image_dup()" ); return 0; } infile = im->infile; outfile = im->outfile; memcpy( im, sim, sizeof *im ); /* reset all pointers */ im->red = im->green = im->blue = im->alpha = NULL; im->red_lut = im->green_lut = im->blue_lut = im->alpha_lut = NULL; im->gray = NULL; im->ci = NULL; im->red16 = im->green16 = im->blue16 = im->alpha16 = NULL; im->packed = NULL; im->wlut = NULL; im->llut[ 0 ] = im->llut[ 1 ] = im->llut[ 2 ] = NULL; im->extra_io_info = NULL; im->info = NULL; flimage_getmem( im ); im->available_type = im->type; im->next = NULL; strcpy( im->infile = infile, sim->infile ); strcpy( im->outfile = outfile, sim->outfile ); /* copy pixels if requested */ if ( pix ) copy_pixels( im, sim ); if ( mapsize ) { if ( flimage_getcolormap( im ) < 0 ) { flimage_error( im, "Can't alloc colormap" ); return 0; } memcpy( im->red_lut, sim->red_lut, mapsize ); memcpy( im->green_lut, sim->green_lut, mapsize ); memcpy( im->blue_lut, sim->blue_lut, mapsize ); memcpy( im->alpha_lut, sim->alpha_lut, mapsize ); } im->io_spec = NULL; if ( sim->spec_size && sim->io_spec ) { im->io_spec = fl_malloc( sim->spec_size ); memcpy( im->io_spec, sim->io_spec, sim->spec_size ); } /* Reset stuff that's on a per-image basis */ im->depth = 0; im->vclass = 0; im->display_type = 0; im->ximage = 0; im->visual = 0; im->pixels = 0; im->pixmap = None; im->ximage = NULL; im->info = 0; im->win = None; im->gc = im->textgc = im->markergc = None; im->text = NULL; im->ntext = 0; im->marker = 0; im->nmarkers = 0; im->comments = NULL; im->comments_len = 0; return im; } /*************************************** ***************************************/ static void error_message( FL_IMAGE * im FL_UNUSED_ARG, const char * s ) { if ( s && *s ) M_err( 0, s ); } /*************************************** * A short cut for error message generating ***************************************/ void flimage_error( FL_IMAGE * im, const char * fmt, ... ) { va_list args; char buf[ 1024 ]; buf[ 0 ] = '\0'; if ( fmt && *fmt ) { va_start( args, fmt ); fli_vsnprintf( buf, sizeof buf, fmt, args ); va_end( args ); } im->error_message( im, buf ); } /*************************************** ***************************************/ void flimage_free_rgb( FL_IMAGE * im ) { fl_free_matrix( im->red ); fl_free_matrix( im->green ); fl_free_matrix( im->blue ); fl_free_matrix( im->alpha ); im->red = im->green = im->blue = im->alpha = NULL; im->available_type &= ~FL_IMAGE_RGB; } /*************************************** ***************************************/ void flimage_free_ci( FL_IMAGE * im ) { fl_free_matrix( im->ci ); im->ci = NULL; im->available_type = ~ FL_IMAGE_CI; } /*************************************** ***************************************/ void flimage_free_gray( FL_IMAGE * im ) { fl_free_matrix( im->gray ); im->gray = NULL; im->available_type = ~FL_IMAGE_GRAY; } /*************************************** * unpack packed bits into color indeces (0 or 1). len is the length * of packed bytes ***************************************/ void unpack_bits( unsigned short * out, unsigned char * in, int len ) { unsigned int mask = 0x80; for ( ; --len >= 0; out++, mask >>= 1 ) { if ( ! mask ) { in++; mask = 0x80; } *out = ( *in & mask ) != 0; } } /*************************************** * pack color index (0 or 1 ) into bytes. len is number of indeces ***************************************/ void pack_bits( unsigned char * out, unsigned short * in, int len) { int k = 0, bit = 0; for ( k = bit = 0; --len >= 0; in++ ) { k = ( k << 1 ) | *in; if ( ++bit == 8 ) { *out++ = k; k = bit = 0; } } /* left overs */ if ( bit ) { k <<= 8 - bit; *out++ = k; } } /*************************************** * Default supported image formats ***************************************/ static void add_default_formats( void ) { if ( ! ppm_added ) { flimage_enable_pnm( ); flimage_enable_genesis( ); } if ( ! gzip_added ) flimage_enable_gzip( ); } /*************************************** * Given a format, find the corresponding io handler ***************************************/ FLIMAGE_IO * flimage_find_imageIO( const char *fmt ) { FLIMAGE_IO *io = flimage_io; for ( ; io && io->formal_name; io++ ) { if ( ! strcmp( io->formal_name, fmt ) || ! strcmp( io->short_name, fmt ) || ! strcmp( io->extension, fmt ) ) return io; } return NULL; } /*************************************** * Annoation IO ***************************************/ static int write_marker( FLIMAGE_MARKER * m, FILE * fp ) { int r, g, b; fprintf( fp, "%s %d %d %d %d %d %d %d %d", m->name, m->x, m->y, m->w, m->h, m->fill, m->angle, m->thickness, m->style ); FL_UNPACK( m->color, r, g, b ); fprintf( fp, " %d %d %d", r, g, b ); FL_UNPACK( m->bcolor, r, g, b ); fprintf( fp, " %d %d %d\n", r, g, b ); return 0; } /*************************************** ***************************************/ static int read_marker( FLIMAGE_MARKER * m, FILE * fp ) { char buf[ 128 ]; static char name[ 64 ]; int r, g, b, br, bg, bb; if ( fgets( buf, sizeof buf - 1, fp ) ) buf[ sizeof buf - 1 ] = '\0'; else return -1; if ( sscanf( buf, "%63s %d %d %d %d %d %d %d %d %d %d %d %d %d %d", name, &m->x, &m->y, &m->w, &m->h, &m->fill, &m->angle, &m->thickness, &m->style, &r, &g, &b, &br, &bg, &bb ) == 15 ) { m->name = name; m->color = FL_PACK( r, g, b ); m->bcolor = FL_PACK( br, bg, bb ); return 0; } return -1; } #define LB '(' #define RB ')' static FLI_VN_PAIR fonts_vn[ ] = { { FL_NORMAL_STYLE, "Helvetica" }, { FL_ITALIC_STYLE, "Helvetica-Oblique" }, { FL_BOLD_STYLE, "Helvetica-Bold" }, { FL_BOLDITALIC_STYLE, " Helvetica-BoldOblique" }, { FL_FIXED_STYLE, "Courier" }, { FL_FIXEDBOLD_STYLE, "Courier-Bold" }, { FL_FIXEDITALIC_STYLE, "Courier-Oblique" }, { FL_FIXEDBOLDITALIC_STYLE, "Courier-BoldOblique" }, { FL_TIMES_STYLE, "Times-Roman" }, { FL_TIMESBOLD_STYLE, "Times-Bold" }, { FL_TIMESITALIC_STYLE, "Times-Oblique" }, { FL_TIMESBOLDITALIC_STYLE, "Times-BoldOblique" }, { FL_SYMBOL_STYLE, "Symbol" }, { FL_SHADOW_STYLE, "Shadow" }, { FL_ENGRAVED_STYLE, "Engraved" }, { FL_ENGRAVED_STYLE, "Enbossed" }, { -1, NULL } /* sentinel */ }; /*************************************** ***************************************/ static const char * get_font_style( int fstyle ) { static char retbuf[ 128 ]; const char *font_spstyle = "normal"; int spstyle = fstyle / FL_SHADOW_STYLE; int style = fstyle % FL_SHADOW_STYLE; strcpy( retbuf, fli_get_vn_name( fonts_vn, style ) ); if ( spstyle ) font_spstyle = fli_get_vn_name( fonts_vn, spstyle * FL_SHADOW_STYLE ); return strcat( strcat( retbuf, " " ), font_spstyle ); } static FLI_VN_PAIR align_vn[ ] = { { FL_ALIGN_CENTER, "center" }, { FL_ALIGN_TOP, "top" }, { FL_ALIGN_LEFT, "left" }, { FL_ALIGN_RIGHT, "right" }, { FL_ALIGN_LEFT_TOP, "lefttop" }, { FL_ALIGN_RIGHT_TOP, "righttop" }, { FL_ALIGN_LEFT_BOTTOM, "leftbottom" }, { FL_ALIGN_RIGHT_BOTTOM, "rightbottom" }, { -1, NULL } /* sentinel */ }; /*************************************** ***************************************/ static void write_text( FLIMAGE_TEXT * t, FILE * fp ) { int r, g, b; char *p; /* output string. */ putc( LB, fp ); for ( p = t->str; p && *p; p++ ) { if ( *p == RB ) putc( '\\', fp ); putc( *p, fp ); } putc( RB, fp ); fprintf( fp, " %s %d %d %d %s %d %d", get_font_style( t->style ), t->size, t->x, t->y, fli_get_vn_name( align_vn, t->align ), t->angle, t->nobk ); FL_UNPACK( t->color, r, g, b ); fprintf( fp, " %d %d %d", r, g, b ); FL_UNPACK( t->bcolor, r, g, b ); fprintf( fp, " %d %d %d\n", r, g, b ); } /*************************************** ***************************************/ static int read_text( FLIMAGE_TEXT * t, FILE * fp ) { char buf[ 1024 ], fnt[ 64 ], style[ 64 ], align[ 64 ]; static char name[ 512 ]; int r, g, b, br, bg, bb; char *p = buf + 1, *s = name, *ss = name + sizeof name - 1; if ( fgets( buf, sizeof buf - 1, fp ) ) buf[ sizeof buf - 1 ] = '\0'; else buf[ 0 ] = '\0'; for ( ; s < ss && *p && ( *p != ')' || *( p - 1 ) == '\\' ); p++ ) *s++ = *p; *s = '\0'; if ( sscanf( p + 1, "%63s %63s %d %d %d %63s %d %d %d %d %d %d %d %d", fnt, style, &t->size, &t->x, &t->y, align, &t->angle, &t->nobk, &r, &g, &b, &br, &bg, &bb ) == 14 ) { t->str = name; t->len = s - name; t->style = fli_get_vn_value( fonts_vn, fnt ); t->style |= fli_get_vn_value( fonts_vn, style ); t->align = fli_get_vn_value( align_vn, align ); t->color = FL_PACK( r, g, b ); t->bcolor = FL_PACK( br, bg, bb ); return 0; } return -1; } /*************************************** ***************************************/ static int skip_line( FILE * fp ) { int c; if ( ( c = getc( fp ) ) == '#' ) { while ( ( c = getc( fp ) ) != '\n' && c != EOF ) /* empty */ ; } else ungetc( c, fp ); return c == '#' && c != EOF; } /*************************************** ***************************************/ #define TEXTVERSION 1 #define MARKERVERSION 1 int flimage_write_annotation( FL_IMAGE * im ) { FILE *fp; int i; FLIMAGE_MARKER *m; FLIMAGE_TEXT *t; if ( !im || im->type == FL_IMAGE_NONE ) return -1; fp = im->fpout; /* Write markers first */ if ( im->nmarkers ) { fprintf( fp, "\n###markers %d %d\n", im->nmarkers, MARKERVERSION ); fprintf( fp, "# name x y w h fill angle thick style r g b r g b\n" ); m = im->marker; for ( i = 0; i < im->nmarkers; i++, m++ ) write_marker( m, fp ); } if ( im->ntext ) { fprintf( fp, "###text %d %d\n", im->ntext, TEXTVERSION ); fprintf( fp, "# (s) font style size x y align angle nobk " "r g b r g b\n" ); t = im->text; for ( i = 0; i < im->ntext; i++, t++ ) write_text( t, fp ); } return 0; } /*************************************** ***************************************/ int flimage_read_annotation( FL_IMAGE * im ) { FILE *fp; FLIMAGE_MARKER m; FLIMAGE_TEXT t; int c, done, v, nmarkers, i, ntext; char buf[ 1024 ]; if ( ! im || im->type == FL_IMAGE_NONE ) return -1; fp = im->fpin; /* Load the markers */ for ( done = 0; ! done; /* empty */ ) { while ( ( c = getc( fp ) ) != EOF && c != '#' ) /* empty */ ; done = c == EOF; if ( fgets( buf, sizeof buf - 1, fp ) ) buf[ sizeof buf - 1 ] = '\0'; else buf[ 0 ] = '\0'; if ( strstr( buf, "#marker" ) ) { sscanf( buf, "%*s %d %d", &nmarkers, &v ); if ( v > MARKERVERSION ) M_err( "ReadMarker", "wrong version" ); for ( i = 0; i < nmarkers; i++ ) { while ( skip_line( fp ) ) /* empty */ ; if ( read_marker( &m, fp ) >= 0 ) flimage_add_marker_struct( im, &m ); } } if ( strstr( buf, "#text" ) ) { sscanf( buf, "%*s %d %d", &ntext, &v ); if ( v > TEXTVERSION ) M_err( "ReadText", "wrong version" ); for ( i = 0; i < ntext; i++ ) { while ( skip_line( fp ) ) /* empty */ ; if ( read_text( &t, fp) >= 0 ) flimage_add_text_struct( im, &t ); } done = 1; } } return 0; } /* * Local variables: * tab-width: 4 * indent-tabs-mode: nil * End: */