//----------------------------------------------------------------------------- // Copyright © 2003 - Philip Howard - All rights reserved // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //----------------------------------------------------------------------------- // package libh/angif // homepage http://libh.slashusr.org/ //----------------------------------------------------------------------------- // author Philip Howard // email libh at ipal dot org // homepage http://phil.ipal.org/ //----------------------------------------------------------------------------- // This library is "patent free" in that it contains no code to implement // the LZW compression, which is covered by patent number 4,558,302 owned // by Unisys. // // GIF is a service mark, property of CompuServe, Inc. // // The GIF standard is obsolete and is being deprecated in favor of the // PNG (Portable Network Graphics) standard. //----------------------------------------------------------------------------- #include "angif_lib.h" __PROTO_BEGIN__ //----------------------------------------------------------------------------- // function angif_put_rgb // // purpose Output a coded RGB mode image array to a stream target. // // arguments 1 (angif_stream *) stream target // 2 (angif_image *) image object // // returns (int) 0 successful completion // (int) <0 error indicated by return value and errno //----------------------------------------------------------------------------- int angif_put_rgb ( angif_stream * stream , angif_image * image ) __PROTO_END__ { angif_image new_image ; long color_table [256]; int color_hash [ANGIF_COLOR_MODULUS]; long * rgb_row_ptr ; long * rgb_pix_ptr ; unsigned char * index_row_ptr ; unsigned char * index_row_end ; unsigned char * index_pix_ptr ; unsigned char * index_pix_end ; unsigned char * index_array ; long array_left ; long array_top ; long array_width ; long array_height ; long image_width ; long image_height ; unsigned int rgb_pix ; int color_count ;// Count of actual colors in table int table_vec ; int hash_vec ; int trans_flag ; int trans_index ; int split ; int rc ; //-- Check arguments. if ( ! stream ) return ANGIF_ERROR_ARG_STREAM; if ( ! image ) return ANGIF_ERROR_ARG_IMAGE; //-- Make sure we have an RGB image. if ( ! image->data_rgb ) return ANGIF_ERROR_DATA; //-- Get frequently used info. image_width = image->image_width; image_height = image->image_height; array_width = image->array_width; array_height = image->array_height; array_left = image->array_left; array_top = image->array_top; //-- Verify image geometry. if ( image_width > ( array_width - array_left ) || image_height > ( array_height - array_top ) ) { return ANGIF_ERROR_GEOMETRY; } //-- Allocate an index image array. index_array = malloc( (size_t) ( image_width * image_height ) ); if ( ! index_array ) return ANGIF_ERROR_ALLOC; //-- Initialize the new image struct. angif_init_image( & new_image ); //-- Initialize the color table to empty. for ( color_count = 0; color_count < 256; ++ color_count ) { color_table[color_count] = -2; } color_count = 0; //-- Initialize the color hash; for ( hash_vec = 0; hash_vec < ANGIF_COLOR_MODULUS; ++ hash_vec ) { color_hash[hash_vec] = -1; } //-- Initialize with no transparency. trans_flag = 0; trans_index = 0; //-- Setup the row starting and ending pointers. rgb_row_ptr = image->data_rgb + array_top * array_width + array_left; index_row_ptr = index_array; index_row_end = index_array + image_height * image_width; //-- Go through all rows of the image. while ( index_row_ptr < index_row_end ) { //-- Setup the pixel starting and ending pointers from the rows. rgb_pix_ptr = rgb_row_ptr; index_pix_ptr = index_row_ptr; index_pix_end = index_row_ptr + image_width; //-- Go through all the pixels of the image row. while ( index_pix_ptr < index_pix_end ) { //-- Get a pixel and look for it in the color table. rgb_pix = * (rgb_pix_ptr ++); hash_vec = ( (unsigned int) ( rgb_pix + 2 ) ) % ANGIF_COLOR_MODULUS; //-- This loop must exit because color_hash cannot fill up //-- as long as ANGIF_COLOR_MODULUS > 256. for (;;) { if ( (table_vec = color_hash[hash_vec]) < 0 ) break; if ( color_table[table_vec] == rgb_pix ) break; if ( (++ hash_vec) >= ANGIF_COLOR_MODULUS ) hash_vec = 0; } //-- If the color is new, count it and put it in the table; if ( table_vec < 0 ) { //-- Assign the color and count it. table_vec = color_count; ++ color_count; //-- If the color table overflows, the image needs to be split. if ( color_count > 256 ) break; //-- Put the new color in the hash and table. color_hash[hash_vec] = table_vec; color_table[table_vec] = rgb_pix; //-- If the color is transparent, then handle it. //-- Leave the -1 in the color table to avoid false lookups. if ( rgb_pix == -1 ) { trans_flag = 1; trans_index = table_vec; } } //-- Put the color table index in the image index. * (index_pix_ptr ++) = table_vec; } //-- If the color table overflows, the image needs to be split. if ( color_count > 256 ) break; //-- Point to next row. rgb_row_ptr += array_width; index_row_ptr += image_width; } //-- Sanity check color count. if ( color_count < 2 ) color_count = 2; //-- Prepare common aspects of new image. new_image.image_left = image->image_left; new_image.image_top = image->image_top; new_image.interlace = image->interlace; new_image.disposal = image->disposal; //-- The compiler is picky about this. rc = 0; //-- If we have a complete index image: if ( color_count <= 256 ) { //-- Build a new image struct for the index image. new_image.data_index = index_array; new_image.color_table = color_table; new_image.color_count = color_count; new_image.image_width = image_width; new_image.image_height = image_height; new_image.array_width = image_width; new_image.array_height = image_height; new_image.delay_time = image->delay_time; new_image.user_input = image->user_input; new_image.trans_flag = trans_flag; new_image.trans_index = trans_index; //-- Put this image as an index image. rc = angif_put_index( stream, & new_image ); } //-- Free the index image array. free( index_array ); //-- If the color table overflowed: if ( color_count > 256 ) { //-- Initialize the new image for splitting. new_image.data_rgb = image->data_rgb; new_image.array_width = array_width; new_image.array_height = array_height; new_image.array_left = image->array_left; new_image.array_top = image->array_top; new_image.delay_time = 0; // no delay between halves new_image.user_input = 0; // no user input between halves //-- Split along a vertical line between left and right halves. if ( image_width > image_height ) { //-- Calculate split with alignment to multiple of 16. split = ( ( image_width / 2 ) + 15 ) & ~15; //-- Put left half. new_image.image_width = split; new_image.image_height = image_height; rc = angif_put_rgb( stream, & new_image ); if ( rc != 0 ) return rc; //-- Put right half. new_image.image_width = image_width - split; new_image.image_left = image->image_left + split; new_image.array_left = image->array_left + split; new_image.delay_time = image->delay_time; new_image.user_input = image->user_input; rc = angif_put_rgb( stream, & new_image ); } //-- Split along a horizontal line between top and bottom halves. else { //-- Calculate split with alignment to multiple of 16. split = ( ( image_height / 2 ) + 15 ) & ~15; //-- Put top half. new_image.image_width = image_width; new_image.image_height = split; rc = angif_put_rgb( stream, & new_image ); if ( rc != 0 ) return rc; //-- Put bottom half. new_image.image_height = image_height - split; new_image.image_top = image->image_top + split; new_image.array_top = image->array_top + split; new_image.delay_time = image->delay_time; new_image.user_input = image->user_input; rc = angif_put_rgb( stream, & new_image ); } } //-- Return the final result. return rc; }