//----------------------------------------------------------------------------- // 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 file is best viewed using a fixed spaced font such as Courier // and in a display at least 120 columns wide. //----------------------------------------------------------------------------- #define GAMMA 2.4426530040802915866 #define MAX_CIRCLES 64 #include #include #include #include #include #include #include //----------------------------------------------------------------------------- // program corner // // purpose Generate routed border corners and output them as GIF image // files. // // usage This program may be used either the command line to make an // image file which will be written to the filename used to // specify the image, or as a CGI program to dynamically make // the image to be written to stdout for a web browser. // // command corner filename.ext [ ... filename.ext ] // // CGI http://www.domain.tld/cgi-bin/corner/filename.ext // http://www.domain.tld/corner.cgi/filename.ext // // filename The parameters for defining the image are encoded in the // file name. The first part of the name is one of "ul","ur", // ll","lr", to identify which quadrant is produced. Each // circle part consists of a color followed by a radius. // The background color and image size are the last numbers // followed by the extension. Colors are coded in RGB with // 3,6,9,12,15,18, or 21 cetal digits. Radii and size are // coded in decimal digits. // // example ur-fff-20-000-40-fff-40.gif //----------------------------------------------------------------------------- #define RESCALE(a,b,x,y,z) (((((b)-(a))*((z)-(x)))+((a)*((y)-(x))))/((y)-(x))) //----------------------------------------------------------------------------- // function convert_rgb // // purpose Convert a string of cetal (hexadecimal) characters which // represent an RGB color triplet into three double values. // // arguments 1 (const char *) the string to convert // 2 (char * *) where to store end pointer // 3 (double *) where to store red value // 4 (double *) where to store green value // 5 (double *) where to store blue value // // returns (int) == 0: OK // (int) < 0: error //----------------------------------------------------------------------------- static int convert_rgb ( const char * arg_str , char * * arg_end , double * arg_red , double * arg_green , double * arg_blue ) { double fscale ; const char * ptr ; long red ; long green ; long blue ; long scale ; size_t len ; int num ; int i ; //--------------------------------------------------------------- // Pre-determine the length so we know how many digits per color. // Go ahead and store the end pointer once the end is found. //--------------------------------------------------------------- ptr = arg_str; while ( ( '0' <= * ptr && * ptr <= '9' ) || ( 'A' <= * ptr && * ptr <= 'F' ) || ( 'a' <= * ptr && * ptr <= 'f' ) ) ++ ptr; if ( arg_end ) * (const char * *) arg_end = ptr; len = ptr - arg_str; num = len / 3; if ( num > 7 ) return -1; ptr = arg_str; //-------------------------------------------------------------- // Calculate the scaling factor to get into the 0.0 - 1.0 range. //-------------------------------------------------------------- scale = 0; for ( i = num; i; -- i ) { scale <<= 4; scale += 15; } fscale = (double) scale; //-------------------------------- // Scan and convert the red value. //-------------------------------- red = 0; for ( i = num; i; -- i ) { red <<= 4; red += ( (* ptr ++) | 4400 ) % 55; } if ( arg_red ) * arg_red = pow( ( (double) red ) / fscale, GAMMA ); //---------------------------------- // Scan and convert the green value. //---------------------------------- green = 0; for ( i = num; i; -- i ) { green <<= 4; green += ( (* ptr ++) | 4400 ) % 55; } if ( arg_green ) * arg_green = pow( ( (double) green ) / fscale, GAMMA ); //--------------------------------- // Scan and convert the blue value. //--------------------------------- blue = 0; for ( i = num; i; -- i ) { blue <<= 4; blue += ( (* ptr ++) | 4400 ) % 55; } if ( arg_blue ) * arg_blue = pow( ( (double) blue ) / fscale, GAMMA ); return 0; } //----------------------------------------------------------------------------- // function circle_part // // purpose Calculate the average intensity value for a rectangular area // where a specified circle contributes 1.0 and the area outside // the circle contributes 0.0. // // arguments 1 (double) rectangular area left bound // 2 (double) rectangular area right bound // 3 (double) rectangular area top bound // 4 (double) rectangular area bottom bound // 5 (double) circle radius (circle origin is 0,0) // 6 (int) limit on recursive descent // // returns (double) average intensity from 0.0 to 1.0 //----------------------------------------------------------------------------- static double circle_part ( double arg_x_left , double arg_x_right , double arg_y_top , double arg_y_bottom , double arg_radius , int arg_limit ) { double sq_radius ; double sq_x_left ; double sq_x_right ; double sq_y_top ; double sq_y_bottom ; double center_x ; double center_y ; double value ; //------------------------------------------------------------ // If the region is entirely in one quadrant, try these tests. //------------------------------------------------------------ if ( ( arg_x_left * arg_x_right > 0.0 ) && ( arg_y_top * arg_y_bottom > 0.0 ) ) { //----------------------------------------------- // Pre-calculate squares for circle radius tests. //----------------------------------------------- sq_radius = arg_radius * arg_radius; sq_x_left = arg_x_left * arg_x_left; sq_x_right = arg_x_right * arg_x_right; sq_y_top = arg_y_top * arg_y_top; sq_y_bottom = arg_y_bottom * arg_y_bottom; //-------------------------------------------------------- // If the region is outside the circle-square, return 0.0. //-------------------------------------------------------- if ( ( sq_y_top > sq_radius && sq_y_bottom > sq_radius ) || ( sq_x_left > sq_radius && sq_x_right > sq_radius ) ) { return 0.0; } //--------------------------------------------------------- // If the region is entirely inside the circle, return 1.0. //--------------------------------------------------------- if ( ( sq_y_top + sq_x_left ) < sq_radius && ( sq_y_top + sq_x_right ) < sq_radius && ( sq_y_bottom + sq_x_left ) < sq_radius && ( sq_y_bottom + sq_x_right ) < sq_radius ) { return 1.0; } //---------------------------------------------------------- // If the region is entirely outside the circle, return 0.0. // Be sure the region does not straddle the circle. //---------------------------------------------------------- if ( ( sq_y_top + sq_x_left ) > sq_radius && ( sq_y_top + sq_x_right ) > sq_radius && ( sq_y_bottom + sq_x_left ) > sq_radius && ( sq_y_bottom + sq_x_right ) > sq_radius && ( arg_y_top * arg_y_bottom ) > 0.0 && ( arg_x_left * arg_x_right ) > 0.0 ) { return 0.0; } } //---------------------------------------------------- // If split recursion limit exceeded, just assume 0.5. //---------------------------------------------------- if ( arg_limit < 1 ) return 0.5; //--------------------------------------------------------------- // Split the region into quadrants and calculate each separately. //--------------------------------------------------------------- -- arg_limit; center_x = ( arg_x_left + arg_x_right ) / 2.0; center_y = ( arg_y_top + arg_y_bottom ) / 2.0; value = circle_part( arg_x_left, center_x, arg_y_top, center_y, arg_radius, arg_limit ); value += circle_part( center_x, arg_x_right, arg_y_top, center_y, arg_radius, arg_limit ); value += circle_part( arg_x_left, center_x, center_y, arg_y_bottom, arg_radius, arg_limit ); value += circle_part( center_x, arg_x_right, center_y, arg_y_bottom, arg_radius, arg_limit ); return value / 4.0; } //----------------------------------------------------------------------------- // function pixel_value // // purpose Correct the gamma of a pixel value in the range 0.0 to 1.0 // and convert to an integer value in the range 0 to 255. // // argument 1 (double) linear pixel value in range 0.0 to 1.0 // // returns (int) gamma corrected quantized integer pixel value //----------------------------------------------------------------------------- static inline int pixel_value ( double arg_pixel ) { int pixel ; pixel = (int) ( (65536.0*255.0) * pow( arg_pixel, 1.0/GAMMA ) ); pixel += 128; pixel >>= 16; return pixel; } //----------------------------------------------------------------------------- // function make_image //----------------------------------------------------------------------------- int make_image ( char * arg_name , FILE * arg_file ) { angif_header gif_header ; angif_stream gif_stream ; angif_image gif_image ; double circle_red [MAX_CIRCLES] ; double circle_green [MAX_CIRCLES] ; double circle_blue [MAX_CIRCLES] ; double circle_radius [MAX_CIRCLES] ; double fsize ; double box_xl ; double box_xr ; double box_yt ; double box_yb ; double pix_xl ; double pix_xr ; double pix_yt ; double pix_yb ; double y ; double x ; long * image_mem ; long * image_ptr ; char * last_dot ; char * ptr ; int num_circles ; int isize ; int ok ; int i ; char file_ext [4] ; //-- Find the last '.' in the name. ptr = arg_name; last_dot = NULL; while ( * ptr ) { if ( * ptr == '.' ) last_dot = ptr; ++ ptr; } if ( last_dot && last_dot[0] && last_dot[1] && last_dot[2] ) { file_ext[0] = last_dot[1] | 0x20; file_ext[1] = last_dot[2] | 0x20; file_ext[2] = last_dot[3] | 0x20; } else { file_ext[0] = 'g'; file_ext[1] = 'i'; file_ext[2] = 'f'; } //-- Skip over first 2 letters. ptr = arg_name + 2; //-- Collect up to MAX_CIRCLES circle colors and sizes. //-- The last is the background color and image size. i = 0; fsize = 0.0; while ( i < MAX_CIRCLES ) { //-- Scan for and convert color. if ( ptr >= last_dot || ! * ptr ) break; while ( ptr < last_dot && * ptr && ! isalnum( * ptr ) ) ++ ptr; if ( ptr >= last_dot || ! * ptr ) break; ok = convert_rgb( ptr, & ptr, circle_red+i, circle_green+i, circle_blue+i ); if ( ok != 0 ) return ok; //-- Scan for and convert size. if ( ptr < last_dot && * ptr ) { while ( ptr < last_dot && * ptr && ! isalnum( * ptr ) ) ++ ptr; if ( ptr >= last_dot || ! * ptr ) break; fsize += strtod( ptr, & ptr ); } circle_radius[i] = fsize; ++ i; } num_circles = i - 1; if ( num_circles < 1 ) return 1; //-- Image size is the last radius. fsize = ceil( circle_radius[ num_circles ] ); isize = (int) fsize; if ( isize < 1 || isize > 2048 ) return 1; //-- Determine the requested corner. box_xl = box_xr = box_yt = box_yb = 0.0; ok = 0; for ( i = 0; i < 2; ++ i ) { int ch; ch = arg_name[i] | 0x20; if ( ch == 't' ) { box_yt = fsize; box_yb = 0.0; ok = 1; } else if ( ch == 'b' ) { box_yt = 0.0; box_yb = -fsize; ok = 1; } else if ( ch == 'l' ) { box_xl = -fsize; box_xr = 0.0; ok = 1; } else if ( ch == 'r' ) { box_xl = 0.0; box_xr = fsize; ok = 1; } else { return 1; } } if ( ! ok ) return 1; //-- Grab space to build the image. image_mem = malloc( sizeof (long) * isize * isize ); if ( ! image_mem ) return 0; image_ptr = image_mem; //-- Step through all the pixels. pix_yt = box_yt; for ( y = 0.0; y < fsize; ) { y += 1.0; pix_yb = RESCALE( box_yt, box_yb, 0.0, fsize, y ); pix_xl = box_xl; for ( x = 0.0; x < fsize; ) { double pix_r, pix_g, pix_b, mult, part, prev; long ir,ig,ib; x += 1.0; pix_xr = RESCALE( box_xl, box_xr, 0.0, fsize, x ); //-- Accumulate the colors for this pixel. pix_r = pix_g = pix_b = 0.0; prev = 0.0; for ( i = 0; i < num_circles && prev < 1.0; ++ i ) { part = circle_part( pix_xl, pix_xr, pix_yt, pix_yb, circle_radius[i], 6 ); if ( part > 0.0 ) { mult = part - prev; pix_r += mult * circle_red[i]; pix_g += mult * circle_green[i]; pix_b += mult * circle_blue[i]; prev = part; } } if ( prev < 1.0 ) { mult = 1.0 - prev; pix_r += mult * circle_red[num_circles]; pix_g += mult * circle_green[num_circles]; pix_b += mult * circle_blue[num_circles]; } ir = pixel_value( pix_r ); ig = pixel_value( pix_g ); ib = pixel_value( pix_b ); * image_ptr = ( ir << 16 ) | ( ig << 8 ) | ib; ++ image_ptr; pix_xl = pix_xr; } pix_yt = pix_yb; } //------------------------------------------------------------ // If the request is for PNG or PPM/PNM then handle that here. //------------------------------------------------------------ if ( file_ext[0] == 'p' ) { //----------------------------------------------------- // If the request is for PNG then generate that format. //----------------------------------------------------- if ( file_ext[2] == 'g' ) { return 1; } //-------------------------------------------------------------- // If the request if for PPM/PNM then generate that format. //-------------------------------------------------------------- if ( file_ext[2] == 'm' ) { int ix,iy; if ( getenv( "REQUEST_METHOD" ) ) { fprintf( arg_file, "Content-type: image/ppm\r\n" ); fprintf( arg_file, "\r\n" ); } fprintf( arg_file, "P6\n%d %d 255\n", isize, isize ); image_ptr = image_mem; for ( ix = 0; ix < isize; ++ ix ) { for ( iy = 0; iy < isize; ++ iy ) { long pix; pix = * image_ptr ++; putc( pix >> 16, arg_file ); putc( pix >> 8, arg_file ); putc( pix , arg_file ); } } return 0; } else return 1; } //------------------------------------------------------ // If the request is for JPEG then generate that format. //------------------------------------------------------ if ( file_ext[0] == 'j' ) { return 1; } //----------------------------------------------------- // If the request is for GIF then generate that format. //----------------------------------------------------- if ( file_ext[0] == 'g' ) { if ( getenv( "REQUEST_METHOD" ) ) { fprintf( arg_file, "Content-type: image/gif\r\n" ); fprintf( arg_file, "\r\n" ); } //-- Setup a stream for the GIF data. angif_init_stream( & gif_stream ); angif_set_file( & gif_stream, arg_file ); //-- Initialize the GIF header. angif_init_header( & gif_header ); gif_header.width = isize; gif_header.height = isize; gif_header.color_res = 7; //-- Initialize the GIF image block. angif_init_image( & gif_image ); gif_image.data_rgb = image_mem; gif_image.image_width = isize; gif_image.image_height = isize; gif_image.array_width = isize; gif_image.array_height = isize; //-- Output the GIF stream. angif_put_header( & gif_stream, & gif_header ); angif_put_rgb( & gif_stream, & gif_image ); angif_put_trailer( & gif_stream ); return 0; } return 1; } //----------------------------------------------------------------------------- // function main //----------------------------------------------------------------------------- int main ( int argc , char * * argv ) { FILE * fp ; char * path_info ; char * http_referer ; char * referer_host ; char * referer_sld ; char * server_name ; char * server_sld ; char copy_referer [65536]; //--------------------------------------------------------------- // Check to see if this process is a CGI request in a web server. //--------------------------------------------------------------- if ( ( path_info = getenv( "PATH_INFO" ) ) ) { //----------------------------------------------------------------- // Make sure the second level domain matches in the referer header. //----------------------------------------------------------------- if ( ( http_referer = getenv( "HTTP_REFERER" ) ) ) { strncpy( copy_referer, http_referer, sizeof copy_referer ); copy_referer[ ( sizeof copy_referer ) - 1 ] = 0; str_split_url( copy_referer, NULL, NULL, NULL, & referer_host, NULL, NULL, NULL ); referer_sld = str_tail( referer_host, '.', 2 ); server_name = getenv( "SERVER_NAME" ); server_sld = str_tail( server_name, '.', 2 ); if ( str_cmp_lim_lower( referer_sld, server_sld, ~0 ) != 0 ) { printf( "Status: 409 Conflicting Referer\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "\r\n" "\r\n" "\r\n" "409 Conflicting Referer\r\n" "\r\n" "

Conflicting Referer

\r\n" "A conflicting referer was encountered while accessing %s on this server.

\r\n" "


\r\n" "
%s Server at %s Port %s
\r\n" "\r\n", getenv( "REQUEST_URI" ), getenv( "SERVER_SOFTWARE" ), server_name, getenv( "SERVER_PORT" ) ); fflush( stdout ); return 0; } } //---------------------------------------------------------------- // Make the image based on the description in the final component // of the PATH_INFO CGI environment variable, outputing to stdout. //---------------------------------------------------------------- make_image( str_tail( path_info, '/', 1 ), stdout ); return 0; } //------------------------------------------------------------- // Scan arguments to generate and write an image file for each. //------------------------------------------------------------- while ( -- argc && * ++ argv ) { if ( ( fp = fopen( * argv, "wb" ) ) ) { if ( make_image( * argv, fp ) == 0 ) { fprintf(stderr,"wrote image \"%s\"\n",*argv); } else { fprintf(stderr,"error writing \"%s\"\n",*argv); } fclose( fp ); } else { fprintf(stderr,"unable to open \"%s\"\n",*argv); } } return 0; }