#include #include #include #include #include "bitops.h" #include "aricoder.h" #include "ppnmtbl.h" #include "ppnmbitlen.h" #if defined BUILD_DLL // define BUILD_DLL from the compiler options if you want to compile a DLL! #define BUILD_LIB #endif #if defined BUILD_LIB // define BUILD_LIB from the compiler options if you want to compile a library! #include "packpnmlib.h" #endif #define INTERN static #define INIT_MODEL_S(a,b,c) new model_s( a, b, c, 255 ) #define INIT_MODEL_B(a,b) new model_b( a, b, 255 ) #define ABS(v1) ( (v1 < 0) ? -v1 : v1 ) #define ABSDIFF(v1,v2) ( (v1 > v2) ? (v1 - v2) : (v2 - v1) ) #define ROUND_F(v1) ( (v1 < 0) ? (int) (v1 - 0.5) : (int) (v1 + 0.5) ) #define CLAMPED(l,h,v) ( ( v < l ) ? l : ( v > h ) ? h : v ) #define SHORT_BE(d) ( ( ( (d)[0] & 0xFF ) << 8 ) | ( (d)[1] & 0xFF ) ) #define SHORT_LE(d) ( ( (d)[0] & 0xFF ) | ( ( (d)[1] & 0xFF ) << 8 ) ) #define INT_BE(d) ( ( SHORT_BE( d ) << 16 ) | SHORT_BE( d + 2 ) ) #define INT_LE(d) ( SHORT_LE( d ) | ( SHORT_LE( d + 2 ) << 16 ) ) #define MEM_ERRMSG "out of memory error" #define FRD_ERRMSG "could not read file / file not found" #define FWR_ERRMSG "could not write file / file write-protected" #define MSG_SIZE 128 #define BARLEN 36 /* ----------------------------------------------- pjg_model class ----------------------------------------------- */ class pjg_model { public: model_s* len; model_b* sgn; model_b* res; pjg_model( int pnmax, int nctx = 2 ) { int ml = BITLENB16N( pnmax ); len = INIT_MODEL_S( ml + 1, ml + 1, nctx ); sgn = INIT_MODEL_B( 16, 1 ); res = INIT_MODEL_B( ml + 1, 2 ); }; ~pjg_model() { delete( len ); delete( sgn ); delete( res ); }; }; /* ----------------------------------------------- cmp mask class ----------------------------------------------- */ class cmp_mask { public: unsigned int m; int p, s; cmp_mask( unsigned int mask = 0 ) { if ( mask == 0 ) m = p = s = 0; else { for ( p = 0; ( mask & 1 ) == 0; p++, mask >>= 1 ); m = mask; for ( s = 0; ( mask & 1 ) == 1; s++, mask >>= 1 ); if ( mask > 0 ) { m = 0; p = -1; s = -1; }; } }; ~cmp_mask() {}; }; /* ----------------------------------------------- function declarations: main interface ----------------------------------------------- */ #if !defined( BUILD_LIB ) INTERN void initialize_options( int argc, char** argv ); INTERN void process_ui( void ); INTERN inline const char* get_status( bool (*function)() ); INTERN void show_help( void ); #endif INTERN void process_file( void ); INTERN void execute( bool (*function)() ); /* ----------------------------------------------- function declarations: main functions ----------------------------------------------- */ #if !defined( BUILD_LIB ) INTERN bool check_file( void ); INTERN bool swap_streams( void ); INTERN bool compare_output( void ); #endif INTERN bool reset_buffers( void ); INTERN bool pack_ppn( void ); INTERN bool unpack_ppn( void ); /* ----------------------------------------------- function declarations: side functions ----------------------------------------------- */ INTERN bool ppn_encode_imgdata_rgba( aricoder* enc, iostream* stream ); INTERN bool ppn_decode_imgdata_rgba( aricoder* dec, iostream* stream ); INTERN bool ppn_encode_imgdata_mono( aricoder* enc, iostream* stream ); INTERN bool ppn_decode_imgdata_mono( aricoder* dec, iostream* stream ); INTERN bool ppn_encode_imgdata_palette( aricoder* enc, iostream* stream ); INTERN bool ppn_decode_imgdata_palette( aricoder* dec, iostream* stream ); INTERN inline void ppn_encode_pjg( aricoder* enc, pjg_model* mod, int** val, int** err, int ctx3 ); INTERN inline void ppn_decode_pjg( aricoder* dec, pjg_model* mod, int** val, int** err, int ctx3 ); INTERN inline int get_context_mono( int x, int y, int** val ); INTERN inline int plocoi( int a, int b, int c ); INTERN inline int pnm_read_line( iostream* stream, int** line ); INTERN inline int pnm_write_line( iostream* stream, int** line ); INTERN inline int hdr_decode_line_rle( iostream* stream, int** line ); INTERN inline int hdr_encode_line_rle( iostream* stream, int** line ); INTERN inline void rgb_process( unsigned int* rgb ); INTERN inline void rgb_unprocess( unsigned int* rgb ); INTERN inline void identify( const char* id, int* ft, int* st ); INTERN inline char* scan_header( iostream* stream ); INTERN inline char* scan_header_pnm( iostream* stream ); INTERN inline char* scan_header_bmp( iostream* stream ); INTERN inline char* scan_header_hdr( iostream* stream ); /* ----------------------------------------------- function declarations: miscelaneous helpers ----------------------------------------------- */ #if !defined( BUILD_LIB ) INTERN inline void progress_bar( int current, int last ); INTERN inline char* create_filename( const char* base, const char* extension ); INTERN inline char* unique_filename( const char* base, const char* extension ); INTERN inline void set_extension( const char* filename, const char* extension ); INTERN inline void add_underscore( char* filename ); #endif INTERN inline bool file_exists( const char* filename ); /* ----------------------------------------------- function declarations: developers functions ----------------------------------------------- */ // these are developers functions, they are not needed // in any way to compress or decompress files #if !defined(BUILD_LIB) && defined(DEV_BUILD) INTERN bool write_errfile( void ); INTERN bool dump_pgm( void ); INTERN bool dump_info( void ); #endif /* ----------------------------------------------- global variables: library only variables ----------------------------------------------- */ #if defined(BUILD_LIB) INTERN int lib_in_type = -1; INTERN int lib_out_type = -1; #endif /* ----------------------------------------------- global variables: data storage ----------------------------------------------- */ INTERN int imgwidth; // width of image INTERN int imgheight; // height of image INTERN int imgwidthv; // visible width of image INTERN int imgbpp; // bit per pixel INTERN int cmpc; // component count INTERN int endian_l; // endianness of image data INTERN unsigned int pnmax; // maximum pixel value (PPM/PGM only!) INTERN cmp_mask* cmask[5]; // masking info for components INTERN int bmpsize; // file size according to header /* ----------------------------------------------- global variables: info about files ----------------------------------------------- */ INTERN char* ppnfilename = NULL; // name of compressed file INTERN char* pnmfilename = NULL; // name of uncompressed file INTERN int ppnfilesize; // size of compressed file INTERN int pnmfilesize; // size of uncompressed file INTERN int filetype; // type of current file INTERN int subtype; // sub type of file INTERN iostream* str_in = NULL; // input stream INTERN iostream* str_out = NULL; // output stream #if !defined( BUILD_LIB ) INTERN iostream* str_str = NULL; // storage stream INTERN char** filelist = NULL; // list of files to process INTERN int file_cnt = 0; // count of files in list INTERN int file_no = 0; // number of current file INTERN char** err_list = NULL; // list of error messages INTERN int* err_tp = NULL; // list of error types #endif /* ----------------------------------------------- global variables: messages ----------------------------------------------- */ INTERN char errormessage [ 128 ]; INTERN bool (*errorfunction)(); INTERN int errorlevel; // meaning of errorlevel: // -1 -> wrong input // 0 -> no error // 1 -> warning // 2 -> fatal error /* ----------------------------------------------- global variables: settings ----------------------------------------------- */ INTERN bool use_rle = 0; // use RLE compression for HDR output #if !defined( BUILD_LIB ) INTERN int verbosity = -1; // level of verbosity INTERN bool overwrite = false; // overwrite files yes / no INTERN bool wait_exit = true; // pause after finished yes / no INTERN int verify_lv = 0; // verification level ( none (0), simple (1), detailed output (2) ) INTERN int err_tol = 1; // error threshold ( proceed on warnings yes (2) / no (1) ) INTERN bool developer = false; // allow developers functions yes/no INTERN int action = A_COMPRESS; // what to do with files INTERN FILE* msgout = stdout; // stream for output of messages INTERN bool pipe_on = false; // use stdin/stdout instead of filelist #else INTERN int err_tol = 1; // error threshold ( proceed on warnings yes (2) / no (1) ) INTERN int action = A_COMPRESS; // what to do with files #endif /* ----------------------------------------------- global variables: info about program ----------------------------------------------- */ INTERN const unsigned char appversion = 16; INTERN const char* subversion = "c"; INTERN const char* apptitle = "packPNM"; INTERN const char* appname = "packPNM"; INTERN const char* versiondate = "01/15/2014"; INTERN const char* author = "Matthias Stirner"; #if !defined(BUILD_LIB) INTERN const char* website = "http://www.elektronik.htw-aalen.de/packjpg/"; INTERN const char* email = "packjpg (at) htw-aalen.de"; INTERN const char* copyright = "2006-2014 HTW Aalen University & Matthias Stirner"; INTERN const char* ppn_ext = "ppn"; #endif /* ----------------------------------------------- main-function ----------------------------------------------- */ #if !defined(BUILD_LIB) int main( int argc, char** argv ) { sprintf( errormessage, "no errormessage specified" ); clock_t begin, end; int error_cnt = 0; int warn_cnt = 0; double acc_pnmsize = 0; double acc_ppnsize = 0; int kbps; double cr; double total; errorlevel = 0; // read options from command line initialize_options( argc, argv ); // write program info to screen fprintf( msgout, "\n--> %s v%i.%i%s (%s) by %s <--\n", apptitle, appversion / 10, appversion % 10, subversion, versiondate, author ); fprintf( msgout, "Copyright %s\nAll rights reserved\n\n", copyright ); // check if user input is wrong, show help screen if it is if ( ( file_cnt == 0 ) || ( ( !developer ) && ( (action != A_COMPRESS) || (verify_lv > 1) ) ) ) { show_help(); return -1; } // (re)set program has to be done first reset_buffers(); // process file(s) - this is the main function routine begin = clock(); for ( file_no = 0; file_no < file_cnt; file_no++ ) { // process current file process_ui(); // store error message and type if any if ( errorlevel > 0 ) { err_list[ file_no ] = (char*) calloc( MSG_SIZE, sizeof( char ) ); err_tp[ file_no ] = errorlevel; if ( err_list[ file_no ] != NULL ) strcpy( err_list[ file_no ], errormessage ); } // count errors / warnings / file sizes if ( errorlevel >= err_tol ) error_cnt++; else { if ( errorlevel == 1 ) warn_cnt++; acc_pnmsize += pnmfilesize; acc_ppnsize += ppnfilesize; } } end = clock(); // errors summary: only needed for -v2 or progress bar if ( ( verbosity == -1 ) || ( verbosity == 2 ) ) { // print summary of errors to screen if ( error_cnt > 0 ) { fprintf( stderr, "\n\nfiles with errors:\n" ); fprintf( stderr, "------------------\n" ); for ( file_no = 0; file_no < file_cnt; file_no++ ) { if ( err_tp[ file_no ] >= err_tol ) { fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] ); } } } // print summary of warnings to screen if ( warn_cnt > 0 ) { fprintf( stderr, "\n\nfiles with warnings:\n" ); fprintf( stderr, "------------------\n" ); for ( file_no = 0; file_no < file_cnt; file_no++ ) { if ( err_tp[ file_no ] == 1 ) { fprintf( stderr, "%s (%s)\n", filelist[ file_no ], err_list[ file_no ] ); } } } } // show statistics fprintf( msgout, "\n\n-> %i file(s) processed, %i error(s), %i warning(s)\n", file_cnt, error_cnt, warn_cnt ); if ( ( file_cnt > error_cnt ) && ( verbosity != 0 ) && ( action == A_COMPRESS ) ) { acc_pnmsize /= 1024.0; acc_ppnsize /= 1024.0; total = (double) ( end - begin ) / CLOCKS_PER_SEC; kbps = ( total > 0 ) ? ( acc_pnmsize / total ) : acc_pnmsize; cr = ( acc_pnmsize > 0 ) ? ( 100.0 * acc_ppnsize / acc_pnmsize ) : 0; fprintf( msgout, " -------------------------------- \n" ); if ( total >= 0 ) { fprintf( msgout, " total time : %8.2f sec\n", total ); fprintf( msgout, " avrg. kbyte per s : %8i kbps\n", kbps ); } else { fprintf( msgout, " total time : %8s sec\n", "N/A" ); fprintf( msgout, " avrg. kbyte per s : %8s kbps\n", "N/A" ); } fprintf( msgout, " avrg. comp. ratio : %8.2f %%\n", cr ); fprintf( msgout, " -------------------------------- \n" ); } // pause before exit if ( wait_exit && ( msgout != stderr ) ) { fprintf( msgout, "\n\n< press ENTER >\n" ); fgetc( stdin ); } return 0; } #endif /* ----------------------- Begin of library only functions -------------------------- */ /* ----------------------------------------------- DLL export converter function ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT bool ppnlib_convert_stream2stream( char* msg ) { // process in main function return ppnlib_convert_stream2mem( NULL, NULL, msg ); } #endif /* ----------------------------------------------- DLL export converter function ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT bool ppnlib_convert_file2file( char* in, char* out, char* msg ) { // init streams ppnlib_init_streams( (void*) in, 0, 0, (void*) out, 0 ); // process in main function return ppnlib_convert_stream2mem( NULL, NULL, msg ); } #endif /* ----------------------------------------------- DLL export converter function ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT bool ppnlib_convert_stream2mem( unsigned char** out_file, unsigned int* out_size, char* msg ) { clock_t begin, end; int total; float cr; // (re)set buffers reset_buffers(); action = A_COMPRESS; // main compression / decompression routines begin = clock(); // process one file process_file(); // fetch pointer and size of output (only for memory output) if ( ( errorlevel < err_tol ) && ( lib_out_type == 1 ) && ( out_file != NULL ) && ( out_size != NULL ) ) { *out_size = str_out->getsize(); *out_file = str_out->getptr(); } // close iostreams if ( str_in != NULL ) delete( str_in ); str_in = NULL; if ( str_out != NULL ) delete( str_out ); str_out = NULL; end = clock(); // copy errormessage / remove files if error (and output is file) if ( errorlevel >= err_tol ) { if ( lib_out_type == 0 ) { if ( filetype == F_PNM ) { if ( file_exists( ppnfilename ) ) remove( ppnfilename ); } else if ( filetype == F_PPN ) { if ( file_exists( pnmfilename ) ) remove( pnmfilename ); } } if ( msg != NULL ) strcpy( msg, errormessage ); return false; } // get compression info total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC ); cr = ( pnmfilesize > 0 ) ? ( 100.0 * ppnfilesize / pnmfilesize ) : 0; // write success message else if ( msg != NULL ) { switch( filetype ) { case F_PNM: sprintf( msg, "Compressed to %s (%.2f%%) in %ims", ppnfilename, cr, ( total >= 0 ) ? total : -1 ); break; case F_PPN: sprintf( msg, "Decompressed to %s (%.2f%%) in %ims", pnmfilename, cr, ( total >= 0 ) ? total : -1 ); break; } } return true; } #endif /* ----------------------------------------------- DLL export init input (file/mem) ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT void ppnlib_init_streams( void* in_src, int in_type, int in_size, void* out_dest, int out_type ) { /* a short reminder about input/output stream types: if input is file ---------------- in_scr -> name of input file in_type -> 0 in_size -> ignore if input is memory ------------------ in_scr -> array containg data in_type -> 1 in_size -> size of data array if input is *FILE (f.e. stdin) ------------------------------ in_src -> stream pointer in_type -> 2 in_size -> ignore vice versa for output streams! */ char buffer[ 2 ]; // (re)set errorlevel errorfunction = NULL; errorlevel = 0; pnmfilesize = 0; ppnfilesize = 0; // open input stream, check for errors str_in = new iostream( in_src, in_type, in_size, 0 ); if ( str_in->chkerr() ) { sprintf( errormessage, "error opening input stream" ); errorlevel = 2; return; } // open output stream, check for errors str_out = new iostream( out_dest, out_type, 0, 1 ); if ( str_out->chkerr() ) { sprintf( errormessage, "error opening output stream" ); errorlevel = 2; return; } // free memory from filenames if needed if ( pnmfilename != NULL ) free( pnmfilename ); pnmfilename = NULL; if ( ppnfilename != NULL ) free( ppnfilename ); ppnfilename = NULL; // check input stream str_in->read( buffer, 1, 2 ); // rewind (need to start from the beginning) if ( str_in->rewind() != 0 ) { sprintf( errormessage, FRD_ERRMSG ); errorlevel = 2; return; } // check file id, determine filetype identify( buffer, &filetype, &subtype ); if ( filetype == F_UNK ) { // file is of unknown type sprintf( errormessage, "filetype of input stream is unknown" ); errorlevel = 2; return; } else if ( filetype == F_PNM ) { // file is PBM/PGM/PPM // copy filenames pnmfilename = (char*) calloc( ( in_type == 0 ) ? strlen( (char*) in_src ) + 1 : 32, sizeof( char ) ); ppnfilename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) ); strcpy( pnmfilename, ( in_type == 0 ) ? (char*) in_src : "PNM in memory" ); strcpy( ppnfilename, ( out_type == 0 ) ? (char*) out_dest : "PPN in memory" ); } else if ( filetype == F_PPN ) { // file is PPN // copy filenames ppnfilename = (char*) calloc( ( in_type == 0 ) ? strlen( (char*) in_src ) + 1 : 32, sizeof( char ) ); pnmfilename = (char*) calloc( ( out_type == 0 ) ? strlen( (char*) out_dest ) + 1 : 32, sizeof( char ) ); strcpy( ppnfilename, ( in_type == 0 ) ? (char*) in_src : "PPN in memory" ); strcpy( pnmfilename, ( out_type == 0 ) ? (char*) out_dest : "PNM in memory" ); } // store types of in-/output lib_in_type = in_type; lib_out_type = out_type; } #endif /* ----------------------------------------------- DLL export version information ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT const char* ppnlib_version_info( void ) { static char v_info[ 256 ]; // copy version info to string sprintf( v_info, "--> %s library v%i.%i%s (%s) by %s <--", apptitle, appversion / 10, appversion % 10, subversion, versiondate, author ); return (const char*) v_info; } #endif /* ----------------------------------------------- DLL export version information ----------------------------------------------- */ #if defined(BUILD_LIB) EXPORT const char* ppnlib_short_name( void ) { static char v_name[ 256 ]; // copy version info to string sprintf( v_name, "%s v%i.%i%s", apptitle, appversion / 10, appversion % 10, subversion ); return (const char*) v_name; } #endif /* ----------------------- End of libary only functions -------------------------- */ /* ----------------------- Begin of main interface functions -------------------------- */ /* ----------------------------------------------- reads in commandline arguments ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN void initialize_options( int argc, char** argv ) { int tmp_val; char** tmp_flp; int i; // get memory for filelist & preset with NULL filelist = (char**) calloc( argc, sizeof( char* ) ); for ( i = 0; i < argc; i++ ) filelist[ i ] = NULL; // preset temporary filelist pointer tmp_flp = filelist; // read in arguments while ( --argc > 0 ) { argv++; // switches begin with '-' if ( strcmp((*argv), "-p" ) == 0 ) { err_tol = 2; } else if ( strcmp((*argv), "-ver" ) == 0 ) { verify_lv = ( verify_lv < 1 ) ? 1 : verify_lv; } else if ( sscanf( (*argv), "-v%i", &tmp_val ) == 1 ){ verbosity = tmp_val; verbosity = ( verbosity < 0 ) ? 0 : verbosity; verbosity = ( verbosity > 2 ) ? 2 : verbosity; } else if ( strcmp((*argv), "-vp" ) == 0 ) { verbosity = -1; } else if ( strcmp((*argv), "-np" ) == 0 ) { wait_exit = false; } else if ( strcmp((*argv), "-o" ) == 0 ) { overwrite = true; } else if ( strcmp((*argv), "-rle" ) == 0 ) { use_rle = true; } #if defined(DEV_BUILD) else if ( strcmp((*argv), "-dev") == 0 ) { developer = true; } else if ( strcmp((*argv), "-test") == 0 ) { verify_lv = 2; } else if ( strcmp((*argv), "-dump") == 0 ) { action = A_PGM_DUMP; } else if ( strcmp((*argv), "-info") == 0 ) { action = A_NFO_DUMP; } else if ( ( strcmp((*argv), "-ppn") == 0 ) || ( strcmp((*argv), "-pnm") == 0 ) || ( strcmp((*argv), "-comp") == 0) ) { action = A_COMPRESS; } #endif else if ( strcmp((*argv), "-") == 0 ) { // switch standard message out stream msgout = stderr; // use "-" as placeholder for stdin *(tmp_flp++) = (char*) "-"; } else { // if argument is not switch, it's a filename *(tmp_flp++) = *argv; } } // count number of files (or filenames) in filelist for ( file_cnt = 0; filelist[ file_cnt ] != NULL; file_cnt++ ); // alloc arrays for error messages and types storage err_list = (char**) calloc( file_cnt, sizeof( char* ) ); err_tp = (int*) calloc( file_cnt, sizeof( int ) ); } #endif /* ----------------------------------------------- UI for processing one file ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN void process_ui( void ) { clock_t begin, end; const char* actionmsg = NULL; const char* errtypemsg = NULL; int total, bpms; float cr; errorfunction = NULL; errorlevel = 0; pnmfilesize = 0; ppnfilesize = 0; #if !defined(DEV_BUILD) action = A_COMPRESS; #endif // compare file name, set pipe if needed if ( ( strcmp( filelist[ file_no ], "-" ) == 0 ) && ( action == A_COMPRESS ) ) { pipe_on = true; filelist[ file_no ] = (char*) "STDIN"; } else { pipe_on = false; } if ( verbosity >= 0 ) { // standard UI fprintf( msgout, "\nProcessing file %i of %i \"%s\" -> ", file_no + 1, file_cnt, filelist[ file_no ] ); if ( verbosity > 1 ) fprintf( msgout, "\n----------------------------------------" ); // check input file and determine filetype execute( check_file ); // get specific action message switch ( action ) { case A_COMPRESS: ( filetype == F_PNM ) ? actionmsg = "Compressing" : actionmsg = "Decompressing"; break; case A_PGM_DUMP: actionmsg = "Dumping"; break; } if ( verbosity < 2 ) fprintf( msgout, "%s -> ", actionmsg ); } else { // progress bar UI // update progress message fprintf( msgout, "Processing file %2i of %2i ", file_no + 1, file_cnt ); progress_bar( file_no, file_cnt ); fprintf( msgout, "\r" ); execute( check_file ); } fflush( msgout ); // main function routine begin = clock(); // streams are initiated, start processing file process_file(); // close iostreams if ( str_in != NULL ) delete( str_in ); str_in = NULL; if ( str_out != NULL ) delete( str_out ); str_out = NULL; if ( str_str != NULL ) delete( str_str ); str_str = NULL; // delete if broken or if output not needed if ( ( !pipe_on ) && ( ( errorlevel >= err_tol ) || ( action != A_COMPRESS ) ) ) { if ( filetype == F_PNM ) { if ( file_exists( ppnfilename ) ) remove( ppnfilename ); } else if ( filetype == F_PPN ) { if ( file_exists( pnmfilename ) ) remove( pnmfilename ); } } end = clock(); // speed and compression ratio calculation total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC ); bpms = ( total > 0 ) ? ( pnmfilesize / total ) : pnmfilesize; cr = ( pnmfilesize > 0 ) ? ( 100.0 * ppnfilesize / pnmfilesize ) : 0; if ( verbosity >= 0 ) { // standard UI if ( verbosity > 1 ) fprintf( msgout, "\n----------------------------------------" ); // display success/failure message switch ( verbosity ) { case 0: if ( errorlevel < err_tol ) { if ( action == A_COMPRESS ) fprintf( msgout, "%.2f%%", cr ); else fprintf( msgout, "DONE" ); } else fprintf( msgout, "ERROR" ); if ( errorlevel > 0 ) fprintf( msgout, "\n" ); break; case 1: fprintf( msgout, "%s\n", ( errorlevel < err_tol ) ? "DONE" : "ERROR" ); break; case 2: if ( errorlevel < err_tol ) fprintf( msgout, "\n-> %s OK\n", actionmsg ); else fprintf( msgout, "\n-> %s ERROR\n", actionmsg ); break; } // set type of error message switch ( errorlevel ) { case 0: errtypemsg = "none"; break; case 1: ( err_tol > 1 ) ? errtypemsg = "warning (ignored)" : errtypemsg = "warning (skipped file)"; break; case 2: errtypemsg = "fatal error"; break; } // error/ warning message if ( errorlevel > 0 ) { fprintf( msgout, " %s -> %s:\n", get_status( errorfunction ), errtypemsg ); fprintf( msgout, " %s\n", errormessage ); } if ( (verbosity > 0) && (errorlevel < err_tol) && (action == A_COMPRESS) ) { if ( total >= 0 ) { fprintf( msgout, " time taken : %7i msec\n", total ); fprintf( msgout, " byte per ms : %7i byte\n", bpms ); } else { fprintf( msgout, " time taken : %7s msec\n", "N/A" ); fprintf( msgout, " byte per ms : %7s byte\n", "N/A" ); } fprintf( msgout, " comp. ratio : %7.2f %%\n", cr ); } if ( ( verbosity > 1 ) && ( action == A_COMPRESS ) ) fprintf( msgout, "\n" ); } else { // progress bar UI // if this is the last file, update progress bar one last time if ( file_no + 1 == file_cnt ) { // update progress message fprintf( msgout, "Processed %2i of %2i files ", file_no + 1, file_cnt ); progress_bar( 1, 1 ); fprintf( msgout, "\r" ); } } } #endif /* ----------------------------------------------- gets statusmessage for function ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline const char* get_status( bool (*function)() ) { if ( function == NULL ) { return "unknown action"; } else if ( function == *check_file ) { return "Determining filetype"; } else if ( function == *pack_ppn ) { return "Converting PNM/BMP/HDR to PPN"; } else if ( function == *unpack_ppn ) { return "Converting PPN to PNM/BMP/HDR"; } else if ( function == *swap_streams ) { return "Swapping input/output streams"; } else if ( function == *compare_output ) { return "Verifying output stream"; } else if ( function == *reset_buffers ) { return "Resetting program"; } #if defined(DEV_BUILD) else if ( function == *dump_pgm ) { return "Dumping RAW PGM"; } else if ( function == *dump_pgm ) { return "Dumping NFO file"; } #endif else { return "Function description missing!"; } } #endif /* ----------------------------------------------- shows help in case of wrong input ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN void show_help( void ) { fprintf( msgout, "\n" ); fprintf( msgout, "Website: %s\n", website ); fprintf( msgout, "Email : %s\n", email ); fprintf( msgout, "\n" ); fprintf( msgout, "Usage: packpnm [switches] [filename(s)]" ); fprintf( msgout, "\n" ); fprintf( msgout, "\n" ); fprintf( msgout, " [-ver] verify files after processing\n" ); fprintf( msgout, " [-v?] set level of verbosity (max: 2) (def: 0)\n" ); fprintf( msgout, " [-o] overwrite existing files\n" ); fprintf( msgout, " [-p] proceed on warnings\n" ); fprintf( msgout, " [-rle] use RLE for writing HDR output\n" ); #if defined(DEV_BUILD) if ( developer ) { fprintf( msgout, "\n" ); fprintf( msgout, " [-dump] dump raw PGM file\n" ); fprintf( msgout, " [-info] dump info file\n" ); fprintf( msgout, " [-test] test algorithms, alert if error\n" ); } #endif fprintf( msgout, "\n" ); fprintf( msgout, "Examples: \"packpgm baboon.pgm\"\n" ); fprintf( msgout, " \"packpgm *.ppm\"\n" ); fprintf( msgout, " \"packpgm kodim??.ppn\"\n" ); } #endif /* ----------------------------------------------- processes one file ----------------------------------------------- */ INTERN void process_file( void ) { if ( filetype == F_PNM ) { switch ( action ) { case A_COMPRESS: execute( pack_ppn ); #if !defined(BUILD_LIB) if ( verify_lv > 0 ) { // verifcation execute( reset_buffers ); execute( swap_streams ); execute( unpack_ppn ); execute( compare_output ); } #endif break; #if !defined(BUILD_LIB) && defined(DEV_BUILD) case A_PGM_DUMP: execute( dump_pgm ); break; case A_NFO_DUMP: execute( dump_info ); break; #else default: break; #endif } } else if ( filetype == F_PPN ) { switch ( action ) { case A_COMPRESS: execute( unpack_ppn ); #if !defined(BUILD_LIB) // this does not work yet! // and it's not even needed if ( verify_lv > 0 ) { // verify execute( reset_buffers ); execute( swap_streams ); execute( pack_ppn ); execute( compare_output ); } #endif break; #if !defined(BUILD_LIB) && defined(DEV_BUILD) case A_NFO_DUMP: execute( dump_info ); break; #else default: break; #endif } } #if !defined(BUILD_LIB) && defined(DEV_BUILD) // write error file if verify lv > 1 if ( ( verify_lv > 1 ) && ( errorlevel >= err_tol ) ) write_errfile(); #endif // reset buffers reset_buffers(); } /* ----------------------------------------------- main-function execution routine ----------------------------------------------- */ INTERN void execute( bool (*function)() ) { if ( errorlevel < err_tol ) { #if !defined BUILD_LIB clock_t begin, end; bool success; int total; // write statusmessage if ( verbosity == 2 ) { fprintf( msgout, "\n%s ", get_status( function ) ); for ( int i = strlen( get_status( function ) ); i <= 30; i++ ) fprintf( msgout, " " ); } // set starttime begin = clock(); // call function success = ( *function )(); // set endtime end = clock(); if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) ) errorfunction = function; // write time or failure notice if ( success ) { total = (int) ( (double) (( end - begin ) * 1000) / CLOCKS_PER_SEC ); if ( verbosity == 2 ) fprintf( msgout, "%6ims", ( total >= 0 ) ? total : -1 ); } else { errorfunction = function; if ( verbosity == 2 ) fprintf( msgout, "%8s", "ERROR" ); } #else // call function ( *function )(); // store errorfunction if needed if ( ( errorlevel > 0 ) && ( errorfunction == NULL ) ) errorfunction = function; #endif } } /* ----------------------- End of main interface functions -------------------------- */ /* ----------------------- Begin of main functions -------------------------- */ /* ----------------------------------------------- check file and determine filetype ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN bool check_file( void ) { char fileid[ 2 ] = { 0, 0 }; const char* filename = filelist[ file_no ]; const char* pnm_ext; // open input stream, check for errors str_in = new iostream( (void*) filename, ( !pipe_on ) ? 0 : 2, 0, 0 ); if ( str_in->chkerr() ) { sprintf( errormessage, FRD_ERRMSG ); errorlevel = 2; return false; } // free memory from filenames if needed if ( pnmfilename != NULL ) free( pnmfilename ); pnmfilename = NULL; if ( ppnfilename != NULL ) free( ppnfilename ); ppnfilename = NULL; // immediately return error if 2 bytes can't be read if ( str_in->read( fileid, 1, 2 ) != 2 ) { filetype = F_UNK; sprintf( errormessage, "file doesn't contain enough data" ); errorlevel = 2; return false; } // rewind (need to start from the beginning) if ( str_in->rewind() != 0 ) { sprintf( errormessage, FRD_ERRMSG ); errorlevel = 2; return false; } // check file id, determine filetype identify( fileid, &filetype, &subtype ); if ( filetype == F_UNK ) { // file is neither sprintf( errormessage, "format of file \"%s\" is not supported", filelist[ file_no ] ); errorlevel = 2; return false; } else if ( filetype == F_PNM ) { // file is PBM / PGM / PPM // create filenames if ( !pipe_on ) { pnmfilename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) ); strcpy( pnmfilename, filename ); ppnfilename = ( overwrite ) ? create_filename( filename, (char*) ppn_ext ) : unique_filename( filename, (char*) ppn_ext ); } else { pnmfilename = create_filename( "STDIN", NULL ); ppnfilename = create_filename( "STDOUT", NULL ); } // open output stream, check for errors str_out = new iostream( (void*) ppnfilename, ( !pipe_on ) ? 0 : 2, 0, 1 ); if ( str_out->chkerr() ) { sprintf( errormessage, FWR_ERRMSG ); errorlevel = 2; return false; } } else if ( filetype == F_PPN ) { // file is PPN // create filenames if ( !pipe_on ) { switch ( subtype ) { case S_PBM: pnm_ext = "pbm"; break; case S_PGM: pnm_ext = "pgm"; break; case S_PPM: pnm_ext = "ppm"; break; case S_BMP: pnm_ext = "bmp"; break; case S_HDR: pnm_ext = "hdr"; break; default: pnm_ext = "unk"; break; } ppnfilename = (char*) calloc( strlen( filename ) + 1, sizeof( char ) ); strcpy( ppnfilename, filename ); pnmfilename = ( overwrite ) ? create_filename( filename, (char*) pnm_ext ) : unique_filename( filename, (char*) pnm_ext ); } else { pnmfilename = create_filename( "STDOUT", NULL ); ppnfilename = create_filename( "STDIN", NULL ); } // open output stream, check for errors str_out = new iostream( (void*) pnmfilename, ( !pipe_on ) ? 0 : 2, 0, 1 ); if ( str_out->chkerr() ) { sprintf( errormessage, FWR_ERRMSG ); errorlevel = 2; return false; } } return true; } #endif /* ----------------------------------------------- swap streams / init verification ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN bool swap_streams( void ) { // store input stream str_str = str_in; str_str->rewind(); // replace input stream by output stream / switch mode for reading str_in = str_out; str_in->switch_mode(); // open new stream for output / check for errors str_out = new iostream( NULL, 1, 0, 1 ); if ( str_out->chkerr() ) { sprintf( errormessage, "error opening comparison stream" ); errorlevel = 2; return false; } return true; } #endif /* ----------------------------------------------- comparison between input & output ----------------------------------------------- */ #if !defined( BUILD_LIB ) INTERN bool compare_output( void ) { unsigned char* buff_ori; unsigned char* buff_cmp; int bsize = 1024; int dsize; int i, b; // reset error level errorlevel = 0; // init buffer arrays buff_ori = ( unsigned char* ) calloc( bsize, sizeof( char ) ); buff_cmp = ( unsigned char* ) calloc( bsize, sizeof( char ) ); if ( ( buff_ori == NULL ) || ( buff_cmp == NULL ) ) { if ( buff_ori != NULL ) free( buff_ori ); if ( buff_cmp != NULL ) free( buff_cmp ); sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // switch output stream mode / check for stream errors str_out->switch_mode(); while ( true ) { if ( str_out->chkerr() ) sprintf( errormessage, "error in comparison stream" ); else if ( str_in->chkerr() ) sprintf( errormessage, "error in output stream" ); else if ( str_str->chkerr() ) sprintf( errormessage, "error in input stream" ); else break; errorlevel = 2; return false; } // compare sizes dsize = str_str->getsize(); if ( str_out->getsize() != dsize ) { if ( str_out->getsize() < dsize ) dsize = str_out->getsize(); sprintf( errormessage, "file sizes do not match" ); errorlevel = 1; } // compare files byte by byte for ( i = 0; i < dsize; i++ ) { b = i % bsize; if ( b == 0 ) { str_str->read( buff_ori, 1, bsize ); str_out->read( buff_cmp, 1, bsize ); } if ( buff_ori[ b ] != buff_cmp[ b ] ) { sprintf( errormessage, "difference found at 0x%X", i ); errorlevel = 2; return false; } } return true; } #endif /* ----------------------------------------------- set each variable to its initial value ----------------------------------------------- */ INTERN bool reset_buffers( void ) { imgwidth = 0; // width of image imgheight = 0; // height of image imgwidthv = 0; // visible width of image imgbpp = 0; // bit per pixel cmpc = 0; // component count pnmax = 0; // maximum pixel value endian_l = 0; // endianness bmpsize = 0; // filesize according to header for ( int i = 0; i < 5; i++ ) { if ( cmask[i] != NULL ) delete( cmask[i] ); cmask[i] = NULL; } return true; } /* ----------------------------------------------- packs all parts to compressed pgs ----------------------------------------------- */ INTERN bool pack_ppn( void ) { char* imghdr = NULL; bool error = false; aricoder* encoder; // parse PNM file header imghdr = scan_header( str_in ); if ( imghdr == NULL ) { errorlevel = 2; return false; } // write PPN file header imghdr[0] = 'S'; str_out->write( imghdr, 1, ( subtype != S_BMP ) ? strlen( imghdr ) : INT_LE( imghdr + 0x0A ) ); str_out->write( ( void* ) &appversion, 1, 1 ); // init arithmetic compression encoder = new aricoder( str_out, 1 ); // arithmetic encode image data (select method) switch ( imgbpp ) { case 1: if ( !(ppn_encode_imgdata_mono( encoder, str_in )) ) error = true; break; case 4: case 8: if ( subtype == S_BMP ) { if ( !(ppn_encode_imgdata_palette( encoder, str_in )) ) error = true; } else if ( !(ppn_encode_imgdata_rgba( encoder, str_in )) ) error = true; break; case 16: case 24: case 32: case 48: if ( !(ppn_encode_imgdata_rgba( encoder, str_in )) ) error = true; break; default: sprintf( errormessage, "%ibpp is not supported", imgbpp ); error = true; break; } // finalize arithmetic compression delete( encoder ); // error flag set? if ( error ) return false; // read BMP junk data if ( subtype == S_BMP ) for ( char bt = 0; str_in->getpos() < bmpsize; ) { if ( str_in->read( &bt, 1, 1 ) != 1 ) { sprintf( errormessage, "incorrect BMP size in header" ); errorlevel = 1; break; } else if ( bt != 0 ) { sprintf( errormessage, "junk data after BMP end-of-image" ); errorlevel = 1; break; } } // check for junk data if ( str_in->getpos() != str_in->getsize() ) { sprintf( errormessage, "junk data after end-of-image" ); errorlevel = 1; } // errormessage if write error if ( str_out->chkerr() ) { sprintf( errormessage, "write error, possibly drive is full" ); errorlevel = 2; return false; } // get filesizes pnmfilesize = str_in->getsize(); ppnfilesize = str_out->getsize(); return true; } /* ----------------------------------------------- unpacks compressed pgs ----------------------------------------------- */ INTERN bool unpack_ppn( void ) { char* imghdr = NULL; bool error = false; unsigned char ver; aricoder* decoder; // parse PPN header imghdr = scan_header( str_in ); if ( imghdr == NULL ) { errorlevel = 2; return false; } // check packPNM version str_in->read( (void*) &ver, 1, 1 ); if ( ver == 14 ) { endian_l = E_LITTLE; // bad (mixed endianness!) (!!!) ver = 16; // compatibility hack for v1.4 } if ( ver != appversion ) { sprintf( errormessage, "incompatible file, use %s v%i.%i", appname, ver / 10, ver % 10 ); errorlevel = 2; return false; } // write PNM file header imghdr[0] = ( subtype == S_BMP ) ? 'B' : ( subtype == S_HDR ) ? '#' : 'P'; str_out->write( imghdr, 1, ( subtype != S_BMP ) ? strlen( imghdr ) : INT_LE( imghdr + 0x0A ) ); // init arithmetic compression decoder = new aricoder( str_in, 0 ); // arithmetic decode image data (select method) switch ( imgbpp ) { case 1: if ( !(ppn_decode_imgdata_mono( decoder, str_out )) ) error = true; break; case 4: case 8: if ( subtype == S_BMP ) { if ( !(ppn_decode_imgdata_palette( decoder, str_out )) ) error = true; } else if ( !(ppn_decode_imgdata_rgba( decoder, str_out )) ) error = true; break; case 16: case 24: case 32: case 48: if ( !(ppn_decode_imgdata_rgba( decoder, str_out )) ) error = true; break; default: error = true; } // finalize arithmetic decompression delete( decoder ); // error flag set? if ( error ) return false; // write BMP junk data if ( subtype == S_BMP ) for ( char bt = 0; str_out->getpos() < bmpsize; ) str_out->write( &bt, 1, 1 ); // errormessage if write error if ( str_out->chkerr() ) { sprintf( errormessage, "write error, possibly drive is full" ); errorlevel = 2; return false; } // get filesizes ppnfilesize = str_in->getsize(); pnmfilesize = str_out->getsize(); return true; } /* ----------------------- End of main functions -------------------------- */ /* ----------------------- Begin of side functions -------------------------- */ /* ----------------------------------------------- PPN PJG type RGBA/E encoding ----------------------------------------------- */ INTERN bool ppn_encode_imgdata_rgba( aricoder* enc, iostream* stream ) { pjg_model* mod[4]; int* storage; // storage array int* val[4][2] = { { NULL } }; int* err[4][2] = { { NULL } }; int* dta[4] = { NULL }; int c, x, y; // init models for ( c = 0; c < cmpc; c++ ) mod[c] = new pjg_model( ( pnmax == 0 ) ? cmask[c]->m : pnmax, ( ( c < 3 ) && ( cmpc >= 3 ) ) ? 3 : 2 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 2 ) * 4 * cmpc, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers for ( c = 0; c < cmpc; c++ ) { val[c][0] = storage + 2 + ( ( imgwidth + 2 ) * ( 0 + (4*c) + ( (y+0) % 2 ) ) ); val[c][1] = storage + 2 + ( ( imgwidth + 2 ) * ( 0 + (4*c) + ( (y+1) % 2 ) ) ); err[c][0] = storage + 2 + ( ( imgwidth + 2 ) * ( 2 + (4*c) + ( (y+0) % 2 ) ) ); err[c][1] = storage + 2 + ( ( imgwidth + 2 ) * ( 2 + (4*c) + ( (y+1) % 2 ) ) ); dta[c] = val[c][0]; } // read line errorlevel = pnm_read_line( stream, dta ); if ( errorlevel == 1 ) sprintf( errormessage, ( subtype == S_HDR ) ? "bitwise reconstruction of HDR RLE not guaranteed" : "excess data or bad data found" ); else if ( errorlevel == 2 ) { sprintf( errormessage, "unexpected end of file" ); free( storage ); for ( c = 0; c < cmpc; c++ ) delete( mod[c] ); return false; } // encode pixel values for ( x = 0; x < imgwidth; x++ ) { if ( cmpc == 1 ) { ppn_encode_pjg( enc, mod[0], val[0], err[0], -1 ); } else { // cmpc >= 3 if ( cmpc > 3 ) ppn_encode_pjg( enc, mod[3], val[3], err[3], -1 ); ppn_encode_pjg( enc, mod[0], val[0], err[0], BITLENB16N(err[2][0][-1]) ); ppn_encode_pjg( enc, mod[2], val[2], err[2], BITLENB16N(err[0][0][0]) ); ppn_encode_pjg( enc, mod[1], val[1], err[1], ( BITLENB16N(err[0][0][0]) + BITLENB16N(err[2][0][0]) + 1 ) / 2 ); } // advance values for ( c = 0; c < cmpc; c++ ) { val[c][0]++; val[c][1]++; err[c][0]++; err[c][1]++; } } } // free storage / clear models free( storage ); for ( c = 0; c < cmpc; c++ ) delete( mod[c] ); return true; } /* ----------------------------------------------- PPN PJG type RGBA/E decoding ----------------------------------------------- */ INTERN bool ppn_decode_imgdata_rgba( aricoder* dec, iostream* stream ) { pjg_model* mod[4]; int* storage; // storage array int* val[4][2] = { { NULL } }; int* err[4][2] = { { NULL } }; int* dta[4] = { NULL }; int c, x, y; // init models for ( c = 0; c < cmpc; c++ ) mod[c] = new pjg_model( ( pnmax == 0 ) ? cmask[c]->m : pnmax, ( ( c < 3 ) && ( cmpc >= 3 ) ) ? 3 : 2 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 2 ) * 4 * cmpc, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers for ( c = 0; c < cmpc; c++ ) { val[c][0] = storage + 2 + ( ( imgwidth + 2 ) * ( 0 + (4*c) + ( (y+0) % 2 ) ) ); val[c][1] = storage + 2 + ( ( imgwidth + 2 ) * ( 0 + (4*c) + ( (y+1) % 2 ) ) ); err[c][0] = storage + 2 + ( ( imgwidth + 2 ) * ( 2 + (4*c) + ( (y+0) % 2 ) ) ); err[c][1] = storage + 2 + ( ( imgwidth + 2 ) * ( 2 + (4*c) + ( (y+1) % 2 ) ) ); dta[c] = val[c][0]; } // decode pixel values for ( x = 0; x < imgwidth; x++ ) { if ( cmpc == 1 ) { ppn_decode_pjg( dec, mod[0], val[0], err[0], -1 ); } else { // cmpc >= 3 if ( cmpc > 3 ) ppn_decode_pjg( dec, mod[3], val[3], err[3], -1 ); ppn_decode_pjg( dec, mod[0], val[0], err[0], BITLENB16N(err[2][0][-1]) ); ppn_decode_pjg( dec, mod[2], val[2], err[2], BITLENB16N(err[0][0][0]) ); ppn_decode_pjg( dec, mod[1], val[1], err[1], ( BITLENB16N(err[0][0][0]) + BITLENB16N(err[2][0][0]) + 1 ) / 2 ); } // advance values for ( c = 0; c < cmpc; c++ ) { val[c][0]++; val[c][1]++; err[c][0]++; err[c][1]++; } } // write line pnm_write_line( stream, dta ); } // free storage / clear models free( storage ); for ( c = 0; c < cmpc; c++ ) delete( mod[c] ); return true; } /* ----------------------------------------------- PPN special mono encoding ----------------------------------------------- */ INTERN bool ppn_encode_imgdata_mono( aricoder* enc, iostream* stream ) { model_b* mod; int* storage; // storage array int* val[3]; int* dta; int x, y; // init model mod = INIT_MODEL_B( ( 1 << 12 ), 1 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 5 ) * 3, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers val[0] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+2) % 3 ) ); val[1] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+1) % 3 ) ); val[2] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+0) % 3 ) ); dta = val[0]; // read line if ( pnm_read_line( stream, &dta ) != 0 ) { sprintf( errormessage, "unexpected end of file" ); errorlevel = 2; free( storage ); delete( mod ); return false; } // encode pixel values for ( x = 0; x < imgwidth; x++, val[0]++, val[1]++, val[2]++ ) { mod->shift_context( get_context_mono( x, y, val ) ); encode_ari( enc, mod, **val ); } } // free storage / clear models free( storage ); delete( mod ); return true; } /* ----------------------------------------------- PPN special mono decoding ----------------------------------------------- */ INTERN bool ppn_decode_imgdata_mono( aricoder* dec, iostream* stream ) { model_b* mod; int* storage; // storage array int* val[3]; int* dta; int x, y; // init model mod = INIT_MODEL_B( ( 1 << 12 ), 1 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 5 ) * 3, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers val[0] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+2) % 3 ) ); val[1] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+1) % 3 ) ); val[2] = storage + 3 + ( ( imgwidth + 2 + 3 ) * ( (y+0) % 3 ) ); dta = val[0]; // decode pixel values for ( x = 0; x < imgwidth; x++, val[0]++, val[1]++, val[2]++ ) { mod->shift_context( get_context_mono( x, y, val ) ); **val = decode_ari( dec, mod ); } // write line pnm_write_line( stream, &dta ); } // free storage / clear models free( storage ); delete( mod ); return true; } /* ----------------------------------------------- PPN encoding for palette based image data ----------------------------------------------- */ INTERN bool ppn_encode_imgdata_palette( aricoder* enc, iostream* stream ) { model_s* mod; int* storage; // storage array int* val[2]; int* dta; int x, y; // init model mod = ( pnmax ) ? INIT_MODEL_S( pnmax + 1, pnmax + 1, 2 ) : INIT_MODEL_S( cmask[0]->m + 1, cmask[0]->m + 1, 2 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 1 ) * 2, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers val[0] = storage + 1 + ( ( imgwidth + 1 ) * ( (y+0) % 2 ) ); val[1] = storage + 1 + ( ( imgwidth + 1 ) * ( (y+1) % 2 ) ); dta = val[0]; // read line errorlevel = pnm_read_line( stream, &dta ); if ( errorlevel == 1 ) sprintf( errormessage, "excess data or bad data found" ); else if ( errorlevel == 2 ) { sprintf( errormessage, "unexpected end of file" ); free( storage ); delete( mod ); return false; } // encode pixel values for ( x = 0; x < imgwidth; x++, val[0]++, val[1]++ ) { shift_model( mod, val[0][-1], val[1][0] ); // shift in context encode_ari( enc, mod, **val ); } } // free storage / clear models free( storage ); delete( mod ); return true; } /* ----------------------------------------------- PPN decoding for palette based image data ----------------------------------------------- */ INTERN bool ppn_decode_imgdata_palette( aricoder* dec, iostream* stream ) { model_s* mod; int* storage; // storage array int* val[2]; int* dta; int x, y; // init model mod = ( pnmax ) ? INIT_MODEL_S( pnmax + 1, pnmax + 1, 2 ) : INIT_MODEL_S( cmask[0]->m + 1, cmask[0]->m + 1, 2 ); // allocate storage memory storage = (int*) calloc( ( imgwidth + 1 ) * 2, sizeof( int ) ); if ( storage == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // arithmetic compression loop for ( y = 0; y < imgheight; y++ ) { // set pointers val[0] = storage + 1 + ( ( imgwidth + 1 ) * ( (y+0) % 2 ) ); val[1] = storage + 1 + ( ( imgwidth + 1 ) * ( (y+1) % 2 ) ); dta = val[0]; // decode pixel values for ( x = 0; x < imgwidth; x++, val[0]++, val[1]++ ) { shift_model( mod, val[0][-1], val[1][0] ); // shift in context **val = decode_ari( dec, mod ); } // write line pnm_write_line( stream, &dta ); } // free storage / clear models free( storage ); delete( mod ); return true; } /* ----------------------------------------------- PPN packJPG type encoding ----------------------------------------------- */ INTERN inline void ppn_encode_pjg( aricoder* enc, pjg_model* mod, int** val, int** err, int ctx3 ) { int ctx_sgn; // context for sign int clen, absv, sgn; int bt, bp; // calculate prediction error **err = **val - plocoi( val[0][-1], val[1][0], val[1][-1] ); // encode bit length clen = BITLENB16N( **err ); if ( ctx3 < 0 ) shift_model( mod->len, BITLENB16N( err[0][-1] ), BITLENB16N( err[1][0] ) ); else shift_model( mod->len, BITLENB16N( err[0][-1] ), BITLENB16N( err[1][0] ), ctx3 ); encode_ari( enc, mod->len, clen ); // encode residual, sign only if bit length > 0 if ( clen > 0 ) { // compute absolute value, sign absv = ABS( **err ); sgn = ( **err > 0 ) ? 0 : 1; // compute sign context ctx_sgn = 0x0; if ( err[0][-2] > 0 ) ctx_sgn |= 0x1 << 0; if ( err[1][-1] > 0 ) ctx_sgn |= 0x1 << 1; if ( err[0][-1] > 0 ) ctx_sgn |= 0x1 << 2; if ( err[1][ 0] > 0 ) ctx_sgn |= 0x1 << 3; // first set bit must be 1, so we start at clen - 2 for ( bp = clen - 2; bp >= 0; bp-- ) { shift_model( mod->res, clen, bp ); // shift in context // encode/get bit bt = BITN( absv, bp ); encode_ari( enc, mod->res, bt ); } // encode sign mod->sgn->shift_context( ctx_sgn ); encode_ari( enc, mod->sgn, sgn ); } } /* ----------------------------------------------- PPN packJPG type decoding ----------------------------------------------- */ INTERN inline void ppn_decode_pjg( aricoder* dec, pjg_model* mod, int** val, int** err, int ctx3 ) { int ctx_sgn; // context for sign int clen, absv, sgn; int bt, bp; // decode bit length (of prediction error) if ( ctx3 < 0 ) shift_model( mod->len, BITLENB16N( err[0][-1] ), BITLENB16N( err[1][0] ) ); else shift_model( mod->len, BITLENB16N( err[0][-1] ), BITLENB16N( err[1][0] ), ctx3 ); clen = decode_ari( dec, mod->len ); // decode residual, sign only if bit length > 0 if ( clen > 0 ) { // compute sign context ctx_sgn = 0x0; if ( err[0][-2] > 0 ) ctx_sgn |= 0x1 << 0; if ( err[1][-1] > 0 ) ctx_sgn |= 0x1 << 1; if ( err[0][-1] > 0 ) ctx_sgn |= 0x1 << 2; if ( err[1][ 0] > 0 ) ctx_sgn |= 0x1 << 3; // first set bit must be 1, so we start at clen - 2 for ( bp = clen - 2, absv = 1; bp >= 0; bp-- ) { shift_model( mod->res, clen, bp ); // shift in context // decode bit bt = decode_ari( dec, mod->res ); // update value absv = absv << 1; if ( bt ) absv |= 1; } // decode sign mod->sgn->shift_context( ctx_sgn ); sgn = decode_ari( dec, mod->sgn ); // store data **err = ( sgn == 0 ) ? absv : -absv; } else **err = 0; // decode prediction error **val = **err + plocoi( val[0][-1], val[1][0], val[1][-1] ); } /* ----------------------------------------------- special context for mono color space ----------------------------------------------- */ INTERN inline int get_context_mono( int x, int y, int** val ) { int ctx_mono = 0; // this is the context modelling used here: // [i] [f] [j] // [g] [c] [b] [d] [h] // [k] [e] [a] [x] // [x] is to be encoded // this function calculates and returns coordinates for a simple 2D context // base context calculation ctx_mono <<= 1; if ( val[0][-1] ) ctx_mono |= 1; // a ctx_mono <<= 1; if ( val[1][ 0] ) ctx_mono |= 1; // b ctx_mono <<= 1; if ( val[1][-1] ) ctx_mono |= 1; // c ctx_mono <<= 1; if ( val[1][ 1] ) ctx_mono |= 1; // d ctx_mono <<= 1; if ( val[0][-2] ) ctx_mono |= 1; // e ctx_mono <<= 1; if ( val[2][ 0] ) ctx_mono |= 1; // f ctx_mono <<= 1; if ( val[1][-2] ) ctx_mono |= 1; // g ctx_mono <<= 1; if ( val[1][ 2] ) ctx_mono |= 1; // h ctx_mono <<= 1; if ( val[2][-1] ) ctx_mono |= 1; // i ctx_mono <<= 1; if ( val[2][ 1] ) ctx_mono |= 1; // j ctx_mono <<= 1; if ( val[0][-3] ) ctx_mono |= 1; // k // special flags (edge treatment) if ( x <= 2 ) { if ( x == 0 ) ctx_mono |= 0x804; // bits 11 (z) & 3 (c) else if ( x == 1 ) ctx_mono |= 0x840; // bits 11 (z) & 6 (g) else ctx_mono |= 0xC00; // bits 11 (z) & 10 (k) } else if ( y <= 1 ) { if ( y == 0 ) ctx_mono |= 0x810; // bits 11 (z) & 4 (d) else ctx_mono |= 0xA00; // bits 11 (z) & 9 (j) } return ctx_mono; } /* ----------------------------------------------- loco-i predictor ----------------------------------------------- */ INTERN inline int plocoi( int a, int b, int c ) { // a -> left; b -> above; c -> above-left int min, max; min = ( a < b ) ? a : b; max = ( a > b ) ? a : b; if ( c >= max ) return min; if ( c <= min ) return max; return a + b - c; } /* ----------------------------------------------- PNM read line ----------------------------------------------- */ INTERN inline int pnm_read_line( iostream* stream, int** line ) { unsigned int rgb[ 4 ] = { 0, 0, 0, 0 }; // RGB + A unsigned char bt = 0; unsigned int dt = 0; int w_excess = 0; int x, n, c; if ( cmpc == 1 ) switch ( imgbpp ) { // single component data case 1: for ( x = 0; x < imgwidth; x += 8 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; for ( n = 8 - 1; n >= 0; n--, bt >>= 1 ) line[0][x+n] = bt & 0x1; } break; case 4: for ( x = 0; x < imgwidth; x += 2 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; line[0][x+1] = (bt>>0) & 0xF; line[0][x+0] = (bt>>4) & 0xF; if ( pnmax > 0 ) { if ( line[0][x+0] > (signed) pnmax ) { line[0][x+0] = pnmax; w_excess = 1; } if ( line[0][x+1] > (signed) pnmax ) { line[0][x+1] = pnmax; w_excess = 1; } } } break; case 8: for ( x = 0; x < imgwidth; x++ ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; if ( pnmax > 0 ) if ( bt > pnmax ) { bt = pnmax; w_excess = 1; } line[0][x] = bt; } break; case 16: for ( x = 0; x < imgwidth; x++ ) { for ( n = 0, dt = 0; n < 16; n += 8 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; dt = ( endian_l ) ? dt | ( bt << n ) : ( dt << 8 ) | bt; } if ( pnmax > 0 ) if ( dt > pnmax ) { dt = pnmax; w_excess = 1; } line[0][x] = dt; } break; } else { // multi component data for ( x = 0; x < imgwidth; x++ ) { if ( subtype == S_BMP ) { // 16/24/32bpp BMP for ( n = 0, dt = 0; n < imgbpp; n += 8 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; dt = ( endian_l ) ? dt | ( bt << n ) : ( dt << 8 ) | bt; } for ( c = 0; c < cmpc; c++ ) { rgb[c] = ( dt >> cmask[c]->p ) & cmask[c]->m; if ( pnmax > 0 ) if ( rgb[c] > pnmax ) { rgb[c] = pnmax; w_excess = 1; } } if ( cmask[4] != NULL ) if ( ( dt >> cmask[4]->p ) & cmask[4]->m ) w_excess = 1; } else if ( subtype == S_PPM ) { // 24/48bpp PPM for ( c = 0; c < 3; c++ ) { if ( imgbpp == 48 ) for ( n = 0, dt = 0; n < 16; n += 8 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; dt = ( endian_l ) ? dt | ( bt << n ) : ( dt << 8 ) | bt; } else { // imgbpp == 24 if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; dt = bt; } if ( pnmax > 0 ) if ( dt > pnmax ) { dt = pnmax; w_excess = 1; } rgb[c] = dt; } } else { // 32bpp HDR for ( c = 0; c < 4; c++ ) { // try uncompressed reading if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; rgb[c] = bt; } if ( x == 0 ) { // check for RLE compression for ( c = 0, dt = 0; c < 4; dt = ( dt << 8 ) | rgb[c++] ); if ( dt == (unsigned) ( 0x02020000 | imgwidth ) ) return hdr_decode_line_rle( stream, line ); } } // RGB color component prediction & copy to line rgb_process( rgb ); for ( c = 0; c < cmpc; c++ ) line[c][x] = rgb[c]; } } // bmp line alignment at 4 byte if ( subtype == S_BMP ) { for ( x = imgwidth * imgbpp; ( x % 32 ) != 0; x += 8 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; if ( bt ) w_excess = 1; } } return w_excess; } /* ----------------------------------------------- PNM write line ----------------------------------------------- */ INTERN inline int pnm_write_line( iostream* stream, int** line ) { unsigned int rgb[ 4 ]; // RGB + A unsigned char bt = 0; unsigned int dt = 0; int x, n, c; if ( cmpc == 1 ) switch ( imgbpp ) { // single component data case 1: for ( x = 0; x < imgwidth; x += 8 ) { for ( n = 0, bt = 0; n < 8; n++ ) { bt <<= 1; if ( line[0][x+n] ) bt |= 0x1; } stream->write( &bt, 1, 1 ); } break; case 4: for ( x = 0; x < imgwidth; x += 2 ) { bt = ( line[0][x+0] << 4 ) | line[0][x+1]; stream->write( &bt, 1, 1 ); } break; case 8: for ( x = 0; x < imgwidth; x++ ) { bt = line[0][x]; stream->write( &bt, 1, 1 ); } break; case 16: for ( x = 0; x < imgwidth; x++ ) { for ( n = 0, dt = line[0][x]; n < 16; n += 8 ) { bt = ( ( endian_l ) ? ( dt >> n ) : ( dt >> ( ( 16 - 8 ) - n ) ) ) & 0xFF; stream->write( &bt, 1, 1 ); } } break; } else { // multi component data if ( use_rle && ( subtype == S_HDR ) ) { // HDR RLE encoding bt = 0x02; stream->write( &bt, 1, 1 ); stream->write( &bt, 1, 1 ); bt = ( imgwidth >> 8 ) & 0xFF; stream->write( &bt, 1, 1 ); bt = imgwidth & 0xFF; stream->write( &bt, 1, 1 ); return hdr_encode_line_rle( stream, line ); } for ( x = 0; x < imgwidth; x++ ) { // copy & RGB color component prediction undo for ( c = 0; c < cmpc; c++ ) rgb[c] = line[c][x]; // RGB color component prediction & copy to line rgb_unprocess( rgb ); if ( subtype == S_BMP ) { // 16/24/32bpp BMP for ( c = 0, dt = 0; c < cmpc; c++ ) dt |= rgb[c] << cmask[c]->p; for ( n = 0; n < imgbpp; n += 8 ) { bt = ( ( endian_l ) ? ( dt >> n ) : ( dt >> ( ( imgbpp - 8 ) - n ) ) ) & 0xFF; stream->write( &bt, 1, 1 ); } } else if ( subtype == S_PPM ) { // 24/48bpp PPM for ( c = 0; c < 3; c++ ) { if ( imgbpp == 48 ) for ( n = 0, dt = rgb[c]; n < 16; n += 8 ) { bt = ( ( endian_l ) ? ( dt >> n ) : ( dt >> ( ( 16 - 8 ) - n ) ) ) & 0xFF; stream->write( &bt, 1, 1 ); } else { // imgbpp == 24 bt = rgb[c]; stream->write( &bt, 1, 1 ); } } } else { // 32bpp HDR for ( c = 0; c < 4; c++ ) { bt = rgb[c]; stream->write( &bt, 1, 1 ); } } } } // bmp line alignment at 4 byte if ( subtype == S_BMP ) { for ( x = imgwidth * imgbpp, bt = 0; ( x % 32 ) != 0; x += 8 ) stream->write( &bt, 1, 1 ); } return 0; } /* ----------------------------------------------- HDR decode RLE ----------------------------------------------- */ INTERN inline int hdr_decode_line_rle( iostream* stream, int** line ) { static unsigned int* data = NULL; static int prev_width = 0; unsigned int* rgb; // RGB + E unsigned char bt = 0; int r, rl; int x, c; // allocate memory for line storage if ( prev_width != imgwidth ) { prev_width = imgwidth; data = ( unsigned int* ) calloc( imgwidth * 4, sizeof( int ) ); if ( data == NULL ) return 2; // bad, but unlikely to happen anyways } // RLE compressed reading for ( c = 0; c < 4; c++ ) { for ( x = 0, rgb = data+c; x < imgwidth; x += rl ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; if ( bt > 0x80 ) { // run of value rl = bt ^ 0x80; // run length: absolute of bt if ( x + rl > imgwidth ) return 2; // sanity check if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; for ( r = 0; r < rl; r++, rgb += 4 ) *rgb = bt; } else { // dump rl = bt; // dump length: same as bt if ( x + rl > imgwidth ) return 2; // sanity check for ( r = 0; r < rl; r++, rgb += 4 ) { if ( stream->read( &bt, 1, 1 ) != 1 ) return 2; *rgb = bt; } } } } // prediction and copy for ( x = 0, rgb = data; x < imgwidth; x++, rgb += 4 ) { rgb_process( rgb ); // prediction for ( c = 0; c < 4; c++ ) line[c][x] = rgb[c]; } return 1; } /* ----------------------------------------------- HDR encode RLE ----------------------------------------------- */ INTERN inline int hdr_encode_line_rle( iostream* stream, int** line ) { static unsigned int* data = NULL; static int prev_width = 0; unsigned int* rgb; // RGB + E unsigned int* dt; unsigned char bt = 0; int rl, nd; int x, rm, c; // allocate memory for line storage if ( prev_width != imgwidth ) { prev_width = imgwidth; data = ( unsigned int* ) calloc( imgwidth * 4, sizeof( int ) ); if ( data == NULL ) return 2; // bad, but unlikely to happen anyways } // undo prediction and copy for ( x = 0, rgb = data; x < imgwidth; x++, rgb += 4 ) { for ( c = 0; c < 4; c++ ) rgb[c] = line[c][x]; rgb_unprocess( rgb ); // prediction undo } // RLE compressed writing for ( c = 0; c < 4; c++ ) { for ( rm = imgwidth, dt = rgb = data+c; rm; ) { if ( rm > 1 ) { rm -= 2; dt += 8; if ( *(dt-8) == *(dt-4) ) { // start of a run for ( rl = 2; ( rm ) && ( rl < 0x7F ) && ( *dt == *(dt-4) ); rl++, rm--, dt += 4 ); bt = rl | 0x80; stream->write( &bt, 1, 1 ); bt = *rgb; stream->write( &bt, 1, 1 ); rgb = dt; } else { // start of a dump for ( nd = 2, rl = 1; ( rm ) && ( nd < 0x80 ); nd++, rm--, dt += 4 ) { if ( *dt == *(dt-4) ) { if ( ++rl > 2 ) { nd -= (rl-1); rm += (rl-1); dt -= ((rl-1)*4); break; } } else rl = 1; } bt = nd; stream->write( &bt, 1, 1 ); for ( ; rgb < dt; rgb += 4 ) { bt = *rgb; stream->write( rgb, 1, 1 ); } } } else { // only one remains bt = 0x01; stream->write( &bt, 1, 1 ); bt = *dt; stream->write( &bt, 1, 1 ); break; } } } return 0; } /* ----------------------------------------------- apply RGB prediction ----------------------------------------------- */ INTERN inline void rgb_process( unsigned int* rgb ) { // RGB color component prediction for ( int c = 0; c < 3; c++ ) if ( c != 1 ) { if ( pnmax == 0 ) { rgb[c] |= 0x40000000; rgb[c] -= ( rgb[1] >> ( cmask[1]->s - cmask[c]->s ) ); rgb[c] &= cmask[c]->m; } else { rgb[c] -= rgb[1]; if ( rgb[c] < 0 ) rgb[c] += pnmax + 1; } } } /* ----------------------------------------------- undo RGB prediction ----------------------------------------------- */ INTERN inline void rgb_unprocess( unsigned int* rgb ) { // RGB color component prediction undo for ( int c = 0; c < 3; c++ ) if ( c != 1 ) { if ( pnmax == 0 ) { rgb[c] += ( rgb[1] >> ( cmask[1]->s - cmask[c]->s ) ); rgb[c] &= cmask[c]->m; } else { rgb[c] += rgb[1]; if ( rgb[c] > pnmax ) rgb[c] -= pnmax + 1; } } } /* ----------------------------------------------- identify file from 2 bytes ----------------------------------------------- */ INTERN inline void identify( const char* id, int* ft, int* st ) { *ft = F_UNK; *st = S_UNK; switch ( id[0] ) { case 'S': switch ( id[1] ) { case '4': *st = S_PBM; break; case '5': *st = S_PGM; break; case '6': *st = S_PPM; break; case 'M': *st = S_BMP; break; case '?': *st = S_HDR; break; } if ( *st != S_UNK ) *ft = F_PPN; break; case 'P': switch ( id[1] ) { case '4': *st = S_PBM; break; case '5': *st = S_PGM; break; case '6': *st = S_PPM; break; } if ( *st != S_UNK ) *ft = F_PNM; break; case 'B': if ( id[1] == 'M' ) { *st = S_BMP; *ft = F_PNM; } break; case '#': if ( id[1] == '?' ) { *st = S_HDR; *ft = F_PNM; } break; } } /* ----------------------------------------------- scans headers of input filetypes (decision) ----------------------------------------------- */ INTERN char* scan_header( iostream* stream ) { char* imghdr; // char id[2]; // identify (again) // stream->read( &id, 2, 1 ); // stream->rewind(); // identify( id, &filetype, &subtype ); // do the actual work switch( subtype ) { case S_PBM: case S_PGM: case S_PPM: imghdr = scan_header_pnm( stream ); break; case S_BMP: imghdr = scan_header_bmp( stream ); break; case S_HDR: imghdr = scan_header_hdr( stream ); break; default: imghdr = NULL; } // check image dimensions if ( ( errorlevel < 2 ) && ( ( imgwidth <= 0 ) || ( imgheight <= 0 ) ) ) { sprintf( errormessage, "image contains no data" ); errorlevel = 2; free ( imghdr ); return NULL; } return imghdr; } /* ----------------------------------------------- scans headers of input filetypes (PNM) ----------------------------------------------- */ INTERN char* scan_header_pnm( iostream* stream ) { char* imghdr; char* ptr0; char* ptr1; char ws; int u, c; // endianness endian_l = E_BIG; // preset image header (empty string) imghdr = ( char* ) calloc( 1024, sizeof( char ) ); if ( imghdr == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return NULL; } // parse and store header for ( ptr0 = imghdr, ptr1 = imghdr, u = 0; ( stream->read( ptr1, 1, 1 ) == 1 ) && ( ptr1 - imghdr < 1023 ); ptr1++ ) { if ( ( ( *ptr1 == ' ' ) && ( *ptr0 != '#' ) ) || ( *ptr1 == '\n' ) || ( *ptr1 == '\r' ) ) { ws = *ptr1; *ptr1 = '\0'; if ( ( strlen( ptr0 ) > 0 ) && ( *ptr0 != '#' ) ) { switch ( u ) { case 0: // first item (f.e. "P5") if ( strlen( ptr0 ) == 2 ) { switch ( ptr0[1] ) { case '4': imgbpp = 1; cmpc = 1; u++; break; case '5': imgbpp = 8; cmpc = 1; u++; break; case '6': imgbpp = 24; cmpc = 3; u++; break; default: u = -1; break; } } else u = -1; break; case 1: // image width ( sscanf( ptr0, "%i", &imgwidthv ) == 1 ) ? u++ : u = -1; break; case 2: // image height ( sscanf( ptr0, "%i", &imgheight ) == 1 ) ? u++ : u = -1; break; case 3: // maximum pixel value ( sscanf( ptr0, "%ui", &pnmax ) == 1 ) ? u++ : u = -1; break; } if ( ( u == 3 ) && ( imgbpp == 1 ) ) u = 4; else if ( u == -1 ) break; } *ptr1 = ws; ptr0 = ptr1 + 1; if ( u == 4 ) break; } } // check data for trouble if ( u != 4 ) { sprintf( errormessage, "error in header structure (#%i)", u ); errorlevel = 2; free ( imghdr ); return NULL; } else *ptr0 = '\0'; // process data - actual line width imgwidth = ( imgbpp == 1 ) ? ( (( imgwidthv + 7 ) / 8) * 8 ) : imgwidthv; // process data - pixel maximum value if ( pnmax > 0 ) { if ( pnmax >= 65536 ) { sprintf( errormessage, "maximum value (%i) not supported", (int) pnmax ); errorlevel = 2; free ( imghdr ); return NULL; } if ( pnmax >= 256 ) imgbpp *= 2; if ( ( pnmax == 0xFF ) || ( pnmax == 0xFFFF ) ) pnmax = 0; } // process data - masks switch ( imgbpp ) { case 1: cmask[0] = new cmp_mask( 0x1 ); break; case 8: cmask[0] = new cmp_mask( 0xFF ); break; case 16: cmask[0] = new cmp_mask( 0xFFFF ); break; case 24: cmask[0] = new cmp_mask( 0xFF0000 ); cmask[1] = new cmp_mask( 0x00FF00 ); cmask[2] = new cmp_mask( 0x0000FF ); break; case 48: for ( c = 0; c < 3; c++ ) { // bpp == 48 cmask[c] = new cmp_mask(); cmask[c]->m = 0xFFFF; cmask[c]->s = 16; cmask[c]->p = ( 2 - c ) * 16; } break; default: break; } return imghdr; } /* ----------------------------------------------- scans headers of input filetypes (BMP) ----------------------------------------------- */ INTERN char* scan_header_bmp( iostream* stream ) { unsigned int bmask[4] = { 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 }; unsigned int xmask = 0x00000000; char* imghdr; int bmpv = 0; int offs = 0; int hdrs = 0; int cmode = 0; int c; // endianness endian_l = E_LITTLE; // preset image header (empty array) imghdr = ( char* ) calloc( 2048, sizeof( char ) ); if ( imghdr == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return NULL; } // read the first 18 byte, determine BMP version and validity if ( stream->read( imghdr, 1, 18 ) == 18 ) { offs = INT_LE( imghdr + 0x0A ); hdrs = INT_LE( imghdr + 0x0E ); switch ( hdrs ) { case 12: bmpv = 2; break; case 40: bmpv = 3; break; case 56: // special v4 case 108: bmpv = 4; break; case 124: bmpv = 5; break; default: bmpv = 0; break; } } // check plausibility if ( ( bmpv == 0 ) || ( offs > 2048 ) || ( offs < hdrs ) || ( stream->getsize() < offs ) ) { sprintf( errormessage, "unknown BMP version or unsupported file type" ); errorlevel = 2; free ( imghdr ); return NULL; } // read remainder data stream->read( imghdr + 18, 1, offs - 18 ); if ( bmpv == 2 ) { // BMP version 2 imgwidthv = SHORT_LE( imghdr + 0x12 ); imgheight = SHORT_LE( imghdr + 0x14 ); imgbpp = SHORT_LE( imghdr + 0x18 ); cmode = 0; } else { // BMP versions 3 and 4 imgwidthv = INT_LE( imghdr + 0x12 ); imgheight = INT_LE( imghdr + 0x16 ); imgbpp = SHORT_LE( imghdr + 0x1C ); cmode = SHORT_LE( imghdr + 0x1E ); bmpsize = INT_LE( imghdr + 0x22 ) + offs; pnmax = INT_LE( imghdr + 0x2E ); } // actual width and height if ( imgheight < 0 ) imgheight = -imgheight; switch ( imgbpp ) { //case 1: imgwidth = ( ( imgwidthv + 7 ) / 8 ) * 8; break; //case 4: imgwidth = ( ( imgwidthv + 1 ) / 2 ) * 2; break; case 1: imgwidth = ( ( imgwidthv + 31 ) / 32 ) * 32; break; case 4: imgwidth = ( ( imgwidthv + 7 ) / 8 ) * 8; break; case 8: imgwidth = ( ( imgwidthv + 3 ) / 4 ) * 4; break; case 16: imgwidth = ( ( imgwidthv + 1 ) / 2 ) * 2; break; default: imgwidth = imgwidthv; break; } // BMP compatibility check if ( ( ( cmode != 0 ) && ( cmode != 3 ) ) || ( ( cmode == 3 ) && ( imgbpp <= 8 ) ) || ( ( cmode == 0 ) && ( imgbpp == 16 ) ) ) { switch ( cmode ) { case 1: sprintf( errormessage, "BMP RLE8 decoding is not supported" ); break; case 2: sprintf( errormessage, "BMP RLE4 decoding is not supported" ); break; default: sprintf( errormessage, "probably not a proper BMP file" ); break; } errorlevel = 2; free ( imghdr ); return NULL; } // read and check component masks cmpc = ( imgbpp <= 8 ) ? 1 : ( ( imgbpp < 32 ) ? 3 : 4 ); if ( ( cmode == 3 ) && ( imgbpp > 8 ) ) { for ( c = 0; c < ( ( bmpv == 3 ) ? 3 : 4 ); c++ ) bmask[c] = INT_LE( imghdr + 0x36 + ( 4 * c ) ); cmpc = ( bmask[3] ) ? 4 : 3; // check mask integers for ( c = 0, xmask = 0; c < cmpc; xmask |= bmask[c++] ); if ( pnmax > xmask ) { sprintf( errormessage, "bad BMP \"color used\" value: %i", (int) pnmax ); errorlevel = 2; free ( imghdr ); return NULL; } xmask ^= 0xFFFFFFFF >> ( 32 - imgbpp ); } // generate masks if ( imgbpp <= 8 ) cmask[0] = new cmp_mask( ( imgbpp == 1 ) ? 0x1 : ( imgbpp == 4 ) ? 0xF : 0xFF ); else for ( c = 0; c < cmpc; c++ ) { cmask[c] = new cmp_mask( bmask[c] ); if ( ( cmask[c]->p == -1 ) || ( ( cmask[c]->p + cmask[c]->s ) > imgbpp ) || ( cmask[c]->s > 16 ) ) { sprintf( errormessage, "corrupted BMP component mask %i:%08X", c, bmask[c] ); errorlevel = 2; free ( imghdr ); return NULL; } } if ( xmask ) cmask[4] = new cmp_mask( xmask ); return imghdr; } /* ----------------------------------------------- scans headers of input filetypes (HDR) ----------------------------------------------- */ INTERN inline char* scan_header_hdr( iostream* stream ) { char* imghdr; char* ptr0; char* ptr1; char res[4]; char ws; // preset image header (empty string) imghdr = ( char* ) calloc( 4096, sizeof( char ) ); if ( imghdr == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return NULL; } // parse and store header for ( ptr0 = ptr1 = imghdr;; ptr1++ ) { if ( ( stream->read( ptr1, 1, 1 ) != 1 ) || ( ptr1 - imghdr >= 4096 ) ) { sprintf( errormessage, "unknown file or corrupted HDR header" ); break; } if ( *ptr1 == '\n' ) { ws = *ptr1; *ptr1 = '\0'; if ( ptr0 == imghdr ) { // check magic number if ( strncmp( ptr0+1, "?RADIANCE", 9 ) != 0 ) { sprintf( errormessage, "looked like HDR, but unknown" ); break; } } else { if ( strncmp( ptr0, "FORMAT", 6 ) == 0 ) { // check format for ( ; ( ptr0 < ptr1 ) && ( *ptr0 != '=' ); ptr0++ ); for ( ptr0++; ( ptr0 < ptr1 ) && ( *ptr0 == ' ' ); ptr0++ ); if ( ptr0 >= ptr1 ) { sprintf( errormessage, "bad HDR FORMAT string" ); break; } if ( ( strncmp( ptr0, "32-bit_rle_rgbe", 15 ) != 0 ) && ( strncmp( ptr0, "32-bit_rle_xyze", 15 ) != 0 ) ) { sprintf( errormessage, "unknown HDR format: %s", ptr0 ); break; } } else if ( ( ( ptr0[0] == '-' ) || ( ptr0[0] == '+' ) ) && ( ( ptr0[1] == 'X' ) || ( ptr0[1] == 'Y' ) ) ) { if ( sscanf( ptr0, "%c%c %i %c%c %i", &res[0], &res[1], &imgheight, &res[2], &res[3], &imgwidthv ) == 6 ) imgwidth = imgwidthv; if ( ( !imgwidth ) || ( ( res[2] != '-' ) && ( res[2] != '+' ) ) || ( ( res[3] != 'X' ) && ( res[3] != 'Y' ) ) || ( res[1] == res[3] ) ) { sprintf( errormessage, "bad HDR resolution string: %s", ptr0 ); imgwidth = 0; break; } } } *ptr1 = ws; ptr0 = ptr1 + 1; if ( imgwidth ) { *ptr0 = '\0'; break; } } } // check for trouble if ( !imgwidth ) { errorlevel = 2; free ( imghdr ); return NULL; } // imgbpp, cmpc, endianness imgbpp = 32; cmpc = 4; endian_l = E_BIG; // build masks cmask[0] = new cmp_mask( 0xFF000000 ); cmask[1] = new cmp_mask( 0x00FF0000 ); cmask[2] = new cmp_mask( 0x0000FF00 ); cmask[3] = new cmp_mask( 0x000000FF ); return imghdr; } /* ----------------------- End of side functions -------------------------- */ /* ----------------------- Begin of miscellaneous helper functions -------------------------- */ /* ----------------------------------------------- displays progress bar on screen ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline void progress_bar( int current, int last ) { int barpos = ( ( current * BARLEN ) + ( last / 2 ) ) / last; int i; // generate progress bar fprintf( msgout, "[" ); #if defined(_WIN32) for ( i = 0; i < barpos; i++ ) fprintf( msgout, "\xFE" ); #else for ( i = 0; i < barpos; i++ ) fprintf( msgout, "X" ); #endif for ( ; i < BARLEN; i++ ) fprintf( msgout, " " ); fprintf( msgout, "]" ); } #endif /* ----------------------------------------------- creates filename, callocs memory for it ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline char* create_filename( const char* base, const char* extension ) { int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1; char* filename = (char*) calloc( len, sizeof( char ) ); // create a filename from base & extension strcpy( filename, base ); set_extension( filename, extension ); return filename; } #endif /* ----------------------------------------------- creates filename, callocs memory for it ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline char* unique_filename( const char* base, const char* extension ) { int len = strlen( base ) + ( ( extension == NULL ) ? 0 : strlen( extension ) + 1 ) + 1; char* filename = (char*) calloc( len, sizeof( char ) ); // create a unique filename using underscores strcpy( filename, base ); set_extension( filename, extension ); while ( file_exists( filename ) ) { len += sizeof( char ); filename = (char*) realloc( filename, len ); add_underscore( filename ); } return filename; } #endif /* ----------------------------------------------- changes extension of filename ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline void set_extension( const char* filename, const char* extension ) { char* extstr; // find position of extension in filename extstr = ( strrchr( filename, '.' ) == NULL ) ? strrchr( filename, '\0' ) : strrchr( filename, '.' ); // set new extension if ( extension != NULL ) { (*extstr++) = '.'; strcpy( extstr, extension ); } else (*extstr) = '\0'; } #endif /* ----------------------------------------------- adds underscore after filename ----------------------------------------------- */ #if !defined(BUILD_LIB) INTERN inline void add_underscore( char* filename ) { char* tmpname = (char*) calloc( strlen( filename ) + 1, sizeof( char ) ); char* extstr; // copy filename to tmpname strcpy( tmpname, filename ); // search extension in filename extstr = strrchr( filename, '.' ); // add underscore before extension if ( extstr != NULL ) { (*extstr++) = '_'; strcpy( extstr, strrchr( tmpname, '.' ) ); } else sprintf( filename, "%s_", tmpname ); // free memory free( tmpname ); } #endif /* ----------------------------------------------- checks if a file exists ----------------------------------------------- */ INTERN inline bool file_exists( const char* filename ) { // needed for both, executable and library FILE* fp = fopen( filename, "rb" ); if ( fp == NULL ) return false; else { fclose( fp ); return true; } } /* ----------------------- End of miscellaneous helper functions -------------------------- */ /* ----------------------- Begin of developers functions -------------------------- */ #if !defined(BUILD_LIB) && defined(DEV_BUILD) /* ----------------------------------------------- Writes error info file ----------------------------------------------- */ INTERN bool write_errfile( void ) { FILE* fp; char* fn; // return immediately if theres no error if ( errorlevel == 0 ) return true; // create filename based on errorlevel if ( errorlevel == 1 ) { fn = create_filename( filelist[ file_no ], "wrn.nfo" ); } else { fn = create_filename( filelist[ file_no ], "err.nfo" ); } // open file for output fp = fopen( fn, "w" ); if ( fp == NULL ){ sprintf( errormessage, FWR_ERRMSG ); errorlevel = 2; return false; } free( fn ); // write status and errormessage to file fprintf( fp, "--> error (level %i) in file \"%s\" <--\n", errorlevel, filelist[ file_no ] ); fprintf( fp, "\n" ); // write error specification to file fprintf( fp, " %s -> %s:\n", get_status( errorfunction ), ( errorlevel == 1 ) ? "warning" : "error" ); fprintf( fp, " %s\n", errormessage ); // done, close file fclose( fp ); return true; } #endif #if !defined(BUILD_LIB) && defined(DEV_BUILD) /* ----------------------------------------------- Dumps image data to PGM ----------------------------------------------- */ INTERN bool dump_pgm( void ) { FILE* fp; char* fn; char* imghdr = scan_header( str_in ); int* data; int* ptr[4] = { NULL }; char bt; // parse PNM file header if ( imghdr == NULL ) { errorlevel = 2; return false; } // allocate memory for data data = ( int* ) calloc( cmpc * imgwidth * imgheight, sizeof( int ) ); if ( data == NULL ) { sprintf( errormessage, MEM_ERRMSG ); errorlevel = 2; return false; } // open output file fn = create_filename( filelist[ file_no ], "dump.pgm" ); fp = fopen( fn, "wb" ); if ( fp == NULL ){ free( data ); sprintf( errormessage, FWR_ERRMSG ); errorlevel = 2; return false; } free( fn ); // read all data from input file... for ( int y = 0; y < imgheight; y++ ) { for ( int c = 0; c < cmpc; c++ ) ptr[c] = data + ( c * imgheight + y ) * imgwidth; pnm_read_line( str_in, ptr ); } // ...and dump it all to one file fprintf( fp, "P5\n%i %i\n255\n", imgwidth, imgheight * cmpc ); for ( int c = 0; c < cmpc; c++ ) for ( int p = 0; p < imgheight * imgwidth; p++ ) { bt = data[ ( c * imgheight * imgwidth ) + p ] << ( 8 - cmask[c]->s ); fwrite( &bt, 1, 1, fp ); } // close file and free data fclose( fp ); free( imghdr ); free( data ); return true; } #endif #if !defined(BUILD_LIB) && defined(DEV_BUILD) /* ----------------------------------------------- Dumps info about image file ----------------------------------------------- */ INTERN bool dump_info( void ) { FILE* fp; char* fn; char* imghdr = scan_header( str_in ); // parse PNM file header if ( imghdr == NULL ) { errorlevel = 2; return false; } // open output file fn = create_filename( filelist[ file_no ], "nfo" ); fp = fopen( fn, "wb" ); if ( fp == NULL ){ sprintf( errormessage, FWR_ERRMSG ); errorlevel = 2; return false; } free( fn ); // dump data to NFO file fprintf( fp, "<%s>\n", filelist[ file_no ] ); fprintf( fp, "type : %scompressed\n", ( filetype == F_PPN ) ? "" : "un" ); fprintf( fp, "sub : " ); switch ( subtype ) { case S_PBM: fprintf( fp, "PBM\n" ); break; case S_PGM: fprintf( fp, "PGM\n" ); break; case S_PPM: fprintf( fp, "PPM\n" ); break; case S_BMP: fprintf( fp, "BMP\n" ); break; case S_HDR: fprintf( fp, "HDR\n" ); break; } fprintf( fp, "dim : %ix%ipx / %ibpp\n", imgwidth, imgheight, imgbpp ); fprintf( fp, "vis : %ix%ipx\n", imgwidthv, imgheight ); fprintf( fp, "max : %s%i\n", ( pnmax ) ? "" : "not set / ", (int) pnmax ); fprintf( fp, "#ch : %i\n", cmpc ); fprintf( fp, "\n" ); for ( int c = 0; c < 5; c++ ) if ( cmask[c] != NULL ) { fprintf( fp, "channel #%i\n", c ); fprintf( fp, "col : " ); switch ( c ) { case 0: fprintf( fp, ( cmpc == 1 ) ? "LUM\n" : "RED\n" ); break; case 1: fprintf( fp, "GREEN\n" ); break; case 2: fprintf( fp, "BLUE\n" ); break; case 3: fprintf( fp, ( subtype == S_HDR ) ? "EXP\n" : "ALPHA\n" ); break; case 4: fprintf( fp, "RES\n" ); break; } fprintf( fp, "mask : 0x%X << %i\n", cmask[c]->m, cmask[c]->p ); fprintf( fp, "\n" ); } // close file and free data fclose( fp ); free( imghdr ); return true; } #endif /* ----------------------- End of developers functions -------------------------- */ /* ----------------------- End of file -------------------------- */