/* # proc: grp4comp - CCITT Group 4 compresses an image. # proc: */ /******************************************************************** * File Name: grp4comp.c * Modified: Darlene E. Frederick * Michael D. Garris * Date: January 25, 1990 * Package: CCITT4 compression routines * * Modified 12/90 by Stan Janet * flush_buffer() was adding an extra byte to data * whether it was already byte-aligned or not * Modified 12/94 by Patrick Grother * Reclared the all variables of type "short" to be * "int", using a macro SHORT defined in the include * file grp4comp.h. * On images with more than 2^15 rows the * result was garbage because of an overflowed line * counter. The new declaration has a limit of 2^31 * * Contents: ccitt4_compress() * read_uncompressed_file_into_memory() * control_compression() * prepare_to_compress() * compress_image() * make_array_of_changing_elements() * set_up_first_and_last_changing_elements_c() * prepare_to_compress_next_line() * set_up_first_line() * crash_c() * ********************************************************************/ #include #include #include #ifdef TIME #include #endif #include "g4codec.h" #include "grp4comp.h" /* Added by MDG in order have option of passing alloc responsibilities to caller */ #define NOALLOC 0 #define ALLOC 1 int comp_alloc_flag = ALLOC; int comp_write_init_flag; #ifdef TIME struct timeval t1, t2; struct timezone tz; #endif /*********************************************************************** * grp4comp is the main routine of this file. It does pre- * * liminary setup, calls routines, and does final processing. * ************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * indata - buffer containing the uncompressed data. * * inbytes - the number of bytes in indata. * * width - Width in pixels of scan line in indata. * * height - Number of lines in indata. * * Returned: * * outdata - buffer containing the compressed data. * * outbytes - the number of bytes in outdata. * ************************************************************************/ int grp4comp(unsigned char* indata, int inbytes, int width, int height, unsigned char* outdata, int *outbytes) { struct uncompressed_descriptor uncompressed; struct compressed_descriptor compressed; uncompressed.pixels_per_line = width; uncompressed.number_of_lines = height; uncompressed.data = indata; comp_alloc_flag = NOALLOC; comp_write_init_flag = 1; read_uncompressed_file_into_memory( &uncompressed); compressed.data = outdata; control_compression( &uncompressed, &compressed ); *outbytes = compressed.length_in_bytes; return *outbytes; /* printf("\ncompressed:lines: %d, pixels:%d, length:%d\n", compressed.number_of_lines, compressed.pixels_per_line, compressed.length_in_bytes); */ } /***************************** control_compression ************************** calls the functions that compress the image *****************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the uncompressed data. * * Returned: * * compressed - structure containing the # of pixels * * per line, the number of lines, and * * the compressed data. * ************************************************************************/ void control_compression(struct uncompressed_descriptor *uncompressed, struct compressed_descriptor *compressed ) { struct parameters sole_parameters; struct parameters *params = &sole_parameters; #ifdef TIME SHORT i; tz.tz_minuteswest = 0; tz.tz_dsttime = 0; gettimeofday(&t1, &tz); #endif prepare_to_compress( uncompressed, compressed, params ); compress_image( uncompressed, compressed, params ); /* memory deallocation added by Michael D. Garris 2/26/90 */ free(params->reference_line); free(params->coding_line); #ifdef TIME gettimeofday(&t2, &tz); printf("\ntime difference: %ld:%ld\n", t2.tv_sec - t1.tv_sec, t2.tv_usec - t1.tv_usec); for(i=0; i<5; i++) printf("%c",'\07');*/ #endif } /************************ read_uncompressed_file_into_memory ******************* allocates memory for the uncompressed image. *****************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the uncompressed data. * * Returned: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the compressed data. * ************************************************************************/ void read_uncompressed_file_into_memory(struct uncompressed_descriptor *uncompressed) { int file_size; if(comp_alloc_flag) { file_size = uncompressed->pixels_per_line * uncompressed->number_of_lines / Pixels_per_byte; if((uncompressed->data = (char *)calloc( file_size, sizeof(char)))== NULL) { printf("\nCannot allocate enough memory for uncomp file.\n"); crash_c(); } } else if(uncompressed->data == NULL) { printf("\nNo memory allocated for input data!\n"); crash_c(); } } /* end read_uncompressed_file_into_memory() */ /*************************** prepare_to_compress **************************** initializes variables in preperation for compression *****************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the uncompressed data. * * Returned: * * compressed - structure containing the # of pixels * * per line, the number of lines, and * * the compressed data. * * params - structure storing information needed for * * comparison and other tasks. * ************************************************************************/ void prepare_to_compress(struct uncompressed_descriptor *uncompressed, struct compressed_descriptor *compressed, struct parameters *params) { params->max_pixel = uncompressed->pixels_per_line; compressed->pixels_per_line = uncompressed->pixels_per_line; compressed->number_of_lines = uncompressed->number_of_lines; set_up_first_line_c( params ); prepare_to_write_bits_c( compressed ); } /* end prepare_to_compress() */ /****************************** compress_image ******************************* compresses the image *****************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the uncompressed data. * * Returned: * * compressed - structure containing the # of pixels * * per line, the number of lines, and * * the compressed data. * * params - structure storing information need for * * comparison and other tasks. * ************************************************************************/ void compress_image(struct uncompressed_descriptor *uncompressed, struct compressed_descriptor *compressed, struct parameters *params) { SHORT line; for(line = 0; line < uncompressed->number_of_lines; line++) { make_array_of_changing_elements( params, uncompressed, line ); set_up_first_and_last_changing_elements_c( params ); compress_line( params ); prepare_to_compress_next_line( params ); } /* end for each line loop */ write_bits_c("000000000001000000000001"); compressed->length_in_bytes = flush_buffer(); } /************************ make_array_of_changing_elements ********************* stores in a list pointed to by "params->coding_line" the pixel numbers of all the changing elements in the coding line *****************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * uncompressed - structure containing the # of pixels * * per line, the number of lines, and * * the uncompressed data. * * line_number - the number of the line in the image * * Returned: * * params - structure storing information need for * * comparison and other tasks. * ************************************************************************/ void make_array_of_changing_elements(struct parameters *params, struct uncompressed_descriptor *uncompressed, SHORT line_number) { SHORT bytes_per_line; int line_offset; SHORT byte_offset; bytes_per_line = params->max_pixel / Pixels_per_byte; line_offset = bytes_per_line * line_number; for(byte_offset=0; byte_offset < bytes_per_line; byte_offset++) { process_char(*(uncompressed->data+line_offset+byte_offset),params); } } /* end make_array_of_changing_elements() */ /******************* set_up_first_and_last_changing_elements_c ***************** initializes the first and last changing elements in the coding line ******************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * params - structure storing information need for * * comparison and other tasks. * * Returned: * * params - structure storing information need for * * comparison and other tasks. * ************************************************************************/ void set_up_first_and_last_changing_elements_c(struct parameters *params) { *(params->coding_line) = Invalid; *(params->coding_line + ++params->index) = params->max_pixel; *(params->coding_line + ++params->index) = params->max_pixel; *(params->coding_line + ++params->index) = params->max_pixel; /* the previous lines may be necessary if when searching for b1, you skip some elements because you know that they are the wrong color */ } /************************ prepare_to_compress_next_line *********************** initializes variables in preperation for compressing another line ******************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * params - structure storing information need for * * comparison and other tasks. * * Returned: * * params - structure storing information need for * * comparison and other tasks. * ************************************************************************/ void prepare_to_compress_next_line(struct parameters *params) { SHORT *temp; /* swap the reference and unchanged coding lines */ temp = params->reference_line; params->reference_line = params->coding_line; params->coding_line = temp; params->pixel = 0; params->index = 0; params->previous_color = White; } /* end prepare_to_read_next_line() */ /******************************* set_up_first_line_c *************************** initializes variables in preperation for compressing the first line ******************************************************************************/ /*********************************************************************** * Arguments * * --------- * * Passed in: * * params - structure storing information need for * * comparison and other tasks. * * Returned: * * params - structure storing information need for * * comparison and other tasks. * ************************************************************************/ void set_up_first_line_c(struct parameters *params) { params->reference_line = (SHORT *)malloc( (params->max_pixel + Extra_positions) * sizeof(SHORT) ); params->coding_line = (SHORT *)malloc( (params->max_pixel + Extra_positions) * sizeof(SHORT) ); *(params->reference_line + 0) = Invalid; *(params->reference_line + 1) = params->max_pixel; *(params->reference_line + 2) = params->max_pixel; *(params->reference_line + 3) = params->max_pixel; /* initialize first changing element on coding line (A0 = -1) */ *(params->coding_line) = Invalid; params->pixel = 0; params->index = 0; params->previous_color = White; } /* end set_up_first_line_c() */ /*********************************** crash_c *********************************** forces the program to crash and create a core file *****************************************************************************/ void crash_c() { FILE *crash_program = NULL; fprintf(crash_program,"This will kill the program and create a core file"); } /***************************************************************************/ /* Originally mode.c */ /***************************************************************************/ static SHORT A0, A0_color, A1, a2, b1, b2; /******************************* compress_line ******************************** compresses a single line of the image *****************************************************************************/ void compress_line(struct parameters *params) { #if Debug static SHORT line = 0; printf("\nLINE %d. ",line); line++; #endif A0 = Invalid; /* set A0 equal to imaginary first array element */ A0_color = White; A1 = 1; initialize_b1(params); b2 = b1 + 1; #if Debug printf("\nA0:%d A1:%d b1:%d b2:%d ", A0, *(params->coding_line+A1), *(params->reference_line+b1),*(params->reference_line+b2)); #endif do { if (*(params->reference_line + b2) < *(params->coding_line + A1)) { pass_mode_c(params); continue; } else if (abs(*(params->coding_line+A1)-*(params->reference_line+b1)) <=3) vertical_mode_c(params); else horizontal_mode_c(params); #if Debug printf("\nA0:%d A1:%d b1:%d b2:%d ", A0, *(params->coding_line+A1), *(params->reference_line+b1),*(params->reference_line+b2)); #endif } while( A0 < params->max_pixel); } /******************************* initialize_b1 ******************************** locates b1's first position in the reference line *****************************************************************************/ void initialize_b1(struct parameters *params) { SHORT last_bit_of_b1; b1 = 1; last_bit_of_b1 = b1 & Last_bit_mask; while( ((*(params->reference_line +b1) <=A0) || (A0_color ==last_bit_of_b1)) && (*(params->reference_line + b1) < params->max_pixel) ) { b1++; last_bit_of_b1 = b1 & Last_bit_mask; } /* end while loop */ #if Debug printf("\nb1:%d :%d, A0:%d", b1, *(params->reference_line+b1), A0); #endif } /********************************** pass_mode_c ******************************** compresses a pass mode *****************************************************************************/ void pass_mode_c(struct parameters *params) { write_bits_c("0001"); #if Debug printf(" P "); #endif /* * Reset the value A0 points to to a'0 (the value that b2 points to). */ A0 = *(params->reference_line + b2); /* * Since A0 is now greater than the pixel b1 points to, both b1 and b2 * must be advanced twice to maintain the color difference between A0 and * b1, and the positional requirement that b1 point to a pixel greater than * the one A0 points to. */ b1 += 2; b2 += 2; /* * Note that the b's can be advanced by two positions without fear of * moving them beyond the last changing element because pass_mode cannot * occur if b2 is already pointing to max_pixel. */ } /****************************** vertical_mode_c ******************************** compresses a vertical mode *****************************************************************************/ void vertical_mode_c(struct parameters *params) { SHORT difference; difference = *(params->coding_line + A1) - *(params->reference_line + b1); A0 = *(params->coding_line + A1); A0_color = !A0_color; A1++; #if Debug printf(" V%d ", difference); #endif switch(difference) { case 0: write_bits_c("1"); if(*(params->reference_line + b1) != params->max_pixel ) { b1++; b2++; } /* end if b1 is not on the last changing element */ break; case 1: write_bits_c("011"); b1++; b2++; if((*(params->reference_line + b1) <= A0) && (*(params->reference_line + b1) != params->max_pixel) ) { b1 += 2; b2 += 2; } break; case -1: write_bits_c("010"); if(*(params->reference_line + b1) != params->max_pixel ) { b1++; b2++; } /* end if b1 is not on the last changing element */ break; case 2: write_bits_c("000011"); b1++; b2++; if((*(params->reference_line + b1) <= A0) && (*(params->reference_line + b1) != params->max_pixel) ) { b1 += 2; b2 += 2; } break; case -2: write_bits_c("000010"); if(*(params->reference_line + b1 - 1) > A0 ) { b1--; b2--; } else if(*(params->reference_line + b1) != params->max_pixel) { b1++; b2++; } break; case 3: write_bits_c("0000011"); b1++; b2++; while ((*(params->reference_line + b1) <= A0) && (*(params->reference_line + b1) != params->max_pixel) ) { b1 += 2; b2 += 2; } break; case -3: write_bits_c("0000010"); if(*(params->reference_line + b1 - 1) > A0 ) { b1--; b2--; } else if(*(params->reference_line + b1) != params->max_pixel) { b1++; b2++; } break; default: printf("ERROR in vertical_mode_c() "); } /* end case of difference */ } /**************************** horizontal_mode_c ******************************** compresses a horizontal mode *****************************************************************************/ void horizontal_mode_c(struct parameters *params) { SHORT run_length; #if Debug printf(" a2:%d H ",*(params->coding_line + a2)); #endif a2 = A1 + 1; write_bits_c("001"); if(A0 == Invalid) /* on imaginary first pixel */ run_length = *(params->coding_line + A1); else run_length = *(params->coding_line + A1) - A0; write_run_length(run_length, A0_color ); /* the last bit contains the color of the changing element */ run_length = *(params->coding_line + a2) - *(params->coding_line + A1); write_run_length(run_length, !A0_color); /* * Must use !A0_color instead of A1 because in cases in which A1 occurs * on max_pixel, its color is bogus. */ /* NOTE: is the above statement true? if A1 were on max_pixel, you should not get horizontal mode. */ A0 = *(params->coding_line + a2); A1 = a2 + 1; while((*(params->reference_line + b1) <= *(params->coding_line + a2)) && ( *(params->reference_line + b1) < params->max_pixel) ) { b1 += 2; /* must move ahead by 2 to maintain color difference with */ b2 += 2; /* A0, whose color does not change in this mode. */ } } /***************************************************************************/ /* Originally write_bits_c.c */ /***************************************************************************/ static SHORT bit_place_mark; static int byte_place_mark; static unsigned char *output_area; static char write_one[Pixels_per_byte] = { (char)0x80, (char)0x40, (char)0x20, (char)0x10, (char)0x8, (char)0x4, (char)0x2, (char)0x1, }; static char write_zero[Pixels_per_byte] = { (char)0x7F, (char)0xBF, (char)0xDF, (char)0xEF, (char)0xF7, (char)0xFB, (char)0xFD, (char)0xFE, }; /*************************** prepare_to_write_bits_c ************************** initializes variables in preperation for writing compressed images *****************************************************************************/ void prepare_to_write_bits_c(struct compressed_descriptor *compressed) { if(comp_alloc_flag){ (char *)compressed->data = calloc( (compressed->pixels_per_line * compressed->number_of_lines / Pixels_per_byte), sizeof(char) ); } /* * This allocation is usually very wasteful, but because there is no * way of knowing how much space is needed, I decided to be generous. */ if (compressed->data == NULL){ printf("\nMemory allocation error for compressed output data.\n"); crash_c(); } output_area = compressed->data; } /******************************** write_bits_c ********************************** writes a variable length series of bits represented by a string of '1's and '0's, which it receives as a parameter *****************************************************************************/ void write_bits_c(char *string_ptr) { /* global switch added by Michael D. Garris 2/26/90 */ if(comp_write_init_flag) { bit_place_mark = 0; byte_place_mark = 0; comp_write_init_flag = 0; } while(*string_ptr != '\0') { if(*string_ptr == '1') *(output_area + byte_place_mark) |= write_one[bit_place_mark]; else *(output_area + byte_place_mark) &= write_zero[bit_place_mark]; if(bit_place_mark == Last_bit_in_a_byte) { bit_place_mark = 0; byte_place_mark++; } /* end if byte is full */ else bit_place_mark++; string_ptr++; } /* end while */ } /******************************** flush_buffer ******************************* writes to memory whatever bits are left in the bit buffer followed by enough zero-bits to pad the compressed image out to a byte boundary. *****************************************************************************/ unsigned int flush_buffer() { SHORT i; if (bit_place_mark != 0) { for (i=bit_place_mark; icoding_line" ******************************************************************************/ void process_char(unsigned char data_byte, struct parameters *params) { static char color = 0; SHORT i = 0; color = -(data_byte & Last_bit_mask); data_byte ^= params->previous_color; /* if the previous color is black - which is contrary to our assumptions - * the bits in the byte must all be changed so that the result, when used * as an index into the array 'bytes,' yields the correct result. In the * above operation, if the previous color is black (11111111b), all bits * are changed; if the previous color is white (00000000b), no bits are * changed. */ while(table[data_byte].pixel[i] != Invalid) *( params->coding_line + ++params->index ) = params->pixel + table[data_byte].pixel[i++]; params->pixel += Pixels_per_byte; params->previous_color = color; /* 'color' is a temporary holding place for the value of previous color */ }